NRF52832学习笔记(41)——添加串口库libuarte
一、背景
由于板子上不支持硬件流控,在使用 app_uart_fifo 库接收串口大数据时,频繁报 APP_UART_COMMUNICATION_ERROR 错误,多次重新初始化后,串口也不再产生中断了。查看官方论坛后决定使用串口异步库 libuarte。
二、简介
Libuarte 是一个 UARTE 库,由以下层组成:
- nrf_libuarte_drv:一个低级 UARTE 驱动程序,具有扩展功能,如连续计算接收的字节、双缓冲、启动和停止接收器的可选事件,以及可在接收器启动和停止时触发的可选任务。
- nrf_libuarte_async:适用于接收和传输异步数据包的库。它管理接收缓冲区并实现接收器非活动超时。库正在使用 nrf_libuarte。它旨在用于典型的 UART 用例,其中交易对手异步发送可变长度的数据包。在这种情况下,用户会在数据包边界(即超时)和 DMA 缓冲区已满时收到事件。
2.1 nrf_libuarte_drv
nrf_libuarte_drv 使用 EasyDMA 双缓冲功能和 PPI 连接来确保可靠的接收。连续接收由 STARTRX 任务建立,连接到 ENDRX 事件。STARTRX 任务启动后,将生成 RXSTARTED 事件。
在 RXSTARTED 事件中:
- EasyDMA 已配置为传输,并且可以配置为下一次传输。
- Configuration registers 被锁存。
使用这种方法时,系统延迟取决于所使用的缓冲区的大小。如果缓冲区足够大,系统可以确保完全接收,而无需流控制,延迟为数毫秒。这个最小延迟可以覆盖 SoftDevice 或任何其他更高优先级的中断和 flash 操作(包括 flash 页面擦除)。
此外,计数器模式下的专用 TIMER 外设用于跟踪接收到的字节数。
nrf_libuarte_drv 可以配置硬件事件来启动和停止接收器。例如,此选项可用于构建具有请求和响应引脚的低功耗 UART 协议,该协议仅在传输期间启用接收器。所有硬件任务和事件都与 PPI 连接,这允许不依赖于中断处理时间的自主操作。
nrf_libuarte_drv 正在使用以下硬件资源:
- UARTE 实例
- TIMER 实例
- PPI 通道:
- 如果未使用可选任务和事件,则至少 2 个 PPI 通道(在最多 255 次 EasyDMA 传输的设备上为 3 个)。
- 如果使用可选任务和事件,则最多 9 个 PPI 通道和 2 个 PPI 组。
2.2 nrf_libuarte_async
nrf_libuarte_async 库构建在 lib_libuarte_drv 驱动程序之上。它实现了接收器非活动超时,这会导致一个包含接收数据量的事件。
该库具有用于接收的专用缓冲区,并处理来自驱动程序 (NRF_LIBUARTE_DRV_EVT_RX_BUF_REQ) 的缓冲区请求事件。
您可以使用以下选项之一实现接收器不活动超时,具体取决于可用资源:
- 专用的 TIMER 外围设备
- 专用 RTC 外设
- 一个 app_timer 实例
TIMER/RTC 外设
TIMER 或 RTC 外设与 TIMER/RTC 外设中的字节边界事件 (RXDRDY) 和任务 CLEAR 之间的 PPI 连接一起使用。
TIMER/RTC 的配置方式是在 compare 事件上触发中断。当任务 CLEAR 未按时触发时,将触发中断。
configurable 中的超时和 resolution 等于所用外设的 tick length。
app_timer实例
除了 TIMER/RTC 外设 之外,还可以使用 app_timer 实例进行超时实现。此方法需要按指定的时间间隔定期生成 app_timer 事件。
超时时,app_timer 事件处理程序检查收到的字节数是否已更改。根据此检查,处理程序报告数据包边界。
与 TIMER/RTC 外设相比,超时分辨率较低,等于可以设置的最短app_timer超时。
nrf_libuarte_async 正在使用以下硬件资源:
- UARTE 实例
- TIMER 实例
- 2 或 3 个 PPI 通道
- 如果 TIMER 或 RTC 外围设备用于接收器非活动超时,则使用以下附加资源:
- RTC 或 TIMER 实例
- 2 个 PPI 通道
三、参考工程
libuarte 在 SDK v16.0 中得到了改进,它也从 Experimental 中移出并取代了串行库。
在 SDK\examples\peripheral\libuarte 中找到示例工程
四、添加组件库
基于 ble_app_uart 的工程,在 nRF_Drivers 文件夹和 nRF_Libraries 文件夹确认以下组件库是否存在,不存在则添加。

