基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)

基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)

文章目录

  • V-LD1
  • 命令发送
  • 消息回复
  • 通信示例
  • 雷达数据获取
  • 宏定义
  • 通信代码
  • 运行效果
  • 附录:压缩字符串、大小端格式转换
    • 压缩字符串
      • 浮点数
      • 压缩Packed-ASCII字符串
    • 大小端转换
      • 什么是大端和小端
      • 数据传输中的大小端
      • 总结
      • 大小端转换函数

V-LD1

该模块是由串口进行控制的
在这里插入图片描述
串口协议结构体如下:

#pragma pack(1)
typedef struct
{char Header[4];uint32_t Length;uint8_t DATA[43];
}V_LD1_Struct;
#pragma pack()

头文字是ASCII码字符串格式
然后四字节的Length表示DATA数据长度
数据位小端格式
在这里插入图片描述
通信方式就是先发一个命令 然后等待RESP返回 随后就是命令对应的数据

INIT命令支持修改波特率 但第一次发的时候必须用115200
在这里插入图片描述修改波特率后 直到GBYE命令或复位、断电之前 都是修改后的波特率

命令发送

读取雷达命令就是GNFD 另外配置雷达参数则是SRPS
在这里插入图片描述
在这里插入图片描述

消息回复

发什么命令 就按什么格式回复 但RESP是肯定会最先回复的
另外 读雷达参数用GRPS命令
在这里插入图片描述

通信示例

在这里插入图片描述

雷达数据获取

通过GNFD命令获取雷达数据
如果不需要读大量数据 可以只使用115200
在这里插入图片描述
在这里插入图片描述

宏定义

#ifndef __V_LD1_H__
#define __V_LD1_H__
#include "main.h"#pragma pack(1)
typedef struct
{char Header[4];uint32_t Length;uint8_t DATA[43];
}V_LD1_Struct;
#pragma pack()#pragma pack(1)
typedef struct
{char Version[19];char Unique_ID[12];uint8_t Distance_Range;uint8_t Threshold_Offset;uint16_t Min_Range_Filter;uint16_t Max_Range_Filter;uint8_t Distance_Average_Count;uint8_t Target_Filter;uint8_t Distance_Precision;uint8_t TX_Power;uint8_t Chirp_Integration_Count;uint8_t Short_Range_Distance_Filter;
}V_LD1_Radar_Parameter_Struct;
#pragma pack()#pragma pack(1)
typedef struct
{uint16_t ADC_Value[1024];
}V_LD1_RADC_Struct;
#pragma pack()#pragma pack(1)
typedef struct
{uint16_t Spectrum_Point[512];uint16_t Threshold_Point[512];
}V_LD1_RFFT_Struct;
#pragma pack()#pragma pack(1)
typedef struct
{float Distance;uint16_t Magnitude_Of_Target;
}V_LD1_PDAT_Struct;
#pragma pack()#pragma pack(1)
typedef struct
{uint32_t Frame_ID;
}V_LD1_DONE_Struct;
#pragma pack()typedef enum
{V_LD1_GNFD_RADC = (1<<0),V_LD1_GNFD_RFFT = (1<<1),V_LD1_GNFD_PDAT = (1<<2),V_LD1_GNFD_DONE = (1<<5),
}V_LD1_GNFD_Enum;typedef enum
{V_LD1_RESP_OK			 									= 0,V_LD1_RESP_Unknown_CMD			 				= 1,V_LD1_RESP_Invalid_Parameter_Value	= 2,V_LD1_RESP_Invalid_RPST_Version			= 3,V_LD1_RESP_UART_Error								= 4,V_LD1_RESP_No_Calibration_Value			= 5,V_LD1_RESP_Timeout									= 6,V_LD1_RESP_NO_Programmed						= 7,
}V_LD1_RESP_Enum;extern uint8_t V_LD1_Status;
extern uint8_t V_LD1_RxBit;
extern uint8_t V_LD1_RxBuffer[1024];
extern uint8_t V_LD1_RxFlag;
extern V_LD1_Radar_Parameter_Struct V_LD1_Radar_Parameter_Global;void Init_V_LD1(void);void Read_V_LD1_Radar(void);
#endif

通信代码

