FreeRTOS: Cách kết thúc và khởi động lại bộ lập lịch


Lê Tường Minh
8 tháng trước
Hữu ích 3 Chia sẻ Viết bình luận 0
Đã xem 2034

Hầu hết các hệ thống máy chủ hoặc máy tính để bàn (giả sử Linux, Mac hoặc Windows) có trường hợp sử dụng bình thường khi bạn khởi động hệ điều hành, giả sử vào buổi sáng, tắt máy vào buổi tối và sau đó bạn rời khỏi máy. Hệ thống nhúng là khác nhau; họ không được tham dự và họ phải chạy 'mãi mãi.' Không phải mọi hệ thống nhúng đều cần chạy HĐH (hoặc trong thế giới đó: Hệ điều hành thời gian thực hoặc RTOS), nhưng áp dụng tương tự ở đây: sau khi RTOS được khởi động, không có ý định tắt và khởi động lại. Ở mức độ đó, họ hoàn toàn không hỗ trợ chức năng 'tắt máy' và 'khởi động lại'. Trong trường hợp thu thập thông tin bảo hiểm, điều này sẽ thực sự hữu ích:

thông tin bảo hiểm từ ứng dụng FreeRTOS

Trong trường hợp FreeRTOS, điều gì xảy ra nếu tôi thực sự cần tắt RTOS và khởi động lại nó, vì theo mặc định, điều này không được hỗ trợ. Đây là những gì chúng tôi sẽ được giải quyết trong bài viết này.

Giới thiệu

Hệ thống nhúng về cơ bản khác với hệ thống máy tính để bàn; đôi khi việc tắt và khởi động lại hệ thống máy tính để bàn hoặc máy tính xách tay là điều bình thường, điều này không được lên kế hoạch hoặc dành cho một hệ thống nhúng: theo bản chất của nó, nó sẽ chạy 'luôn luôn'. Điều này được hiển thị rõ hơn từ 'chính' của một hệ thống nhúng: thông thường, chính không bao giờ quay lại và vẫn chạy, đại loại như thế này:

void main(void) {
  InitClocks();
  InitPins();
  InitDrivers();
  for(;;) {
    AppRun();
  }
  /* never leave main */
}


Một điều tương tự áp dụng cho các hệ thống nhúng nếu chạy với RTOS, trong đó điều này trông giống như thế này:

void main(void) {
  InitClocks();
  InitPins();
  InitDrivers();
  CreateInitialTasks();
  StartScheduler();
  /* should never get here as scheduler never terminates */
  for(;;) { }
  /* never leave main */
}


Ảnh chụp màn hình trong bài đăng này đang sử dụng IDE IDE NXP MCUXpresso IDE V10.3.0 không giới hạn và miễn phí với FreeRTOS V10.1.1

Tại sao phải tắt máy và khởi động lại?

Rõ ràng, nhu cầu tắt hoặc khởi động lại RTOS có lẽ không phải là thứ cần thiết nhất cho một RTOS nhúng. Tuy nhiên, tôi đã tìm thấy nó rất hữu ích.

Thật ý nghĩa khi chạy một số phần mềm * sau khi * RTOS đã bị tắt. Ví dụ: nếu tôi thu thập thông tin về phạm vi bảo hiểm (xem phần Thêm công cụ bảo hiểm GNU vào EclipseBảo hiểm mã GNU trên mục tiêu nhúng với Eclipse neon và ARM gcc 5 vân), tôi không muốn can thiệp vào quá trình cuối cùng đó để đổ dữ liệu với RTOS. Ngoài ra, tôi cần quyền truy cập độc quyền vào tài nguyên hệ thống bao gồm nhiều không gian ngăn xếp: tắt RTOS sẽ trả lại cho tôi tất cả RAM và dung lượng ngăn xếp tôi cần cho các hoạt động I / O của tệp.

viết thông tin bảo hiểm gcov sau khi tắt lịch trình

