系列文章 〇、准备工作

之前的STM32L4小熊派开发板用于发送数据,这里我们使用一个STM32G070RB的板子连接DW1000官方评估板,作为数据接收端,硬件连接方式和STM32CubeMX工程配置生成请参考第二篇文章。

这里DW1000评估板和STM32G0开发板的连接情况如下表:

DW1000评估板引脚名 STM32G0引脚
RST PB3(输出)(评估板没有,预留出来这个引脚)
WAKEUP PB2(输出)
IRQ PB3(EXTI输入)
SPI_MISO PC2(SPI2_MISO)
SPI_MOSI PC3(SPI2_MOSI)
SPI_SCK PB13(SPI2_SCK)
SPI_CSn PB12(SPI2_NSS)

实物如下图:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_dw1000
接下来参考第二篇文章,使用STM32CubeMX配置SPI2、USART1、以及额外的两个GPIO,并测试是否可以正确读取DeviceID。

DW1000开发笔记(五)DW1000使用轮询方式接收数据_DW1000_02
测试无误后,参考第三篇文章,将之前移植的HAL库通用驱动复制过来,复制过来之后,如图将驱动添加到MDK工程中,将头文件路径添加到MDK中:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_STM32_03
接着需要在dw1000.h中配置控制引脚号,并根据板子实际的时钟主频来修改SPI配置高速率和低速率的函数,这里STM32G0T0RB的主频为64Mhz,SPI低速率最大3Mhz,所以低速率分频系数应该为32分频,SPI高速率最大20Mhz,所以高速率分频系数为4,综上所述,改为如下配置:

#ifndef _DW1000_H_
#define _DW1000_H_

#include "stm32g0xx_hal.h"

/*
	CS <-------------  PB12
	RST <------------- PB4
	WAKEUP <---------- PB2
*/
#define DW1000_CS_PORT			GPIOB
#define DW1000_CS_PIN			GPIO_PIN_12
#define DW1000_RST_PORT			GPIOB
#define DW1000_RST_PIN			GPIO_PIN_4
#define DW1000_WAKEUP_PORT	    GPIOB
#define DW10000_WAKEUP_PIN	    GPIO_PIN_2

/*
	IRQ --------------> PB3
*/
#define DW1000_IRQn_TYPE		EXTI2_3_IRQn
#define DW1000_IRQ_PORT		    GPIOB
#define DW1000_IRQ_PIN			GPIO_PIN_3

/*
	SPI Interface <---> SPI2
	SPI_CS <----------> PB12
	SPI_CLK <---------> PB13
	SPI_MISO <--------> PC2
	SPI_MOSI <--------> PC3
*/
extern SPI_HandleTypeDef hspi2;
#define DW1000_SPI_Handle hspi2

/*
    SPI rate = HCLK / SPI_BAUDRATEPRESCALE
    SPI low rate must less than 3Mhz
    SPI High rate must less than 20Mhz
*/
#define SPI_LOW_RATE    SPI_BAUDRATEPRESCALER_32
#define SPI_HIGH_RATE   SPI_BAUDRATEPRESCALER_4

void reset_DW1000(void);
void spi_set_rate_low(void);
void spi_set_rate_high(void);

#endif /* _DW1000_H_ */

之后参考第三篇文章,测试使用驱动初始化DW1000,测试结果如下:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_dw1000_04

一、移植官方示例

1. 复制官方示例文件

将官方驱动库中example下的第二个示例移植过来:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_STM32_05

复制到之前移植的STM32CubeMX生成的工程文件中,并重命名文件为simple_rx_example.c:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_UWB_06

将其添加到MDK工程中:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_HAL_07

2. 修改官方示例文件

① 修改替换头文件:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_dw1000_08

② 修改函数名,修改打印和延时函数:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_dw1000_09
添加打印信息:

/* hex dump */
printf("Recv Frame, frame_len is %d, frame data:\r\n", frame_len);
for (i = 0; i < frame_len; i++)
{
    printf("%02x ", rx_buffer[i]);
}
printf("\r\n");

DW1000开发笔记(五)DW1000使用轮询方式接收数据_UWB_10
修改完成。

3. 调用示例代码

修改main.c,移除我们之前自己添加的所有测试代码。

接着先引入外部定义:

/* USER CODE BEGIN PFP */
extern int example_application_entry(void);
/* USER CODE END PFP */

然后在main函数中调用:

/* USER CODE BEGIN 2 */
printf("DW1000 UWB ic port on STM32G0 board By Mculover666\r\n");

example_application_entry();
 
/* USER CODE END 2 */

需要注意:

此函数需要在GPIO、USART、SPI外设初始化完成之后调用
此函数中已经包含while(1)循环,所以此函数之后的代码无效

