Học FreeRTOS, p3

Bài này tiếp tục series học FreeRTOS, bàn về hàng đợi (queue), tiếp tục Phần 1Phần 2.

Trong FreeRTOS, mỗi task như là một chương trình riêng biệt, và để liên lạc giữa các task này, kernel cung cấp một số phương tiện có thể kể đến như queue, binary semaphores, counting semaphores, multex, và task notification. Sử dụng semaphores được đề cập ở những phần tiếp theo.

Đây là API đầy đủ của queue, một số hàm cơ bản về create, read, write, và delete tương ứng như sau cho các ví dụ ở learn_freertos/chapter2.

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                            UBaseType_t uxItemSize );

BaseType_t xQueueSend( QueueHandle_t xQueue,
                       const void * pvItemToQueue,
                       TickType_t xTicksToWait );

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void *pvBuffer,
                          TickType_t xTicksToWait );
void vQueueDelete( QueueHandle_t xQueue );

Đặt điểm queue trong FreeRTOS:

  • Một queue chứa một số hữu hạn các phần tử được khai báo khi khởi tạo. Quá trình khởi tạo chỉ không thành công khi vùng heap không còn đủ chổ trống.
  • Queue không được sở hữu bởi một task cụ thể nào cả, và được truy cập từ nhiều task. Thông thường một queue được ghi từ nhiều task, và được đọc ở một task nào đấy.
  • Queue hoạt động theo cơ chế FIFO (first in first out). Việc đọc thực hiện ở cuối queue, và việc ghi có thể ở đầu (xQueueSendToFront) hoặc đuôi (xQueueSendToBack). Một item khi được ghi vào queue thì giá trị của nó được sao chép lại, nghĩa là ta có thể free sau đó, hoặc sử dụng biến cục bộ cho việc ghi.
  • Block khi đọc queue. Khi một task ra lệnh đọc một queue, nếu queue đang trống nó sẽ đi vào chế độ Block và chờ. Task sẽ thoát ra khỏi chế độ Block khi một task khác hoặc interrupt service routine nào đó ghi vào queue này. Task cũng sẽ đi đến Ready nếu thời gian chờ kết thúc. Trong trường hợp nhiều task đang đọc, chỉ có một task được unblock, task đó có priority cao nhất. Nếu chúng cùng priority, task nào chờ lâu hơn thì sẽ được ưu tiên trước.
  • Block khi ghi queue. Khi một task ra lệnh ghi vào queue, nó sẽ đi vào chế độ Block nếu queue đang đầy. Task sẽ thoát ra khỏi Block khi queue có chổ trống trở lại, hoặc kết thúc thời gian chờ. Trường hợp nhiều task đang chờ để ghi thì task nào có priority cao hơn sẽ được unblock trước. Nếu cùng priority, task nào chờ lâu hơn sẽ được ưu tiên trước.

 

Ví dụ

blocking ở receiving

Nằm ở learn_freertos/chapter2/block_receiving.c. Chương trình làm việc như sau: 2 task ghi vào một queue kích thước 5 số long, một task khác đọc từ queue và in ra số long này.

Receiver của vReceiverTask có ưu tiên cao hơn, nên được thực thi trước, khi truy xuất đến queue thì nó nhảy vào trạng thái Block với timeout là 0 (chờ mãi mãi). Lúc này, vSenderTask với instance Task 1 được thực thi, và gởi số 100 vào queue. Ngay lập tức Receiver nhảy đến trạng thái Running, đọc từ queue, in ra terminal, rồi quay lại vòng lặp và việc đọc queue tiếp theo đặt task này vào trạng thái block lần nữa. Task 1 được resume, chạy tiếp đến taskYIED.

taskYIED báo với kernel rằng nó không chiếm đoạt CPU nữa, và kernel đặt Task 2 vào trạng thái Running. Quá trình tương tự như Task 1.

