今日尝试配通STM32 F103 ZET6与HLK-LD303-24G测距雷达的串口通信解码
文章提供测试代码......
目录
HLK-LD303-24G测距雷达外观:
线路连接准备:
定时器与串口配置准备:
定时器2的初始化:
串口1、2初始化:
串口1、2自定义打印printf()函数的编写:
串口通信协议解码与校验配置:
首先了解一下它的通信协议:
编辑
定义数据接收的结构体:
数据处理函数:
简易状态机接收检验函数:
测试效果:
HLK-LD303-24G测距雷达外观:
线路连接准备:
我选择使用串口2进行与测距雷达的通信,串口1留着连接电脑进行测试:
定时器与串口配置准备:
先建立一个基本工程:初始化定时器2为1ms溢出一次,并初始化串口1和串口2:
定时器2的初始化:
#include "TIMER_init.h"//初始化定时器2用作计时中断定时器:
void Timer2_Init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_InternalClockConfig(TIM2);//选择哪个中断就写哪个TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //修改分频,对实际情况影响不大,可以不修改,这里是不分频(可选1~72)TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上对齐模式,同时还有向下对齐,中央对齐模式TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //计数器周期。该参数决定了计数器计数溢出前的最大值。TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //分频器预分频系数。该参数决定了计数器时钟频率的变化程度。TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //高级计数器需要,不需要用到的直接给0就好TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update); //用于解决一复位时就先进一次中断的情况TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM2, ENABLE);}
串口1、2初始化:
void Usart1_Init(unsigned int baud)
{GPIO_InitTypeDef gpio_initstruct;USART_InitTypeDef usart_initstruct;NVIC_InitTypeDef nvic_initstruct;// 打开串口GPIO的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 打开串口外设的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//PA9 TXD // 将USART Tx的GPIO配置为推挽复用模式gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;gpio_initstruct.GPIO_Pin = GPIO_Pin_9;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);//PA10 RXD // 将USART Rx的GPIO配置为浮空输入模式gpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;gpio_initstruct.GPIO_Pin = GPIO_Pin_10;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);usart_initstruct.USART_BaudRate = baud; //配置波特率usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控 usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送 usart_initstruct.USART_Parity = USART_Parity_No; //无校验usart_initstruct.USART_StopBits = USART_StopBits_1; //配置停止位 1位停止位usart_initstruct.USART_WordLength = USART_WordLength_8b; //配置 针数据字长 8位数据位// 完成串口的初始化配置USART_Init(USART1, &usart_initstruct);USART_Cmd(USART1, ENABLE); //使能串口USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能接收中断NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 嵌套向量中断控制器组选择 */nvic_initstruct.NVIC_IRQChannel = USART1_IRQn; /* 配置USART为中断源 */nvic_initstruct.NVIC_IRQChannelCmd = ENABLE; /* 使能中断 */nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0; /* 抢断优先级*/nvic_initstruct.NVIC_IRQChannelSubPriority = 2; /* 子优先级 */NVIC_Init(&nvic_initstruct); /* 初始化配置NVIC */}void Usart2_Init(unsigned int baud)
{GPIO_InitTypeDef gpio_initstruct;USART_InitTypeDef usart_initstruct;NVIC_InitTypeDef nvic_initstruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//PA2 TXDgpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;gpio_initstruct.GPIO_Pin = GPIO_Pin_2;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);//PA3 RXDgpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;gpio_initstruct.GPIO_Pin = GPIO_Pin_3;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);usart_initstruct.USART_BaudRate = baud;usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送usart_initstruct.USART_Parity = USART_Parity_No; //无校验usart_initstruct.USART_StopBits = USART_StopBits_1; //1位停止位usart_initstruct.USART_WordLength = USART_WordLength_8b; //8位数据位USART_Init(USART2, &usart_initstruct);USART_Cmd(USART2, ENABLE); //使能串口USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断nvic_initstruct.NVIC_IRQChannel = USART2_IRQn;nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;nvic_initstruct.NVIC_IRQChannelSubPriority = 0;NVIC_Init(&nvic_initstruct);
}
串口1、2自定义打印printf()函数的编写:
不理解这个的看我之前MSP432的文章有解释:
MSP432自主开发笔记3:串口__编写自定义printf发送函数、编写发送字节字符串函数编写_msp432单片机串口编程-CSDN博客
//选择串口发送数据--自定义Printf
void UsartPrintf (USART_TypeDef *USARTx, char *fmt,...)
{unsigned char UsartPrintfBuf[296]; //最大长度296va_list ap;unsigned char *pStr = UsartPrintfBuf;va_start(ap, fmt);vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化va_end(ap);while(*pStr != 0){USART_SendData(USARTx, *pStr++);while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);}
}
串口通信协议解码与校验配置:
首先了解一下它的通信协议:
这里比较重要的是与它通信的串口的波特率须是115200 :
它有需要我们发送一个固定查询命令的操作来查阅探测结果:
通信格式如下:
定义数据接收的结构体:
根据以上的学习,我们可以初步决定使用一个结构体来清晰地接收这些数据:
这样对于数据处理与转发就十分清晰与明白了:
//雷达数据反馈结构体
typedef struct {uint8_t length; // 长度:除帧头及校验字节外的字节数,0x0A,固定字节uint8_t address; // 地址:固定字节uint16_t distance; // 距离 cm uint8_t reserved; // 占 1 个字节,取值 0x00,固定字节uint8_t status; // 0:无人, 1:有人 uint16_t signalStrength; // 单位 k,信号强度 uint8_t microMotion; // 0:无微动, 1:有微动 uint8_t radarOff; // 0:没有关闭, 1:已关闭 uint8_t checksum; // 校验和
}SenserDataFarm;extern SenserDataFarm SDF; //实例化结构体
数据处理函数:
//处理数据的代码,例如更新距离、状态等变量
void parse_data(uint8_t *data, uint8_t leng)
{//校验和正确,提取数据SDF.address=data[0];SDF.distance = (data[1]<<8) | data[2]; //距离SDF.reserved=data[3]; //预留SDF.status = data[4]; //有人、无人SDF.signalStrength = (data[5] << 8) | data[6]; //信强度SDF.microMotion= data[7]; // 0:无微动, 1:有微动 单位 k,SDF.radarOff=data[8]; //0:没有关闭, 1:已关闭
}
简易状态机接收检验函数:
这里用到了状态机思维进行接收数据:
状态机放在串口中断服务函数调用:
/* 数据帧处理函数帧头:0x55 A5(2字节)长度:0x0A(1字节)地址:0xD3(1字节)距离:高位在前(2字节)预留:0x00(1字节)状态:0x00(无人)或0x01(有人)(1字节)信号强度:高位在前(2字节)微动:0x00(无微动)或0x01(有微动)(1字节)雷达关闭状态:0x00(未关闭)或0x01(已关闭)(1字节)校验和:除校验字节外所有字节的和的低8位(1字节)
*/
//数据帧处理函数 用到简易的状态机
void uart_rx_callback(uint8_t data)
{ static uint8_t state = 0; // 状态机状态
// static uint8_t checksum = 0; // 校验和 static uint8_t expected_length = 10; // 期望的数据长度 static uint8_t received_length = 0; // 已接收的数据长度 switch (state) { case 0: // 搜索帧头1 if (data== FRAME_HEADER_1) { state = 1;} break;case 1: // 搜索帧头2 if (data== FRAME_HEADER_2) { state = 2;} else{ state = 0; } // 重新开始搜索帧头1 break;case 2: // 搜索帧头2 if (data== FRAME_LENGTH) { state = 3;received_length = 0; // 重置已接收长度 } else{ state = 0; } // 重新开始搜索帧头1 break; case 11: // 读取校验和字节parse_data(rx_buffer,received_length); // 处理数据帧state = 0; //重置状态机,准备接收下一个数据帧 break;default: //处理其他数据字段rx_buffer[received_length++]=data; // 存储数据到缓冲区if (received_length == expected_length)// 数据接收完毕{ state = 11;} else{ // 继续接收数据字段 state++; }break; }
}数据帧处理函数 用到简易的状态机
//void uart_rx_callback(uint8_t data)
//{
// static uint8_t state = 0; // 状态机状态 static uint8_t checksum = 0; // 校验和
// static uint8_t expected_length = 10; // 期望的数据长度
// static uint8_t received_length = 0; // 已接收的数据长度
//
// switch (state)
// {
// case 0: // 搜索帧头1
// if (data== FRAME_HEADER_1)
// {
// state = 1;checksum+=data; // 更新校验和
// }
// break;
//
// case 1: // 搜索帧头2
// if (data== FRAME_HEADER_2)
// {
// state = 3; checksum+=data; // 更新校验和
// received_length = 0; // 重置已接收长度
// }
// else
// { checksum = 0; // 重置校验和
// state = 0; // 重新开始搜索帧头1
// }
// break;
// case 2: // 读取长度字节 expected_length = data; // 设置期望的数据长度 state = 3; checksum+=data; // 更新校验和received_length = 0; // 重置已接收长度 break;
//
// // ... 添加其他状态来处理地址、距离、状态等字段 ...
//
// case 11: // 读取校验和字节
// // 校验和匹配if ((checksum & 0xFF) == data) {
// parse_data(rx_buffer,received_length); // 处理数据帧}
// // 否则,丢弃该数据帧
// state = 0; //重置状态机,准备接收下一个数据帧
// break;
//
// default: //处理其他数据字段
// rx_buffer[received_length++]=data; // 存储数据到缓冲区checksum += data; // 更新校验和
//
// if (received_length == expected_length)
//
// {
// // 数据接收完毕,等待校验和字节
// state = 11;
// }
// else
// {
// // 继续接收数据字段
// state++;
// }
// break;
// }
//}