Nó có ý nghĩa để chạy RTOS trong khi nghe cập nhật. Chạy RTOS trong khi thực hiện cập nhật chắc chắn là có thể trong một số trường hợp, nhưng tại một số giai đoạn, tôi phải dừng nó và khởi động lại nó. Điều này có thể được thực hiện với việc thiết lập lại và khởi động lại hệ thống (ví dụ: xem Cách làm thế nào để thiết lập lại ARM Cortex-M bằng phần mềm ). Tôi đã thấy rằng đó là một cách tốt hơn để tắt RTOS thay vì thực hiện cập nhật bên ngoài RTOS và khởi động lại hệ thống.

kết thúc bộ lập lịch FreeRTOS từ một tác vụ

Một trường hợp sử dụng khác là cho các ứng dụng năng lượng thấp. Mặc dù FreeRTOS cũng tuyệt vời ở các chế độ năng lượng thấp (xem Power Low Power với FreeRTOS: Tickless Idle Mode Chế độ ), ứng dụng có thể chuyển sang các chế độ năng lượng thấp hơn với chức năng hạn chế hơn nếu tôi có thể tắt nhiều hệ thống hoạt động hơn. Vì vậy, việc có khả năng chạy RTOS chỉ trong các giai đoạn trong suốt vòng đời của ứng dụng của tôi, nơi nó có ý nghĩa và không ở các phần khác cho tôi sự linh hoạt cao hơn và cơ hội cho sức mạnh thậm chí thấp hơn.

vTaskStartScheduler () theo sau là chế độ năng lượng thấp

Vì vậy, tôi hy vọng bạn thấy lý do tại sao việc tắt và khởi động lại RTOS có thể có ý nghĩa. Hầu hết các RTOS, bao gồm FreeRTOS, có khả năng 'im lặng' trình lập lịch biểu (  vTaskSuspendAll() ví dụ), tuy nhiên, RTOS vẫn ở đó và sử dụng tài nguyên hệ thống. Nhưng khả năng hoàn toàn 'loại bỏ' nó và khởi động lại nó nếu cần sẽ là một điều tuyệt vời.

FreeRTOS

FreeRTOS có chức năng bắt đầu lên lịch (  vTaskStartScheduler()) và thậm chí có  vTaskEndScheduler() chức năng trong API của nó:

Hàm API FreeRTOS vTaskEndScheduler ()

Nhưng như đã lưu ý trong các diễn đàn và trong mô tả API, nó chỉ được hỗ trợ cho cổng PC ( xem mô tả API ):

Lưu ý: Điều này chỉ được triển khai cho cổng PC x86 Real Mode.

Điều đó đúng với cổng FreeRTOS ban đầu. Cổng tôi đã mở rộng hỗ trợ điều này và tôi đang sử dụng nó trong ứng dụng ARM Cortex-M và HCS08.

vTaskEndScheduler () và vTaskStartScheduler ()

Mặc dù RTOS có lệnh gọi API được chuẩn bị để tắt nó, FreeRTOS không có cơ sở hạ tầng để khởi động lại RTOS sau một  vTaskEndScheduler() cuộc gọi. Nhưng đây chính xác là những gì tôi muốn - khởi động lại RTOS sau khi nó đã kết thúc.

Để có khả năng kết thúc bộ lập lịch, macro sau phải được đặt thành 1 trong FreeRTOSConfig.h:

#define INCLUDE_vTaskEndScheduler                


Thách thức là sau khi bộ lập lịch đã được bắt đầu, không dễ để quay lại mã ngay sau  vTaskStartScheduler() cuộc gọi. Bởi vì các tác vụ và ngăn xếp riêng của chúng, cộng với cổng tiêu chuẩn cho FreeRTOS và M3, M4 và M7, thậm chí đặt lại con trỏ ngăn xếp MSP (xem thảo luận trong diễn đàn này ). Vì vậy, nếu tôi muốn quay lại nơi khởi động bộ lập lịch, tôi cần ngăn việc đặt lại con trỏ ngăn xếp MSP trên ARM Cortex. Đây là lý do tại sao tôi đã thêm một cài đặt vào FreeRTOS để định cấu hình:

đặt lại cài đặt MSP

Điều này đặt cấu hình sau xác định:

#ifndef configRESET_MSP
  #define configRESET_MSP                         (0)
   /*!< 1: reset MSP at scheduler start (Cortex M3/M4/M7 only); 0: do not reset MSP */
#endif


Với cài đặt này được đặt thành 0, cổng của tôi không * không * đặt lại MSP tại điểm bắt đầu của bộ lập lịch.

