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

news2024/11/24 13:00:41

今日尝试配通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	TXD
	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);
	
	//PA3	RXD
	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; 
//    }  
//}

测试效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1600773.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue加载glb / gltf模型(如何在vue中使用Three.js,vue使用threejs加载glb模型)

简介&#xff1a;Three.js 是一个用于在 Web 上创建和显示 3D 图形的 JavaScript 库。它提供了丰富的功能和灵活的 API&#xff0c;使开发者可以轻松地在网页中创建各种 3D 场景、模型和动画效果。可以用来展示产品模型、建立交互式场景、游戏开发、数据可视化、教育和培训等等…

互联网大厂ssp面经,数据结构:part1

1. 数组和链表的区别是什么&#xff1f; a. 数组是一种线性数据结构&#xff0c;存储在连续的内存块中&#xff0c;元素可以通过索引直接访问。 b. 链表是由节点组成的数据结构&#xff0c;每个节点包含数据和指向下一个节点的指针。 2. 数组和链表的的优缺点是什么&#xff…

原始部落版本潮玩宇宙小程序定制大逃杀游戏APP开发H5游戏

原始部落版本潮玩宇宙小程序定制大逃杀游戏APP开发H5游戏 潮玩宇宙小程序定制大逃杀游戏APP开发H5游戏 潮玩宇宙大逃杀小游戏模块成品源码&#xff0c;可嵌入任何平台系统&#xff0c;增加用户粘性&#xff0c;消除泡沫&#xff0c;短视频直播引流。 玩家选择一间房间躲避杀手…

VBA脚本: excel隐藏和展开指定行 【图文】

打开开发工具功能 【文件】-》【选项】-》【自定义功能区】-》勾选【开发工具】-》【确定】 代开VBA编辑器 【开发工具】-》【Visual Basic】 插入模块 编写代码 所有sheet 关闭 Sub HideRowsInAllSheets()Dim ws As WorksheetDim i As Integer 循环遍历所有工作表For E…

STM32学习和实践笔记(13):数码管显示实验

共阳就是共正极&#xff0c;也就是正极全部接在一起。 共阴就是共负极&#xff0c;也就是负极全部接在一起。 我目前使用这款PZ6806L&#xff0c;使用了一个共阳数码管。 共阴与共阳在码表上其实就是正好取反就可以了&#xff0c;所以可以共用一个码表。 数码管显示程序主要分…

bp神经网络拟合函数未知参数【源码+视频教程】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《复杂函数拟合案例分享》本专栏旨在提供 1.以案例的形式讲解各类复杂函数拟合的程序实现方法&#xff0c;并提供所有案例完整源码&#xff1b;2.…

【Java】通过poi给word首页添加水印图片

背景&#xff1a; poi并没有提供直接插入水印图片的方法&#xff0c;目前需要再word的首页插入一张水印图片&#xff0c;于是就需要通过另一种方式&#xff0c;插入透明图片&#xff08;png格式&#xff09;并将图片设置为“浮于文字上方”的方式实现该需求。 所需jar&#xf…

AI智能体技术突破:引领科技新浪潮

AI智能体技术突破&#xff1a;引领科技新浪潮 基于大模型的 AI Agent 工作流基于大模型的 AI Agent 工作流效果AI Agent 的四种设计模式Reflection 反思设计模式Tool use 工具使用设计模式Planning 规划设计模式Multiagent collaboration 多智能体协作设计模式 吴恩达在红杉美国…

吃透2000-2024年600道真题和解析,科学高效通过2025年AMC8竞赛

为帮助孩子科学、有效备考AMC8竞赛&#xff0c;我整理了2000-2004年的全部AMC8真题&#xff08;完整版共600道&#xff0c;且修正了官方发布的原试卷中的少量bug&#xff09;&#xff0c;并且独家制作成多种在线练习&#xff0c;利用碎片化时间&#xff0c;8个多月的时间足以通…

【C++题解】1317. 正多边形每个内角的度数?

问题&#xff1a;1317. 正多边形每个内角的度数&#xff1f; 类型&#xff1a;基本运算、小数运算 题目描述&#xff1a; 根据多边形内角和定理&#xff0c;正多边形内角和等于&#xff1a;&#xff08; n&#xff0d;2 &#xff09; 180∘ ( n 大于等于 3 且 n 为整数&#…

