一个简单的基础通信协议的设计与实现
不同设备之间的通信,都需要设计自己的通信协议。为了保证设备与设备之间的数据的稳定传输,通信协议的设计需要考虑很多的问题。当然应对不同的应用场景,可以有针对性的设计不同的通信协议。
一种常见的通信协议格式
这是一种我们比较常见的通信协议格式
帧头 | 地址位 | 功能位 | 帧序号 | 数据长度 | 数据内容 | 校验位 | 帧尾 |
---|---|---|---|---|---|---|---|
1/2字节 | 1字节 | 1字节 | 2字节 | 2字节 | n字节 | 1/2字节 | 1/2字节 |
而为了应对不同的情况,可以依照情况做删改,例如减少帧头和帧尾,减少帧序号等等。
而本篇实现的通信协议如下,这里将几个部分都做了,实际中可能并不需要这么冗余的帧,可以按需求适当删改:
地址位 | 功能位 | 帧序号 | 数据长度 | 数据内容 | 校验位 |
---|---|---|---|---|---|
1字节 | 1字节 | 1字节 | 1字节 | n字节 | 1字节 |
本篇例程使用的开发板是STM32F103VET6,应用工具是MDK-ARM v5.33,STM32CubeMX V6.1.1
注:STM32CubeMX需要安装JAVA环境(JRE)。
搭建串口收发环境
参考:https://blog.csdn.net/u014470361/article/details/79206352#comments
使用串口1,DMA方式收发数据
注:DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
配置STM32CubeMX
打开STM32CubeMX,File->New Project->Start Project
RCC->打开外部时钟
USART1->Asynchronous 异步通信
下面NVIC Settings->Enabled 使能串口中断
还是下面DMA Setthing->ADD->USART1_RX/USART_TX->Priority 使能DMA收发模式,高优先级
SYS->Dubug-Serial Wire 启用调试引脚,因为我使用ST-Link进行调试,不使能调试引脚的话没法调试。
上面的Clock Configuration时钟配置可以忽略,使用默认8MHz即可,然后是第三个选项Project Manager->Project Name设置工程名->Project Location设置工程路径,然后选择IDE->MDK-ARM
注意工程名和路径都不要出现中文字符
最后点击GENERATE CODE生成工程文件,如果失败的话,可以尝试更换JAVA环境。
添加USART部分代码
在main.h宏定义一个最大接收字节数1024
#define UART_RX_LEN 1024
打开工程,并在main.c中添加部分代码
定义接收数组,接收数据长度以及标识。UART_RX_STA的0-14位存储数据长度,第15位表示接收状态。
/* USER CODE BEGIN PV */
uint8_t UART_RX_BUF[UART_RX_LEN];
__IO uint16_t UART_RX_STA;
/* USER CODE END PV */
注意位置DMA初始化需在MX_USART1_UART_Init();
串口初始化之后。
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart1, UART_RX_BUF, UART_RX_LEN); // 启动DMA接收
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能空闲中断
/* USER CODE END 2 */
在while循环中添加DMA发送指令,将接收到的数据发送回去
if(UART_RX_STA & 0X8000)
{
HAL_UART_Transmit_DMA(&huart1, UART_RX_BUF, UART_RX_STA & 0X7FFF); // 将接收到的数据发送回去
UART_RX_STA = 0;
}
/* USER CODE END WHILE */
打开stm32f1xx_it.c文件添加代码
/* USER CODE BEGIN PD */
extern uint8_t UART_RX_BUF[UART_RX_LEN];
extern __IO uint16_t UART_RX_STA;
/* USER CODE END PD */
拉到底,找到USART1中断。修改如下
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET&&(UART_RX_STA&0x8000==0)) // 空闲中断标记被置位,并且拆包完成
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1); // 清楚中断标记
HAL_UART_DMAStop(&huart1); // 停止DMA接收
UART_RX_STA = UART_RX_LEN - __HAL_DMA_GET_COUNTER(huart1.hdmarx); // 总数据量减去未接收到的数据量为已经接收到的数据量
UART_RX_BUF[UART_RX_STA] = 0; // 添加结束符
UART_RX_STA |= 0X8000; // 标记接收结束
HAL_UART_Receive_DMA(&huart1