自学内容网 自学内容网

STM32 F103 C8T6开发笔记14:与HLK-LD303-24G测距雷达通信

今日尝试配通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);


//PA9TXD// 将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);

//PA10RXD  // 将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);

//PA2TXD
gpio_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);

//PA3RXD
gpio_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];                                  //最大长度296
va_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; 
//    }  
//}

测试效果:


原文地址:https://blog.csdn.net/qq_64257614/article/details/137640931

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!