mã cổng để thiết lập lại con trỏ ngăn xếp msp

Điều này có nghĩa là không phải toàn bộ ngăn xếp MSP sẽ có sẵn cho các ngắt (xem phần gián đoạn ARM ARM Cortex-M và FreeRTOS: Phần 3 Hồi ), nhưng điều đó cũng thường ổn.

Để có thể quay trở lại điểm bắt đầu của trình lập lịch biểu, tôi sử dụng một tính năng thú vị của thư viện C: setjmp / longjmp (xem  http://web.eecs.utk.edu/~huangj/cs360/360/notes/ Setjmp / giảng.html) .

Đầu tiên, tôi cần một biến bộ đệm nhảy:

#if INCLUDE_vTaskEndScheduler
#include <setjmp.h>
static jmp_buf xJumpBuf; /* Used to restore the original context when the scheduler is ended. */
#endif


Bên trong   xPortStartScheduler(), tôi thiết lập bộ đệm nhảy:

#if INCLUDE_vTaskEndScheduler
    if(setjmp(xJumpBuf) != 0 ) {
      /* here we will get in case of a call to vTaskEndScheduler() */
      __asm volatile(
        " movs r0, #0         \n" /* Reset CONTROL register and switch back to the MSP stack. */
        " msr CONTROL, r0     \n"
        " dsb                 \n"
        " isb                 \n"
      );
      return pdFALSE;
    }
#endif
  vPortStartFirstTask(); /* Start the first task. */
  /* Should not get here! */
  return pdFALSE;


setjmp() trả về 0 nếu thiết lập bộ đệm nhảy và nó sẽ trả về! = 0 trong trường hợp  setjmp() được gọi, nó sẽ được gọi trong khi   vTaskEndScheduler().

void vTaskEndScheduler( void )
{
  /* Stop the scheduler interrupts and call the portable scheduler end
     routine so the original ISRs can be restored if necessary.  The port
     layer must ensure interrupts enable bit is left in the correct state. */
  portDISABLE_INTERRUPTS();
  xSchedulerRunning = pdFALSE;
  vPortEndScheduler();
}


Sau  vPortEndscheduler() đó thực hiện tất cả việc dọn dẹp và thiết lập lại RTOS. Việc thiết lập lại là cần thiết để không nhầm lẫn bất kỳ nhận thức RTOS nào trong trình gỡ lỗi:

void vPortEndScheduler(void) {
  vPortStopTickTimer();
  vPortInitializeHeap();
  uxCriticalNesting = 0xaaaaaaaa;
  /* Jump back to the processor state prior to starting the
     scheduler.  This means we are not going to be using a
     task stack frame so the task can be deleted. */
#if INCLUDE_vTaskEndScheduler
  longjmp(xJumpBuf, 1);
#else
  for(;;){} /* wait here */
#endif
}


Với điều này, bộ lập lịch được kết thúc một cách duyên dáng và tôi trở lại ngăn xếp chính một lần nữa:

trên msp stack sau khi gọi vTaskEndScheduler ()

Tôi thấy điều đó rất tuyệt

Tóm lược

Theo mặc định, đối với các mục tiêu được nhúng, FreeRTOS không hỗ trợ kết thúc bộ lập lịch hoặc khởi động lại bộ lập lịch sau khi kết thúc nó. Bài viết này mô tả một cổng cho ARM Cortex-M, thực hiện  vTaskEndScheduler() và khả năng gọi  vTaskStartScheduler()  lại mà không cần thiết lập lại bật nguồn. Kết thúc và khởi động bộ lập lịch rất hữu ích cho các ứng dụng năng lượng thấp và sử dụng các trường hợp không cần RTOS trong vòng đời của ứng dụng hoặc cho các ứng dụng bộ nạp khởi động. Khái niệm này được sử dụng trên các lõi ARM Cortex-M khác nhau từ NXP bao gồm cả vi điều khiển S / 8 / 16bit nhưng có thể dễ dàng sử dụng cho mọi kiến ​​trúc vi điều khiển khác.

Chúc mừng lên lịch!

Liên kết

Hữu ích 3 Chia sẻ Viết bình luận 0
Đã xem 2034