# include "V_LD1.h"uint8_t V_LD1_RxBit=0;
uint8_t V_LD1_RxBuffer[1024]={0};
uint8_t V_LD1_RxFlag=0;
uint8_t V_LD1_Status=0;V_LD1_Radar_Parameter_Struct V_LD1_Radar_Parameter_Global={0};V_LD1_Struct Read_V_LD1_Stu(void)
{V_LD1_Struct V_LD1_Stu;memset(&V_LD1_Stu,0,sizeof(V_LD1_Stu));uint8_t i=0;while(V_LD1_Status<2){i++;delay_ms(10);if(i>=50){V_LD1_RxBit=0;V_LD1_Status=0;V_LD1_RxFlag=0;return V_LD1_Stu;}}memcpy(&V_LD1_Stu.Header[0],&V_LD1_RxBuffer[0],4);memcpy(&V_LD1_Stu.Length,&V_LD1_RxBuffer[4],4);memcpy(&V_LD1_Stu.DATA[0],&V_LD1_RxBuffer[8],V_LD1_Stu.Length);V_LD1_RxBit=0;V_LD1_Status=0;V_LD1_RxFlag=0;return V_LD1_Stu;
}void Send_V_LD1_Stu(V_LD1_Struct V_LD1_Stu)
{uint8_t buf[51]={0};memcpy(buf,&V_LD1_Stu,V_LD1_Stu.Length+8);V_LD1_RxBit=0;V_LD1_Status=0;V_LD1_RxFlag=0;HAL_UART_Transmit(&V_LD1_UART_Handle,buf,V_LD1_Stu.Length+8,0xFFFF);
}int Read_V_LD1_RESP(void)
{V_LD1_Struct V_LD1_Stu=Read_V_LD1_Stu();if (V_LD1_Stu.Header[0]=='R' && V_LD1_Stu.Header[1]=='E' && V_LD1_Stu.Header[2]=='S' && V_LD1_Stu.Header[3]=='P' && V_LD1_Stu.Length==1){return V_LD1_Stu.DATA[0];}else{return -1;}
}int Read_V_LD1_VERS(V_LD1_Struct* V_LD1)
{V_LD1_Struct V_LD1_Stu=Read_V_LD1_Stu();if (V_LD1_Stu.Header[0]=='V' && V_LD1_Stu.Header[1]=='E' && V_LD1_Stu.Header[2]=='R' && V_LD1_Stu.Header[3]=='S' && V_LD1_Stu.Length==19){memcpy(V_LD1,&V_LD1_Stu,27);return 0;}else{return -1;}
}void Read_V_LD1_Radar(void)
{V_LD1_Struct V_LD1_Stu={0};V_LD1_PDAT_Struct PDAT_Stu = {0};GUI_Struct Stu={0};uint8_t RESP_Code=0;memcpy(&V_LD1_Stu.Header[0],"GNFD",4);V_LD1_Stu.Length=1;V_LD1_Stu.DATA[0]=0|V_LD1_GNFD_PDAT;	Send_V_LD1_Stu(V_LD1_Stu);RESP_Code=Read_V_LD1_RESP();printf("[INFO] GNFD PDAT RESP: %d\n",RESP_Code);V_LD1_Stu=Read_V_LD1_Stu();if (V_LD1_Stu.Header[0]=='P' && V_LD1_Stu.Header[1]=='D' && V_LD1_Stu.Header[2]=='A' && V_LD1_Stu.Header[3]=='T' && V_LD1_Stu.Length==6){memcpy(&PDAT_Stu,&V_LD1_Stu.DATA[0],6);printf("[INFO] PDAT: %f %d\n",PDAT_Stu.Distance,PDAT_Stu.Magnitude_Of_Target);Stu.COM=0x00;Stu.BCNT[0]=0;Stu.BCNT[1]=6;memcpy(&Stu.DATA[0],&PDAT_Stu,6);GUI_Slave_Send(Stu);}
}void Init_Radar_Parameter(void)
{V_LD1_Radar_Parameter_Global.Distance_Range=0;V_LD1_Radar_Parameter_Global.Threshold_Offset=60;V_LD1_Radar_Parameter_Global.Min_Range_Filter=5;V_LD1_Radar_Parameter_Global.Max_Range_Filter=460;V_LD1_Radar_Parameter_Global.Distance_Average_Count=5;V_LD1_Radar_Parameter_Global.Target_Filter=0;V_LD1_Radar_Parameter_Global.Distance_Precision=1;V_LD1_Radar_Parameter_Global.TX_Power=31;V_LD1_Radar_Parameter_Global.Chirp_Integration_Count=20;V_LD1_Radar_Parameter_Global.Short_Range_Distance_Filter=0;
}void Init_V_LD1(void)
{V_LD1_Struct V_LD1_Stu={0};V_LD1_RxBit=0;V_LD1_Status=0;V_LD1_RxFlag=0;uint8_t RESP_Code=0;memset(V_LD1_RxBuffer,0,sizeof(V_LD1_RxBuffer));memcpy(&V_LD1_Stu.Header[0],"RFSE",4);V_LD1_Stu.Length=0;Send_V_LD1_Stu(V_LD1_Stu);RESP_Code=Read_V_LD1_RESP();printf("[INFO] RFSE RESP: %d\n",RESP_Code);memcpy(&V_LD1_Stu.Header[0],"INIT",4);V_LD1_Stu.Length=1;V_LD1_Stu.DATA[0]=0;	Send_V_LD1_Stu(V_LD1_Stu);RESP_Code=Read_V_LD1_RESP();printf("[INFO] INIT RESP: %d\n",RESP_Code);if(RESP_Code==V_LD1_RESP_OK){if(Read_V_LD1_VERS(&V_LD1_Stu)==0){printf("[INFO] V_LD1_Version: %s\n",V_LD1_Stu.DATA);memcpy(&V_LD1_Radar_Parameter_Global.Version[0],&V_LD1_Stu.DATA[0],19);}}memcpy(&V_LD1_Stu.Header[0],"TGFI",4);V_LD1_Stu.Length=1;	V_LD1_Stu.DATA[0]=0	;Send_V_LD1_Stu(V_LD1_Stu);RESP_Code=Read_V_LD1_RESP();printf("[INFO] TGFI RESP: %d\n",RESP_Code);memcpy(&V_LD1_Stu.Header[0],"INTN",4);V_LD1_Stu.Length=1;	V_LD1_Stu.DATA[0]=20;Send_V_LD1_Stu(V_LD1_Stu);RESP_Code=Read_V_LD1_RESP();printf("[INFO] INTN RESP: %d\n",RESP_Code);memcpy(&V_LD1_Stu.Header[0],"SRDF",4);V_LD1_Stu.Length=1;	V_LD1_Stu.DATA[0]=0;Send_V_LD1_Stu(V_LD1_Stu);RESP_Code=Read_V_LD1_RESP();printf("[INFO] SRDF RESP: %d\n",RESP_Code);Read_V_LD1_Radar();	
}

