文章內容:
思嵐激光雷達M8A1使用STM32F407來做初步的資料觀測,
注:由于激光雷達發送資料量十分之大,因此使用串口來做測距與測角度得到的與當前值的偏差較大,因此本文只是初步介紹,
若需要解算出精確資料則需要使用DMA,我將在之后的文章使用DMA來解算資料,敬請期待!
文章包含:
1>使用串口發送資料來啟動激光雷達旋轉.
2>再串口列印出具體的十六進制距離和角度資料
學習目標:
通過本文初步了解激光雷達如何使用STM32來做到測距測角度,
學習內容:
1>配置串口
2>配置PWM
3>串口發送資料給激光雷達
4>配置占空比來使激光雷達旋轉

途中已經表示出激光雷達的串口協議,我們只需按照要求配置串口即可,
激光雷達在只為了測算距離和角度時不需要配置雙串口,但由于本文目的是為了讓大家能在串口處看到激光雷達回傳的資料,因此需要第二個串口來列印出回傳資料,
注:本文使用串口1與串口6,也可使用其他串口.以及我是用的是CH340串口工具,接線在接下來提出,
代碼講解:
1> 串口1的配置
#include "sys.h"
#include "usart.h"
#include "led.h"
#include "delay.h"
#if SYSTEM_SUPPORT_UCOS //時鐘配置
#include "includes.h" //ucos 使用
#endif
#if 1
#pragma import(__use_no_semihosting)
//標準庫需要的支持函式
struct __FILE
{
int handle;
};
FILE __stdout;
//定義_sys_exit()以避免使用半主機模式
int _sys_exit(int x)
{
x = x;
}
//重定義fputc函式
//使串口1能使用printf函式
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//回圈發送,直到發送完畢
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中斷服務程式
//注意,讀取USARTx->SR能避免莫名其妙的錯誤
u8 USART_RX_BUF[USART_REC_LEN]; //接識訓沖,最大USART_REC_LEN個位元組.
//接收狀態
//bit15, 接收完成標志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效位元組數目
u16 USART_RX_STA=0; //接收狀態標記
u8 RX_buffer[5]={0};
//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound) //串口1
{
//GPIO埠設定
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1時鐘
//串口1對應引腳復用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9復用為USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10復用為USART1
//USART1埠配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9與GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復用輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
//USART1 初始化設定
USART_InitStructure.USART_BaudRate = bound;//波特率設定
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位資料格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體資料流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟相關中斷
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的引數初始化VIC暫存器、
}
void USART1_IRQHandler(void) //串口1中斷服務函式
{
printf("distance=%d\n",distance);
printf("angle=%d\n",angle);
}
2>串口6配置
void init(u32 bound) //串口6
{
//GPIO埠設定
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE); //使能GPIOA時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6,ENABLE);//使能USART1時鐘
//串口1對應引腳復用映射
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_USART6); //GPIOC6復用為USART6
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_USART6); //GPIOC7復用為USART6
//USART1埠配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //GPIOC6與GPIOC7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復用輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PA9,PA10
//USART1 初始化設定
USART_InitStructure.USART_BaudRate = bound;//波特率設定
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位資料格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體資料流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式
USART_Init(USART6, &USART_InitStructure); //初始化串口1
USART_ClearFlag(USART6, USART_FLAG_TC);
USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);//開啟相關中斷
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn;//串口1中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的引數初始化VIC暫存器、
}
u8 jp=0; //宣告一個變數用于存盤資料
void USART6_IRQHandler(void) //串口6中斷服務程式
{
if(USART_GetITStatus(USART6, USART_IT_RXNE) != RESET)
{
RX_buffer[jp]=USART_ReceiveData(USART6);
jp++;
if(jp%5==0) //確保收到了完整的一組5個資料后才開始解算資料
{
distance=(RX_buffer[2]<<8|RX_buffer[1])/0X04;
angle=(RX_buffer[4]<<8|RX_buffer[3]>>1)/0X80;
//printf("distance=%d\n",distance); 距離角度等資料通過串口1列印
//printf("angle=%d\n",angle);
jp=0;
delay_ms(50); //由于32算力不夠,且資料量過大,選擇延遲一定時間來漏掉一些資料,
//是否需要可看自己
}
}
}
至此,串口配置完成,接下來開始PWM的配置,
我使用的是TIM1的定時器,時鐘頻率為168MHz.
3>PWM配置
代碼如下:
void RIGHT_PWM_Init(u32 arr,u32 psc) //激光雷達旋轉
{
//此部分需手動修改IO口設定
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); //TIM14時鐘使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTF時鐘
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM1); //GPIOF9復用為定時器1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //GPIOF8
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //復用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復用輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PF8
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定時器分頻
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上計數模式
TIM_TimeBaseStructure.TIM_Period=arr; //自動重裝載值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);//初始化定時器13
//初始化TIM13 Channel1 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈沖寬度調制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //輸出極性:TIM輸出比較極性低
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根據T指定的引數初始化外設TIM1 4OC1
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能TIM14在CCR1上的預裝載暫存器
TIM_ARRPreloadConfig(TIM1,ENABLE);//ARPE使能
TIM_Cmd(TIM1, ENABLE); //使能TIM14
}
4>串口6發送啟動激光雷達的資料
{
USART_ClearFlag(USART6,USART_FLAG_TC);
if(1)
{ USART_SendData(USART6,0xA5); //從串口1發送開始指令 USART_FLAG_TC: 發送移位暫存器發送完成標志位,全部發送完畢會置 1
while(USART_GetFlagStatus(USART6,USART_FLAG_TC)!=SET);//等待發送結束
USART_SendData(USART6,0x20); //從串口1發送結束指令
while(USART_GetFlagStatus(USART6,USART_FLAG_TC)!=SET);//等待發送結束
}
}
5>主函式配置(波特率選擇以及PWM頻率配置)
#include "sys.h"
#include "delay.h"
#include "usart.h" //串口1 6配置
#include "led.h" //pwm和激光雷達初始化
u16 distance; //距離
u8 angle; //角度
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設定系統中斷優先級分組2
delay_init(168); //延時初始化
LidarInit(); //初始化激光雷達
uart_init(115200); //串口1
init(115200); //串口6
RIGHT_PWM_Init(2500-1,168-1); //PA8 1000000/5000=2000;
//每一秒能計數250/1000000=1/4000,也就是1s能進入4000次更新,250us進入一次更新
while(1)
{
}
}
至此,代碼部分結束,現在來接線,


圖中可以觀察到激光雷達的電壓供給為5V,因此我們需要接在STM32F407的5V介面上且需要兩個,但這樣做并滿足不了電流的要求,因此需要為STM32接上一根供電線,
接線:
TX與PC7連接
RX與PC6連接
VCC_5V,5V_MOTO與5V連接
GND,GND_MOTO與GND連接
MOTOCTL與PA8連接

上圖為CH340,通過杜邦線連接GND,RXD,TXD,3.3V到STM32上完成與STM32的連接,并將另一頭與電腦連接,
至此,所有接線也完成,此時發現激光雷達開始旋轉,
之后便是使用SSCOM,這一部分不多講解,之后便會發現串口工具瘋狂列印資料,
至此,全部結束,
如果不滿足于使用串口來做距離和角度測算的,可以使用DMA,若有機會我會使用DMA來講解激光雷達,
敬請期待!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/275899.html
標籤:其他