Kết quả ở terminal chạy file thực thi như sau:

Running as PID: 26812
Timer Resolution for Run TimeStats is 100 ticks per second.
Receiver: 100
Receiver: 200
Receiver: 100
Receiver: 200
Receiver: 100
Receiver: 200
Receiver: 100

Mô hình hoạt động các task này như hình bên dưới.

block_receiving

 

Để hiểu thêm, một số thông số sau có thể được thay đổi.

  • thay đổi `mainTASK_RECEIVER_PRIORITY` thành nhỏ hơn hoặc bằng `mainTASK_SENDER_PRIORITY`.
  • đổi `mainQUEUE_SIZE` thành 1, hoặc 10.
  • xóa lệnh `taskYIELD` trong `vSenderTask`.

blocking ở sender

Nằm ở learn_freertos/chapter2/block_sending.c. Chương trình này cũng giống chương trình ở trên, khác biệt là receive task có priority nhỏ hơn sending task.

Queue được khởi tạo để chứa mainQUEUE_SIZE = 3 struct xData. Quá trình chạy được diễn tả ngắn gọn như sau:

  • sender 1 đẩy vào queue, queue length = 1. `taskYield` cho phép sender 2 chạy.
  • sender 2 đẩy vào queue, queue length = 2. `taskYield` cho phép sender 1 chạy.
  • sender 1 đẩy vào queue, queue length = 3, queue đầy và không có khả năng nhận thêm dữ liệu nữa. `taskYield` cho phép sender 2 chạy.
  • sender 2 đẩy dữ liệu vào queue, nhưng không được. sender 2 chuyển sang trạng thái Blocking, với timeout là `xTicksToWait = 100ms`. sender 1 được kernel gọi lên thực thi.
  • sender 1 đẩy dữ liệu vào queue, nhưng không được. sender 1 chuyển sang trạng thái blocking. Kernel lúc này cho phép receiver task thực thi.
  • receiver task đọc 1 item từ queue, làm cho queue không còn đầy nữa. Sự kiện này trigger sender 2 task sống dậy (tại sao không phải là sender 1?)
  • sender 2 ghi tiếp một item vào queue, làm queue đầy trở lại. Sau đó nó gọi `taskYield` nhưng sender 1 đang ở trạng thái blocking nên chính sender 2 lại tiếp tục thực hiện, ghi item tiếp theo vào queue; việc queue đã đầy khiến sender 2 được đặt vào trạng thái blocking. receving task được kích hoạt.
  • receiving task đọc 1 item từ queue, ngay lập tức trigger sender 1 task nhảy sang trạng thái running. Quá trình giống sender 2, và được lặp lại vô tận.

Nếu đoạn trên dài quá thì bạn có thể xem hình sau.

block_receiving.PNG

Để hiểu thêm, bạn có thể thêm vào một task sender (sender 3 chẳng hạn).

 

Xác nhận: Các hình ở trên được lấy từ sách Using The FreeRTOS Realtime Kernel của Richard Barry.

Học FreeRTOS, p2

Phần 2 trong series này sẽ tiếp tục với phần quản lý bộ nhớ của FreeRTOS; tiếp tục sau Phần 1 đề cập đến việc tạo task.