运行效果

在这里插入图片描述

附录:压缩字符串、大小端格式转换

压缩字符串

首先HART数据格式如下:
在这里插入图片描述
在这里插入图片描述
重点就是浮点数和字符串类型
Latin-1就不说了 基本用不到

浮点数

浮点数里面 如 0x40 80 00 00表示4.0f

在HART协议里面 浮点数是按大端格式发送的 就是高位先发送 低位后发送

发送出来的数组为:40,80,00,00

但在C语言对浮点数的存储中 是按小端格式来存储的 也就是40在高位 00在低位
浮点数:4.0f
地址0x1000对应00
地址0x1001对应00
地址0x1002对应80
地址0x1003对应40

若直接使用memcpy函数 则需要进行大小端转换 否则会存储为:
地址0x1000对应40
地址0x1001对应80
地址0x1002对应00
地址0x1003对应00

大小端转换:

void swap32(void * p)
{uint32_t *ptr=p;uint32_t x = *ptr;x = (x << 16) | (x >> 16);x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);*ptr=x;
}

压缩Packed-ASCII字符串

本质上是将原本的ASCII的最高2位去掉 然后拼接起来 比如空格(0x20)
四个空格拼接后就成了
1000 0010 0000 1000 0010 0000
十六进制:82 08 20
对了一下表 0x20之前的识别不了
也就是只能识别0x20-0x5F的ASCII表
在这里插入图片描述

压缩/解压函数后面再写:

//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_ASCII_to_Pack(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{if(str_len%4){return 0;}uint8_t i=0;memset(buf,0,str_len/4*3);	  for(i=0;i<str_len;i++){if(str[i]==0x00){str[i]=0x20;}}for(i=0;i<str_len/4;i++){buf[3*i]=(str[4*i]<<2)|((str[4*i+1]>>4)&0x03);buf[3*i+1]=(str[4*i+1]<<4)|((str[4*i+2]>>2)&0x0F);buf[3*i+2]=(str[4*i+2]<<6)|(str[4*i+3]&0x3F);}return 1;
}//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_Pack_to_ASCII(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{if(str_len%4){return 0;}uint8_t i=0;memset(str,0,str_len);for(i=0;i<str_len/4;i++){str[4*i]=(buf[3*i]>>2)&0x3F;str[4*i+1]=((buf[3*i]<<4)&0x30)|(buf[3*i+1]>>4);str[4*i+2]=((buf[3*i+1]<<2)&0x3C)|(buf[3*i+2]>>6);str[4*i+3]=buf[3*i+2]&0x3F;}return 1;
}

大小端转换

在串口等数据解析中 难免遇到大小端格式问题

什么是大端和小端

所谓的大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

所谓的小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

简单来说:大端——高尾端,小端——低尾端

举个例子,比如数字 0x12 34 56 78在内存中的表示形式为:

1)大端模式:

低地址 -----------------> 高地址

0x12 | 0x34 | 0x56 | 0x78

2)小端模式:

低地址 ------------------> 高地址

0x78 | 0x56 | 0x34 | 0x12

可见,大端模式和字符串的存储模式类似。

数据传输中的大小端

比如地址位、起止位一般都是大端格式
如:
起始位:0x520A
则发送的buf应为{0x52,0x0A}

而数据位一般是小端格式(单字节无大小端之分)
如:
一个16位的数据发送出来为{0x52,0x0A}
则对应的uint16_t类型数为: 0x0A52

而对于浮点数4.0f 转为32位应是:
40 80 00 00

以大端存储来说 发送出来的buf就是依次发送 40 80 00 00

以小端存储来说 则发送 00 00 80 40

由于memcpy等函数 是按字节地址进行复制 其复制的格式为小端格式 所以当数据为小端存储时 不用进行大小端转换
如:

uint32_t dat=0;
uint8_t buf[]={0x00,0x00,0x80,0x40};memcpy(&dat,buf,4);float f=0.0f;f=*((float*)&dat); //地址强转printf("%f",f);

或更优解:

   uint8_t buf[]={0x00,0x00,0x80,0x40};   float f=0.0f;memcpy(&f,buf,4);

而对于大端存储的数据(如HART协议数据 全为大端格式) 其复制的格式仍然为小端格式 所以当数据为小端存储时 要进行大小端转换
如:

uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};memcpy(&dat,buf,4);float f=0.0f;swap32(&dat); //大小端转换f=*((float*)&dat); //地址强转printf("%f",f);

或:

uint8_t buf[]={0x40,0x80,0x00,0x00};memcpy(&dat,buf,4);float f=0.0f;swap32(&f); //大小端转换printf("%f",f);

或更优解:

uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};float f=0.0f;dat=(buf[0]<<24)|(buf[0]<<16)|(buf[0]<<8)|(buf[0]<<0)f=*((float*)&dat);

总结

固 若数据为小端格式 则可以直接用memcpy函数进行转换 否则通过移位的方式再进行地址强转

对于多位数据 比如同时传两个浮点数 则可以定义结构体之后进行memcpy复制(数据为小端格式)

对于小端数据 直接用memcpy写入即可 若是浮点数 也不用再进行强转

对于大端数据 如果不嫌麻烦 或想使代码更加简洁(但执行效率会降低) 也可以先用memcpy写入结构体之后再调用大小端转换函数 但这里需要注意的是 结构体必须全为无符号整型 浮点型只能在大小端转换写入之后再次强转 若结构体内采用浮点型 则需要强转两次

所以对于大端数据 推荐通过移位的方式来进行赋值 然后再进行个别数的强转 再往通用结构体进行写入

多个不同变量大小的结构体 要主要字节对齐的问题
可以用#pragma pack(1) 使其对齐为1
但会影响效率

大小端转换函数

直接通过对地址的操作来实现 传入的变量为32位的变量
中间变量ptr是传入变量的地址

void swap16(void * p)
{uint16_t *ptr=p;uint16_t x = *ptr;x = (x << 8) | (x >> 8);*ptr=x;
}void swap32(void * p)
{uint32_t *ptr=p;uint32_t x = *ptr;x = (x << 16) | (x >> 16);x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);*ptr=x;
}void swap64(void * p)
{uint64_t *ptr=p;uint64_t x = *ptr;x = (x << 32) | (x >> 32);x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x >> 16) & 0x0000FFFF0000FFFF);x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF00FF00FF);*ptr=x;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VueRequest——管理请求状态库

