网站建设的技术方案模板下载,应用商店下载安装,wordpress全自动发布,自己设置网站怎么做【独家框架】UWB DWM1000 开源项目框架 - 基础知识 51uwb.cn [重要更新]51uwb_base code中断更新 - 基础知识 51uwb.cn 【软件资料】BP50 套件新框架定位代码实现 - 基础知识 51uwb.cn 【开源项目】纯Python TWR算法UWB上位机 - 基础知识 51uwb.cn 上位机软件通信协议更改说明 … 【独家框架】UWB DWM1000 开源项目框架 - 基础知识 51uwb.cn [重要更新]51uwb_base code中断更新 - 基础知识 51uwb.cn 【软件资料】BP50 套件新框架定位代码实现 - 基础知识 51uwb.cn 【开源项目】纯Python TWR算法UWB上位机 - 基础知识 51uwb.cn 上位机软件通信协议更改说明 - 基础知识 51uwb.cn Bphero-UWB 基站0 和 电脑串口数据格式定义 - 基础知识 51uwb.cn DWM1000中断方式接收信息 代码参考 - 基础知识 51uwb.cn 配置STM32 中断引脚 – DWM1000 IRQ 【STM32 基础知识】 - 基础知识 51uwb.cn DWM1000 自动应答 — 代码实现 - 基础知识 51uwb.cn 基站如何传输GPS数据给标签呢要怎么更改发送帧的内容阿 - 问答社区 51uwb.cn DWM1000 帧过滤代码实现 - tuzhuke - 博客园 (cnblogs.com) [新人福利]UWB定位模块PCB及原理图 - 基础知识 51uwb.cn STM32程序移植方法【STM32 基础知识】 - 基础知识 51uwb.cn 求助 DWM1000 谁有信号质量 和功率代码和计算方式 - 问答社区 51uwb.cn UWB怎么校准 - 基础知识 51uwb.cn 关于帧过滤问题请大神解释一下非常感谢 - 基础知识 51uwb.cn dw1000帧过滤问题 - 基础知识 51uwb.cn DWM1000 帧过滤功能实现 - 基础知识 51uwb.cn 基站0串口数据刷新慢解决方案【代码优化】 - 基础知识 51uwb.cn UWB定位上位机增加导出数据功能 - 问答社区 51uwb.cn 上位机软件 代码注释 - 问答社区 51uwb.cn UWB标签显示坐标的两种思路 - 基础知识 51uwb.cn DW1000的移动标签能否自己解算位置 - 问答社区 51uwb.cn bp hero的位置数据从哪里读取啊 - 问答社区 51uwb.cn 求一个小车轨迹追踪上位机设计论文的上位机软件部分怎么写 - 问答社区 51uwb.cn 官方上位机修改 主窗体 名字方法 - 基础知识 51uwb.cn 卡尔曼滤波 RSSI信号强度 三边定位 BP-50 开发框架
概述 模块角色定义 在 bphero_uwb.h 文件中通过软件定义模块角色可以设置为基站RX节点或标签TX节点。通过注释或取消注释特定的宏定义来选择角色。如定义 RX_NODE 表示该模块为基站定义 TX_NODE 表示为标签。 //rx为基站tx为标签
#define RX_NODE // 基站
//#define TX_NODE //标签基站和标签的地址设置 基站的地址从 0x0001 开始且在部署完毕后应将地址为 0x0001 的基站连接到串口。标签的地址从 0x0005 开始确保标签和基站的地址不重叠。 //基站节点地址0x0001 0x0002 0x0003
//部署完毕基站0x0001 链接串口
//基站地址必须从0x0001开始
#ifdef RX_NODE#define SHORT_ADDR 0x0001
//#define LCD_ENABLE //没有液晶的时候把这个宏定义注释掉
#endif//标签和基站地址不能重叠
//标签节点地址 0x0005 0x0006 0x0007
#ifdef TX_NODE#define SHORT_ADDR 0x0005#define LCD_ENABLE //没有液晶的时候把这个宏定义注释掉
#endif多基站多标签的支持 通过修改 tx_main.c 文件来控制多标签和多基站的配置。对于多个基站只需修改 MAX_ANTHOR 宏定义。基站地址应从 0x0001 开始。SEPC_ADDRESS 定义为规0地址用于在规0后向基站 0x0001 发送距离信息。DEST_BEGIN_ADDR 和 DEST_END_ADDR 定义了基站的地址范围。对于多标签控制可以通过调整 MAX_FREQ_HZ定位频率和 MAX_TX_Node系统中最多标签节点数来进行配置。 //多基站控制/************************!!!重要宏定义******************************/
/****************多基站只需要修改MAX_ANTHOR即可***************************/
/*****************基站的地址必须是从0x0001 开始***************************/
#define MAX_ANTHOR 4
//anthor range
#define SEPC_ADDRESS 0x0000 //规0地址每次规0后向基站0x0001发送距离信息
#define DEST_BEGIN_ADDR 0x0001 //基站起始地址
#define DEST_END_ADDR DEST_BEGIN_ADDR MAX_ANTHOR - 1 //anthro address 0x001 0x002 0x003 for 2D ,0x0001 0x0002 0x0003 0x0004 for 3D//多标签控制
#define MAX_FREQ_HZ 10 //定位10HZ
#define MAX_TX_Node 2 //系统中实际存在最多标签节点技术支持 遇到使用中的问题可以通过51uwb.cn网站与开发团队联系交流。
mian.c
#include stm32f10x.h // 引入STM32F10x系列微控制器的头文件
#include stdio.h // 引入标准输入输出库
#include deca_device_api.h // 引入Decawave UWB设备的API定义
#include deca_regs.h // 引入Decawave设备的寄存器定义
#include deca_sleep.h // 引入Decawave设备的睡眠模式处理库
#include port.h // 引入与硬件端口相关的定义和函数
#include bphero_uwb.h // 引入与该UWB系统特定功能相关的头文件
//#include lcd_oled.h // 被注释掉的头文件可能用于OLED显示的库extern int rx_main(void); // 声明外部函数rx_main用于接收节点基站
extern int tx_main(void); // 声明外部函数tx_main用于发送节点标签int main(void)
{peripherals_init(); // 初始化微控制器的外围设备BPhero_UWB_Message_Init(); // 初始化UWB消息系统BPhero_UWB_Init(); // 初始化UWB系统#ifdef RX_NODErx_main(); // 如果定义了RX_NODE作为接收节点执行rx_main函数 基站
#endif#ifdef TX_NODEtx_main(); // 如果定义了TX_NODE作为发送节点执行tx_main函数 标签
#endif
}
这段代码是一个用于超宽带UWB技术的定位系统的主函数main部分。我将逐行解释其功能和作用 包含头文件 #include stm32f10x.h: 引入STM32F10x系列微控制器的头文件提供基础的硬件定义。#include stdio.h: 引入标准输入输出库用于基本的I/O操作。#include deca_device_api.h: 引入Decawave UWB设备的API定义。#include deca_regs.h: 引入Decawave设备的寄存器定义。#include deca_sleep.h: 引入Decawave设备的睡眠模式处理库。#include port.h: 引入与硬件端口相关的定义和函数。#include bphero_uwb.h: 引入与该UWB系统特定功能相关的头文件。//#include lcd_oled.h: 被注释掉的头文件可能是用于OLED显示的库但在此代码中未使用。 外部函数声明 extern int rx_main(void);: 声明外部函数rx_main这是作为接收节点基站的主要功能函数。extern int tx_main(void);: 声明外部函数tx_main这是作为发送节点标签的主要功能函数。 主函数main的定义 peripherals_init();: 初始化微控制器的外围设备。BPhero_UWB_Message_Init();: 初始化UWB消息系统可能是用于配置消息传输相关的参数。BPhero_UWB_Init();: 初始化UWB系统可能包括设置UWB设备的参数和状态。 条件编译指令 #ifdef RX_NODE: 这是一个预处理指令检查是否定义了RX_NODE。如果定义了意味着当前模块配置为接收节点基站。 rx_main();: 在基站模式下调用rx_main函数执行接收节点的主要操作。 #ifdef TX_NODE: 这是另一个预处理指令检查是否定义了TX_NODE。如果定义了意味着当前模块配置为发送节点标签。 tx_main();: 在标签模式下调用tx_main函数执行发送节点的主要操作。
总结来说这段代码是UWB系统主程序的入口根据预编译的宏定义来决定当前设备是作为基站还是标签并执行相应的初始化和主要功能函数。这种设计使得同一套代码可以用于不同的硬件配置只需改变宏定义即可切换角色。
bp_filter.c
#define Filter_N 5 // 定义系统中使用的滤波器数量为5
#define Filter_D 5 // 定义每个滤波器包含的数据数量为5
int Value_Buf[Filter_N][Filter_D] {0}; // 二维数组存储每个滤波器的数据点
int filter_index[Filter_N] {0}; // 数组记录每个滤波器的当前数据点索引// 定义滤波函数
int filter(int input, int fliter_idx )
{char count 0; // 计数器变量int sum 0, min, max; // 定义求和、最小值、最大值变量min max Value_Buf[fliter_idx][0]; // 初始化最小值和最大值为当前滤波器的第一个数据点的值// 如果输入值大于0更新数据缓冲区和索引if(input 0){Value_Buf[fliter_idx][filter_index[fliter_idx]]input;if(filter_index[fliter_idx] Filter_D) filter_index[fliter_idx] 0;}// 遍历当前滤波器的所有数据点for(count 0; count Filter_D; count){// 找出最大值和最小值if (Value_Buf[fliter_idx][count] max) max Value_Buf[fliter_idx][count];if (Value_Buf[fliter_idx][count] min) min Value_Buf[fliter_idx][count];// 计算总和sum Value_Buf[fliter_idx][count];}// 从总和中减去最大值和最小值sum sum - max - min;// 返回处理后的平均值排除了最大值和最小值return (int)(sum / (Filter_D - 2));
}
这段代码定义了一个简单的滤波器函数用于处理数字信号。代码的主要部分如下 宏定义 #define Filter_N 5: 定义系统中使用的滤波器数量这里是5个。#define Filter_D 5: 定义每个滤波器包含的数据数量这里是5个数据点。 全局变量定义 int Value_Buf[Filter_N][Filter_D] {0};: 定义一个二维数组用于存储每个滤波器的所有数据点。数组的大小由上面定义的 Filter_N 和 Filter_D 决定。int filter_index[Filter_N] {0};: 定义一个数组用于记录每个滤波器的当前数据点索引。 滤波函数 filter int filter(int input, int fliter_idx ): 这个函数接受一个输入值 input 和一个滤波器索引 fliter_idx。char count 0;: 定义一个计数器变量。int sum 0, min, max;: 定义用于计算的变量 sum、min 和 max。min max Value_Buf[fliter_idx][0];: 初始化 min 和 max 为当前滤波器的第一个数据点的值。if(input 0) {...}: 如果输入值大于0则将其添加到相应滤波器的数据缓冲区中并更新索引。for(count 0; count Filter_D; count) {...}: 遍历当前滤波器的所有数据点。 在这个循环中代码找出最大值和最小值并计算所有数据点的总和。 sum sum - max - min;: 从总和中减去找到的最大值和最小值。return (int)(sum / (Filter_D - 2));: 返回处理后的平均值排除了最大值和最小值。
这个滤波器基本上是一个简化版的中值滤波器它排除了每个样本集中的最大值和最小值然后计算剩余值的平均值。这种方法有助于减少由极端值或噪声引起的误差。
rx_main.c
/********************************************************************************* file Project/STM32F10x_StdPeriph_Template/main.c* author MCD Application Team* version V3.5.0* date 08-April-2011* brief Main program body******************************************************************************* attention** 本固件仅供指导之用旨在为客户提供有关其产品的编码信息以节省时间。* 因此STMicroelectronics 对于任何由于本固件内容和/或客户使用此编码信息而产生的任何直接、* 间接或后果性损害不承担任何责任。** h2centercopy; COPYRIGHT 2011 STMicroelectronics/center/h2*******************************************************************************/#include stm32f10x.h // 引入STM32F10x系列微控制器的头文件
#include stdio.h // 引入标准输入输出库
#include deca_device_api.h // 引入Decawave UWB设备的API定义
#include deca_regs.h // 引入Decawave设备的寄存器定义
#include deca_sleep.h // 引入Decawave设备的睡眠模式处理库
#include port.h // 引入与硬件端口相关的定义和函数
#include lcd_oled.h // 引入OLED显示屏的库
#include frame_header.h // 引入帧头文件
#include bp_filter.h // 引入滤波器头文件
#include common_header.h // 引入公共头文件
#include bphero_uwb.h // 引入与该UWB系统特定功能相关的头文件
#include dwm1000_timestamp.h // 引入与DW1000时间戳相关的头文件
#include kalman.h // 引入卡尔曼滤波算法的头文件static void Handle_TimeStamp(void); // 处理时间戳的函数声明
#define MAX_ANTHOR_NODE 40 // 定义最大的基站节点数为40
struct distance_struct // 定义距离结构体
{double rx_distance; // 接收距离struct time_timestamp tx_node; // 发送节点的时间戳struct time_timestamp rx_node; // 接收节点的时间戳bool present; // 标记是否存在int count; // 计数器
} bphero_distance[MAX_ANTHOR_NODE]; // 定义存储最大基站节点距离的数组typedef signed long long int64; // 定义64位有符号整型
typedef unsigned long long uint64; // 定义64位无符号整型
static uint64 poll_rx_ts; // 接收轮询时间戳
static uint64 resp_tx_ts; // 响应发送时间戳
static uint64 final_rx_ts; // 最终接收时间戳static uint64 poll_tx_ts; // 发送轮询时间戳
static uint64 resp_rx_ts; // 响应接收时间戳
static uint64 final_tx_ts; // 最终发送时间戳static srd_msg_dsss *msg_f; // 定义消息指针
static double tof; // 飞行时间
static double distance_temp 0; // 临时距离
static double distance[256] {0}; // 距离数组
extern dwt_config_t config; // 引入DW1000配置/* 私有函数 ---------------------------------------------------------*/
void Simple_Rx_Callback()
{uint32 status_reg 0, i 0; // 定义并初始化状态寄存器和循环变量uint32 poll_tx_ts, resp_rx_ts, final_tx_ts; // 定义时间戳变量uint32 poll_rx_ts_32, resp_tx_ts_32, final_rx_ts_32; // 定义32位时间戳变量double Ra, Rb, Da, Db; // 定义计算飞行时间所需的变量int64 tof_dtu; // 定义飞行时间单位变量char dist_str[16] {0}; // 定义距离字符串数组for (i 0; i FRAME_LEN_MAX; i ){rx_buffer[i] \0; // 清空接收缓冲区}dwt_enableframefilter(DWT_FF_RSVD_EN); // 启用帧过滤功能此处设置为禁用接收status_reg dwt_read32bitreg(SYS_STATUS_ID); // 读取系统状态寄存器if (status_reg SYS_STATUS_RXFCG) // 如果接收到有效的消息{frame_len dwt_read32bitreg(RX_FINFO_ID) RX_FINFO_RXFL_MASK_1023; // 获取帧长度if (frame_len FRAME_LEN_MAX){dwt_readrxdata(rx_buffer, frame_len, 0); // 读取接收到的数据msg_f (srd_msg_dsss*)rx_buffer; // 转换接收缓冲区为消息结构体msg_f_send.destAddr[0] msg_f-sourceAddr[0]; // 复制源地址作为目的地址msg_f_send.destAddr[1] msg_f-sourceAddr[1]; // 复制源地址作为目的地址msg_f_send.seqNum msg_f-seqNum; // 复制序列号switch(msg_f-messageData[0]) // 根据消息类型进行处理{case P: // 处理轮询消息msg_f_send.messageData[0]A; // 设置轮询应答消息int temp (int)(distance[msg_f_send.destAddr[0]]*100); // 将距离转换为厘米msg_f_send.messageData[1]temp/100;msg_f_send.messageData[2]temp%100;dwt_writetxdata(14, (uint8 *)msg_f_send, 0); // 写入帧数据dwt_writetxfctrl(14, 0); // 设置帧控制dwt_starttx(DWT_START_TX_IMMEDIATE); // 立即开始发送while (!(dwt_read32bitreg(SYS_STATUS_ID) SYS_STATUS_TXFRS)) { }; // 等待发送完成poll_rx_ts get_rx_timestamp_u64(); // 获取接收时间戳break;case F: // 处理最终消息/* 获取响应发送和最终接收的时间戳 */resp_tx_ts get_tx_timestamp_u64(); // 获取响应发送时间戳final_rx_ts get_rx_timestamp_u64(); // 获取最终接收时间戳/* 从最终消息中获取嵌入的时间戳 */final_msg_get_ts(msg_f-messageData[FINAL_MSG_POLL_TX_TS_IDX], poll_tx_ts); // 获取轮询发送时间戳final_msg_get_ts(msg_f-messageData[FINAL_MSG_RESP_RX_TS_IDX], resp_rx_ts); // 获取响应接收时间戳final_msg_get_ts(msg_f-messageData[FINAL_MSG_FINAL_TX_TS_IDX], final_tx_ts); // 获取最终发送时间戳/* 计算飞行时间ToF。即使时钟发生了回绕32位减法也能给出正确答案 */poll_rx_ts_32 (uint32)poll_rx_ts; // 将64位时间戳转换为32位resp_tx_ts_32 (uint32)resp_tx_ts; // 将64位时间戳转换为32位final_rx_ts_32 (uint32)final_rx_ts; // 将64位时间戳转换为32位Ra (double)(resp_rx_ts - poll_tx_ts); // 计算Ra值Rb (double)(final_rx_ts_32 - resp_tx_ts_32); // 计算Rb值Da (double)(final_tx_ts - resp_rx_ts); // 计算Da值Db (double)(resp_tx_ts_32 - poll_rx_ts_32); // 计算Db值tof_dtu (int64)((Ra * Rb - Da * Db) / (Ra Rb Da Db)); // 计算飞行时间单位tof tof_dtu * DWT_TIME_UNITS; // 将飞行时间单位转换为实际时间distance_temp tof * SPEED_OF_LIGHT; // 根据飞行时间计算距离distance[msg_f_send.destAddr[0]] distance_temp - dwt_getrangebias(config.chan,(float)distance_temp, config.prf); // 减去矫正系数得到实际距离#if 0 // 如果启用了卡尔曼滤波distance[msg_f_send.destAddr[0]] KalMan(distance[msg_f_send.destAddr[0]]); // 应用卡尔曼滤波#endif#if 0 // 为了加快测距频率基站尽量不要显示距离{sprintf(dist_str, an0:%3.2fm , distance[msg_f_send.destAddr[0]]); // 格式化距离字符串OLED_ShowString(0, 4, dist_str); // 在OLED显示屏上显示距离}#endifbreak;case M: // 处理其他类型的消息USART1WriteDataToBuffer(msg_f-messageData[1],16); // 将数据发送到电脑break;default:break;}}dwt_write32bitreg(SYS_STATUS_ID, (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR)); // 重设系统状态dwt_enableframefilter(DWT_FF_DATA_EN); // 重新启用数据帧过滤功能dwt_setrxtimeout(0); // 重新设置接收超时dwt_rxenable(0); // 重新启用接收功能}else{dwt_write32bitreg(SYS_STATUS_ID, (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR)); // 清除错误标志dwt_enableframefilter(DWT_FF_DATA_EN); // 重新启用数据帧过滤功能dwt_rxenable(0); // 重新启用接收功能}
}int rx_main(void)//接收函数
{OLED_ShowString(0,0, 51UWB Node); // 在OLED显示屏上显示“51UWB Node”OLED_ShowString(0,4, www.51uwb.cn); // 在OLED显示屏上显示网址“www.51uwb.cn”OLED_ShowString(0,2, Rx Node ); // 在OLED显示屏上显示“Rx Node”表明这是接收节点#if 0KalMan_Init(); // 初始化卡尔曼滤波器当前被注释不会执行#endifdwt_setrxtimeout(0); // 设置DW1000的接收超时时间为0禁用接收超时dwt_enableframefilter(DWT_FF_DATA_EN); // 启用DW1000的数据帧过滤功能dwt_rxenable(0); // 启用DW1000的接收功能// 设置接收回调函数bphero_setcallbacks(Simple_Rx_Callback); // 当接收到数据时调用Simple_Rx_Callback函数while (1) // 无限循环等待接收数据{}
}#ifdef USE_FULL_ASSERT
/*** brief 报告源文件名和发生assert_param错误的行号。* param file: 指向源文件名的指针* param line: assert_param错误行的行号* retval None*/
void assert_failed(uint8_t* file, uint32_t line)
{/* 用户可以添加自己的实现来报告文件名和行号,例如: printf(参数错误文件 %s 在第 %d 行\r\n, file, line) *//* 无限循环 */while (1){}
}
#endif/*** }*/
/*** }*/
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
这段代码是一个基于STM32F10x微控制器和Decawave的DW1000 UWB (Ultra-Wideband) 模块的定位系统程序。代码的主要组成部分如下
文件和作者信息
这部分包含了文件的基本信息例如文件名、作者、版本、日期和简要描述。
包含的头文件
包括STM32F10x系列微控制器的头文件和其他必要的库如Decawave的UWB API、寄存器定义、睡眠处理等。
定义和结构体
定义了最大基站节点数 (MAX_ANTHOR_NODE) 和 distance_struct 结构体用于存储与每个节点相关的距离信息和时间戳。
类型定义
定义了64位整型变量 int64 和 uint64。
全局变量
定义了一系列变量用于处理UWB通信中的时间戳和距离计算。
Simple_Rx_Callback 函数 这是一个回调函数用于处理接收到的UWB消息。它读取状态寄存器处理接收到的消息并根据消息类型执行不同的操作如处理距离测量和发送数据。 这段代码是Simple_Rx_Callback函数的实现它是UWB超宽带定位系统中用于处理接收到的消息的回调函数。下面详细解释这段代码 初始化和读取状态 uint32 status_reg 0, i 0;: 定义并初始化用于存储状态寄存器值的变量status_reg和循环变量i。初始化时间戳变量和其他所需的变量。for (i 0; i FRAME_LEN_MAX; i) { rx_buffer[i] \0; }: 清空接收缓冲区。 启用帧过滤器 dwt_enableframefilter(DWT_FF_RSVD_EN);: 启用帧过滤功能但在这里它被设置为禁用接收。 读取并处理接收到的消息 status_reg dwt_read32bitreg(SYS_STATUS_ID);: 读取系统状态寄存器。if (status_reg SYS_STATUS_RXFCG) { ... }: 如果接收到了有效的消息则进入此条件块。 处理接收到的帧 获取帧长度读取接收到的数据并根据接收到的消息类型执行不同的操作。操作包括处理轮询消息、最终消息并根据消息类型计算飞行时间ToF。 计算飞行时间ToF并更新距离 使用DW1000的时间戳来计算ToF进而计算出距离。 如果启用了卡尔曼滤波可以对距离值进行滤波处理。 这段代码是从一个UWBUltra-Wideband系统的接收回调函数中摘录的主要用于计算从一个设备到另一个设备的飞行时间ToFTime of Flight进而估计它们之间的距离。下面是对代码的详细解释 获取时间戳 resp_tx_ts get_tx_timestamp_u64();获取响应消息的发送时间戳。final_rx_ts get_rx_timestamp_u64();获取最终接收的时间戳。 从接收到的消息中提取时间戳 final_msg_get_ts(msg_f-messageData[FINAL_MSG_POLL_TX_TS_IDX], poll_tx_ts);从消息中提取轮询消息的发送时间戳。final_msg_get_ts(msg_f-messageData[FINAL_MSG_RESP_RX_TS_IDX], resp_rx_ts);提取响应消息的接收时间戳。final_msg_get_ts(msg_f-messageData[FINAL_MSG_FINAL_TX_TS_IDX], final_tx_ts);提取最终消息的发送时间戳。 计算飞行时间ToF 时间戳转换将64位的时间戳转换为32位以便进行计算。计算四个关键值Ra, Rb, Da, Db这些值基于不同的时间戳差来计算。tof_dtu (int64)((Ra * Rb - Da * Db) / (Ra Rb Da Db));使用这四个值来计算飞行时间单位ToF。 计算距离 tof tof_dtu * DWT_TIME_UNITS;将飞行时间单位转换为实际时间。distance_temp tof * SPEED_OF_LIGHT;使用光速乘以飞行时间来计算距离。distance[msg_f_send.destAddr[0]] distance_temp - dwt_getrangebias(config.chan, (float)distance_temp, config.prf);从计算出的距离中减去任何由硬件或配置引入的偏差。 可选的卡尔曼滤波 如果启用了卡尔曼滤波使用KalMan函数进一步处理距离数据。 可选的显示距离 如果需要在OLED显示屏上显示距离格式化距离字符串并显示。 这段代码的关键在于精确计算飞行时间这是UWB定位系统测距的核心。通过精确的时间戳测量和计算可以估计出发射器和接收器之间的距离。可选的卡尔曼滤波和显示功能提供了额外的数据处理和用户界面支持。 这段代码是一个条件编译语句用于控制是否应用卡尔曼滤波器Kalman Filter来处理UWB定位系统中的距离数据。下面是对代码的详细解释以及如何启用卡尔曼滤波 代码解释 条件编译指令 #if 0: 这是一个预处理指令用于条件编译。当表达式的值为0时编译器会忽略该块代码。在这个例子中#if 0意味着紧随其后的代码不会被编译。 卡尔曼滤波应用 distance[msg_f_send.destAddr[0]] KalMan(distance[msg_f_send.destAddr[0]]);: 这行代码是将卡尔曼滤波应用于计算出的距离上。KalMan函数可能是实现卡尔曼滤波算法的函数它接收原始距离作为输入并返回经过滤波处理的距离。 结束条件编译 #endif: 这标志着条件编译块的结束。 如何启用卡尔曼滤波 要启用这段代码中的卡尔曼滤波需要更改预处理指令#if 0为#if 1或其他非零值。这样做将使得编译器编译并包含这段代码。更改后的代码如下 #if 1 // 启用卡尔曼滤波
distance[msg_f_send.destAddr[0]] KalMan(distance[msg_f_send.destAddr[0]]);
#endif在进行此更改后编译并运行程序时卡尔曼滤波算法将被应用于处理距离数据。卡尔曼滤波是一种高效的算法用于减少测量中的随机噪声使得距离估计更加准确和稳定。它特别适合于处理动态系统中的数据如跟踪移动的对象。 处理特定类型的消息 根据接收到的消息类型如’P’, ‘F’, ‘M’进行不同的处理。 重新启用接收 在消息处理完毕后重新设置系统状态启用帧过滤和接收功能准备接收下一个消息。 错误处理 如果未接收到有效消息则清除错误标志并重新启用接收。 总体来说这个函数负责接收和处理来自其他UWB节点的消息包括计算距离处理不同类型的数据帧以及更新系统状态以准备接收下一条消息。这是UWB定位系统中接收节点功能的核心部分。
rx_main 函数 主要的接收函数。初始化OLED显示设置超时启用帧过滤器然后进入一个无限循环等待接收消息。 这段代码是rx_main函数的实现它是一个用于超宽带UWB定位系统的接收节点Rx Node的主要函数。下面详细解释这段代码 OLED 显示初始化 OLED_ShowString(0,0, 51UWB Node);: 在OLED显示屏上的第0行第0列位置显示字符串 51UWB Node。这可能是显示固定的标题或标识。OLED_ShowString(0,4, www.51uwb.cn);: 在第4行显示网址 www.51uwb.cn可能是制造商或项目的网站。OLED_ShowString(0,2, Rx Node );: 在第2行显示 Rx Node 表明这个节点是一个接收节点。 卡尔曼滤波初始化如果启用 #if 0: 这是一个条件编译语句当前设置为0意味着下面的代码不会被编译。如果将0改为1或其他非0值则下面的代码会被编译。KalMan_Init();: 初始化卡尔曼滤波器。卡尔曼滤波是一种有效的算法用于在存在噪声的情况下估计系统的状态。 DW1000 UWB模块的设置 dwt_setrxtimeout(0);: 设置DW1000模块的接收超时时间。这里设置为0表示禁用接收超时。dwt_enableframefilter(DWT_FF_DATA_EN);: 启用帧过滤器。这里启用的是数据帧过滤意味着模块将只处理数据帧。dwt_rxenable(0);: 启用接收功能。参数0可能表示立即启动接收或某种默认配置。 设置接收回调函数 bphero_setcallbacks(Simple_Rx_Callback);: 设置一个回调函数Simple_Rx_Callback当接收到数据时这个函数会被调用。 无限循环 while (1) { }: 这是一个无限循环使得程序持续运行并等待接收数据。在这个循环内部没有执行任何操作实际的数据处理逻辑是在回调函数Simple_Rx_Callback中实现的。 总体来说这个函数设置了接收节点的初步配置包括初始化显示屏配置UWB模块以及设置用于接收数据的回调函数。然后它进入一个无限循环等待并处理接收到的数据。
assert_failed 函数
用于调试目的当 USE_FULL_ASSERT 被定义时报告源文件名和发生assert_param错误的行号。
文件结束注释
表明文件属于STMicroelectronics的版权以及文件结束的标记。
这个程序主要用于基于UWB技术的距离测量和定位。它通过处理来自UWB模块的信号计算设备之间的时间差和距离。这对于需要精确定位的应用如室内导航、资产跟踪非常有用。程序中包含的不同部分是为了处理UWB信号、计算距离、显示信息以及处理错误情况。
tx_main.c
/********************************************************************************* file Project/STM32F10x_StdPeriph_Template/main.c* author MCD Application Team* version V3.5.0* date 08-April-2011* brief Main program body******************************************************************************* attention** 本固件仅供指导之用旨在为客户提供有关其产品的编码信息以节省时间。* 因此STMicroelectronics 对于任何由于本固件内容和/或客户使用此编码信息而产生的任何直接、* 间接或后果性损害不承担任何责任。** h2centercopy; COPYRIGHT 2011 STMicroelectronics/center/h2*******************************************************************************//* 包含的头文件 -----------------------------------------------------------*/
#include stm32f10x.h // 包含STM32F10x系列微控制器的头文件
#include stdio.h // 包含标准输入输出库
#include deca_device_api.h // 包含Decawave UWB设备的API定义
#include deca_regs.h // 包含Decawave设备的寄存器定义
#include deca_sleep.h // 包含Decawave设备的睡眠模式处理库
#include port.h // 包含与硬件端口相关的定义和函数
#include frame_header.h // 包含帧头文件
#include common_header.h // 包含公共头文件
#include bphero_uwb.h // 包含与该UWB系统特定功能相关的头文件
#include dwm1000_timestamp.h // 包含与DW1000时间戳相关的头文件static unsigned char distance_seqnum 0; // 距离测量的序列号// 定义全局变量和类型
static srd_msg_dsss *msg_f_recv; // 定义接收消息的指针
typedef signed long long int64; // 定义64位有符号整型
typedef unsigned long long uint64; // 定义64位无符号整型
typedef unsigned int uint32; // 定义32位无符号整型static uint64 poll_rx_ts; // 定义轮询接收时间戳
static uint64 resp_tx_ts; // 定义响应发送时间戳
static uint64 final_rx_ts; // 定义最终接收时间戳static uint64 poll_tx_ts; // 定义轮询发送时间戳
static uint64 resp_rx_ts; // 定义响应接收时间戳
static uint64 final_tx_ts; // 定义最终发送时间戳static void Send_Dis_To_Anthor1(void); // 发送距离信息到指定基站的函数/************************!!!重要宏定义******************************/
/****************多基站只需要修改MAX_ANTHOR即可***************************/
/*****************基站的地址必须是从0x0001 开始***************************/
#define MAX_ANTHOR 4 // 定义最大基站数量为4
#define SEPC_ADDRESS 0x0000 // 定义规0地址
#define DEST_BEGIN_ADDR 0x0001 // 定义基站起始地址
#define DEST_END_ADDR DEST_BEGIN_ADDR MAX_ANTHOR - 1 // 定义基站地址范围int Final_Distance[MAX_ANTHOR] {0}; // 存储从基站和标签之间的所有距离char dist_str[16] {0}; // 定义显示距离的字符串
unsigned long time_count 0; // 定义时间计数器
unsigned long Tag_receive_poll 0; // 标记是否接收到轮询
uint16 Dest_Address DEST_BEGIN_ADDR; // 定义目标地址/* 私有函数 ---------------------------------------------------------*/
void Tx_Simple_Rx_Callback()
{uint32 status_reg 0,i0; // 定义状态寄存器和计数变量uint32 final_tx_time; // 定义最终传输时间dwt_enableframefilter(DWT_FF_RSVD_EN);//禁用接收status_reg dwt_read32bitreg(SYS_STATUS_ID); // 读取系统状态寄存器if (status_reg SYS_STATUS_RXFCG) // 检查是否成功接收到帧{/* 已收到帧将其复制到本地缓冲区中。 */frame_len dwt_read32bitreg(RX_FINFO_ID) RX_FINFO_RXFL_MASK_1023; // 读取帧长度if (frame_len FRAME_LEN_MAX) // 检查帧长度是否有效{dwt_readrxdata(rx_buffer, frame_len, 0); // 读取接收到的帧msg_f_recv (srd_msg_dsss*)rx_buffer; // 转换接收缓冲区为消息格式msg_f_send.destAddr[0] msg_f_recv-sourceAddr[0]; // 设置发送消息的目标地址msg_f_send.destAddr[1] msg_f_recv-sourceAddr[1];msg_f_send.seqNum msg_f_recv-seqNum; // 设置发送消息的序列号switch(msg_f_recv-messageData[0]) // 根据收到的消息类型进行处理{case A: // 收到轮询确认消息{/* 检索轮询传输和响应接收时间戳。 */resp_rx_ts get_rx_timestamp_u64(); // 获取响应接收时间戳final_tx_time dwt_readsystimestamphi32() 0x17cdc00/80; // 设置最终传输时间dwt_setdelayedtrxtime(final_tx_time); // 设置延迟传输时间/* 最终传输时间戳是我们编程的传输时间加上TX天线延迟。 */final_tx_ts (((uint64)(final_tx_time 0xFFFFFFFE)) 8); // 计算最终传输时间戳msg_f_send.messageData[0]F; // 设置消息为最终消息/* 在最终消息中写入所有时间戳。 */final_msg_set_ts(msg_f_send.messageData[FINAL_MSG_POLL_TX_TS_IDX], poll_tx_ts); // 设置轮询传输时间戳final_msg_set_ts(msg_f_send.messageData[FINAL_MSG_RESP_RX_TS_IDX], resp_rx_ts); // 设置响应接收时间戳final_msg_set_ts(msg_f_send.messageData[FINAL_MSG_FINAL_TX_TS_IDX], final_tx_ts); // 设置最终传输时间戳dwt_writetxdata(25, (uint8 *)msg_f_send, 0) ; // 写入帧数据dwt_writetxfctrl(25, 0); // 设置传输控制dwt_starttx(DWT_START_TX_DELAYED); // 开始延迟传输// 计算并过滤最终距离Final_Distance[(msg_f_send.destAddr[1]8)|msg_f_send.destAddr[0] - 1] (msg_f_recv-messageData[1]*100 msg_f_recv-messageData[2]); // 计算厘米单位的最终距离Final_Distance[(msg_f_send.destAddr[1]8)|msg_f_send.destAddr[0] - 1] filter(Final_Distance[(msg_f_send.destAddr[1]8)|msg_f_send.destAddr[0] - 1],\(msg_f_send.destAddr[1]8)|msg_f_send.destAddr[0] - 1); // 过滤最终距离while (!(dwt_read32bitreg(SYS_STATUS_ID) SYS_STATUS_TXFRS)){ }; // 等待传输完成}break;default: // 默认情况不做处理break;}}}else // 如果没有成功接收到帧{if(!(status_reg SYS_STATUS_RXRFTO)) // 如果不是超时错误Tag_receive_poll 1; // 设置为数据帧干扰dwt_write32bitreg(SYS_STATUS_ID, (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR|SYS_STATUS_TXFRS)); // 清除错误状态}
}/************************标签发送数据给基站进行测距*****************************/
void BPhero_Distance_Measure_Specail_ANTHOR(void)
{msg_f_send.destAddr[0] (Dest_Address) 0xFF; // 设置消息的目标地址的低8位msg_f_send.destAddr[1] ((Dest_Address) 8) 0xFF; // 设置消息的目标地址的高8位msg_f_send.seqNum distance_seqnum; // 设置消息的序列号msg_f_send.messageData[0] P; // 设置消息类型为轮询(Poll)消息dwt_writetxdata(12, (uint8 *)msg_f_send, 0); // 将12字节的消息数据写入UWB模块dwt_writetxfctrl(12, 0); // 设置帧控制为12字节dwt_starttx(DWT_START_TX_IMMEDIATE); // 立即开始发送数据while (!(dwt_read32bitreg(SYS_STATUS_ID) SYS_STATUS_TXFRS)) { }; // 等待直到消息发送完成dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG | SYS_STATUS_TXFRS); // 清除接收和发送完成标志poll_tx_ts get_tx_timestamp_u64(); // 获取并保存轮询消息的发送时间戳for (int i 0; i FRAME_LEN_MAX; i){rx_buffer[i] \0; // 清空接收缓冲区}dwt_enableframefilter(DWT_FF_DATA_EN); // 启用数据帧过滤dwt_setrxtimeout(RESP_RX_TIMEOUT_UUS * 10); // 设置接收超时时间dwt_rxenable(0); // 启用接收功能if (distance_seqnum 255)distance_seqnum 0; // 序列号递增达到255则重置为0
}/************************************************************************/
/************************************************************************/
/************************标签发送数据给基站******************************/
/********************************BEGIN***********************************/
void TAG_SendOut_Messge(void)
{if(Dest_Address SEPC_ADDRESS){Send_Dis_To_Anthor1(); // 如果目标地址是特定地址发送汇总距离信息到特定基站}else{BPhero_Distance_Measure_Specail_ANTHOR(); // 否则与特定基站进行距离测量}Dest_Address; // 递增目标地址准备下一次通信if(Dest_Address DEST_END_ADDR 1){Dest_Address SEPC_ADDRESS; // 如果目标地址超过最后一个基站地址重置为特定地址}
}/************************使用定时器周期性启动测距************************/
/*********************************END***********************************/
void TIM3_Int_Init_timeout(u16 arr)// 初始化TIM3作为定时器用于超时中断
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 定义时基结构体NVIC_InitTypeDef NVIC_InitStructure; // 定义NVIC初始化结构体TIM_Cmd(TIM3, DISABLE); // 关闭TIM3以进行设置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能TIM3的时钟arr arr*10-1; // 根据参数计算自动重载值TIM_TimeBaseStructure.TIM_Period arr; // 设置自动重载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler 7199; // 设置预分频值决定计时速度TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; // 设置时钟分割这里不分割TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; // 设置为向上计数模式TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); // 根据指定的参数初始化TIM3的时间基础设置TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); // 使能TIM3的更新中断NVIC_InitStructure.NVIC_IRQChannel TIM3_IRQn; // 设置NVIC中断通道为TIM3中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; // 设置抢占优先级为0NVIC_InitStructure.NVIC_IRQChannelSubPriority 5; // 设置子优先级为5NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; // 使能中断通道NVIC_Init(NVIC_InitStructure); // 根据设定初始化NVICTIM_Cmd(TIM3, ENABLE); // 最后使能TIM3开始计时
}void TIM3_IRQHandler(void) // TIM3定时器的中断服务函数
{if (TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) // 检查TIM3更新中断是否发生{TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // 清除TIM3更新中断标志准备下一次中断if( Tag_receive_poll 0) // 检查标志位确定是否存在数据冲突{dwt_forcetrxoff(); // 强制关闭接收/发送功能准备发送新的数据// 清除上次所有标志代码被注释掉了// dwt_write32bitreg(SYS_STATUS_ID, (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR|SYS_STATUS_TXFRS));TAG_SendOut_Messge(); // 发送定位信息if(Dest_Address DEST_BEGIN_ADDR){// 如果目的地址是起始地址则执行特定操作当前为空}time_count portGetTickCnt(); // 更新时间计数TIM3-ARR TIM3_ReLoad; // 重新设置定时器的自动重载值准备下一次定时}else // 如果存在数据冲突{TIM3-ARR TIM3_Delay_Step*((SHORT_ADDR%10)1); // 设置一个基于短地址的随机延时值用于动态避让Tag_receive_poll 0; // 重置数据冲突标志位}TIM_Cmd(TIM3, ENABLE); // 重新启用TIM3继续计数直到下一次中断}
}/************************使用定LCD 显示调试距离信息***********************/
/*********************调试完毕关闭LCD显示提高刷新频率*********************/
/********************************BEGIN***********************************/
static void LCD_Display_Distance(void) // 定义一个静态函数用于在LCD显示距离信息
{char dist_str[16] {0}; // 定义一个字符数组用于存储距离字符串if(Final_Distance[0]0) // 如果第一个距离值大于0{sprintf(dist_str, an1:%3.2fm , (float)Final_Distance[0]/100); // 格式化第一个距离值为字符串OLED_ShowString(0, 2,dist_str); // 在OLED上第2行显示第一个距离}if(Final_Distance[1]0) // 如果第二个距离值大于0{sprintf(dist_str, an2:%3.2fm , (float)Final_Distance[1]/100); // 格式化第二个距离值为字符串OLED_ShowString(0, 4,dist_str); // 在OLED上第4行显示第二个距离}if(Final_Distance[2]0) // 如果第三个距离值大于0{sprintf(dist_str, an3:%3.2fm , (float)Final_Distance[2]/100); // 格式化第三个距离值为字符串OLED_ShowString(0, 6,dist_str); // 在OLED上第6行显示第三个距离}
}static void Send_Dis_To_Anthor1(void)
{static int framenum 0 ; // 初始化静态变量framenum用于追踪消息帧数// 以下设置消息的目的地址为0x0001msg_f_send.destAddr[0] (0x0001) 0xFF; // 目标地址的低8位msg_f_send.destAddr[1] ((0x0001)8) 0xFF; // 目标地址的高8位msg_f_send.seqNum distance_seqnum; // 设置消息的序列号#if 0 // 计算智能车的角度部分当前被注释掉// 部分跟随小车代码计算角度float dis3_constans DISTANCE3;float cos 0;float angle 0 ;float dis1 (float)Final_Distance[0]/100; // 将距离1转换为米float dis2 (float)Final_Distance[1]/100; // 将距离2转换为米if(dis1 dis3_constans dis2 || dis2dis3_constans dis1){}cos (dis1*dis1 dis3_constans* dis3_constans - dis2*dis2)/(2*dis1*dis3_constans);angle acos(cos)*180/3.1415926;sprintf(dist_str, angle: %3.2f m, angle); // 将角度转换为字符串OLED_ShowString(0, 6, ); // 清除显示OLED_ShowString(0, 6,dist_str); // 显示角度if(dis1 1){if(angle 110){printf(turn right\r\n); // 角度大于110度时向右转}else if(angle 75){printf(turn left\r\n); // 角度小于75度时向左转}else{printf(forward\r\n); // 角度在这两者之间时直行}}else{printf(stay here\r\n); // 距离太近时停在原地}
#else// 将距离信息整个发送到地址为0x0001的基站数据包以‘M’标记msg_f_send.messageData[0]M; // 数据包的开始标记M{uint8 len 0; // 定义长度变量uint8 LOCATION_INFO_START_IDX 1; // 位置信息的起始索引msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] m; // 加入位置信息标记为mmsg_f_send.messageData[LOCATION_INFO_START_IDX (len)] r; // 加入位置信息标记为rmsg_f_send.messageData[LOCATION_INFO_START_IDX (len)] 0x02; // 加入位置信息指定格式或者类型标识为0x02msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] SHORT_ADDR;// 将TAG ID加入消息数据msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)(framenum0xFF); // 加入帧号的低8位msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)((framenum8)0xFF); // 加入帧号的高8位// 加入距离信息将每个距离分成两个字节进行发送msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)((Final_Distance[0]0?Final_Distance[0]:0xFFFF)0xFF); // 加入第一个距离的低8位msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)(((Final_Distance[0]0?Final_Distance[0]:0xFFFF) 8)0xFF); // 加入第一个距离的高8位msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)((Final_Distance[1]0?Final_Distance[1]:0xFFFF)0xFF); // 加入第二个距离的低8位msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)(((Final_Distance[1]0?Final_Distance[1]:0xFFFF) 8)0xFF); // 加入第二个距离的高8位msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)((Final_Distance[2]0?Final_Distance[2]:0xFFFF)0xFF); // 加入第三个距离的低8位msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)(((Final_Distance[2]0?Final_Distance[2]:0xFFFF) 8)0xFF); // 加入第三个距离的高8位// 如果有超过3个基站则包括第四个距离信息if(MAX_ANTHOR 3){msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)((Final_Distance[MAX_ANTHOR-1]0?Final_Distance[MAX_ANTHOR-1]:0xFFFF)0xFF);msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)(((Final_Distance[MAX_ANTHOR-1]0?Final_Distance[MAX_ANTHOR-1]:0xFFFF) 8)0xFF);}else{msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)((Final_Distance[0]0?Final_Distance[0]:0xFFFF)0xFF);msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] (uint8)(((Final_Distance[0]0?Final_Distance[0]:0xFFFF) 8)0xFF);}msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] \n; // 加入换行符msg_f_send.messageData[LOCATION_INFO_START_IDX (len)] \r; // 加入回车符}
#endif// 发送数据dwt_writetxdata(11 17,(uint8 *)msg_f_send, 0) ; // 写入帧数据dwt_writetxfctrl(11 17, 0); // 设置传输控制dwt_starttx(DWT_START_TX_IMMEDIATE); // 立即开始传输while (!(dwt_read32bitreg(SYS_STATUS_ID) SYS_STATUS_TXFRS)){ }; // 等待传输完成framenum; // 增加帧数LCD_Display_Distance(); // 显示距离信息
}int tx_main(void) // 定义tx_main函数作为主要的发送端程序
{OLED_ShowString(0,0, 51UWB Node); // 在OLED第0行显示“51UWB Node”作为设备标题sprintf(dist_str, Tx Node); // 将“Tx Node”字符串格式化到dist_str变量中表示这是一个发送节点OLED_ShowString(0,2,dist_str); // 在OLED第2行显示dist_str变量的内容即“Tx Node”OLED_ShowString(0,6, www.51uwb.cn); // 在OLED第6行显示网址“www.51uwb.cn”可能是产品或公司的网址bphero_setcallbacks(Tx_Simple_Rx_Callback); // 设置回调函数为Tx_Simple_Rx_Callback用于处理接收数据dwt_forcetrxoff(); // 强制关闭无线传输的接收和发送功能准备新的传输任务// 定时周期性发送数据TIM3_Int_Init_timeout(TIM3_ReLoad); // 初始化TIM3定时器设置定时周期为TIM3_ReLoadwhile(1) // 进入无限循环{// 循环体为空通常意味着程序的主要功能在中断或回调函数中实现}
}#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{// 断言失败处理函数// 以下是报告文件名和行号的代码...while (1){}
}
#endif/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
这段代码是一个较为完整的主程序用于基于STM32F10x微控制器和Decawave UWBUltra-Wideband模块的定位系统。程序的主要功能是通过UWB技术进行距离测量和定位。下面是对代码的详细解释
代码结构和功能 文件和作者信息 代码开头的注释部分包含文件描述、作者、版本、日期以及版权信息。 包含的头文件 #include 指令包含了所需的头文件如STM32F10x的库文件和Decawave UWB模块的API。 全局变量和类型定义 定义了用于存储时间戳、距离等信息的变量。定义了有符号和无符号的64位整数类型以及32位的无符号整数类型。 宏定义 MAX_ANTHOR 定义了基站的最大数量以及与之相关的地址范围。 距离测量相关的函数 Tx_Simple_Rx_Callback接收回调函数处理接收到的UWB消息并计算距离。 这段代码是Tx_Simple_Rx_Callback函数的实现用于处理超宽带UWB定位系统中发送节点接收到的消息。函数主要处理接收到的UWB消息计算时间戳以及发送回复消息。下面是对代码的详细解释 代码解释 初始化变量 uint32 status_reg 0, i 0;定义并初始化用于存储状态寄存器的变量status_reg和循环计数器i。uint32 final_tx_time;定义用于存储最终发送时间的变量。 禁用接收并读取状态 dwt_enableframefilter(DWT_FF_RSVD_EN);禁用接收功能启用帧过滤。status_reg dwt_read32bitreg(SYS_STATUS_ID);读取并存储系统状态寄存器的值。 处理接收到的帧 if (status_reg SYS_STATUS_RXFCG)检查是否成功接收到帧RXFCG标志。获取帧长度并从UWB模块的接收缓冲区读取数据。 解析接收到的消息并发送响应 switch(msg_f_recv-messageData[0])根据接收到的消息类型进行不同的处理。对于A轮询应答消息类型的消息执行以下操作 获取响应接收时间戳。计算并设置最终消息的发送时间。设置最终发送时间戳。准备并发送最终消息。计算并保存距离信息。使用滤波器处理距离数据。等待直到消息发送完成。 处理错误和超时 如果未接收到有效消息并且不是因为超时设置Tag_receive_poll标志表示可能的数据帧干扰。清除系统状态寄存器的相关标志准备接收下一个消息。 总结 这个函数是UWB系统中发送节点的关键部分负责接收来自其他节点的消息根据这些消息计算时间戳进而估算距离。它还处理了发送响应消息的逻辑以及对接收到的数据进行滤波处理以提高距离测量的准确性。此外函数还包含了处理潜在的通信干扰和错误的逻辑。 BPhero_Distance_Measure_Specail_ANTHOR用于与特定基站进行距离测量。 这段代码是BPhero_Distance_Measure_Specail_ANTHOR函数的实现它是超宽带UWB定位系统中用于与特定基站进行距离测量的函数。下面是对代码的详细解释 代码解释 设置目标地址 msg_f_send.destAddr[0] (Dest_Address) 0xFF;msg_f_send.destAddr[1] ((Dest_Address) 8) 0xFF;这两行代码设置消息的目标地址。Dest_Address变量表示目标基站的地址它被分解为两个字节。 配置消息内容 msg_f_send.seqNum distance_seqnum;设置消息的序列号。msg_f_send.messageData[0] P;设置消息类型为’P’代表轮询消息。 发送消息 dwt_writetxdata(12, (uint8 *)msg_f_send, 0);写入帧数据到UWB模块。dwt_writetxfctrl(12, 0);设置帧控制。dwt_starttx(DWT_START_TX_IMMEDIATE);立即开始发送数据。循环等待直到消息发送完成。 准备接收响应 poll_tx_ts get_tx_timestamp_u64();获取并保存轮询消息的发送时间戳。清空接收缓冲区。dwt_enableframefilter(DWT_FF_DATA_EN);启用数据帧过滤。dwt_setrxtimeout(RESP_RX_TIMEOUT_UUS * 10);设置接收超时。dwt_rxenable(0);启用接收功能。 更新序列号 if (distance_seqnum 255) distance_seqnum 0;递增序列号如果达到255则重置为0。 总结 这个函数是UWB系统中的关键部分负责向特定基站发送轮询消息用于测量从发送节点到该基站的距离。它设置了目标基站的地址配置了消息内容发送轮询消息并准备接收响应。该函数还处理了序列号的更新和接收超时的设置确保了系统的连续性和稳定性。 TAG_SendOut_Messge用于发送消息到基站进行距离测量。 这段代码是TAG_SendOut_Messge函数的实现用于超宽带UWB定位系统中标签节点向基站发送信息。该函数根据当前的目标地址决定是发送汇总的距离信息到特定基站还是与特定基站进行距离测量。下面是对代码的详细解释 代码解释 判断目标地址 if (Dest_Address SEPC_ADDRESS) { ... } else { ... }这里检查当前的目标地址Dest_Address是否等于特定的地址SEPC_ADDRESS。这个特定地址用于控制是否向一个特定基站发送距离汇总信息。 发送距离汇总信息或进行距离测量 Send_Dis_To_Anthor1();如果目标地址是特定地址则调用Send_Dis_To_Anthor1函数该函数负责将汇总的距离信息发送到特定基站。BPhero_Distance_Measure_Specail_ANTHOR();如果目标地址不是特定地址则调用BPhero_Distance_Measure_Specail_ANTHOR函数与特定基站进行距离测量。 更新目标地址 Dest_Address;递增目标地址以便下次发送消息到下一个基站。if (Dest_Address DEST_END_ADDR 1) { Dest_Address SEPC_ADDRESS; }如果目标地址超过了最后一个基站的地址就将其重置为特定地址SEPC_ADDRESS。这样可以确保标签节点轮流与所有基站通信。 总结 这个函数是UWB定位系统中标签节点的关键部分负责管理与不同基站的通信。它根据当前目标地址决定是发送汇总的距离信息还是进行距离测量并在所有基站间循环发送消息。这种机制允许系统有效地从多个基站收集距离数据用于后续的定位计算。 定时器初始化和中断处理函数 TIM3_Int_Init_timeout 和 TIM3_IRQHandler 用于定时发送距离测量请求。 这段代码是用于初始化STM32微控制器中的定时器3TIM3的函数主要用于设置定时器的基础配置并使其能够在指定的时间后产生中断。以下是该函数逐行解释 函数定义 void TIM3_Int_Init_timeout(u16 arr): 这是一个名为TIM3_Int_Init_timeout的函数它接受一个名为arr的参数这个参数与定时器超时时间有关。 函数内部代码 变量声明: TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;: 声明一个时基配置结构体。NVIC_InitTypeDef NVIC_InitStructure;: 声明一个嵌套向量中断控制器NVIC配置结构体。 禁用定时器: TIM_Cmd(TIM3, DISABLE);: 在配置之前先禁用TIM3。 时钟使能: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);: 使能TIM3的时钟。在STM32中外设如定时器都需要手动开启时钟才能工作。 设置计时周期: arr arr*10-1;: 根据传入的参数计算自动重装载寄存器的值。这里将时间乘以10可能是根据时钟频率和需要的时间分辨率来设定的。TIM_TimeBaseStructure.TIM_Period arr;: 设置定时器的周期即何时产生一个更新或中断事件。TIM_TimeBaseStructure.TIM_Prescaler 7199;: 设置预分频器的值。这里的值与时钟频率相关用于确定定时器的计时频率。TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1;: 设置时钟分割。这里设置为不分割。TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up;: 设置为向上计数模式。 初始化定时器: TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure);: 用前面设置的参数初始化TIM3的时间基础。 中断配置: TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );: 使能TIM3的更新中断当计时器溢出更新时发生。NVIC_InitStructure.NVIC_IRQChannel TIM3_IRQn;: 指定TIM3中断。NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0;: 设置中断的抢占优先级为0。NVIC_InitStructure.NVIC_IRQChannelSubPriority 5;: 设置中断的子优先级为5。NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;: 使能中断通道。NVIC_Init(NVIC_InitStructure);: 根据指定的参数初始化NVIC寄存器。 使能定时器: TIM_Cmd(TIM3, ENABLE);: 最后使能TIM3开始计时。 总结 这个函数通过配置STM32的定时器3来实现在给定的时间后产生一个中断的功能。它详细设置了定时器的工作周期、频率、计数模式以及中断的优先级和使能最终通过启用定时器来开始计时。这种类型的功能在需要精确计时的嵌入式系统应用中十分常见比如定时采样、定时响应等。 这段代码是STM32微控制器中TIM3定时器的中断处理函数。它主要用于在定时器达到预设时间时执行特定的任务。以下是该函数逐行的详细解释 函数定义 void TIM3_IRQHandler(void): 这是TIM3定时器的中断处理函数。 函数内部代码 中断事件检查: if (TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET): 这行代码检查TIM3的更新中断是否发生。如果发生表示定时器计数到达了预设值。 清除中断标志: TIM_ClearITPendingBit(TIM3, TIM_IT_Update);: 如果检测到更新中断该行代码将清除TIM3更新中断标志以便于定时器可以继续工作而不会被之前的中断影响。 根据标志位发送信息或进行延时: if( Tag_receive_poll 0): 检查标志位Tag_receive_poll如果为0表示没有数据冲突可以进行正常的定位信息发送。 dwt_forcetrxoff();: 强制关闭接收和发送准备发送新的消息。TAG_SendOut_Messge();: 调用函数发送定位消息。if(Dest_Address DEST_BEGIN_ADDR){}: 一个空的条件语句可能是预留给特定地址处理的代码。time_count portGetTickCnt();: 更新时间计数可能用于下次发送或延时。TIM3-ARR TIM3_ReLoad;: 重新加载定时器的计数值准备下一次中断。 else: 如果Tag_receive_poll不为0表示存在数据冲突。 TIM3-ARR TIM3_Delay_Step*((SHORT_ADDR%10)1);: 设置定时器的计数值为一个基于设备地址的随机延时作为动态避让机制。Tag_receive_poll 0;: 重置标志位为下一次中断准备。 重新启动定时器: TIM_Cmd(TIM3, ENABLE);: 再次启用TIM3继续计数直到下一次中断。 总结 这个中断处理函数用于处理定时器的更新事件。它根据是否存在数据冲突来决定是立即发送定位信息还是延时发送以避免冲突。此函数的操作确保了定时器能够在特定的时间点触发发送定位信息或进行必要的延时以优化通信和避免可能的数据干扰。 LCD显示和距离信息发送 LCD_Display_Distance 函数用于在LCD显示屏上显示距离信息。 这段代码定义了一个函数LCD_Display_Distance它的作用是在LCD屏幕上显示距离信息。以下是逐行解释 函数定义 static void LCD_Display_Distance(void): 定义一个静态函数仅在声明它的文件内部可见用于显示距离信息。 函数内部代码 变量声明: char dist_str[16] {0};: 定义一个字符数组dist_str用于存储将要显示的距离字符串初始化所有元素为0。 显示第一个距离an1: if(Final_Distance[0]0): 如果数组Final_Distance的第一个元素表示第一个距离大于0则执行以下代码。 sprintf(dist_str, an1:%3.2fm , (float)Final_Distance[0]/100);: 使用sprintf将第一个距离格式化为字符串并存储在dist_str中。距离转换为米单位假设Final_Distance中的值以厘米为单位。OLED_ShowString(0, 2,dist_str);: 调用OLED_ShowString函数在OLED屏幕的指定位置第0列第2行显示距离字符串。 显示第二个距离an2: if(Final_Distance[1]0): 如果数组Final_Distance的第二个元素表示第二个距离大于0则执行以下代码。 sprintf(dist_str, an2:%3.2fm , (float)Final_Distance[1]/100);: 格式化第二个距离并存储在dist_str中。OLED_ShowString(0, 4,dist_str);: 显示第二个距离字符串在OLED屏幕的第0列第4行。 显示第三个距离an3: if(Final_Distance[2]0): 如果数组Final_Distance的第三个元素表示第三个距离大于0则执行以下代码。 sprintf(dist_str, an3:%3.2fm , (float)Final_Distance[2]/100);: 格式化第三个距离并存储在dist_str中。OLED_ShowString(0, 6,dist_str);: 显示第三个距离字符串在OLED屏幕的第0列第6行。 总结 这个函数用于显示三个不同的距离值如果它们大于0在OLED屏幕上每个距离值显示在不同的行上。它使用sprintf函数来格式化距离值为字符串并调用OLED_ShowString来在OLED上显示这些字符串。这种显示功能通常用于实时更新和监控距离信息如在定位系统或距离感测设备中。 Send_Dis_To_Anthor1 用于将距离信息发送到特定的基站。 这段代码看起来是一个更大程序的一部分很可能用于无线通信或涉及智能车或机器人的跟踪系统。它用C语言编写涉及发送和处理位置或移动指令的数据包。以下是代码不同部分的详细解释 函数概览 函数名称 Send_Dis_To_Anthor1目的 这个函数似乎负责向另一个设备或基站发送距离或位置数据。 变量初始化 static int framenum 0;初始化了一个静态变量 framenum它可能用于跟踪发送的帧数或消息数。作为静态变量它在函数调用之间保持其值不变。 设置目标地址 代码设置了消息msg_f_send.destAddr的目的地址为 0x0001将16位地址分成两个8位部分并存储在数组中。 序列号 它将 distance_seqnum 分配给 msg_f_send.seqNum这可能是用于跟踪消息的序列号。 角度计算块被注释 用 #if 0 包围的这部分代码当前不活跃。如果它被激活它将根据距离dis1、dis2 和一个常数 dis3_constans使用三角函数计算角度。这个计算看来是为了决定智能车或类似设备转向或移向的方向。它使用 acos 函数来确定角度然后根据这个角度做出决策例如向右转、向左转、前进或停留。这部分还包括使用角度信息更新OLED显示。 活动数据包准备块 条件编译的另一部分#else 块准备了一个用于发送的数据包。它以 ‘M’ 标记并包含一系列可能与位置或距离有关的测量和标识符。它包括帧号、标签ID和距离Final_Distance[0] 到 Final_Distance[2]如果 MAX_ANTHOR 3 则条件性地包括第四个距离。距离作为16位值发送分成两个8位块。以换行符和回车符结束消息这通常用来表示文本或消息的行结束。 发送数据 调用 dwt_writetxdata 和 dwt_writetxfctrl 分别设置传输数据和控制然后调用 dwt_starttx 启动传输。代码在循环中等待直到传输完成通过检查系统状态寄存器的 SYS_STATUS_TXFRS 标志来判断。 传输后 framenum发送包后帧数增加为下一条消息做准备。LCD_Display_Distance()在最后调用这个函数可能是为了更新LCD显示最新的距离或状态信息虽然这个函数的具体内容没有包括在内。 注释和观察 代码看起来是实时定位系统RTLS相关的一部分可能涉及UWB超宽带标签用于跟踪和导航。使用 #if 0 是在开发或调试期间轻松切换代码块的一种方式。它通常不用于生产代码。错误处理、距离验证或如果传输失败会发生什么在这个代码片段中不明显。这些方面对于现实世界应用的健壮性至关重要。代码注释混合使用英文和看似中文表明它可能是双语开发环境或团队的一部分。 这段代码是特定应用的专门部分要完全理解其上下文需要更多关于它所针对的硬件和系统架构的细节。 主函数 tx_main 初始化OLED显示屏。 设置回调函数。 使用定时器周期性地发送距离测量请求。 这段代码定义了一个名为tx_main的函数看起来是用于初始化一个无线传输节点并设置其显示和传输行为。以下是逐行的详细解释 函数定义 int tx_main(void): 定义了一个名为tx_main的函数返回类型为int。 函数内部代码 OLED显示初始化信息: OLED_ShowString(0,0, 51UWB Node);: 在OLED显示屏的第0行第0列位置显示字符串 51UWB Node这似乎是设备的名称或标题。sprintf(dist_str, Tx Node);: 使用sprintf函数将字符串 Tx Node格式化到dist_str变量中表示这个设备是一个发送节点。OLED_ShowString(0,2,dist_str);: 在OLED显示屏的第2行显示dist_str的内容即 Tx Node。OLED_ShowString(0,6, www.51uwb.cn);: 在OLED显示屏的第6行显示网址 www.51uwb.cn可能是设备的官方网站或相关信息。 设置回调函数和关闭无线传输: bphero_setcallbacks(Tx_Simple_Rx_Callback);: 设置无线传输的回调函数为Tx_Simple_Rx_Callback这通常用于处理接收到的数据或状态更改。dwt_forcetrxoff();: 强制关闭无线设备的接收和发送功能准备开始新的发送任务。 初始化定时器并进入发送循环: TIM3_Int_Init_timeout(TIM3_ReLoad);: 调用TIM3_Int_Init_timeout函数初始化TIM3定时器TIM3_ReLoad可能是一个定义好的重装载值用于设定定时器的超时时间。while(1){}: 一个空的无限循环表示主要的工作可能在中断或回调函数中完成这里保持程序不退出等待事件。 总结 tx_main函数是一个无线传输节点的主要初始化和运行函数。它首先在OLED显示屏上显示节点的信息和状态然后设置数据接收时的回调处理并关闭当前的无线传输以准备新的传输任务。随后它初始化一个定时器用于周期性地发送数据最后进入一个等待状态等待定时器中断或接收数据。这样的设计模式在嵌入式系统和无线节点中很常见特别是在需要周期性或按需发送数据的应用中。 断言失败处理 assert_failed 函数提供了一种调试机制用于报告断言失败的详细信息。
总结
这个程序是一个UWB基于时间测距TDoATime Difference of Arrival的定位系统的示例。它涉及发送UWB信号、接收响应、计算飞行时间ToF和距离以及处理和显示这些数据。通过对多个基站的测距可以实现2D或3D定位。程序还包含了周期性发送测距请求的逻辑以及可选的LCD显示功能。
bphero_uwb.h
#ifndef BPHERO_UWB_H // 防止头文件重复引用
#define BPHERO_UWB_H#include frame_header.h // 包含帧头定义
#include common_header.h // 包含通用头文件定义// 定义设备角色为基站或标签
#define RX_NODE // 定义为基站
//#define TX_NODE // 定义为标签当前被注释// 基站地址配置地址从0x0001开始
#ifdef RX_NODE#define SHORT_ADDR 0x0001 // 为基站设置短地址
//#define LCD_ENABLE // 如果有LCD显示则定义此宏没有则注释
#endif// 标签地址配置确保与基站地址不重叠
#ifdef TX_NODE#define SHORT_ADDR 0x0005 // 为标签设置短地址#define LCD_ENABLE // 如果有LCD显示则定义此宏没有则注释
#endif// 外部变量声明
extern int psduLength ;
extern srd_msg_dsss msg_f_send ; // 定义16位地址的ranging消息帧// 常量定义
#ifndef SPEED_OF_LIGHT
#define SPEED_OF_LIGHT (299702547.0) // 空气中的光速单位m/s
#endif/* 接收帧的最大长度 */
#ifndef FRAME_LEN_MAX
#define FRAME_LEN_MAX 127
#endif/* 天线延迟相关常量定义 */
#ifndef TX_ANT_DLY
#define TX_ANT_DLY 0
#endif#ifndef RX_ANT_DLY
#define RX_ANT_DLY 32950
#endif/* 时间戳相关常量定义 */
#define FINAL_MSG_POLL_TX_TS_IDX 2
#define FINAL_MSG_RESP_RX_TS_IDX 6
#define FINAL_MSG_FINAL_TX_TS_IDX 10
#define FINAL_MSG_TS_LEN 4/* 微秒uus到设备时间单元dtu转换因子定义 */
#define UUS_TO_DWT_TIME 65536/* 帧间延迟定义 */
#define POLL_RX_TO_RESP_TX_DLY_UUS 2600
#define RESP_TX_TO_FINAL_RX_DLY_UUS 500
#define FINAL_RX_TIMEOUT_UUS 3300
#define POLL_TX_TO_RESP_RX_DLY_UUS 150
#define RESP_RX_TO_FINAL_TX_DLY_UUS 3000 //2700将会失败
#define RESP_RX_TIMEOUT_UUS 2700// 全局变量声明
extern uint8 rx_buffer[FRAME_LEN_MAX]; // 接收缓冲区
extern uint16 frame_len ; // 接收到的帧的长度
extern void BPhero_UWB_Message_Init(void); // UWB消息初始化函数声明
extern void BPhero_UWB_Init(void); // UWB设备初始化函数声明#endif // BPHERO_UWB_H 结束头文件保护
这段代码是一个C语言的头文件通常以.h结尾名为bphero_uwb.h。它是为UWB超宽带应用编写的提供了一系列的宏定义、变量声明和函数原型用于配置和操作UWB设备。以下是对该头文件中各部分的详细解释
头文件保护
#ifndef BPHERO_UWB_H 到 #endif: 这是一个常见的C语言技巧用于防止头文件内容被重复包含。如果BPHERO_UWB_H未定义则定义它并包含整个文件内容否则跳过内容。
包含的其他头文件
#include frame_header.h 和其他类似语句包含了其他相关的头文件这些文件中可能声明了与UWB设备操作相关的结构、常量或函数。
基站和标签定义
#define RX_NODE 和 #ifdef RX_NODE这部分代码用于根据设备是作为基站还是标签来定义不同的行为。如果设备是基站RX_NODE则设置其短地址为0x0001如果设备是标签TX_NODE则设置其短地址为0x0005。
外部变量声明
extern int psduLength; 和其他类似语句声明了在其他文件中定义的全局变量这些变量用于存储UWB通信中的数据长度、消息框架和接收缓冲区等信息。
常量定义
#define SPEED_OF_LIGHT 和其他类似语句定义了一些常量如光速、最大帧长度、天线延迟等这些常量用于UWB距离计算和配置。
时间相关宏定义
#define UUS_TO_DWT_TIME 和其他与时间相关的宏定义了与时间相关的转换因子和延迟参数。这些参数对于配置DW1000设备的定时器和处理响应超时非常关键。
函数原型
extern void BPhero_UWB_Message_Init(void); 和 extern void BPhero_UWB_Init(void);声明了在其他文件中定义的函数这些函数用于初始化UWB消息和设备。
总结
bphero_uwb.h头文件为UWB系统的实现提供了必要的宏定义、外部变量声明和函数原型。它允许在整个项目中共享这些定义和声明确保UWB设备的配置和操作的一致性。通过定义基站和标签、设置通信参数和声明关键函数这个头文件为构建UWB应用提供了基础。
bphero_uwb.c
#include bphero_uwb.h // 引入UWB相关的头文件
#include port.h // 引入端口定义相关的头文件
#include math.h // 引入数学运算相关的头文件
int psduLength 0; // 定义变量存储PSDU长度
srd_msg_dsss msg_f_send; // 定义结构体存储即将发送的范围测量消息帧
uint8 rx_buffer[FRAME_LEN_MAX]; // 定义接收缓冲区数组
uint16 frame_len 0; // 定义变量存储接收到的帧长度// 初始化UWB消息的内容
void BPhero_UWB_Message_Init(void)
{// 设置帧控制字节msg_f_send.frameCtrl[0] 0x1 /* 帧类型为数据帧 */ | 0x40 /* PAN ID压缩 */|0x20 /* 请求ACK */;msg_f_send.frameCtrl[1] 0x8 /* 目的地址模式为16位 */ | 0x80 /* 源地址模式为16位 */;msg_f_send.panID[0] 0xF0; // 设置PAN IDmsg_f_send.panID[1] 0xF0;// 初始化序列号和消息数据msg_f_send.seqNum 0;msg_f_send.messageData[POLL_RNUM] 3; // 设置新的范围编号msg_f_send.messageData[FCODE] RTLS_DEMO_MSG_ANCH_POLL; // 设置消息功能码psduLength (TAG_POLL_MSG_LEN FRAME_CRTL_AND_ADDRESS_S FRAME_CRC);// 初始化源地址和目的地址msg_f_send.seqNum 0;msg_f_send.sourceAddr[0] SHORT_ADDR 0xFF; msg_f_send.sourceAddr[1] (SHORT_ADDR8) 0xFF; msg_f_send.destAddr[0] 0x01; msg_f_send.destAddr[1] 0x01;
}// 默认通信配置
dwt_config_t config
{2, /* 信道号 */DWT_PRF_64M, /* 脉冲重复频率 */DWT_PLEN_1024, /* 前导码长度 */DWT_PAC32, /* 前导码获取块大小 */9, /* 发送前导码 */9, /* 接收前导码 */1, /* 使用非标准SFD */DWT_BR_110K, /* 数据速率 */DWT_PHRMODE_STD, /* PHY头模式 */(1025 64 - 32) /* SFD超时仅接收 */
};// 初始化UWB设备
void BPhero_UWB_Init(void)
{reset_DW1000(); // 重置DW1000设备spi_set_rate_low(); // 设置SPI为低速dwt_rxreset(); // 重置DW1000接收器// 初始化DW1000失败则持续指示错误if(dwt_initialise(DWT_LOADUCODE) -1) {while (1){led_on(LED_ALL); // 开启所有LEDdeca_sleep(100); // 等待led_off(LED_ALL); // 关闭所有LEDdeca_sleep(100);}}spi_set_rate_high(); // 设置SPI为高速dwt_configure(config); // 配置DW1000dwt_setleds(1); // 设置LED指示灯dwt_SetTxPower(config); // 设置发送功率dwt_setpanid(0xF0F0); // 设置PAN IDdwt_setaddress16(SHORT_ADDR); // 设置短地址// 设置天线延时dwt_setrxantennadelay(RX_ANT_DLY); dwt_settxantennadelay(TX_ANT_DLY);// 设置中断dwt_setinterrupt(DWT_INT_RFCG | (DWT_INT_ARFE | DWT_INT_RFSL | DWT_INT_SFDT | DWT_INT_RPHE | DWT_INT_RFCE | DWT_INT_RFTO /*| DWT_INT_RXPTO*/), 1);
}// 计算接收功率
static float calculatePower(float base, float N, uint8_t pulseFrequency)
{float A, corrFac;// 根据脉冲频率选择相关参数if(DWT_PRF_16M pulseFrequency){A 115.72;corrFac 2.3334;}else{A 121.74;corrFac 1.1667;}// 计算并返回功率估算值float estFpPwr 10.0 * log10(base / (N * N)) - A;// 功率校正if(estFpPwr -88){return estFpPwr;}else{// 近似计算修正后的功率值estFpPwr (estFpPwr 88) * corrFac;}return estFpPwr;
}// 获取接收功率
float dwGetReceivePower(void)
{dwt_rxdiag_t *diagnostics;dwt_readdiagnostics(diagnostics);// 获取并处理诊断信息float C (diagnostics-stdNoise)[3];float N diagnostics-rxPreamCount;float twoPower17 131072.0; // 2^17// 计算并返回接收功率return calculatePower(C * twoPower17, N, config.prf);
}
这段代码涉及初始化和配置UWBUltra-Wideband设备的步骤以及计算接收信号功率的功能。它使用了一系列的硬件抽象和配置函数这些函数通常是特定于设备的。以下是对代码各部分的逐行解释
头文件和全局变量
#include语句引入了必要的头文件这些头文件中包含了UWB设备操作、端口和数学运算的相关函数和定义。int psduLength 0;定义了一个整型变量psduLength用来存储PSDUPHY Service Data Unit的长度。srd_msg_dsss msg_f_send;定义了一个结构体msg_f_send用于存储即将发送的范围测量消息帧。uint8 rx_buffer[FRAME_LEN_MAX];定义了一个接收缓冲区数组rx_buffer。uint16 frame_len 0;定义了一个变量frame_len用来存储接收到的帧长度。
UWB消息初始化 void BPhero_UWB_Message_Init(void)定义了一个函数用于初始化UWB消息的内容包括设置帧类型、地址模式、序列号、目的地址等。 这段代码定义了一个名为BPhero_UWB_Message_Init的函数其主要目的是初始化用于UWB通信的消息结构体msg_f_send。以下是逐行的详细解释 函数定义 void BPhero_UWB_Message_Init(void): 定义了一个无返回值无参数的函数用于初始化UWB消息。 设置帧控制信息 msg_f_send.frameCtrl[0] 0x1 /*frame type 0x1 data*/ | 0x40 /*PID comp*/|0x20/* ACK request*/;: 设置帧控制字段的第一个字节。其中0x1设置帧类型为数据帧0x40表示PAN ID压缩0x20表示需要ACK响应。 设置地址模式 msg_f_send.frameCtrl[1] 0x8 /*dest extended address (16bits)*/ | 0x80 /*src extended address (16bits)*/;: 设置帧控制字段的第二个字节其中0x8和0x80分别设置目的地址和源地址为16位扩展地址模式。 设置PAN ID msg_f_send.panID[0] 0xF0; msg_f_send.panID[1] 0xF0;: 设置PAN ID为0xF0F0。 初始化序列号和消息数据 msg_f_send.seqNum 0;: 初始化消息的序列号为0。msg_f_send.messageData[POLL_RNUM] 3;: 在消息数据中设置范围编号POLL_RNUM为3。msg_f_send.messageData[FCODE] RTLS_DEMO_MSG_ANCH_POLL;: 设置消息功能码指明消息是轮询、响应还是其他类型。 设置PSDU长度 psduLength (TAG_POLL_MSG_LEN FRAME_CRTL_AND_ADDRESS_S FRAME_CRC);: 计算并设置PSDU长度。 设置源地址和目的地址 msg_f_send.sourceAddr[0] SHORT_ADDR 0xFF; msg_f_send.sourceAddr[1] (SHORT_ADDR8) 0xFF;: 从SHORT_ADDR中提取源地址并分为两个字节。msg_f_send.destAddr[0] 0x01; msg_f_send.destAddr[1] 0x01;: 设置目的地址为0x0101。 总结 BPhero_UWB_Message_Init函数负责初始化UWB消息结构体包括设置帧类型、地址模式、序列号、消息数据和目的地址等以准备发送。这些设置是UWB通信中必要的步骤确保消息能够被正确构造并发送到目标设备。
UWB通信配置 dwt_config_t config {...}定义了一个结构体变量config用于配置UWB设备的通信参数如信道、脉冲频率、前导码长度、数据速率等。 这段代码定义了dwt_config_t类型的变量config该变量用于配置UWB设备如Decawave DW1000的通信参数。dwt_config_t通常是一个结构体包含了一系列用于设定UWB设备操作模式的字段。以下是对各个字段的逐行解释 结构体定义 dwt_config_t config {...};: 定义并初始化一个名为config的结构体变量。 结构体成员 信道号: 2,: 设置UWB设备的信道号为2。不同信道对应不同的频率和带宽。 脉冲重复频率: DWT_PRF_64M,: 设置脉冲重复频率为64MHz。PRF是影响测距精度和范围的关键参数。 前导码长度: DWT_PLEN_1024,: 设置前导码长度为1024。前导码长度影响接收器的锁定性能和通信范围。 前导码获取块大小: DWT_PAC32,: 设置前导码获取块大小为32。这个参数与前导码长度相关用于接收过程中的帧同步。 发送前导码和接收前导码: 9, 9,: 分别设置发送和接收前导码为编码9。前导码是UWB信号的一部分有助于接收器进行帧同步和信号质量评估。 使用非标准SFD: 1,: 设置为使用非标准的Start of Frame DelimiterSFD。SFD是帧开始的标记非标准SFD可能用于特定的应用或与特定的硬件兼容。 数据速率: DWT_BR_110K,: 设置数据速率为110kbps。较低的数据速率通常可以提供更远的通信距离和更强的信号穿透能力。 PHY头模式: DWT_PHRMODE_STD,: 设置PHY头模式为标准模式。 SFD超时: (1025 64 - 32),: 设置SFD超时。这个参数是接收时间的一个限制用于确定何时放弃等待SFD。 总结 config结构体中的这些设置对于UWB设备的性能和行为至关重要。它们影响了设备的通信范围、速率、精度和兼容性。在初始化UWB设备时正确配置这些参数可以确保设备按照预期的方式运行。
UWB设备初始化 void BPhero_UWB_Init(void)定义了一个函数用于初始化UWB设备。它包括重置设备、设置SPI速率、配置设备参数、设置LED指示灯等步骤。如果初始化失败它会通过LED指示灯不断闪烁来提示错误。 这段代码定义了一个名为BPhero_UWB_Init的函数用于初始化DW1000 UWB模块。以下是逐行的详细解释 函数定义 void BPhero_UWB_Init(void): 定义了一个无返回值无参数的函数用于初始化UWB模块。 设备重置和SPI速率设置 reset_DW1000();: 调用函数重置DW1000 UWB模块确保从已知状态开始配置。spi_set_rate_low();: 将SPI通信速率设置为低这通常在初始化期间需要以确保稳定的通信。 接收器重置和设备初始化 dwt_rxreset();: 重置DW1000的接收器部分以确保无残留状态或错误。if(dwt_initialise(DWT_LOADUCODE) -1): 调用dwt_initialise函数来初始化DW1000设备如果返回-1表示初始化失败。DWT_LOADUCODE参数通常用于指示是否加载特定的微码以支持高级功能。 错误指示 while (1) { ... }: 如果初始化失败进入一个无限循环并通过LED灯闪烁来指示错误。 SPI速率和设备配置 spi_set_rate_high();: 初始化成功后将SPI速率提高以优化性能。dwt_configure(config);: 使用前面定义的config结构体来配置DW1000的通信参数。 LED、功率、PAN ID、地址和天线延时设置 dwt_setleds(1);: 启用LED指示灯用于显示设备状态。dwt_SetTxPower(config);: 设置发送功率通常根据配置或法规要求。dwt_setpanid(0xF0F0);: 设置网络识别的PAN ID。dwt_setaddress16(SHORT_ADDR);: 设置设备的短地址用于网络中设备识别。dwt_setrxantennadelay(RX_ANT_DLY); 和 dwt_settxantennadelay(TX_ANT_DLY);: 设置接收和发送天线的延迟这对于精确的距离测量非常重要。 中断设置 dwt_setinterrupt(DWT_INT_RFCG | (DWT_INT_ARFE | DWT_INT_RFSL | DWT_INT_SFDT | DWT_INT_RPHE | DWT_INT_RFCE | DWT_INT_RFTO /*| DWT_INT_RXPTO*/), 1);: 配置DW1000的中断以响应不同的事件和错误。这些中断帮助软件正确响应接收和发送状态以及错误条件。 总结 BPhero_UWB_Init函数是UWB设备的初始化脚本负责设备的重置、配置、和错误处理。每一步都是确保UWB模块能够按照预期参数运行的关键包括通信设置、地址分配和中断管理等。在实际应用中这些配置需要根据具体的应用需求和法规要求进行调整。
功率计算函数 static float calculatePower(float base, float N, uint8_t pulseFrequency)定义了一个函数根据给定的参数计算接收信号的功率。它使用对数函数和一些固定参数来计算功率值。 这段代码定义了一个名为calculatePower的函数用于根据给定的基础功率值、脉冲数量以及脉冲频率来估算UWB设备的接收信号功率。以下是逐行的详细解释 函数定义 static float calculatePower(float base, float N, uint8_t pulseFrequency): 定义了一个静态函数返回类型为float接收三个参数base基础功率值N脉冲数量pulseFrequency脉冲频率。 参数选择 float A, corrFac;: 定义两个浮点型变量A用于存储固定的衰减因子corrFac用于存储功率校正因子。if(DWT_PRF_16M pulseFrequency) { ... } else { ... }: 根据传入的脉冲频率选择不同的参数值。如果脉冲频率是16MHz则设置A和corrFac为特定值否则使用不同的值。这些参数对于估算功率值至关重要。 功率估算 float estFpPwr 10.0 * log10(base / (N * N)) - A;: 计算接收功率的估算值。这里使用对数函数来根据输入的基础功率值和脉冲数量计算接收信号的功率水平并从中减去固定的衰减因子。 功率校正 if(estFpPwr -88) { return estFpPwr; }: 如果计算出的估算功率值小于或等于-88dBm则直接返回这个值。这可能是基于实际观察或设备规格确定的阈值。estFpPwr (estFpPwr 88) * corrFac;: 如果估算的功率值大于-88dBm则进行额外的校正计算使用之前确定的corrFac进行调整。这是一个近似的校正方法可能基于经验或设备特性。 返回结果 return estFpPwr;: 返回最终计算和校正后的功率值。 总结 calculatePower函数是一个基于特定公式和校正方法来估算UWB设备接收信号功率的函数。它根据测量的基础功率、脉冲数量和脉冲频率来计算功率值并对结果进行校正以提供更准确的估算。这种计算对于理解和调整UWB设备的性能非常重要特别是在需要精确控制信号强度和质量的应用中。
接收功率获取函数 float dwGetReceivePower(void)定义了一个函数用于获取UWB设备接收信号的功率。它首先从设备中读取诊断信息然后调用calculatePower函数来计算功率。 这段代码定义了一个名为dwGetReceivePower的函数用于获取并计算DW1000 UWB模块的接收信号功率。以下是逐行的详细解释 函数定义 float dwGetReceivePower(void): 定义了一个无参数的函数返回类型为float用于获取接收功率。 变量定义与诊断信息读取 dwt_rxdiag_t *diagnostics;: 定义了一个指向dwt_rxdiag_t类型的指针该类型是用于存储接收诊断信息的结构体。dwt_readdiagnostics(diagnostics);: 调用函数dwt_readdiagnostics读取接收诊断信息并将结果存储在diagnostics指针指向的结构体中。 获取并处理诊断信息 float C (diagnostics-stdNoise)[3];: 从诊断信息中获取标准噪声级的第四个元素值并将其赋给变量C。这个值通常与信号的噪声水平有关。float N diagnostics-rxPreamCount;: 获取接收到的前导码计数值并将其赋给变量N。前导码计数是接收信号中前导序列的数量与信号强度有关。 计算接收功率 float twoPower17 131072.0;: 定义一个常数表示(2^{17})用于功率计算中的缩放。return calculatePower(C * twoPower17, N, config.prf);: 调用calculatePower函数计算接收功率并返回计算结果。传入的参数是调整后的标准噪声级C、前导码计数N和配置中的脉冲重复频率config.prf。 总结 dwGetReceivePower函数是用于读取UWB模块的接收诊断信息并基于这些信息计算接收信号的功率。这个计算涉及到对接收信号的标准噪声水平和前导码计数的测量以及对结果的数学处理。计算出的接收功率可以用于评估信号质量、调整传输功率或进行其他信号处理操作。
总结
这段代码是UWB设备如Decawave的DW1000的初始化、配置和功率计算的集合。代码中使用了许多特定于设备的函数和结构体这些通常在设备的SDK或API文档中有详细描述。整体来看这段代码用于设备的基础设置、通信配置和功率测量是UWB定位或通信系统中常见的操作。
stm32f10x_it.c
//extern void Simple_Rx_Callback(void); // 外部定义的接收回调函数当前未使用已被注释
extern void (*bphero_rxcallback)(void); // 声明一个外部的函数指针指向接收处理函数void EXTI0_IRQHandler(void) // 定义外部中断0处理函数
{if(EXTI_GetITStatus(EXTI_Line0)! RESET) // 如果外部中断0的标志位被设置{(*bphero_rxcallback)(); // 通过函数指针调用回调函数处理中断EXTI_ClearITPendingBit(EXTI_Line0); // 清除外部中断0的标志位以防止再次触发}
}
这段代码定义了一个名为EXTI0_IRQHandler的外部中断服务例程External Interrupt Handler用于STM32微控制器的第0号外部中断EXTI0。以下是逐行的详细解释
注释掉的外部函数声明
//extern void Simple_Rx_Callback(void);: 这是一个被注释掉的外部函数声明原本可能用于处理接收回调但在当前上下文中未被使用。
外部函数指针声明
extern void (*bphero_rxcallback)(void);: 声明了一个外部的函数指针bphero_rxcallback它指向一个无返回值、无参数的函数。这意味着bphero_rxcallback可以被指定为任何符合此形式的函数并在中断发生时被调用。
中断服务例程定义
void EXTI0_IRQHandler(void): 定义了一个名为EXTI0_IRQHandler的函数作为第0号外部中断的处理函数。
中断状态检查和处理
if(EXTI_GetITStatus(EXTI_Line0)! RESET): 检查第0号外部中断线EXTI_Line0的中断状态。如果该中断线的中断标志位被设置即发生了中断事件则执行大括号内的代码。
调用回调函数
(*bphero_rxcallback)();: 通过函数指针调用外部定义的回调函数。这意味着当EXTI0中断发生时会执行bphero_rxcallback所指向的函数。
清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);: 清除第0号外部中断线的中断标志位以确保中断不会再次立即触发并允许识别新的中断事件。
总结
EXTI0_IRQHandler函数是一个外部中断处理器用于STM32的第0号外部中断。当EXTI0发生中断时它会调用bphero_rxcallback指向的函数处理中断事件然后清除中断标志位。这种中断处理机制常用于响应外部事件如按钮按下、传感器信号变化等并使系统能够在事件发生时立即执行相关处理。通过使用函数指针这个处理器可以非常灵活地关联到不同的回调函数以适应不同的处理需求。
关于回调函数如何被调用
为了解释Simple_Rx_Callback函数如何被调用我们需要看看之前提到的几个代码段是如何一起工作的。这包括函数指针的声明、设置回调函数、中断处理程序以及回调函数的调用。以下是整合代码及其调用流程的解释
1. 函数指针的声明
extern void (*bphero_rxcallback)(void);这行代码声明了一个名为bphero_rxcallback的全局函数指针它可以指向任何无参数和无返回值的函数。 这行代码是C语言中的一个外部变量声明涉及到函数指针的使用。以下是逐部分的详细解释 extern 关键字 extern: 这个关键字用于声明一个变量是在其他地方定义的。在这种情况下它告诉编译器bphero_rxcallback变量在其他文件中定义这是一种跨文件引用全局变量的方式。 函数指针 void (*bphero_rxcallback)(void);: 这部分定义了一个函数指针bphero_rxcallback。让我们分解一下这个声明 void (...): 表明这个函数没有返回值。(*bphero_rxcallback): 这是函数指针的名称即bphero_rxcallback。(void): 表明这个函数不接受任何参数。 整体含义 整体来看这行代码声明了一个名为bphero_rxcallback的全局函数指针该指针可以指向任何不接受参数且没有返回值的函数。在程序中其他文件可能会定义一个实际的函数该函数符合上述条件即无参数无返回值并将该函数的地址赋给bphero_rxcallback。这样程序的其他部分就可以通过bphero_rxcallback()语法调用这个回调函数了而具体调用的是哪个函数则取决于在运行时bphero_rxcallback被设置为指向哪个函数的地址。 应用场景 在事件驱动的编程中如处理外部中断、接收数据等情况函数指针作为回调函数非常常见。这允许程序设计者灵活地在运行时决定应该执行哪个函数从而可以根据不同的事件或状态调用不同的处理函数而无需改变调用函数的代码。这种设计提高了代码的模块化和复用性。 2. 设置回调函数
// 定义设置回调函数的函数
void bphero_setcallbacks(void (*rxcallback)(void))
{bphero_rxcallback rxcallback; // 将传入的函数指针赋值给全局函数指针变量bphero_rxcallback
}bphero_setcallbacks(Simple_Rx_Callback); // 调用bphero_setcallbacks函数将Simple_Rx_Callback设置为接收回调函数
首先bphero_setcallbacks函数被定义用于将传入的函数地址赋给bphero_rxcallback函数指针。然后bphero_setcallbacks(Simple_Rx_Callback)被调用实际上是将Simple_Rx_Callback函数的地址赋给了bphero_rxcallback。这样bphero_rxcallback现在指向Simple_Rx_Callback函数。 这段代码涉及到两个主要部分一个是bphero_setcallbacks函数的定义另一个是对该函数的调用。以下是逐行的详细解释 函数定义bphero_setcallbacks void bphero_setcallbacks(void (*rxcallback)(void)): 定义了一个名为bphero_setcallbacks的函数它接受一个参数这个参数是一个指向函数的指针具体来说是指向一个没有参数和没有返回值的函数的指针。 函数内部代码 bphero_rxcallback rxcallback;: 这行代码是函数的主体部分。它将传入的函数指针rxcallback赋值给全局变量bphero_rxcallback。此操作实质上是将bphero_rxcallback指向了rxcallback所指向的函数这样当需要调用回调函数时可以通过bphero_rxcallback来调用。 函数调用 bphero_setcallbacks(Simple_Rx_Callback);: 这行代码调用了bphero_setcallbacks函数并将Simple_Rx_Callback作为参数传递给这个函数。Simple_Rx_Callback是定义在程序其他部分的一个函数它应该满足没有参数和没有返回值的条件。 总结 这段代码的主要作用是提供一种机制允许在程序的其他部分动态地指定某个函数在这个例子中是Simple_Rx_Callback作为回调函数来处理特定事件。通过bphero_setcallbacks(Simple_Rx_Callback);调用Simple_Rx_Callback函数的地址被赋值给了全局的函数指针bphero_rxcallback。在程序的其他地方当相关事件发生如接收到数据可以通过调用bphero_rxcallback指向的函数来处理该事件从而实现了回调机制。这使得程序在运行时更加灵活可以根据需要选择或更改事件处理逻辑。 3. 外部中断处理程序
//extern void Simple_Rx_Callback(void); // 外部定义的接收回调函数当前未使用已被注释
extern void (*bphero_rxcallback)(void); // 声明一个外部的函数指针指向接收处理函数void EXTI0_IRQHandler(void) // 定义外部中断0处理函数
{if(EXTI_GetITStatus(EXTI_Line0)! RESET) // 如果外部中断0的标志位被设置{(*bphero_rxcallback)(); // 通过函数指针调用回调函数处理中断EXTI_ClearITPendingBit(EXTI_Line0); // 清除外部中断0的标志位以防止再次触发}
}
这是一个外部中断服务例程。当外部中断0触发时它会检查中断标志位。如果中断确实发生它通过调用(*bphero_rxcallback)();来执行Simple_Rx_Callback。因为之前已经通过bphero_setcallbacks(Simple_Rx_Callback)将bphero_rxcallback指向了Simple_Rx_Callback函数所以这里实际上是在调用Simple_Rx_Callback。
总结
当系统初始化时通过bphero_setcallbacks(Simple_Rx_Callback)将Simple_Rx_Callback函数设置为回调函数。然后每当EXTI0外部中断触发EXTI0_IRQHandler中断服务例程将被调用它进一步调用(*bphero_rxcallback)()即Simple_Rx_Callback函数。这是一个典型的中断驱动的回调机制在事件发生时如接收到数据特定的回调函数在本例中为Simple_Rx_Callback将被执行以处理该事件。这种设计模式允许高度灵活和可配置的事件处理。
函数指针
函数指针是C语言中一个指向函数的指针即它的值是一个函数的地址。这样的指针可以用来调用函数和传递函数作为参数给其他函数。它们在编写可重用代码或回调函数时非常有用。
函数指针的基本语法
在C语言中函数指针的声明包含了函数的返回类型、函数指针的名字以及函数的参数类型。以下是基本的函数指针声明语法
返回类型 (*指针变量名)(参数类型);函数指针的使用
声明函数指针首先需要声明一个函数指针指明它所指向的函数的类型。指向函数然后将函数指针指向一个具体的函数。通过函数指针调用函数一旦函数指针被赋予了一个函数的地址就可以通过它来调用这个函数。
代码实例
假设我们有一个简单的函数该函数接受两个整数参数并返回它们的和。然后我们使用函数指针调用这个函数。
#include stdio.h// 一个简单的函数计算两个整数的和
int add(int a, int b) {return a b;
}int main() {// 声明一个指向函数的指针int (*funcPtr)(int, int);// 将funcPtr指向add函数funcPtr add;// 使用函数指针调用函数int sum funcPtr(2, 3);printf(Sum %d\n, sum);return 0;
}在这个例子中
int add(int a, int b) 是我们想要通过函数指针调用的函数。int (*funcPtr)(int, int); 声明了一个函数指针funcPtr它指向一个接受两个int类型参数并返回int类型的函数。funcPtr add; 将funcPtr指向add函数。int sum funcPtr(2, 3); 通过函数指针调用add函数。
函数指针的应用场景
函数指针广泛用于以下场景
回调函数允许用户将自己的函数传递给库函数然后在适当的时候由库函数回调。事件驱动的编程在事件发生时调用预先指定的函数。接口实现允许变更算法或策略动态改变调用的函数。表驱动的代码函数指针数组可以用于根据条件简洁地选择并执行函数。
总结
函数指针是C语言中一个强大的特性允许编程更灵活、代码更模块化。它们使得程序可以在不同的上下文中重用函数提高了代码的通用性和灵活性。
帧过滤