4. 移植结果

编译、下载程序,在串口助手中查看打印日志:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_dw1000_11
此时在等待接收数据,将第四节中完成的数据发送板上电,即可接收到发送板发送的数据:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_HAL_12
示例程序移植成功。

二、示例程序分析

1. DW1000初始化API

和第四篇文章相同。

2. DW1000配置API(重点)

和第四篇文章相同。

3. DW1000使用轮询方式接收数据流程

3.1. 轮询接收流程

DW1000开发笔记(五)DW1000使用轮询方式接收数据_DW1000_13

3.2. 接收使能API

该API的定义在deca_device_api.h中,实现在deca_device.c中,其原型如下:

int dwt_rxenable(int mode);

该函数使能DW1000接收数据

入参只有一个,表示接收数据模式,意义如下表:

意义
0 立即使能接收
1 设置延迟接收
3
4

这些值都有对应的宏定义:

#define DWT_START_RX_IMMEDIATE  0
#define DWT_START_RX_DELAYED    1    // Set up delayed RX, if "late" error triggers, then the RX will be enabled immediately
#define DWT_IDLE_ON_DLY_ERR     2    // If delayed RX failed due to "late" error then if this
                                     // flag is set the RX will not be re-enabled immediately, and device will be in IDLE when function exits
#define DWT_NO_SYNC_PTRS        4    // Do not try to sync IC side and Host side buffer pointers when enabling RX. This is used to perform manual RX
                                     // re-enabling when receiving a frame in double buffer mode.

返回值也有两个:

返回值 意义
DWT_SUCCESS 使能成功
DWT_ERROR 使能错误(如果延迟时间已过,则延迟接收失败)

3.3. 查询中断标志寄存器

while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS))  { };

dwt_read32bitreg用来读取DW1000中一个32位的寄存器,SYS_STATUS_ID表示系统状态寄存器(System event Status Register)的ID:

#define SYS_STATUS_ID           0x0F            /* System event Status Register */

SYS_STATUS_RXFCG表示该寄存器中的RXFCG位,该位在接收到一帧数据成功后会被设置为1:

#define SYS_STATUS_RXFCG        0x00004000UL    /* Receiver FCS Good */

SYS_STATUS_RXFCE表示该寄存器中的RXFCE,该位在接收一帧数据错误后会被设置为1:

#define SYS_STATUS_RXFCE        0x00008000UL    /* Receiver FCS Error */

DW1000开发笔记(五)DW1000使用轮询方式接收数据_STM32_14

查询到这两位被设置之后,意味着接收到数据,但成功还是失败未知,进一步进行判断,使用完毕后,向对应的位写1,清空该位:

if (status_reg & SYS_STATUS_RXFCG) {
	//接收成功
	
	/* Clear good RX frame event in the DW1000 status register. */
    dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG);
} else {
	//接收失败

	/* Clear RX error events in the DW1000 status register. */
    dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
}

3.4. 读取接收的帧长度

判断到接收成功后,读取接收帧信息寄存器( RX_FINFO 寄存器),获取帧的信息:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_dw1000_15
这两个数据域的作用如下:
DW1000开发笔记(五)DW1000使用轮询方式接收数据_DW1000_16
这里读取的时候有三种读取模式:

  • 只读取帧长度
  • 只读取帧长度扩展
  • 同时读取帧长度和帧长度扩展
#define RX_FINFO_RXFLEN_MASK    0x0000007FUL    /* Receive Frame Length (0 to 127) */
#define RX_FINFO_RXFLE_MASK     0x00000380UL    /* Receive Frame Length Extension (0 to 7)<<7 */
#define RX_FINFO_RXFL_MASK_1023 0x000003FFUL    /* Receive Frame Length Extension (0 to 1023) */

3.5. 读取接收的帧内容API

该API的定义在deca_device_api.h中,实现在deca_device.c中,其原型如下:


/*! ------------------------------------------------------------------------------------------------------------------
 * @fn dwt_readrxdata()
 *
 * @brief This is used to read the data from the RX buffer, from an offset location give by offset parameter
 *
 * input parameters
 * @param buffer - the buffer into which the data will be read
 * @param length - the length of data to read (in bytes)
 * @param rxBufferOffset - the offset in the rx buffer from which to read the data
 *
 * output parameters
 *
 * no return value
 */
void dwt_readrxdata(uint8 *buffer, uint16 length, uint16 rxBufferOffset);

该函数从DW1000接收缓冲区中读取接收的帧的内容

入参有三个,意义如下表:

意义
buffer 用于存储读取的内容
length 读取的数据的长度(一般为上一步获取到的帧长度)
rxBufferOffset 读取开始时在接收缓冲区中的偏移量

至此,DW1000接收示例代码移植、学习完成。