串口DMA——激光雷达数据处理
本文主要介绍一款激光雷达数据的读取和处理,读出数据主要采用STM32的串口,应为数据量比较大为了方便处理数据,所以采用了DMA的方式缓存数据然后在进行处理。
一、激光雷达简介
该款激光雷达主要是通过串口把扫描到的角度数据和距离数据通过固定的报文格式传输出来。该激光雷达报文格式如下图:
报文中可以看出我们可以利用提供的S、!S、C三个位进行运算来判断是否是报头,判断公式如下:
如果运算结果为1则为报头,否则则不是报头
二、STM32串口配置
激光雷达所需要的波特率为115200bps所以我们将STM32串口1外设设置为:115600bps、8位数据位、1位停止位、使能接收。即可,在这里可以不需要必将串口中断打开。串口配置代码如下:
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1, ENABLE);
三、STM32串口接收DMA设置
从STM32的手册中可以查看出DMA的各个通道请求总括:
由总括可以看出,USART1的接收寄存器对应的是DMA的第6通道。由于我们接收的报文长度一次为5个字节。所以我们可以将DMA的缓存区设置为50字节、数据宽度为16位、工作在循环缓存模式。因为这里只使用了一个DMA通道,所以直接将该通道的优先级设置为最高即可。配置代码如下:
void init_DMA()
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel5); //将DMA的通道5寄存器重设为缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA外设ADC基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&RX_Value; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地
DMA_InitStructure.DMA_BufferSize = 50; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道
DMA_Cmd(DMA1_Channel5, ENABLE);//打开 DMA
}
四、DMA缓存数据处理
报文数据主要包括角度和距离数据,并且每个角度对应一个距离。这样我们就可以根据极坐标来计算障碍物相对激光雷达的相对坐标了。
处理数据之前我们需要先定义一个长度为360*2双浮点的变量。其中用于保存我们处理后的距离和角度数据数据。其中第一列保存角度数据,第二列保存距离数据。我们将接收到的角度数据进行四舍五入作为变量下标,例如:接收到一组角度为30.789°距离为1m的数据。我们就将数据角度数据保存在[31][1]中,距离数据保存在[31][2]中。具体处理代码如下所示:
void disposeDMA()
{
unsigned char cData[50];
unsigned char i;
unsigned char cNum=0;//用于计数
double dAngle;//角度数据
double dDistance;//距离数据
unsigned int iAngle_x;//下标
while(cNum<45)
{
for(i=0;i<50;i++)
{
cData[i] = RX_Value[i];//保存现场
}
if(cData[cNum]^cData[cNum]>>1&&cData[cNum+1])//判断是否为报头
{
dAngle = cData[cNum+1]>>1|cData[cNum+2]<<7;//保存角度数据
dAngle/=64.0;//角度数据
dDistance = cData[cNum+3]|cData[cNum+4]<<8;//距离数据
dDistance /= 4.0;//距离数据
iAngle_x = (unsigned int)dAngle+0.5;//角度四舍五入处理
g_iDistance[iAngle_x][0] = dAngle;//保存角度数据
g_iDistance[iAngle_x][1] = dDistance;//保存距离数据
}
else
{
cNum++;
}
}
}
五、总结
本文记录了一种一次性处理大量串口数据的方法,主要采用了DMA的方式先将串口数据进行缓存,由于激光雷达启动后如果没有接收到停止指令之前会一直不断的发送数据,但是单片机CPU速度有限,为了保证在单片机在处理别的任务时不会被串口的数据干扰所以笔者采用了该方式。
笔者能力有限,如有错误还望包含和指出