ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

[嵌入式框架][nrf51822][SDK12.3] BLE分层设计 NUS 透传数据队列发送,提升带宽利用率

[嵌入式框架][nrf51822][SDK12.3] BLE分层设计 NUS 透传数据队列发送,提升带宽利用率 接上一篇[嵌入式框架][nrf51822][SDK12.3] BLE分层设计 集合(OTA、透传、电量、设备信息、HID)[单片机框架] [queue] 实现一个简易的消息队列文章目录一、 透传服务加入队列机制问题1为什么会使用消息队列问题2消息队列的优缺点问题3如何增强消息队列使用的稳定性二、示例程序三、总结参考资料一、 透传服务加入队列机制问题1为什么会使用消息队列由于业务划分有用户层中间层和系统层。要保证用户层的多个用户对中间层的不同模块之间的访问以及多个中间层的模块要访问系统层的不同模块选择client-server模型来满足设计需求。要完成client-server之间的通信有多种途径选择消息队列一方面是为了将系统的耦合性降低另一方面是为了保持模块异步运行减少阻塞。下面是 client-server 模型下消息队列的通信流程图服务端系统层消息队列中间层客户端用户层发送消息发送消息缓存消息接收处理处理完成客户端A客户端B消息队列缓存服务端处理模块流程说明客户端用户层将消息发送到消息队列中间层进行缓存服务端系统层从队列中取出消息并处理处理完成后通知队列删除该消息实现模块间的异步解耦通信。问题2消息队列的优缺点消息队列出问题将会引起整个系统紊乱因为在client-server模型中通信都是采用的消息队列所以要是消息队列出问题就会引起整个系统的紊乱。消息有可能会丢失重复使用等问题下面是消息队列与其他常见通信方式的对比通信方式耦合性异步性实时性资源占用适用场景消息队列低发送方与接收方解耦高数据先入队接收方空闲时处理中取决于队列调度与处理时机较高需额外分配队列缓冲区模块间异步解耦、数据突发缓冲、多对多通信直接调用高调用方与被调用方强依赖低同步执行调用方需等待返回高立即执行并返回结果低无需额外缓冲区简单同步操作、实时性要求高的短流程信号量中仅用于同步与互斥不传数据低主要用于任务同步不承载业务数据高可立即唤醒等待任务低仅需少量内核对象任务同步、临界区互斥、事件通知事件标志组中用于事件通知不传数据中任务可等待多个事件高事件置位后立即唤醒任务低仅需少量内核对象多事件触发、任务状态同步、条件等待对比说明消息队列的核心优势在于低耦合与异步解耦适合数据量较大、发送方与接收方处理速度不一致的场景直接调用实时性最好但耦合度高适合简单同步流程信号量与事件标志组主要用于任务同步与互斥不承载业务数据资源开销小但无法传递数据内容。实际嵌入式设计中常将消息队列与信号量/事件标志组配合使用兼顾数据传递与任务调度。问题3如何增强消息队列使用的稳定性为了防止消息重复使用一方面不同client有各自的标志防止特定的消息被其它模块使用另一方面消息在接收完成之后将会被删除。对消息队列的消息长度要多适当调整避免有些消息过长而被丢弃。至于消息队列本身的问题需要从系统层面去多分析和优化。二、示例程序下面是数据从用户层发送到 BLE 的完整时序图BLE NUS 服务ble_data_send_dispose消息队列 m_ble_buff_qbiz_ble_data_send用户层BLE NUS 服务ble_data_send_dispose消息队列 m_ble_buff_qbiz_ble_data_send用户层alt[队列为空且连接正常][队列非空或发送失败]空闲循环中调用调用 biz_ble_data_send(data, len)直接发送数据发送成功queue_en(data, len) 数据入队入队成功queue_get_space() 查询队列空间返回消息数量queue_de() 取出数据返回单条消息组合多条消息到 ble_send_buffble_nus_data_send() 发送发送结果时序说明用户层调用biz_ble_data_send发送数据若队列为空且连接正常则直接发送否则数据通过queue_en进入消息队列缓存。系统空闲时由ble_data_send_dispose从队列中取出数据queue_de组合后调用ble_nus_data_send发送到 BLE 对端实现异步解耦发送。/******************************************************************************** * file biz_ble_nus.c * author jianqiang.xue * Version V1.0.0 * Date 2022-03-16 * brief [业务] ble数据透传服务(从机) https://blog.csdn.net/weixin_41572450/article/details/84036968 ********************************************************************************//* Private Includes ----------------------------------------------------------*/#includequeue.h#includebusiness_function.h#includelog.h#includebiz_ble_cfg.h#includebiz_ble.h#includebiz_ble_nus.h/* Private Define ------------------------------------------------------------*//* External Variables --------------------------------------------------------*//* Private Macro -------------------------------------------------------------*//* Private Variables ---------------------------------------------------------*/#ifBLE_NUS_ENABLEDble_nus_tm_nus;/** Structure to identify the Nordic UART Service. */typedefvoid(*ble_rx_event_callback)(uint8_t*data,uint16_tlen);staticble_rx_event_callback g_rx_dispose_callback;/***************消息队列定义**************/// 用于BLE数据发送消息队列总缓存区staticuint8_tm_ble_q_buff[BLE_BUFF_Q_ITEM_CNT*BLE_BUFF_Q_ITEM_SIZE]{0};queue_tm_ble_buff_q{.pbuffm_ble_q_buff,.front0,.rear0,.item_cntBLE_BUFF_Q_ITEM_CNT,.item_sizeBLE_BUFF_Q_ITEM_SIZE};uint8_tg_ble_single_buff_data[BLE_BUFF_Q_ITEM_SIZE]{0};// 用于单个BLE数据临时缓存区// 如果发送失败则组合上次数据一并发送。最大程度利用带宽使数据正常发送。uint8_tble_send_buff[BLE_BUFF_SEND_MAX_SIZE]{0};// 用于组合多个BLE数据// 记录当前发送长度uint16_tble_send_len0;#endif/* Private Function Prototypes -----------------------------------------------*/#ifBLE_NUS_ENABLED/** * brief [透传数据服务][接收事件] 接收数据事件 [处理通过BLE接收的数据] * param p_nus: 透传服务结构体包含一些回调函数。(本接收事件没有使用) * param p_data: 数据包指针头 * param length: 数据长度 */staticvoidnus_data_handler(ble_nus_t*p_nus,uint8_t*p_data,uint16_tlength){LOG_D(DEBUG RX:%02x %02x %02x %02x %02x|%d\r\n,p_data[0],p_data[1],p_data[2],p_data[3],p_data[4],length);// 将数据传给外部[业务层]处理if(g_rx_dispose_callback){g_rx_dispose_callback(p_data,length);}}#endif/* Public Function Prototypes ------------------------------------------------*/#ifBLE_NUS_ENABLEDvoidbiz_ble_nus_init(void){uint32_terr_code;/**** NUS 串口透传服务 ****/ble_nus_init_tnus_init;memset(nus_init,0,sizeof(nus_init));nus_init.data_handlernus_data_handler;err_codeble_nus_init(m_nus,nus_init);LOG_D(DEBUG NUS INIT:%x\r\n,err_code);APP_ERROR_CHECK(err_code);queue_init(m_ble_buff_q,m_ble_q_buff,BLE_BUFF_Q_ITEM_CNT,BLE_BUFF_Q_ITEM_SIZE);memset(m_ble_q_buff,0,BLE_BUFF_Q_ITEM_CNT*BLE_BUFF_Q_ITEM_SIZE);memset(g_ble_single_buff_data,0,BLE_BUFF_Q_ITEM_SIZE);}/** * brief [串口透传服务] 注册接收回调 * param *event: 格式 -- func(uint8_t *data, uint16_t len); * retval false--失败 true--成功 */boolbiz_ble_nus_rx_reg_callback(void*event){if(g_rx_dispose_callback!NULL){returnfalse;}else{g_rx_dispose_callback(ble_rx_event_callback)event;}returntrue;}/** * brief [数据透传] 将队列数据依次发送 本函数建议放在空闲函数使用 * retval 0--成功 1--失败 */boolble_data_send_dispose(void){staticbool old_send_failfalse;staticuint8_tmsg_mem_num0;if(!old_send_fail){msg_mem_numqueue_get_space(m_ble_buff_q);if(msg_mem_num!0){ble_send_len0;// 将队列数据取出进行组合。msg_mem_nummsg_mem_num(BLE_NUS_MAX_DATA_LEN/BLE_BUFF_Q_ITEM_SIZE)?(BLE_NUS_MAX_DATA_LEN/BLE_BUFF_Q_ITEM_SIZE):msg_mem_num;for(uint8_ti0;imsg_mem_num;i){if(!queue_de(m_ble_buff_q,g_ble_single_buff_data)){break;}memcpy(ble_send_buffble_send_len,g_ble_single_buff_data,m_ble_buff_q.item_size);ble_send_lenm_ble_buff_q.item_size;}}}if(ble_send_len0){if(get_ble_conn_state()){ret_code_tble_send_err_code;#ifNRF51ble_send_err_codeble_nus_string_send(m_nus,ble_send_buff,ble_send_len);#elifNRF52ble_send_err_codeble_nus_data_send(m_nus,ble_send_buff,ble_send_len,m_conn_handle);#endifif(ble_send_err_codeNRF_SUCCESS){old_send_failfalse;ble_send_len0;}else{old_send_failtrue;}returntrue;}else{// 蓝牙断开则清除所有队列数据queue_init(m_ble_buff_q,m_ble_q_buff,BLE_BUFF_Q_ITEM_CNT,BLE_BUFF_Q_ITEM_SIZE);old_send_failfalse;ble_send_len0;returnfalse;}}else{returnfalse;}}/** * brief [数据透传] 发送数据 非实时数据入队列 * param *data: 数据头指针 * param len: 数据长度 * retval 0--成功 1--失败 */boolbiz_ble_data_send(uint8_t*data,uint8_tlen){if(get_ble_conn_state()){if(lenBLE_NUS_MAX_DATA_LEN){LOG_E(ERR [biz_ble_data_send] Len 20 , Fail\r\n);}if(queue_is_null(m_ble_buff_q)){ret_code_tble_send_err_code;#ifNRF51ble_send_err_codeble_nus_string_send(m_nus,data,len);#elifNRF52ble_send_err_codeble_nus_data_send(m_nus,data,len,m_conn_handle);#endifif(ble_send_err_codeNRF_SUCCESS){returntrue;}}if(m_conn_handleBLE_CONN_HANDLE_INVALID){returnfalse;}if(!queue_en(m_ble_buff_q,data,len)){LOG_D(DEBUG [biz_ble_data_send] queue_en fail\r\n);returnfalse;}}returntrue;}#elsevoidbiz_ble_nus_init(void){}boolbiz_ble_nus_rx_reg_callback(void*event){returnfalse;}boolble_data_send_dispose(void){returnfalse;}boolbiz_ble_data_send(uint8_t*data,uint8_tlen){returnfalse;}#endif/******************************************************************************** * file biz_ble_nus.h * author jianqiang.xue * Version V1.0.0 * Date 2022-03-16 * brief [业务] ble数据透传服务(从机) ********************************************************************************/#ifndef__BIZ_BLE_NUS_H#define__BIZ_BLE_NUS_H/* Includes ------------------------------------------------------------------*/#includestdint.h#includestdbool.h#includebiz_ble_cfg.h#includeble_nus.h/* Define --------------------------------------------------------------------*//* External Variables --------------------------------------------------------*/#ifBLE_NUS_ENABLEDexternble_nus_tm_nus;#endif/* Public Function Prototypes ------------------------------------------------*/voidbiz_ble_nus_init(void);boolbiz_ble_data_send(uint8_t*data,uint8_tlen);boolble_data_send_dispose(void);boolbiz_ble_nus_rx_reg_callback(void*event);#endif三、总结本文以 BLE 透传服务为例介绍了在 client-server 模型下引入消息队列的设计思路与实现要点。核心设计要点可概括为以下几点分层解耦通过用户层、中间层、系统层的划分配合消息队列实现模块间的异步通信降低各层之间的耦合度使多个客户端与服务端模块可以独立开发与维护。异步缓冲发送方调用biz_ble_data_send时若连接正常且队列为空则直接发送否则数据先进入m_ble_buff_q队列缓存由空闲循环中的ble_data_send_dispose统一取出并组合发送避免阻塞用户层调用。稳定性保障通过不同 client 的标志位防止消息被误用接收完成后及时删除消息并合理调整队列长度与条目大小避免消息过长被丢弃同时从系统层面持续分析与优化队列本身。资源与实时性权衡消息队列在带来低耦合与异步优势的同时也需要额外分配队列缓冲区实时性取决于队列调度与处理时机。实际设计中常与信号量、事件标志组配合兼顾数据传递与任务调度。总体而言消息队列非常适合 BLE 透传这类「发送方与接收方处理速度不一致、数据可能突发」的场景是嵌入式分层设计中实现模块解耦与异步处理的有效手段。参考资料Nordic Semiconductor Infocenter – nRF51 SeriesNordic Semiconductor Infocenter – nRF52 SeriesNordic SDK 12.3 文档 – BLE NUS 服务[嵌入式框架][nrf51822][SDK12.3] BLE分层设计 集合(OTA、透传、电量、设备信息、HID)[单片机框架] [queue] 实现一个简易的消息队列[业务] ble数据透传服务(从机) 参考实现
返回列表