这一节主要内容是 UART 驱动的分层实现、UART APIs 以及如何调用 UART APIs 来实现基本的串口打印。
UART 用于芯片和串行端口之间的数据传输。UART 驱动程序经过多层的封装简化了应用程序对 UART 外设的读写操作,应用程序开发者只需要调用封装好的驱动接口就可以操作串口进行读写了。
当然 UART 也有多种操作模式,例如:阻塞、非阻塞、轮询以及文本/二进制模式。我们可以通过相应的参数配置来选择需要的模式继而进行数据传输。
虽然在应用层直接调用几个驱动接口就可操作 UART 进行读写,但是在驱动程序内部从接口函数到底层硬件操作是通过了多层封装的。如图 1 所示是 UART 驱动程序的分层实现图。
图1. UART 驱动程序的分层实现
图 1 可以看出应用程序开发者只需要直接调用中间件层的驱动接口(例如:UART_init、UART_open 等等)就可以实现 UART 驱动功能。这里的中间件层就是程序中的 UART.c 和 UART.h 所在层。
中间件层规范统一了应用程序的调用接口,对于 TI 不同类型的芯片平台它们在这一层给出的接口都是一样的,应用层都是调用相同的接口来实现 UART 功能。这样做的好处是增强了程序的可移植性,不管你的平台怎么换,应用程序都是不变的。
中间件层往下就是业务逻辑层,从业务逻辑层开始往下根据不同的芯片平台其接口封装实现就不尽相同了。以 CC26XX 芯片平台为例,业务逻辑层就位于 UARTCC26XX.c 和 UARTCC26XX.h 所在的层。这一层主要是调用下一层驱动库中的函数进行一些逻辑操作、实现相应驱动功能接口的封装。需要注意的是这一层封装的驱动接口函数被全部放在一个函数指针结构体中。
如清单 1 所示,中间件层不直接调用这些驱动接口,而是通过一个配置文件( CC2640R2_LAUNCHXL.c )将装有驱动接口指针的结构体指针注册到 UART_config 中。
如清单 2 所示,这样中间件层通过调用 UART_config 中的结构体指针就可以调用业务逻辑层的驱动接口了。
清单1 :业务逻辑层驱动接口指针结构
const UART_FxnTable UARTCC26XX_fxnTable = {
UARTCC26XX_close,
UARTCC26XX_control,
UARTCC26XX_init,
UARTCC26XX_open,
UARTCC26XX_read,
UARTCC26XX_readPolling,
UARTCC26XX_readCancel,
UARTCC26XX_write,
UARTCC26XX_writePolling,
UARTCC26XX_writeCancel
};
清单2 :UART_config 中的驱动接口结构体指针注册
const UART_Config UART_config[CC2640R2_LAUNCHXL_UARTCOUNT] = {
{
.fxnTablePtr = &UARTCC26XX_fxnTable,//业务逻辑层接口函数结构体指针注册
.object = &uartCC26XXObjects[CC2640R2_LAUNCHXL_UART0],
.hwAttrs = &uartCC26XXHWAttrs[CC2640R2_LAUNCHXL_UART0]
},
};
业务逻辑层再往下就是驱动库层( driver library ),业务逻辑层是直接调用这一层的接口函数来实现相应功能的。驱动库层位于 uart.c 和 uart.h 所在的层,这一层就开始与硬件接触,对相应寄存器操作来实现串口驱动了。
之前提到 UART 的配置数组 UART_config[]位于相应芯片平台的配置文件中。以 CC26XX 芯片平台为例,其配置文件为 CC2640R2_LAUNCHXL.c 。如清单 3 所示,是 CC2640R2_LAUNCHXL.c 中关于 UART 的配置代码段。
清单3 :UART 的配置代码段
/*
* =============================== UART ===============================
*/
#include <ti/drivers/UART.h>
#include <ti/drivers/uart/UARTCC26XX.h>
UARTCC26XX_Object uartCC26XXObjects[CC2640R2_LAUNCHXL_UARTCOUNT];
const UARTCC26XX_HWAttrsV2 uartCC26XXHWAttrs[CC2640R2_LAUNCHXL_UARTCOUNT] = {
{
.baseAddr = UART0_BASE,
.powerMngrId = PowerCC26XX_PERIPH_UART0,
.intNum = INT_UART0_COMB,
.intPriority = ~0,
.swiPriority = 0,
.txPin = CC2640R2_LAUNCHXL_UART_TX,
.rxPin = CC2640R2_LAUNCHXL_UART_RX,
.ctsPin = PIN_UNASSIGNED,
.rtsPin = PIN_UNASSIGNED
}
};
const UART_Config UART_config[CC2640R2_LAUNCHXL_UARTCOUNT] = {
{
.fxnTablePtr = &UARTCC26XX_fxnTable,
.object = &uartCC26XXObjects[CC2640R2_LAUNCHXL_UART0],
.hwAttrs = &uartCC26XXHWAttrs[CC2640R2_LAUNCHXL_UART0]
},
};
const uint_least8_t UART_count = CC2640R2_LAUNCHXL_UARTCOUNT;
可以看到 UART_config[] 数组中的元素有三个参数,分别是 .fxnTablePtr
、.object
、.hwAttrs
。下面分别来看一下这三个参数的意义。
.fxnTablePtr
里面是驱动具体的实现函数。这些驱动函数就是来自业务逻辑层。它被赋值成 UARTCC26XX_fxnTable
的指针,这个结构体就是清单 1 中所示的业务逻辑层的驱动函数列表,在这里进行赋值配置之后,中间层的接口函数就可以链接使用它们了。.object
是存放 UART 的各种参数数据。例如控制参数、读写参数等。.hwAttrs
是存放 UART 硬件配置参数的数组。如清单 3 所示,这些硬件参数是需要在使用 UART 之前配置好的。在应用程序层实现 UART 功能的时候能够调用中间件层的接口函数有 11 个。其功能、形参以及返回值如下所示
void UART_init(void)
void UART_Params_init(UART_Params *params)
params
、UART_Params 类型的结构体指针,存放 UART 的相关参数UART_Handle UART_open(uint_least8_t index, UART_Params *params)
index
UART 外设接口的索引;params
UART_Params 类型的结构体指针,存放 UART 的相关参数UART_Handle
如果成功打开UART 外设接口就返回该接口配置数组(UART_config)的句柄,如果发生错误或需要打开的 UART 外设接口已经被打开,则会返回 NULL 指针int_fast16_t UART_control(UART_Handle handle, uint_fast16_t cmd, void *arg)
handle
UART_open 中返回的句柄;cmd
需要处理的命令事务;arg
不用命令事务可能需要的不同类型的参数指针void UART_close(UART_Handle handle)
handle
从 UART_open() 中返回的 UART 外设接口配置数组的句柄,以此来关闭外设接口int_fast32_t UART_write(UART_Handle handle, const void *buffer, size_t size)
handle
UART_open 中返回的句柄;buffer
一个只读指针,其中包含了将要写入的数据;size
需要写入数据的字节数int_fast32_t UART_writePolling(UART_Handle handle, const void *buffer, size_t size)
handle
从 UART_open() 中返回的 UART 外设接口配置数组的句柄; buffer
一个只读指针,其中包含了将要写入的数据;size
需要写入数据的字节数void UART_writeCancel(UART_Handle handle)
handle
从 UART_open() 中返回的 UART 外设接口配置数组的句柄int_fast32_t UART_read(UART_Handle handle, void *buffer, size_t size)
handle
从 UART_open() 中返回的UART外设接口配置数组的句柄; buffer
需要读取数据的目的地址;size
需要读取数据的字节数int_fast32_t UART_readPolling(UART_Handle handle, void *buffer, size_t size)
handle
从 UART_open() 中返回的 UART 外设接口配置数组的句柄; buffer
需要读取数据的额缓冲区地址;size
需要读取数据的字节数void UART_readCancel(UART_Handle handle)
handle
从 UART_open() 中返回的 UART 外设接口配置数组的句柄接下来调用中间件层提供的 UART 接口建立一个独立的线程,实现 “hello world!” 的串口打印。这里直接给出主线程的接口调用例程,如清单4所示:
清单4:UART 外设接口打印主线程代码实现
/*
* ======== uartprintf.c ========
*/
#include <stdint.h>
#include <stddef.h>
/* Driver Header files */
#include <ti/drivers/UART.h>
/* Example/Board Header files */
#include "Board.h"
/*
* ======== mainThread ========
*/
void *mainThread(void *arg0)
{
char input;
const char string[] = "hello world!\r\n";
UART_Handle uart;
UART_Params uartParams;
/* Call driver init functions */
UART_init();
/* Create a UART with data processing off. */
UART_Params_init(&uartParams);
uartParams.writeDataMode = UART_DATA_BINARY;
uartParams.readDataMode = UART_DATA_BINARY;
uartParams.readReturnMode = UART_RETURN_FULL;
uartParams.readEcho = UART_ECHO_OFF;
uartParams.baudRate = 115200;
uart = UART_open(Board_UART0, &uartParams);
if (uart == NULL) {
/* UART_open() failed */
while (1);
}
UART_write(uart, string, sizeof(string));
while(1){
UART_read(uart, &input, 1);
UART_write(uart, &input, 1);
}
}
UART_init()
来初始化 UART 外设接口配置,配置信息是在配置文件 CC2640R2_LAUNCHXL.c 中设置的。上文 UART 的驱动配置中有讲到,在调用 UART_init()
之前,必须要在 CC2640R2_LAUNCHXL.c 中完成所有的配置。UART_Params_init()
将 uartParams 结构体中的参数全部初始化为默认值。UART_write()
将字符串 hello world!
打印出来,然后进入无限循环一直执行接收串口数据,再将串口数据打印出来的操作。下面看一下如何编译,先利用串口工具调试串口功能:
.c
文件,将该段代码拷贝到 .c
文件中,假设给该 .c
文件命名为 uartdebug.c
。uartdebug.c
文件保存在 C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\drivers\uartecho
目录下。C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\drivers\uartecho\tirtos\iar
文件夹下打开 uartecho.eww
IAR 工程,如图2是 IAR 的工程目录。图2. IAR 工程目录
uartcho.c
文件,点击右键,选择 remove 。此时可以看到 uartcho.c
文件被移出工程。source files
文件夹,选择 Add 条目下的 Add Files... ,然后将存放的 uartdebug.c
添加进工程项目如图 3 。图3. IAR 添加文件
uartecho-Debug
点击右键,选择 Rebuild All
编译工程。图4. 调试板程序下载
图5. 串口调试工具调试环境
hello world!
被打印出来,如图 6 所示。图6. 串口调试工具打印效果
test
,如图 7 所示。图7. 串口调试工具发送字符
test
之后,字符串就会被读取然后在串口调试工具中打印出来,如图 8 所示。图8. 串口调试工具不断读取打印字符
文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。