STM32+DWM1000开发uwb测距系列教程之二:源码分析及源码移植(基于STM32 cubemx+keil MDK)
目錄
- *資源下載
- 1.本篇簡介
- 2 移植前規劃
- 3 使用stm32 cubemx生成硬件初始化工程
- 4 打開工程并添加官方驅動庫
- 5 include文件路徑添加
- 6 精確延時函數實現
- 7 dwb接口函數修改
- 7.1 deca_spi.c
- 7.2 port.c
- 7.2.1 portGetTickCnt()
- 7.2.2 usleep()延時函數
- 7.2.3 Sleep()延時函數
- 7.2.4 reset_DW1000()函數實現
- 7.2.5 setup_DW1000RSTnIRQ() 設置復位引腳工作模式
- 7.2.6 port_wakeup_dw1000_fast()函數,不需要修改
- 7.2.7 添加對應的example c文件到工程,并添加相應的宏定義
- 7.2.8 其它工作
- 8 代碼分析及調試
*資源下載
一個基站和兩個標簽實現官方twr測距例程下載鏈接:一基站兩標簽測距例程下載
官方dwm1000模塊例程下載鏈接:官方源碼下載鏈接
1.本篇簡介
STM32+DWM1000開發uwb測距系列教程之一
上一篇 文章主要簡單介紹了一下官方最新示例代碼的打開和基本工程目錄結構。
本篇在前一篇的基礎上,進行工程移植,移植思路是,首先保持官方口味不變,因為官方代碼應該是正常可用的,另外例程豐富;其次是盡可能使用cube mx,在不考慮程序執行效率的外加因素的情況下,stm32開發,使用cubemx 是最快捷方便的,并且出錯概率也是最小的,當然stm32相關的手冊是必須放在手邊的,有備無患(事實證明,確實是這樣)。
2 移植前規劃
官方使用的stm32芯片是stm32f105xc的芯片,本次使用的芯片是stm32f103rbt6和GD32F103RET6,GD32的片子暫且當STM32來用(事實證明,這樣沒有任何問題)。
官方主要使用了USB CDC接口作為和上位機通信的接口,在這里使用UART1接口代替,作為程序調試的printf輸出使用;
官方使用了LCD,在這里,使用oled128*64,接口為SPI接口,使用stm32的spi2
官方dwm1000和stm32通信主要使用了spi1,在這里保持一致。
另外官方demo中dwm1000執行過程中使用到了us級和ms級延時,這里為了保證后期擴展/增加rtos的可能性,systick時鐘保持默認。dwm相關延時操作使用timer4來進行。
總結以上,需要進行的操作有
| 1 | printf | 刪掉USB部分 | 硬件配置UART1,并對printf進行重定向 |
| 2 | 輸出顯示 | 刪除原來LCD部分代碼 | 增加OLED接口對應spi硬件初始化及OLED初始化、顯示燈API |
| 3 | dwm1000 | 修改硬件不通接口部分 | 在這里,盡量給原程序保持硬件接口一致。但因為身邊開發板的原因,不得已部分引腳不兼容,做了修改。 |
| 4 | 其它 | 暫無 | 暫無 |
dwm1000與STM32連接硬件接口差異表:
| 1 | PB5 | IRQn | PA1 |
| 2 | PB0 | WAKEUP | PC4 |
| 3 | PA0 | RESET | PA3 |
| 4 | PA4 | NSS | PA4 |
| 5 | PA5 | SCK | PA5 |
| 6 | PA6 | MISO | PA6 |
| 7 | PA7 | MOSI | PA7 |
3 使用stm32 cubemx生成硬件初始化工程
*注**意:*這里邊涉及到dwm1000接口,io引腳名稱跟原工程保持一致,這樣可以減少大量的后期程序移植修改工作
按照上邊的接口,完成基本的硬件配置,這里主要有時鐘、SWD JTAG接口、systick使能系統滴答時鐘、UART1的初始化、timer4的初始化(為了實現硬件定時方便,這里使用了LL庫),SPI1和SPI2的初始化(兩者時鐘極性和相位根據dwm1000和oled進行不同設置)。
spi1(dwm1000)的初始化(不使用中斷):
spi2(OLED)的初始化:
其余gpio初始化:
其中,led1、led2、led3接口為完結led,key為用戶按鍵,保留。
timer4定時器初始化(不使用中斷)
因為要使用us級延時,所以,系統時鐘72Mhz,選擇了9分頻,1us計數值為8,ARR值等于8000的話,正好1ms。這里要兼顧us級延時的精度和最大延時的時間,ARR為16位,最大值0xFFFF。
可以根據延時值去動態修改預分頻和計數周期ARR的值,這樣既保證了延時精度有能時間ms級延時時間長度。
主要的細節如上,設置完成后,就可以單擊“GENERATE CODE”生成MDK工程代碼。
4 打開工程并添加官方驅動庫
打開上邊生成的工程目錄,復制原工程下的compiler、decadriver、platform文件夾,粘貼至目標工程的Drivers文件夾下,另外復制OLED相關驅動及API接口文件至oled_driver文件夾下。復制原工程下的examples文件夾粘貼至目標工程的core文件夾下。
最后的工程目錄結構如下:
在keil中,右鍵單擊工程名稱,選擇“manage project items”進行分組管理,添加如下的分組
并依次給每一個分組添加對應文件夾下的c文件。
oled見上圖
上圖可加可不加。
example分組下可以選擇一個或者多個*****_main.c(這個文件為每一個example的主要代碼文件)的文件,實際使用只用一個,后邊詳解。
5 include文件路徑添加
6 精確延時函數實現
這一步非必須,只要實現us級延時和ms級延時就可以了。
分別在src文件夾下和inc文件夾下新建delay.c文件和delay.h文件,文件代碼如下:
delay.c:
delay.h:
#ifndef __DELAY_H_ #define __DELAY_H_#include "main.h"void delay_init(void); void delay_ms(uint16_t nms); void delay_us(uint16_t nus);#endif/********************************End of File************************************/7 dwb接口函數修改
7.1 deca_spi.c
主要實現writetospi()函數和readfromspi()函數,同時修改局部優化等級指令。使用的都是HAL的庫函數,主要實現了stm32和dwm1000的spi接口讀寫數據通信接口函數,比較容易修改,再此不表。
7.2 port.c
7.2.1 portGetTickCnt()
保持原樣,在port_wakeup_dw1000_fast()函數中有用到。獲取的是systick中斷里更新的uwTick值。
7.2.2 usleep()延時函數
#pragma -O0 int usleep(uint32_t usec) {delay_us(usec);return 0; }這里其實需要注意數據越界問題,或者統一函數入口參數
7.2.3 Sleep()延時函數
__INLINE void Sleep(uint32_t x) {HAL_Delay(x); }這里同上,需要注意數據越界問題,或者統一函數入口參數
7.2.4 reset_DW1000()函數實現
幾乎不需要改動
7.2.5 setup_DW1000RSTnIRQ() 設置復位引腳工作模式
在main.h中添加如下宏定義,
#define DW_RESET_EXTI_IRQn EXTI3_IRQn修改setup_DW1000RSTnIRQ()為:
void setup_DW1000RSTnIRQ(int enable) {GPIO_InitTypeDef GPIO_InitStruct;if(enable) /* 1 中斷模式*/{// Enable GPIO used as DECA RESET for interruptGPIO_InitStruct.Pin = DW_RESET_Pin;GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(DW_RESET_GPIO_Port, &GPIO_InitStruct);HAL_NVIC_EnableIRQ(DW_RESET_EXTI_IRQn); //pin #0 -> EXTI #0HAL_NVIC_SetPriority(DW_RESET_EXTI_IRQn, 3, 0);}else /* 0 gpio模式*/{HAL_NVIC_DisableIRQ(DW_IRQn_EXTI_IRQn); //pin #0 -> EXTI #0//put the pin back to tri-state ... as//output open-drain (not active)GPIO_InitStruct.Pin = DW_RESET_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(DW_RESET_GPIO_Port, &GPIO_InitStruct);HAL_GPIO_WritePin(DW_RESET_GPIO_Port, DW_RESET_Pin, GPIO_PIN_SET);} }7.2.6 port_wakeup_dw1000_fast()函數,不需要修改
7.2.7 添加對應的example c文件到工程,并添加相應的宏定義
原工程examples文件夾下,每一個示例程序都在一個子文件夾下,本例中借ex_05b_main.c來進行使用說明。
添加ex_05b_main.c到工程中,然后點魔術棒,打開工程的options選項,在C/C++一欄的define中輸入EX_05B_DEF 。進行預定義,因為整個x_05b_main.c文件內的內容都在如下的宏定義范圍內。
在main.c中main()函數之前加入如下語句:
extern int dw_main ( void ); //聲明外部函數引用在main()中的while(1)語句上方調用dw_main()函數。
7.2.8 其它工作
移植部分告一段落,剩下的工作就是要把原工程中未使用的函數,比如usb部分的代碼刪掉、lcd部分的代碼,需要在輸出顯示的地方使用oled相關函數替換或者使用printf來輸出到上位機顯示,剩下的代碼刪掉。以及未使用的led接口函數、按鍵接口函數等刪掉或者暫時注釋掉即可。
8 代碼分析及調試
int dw_main(void) {/* Display application name on LCD. */printf(APP_NAME);printf("\r\n");dwt_spicswakeup ( dummy_buffer, DUMMY_BUFFER_LEN ); /* Reset and initialise DW1000.* For initialisation, DW1000 clocks must be temporarily set to crystal speed. After initialisation SPI rate can be increased for optimum* performance. */reset_DW1000(); /* Target specific drive of RSTn line into DW1000 low for a period. */port_set_dw1000_slowrate();if (dwt_initialise(DWT_LOADUCODE) == DWT_ERROR){printf("INIT FAILED\r\n");while (1){ };}port_set_dw1000_fastrate();每一個示例源文件中,dw_main()函數的前幾行都是基本一樣的,同時每一條指令都有英文解釋,比較容易理解。dwt_initialise()中調用了dwt_readdevid()來獲取dwm1000的ID號,為uint32位整數,可以使用這個函數來判斷stm32和dwm1000通訊是否正常,
dwt_spicswakeup ( dummy_buffer, DUMMY_BUFFER_LEN ); reset_DW1000(); /* Target specific drive of RSTn line into DW1000 low for a period. */port_set_dw1000_slowrate();uint32 dwt_ID=0;dwt_ID = dwt_readdevid();printf("ID:%lx\r\n",dwt_ID);如果上位機收到的ID等于0xDECA0130,那么恭喜你,萬里長征一大步就完成了,剩下的就愉快的寫代碼吧。
總結
以上是生活随笔為你收集整理的STM32+DWM1000开发uwb测距系列教程之二:源码分析及源码移植(基于STM32 cubemx+keil MDK)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 删除进程 linux,linux批量删除
- 下一篇: 代码:小波包分解与重构、小波包能量特征提