标题:
分享个自己写的自定义串口协议 附单片机代码
[打印本页]
作者:
范莉萍
时间:
2024-6-12 11:07
标题:
分享个自己写的自定义串口协议 附单片机代码
最近在写一个通过手控器控制设备动作的程序,手控器按键较多,所以通过串口来和外设设备进行通信,由于命令较多,所以做了个小协议以后也能用到,可做主机可做从机,适合近距离单机通讯,第二位功能码可做地址组成多机通讯,但最好加上校验位。新手入门,大佬请多多指教
附上代码
//自定义串口发送协议 例:68 01 00 03 FF ,除去包头包尾,中间为 功能码,数据1,数据2
// 发送数据包函数 功能码,第一位数据,第二位数据
void SendDataPacket(uint8_t function, uint8_t data1, uint8_t data2) {
// 发送包头
USART1_SendData(PACKET_HEADER);
// 发送功能码
USART1_SendData(function);
// 发送数据1和数据2
USART1_SendData(data1);
USART1_SendData(data2);
// 发送包尾
USART1_SendData(PACKET_TAILER);
}
void Anlsy_Data(uint8_t function, uint8_t data1, uint8_t data2)//所有按键接收解析在这里写
{
if(function == KEY_STAUS)
{
SysChenyuan.mark = QX_MARK;
switch(data2)
{
case KEY1:
SysChenyuan.Uart_Comd = QX1;
printf("清洗1按键按下,data2 = %02d,function = %02d", data2, function);
break;
case KEY2:
SysChenyuan.Uart_Comd = QX2;
printf("清洗2按键按下,data2 = %02d,function = %02d", data2, function);
break;
case KEY3:
SysChenyuan.Uart_Comd = QX3;
printf("清洗3按键按下,data2 = %02d,function = %02d", data2, function);
break;
case KEY_STOP:
SysChenyuan.all_stopped = YES;//停止标志
printf("停止按键按下,data2 = %02d,function = %02d", data2, function);
break;
case KEY_CHONGSHUI:
SysChenyuan.QiTa = CHONGSHUI;//冲水标志
printf("冲水按键按下,data2 = %02d,function = %02d", data2, function);
break;
case KEY_HONGGAN:
SysChenyuan.QiTa = HONGGAN;//烘干标志
printf("烘干按键按下,data2 = %02d,function = %02d", data2, function);
break;
default:
// 处理未知按键情况
break;
}
}
}
extern uint8_t Serial_Rx3Flag,time_out3,time_out3_flag;
// 解析数据包的函数
int ParseDataPacket(void) {
static uint8_t expectingHeader = 1; // 标记是否正在等待包头
static uint8_t packetIndex = 0, i = 0; // 当前数据包内的索引
uint8_t function, data1, data2;
if(Serial_Rx3Flag == 1)
{
time_out3 = 0;
time_out3_flag = 0;
// 遍历缓冲区中的所有数据(在实际情况中,你可能只处理新接收到的数据)
for ( i = 0; i < rxIndex; ++i) {
if (expectingHeader) {
if (rxBuffer[i] == PACKET_HEADER) {
expectingHeader = 0; // 找到了包头,开始解析数据包
packetIndex = 0; // 重置数据包索引
}
// 如果不是包头,则忽略该字节或进行错误处理
} else {
switch (packetIndex) {
case 0: // 功能码
function = rxBuffer[i];
break;
case 1: // 数据1
data1 = rxBuffer[i];
break;
case 2: // 数据2
data2 = rxBuffer[i];
// 检查包尾
if (rxBuffer[i+1] == PACKET_TAILER) {
// 完整的数据包已接收,可以处理数据
Anlsy_Data(function, data1, data2);
// processPacket(function, data1, data2);
// 重置状态以接收下一个数据包
expectingHeader = 1;
packetIndex = 0;
rxIndex = 0; // 清除缓冲区(或者只清除已处理的部分)
} else {
// 没有找到包尾,可能是数据损坏或丢失,进行错误处理
// TODO: 错误处理代码
expectingHeader = 1; // 重置状态以接收下一个数据包
packetIndex = 0;
}
break;
default:
// 无效的数据包索引,进行错误处理
expectingHeader = 1; // 重置状态以接收下一个数据包
packetIndex = 0;
break;
}
if (packetIndex < 3) { // 还未到达包尾,递增索引
packetIndex++;
}
}
}
rxIndex = 0;// 清除已处理的数据(如果rxIndex不是自动管理的)
Serial_Rx3Flag = 0;
}
return 0; // 返回值可以根据需要来定义,例如表示是否成功解析了一个数据包
}
//下面是串口超时代码,我放在了10ms定时器里,看需要。
// 定时器回调函数 10ms
void vTimerCallback( TimerHandle_t xTimer )
{
if(time_out3 < 10 && time_out3_flag == 1) {
time_out3++;
}
// 检查是否超时
if(time_out3 >= 10) {
time_out3 = 0;
time_out3_flag = 0;
// 执行超时处理,例如发送确认信息或者进行数据处理
Serial_Rx3Flag = 1;
}
}
//串口中断
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET) //RXNE 标志位为1 表示可以接收数据
{
uint8_t ucReceivedChar;
// 读取接收到的字符
ucReceivedChar = USART_ReceiveData(USART1);
if (rxIndex < RX_BUFFER_SIZE) {
rxBuffer[rxIndex++] = ucReceivedChar;
time_out3 = 0;
time_out3_flag = 1;
}
// if(Serial_Rx3Flag == 1)
// {
// rxIndex = 0;
// }
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除RXNE标志位
}
}
复制代码
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1