《Arduino 手册(思路与案例)》栏目介绍:
在电子制作与智能控制的应用领域,本栏目涵盖了丰富的内容,包括但不限于以下主题:Arduino BLDC、Arduino CNC、Arduino E-Ink、Arduino ESP32 SPP、Arduino FreeRTOS、Arduino FOC、Arduino GRBL、Arduino HTTP、Arduino HUB75、Arduino IoT Cloud、Arduino JSON、Arduino LCD、Arduino OLED、Arduino LVGL、Arduino PID、Arduino TFT,以及Arduino智能家居、智慧交通、月球基地、智慧校园和智慧农业等多个方面与领域。不仅探讨了这些技术的基础知识和应用领域,还提供了众多具体的参考案例,帮助读者更好地理解和运用Arduino平台进行创新项目。目前,本栏目已有近4000篇相关博客,旨在为广大电子爱好者和开发者提供全面的学习资源与实践指导。通过这些丰富的案例和思路,读者可以获取灵感,推动自己的创作与开发进程。
https://blog.csdn.net/weixin_41659040/category_12422453.html
Arduino RTOS 之 UART 命令解析与执行
-
主要特点
1.1 实时性
• Arduino RTOS(实时操作系统)能够确保在特定时间内完成任务,UART命令解析在接收到数据后能够迅速响应,提高系统的实时性。
1.2 多任务处理
• RTOS支持多任务并发执行,UART命令解析可以在独立的任务中进行,从而不影响其他任务的执行。
1.3 资源管理
• RTOS提供了任务调度、内存管理、信号量等机制,帮助高效管理UART资源,避免数据丢失和冲突。
1.4 易于扩展
• 基于RTOS的架构便于后续功能拓展,例如添加更多的UART通道或其他通信协议。 -
应用场景
2.1 嵌入式系统通信
• 在嵌入式设备中,UART常用于模块间的通信,如传感器、执行器与主控制器之间的数据交换。
2.2 机器人控制
• 在机器人系统中,UART可用于接收远程控制指令,实时解析并执行,从而实现对机器人的精准控制。
2.3 数据采集
• 在数据采集系统中,通过UART接收传感器数据,实时解析并存储或处理,适合用于监控和自动化系统。
2.4 物联网设备
• 在物联网应用中,UART用于设备间的通信,结合RTOS可以实现更复杂的控制逻辑和数据处理。 -
注意事项
3.1 数据完整性
• 在解析UART命令时,应考虑数据的完整性和有效性,确保不会因数据错误导致系统异常。
3.2 任务优先级
• 在RTOS中,合理设置UART命令解析任务的优先级,确保其在高负载情况下仍能及时响应。
3.3 中断管理
• UART通信通常依赖中断机制,需合理处理中断,以避免丢失数据或造成冲突。
3.4 资源竞争
• 多任务环境下,需注意资源竞争问题,使用信号量或互斥锁等机制保证线程安全。
3.5 电源管理
• 在低功耗应用中,应考虑UART通信对电源的影响,合理设计休眠与唤醒策略,以延长设备的使用寿命。
1、基础 UART 命令解析
#include <Arduino.h>
#include <FreeRTOS.h>
void setup() {
Serial.begin(9600);
xTaskCreate(task_uart, "UART Task", 1000, NULL, 1, NULL);
}
void loop() {
// 空循环
}
void task_uart(void *pvParameters) {
char command[50];
while (1) {
if (Serial.available()) {
int len = Serial.readBytesUntil('\n', command, sizeof(command) - 1);
command[len] = '\0'; // 确保字符串以 null 结尾
Serial.println(command); // 回显命令
// 解析和执行命令
}
vTaskDelay(100);
}
}
- LED 控制命令
#include <Arduino.h>
#include <FreeRTOS.h>
#define LED_PIN 13
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(9600);
xTaskCreate(task_uart, "UART Task", 1000, NULL, 1, NULL);
}
void loop() {
// 空循环
}
void task_uart(void *pvParameters) {
char command[50];
while (1) {
if (Serial.available()) {
int len = Serial.readBytesUntil('\n', command, sizeof(command) - 1);
command[len] = '\0';
if (strcmp(command, "ON") == 0) {
digitalWrite(LED_PIN, HIGH);
Serial.println("LED ON");
} else if (strcmp(command, "OFF") == 0) {
digitalWrite(LED_PIN, LOW);
Serial.println("LED OFF");
}
}
vTaskDelay(100);
}
}
- 温度读取命令
#include <Arduino.h>
#include <FreeRTOS.h>
void setup() {
Serial.begin(9600);
xTaskCreate(task_uart, "UART Task", 1000, NULL, 1, NULL);
}
void loop() {
// 空循环
}
void task_uart(void *pvParameters) {
char command[50];
while (1) {
if (Serial.available()) {
int len = Serial.readBytesUntil('\n', command, sizeof(command) - 1);
command[len] = '\0';
if (strcmp(command, "TEMP") == 0) {
float temperature = readTemperature(); // 假设有一个读取温度的函数
Serial.print("Temperature: ");
Serial.println(temperature);
}
}
vTaskDelay(100);
}
}
float readTemperature() {
// 模拟读取温度,实际情况取决于传感器
return 25.0;
}
要点解读
RTOS 任务创建:
使用 xTaskCreate 创建一个任务来处理 UART 通信,使得主循环可以保持空闲,避免阻塞。
命令接收与解析:
使用 Serial.readBytesUntil 方法接收命令,确保能够处理多行输入,并通过 \n 确定命令结束。
命令执行:
通过 strcmp 比较接收到的命令,执行相应的操作(例如,控制 LED 或读取温度)。
非阻塞的延时:
使用 vTaskDelay 进行非阻塞延时,避免占用 CPU 资源,确保其他任务可以运行。
可扩展性:
该结构可轻松扩展,通过增加更多的 if 语句或使用状态机来处理复杂的命令解析和执行逻辑。
4、基础命令解析与LED控制(基于FreeRTOS+CLI框架)
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
#include <queue.h>
#include <stdio.h>
// FreeRTOS CLI定义
#define cmdMAX_INPUT_SIZE 50
static char pcInputString[cmdMAX_INPUT_SIZE];
static int xInputIndex = 0;
static SemaphoreHandle_t xSerialSemaphore = NULL;
// 命令处理函数
static BaseType_t prvLEDCommand(int8_t *pcWriteBuffer, size_t xWriteBufferLen, const int8_t *pcCommandString) {
const char *pcParam = (const char *)pcCommandString;
if (strstr(pcParam, "on") != NULL) {
digitalWrite(LED_BUILTIN, HIGH);
snprintf(pcWriteBuffer, xWriteBufferLen, "LED已开启\r\n");
} else if (strstr(pcParam, "off") != NULL) {
digitalWrite(LED_BUILTIN, LOW);
snprintf(pcWriteBuffer, xWriteBufferLen, "LED已关闭\r\n");
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "无效命令,用法: LED <on|off>\r\n");
}
return pdFALSE; // 继续执行其他命令
}
// 注册命令到CLI
void vRegisterCLICommands() {
static const CLI_Command_Definition_t xLEDCommand = {
"LED",
"LED <on|off>: 控制板载LED",
prvLEDCommand,
0
};
FreeRTOS_CLIRegisterCommand(&xLEDCommand);
}
// UART接收任务
void vTaskUARTReceive(void *pvParameters) {
char cRxedChar;
while (1) {
if (Serial.available() > 0) {
cRxedChar = Serial.read();
if (xSerialSemaphore != NULL) {
if (cRxedChar == '\r' || cRxedChar == '\n') {
pcInputString[xInputIndex] = '\0'; // 终止字符串
FreeRTOS_CLIProcessCommand(pcInputString, NULL, 0);
xInputIndex = 0; // 重置索引
} else if (xInputIndex < cmdMAX_INPUT_SIZE - 1) {
pcInputString[xInputIndex++] = cRxedChar;
}
}
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
xSerialSemaphore = xSemaphoreCreateMutex();
if (xSerialSemaphore != NULL) {
xSemaphoreGive(xSerialSemaphore);
}
vRegisterCLICommands();
xTaskCreate(vTaskUARTReceive, "UART_RX", 256, NULL, 1, NULL);
}
void loop() {}
要点解读:
CLI框架集成:通过FreeRTOS_CLIRegisterCommand注册命令,将用户输入的字符串映射到处理函数(如prvLEDCommand),实现命令解析与执行分离。
任务同步机制:使用互斥锁(xSerialSemaphore)保护共享资源(UART缓冲区),避免多任务竞争导致数据错乱。
命令行结束符处理:检测\r或\n作为命令结束标志,触发CLI处理函数,符合终端输入习惯。
参数解析:在命令处理函数中通过strstr解析参数(如on/off),实现灵活控制。
实时响应:UART接收任务以高优先级运行,确保命令输入后立即处理,适合交互式场景。
5、传感器数据采集与转发(UART+DMA+IDLE中断)
#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <HardwareSerial.h>
#define BUFFER_SIZE 256
QueueHandle_t xSensorQueue;
uint8_t ucDMABuffer[BUFFER_SIZE];
volatile bool bDataReady = false;
// DMA接收完成回调函数
void vUART_DMA_Callback() {
bDataReady = true;
}
// 传感器数据处理任务
void vTaskProcessSensor(void *pvParameters) {
uint8_t ucData[BUFFER_SIZE];
while (1) {
if (bDataReady) {
memcpy(ucData, ucDMABuffer, BUFFER_SIZE);
// 解析传感器数据(示例:假设数据格式为"TEMP:25.5\r\n")
char *pcTemp = strstr((char *)ucData, "TEMP:");
if (pcTemp != NULL) {
float fTemperature = atof(pcTemp + 5);
xQueueSend(xSensorQueue, &fTemperature, portMAX_DELAY);
}
bDataReady = false;
// 重启DMA接收
Serial1.readBytes(ucDMABuffer, BUFFER_SIZE); // 简化示例,实际需配置DMA
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
// 数据转发任务
void vTaskForwardData(void *pvParameters) {
float fTemperature;
while (1) {
if (xQueueReceive(xSensorQueue, &fTemperature, portMAX_DELAY) == pdPASS) {
Serial.print("转发温度: ");
Serial.println(fTemperature, 1);
}
}
}
void setup() {
Serial.begin(115200);
Serial1.begin(9600, SERIAL_8N1, RXD1_PIN, TXD1_PIN); // 配置UART1引脚
xSensorQueue = xQueueCreate(10, sizeof(float));
xTaskCreate(vTaskProcessSensor, "SensorProcess", 512, NULL, 2, NULL);
xTaskCreate(vTaskForwardData, "DataForward", 256, NULL, 1, NULL);
// 配置DMA和IDLE中断(实际需根据硬件平台调整)
Serial1.onReceive(vUART_DMA_Callback);
}
void loop() {}
要点解读:
DMA加速数据接收:通过DMA将UART数据直接存入内存缓冲区,减少CPU占用,适合高速传感器数据采集。
IDLE中断检测帧结束:利用串口空闲中断(IDLE)判断数据帧完成,避免固定长度接收导致的粘包问题。
队列任务通信:使用FreeRTOS队列(xSensorQueue)在任务间传递解析后的数据,实现解耦和异步处理。
多UART协同:UART0用于调试输出,UART1用于传感器通信,体现RTOS多任务资源管理能力。
数据格式解析:在任务中解析字符串格式的传感器数据(如TEMP:25.5),转换为浮点数供后续使用。
6、无线模块AT指令交互(UART+状态机)
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
#include <string.h>
#define AT_CMD_TIMEOUT 2000 / portTICK_PERIOD_MS
SemaphoreHandle_t xATMutex = NULL;
enum AT_State { IDLE, SENDING, WAITING_RESPONSE };
// AT指令发送任务
void vTaskSendATCommand(void *pvParameters) {
const char *pcATCmd = "AT+CSQ\r\n"; // 查询信号质量
while (1) {
if (xSemaphoreTake(xATMutex, AT_CMD_TIMEOUT) == pdTRUE) {
Serial2.print(pcATCmd); // UART2连接无线模块
xSemaphoreGive(xATMutex);
}
vTaskDelay(5000 / portTICK_PERIOD_MS); // 每5秒查询一次
}
}
// 响应处理任务
void vTaskProcessResponse(void *pvParameters) {
char cRxChar;
static String sResponse;
enum AT_State eState = IDLE;
while (1) {
switch (eState) {
case IDLE:
if (Serial2.available() > 0) {
cRxChar = Serial2.read();
if (cRxChar == '\r' || cRxChar == '\n') {
if (sResponse.length() > 0) {
eState = WAITING_RESPONSE;
}
} else {
sResponse += cRxChar;
}
}
break;
case WAITING_RESPONSE:
if (sResponse.startsWith("+CSQ:")) { // 解析信号质量
int iRSSI = 0, iBER = 0;
sscanf(sResponse.c_str(), "+CSQ: %d,%d", &iRSSI, &iBER);
Serial.print("信号强度: ");
Serial.print(iRSSI);
Serial.println(" dBm");
}
sResponse = "";
eState = IDLE;
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
Serial2.begin(115200, SERIAL_8N1, RXD2_PIN, TXD2_PIN); // 配置UART2引脚
xATMutex = xSemaphoreCreateMutex();
if (xATMutex != NULL) {
xSemaphoreGive(xATMutex);
}
xTaskCreate(vTaskSendATCommand, "AT_Sender", 256, NULL, 2, NULL);
xTaskCreate(vTaskProcessResponse, "AT_Parser", 512, NULL, 1, NULL);
}
void loop() {}
要点解读:
状态机设计:通过枚举类型(AT_State)管理AT指令交互流程(空闲→发送→等待响应),避免复杂条件判断。
互斥锁保护:使用xATMutex确保AT指令发送和响应处理不会因并发访问UART导致数据错乱。
异步响应处理:响应处理任务独立运行,通过状态机逐字符解析模块返回的数据,支持流式处理。
超时机制:在发送任务中通过xSemaphoreTake的超时参数防止死锁,增强系统健壮性。
协议适配性:通过修改pcATCmd和响应解析逻辑(如sscanf),可快速适配不同无线模块的AT指令集。
注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。