Backend - Django Swagger

目录 一、安装依赖 二、配置环境 三、路由&#xff08;urls&#xff09; 四、swagger UI 界面 &#xff08;一&#xff09;UI 界面 &#xff08;二&#xff09;单引号问题&#xff1a;Expecting property name enclosed in double quotes 1. 原因 2. 解决 五、自定义s…

Go 单元测试基本介绍

文章目录 引入一、单元测试基本介绍1.1 什么是单元测试&#xff1f;1.2 如何写好单元测试1.3 单元测试的优点1.4 单元测试的设计原则 二、Go语言测试2.1 Go单元测试概要2.2 Go单元测试基本规范2.3 一个简单例子2.3.1 使用Goland 生成测试文件2.3.2 运行单元测试2.3.3 完善测试用…

基于Adaboost模型的数据预测和分类matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 AdaBoost&#xff08;Adaptive Boosting&#xff09;是一种集成学习方法&#xff0c;由Yoav Freund和Robert Schapire于1995年提出&#xff0c;主要用于提高弱分类…

Docker 学习笔记(九):Docker 网络原理,理解 docker0,虚拟网卡,容器互联,以及跨网络连通

一、前言 记录时间 [2024-4-16] 系列文章简摘&#xff1a; Docker 学习笔记&#xff08;六&#xff09;&#xff1a;挑战容器数据卷技术一文通&#xff0c;实战多个 MySQL 数据同步&#xff0c;能懂会用&#xff0c;初学必备 Docker 学习笔记&#xff08;七&#xff09;&#x…

引领智能互联时代,紫光展锐赋能百业创新发展

随着5G技术的快速发展&#xff0c;各行各业对通信技术的需求也在不断升级。紫光展锐持续深耕5G垂直行业&#xff0c;不断推进5G标准演进&#xff0c;从R15到R16&#xff0c;再到R17&#xff0c;展锐携手生态合作伙伴&#xff0c;不断推出创新性解决方案&#xff0c;在5G RedCap…

FMEA赋能可穿戴设备:打造安全可靠的未来科技新宠!

在科技日新月异的今天&#xff0c;可穿戴设备已成为我们生活中不可或缺的一部分。它们以其便携性、智能化和个性化的特点&#xff0c;深受消费者喜爱。然而&#xff0c;随着可穿戴设备市场的快速扩张&#xff0c;其安全性和可靠性问题也日益凸显。为了确保产品质量&#xff0c;…

【Git】常用命令速查

目录 一、创建版本 二、修改和提交 三、查看提交历史 四、撤销 五、分支与标签 六、合并与衍合 七、远程操作 一、创建版本 命令简要说明注意事项git clone <url>克隆远程版本库 二、修改和提交 命令简要说明注意事项 三、查看提交历史 命令简要说明注意事项 …

Adobe AE(After Effects)2017下载地址及安装教程

Adobe After Effects是一款专业级别的视觉效果和动态图形处理软件&#xff0c;由Adobe Systems开发。它被广泛用于电影、电视节目、广告和其他多媒体项目的制作。 After Effects提供了强大的合成和特效功能&#xff0c;可以让用户创建出令人惊艳的动态图形和视觉效果。用户可以…

OpenCV轻松入门(八)——图片卷积

对图像和滤波矩阵进行逐个元素相乘再求和的操作就相当于将一个二维的函数移动到另一个二维函数的所有位置&#xff0c;这个操作就叫卷积。 卷积需要4个嵌套循环&#xff0c;所以它并不快&#xff0c;除非我们使用很小的卷积核。这里一般使用3x3或者5x5 图像滤波 图像滤波是尽…

Map与Set的模拟实现封装

目录 一. 底层原理 二. 红黑树节点的定义 三. 仿函数封装 四. 基本函数的封装 五. 迭代器的封装 5.1 迭代器的基本定义 5.2 *与->操作 5.3 迭代器的操作 5.3.1 右子树不为空 5.3.2 右子树为空 5.4 迭代器的--操作 5.4.1 当前节点的父节点…