在 ble_app_uart 的工程中后三个文件没有,需要添加:
-
添加 nrfx_ppi.c、nrfx_rtc.c 和 nrfx_timer.c
位于 SDK\modules\nrfx\drivers\src

-
添加 nrfx_libuarte_async.c 和 nrfx_libuarte_drv.c
位于 SDK\components\libraries\libuarte

-
添加 nrfx_queue.c
位于 SDK\components\libraries\queue

-
添加上述编译文件路径



五、SDK配置
点击 sdk_config.h 文件

选择 Configuration Wizard

nRF_Drivers 中勾选PPI、RTC、TIMER相关选项


修改时钟配置,由于要移植的工程是ble_app_uart,其中蓝牙协议栈SoftDevice使用了RTC0和TIMER0,app_timer模块使用了RTC1,所以我们用于串口超时的时钟有RTC2、TIMER1和TIMER2等等。


nRF_Libraries 中勾选QUEUE相关选项

添加 NRF_LIBUARTE_ASYNC_WITH_APP_TIMER 和 nrf_libuarte_drv 并勾选
// <q> NRF_LIBUARTE_ASYNC_WITH_APP_TIMER - nrf_libuarte_async - libUARTE_async library#ifndef NRF_LIBUARTE_ASYNC_WITH_APP_TIMER
#define NRF_LIBUARTE_ASYNC_WITH_APP_TIMER 1
#endif// <h> nrf_libuarte_drv - libUARTE library//==========================================================
// <q> NRF_LIBUARTE_DRV_HWFC_ENABLED - Enable HWFC support in the driver#ifndef NRF_LIBUARTE_DRV_HWFC_ENABLED
#define NRF_LIBUARTE_DRV_HWFC_ENABLED 0
#endif// <q> NRF_LIBUARTE_DRV_UARTE0 - UARTE0 instance#ifndef NRF_LIBUARTE_DRV_UARTE0
#define NRF_LIBUARTE_DRV_UARTE0 1
#endif// <q> NRF_LIBUARTE_DRV_UARTE1 - UARTE1 instance#ifndef NRF_LIBUARTE_DRV_UARTE1
#define NRF_LIBUARTE_DRV_UARTE1 0
#endif// </h>
//==========================================================