文章目录 前言一、为什么选择 VueRequest&#xff1f;二、使用步骤1.安装2.用例 前言 VueRequest——开发文档 VueReques——GitHub地址 在以往的业务项目中&#xff0c;我们经常会被 loading 状态的管理、请求的节流防抖、接口数据的缓存、分页等重复的功能实现所困扰。每次开…

MySQL Command Line Client 运行闪退问题解决,缺少my.ini文件

MySQL Command Line Client 运行闪退问题解决&#xff1a; 问题排查&#xff1a; 1.找到Command Line Client的路径位置&#xff0c;并查看属性&#xff0c;步骤截图&#xff1a; 查看属性&#xff1a; 查看属性中的目标路径&#xff1a; 2.进入属性中的目标路径&#xff0c;…

GreenCloud VPS 重装系统后无法 SSH 的解决方法

发布于 2023-07-17 在 https://chenhaotian.top/vps/greencloud-ssh-fix/ 解决方法 发工单让客服解决即可。 操作过程 Tu Pham Operator 客服 Hello, We have fixed your problem, please try again! Thanks! Tu Pham, Senior Technician - GreenCloudVPS 17th July 2023…

mysql之主从复制和读写分离

一、主从复制 1、定义 主mysql上的数据&#xff08;新增或修改库、表里的数据&#xff09;都会同步到从mysql上 2、mysql的主从复制模式&#xff08;面试题&#xff09; &#xff08;1&#xff09;异步复制&#xff08;常用&#xff09;&#xff1a;默认的复制模式。客户端…

NSSCTF第12页(1)

[FSCTF 2023]细狗2.0 应该是和[HUBUCTF 2022 新生赛]ezsql搞混掉了 点击按钮出现了 发现输入什么回显什么 伪协议也不行 看源代码发现了这个玩意 输入了1;发现了其他回显 ls 发现了两个文件 发现被限制了 不知道是cat还是空格 绕过 直接找吧还是 得到flag [SCTF 2021]loginm…

Linux下向Github仓库推送

文章目录 Git 与 Github安装git在github下创建项目下载项目到本地Git三板斧第一板斧 git add第二板斧 git commit第三板斧 git push Git 与 Github Git是目前从开发人员到设计人员的版本控制技术。gitee是国内社交代码托管平台。这是一个你可以玩和实验的地方。在这里你可以找…

核心!华为自研系统鸿蒙趋势

鸿蒙系统的推出引起了全球的关注&#xff0c;毕竟这是华为自主研发的操作系统。这个系统有一些特点很独特。首先&#xff0c;它的自主可控性是一大特色。因为是自家研发的&#xff0c;所以更容易适应外界变化。其次&#xff0c;它采用了分布式架构&#xff0c;这样不同设备之间…

【Windows 开发环境配置——NVIDIA 篇】CUDA、cuDNN、TensorRT 三件套安装

CUDA 从CUDA Toolkit Archive下载相应版本的离线安装包&#xff0c;这里以11.7为例。 打开安装包&#xff0c;在安装选项选择自定义模式&#xff0c;点击下一步。 在自定义安装选项中&#xff0c;仅选择CUDA组件&#xff08;其中Nsight相关组件用于代码调试与性能分析&#xff…

基于单片机的电源切换控制器设计(论文+源码)

1.系统设计 在基于单片机的电源切换控制器设计中&#xff0c;系统功能设计如下&#xff1a; &#xff08;1&#xff09;实现电源的电压检测&#xff1b; &#xff08;2&#xff09;如果电压太高&#xff0c;通过蜂鸣器进行报警提示&#xff0c;继电器进行切换&#xff0c;使…

MATLAB | 官方举办的动图绘制大赛 | 第一周赛情回顾

嘿真的又是很久没见了&#xff0c;最近确实有点非常很特别小忙&#xff0c;今天带来一下MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展&#xff01;&#xff01;目前比赛已经刚好进行了一周&#xff0c;前两届都要求提交280个字符内的代码来生成…

使用字典树实现一个可以自动补全的输入框

说在前面 平时我们在终端输入命令的时候是不是都可以通过tab键来进行快速补全&#xff1f;那么有没有想过怎么去实现这个自动补全的功能呢&#xff1f;今天让我们一起来使用字典树实现一个可以自动补全的输入框。 效果展示 体验地址 http://jyeontu.xyz/jvuewheel/#/JAutoComp…