Nếu lập trình trên PC, bạn sẽ quen thuộc với malloc, free để tạo, hủy một vùng nhớ. Ở trên hệ thống nhúng, việc sử dụng memory dynamic allocation không được khuyến khích. Các nguyên nhân có thể tóm gọn như sau:

  • Không xác định (not deterministic). Một hàm có thể được thực hiện/không tùy thuộc vào thời điểm gọi. Trong hệ thống RTOS điều này có thể khó chấp nhận khi không có người tương tác, và người thiết kế mong đợi chương trình luôn chạy theo kịch bản định sẵn.
  • Chương trình malloc/free có thể chiếm tài nguyên trong các hệ thống nhúng (phụ thuộc vào compiler) thường hạn hẹp RAM/ROM.
  • [Phân mảnh](http://www.design-reuse.com/articles/25090/dynamic-memory-allocation-fragmentation-c.html) bộ nhớ.
  • Một số implement malloc/free thường không thread-safe. Nếu chương trình multitask (đó là lý do bạn dùng FreeRTOS), bạn nên bảo vệ nó.

Do đó, khi viết chương trình nhúng, tôi hạn chế malloc/free.

Với kernel thì không có sự lựa chọn khác. FreeRTOS cần tạo/xóa vùng nhớ động mỗi khi các task, queue, hay semaphore được tạo/hủy. Đây là một phần của kernel.

FreeRTOS định nghĩa tổng số byte heap được cung cấp ở configTOTAL_HEAP_SIZE, trong file FreeRTOSConfig.h. Vùng heap này được dùng cho pvPortMallocvPortFree, hai implementation của malloc/free.

FreeRTOS cung cấp lớp quản lý bộ nhớ này độc lập, để kernel này chạy được với các nền tảng phần cứng khác nhau và trình biên dịch khác nhau, ở FreeRTOS/Source/portable/MemMang. Bạn có thể thấy tất cả các lựa chọn, gồm `heap_1.c, heap_2.c, heap_3.c, heap_4.c, heap_5.c’. Hoặc bạn có thể viết chương trình quản lý bộ nhớ của riêng mình :).

Theo mặc định của trong series này https://github.com/nqd/learn_freertos, heap_3.c heap_4.c được sử dụng (nguyên nhân ở bên dưới). Bạn có thể đổi dòng tương ứng ở dòng CFILES += heap_3.c port.c, Makefile.common để có sự lựa chọn khác.

Phần sau tôi sẽ trình bay sự khác nhau của các loại heap_* này.

 

Heap_1.c

Đơn gianr nhất, chỉ cung cấp malloc với hàm pvPortMalloc và không có vPortFree.

Selection_015

Hình trên diễn tả qúa trình các task chiếm dụng configTOTAL_HEAP_SIZE. A là lúc chưa có task nào được khởi tạo. B là lúc có một task được tạo. Mỗi task có stack riêng, quyết định bởi thông số đầu vào usStackDepth của hàm xTaskCreate. Mỗi task còn có vùng task control block (TCB) để làm đại diện cho task đó, xem thêm về TCB.

Như vậy số heap trống sẽ được xác định rõ ràng như trên hình, và lấy được thông qua hàm xPortGetFreeHeapSize.

Kết luận: Nếu chương trình của bạn không hủy task, queue, semaphore, hay gọi malloc, task_1.c là sự lựa chọn cho quản lý bộ nhớ.

 

Heap_2.c

Phức tạp hơn heap_1, với cơ chế free đơn gianr.

Heap_2 sử dụng tốt nhất khi pvPortMalloc sử dụng lại một lượng bộ nhớ vừa đúng với kích thước đang còn trống. Ví dụ trong chương trình tuần tự tạo và xóa một task mà kích thước stack cho task này không thay đổi.

Selection_016

Hình trên diễn tả qúa trình task được khởi tạo, xóa, và sử dụng lại. A là lúc 3 task đang hoạt động. Heap trông nằm ở trên cùng. B là lúc task 2 được free, và có một lỗ trống nằm giữa task 1 và 3. Tuy nhiên, heap_2 không có cơ chế gom bộ nhớ, nên free space giữa hai task vẫn nằm vậy.

C diễn tả qúa trình một task mới được khởi tạo, hai vùng nhớ TCB và stack được gọi tương ứng; task này đặt vừa vào vùng nhớ giữa. Qúa trình này diễn tả trường hợp hoạt động tốt nhất của heap_2.

Heap_2 sử dụng best fit algorithm cho pvPortMalloc. Ví dụ có 3 khoảng trống trong heap với kích thước 5 Bytes, 25 Bytes, và 50 Bytes. pvPortMalloc được gọi với yêu cầu 20B. Lúc này heap_2 chia vùng nhớ 25B làm 2: 20B và 5B, rồi sử dụng 20B này. Để lại 5B cho lần gọi kế tiếp.

Bạn sẽ không muốn dùng heap_2 cho các ứng dụng pvPortMalloc/pvPortFree được gọi tùy ý.

Cũng như heap_1, configTOTAL_HEAP_SIZE trong FreeRTOSConfig.h khai báo vùng nhớ heap tổng cộng. Tổng các vùng nhớ trống được trả về qua hàm xPortGetFreeHeapSize.

 

Heap_3

Heap_3 sử dụng malloc/free của trình biên dịch cho pvPortMalloc/pvPortFree tương ứng. Heap_3 cung cấp thêm cơ chế bảo vệ hai hàm này để chúng thread safe bằng cách dừng task scheduler.

Nếu dùng gcc, bạn sẽ dùng implementation của newlib.

Với heap_3, linker heap và FreeRTOS là một, và configTOTAL_HEAP_SIZE không có tác dụng gì.

 

Heap_4

Heap_4 được xem như là phiên bản cải tiến của heap_2, với khả năng gom các vùng nhớ trống liên tiếp nhau thanh một block lớn hơn.

Ví dụ bạn có var_1, var_2, và var_3 chiếm các vị trí liên tiếp nhau 5, 25, và 100 bytes trong vùng nhớ heap của FreeRTOS. var_1 và var_2 được xóa đi, heap_2 sẽ tạo ra hai vùng nhớ trống 5 và 25 bytes; heap_4 gom lại rồi tạo ra vùng nhớ trống 30 bytes.

Heap_4 nên được sử dụng khi chương trình liên tục tạo/xóa task, queue, semaphore …, vùng heap của hệ thống sẽ ít bị phân mảnh hơn, so với heap_2.

FreeRTOS đồng thời tuyên bố nó tốt hơn hầu hết các malloc implementation khác? (xem http://www.freertos.org/a00111.html).

 

Bình luận

sử dụng heap_*.c nào tùy thuộc vào app của bạn. Đơn gianr như heap_1, hoặc heap_4 với đánh đổi tài nguyên của hệ thóng.

 

Xác nhận: Các hình ở trên được lấy từ sách Using The FreeRTOS Realtime Kernel của Richard Barry.

Học FreeRTOS, p1

 

Tôi bắt đầu series này với tư cách là người hoàn toàn mới về RTOS nói chung và FreeRTOS nói riêng. Tôi sẽ liệt kê các bình luận và code khi đọc các chương của cuốn Using The FreeRTOS Realtime Kernel.

Tại sao FreeRTOS? Sau một thời gian làm trong lĩnh vực hệ thống nhúng, và IOT, tôi nghĩ nếu một phần cứng không chạy được Linux nhúng, nó nên chạy với FreeRTOS và lwIP? Bạn có thể đưa luận điểm có những nơi mà FreeRTOS+lwIP không phù hợp, Contiki hay RIOT phù hợp hơn. Nhưng qủa thật khả năng kết nối trực tiếp và bảo mật cao từ node đến cloud mang lại sự tiện lợi vô cùng lớn so với việc sử dụng border router.

Do tôi không muốn phụ thuộc vào phần cứng khi làm quen với RTOS này, nên đã setup một môi trường mô phỏng trên Linux, ở https://github.com/nqd/freertos_linux_devl. Trong môi trường này v8.2.3 được sử dụng, lwIP v1.4.1 về sau cũng được tích hợp vào.

Môi trường này không tồn tại nếu không có bản port tuyệt vời của William Davy và fork của megakilo.

Những dòng lệnh sau sẽ chuẩn bị môi trường cho phần này và những phần sau.


git clone --recursive https://github.com/nqd/learn_freertos.git

cd freertos_linux_devl

make -f Makefile.tools

Nếu bạn tò mò, Makefile.tools để lấy freertos v8.2.3 xuống.

Bài đầu tiên, tạo task.


cd ../chapter1

make

./create_task.out

Bình luận

Nếu thay phần vTaskDelay(1000 / portTICK_RATE_MS); bằng vòng lặp for.

Với For delay

Cho mainDELAY_LOOP_COUNT gía trị 10000, kế qủa là task 1 được in ra liên tiếp, rồi đến task 2 và ngược lại.


Task 1 is running
Task 1 is running
...
Task 1 is running
Task 1 is running
Task 1 is running
Task 2 is running
Task 2 is running
...
Task 2 is running

Sau đó tăng mainDELAY_LOOP_COUNT lên 1000000 (tùy thuộc vào tốc độ của PC đang chạy chương trình), kết qủa Task 1/2 được in ra luân phiên nhau.

Task 1 is running
Task 2 is running
Task 1 is running
Task 2 is running
Task 2 is running
Task 1 is running

...

Nguyên nhân? Xem hình dưới. Mỗi task chạy một khoảng delta(T) = t2-t1, ứng với thời gian giữa các tick của kernel. mainDELAY_LOOP_COUNT càng nhỏ thì càng nhiều vòng lặp được thực hiện trong khoảng delta(T) này, dẫn đến Task x is running xuất hiện liên tục nhiều hơn.

freertos-task1-task2-same-priority-delay

Nếu cho task 2 có priority cao hơn, ví dụ bằng cách #define mainTASK2_PRIORITY (tskIDLE_PRIORITY + 2). Lúc này task 1 không có cơ hội được thực thi.


Task 2 is running
Task 2 is running
Task 2 is running
Task 2 is running

freertos-task-priorities

Việc delay bằng vòng lặp for dẫn đến các task có mức độ ưu tiên thấp hơn không có điều khiện thực thi. Đều này nên tránh khi viết chương trình.

vTaskDelay, không For

Quay trở lại với delay bằng vTaskDelay. Chương trình cho kết qủa


Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running

một cách tuần tự dù Task 2 có độ ưu tiên cao hơn Task 1. Vì sao?

Lúc này xuất hiện khái niệm Blocked/Ready state.

freertos-tasks

Mỗi task nằm trong một trong các trạng thái

  • Blocked là một tráng thái “Not Running”. Lúc này task đang chờ một trong hai sự kiện: Time hoặc Synchronization. Liên quan đến thời gian như Task X sẽ chuyển đến trạng thái Block trong 100 ms. Synchronization khi nó đang tương tác với các Task khác (semaphore, queue, …) hoặc interrupt.
  • Suspended là một trạng thái “Not Running”. Một Task vào trạng thái này khi được gọi với vTaskSuspend(), và thoát ra với vTaskResume() hoặc xTaskResumeFromISR(). Task với trạng thái này sẽ được được schedule bởi kernel.
  • Ready cũng là một trạng thái “Not Running”. Task này sẵng sàng để chạy, nhưng chưa chuyển sang Running.
  • Running là trạng thái Task được CPU thực thi.

Quay trở lại chương trình, Task 2 với ưu tiên cao hơn, được thực thi trước. Khi nó được chuyển sang trạng thái Blocked với hàm vTaskDelay, Task 1 được kernel gọi thực thi. Sau đó Task 1 cũng được chuyển sang trạng thái Blocked này.

Kernel sang trạng thái idle, và vApplicationIdleHook được gọi. Chương trình có thể đặt cpu sang chế độ ngủ nếu năng lượng là vấn đề của hệ thống.

Sau khoảng thời gian delay, Task 2 chuyển sang trạng thái Running, in ra terminal, rồi lại chuyển sang Blocked. Tương tự như vậy với Task 1.

Xác nhận: Các hình ở trên được lấy từ sách Using The FreeRTOS Realtime Kernel của Richard Barry.

chạy unity trên chip nhúng

Trong khi đọc cuốn Test Driven Development for Embedded C tôi được giới thiệu làm quen với unity. Unity là test framework gọn nhẹ, viết hoàn toàn bằng C; điều này có nghĩa là  nó có thể dịch chạy trên PC và cho cả trên MCU nhúng.

Ngoài việc viết test unit chạy trên PC, tôi cũng muốn các test unit này chạy trực tiếp trên MCU. Tại sao? Tôi nghĩ code sẽ chạy trên MCU, tại sao không test trực tiếp trên đối tượng hoạt động, đặt biệt khi trình một trình biên dịch phổ biến cho hệ thống nhúng cũng có nhiều lỗi.

Để khởi đầu cho hành trình này, tôi sử dụng CC3200 MCU với arm-none-eabi-gcc 4.8.2. Để tiết kiệm thời gian chạy các test tôi load chương trình trực tiếp xuống RAM thông qua JTAG để chạy thay vì flash. Nguyên nhân: quá trình test càng chậm thì tôi càng ít muốn chạy test, điều này không tốt cho quá trình phát triển TDD, khi lời khuyên là chạy test cho mỗi lần commit.

Unity có một vài file, nên liên kết đến nó sẽ khá đơn giản:

UNITY_ROOT=$(PROJECTROOT)/tools/unity

UNITY_DIR = \
$(UNITY_ROOT)/src \
$(UNITY_ROOT)/extras/fixture/src

VPATH += $(UNITY_DIR)
IPATH += $(UNITY_DIR)

Bài test đầu tiên của tôi là kiểm tra flash driver của CC3200. Và khi dịch thì báo lỗi như bên dưới.

/usr/lib/gcc/arm-none-eabi/4.8.2/../../../arm-none-eabi/lib/armv7e-m/libc.a(lib_a-writer.o): In function `_write_r':
/build/buildd/newlib-2.1.0/build/arm-none-eabi/armv7e-m/newlib/libc/reent/../../../../../../newlib/libc/reent/writer.c:58: undefined reference to `_write'
/usr/lib/gcc/arm-none-eabi/4.8.2/../../../arm-none-eabi/lib/armv7e-m/libc.a(lib_a-closer.o): In function `_close_r':
/build/buildd/newlib-2.1.0/build/arm-none-eabi/armv7e-m/newlib/libc/reent/../../../../../../newlib/libc/reent/closer.c:53: undefined reference to `_close'
/usr/lib/gcc/arm-none-eabi/4.8.2/../../../arm-none-eabi/lib/armv7e-m/libc.a(lib_a-fstatr.o): In function `_fstat_r':
/build/buildd/newlib-2.1.0/build/arm-none-eabi/armv7e-m/newlib/libc/reent/../../../../../../newlib/libc/reent/fstatr.c:62: undefined reference to `_fstat'
/usr/lib/gcc/arm-none-eabi/4.8.2/../../../arm-none-eabi/lib/armv7e-m/libc.a(lib_a-isattyr.o): In function `_isatty_r':
/build/buildd/newlib-2.1.0/build/arm-none-eabi/armv7e-m/newlib/libc/reent/../../../../../../newlib/libc/reent/isattyr.c:58: undefined reference to `_isatty'
/usr/lib/gcc/arm-none-eabi/4.8.2/../../../arm-none-eabi/lib/armv7e-m/libc.a(lib_a-lseekr.o): In function `_lseek_r':
/build/buildd/newlib-2.1.0/build/arm-none-eabi/armv7e-m/newlib/libc/reent/../../../../../../newlib/libc/reent/lseekr.c:58: undefined reference to `_lseek'
/usr/lib/gcc/arm-none-eabi/4.8.2/../../../arm-none-eabi/lib/armv7e-m/libc.a(lib_a-readr.o): In function `_read_r':
/build/buildd/newlib-2.1.0/build/arm-none-eabi/armv7e-m/newlib/libc/reent/../../../../../../newlib/libc/reent/readr.c:58: undefined reference to `_read'

 

Nguyên nhân do Unity sử dụng UNITY_OUTPUT_CHAR để in kết quả. Theo mặt định, UNITY_OUTPUT_CHAR liên kết đến putchar() để kết quả hiển thị lên terminal nhờ libc.a khi dịch trên PC. Trong trường hợp CC3200 (và hầu hết các hệ thống nhúng khác), OS nhúng không cung cấp sẵn implement cho hàm này, dẫn đến lỗi khi dịch. Tôi cần chỉ dẫn unity sử dụng hàm khác để in ra cổng UART.

Nhìn vào unity_internals.h, tôi thấy cách để liên kết con trỏ UNITY_OUTPUT_CHAR này mà không thay đổi thư viện unity bẳng lệnh tiền xử lý

#ifdef UNITY_INCLUDE_CONFIG_H
#include "unity_config.h"
#endif

...

#ifndef UNITY_OUTPUT_CHAR
//Default to using putchar, which is defined in stdio.h
#include <stdio.h>
#define UNITY_OUTPUT_CHAR(a) putchar(a)
#else
//If defined as something else, make sure we declare it here so it's ready for use
extern int UNITY_OUTPUT_CHAR(int);
#endif

Vậy thêm cờ ở  Makefile

CFLAGS+=-DUNITY_INCLUDE_CONFIG_H

, đồng thời thêm file unity_config.h

#define UNITY_OUTPUT_CHAR(x) PRINT(x)

PRINT(x) được thực thi ở đâu đó trong chương trình, nhằm đẩy byte x ra cổng UART.

 

Đây là kết quả khiêm tốn cho một số test:

Unity test run 1 of 1
......

-----------------------
6 Tests 0 Failures 0 Ignored

 

Kết luận: Unity là một công cụ nhỏ rất tiện lợi để test chương trình, có thể chạy trên PC hoặc trên MCU nhờ vào việc nó sư dụng C thuần túy và sự gọn nhẹ của thư viện này.

debug custom cc3200 board with launchpad

Kudos for TI that they are supporting GCC and FreeRTOS as first citizens in their Internet on Chip cc3200. TI provides the linker, startup code which run out of box. I can also manage to run GDB on the launchpad also.

Last days I get a couple of custom cc3200 boards. The board has 4 pins for JTAG. My first attempt is to use the JTAG debugging of the launchpad, since I don’t want to go through flashing step. With make debug, the program is loaded to RAM, and execute from here; and debugging with GDB is so valuable compare to, say, printf.

The hardware connection is deadly easy. Launchpad give 4 JTAG pin rough jumper J6-J9, in the power side; connect them direct to MCU JTAG pin. Done.

This is the test, running freertos-demo example.

cc3200_jtag

Pic: Connect a custom board to Launchpad with serial and jtag.

30 buck for the launchpad, with JTAG, a dev board, is a good spending isn’t it.

cc3200 paho client breaks freertos task scheduling

Paho MQTT client already provides an adaptation layer for CC3200 at MQTTClient-C/src/cc3200. In basic, the embedded-C Paho requires 2 resources from the device: timer and socket.

cc3200

The CC3200 porting runs out of the box; however, when you run with FreeRTOS, the task scheduling of the OS may broken.

Why? Look at MQTTCC3200.c, we find that the timer is got from Systick interrupt handling, but Systick is occupied already for FreeRTOS scheduling. It seems that most of Cortex M3/M4 FreeRTOS use Systick for kernel timing. Good practice is to avoid the use of it in app layer.

Work around? Need timer? Get from OS kernel itself, which retrieve from Systick,  at xTaskGetTickCount().