目录

  • *资源下载
  • 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连接硬件接口差异表:

序号 stm32f105 dwm1000接口 stm32f103rbt6
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:

#include "delay.h"static uint8_t   fac_us = 8; //us延时倍乘数
static uint16_t fac_ms = 8000; //ms延时倍乘数/*******************************************************************************
** 函数名称: delay_init
** 功能描述: 延时初始化程序,暂时未使用,使用cubeMX生成的通用定时器初始化函数
********************************************************************************/
void delay_init()
{}/*******************************************************************************
** 函数名称: delay
** 功能描述: 用于通用定时器延时时间函数,传入参数为定时器预装值,定时器向上计数
** 参数说明: delay_t: [输入/出]
** 返回说明: None
** 创建人员: wht
** 创建日期: 2020-05-04
********************************************************************************/
static void delay(uint16_t delay_t)
{uint16_t temp;TIM4->ARR = delay_t; //时间加载TIM4->CNT = 0;       //清空计数器TIM4->SR = 0;      //清空状态TIM4->CR1 = TIM_CR1_CEN; //使能定时器do{temp = TIM4->SR ;}while(!(temp & LL_TIM_SR_UIF)); //等待时间到达TIM4->CR1 &= ~TIM_CR1_CEN;     //关闭TIM4->CNT = 0X00;       //清空
}
/*******************************************************************************
** 函数名称: delay_us
** 功能描述: 延时us函数,延时小于等于8191(系统72MHZ时钟,9分频,1us计数值为8)
** 参数说明: nus: [输入/出]
** 返回说明: None
** 创建人员: wht
** 创建日期: 2020-05-04
********************************************************************************/
void delay_us(uint16_t nus)
{delay(nus * fac_us);
}/*******************************************************************************
** 函数名称: delay_ms
** 功能描述: 延时ms函数,延时小于等于8ms时使用通用定时器,大于8ms时使用systick
** 参数说明: nms: [输入/出]
** 返回说明: None
** 创建人员: wht
** 创建日期: 2020-05-04
********************************************************************************/
void delay_ms(uint16_t nms)
{if(nms > 8)HAL_Delay(nms);else{delay(nms * fac_ms);}
}
/********************************End of File************************************/

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文件内的内容都在如下的宏定义范围内。

#ifdef EX_05B_DEF
...
...
...
#endif

在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)相关推荐

  1. 乐鑫代理-启明云端分享ESP32系列教程之二:Linux搭建esp-idf环境

    提示:此教程依据乐鑫官方的ESP32入门教程总结而来,仅供个人参考学学习,如有错误,欢迎批评指正.乐鑫官方参考: 乐鑫官方文档地址 1.搭建esp-idf环境 1.1安装虚拟机与Ubuntu 未安装请 ...

  2. React开发chrome插件系列教程之chrome插件基本介绍

    文章目录 chrome插件能干什么 chrome插件的版本 chrome插件的浏览器支持 chrome插件的功能组成 manifest.json popup content script backgr ...

  3. SAP系统和微信集成的系列教程之二:如何通过微信公众号消费API

    本系列的英文版Jerry写作于2017年,这个教程总共包含十篇文章,发表在SAP社区上. 系列目录 (1) 微信开发环境的搭建 (2) 如何通过微信公众号消费API(本文) (3) 微信用户关注公众号 ...

  4. RabbitMQ系列教程之二:工作队列(Work Queues)

    今天开始RabbitMQ教程的第二讲,废话不多说,直接进入话题.   (使用.NET 客户端 进行事例演示) 在第一个教程中,我们编写了一个从命名队列中发送和接收消息的程序.在本教程中,我们将创建一个 ...

  5. NVIDIA Jetson TX1 系列开发教程之二:刷机与开发前准备

    NVIDIA Jetson TX1 系列开发教程之二:刷机与开发前准备 转载请注明作者和出处:http://blog.csdn.net/u011475210 嵌入式平台:NVIDIA Jetson T ...

  6. 10 51单片机汇编:让定时器产生中断(使用Keil模拟器)[系列教程之10]

    10 51单片机汇编:让定时器产生中断(使用Keil模拟器)[系列教程之10] 该系列主仓库地址:https://gitee.com/langcai1943/8051-from-boot-to-app ...

  7. 一步一步实现STM32-FOTA系列教程之FLASH静态区读写

    一步一步实现STM32-FOTA系列教程之FLASH静态区读写 文章系列链接 <一步一步实现STM32-FOTA系列教程之bin文件生成> <一步一步实现STM32-FOTA系列教程 ...

  8. java jdbc 教程_java JDBC系列教程之JDBC类的简析与JDBC的基础操作

    什么是JDBC? 概念:JAVA Database Connectivity Javas数据库连接,Java语言操作数据库接口,然后由各个数据库厂商去实现这个接口,提供数据库驱动java包,我们可以使 ...

  9. 推荐系统系列教程之十:协同过滤中的相似度计算方法有哪些?

    编者按:之前推出了<推荐系统系列教程>,反响不错,前面已经推出了九期,今天按约推出第十期:协同过滤中的相似度计量方法有哪些?希望朋友们多点"在看",多多转发,我会在&q ...

  10. 【重磅推出】推荐系统系列教程之九:解密“看了又看”和“买了又买”(Item-Based)...

    编者按:之前推出了<推荐系统系列教程>,反响不错,前面已经推出了八期,今天按约推出第九期:解密"看了又看"和"买了又买"(基于物品的协同过滤).希望 ...

最新文章

  1. HB限时领Deponia: The Complete Journey
  2. python下载的库要放到哪里-Python下载url并保存文件的三种方法
  3. C++中基类的析构函数为什么要用virtual虚析构函数
  4. Autofac 一个使用Demo
  5. Linux高可用性方案之Heartbeat的CRM节点得分计算(原创)
  6. JavaScript中的一些细节
  7. SAP Spartacus里如何禁掉默认的css style
  8. PDH光端机的作用及其特点
  9. Android中设置输入法为数字输入
  10. 深源恒际医疗票据OCR落地九省市 服务范围覆盖过半市场
  11. 3分钟创建一个游戏类容器应用【华为云分享】
  12. 金蝶KIS15.1专业版注册流程和企业认证流程
  13. 呕心推荐深入浅出区块链技术,系统学习区块链技术网站-登链社区
  14. Excel:用VBA添加分页符
  15. java timezone_详解Java中的时区类TimeZone的用法
  16. java BMI体重测量代码
  17. 【游戏客户端】制作节奏大师Like音游(上)
  18. react + zarm 实现账单列表展示页
  19. 【科研】浅学Cross-attention?
  20. 贝叶斯 定理_贝叶斯定理:批判性思维框架

热门文章

  1. 【activiti6】设计器的前后端集成与汉化
  2. python 异步定时任务
  3. (转)用 Asterisk 搭建自己的免费 VoIP 服务器
  4. 机顶盒网络包获取方式
  5. 【Kilav】数据库知识点速通 其二
  6. 好程序员Java教程分享javaweb框架
  7. 看拉扎维《模拟CMOS集成电路设计》的一些总结和思考(十三)——非线性与不匹配
  8. SubSonic学习(二)
  9. SCM供应链管理系统实施困难及解决方案
  10. centos是什么操作系统