小白爱上51 发表于 2024-6-13 08:36:50

分享个自己写的自定义串口协议

最近在写一个通过手控器控制设备动作的程序,手控器按键较多,所以通过串口来和外设设备进行通信,由于命令较多,所以做了个小协议以后也能用到,可做主机可做从机,适合近距离单机通讯,第二位功能码可做地址组成多机通讯,但最好加上校验位。新手入门,大佬请多多指教
附上代码

[*]//自定义串口发送协议 例: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 == PACKET_HEADER) {
[*]                                    expectingHeader = 0; // 找到了包头,开始解析数据包
[*]                                    packetIndex = 0; // 重置数据包索引
[*]                            }
[*]                            // 如果不是包头,则忽略该字节或进行错误处理
[*]                  } else {
[*]                            switch (packetIndex) {
[*]                                    case 0: // 功能码
[*]                                          function = rxBuffer;
[*]                                          break;
[*]                                    case 1: // 数据1
[*]                                          data1 = rxBuffer;
[*]                                          break;
[*]                                    case 2: // 数据2
[*]                                          data2 = rxBuffer;
[*]
[*]                                          // 检查包尾
[*]                                          if (rxBuffer == 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 = ucReceivedChar;
[*]                  time_out3 = 0;
[*]                  time_out3_flag = 1;
[*]                }
[*]//                if(Serial_Rx3Flag == 1)
[*]//                {
[*]//                  rxIndex= 0;
[*]//                }
[*]      USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除RXNE标志位
[*]    }
[*]}


神农鼎 发表于 2024-6-13 08:42:15

帮忙再加上 DMA 的演示,可参考下面的程序

4组串口UART使用DMA收发 @STC32G,易用,高效,稳定 !精品实战代码 - DMA: 支持4组串口,3组SPI,I2C,TFT-i8080/M6800,ADC 国芯技术交流网站 - STC全球32位8051爱好者互助交流社区 (stcaimcu.com)



深圳国芯人工智能有限公司-产品_STC32系列 (stcai.com)

页: [1]
查看完整版本: 分享个自己写的自定义串口协议