六、使用例子
1)添加头文件
#include "nrf_libuarte_async.h"
2)添加全局变量(SDK16.0以上 中 ble_peripheral 的 ble_app_uart 工程)
// 使用TIMER2作为计数,不使用RTC和TIMER用作超时(如果前两个都不使用,则使用app_timer作为超时)
NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 2, NRF_LIBUARTE_PERIPHERAL_NOT_USED, NRF_LIBUARTE_PERIPHERAL_NOT_USED, 255, 3);
// 使用TIMER2作为计数,使用RTC2用作超时
//NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 2, 2, NRF_LIBUARTE_PERIPHERAL_NOT_USED, 255, 3);static uint8_t text[] = "UART example started.\r\n Loopback:\r\n";
static uint8_t text_size = sizeof(text);
static volatile bool m_loopback_phase;typedef struct {uint8_t * p_data;uint32_t length;
} buffer_t;NRF_QUEUE_DEF(buffer_t, m_buf_queue, 10, NRF_QUEUE_MODE_NO_OVERFLOW);
第3,4,5参数分别是TIMER接口用作libuarte字节统计,RTC接口用作超时,TIMER接口用作超时,二者选其一即可,如果都不使用,则使用app_timer作为超时。
其中蓝牙协议栈SoftDevice使用了RTC0和TIMER0,app_timer模块使用了RTC1,所以我们用于串口超时的时钟有RTC2、TIMER1和TIMER2等等。
3)修改main函数
这里由于串口超时使用的是app_timer,而非RTC或TIMER,所以这里将uart_init()往后放。
/**@brief Application main function.*/
int main(void)
{bool erase_bonds;// Initialize.
// uart_init();log_init();timers_init();uart_init();buttons_leds_init(&erase_bonds);power_management_init();ble_stack_init();gap_params_init();gatt_init();services_init();advertising_init();conn_params_init();// Start execution.
// printf("\r\nUART started.\r\n");NRF_LOG_INFO("Debug logging for UART over RTT started.");advertising_start();// Enter main loop.for (;;){idle_state_handle();}
}
4)添加串口初始化函数
这里由于串口超时使用的是app_timer,而非RTC或TIMER,所以这里将串口中断优先级调整的app_timer优先级高,修改为 APP_IRQ_PRIORITY_LOW_MID 及以上。
/**@brief Function for initializing the UART module.*/
/**@snippet [UART Initialization] */
static void uart_init(void)
{ret_code_t err_code;nrf_libuarte_async_config_t nrf_libuarte_async_config = {.tx_pin = TX_PIN_NUMBER,.rx_pin = RX_PIN_NUMBER,.baudrate = NRF_UARTE_BAUDRATE_115200,.parity = NRF_UARTE_PARITY_EXCLUDED,.hwfc = NRF_UARTE_HWFC_DISABLED,.timeout_us = 100,.int_prio = APP_IRQ_PRIORITY_LOW_MID};err_code = nrf_libuarte_async_init(&libuarte, &nrf_libuarte_async_config, uart_event_handler, (void *)&libuarte);APP_ERROR_CHECK(err_code);nrf_libuarte_async_enable(&libuarte);err_code = nrf_libuarte_async_tx(&libuarte, text, text_size);APP_ERROR_CHECK(err_code);
}
/**@snippet [UART Initialization] */
5)添加串口中断处理函数
每当 nrf_libuarte_drv 设置 EasyDMA 接收缓冲区时,都会生成缓冲区请求事件 (NRF_LIBUARTE_DRV_EVT_RX_BUF_REQ)。在这种情况下,请确保应用程序使用 nrf_libuarte_drv_rx_buf_rsp 进行响应并提供新的缓冲区。新的接收缓冲区必须在刚刚开始的传输结束之前提供,否则 UARTE 将开始覆盖活动缓冲区,因为接收会自主重新启动。
如果设备不支持 16 位长的 EasyDMA 传输,则使用 EasyDMA 双缓冲功能以及 ENDTX 事件和 STARTTX 任务之间的 PPI 连接来确保连续传输。传输完成后,将生成 NRF_LIBUARTE_DRV_EVT_TX_DONE 事件。
void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
{nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;ret_code_t ret;switch (p_evt->type){case NRF_LIBUARTE_ASYNC_EVT_ERROR:bsp_board_led_invert(0);break;case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:ret = nrf_libuarte_async_tx(p_libuarte,p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);if (ret == NRF_ERROR_BUSY){buffer_t buf = {.p_data = p_evt->data.rxtx.p_data,.length = p_evt->data.rxtx.length,};ret = nrf_queue_push(&m_buf_queue, &buf);APP_ERROR_CHECK(ret);}else{APP_ERROR_CHECK(ret);}bsp_board_led_invert(1);m_loopback_phase = true;break;case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:if (m_loopback_phase){nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);if (!nrf_queue_is_empty(&m_buf_queue)){buffer_t buf;ret = nrf_queue_pop(&m_buf_queue, &buf);APP_ERROR_CHECK(ret);UNUSED_RETURN_VALUE(nrf_libuarte_async_tx(p_libuarte, buf.p_data, buf.length));}}bsp_board_led_invert(2);break;default:break;}
}
6)串口接收
每次完成 EasyDMA 传输时,都会生成 NRF_LIBUARTE_DRV_EVT_RX_DATA 事件。事件结构包含指向数据和接收的数据量的指针。处理数据时,必须通过调用 nrf_libuarte_async_rx_free 用于释放接收到的缓冲区数据的函数。
上面串口中断处理函数是为了串口回环打印测试,实际应用可改为以下逻辑:
case NRF_LIBUARTE_ASYNC_EVT_RX_DATA: {在此自定义串口处理(event->data.rxtx.p_data, event->data.rxtx.length)nrf_libuarte_async_rx_free(libuarte, event->data.rxtx.p_data, event->data.rxtx.length);} break;
7)串口发送
nrf_libuarte_async_tx 用于启动传输。
在传输完成时调用具有 NRF_LIBUARTE_ASYNC_EVT_TX_DONE 事件的事件处理程序。
七、工程代码
基于 nRF5_SDK_17.1.0_ddde560,仅用于移植文件添加参考。
百度网盘: https://pan.baidu.com/s/1yu34SDNoBl7Uy-WV18UO8A?pwd=999m 提取码: 999m
• 由 Leung 写于 2024 年 11 月 4 日
• 参考:nRF5 SDK v17.1.0: Libuarte - advanced UARTE driver (nordicsemi.com)
nrf52840蓝牙开发之Libuarte外设移植
相关文章:
NRF52832学习笔记(41)——添加串口库libuarte
一、背景 由于板子上不支持硬件流控,在使用 app_uart_fifo 库接收串口大数据时,频繁报 APP_UART_COMMUNICATION_ERROR 错误,多次重新初始化后,串口也不再产生中断了。查看官方论坛后决定使用串口异步库 libuarte。 二、简介 Li…...
Moore Perf System 1.1版本
Moore Perf System(一款性能分析工具) 提供可视化界面,在时间轴上按时间顺序显示 CPU 和 GPU 的事件、吞吐和性能指标,帮助开发人员方便、快速、准确的定位到系统级别的性能瓶颈,进而进行针对性分析和优化,…...
SpringBoot+Shirp的权限管理
目录 怎么实现动态菜单 1.html页面 2.获取动态菜单 Shiro权限刷新 1. 配置Shiro 2. 创建权限刷新服务 3. 调用权限刷新服务 注意事项 如何更新ShiroFilter初始权限 怎么实现动态菜单 1.html页面 <ul class"nav side-menu"><!--第一重循环…...
OpenCV图像基础
目录 显示窗口 创建空白图像 保存图片 图像裁剪 调整图片大小 图像绘制 绘制圆形 绘制矩形 绘制直线 绘制文本 中文乱码 控制鼠标 视频处理 显示窗口 cv2.namedWindow(winname, flagsNone) 创建一个命名窗口,以便在该窗口中显示图像或进行其他图形操作…...
基于MATLAB的图像拼接技术
实验名称:基于MATLAB的图像拼接技术实验目的:利用图像拼接技术得到超宽视角的图像,用来虚拟实际场景。实验原理: 基于相位相关的图像拼接技术是一种基于频域的方法,通过求得图像在频域上是相位相关特点来找到特征位置…...
ComfyUI 快速入门(环境搭建)
ComfyUI 是一个现代化、灵活的用户界面(UI)工具,专为 AI 模型和深度学习框架设计,能够快速实现可视化操作和定制化界面。在本教程中,我们将介绍如何在本地机器上快速搭建 ComfyUI 环境,帮助你开始使用这一工…...
将HTML项目上传至Gitee仓库(详细教程)
1.登录giett giett地址链接:Gitee - 基于 Git 的代码托管和研发协作平台 2.新建一个giett仓库 创建后得到远程仓库: 3、在本地项目文件夹右击鼠标点击 Open Git Bash Here 4、输入命令 命令:git init,这个目录变成git可以管理的仓库,会出…...
如何应对Oracle SQL语句的数据去重问题,应该考虑哪几个方面?
引言 在数据管理和数据库设计中,数据去重是一个重要的课题。随着信息技术的快速发展,数据的产生速度和数量都在急剧增加,如何有效地管理和维护这些数据成为了一个亟待解决的问题。 数据去重不仅可以减少存储空间的占用,还可以提高数据查询的效率,确保数据的准确性和一致…...
论负载均衡技术在Web系统中的应用论文
一、概要叙述软件项目及其主要工作 在2023年,我有幸参与了某公司电子商务平台的研发项目,担任系统架构设计师一职。该项目旨在构建一个高性能、高可用性的电子商务平台,以支撑公司日益增长的在线业务需求。作为系统架构设计的核心成员&#…...
NumPy 数据类型
1.常用 NumPy 基本类型 (1)bool_:布尔型数据类型(True 或者 False) (2)int_:默认的整数类型(类似C 语言long,int32 或 int64) (3&a…...
JavaScript——(4)
【DOM】 一、DOM基本概念 DOM(Document Object Model,文档对象模型)是 JavaScript 操作 HTML 文档的接口,使文档操作变得非常优雅、简便。 DOM 最大的特点就是将 HTML 文档表示为 “节点树”。 DOM 元素/节点:就是…...
每日一练 | DHCP Relay(DHCP 中继)
01 真题题目 DHCP Relay 又称为 DHCP 中继,下列关于 DHCP Relay 的说法正确的是(多选): A. DHCP 协议多采用广播报文,如果出现多个子网则无法穿越,所以需要 DHCP Relay 设备。 B. DHCP Relay 一定是一台交…...
`psdparse`:解锁Photoshop PSD文件的Python密钥
文章目录 psdparse:解锁Photoshop PSD文件的Python密钥背景:为何选择psdparse?psdparse是什么?如何安装psdparse?简单函数使用方法应用场景常见Bug及解决方案总结 psdparse:解锁Photoshop PSD文件的Python密…...
考研要求掌握的C语言程度(插入排序)
插入排序是啥类型的排序 插入类型的 插入排序经常用在啥类型场景下 用在有序序列下的基础上插入新数据 时间复杂度分析 如果是有序的基础下,最好的时间复杂度是O(n); 普通情况下是O(n^2) 插入排序的原理是啥&am…...
mybatis源码解析-sql执行流程
1 执行器的创建 1. SimpleExecutor 描述:最基本的执行器,每次查询都会创建新的语句对象,并且不会缓存任何结果。 特点: 每次查询都会创建新的 PreparedStatement 对象。 不支持一级缓存。 适用于简单的查询操作,不…...
Golang | Leetcode Golang题解之第538题把二叉搜索树转换为累加树
题目: 题解: func getSuccessor(node *TreeNode) *TreeNode {succ : node.Rightfor succ.Left ! nil && succ.Left ! node {succ succ.Left}return succ }func convertBST(root *TreeNode) *TreeNode {sum : 0node : rootfor node ! nil {if n…...
【linux】HTTPS 协议原理
1. 了解 HTTPS 协议原理 (一)认识 HTTPS HTTPS 也是一种应用层协议,是在 HTTP 协议的基础上引入了一个加密层 因为 HTTP协议的内容都是按照文本的方式进行传输的,这个过程中,可能会出现一些篡改的情况 (…...
安利一款开源企业级的报表系统SpringReport
SpringReport是一款企业级的报表系统,支持在线设计报表,并绑定动态数据源,无需写代码即可快速生成想要的报表,可以支持excel报表和word报表两种格式,同时还可以支持excel多人协同编辑,后续考虑实现大屏设计…...
数据安全-接口数据混合加密笔记
接口数据传输安全设计方案 采用非对称加密对称加密混合方式,接口混合加、解密过程梳理: 后端准备sm2公钥和私钥后端将SM2公钥传输到前端前端生成SM4密钥前端使用SM2公钥加密SM4秘钥,获得密文使用SM4秘钥加密数据将密文和加密数据传输至后端…...
JeecgBoot入门
最近在了解低代码平台,其中关注到gitee上开源项目JeecgBoot,JeecgBoot官方也有比较完整的入门教学文档,这里我们将耕者官方教程学习,并将其记录下来。 一、项目简介 JeecgBoot 是一款基于代码生成器的低代码开发平台拥有零代码能力…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
