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

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

文章目录

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

V-LD1

该模块是由串口进行控制的
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第1张图片
串口协议结构体如下:

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

头文字是ASCII码字符串格式
然后四字节的Length表示DATA数据长度
数据位小端格式
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第2张图片
通信方式就是先发一个命令 然后等待RESP返回 随后就是命令对应的数据

INIT命令支持修改波特率 但第一次发的时候必须用115200
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第3张图片修改波特率后 直到GBYE命令或复位、断电之前 都是修改后的波特率

命令发送

读取雷达命令就是GNFD 另外配置雷达参数则是SRPS
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第4张图片
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第5张图片

消息回复

发什么命令 就按什么格式回复 但RESP是肯定会最先回复的
另外 读雷达参数用GRPS命令
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第6张图片

通信示例

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

雷达数据获取

通过GNFD命令获取雷达数据
如果不需要读大量数据 可以只使用115200
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第8张图片
基于RFbeam的V-LD1-60GHz毫米波雷达传感器数据获取(通过UART串口来控制模块)_第9张图片

宏定义

#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();	
}

运行效果

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

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

压缩字符串

首先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;
}

你可能感兴趣的:(嵌入式硬件芯片开发笔记,嵌入式,c语言,单片机,物联网,mcu,iot,stm32)