Làm thế nào để áp dụng RTOS vào dự án hiện tại ?? Lâu lắm mới có quay trở lại mặt đất, sau bài thần gió về lý thuyết RTOS cơ bản chắc các bạn cũng biết được sơ sơ về RTOS nó có gì, ứng dụng nó ra làm sao, nói chung là toàn gió, đọc xong rồi thôi, chẳng biết tạo 1 project thực tế thì nó như thế nào, mấy cái hướng dẫn thì ba vớ linh tinh toàn hướng dẫn blink LED xong hết. Nay mình sẽ chia sẻ 1 chút nho nhỏ về ứng dụng cụ thể RTOS các bạn làm rõ vấn đề đó
Giới thiệu
Mình vào vấn đề chính luôn, trong bài viết này sẽ gồm có 2 phần, 1 phần là dự án chưa dùng RTOS và cái sau đó là chuyển cái non-RTOS này sang RTOS, nếu bình thường thì chỉ 2 task là blink LED 1 và blink LED 2 là xong rồi, đây thì phức tạp hơn 1 xíu xiu, mình sẽ đọc gyroscope và hiển thị giá trị đó qua cổng USB, khi nghiêng ngả kit về các phía sẽ có LED sáng theo để dễ quan sát
Trong bài viết mình sẽ không nói cụ thể vào cách tạo project như thế nào nhưng sẽ có các lưu ý về cách tận dụng các nguồn sẵn có từ ST cũng như các khác biệt sau khi bạn gen code từ cubemx
Chuẩn bị
Phần cứng
- STM32F407 Discovery (hàng có sẵn nên mình dùng)
- 2 sợi cáp USB mini và micro cho ST-Link và USB debug (2 cổng usb có sẵn trên kit)
Phần mềm
- CubeMX phiên bản mới nhất (nhiều bug) và thư viện cubemx cho STM32F4
- Keil C v5 (khuyến khích dùng bản Lite hạn chế dùng lậu)
- Teraterm
- Virtual COM port
Project
- Các bạn có thể clone project về từ
https://github.com/hocarm/FreeRTOS-STM32F4-Tutorial.git
Non-RTOS
Lời hay không bằng cho ngay cái hình, thôi các bạn nhìn hình bên dưới để biết nội dung chính mình cần làm nhé
Nhìn vào hình thì chúng ta có thể biết được việc cần làm là đọc dữ liệu từ gyro là L3GD20, con này có sẵn trên kit F4 thông qua SPI, sau đó xuất giá trị đọc được này lên máy tính thông qua công USB VCOM (USB CDC), ngoài ra có phần GPIO để kết nối với 4 LED sẵn có, cứ nghiêng bên nào thì LED bên đó sáng
Hình minh họa về gyro trên kit và cổng USB các bạn có thể xem tham khảo
Flowchart
Flowchart của chúng ta sẽ đơn giản như sau
- Khởi tạo USB
- Khởi tạo gyroscope
- Lấy dữ liệu thô từ gyroscope
- Gửi dữ liệu này thông qua USB host
- Xử lý dữ liệu từ gyroscope và blink với LED tương ứng
Nếu bạn muốn coi thẳng luôn trong code có gì thì có thể mở project keil MDK trong thư mục F4-Gyro lên build và nạp xuống để xem kết quả.
Nếu muốn tăng độ khó của game lên thì bạn có thể tự tạo ra một project ban đầu với các lưu ý như sau.
CubeMX
- Tạo project với các thiết lập mặc định của F407 Discovery (Initialize all peripherals with their default Mode)
- Chọn
USB_OTG_FS Mode
làDevice Only
vàUSB_DEVICE
làCommunication Device Class(Virtual Port Com)
- Các phần còn lại cứ để mặc định không sao cả, sau đó gen code
ARM Keil C
- Chỉnh lại thư viện USB-CDC, chỗ này sau khi gen code từ cubemx có thể đã có bug làm máy tính hiện ra driver có dấu chấm than, để hết lỗi bạn có thể vào usbd_cdc.h, chỉnh lại thành1
#define CDC_DATA_HS_MAX_PACKET_SIZE 64
- Add thêm Driver BSP của STM32F4 để có thể read được data từ gyro, chúng ta cần add 4 file vào project là
lis302dl.c, lis3dsh.c, stm32f4_discovery.c
vàstm32f4_discovery_accelerometer.c
, driver này bạn có thể tìm được ởC:\Users\XXX\STM32Cube\Repository\STM32Cube_FW_F4_V1.21.0\Drivers\BSP
- Ở chương trình chính (file main.c) thì trong vòng lặp vô tận chúng ta chỉ làm 1 việc duy nhất là đọc data, delay, gửi data qua usb, kiểm tra giá trị data và blink LED.
Tương ứng với flowchart ban đầu sẽ có 1 số hàm tương ứng tượng trưng như sau
- Khởi tạo USB (MX_USB_DEVICE_Init)
- Khởi tạo gyroscope (BSP_ACCELERO_Init)
- Lấy dữ liệu thô từ gyroscope (BSP_ACCELERO_GetXYZ)
- Gửi dữ liệu này thông qua USB host(Send_Data_to_USB)
- Xử lý dữ liệu từ gyroscope và blink với LED tương ứng (BSP_LED_On/BSP_LED_Off)
Lưu ý: Với các đoạn code trong cubemx thì các bạn nên giữ nguyên cấu trúc của code mà cubemx sinh ra, không nên xóa các comment có sẵn trong code nếu bạn còn muốn sử dụng lại code cũ và add thêm các config mới, thường code của chúng ta sẽ nằm giữa đoạn
/* USER CODE BEGIN xx */
và/* USER CODE END xx */
Kết quả
Sau khi nạp chương trình xuống thì ta cắm usb ở cổng micro vào máy tính, kiểm tra driver ok chưa ? máy tính có nhận không ? Nếu có nghĩa là phần Init USB đã thành công.
Kết nối với teraterm và config như hình bên dưới(Setup -> Terminal chọn Receive là LF) để có thể xem được dữ liệu, tốc độ baudrate của các bạn có thể là 9600 hoặc 115200 đều được nhé, USB CDC sẽ tự config baud tương ứng với baud của bạn
Thực hiện di chuyển board để có thể quan sát được dữ liệu cảm biến thay đổi
RTOS
Sau khi đã code được cơ bản yêu cầu thì chúng ta sẽ chuyển project ở trên từ nonRTOS sang RTOS. Khi đó ứng dụng sẽ được tổ chức thành 2 task
- Task 01 làm nhiệm vụ kết nối cổng USB, gửi dữ liệu nhận được từ gyroscope sang USB host
- Task 02 thì sẽ đọc dữ liệu từ gyroscope và blink LED tương ứng với dữ liệu từ gyroscope này
Flowchart
Ta sẽ có hình ảnh so sánh về flow của việc không dùng và dùng RTOS
Cụ thể thì các task sẽ hoạt động như thế nào
Task 01 (USB)
- Sau khi init ngoại vi của USB thì sẽ đợi USB host cấu hình virtual comport trước khi thực thi, nghĩa là STM32F4 cấu hình xong USB thì PC sẽ cấu hình comport ảo, kết thúc sẽ là kiểm tra hardware ta thấy nhận cổng COM, nếu lỗi thì sẽ không nhận COM hoặc có dấu chấm than vàng
- Gửi 1 Signal tới Task 2 để thực thi
- Lấy dữ liệu của gyroscope từ mail queue và gửi nó ra USB host
- Tiếp tục lặp lại quá trình trên liên tục
Task 02 (Gyroscope và LED)
- Sau khi init gyroscrope thì task 02 sẽ đợi task 01 gửi signal tới trước khi thực thi
- Lấy data của gyroscope và gửi tới Task 01 sử dụng mail queue
- Blink LED dựa trên data của gyroscrope
Với chương trình của RTOS này các bạn có thể tham khảo project mình đã tạo sẵn ở thư mục Gyro-RTOS
CubeMX
Nếu bạn vẫn thích em yêu khoa học như project non RTOS trên thì chúng ta copy file CubeMX đã tạo ở non-RTOS ra một chỗ khác, sau đó cấu hình thêm như sau
- Enable RTOS bằng cách chọn FreeRTOS -> Enable
- Đổi time base mặc định của source từ Systick sang TIM6 bằng cách SYS-> Timebase Source -> TIM6 vì FreeRTOS sẽ dùng Systick nên để tránh xung đột thì ta cho nó sang time base khác là TIM6
- Cấu hình tab FreeRTOS
- Thay đổi task mặc định thành task với các thông số Task01 – osPriorityNormal – 256 – StartTask01
- Thêm một task thứ 2 với thông số Task02 – osPriorityNormal – 128 – StartTask02
- Lưu lại và gen config
ARM Keil C
Gen code xong chúng ta phải chú ý điều chỉnh thêm ở project của Keil C thêm ít nữa
- Add thư viện BSP vào như ở trên nonRTOS
- Sửa bug của thư viện USB
Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.
#define CDC_DATA_HS_MAX_PACKET_SIZE 64
- Điều chỉnh lại code trong
main.c
- Define mail queue với hàm osMailQId.
- Tạo và cấp phát bộ nhớ cho mail queue với hàm
osMailQDef()
vàosMailCreate()
- Thêm 2 hàm USB_Task tương ứng task 01 và
GYRO_Task()
là task 02 - Chỉnh lai code
usbd_cdc_if.c
để set signal bằng hàmosSignalSet()
trong caseCDC_SET_LINE_CODING
, cần thêm#include "cmsis_os.h"
Bạn có thể xem các API name có thể dùng tại file cmisis_rtos.c
Kết quả
Các bạn nạp code xuống sau đó thực hiện các bước test lại như project non-RTOS và quan sát kết quả
Tạm kết
Vậy là mình đã hướng dẫn xong sơ bộ về việc code một project để đọc dữ liệu cảm biến và hiển thị giá trị đó lên PC thông qua giao tiếp USB và chuyển project này về FreeRTOS, mình nghĩ các bạn có thể đọc chút qua về lý thuyết, xem qua code của mình làm và tự thực hành việc code nonRTOS và chuyển sang RTOS, sau đó có thể áp dụng được vào cho chính dự án của các bạn. Tất nhiên trong quá trình làm sẽ có nhiều lỗi xảy ra, nếu gặp lỗi về USB bạn có thể tham khảo video về USB-CDC này để test thử (lưu ý là nhớ sửa lại code về 64 khi gen xong CubeMX nhé), ngoài ra nếu bạn còn câu hỏi nào khác có thể hỏi bên dưới bài viết, mình sẽ cố gắng hỗ trợ các bạn hết mức có thể.