建设网站要那些,小程序开发费用明细怎么填,全屏网页设计尺寸,wordpress编辑器视频一、背景
由于板子上不支持硬件流控#xff0c;在使用 app_uart_fifo 库接收串口大数据时#xff0c;频繁报 APP_UART_COMMUNICATION_ERROR 错误#xff0c;多次重新初始化后#xff0c;串口也不再产生中断了。查看官方论坛后决定使用串口异步库 libuarte。
二、简介
Li…一、背景
由于板子上不支持硬件流控在使用 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和TIMER0app_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.h2添加全局变量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和TIMER0app_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?pwd999m 提取码: 999m • 由 Leung 写于 2024 年 11 月 4 日
• 参考nRF5 SDK v17.1.0: Libuarte - advanced UARTE driver (nordicsemi.com) nrf52840蓝牙开发之Libuarte外设移植