赞
踩
点击上方蓝字,关注微联智控工作室
可点击右上角的 …,分享这篇文章
Nordic nRF52840芯片内部集成了一个UART外设模块,用于双向异步串口通信。这个外设模块主要有以下特性:全双工,自动硬件流控、奇偶校验、1位停止位。
nRF52840芯片的UART模块在引脚管理上比很多单片机灵活,可以通过引脚配置寄存器,把TXD,RXD,CTS,RTS这四个控制信号映射到48个引脚中的任何一个物理引脚。由于nRF52840芯片只有一个UART模块,因此,不能把同一个引脚都映射为不同的信号,例如,不能同时把P0.06引脚同时映射为TXD和RXD。
nRF52840芯片内部除了集成UART串口模块,还有UARTE模块,UARTE模块和UART模块这两者的区别是,UARTE模块是通过EasyDMA进行数据收发的,使用DMA技术进行数据收发,可以有效降低CPU的负担,因此,后续的例程都是使用UARTE模块进行开发。
nRF52840的UARTE模块的工作原理:不管是发送数据还是接收数据,都是由EasyDMA负责数据在内存RAM和物理线路之间的传输。发送数据的时候,EasyDMA把数据从内存RAM中读出并传输到物理线路中。接收数据的时候,EasyDMA把接收到的数据从物理线路写入到内存RAM中,因此,使用UARTE收发数据时,需要在内存RAM中指定接收和发送缓冲区。
在Nordic提供的SDK里面,有专门针对UART和UARTE操作的库函数,这两者共用一个程序函数库,称为APP_UART,在这个APP_UART中,封装了对UART和UARTE的操作函数,并且通过配置sdk_config.h里面的宏定义来进行条件编译。
软件开发前准备:
开发板:Nordic官方开发板nRF52840-DK(PCA10056)。
编译器:SEGGEREmbedded Studio v4.22
SDK版本:nRF5_SDK_15.2.0_9412b96
1、我们基于前面构建的“003_gpio_test”工程来进行软件开发。先复制一份工程,并重命名为“004_uart_test”,并且把GPIOTE相关的驱动模块添加到工程,完成后打开工程,如下图所示。
以上添加的文件,具体目录如下表所示:
源文件 | SDK中的路径 | 描述说明 |
nrf_drv_uart.c | integration/nrfx/legacy | 旧的UART驱动程序 |
nrfx_uart.c | modules/nrfx/drivers/src | 新的UART驱动程序 |
nrfx_uarte.c | modules/nrfx/drivers/src | 新的UARTE驱动程序 |
app_fifo.c | components/libraries/fifo | FIFO程序模块 |
app_uart_fifo.c | components/libraries/uart | 串口FIFO库文件 |
nrfx_prs.c | modules/nrfx/drivers/src/prs | 外设资源共享文件 |
retarget.c | components/libraries/uart | 串口重定向文件 |
2、为了便于工程管理,我们新建两个文件uarte.c和uarte.h,用来编写串口相关的操作函数,头文件uarte.h的内容如下图所示。
#ifndef _UARTE_H_#define _UARTE_H_#include "nrf_gpio.h"#include "nordic_common.h"#define RX_PIN_NUMBER NRF_GPIO_PIN_MAP(0,8)#define TX_PIN_NUMBER NRF_GPIO_PIN_MAP(0,6)#define CTS_PIN_NUMBER NRF_GPIO_PIN_MAP(0,7)#define RTS_PIN_NUMBER NRF_GPIO_PIN_MAP(0,5)#define UART_TX_BUF_SIZE 256#define UART_RX_BUF_SIZE 256#define UART_HWFC APP_UART_FLOW_CONTROL_DISABLEDextern void my_uart_init(void);#endif
从上述代码可知,头文件根据开发板的原理图,定义了RXD,TXD,CTS,RTS这几个通信引脚的具体物理引脚编号,还有发送和接收缓冲区的大小,定义是否启动硬件流控。
3、再来看看uarte.c文件的内容,如下图所示。
#include #include #include #include "nrf_uart.h"#include "nrf_uarte.h"#include "app_uart.h"#include "app_error.h"#include "uarte.h"static void uart_event_handler(app_uart_evt_t * p_event){ switch(p_event->evt_type){ /*An event indicating that UART data has been received. The data is available in the FIFO and can be fetched using @ref app_uart_get*/ case APP_UART_DATA_READY:{ uint8_t cr; while (app_uart_get(&cr) != NRF_SUCCESS);while (app_uart_put(cr) != NRF_SUCCESS); }break; /*An error in the FIFO module used by the app_uart module has occured. The FIFO error code is stored in app_uart_evt_t.data.error_code field*/ case APP_UART_FIFO_ERROR: APP_ERROR_HANDLER(p_event->data.error_code); break; /*An communication error has occured during reception. The error is stored in app_uart_evt_t.data.error_communication field */ case APP_UART_COMMUNICATION_ERROR: APP_ERROR_HANDLER(p_event->data.error_communication); break; /*An event indicating that UART has completed transmission of all available data in the TX FIFO */ case APP_UART_TX_EMPTY: break; /*An event indicating that UART data has been received, and data is present in data field. This event is only used when no FIFO is configured */ case APP_UART_DATA: break; default:break; }}void my_uart_init(void){ uint32_t err_code; const app_uart_comm_params_t comm_params = { RX_PIN_NUMBER,TX_PIN_NUMBER, RTS_PIN_NUMBER,CTS_PIN_NUMBER, UART_HWFC,false,NRF_UARTE_BAUDRATE_115200 }; APP_UART_FIFO_INIT(&comm_params,UART_RX_BUF_SIZE,UART_TX_BUF_SIZE,uart_event_handler,APP_IRQ_PRIORITY_LOWEST,err_code); APP_ERROR_CHECK(err_code);}
4、先来看看my_uart_init()函数,这个函数主要是调用了串口初始化宏APP_UART_FIFO_INIT,跟踪这个宏定义,展开后的函数原型如下图所示。
这个宏定义主要有5个输入参数和1个输出参数。最后主要是调用了app_uart_init()函数来进行实际的串口初始化。这个宏一开始定义了一个app_uart_buffers_t类型的变量,这个结构体类型是用来指定接收和发送缓存的地址和大小的。对于这个宏的输入参数描述,如下所示。
(1)P_COMM_PARAMS:指向串口通信配置参数的结构体。
(2)RX_BUF_SIZE:数据接收缓冲区的大小,需要2的整数倍。
(3)TX_BUF_SIZE:数据发送缓冲区的大小,需要2的整数倍。
(4)EVT_HANDLER:串口数据事件处理的回调函数。
(5)IRQ_PRIO:串口中断优先级。
(6)ERR_CODE:错误代码。属于输出参数。
5、宏APP_UART_FIFO_INIT最终是调用了app_uart_init()函数来进行串口初始化的,这个函数的声明如下图所示。
这个函数主要有4个输入参数,输入参数描述如下所示。
(1)p_comm_params:串口配置参数结构体。应用程序一开始需要定义一个结构体变量,类型为app_uart_comm_params_t,用来配置串口工作的各种参数。
(2)p_buffers:接收和发送缓冲区,NULL表示不使用缓冲区。
(3)error_handler:串口事件处理的回调函数。
(4)irq_priority:串口中断优先级。
6、在使用APP_UART_FIFO_INIT宏初始化串口时,需要定义串口事件处理的回调函数,当串口有各种事件产生的时候,这个函数就会被调用,在这个函数里面就可以处理各种串口事件。APP_UART这个库函数定义了5种串口事件,定义在app_uart_evt_type_t这个枚举中,如下图所示。
枚举里面定义的串口事件,具体如下:
APP_UART_DATA_READY:串口接收到数据并已存入缓冲区,可通过app_uart_get()函数来读取数据。
APP_UART_FIFO_ERROR:表示串口的FIFO缓冲区发生错误,错误的代码可以通过app_uart_evt_t.data.error_code这个变量查看。
APP_UART_COMMUNICATION_ERROR:串口通信发生错误,错误的代码可以通过app_uart_evt_t.data.error_communication这个变量查看。
APP_UART_TX_EMPTY:表示发送缓冲区的数据以及全部发送完成。
APP_UART_DATA:表示串口收到数据。这个事件仅限在不使用缓冲区的情况下使用。
7、串口事件处理的回调函数,如下图所示。
这个回调函数处理了串口所有的事件,用户可以在这个函数里面处理串口接收到的数据,上述代码是把串口接收到的数据,原包发送出去,实现一个串口收发回环。
8、SDK里面提供两个函数来处理串口数据收发,app_uart_put()函数用来发送数据,app_uart_get()函数用来接收数据。这两个函数都是单字节操作函数。需要注意的是,这两个函数操作的时候,都是非阻塞的。也就是说,当使用app_uart_put()函数发送一个数据的时候,发送成功是表示写入发送缓存成功,不表示数据已经通过物理线路发送成功。当使用app_uart_get()函数来接收数据的时候,如果缓冲区为空,将返回错误代码。
9、最后配置sdk_config.h文件,勾选NRFX_UARTE_ENABLED宏和UART_ENABLED宏,不勾选NRF_UARTE_ENABLED宏,使能UARTE模块的相关代码,如下图所示。
10、连接好开发板,点击“Debug->Go”或按F5,即可编译程序并且把程序下载到开发板运行,使用串口收发工具,不断向开发板串口发送数据。程序的运行情况如以下图所示。
11、源码下载链接:
https://github.com/embediot/bluetooth_low_energy
感谢阅读!
-- END --
欢迎关注 --- 微联智控工作室
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。