UART 串口是最常用的外设功能之一,csk6 UART 串口数据通信方式有两种:轮询(polling)和中断(interrupt),本章节将通过示例介绍 串口 API 接口和使用方法。
CSK6 提供了 3 组 UART 串口外设(UART0、UART1、UART2),UART 外设具有如下特性:
SDK 中提供了四个与 UART 相关的示例:
示例路径 | 功能说明 |
---|---|
{SDK}\.sdk\csk\samples\driver\uart\uart_configure |
演示串口配置的动态切换。 |
{SDK}\.sdk\csk\samples\driver\uart\uart_polling |
演示串口通过轮询自发自收。 |
{SDK}\.sdk\csk\samples\driver\uart\uart_interrupt |
演示中断模式下串口1和串口2互相发送数据。 |
{SDK}\.sdk\csk\samples\driver\uart\uart_async |
简单的async示例,需将UART1的TX与RX进行连接。 |
串口接收数据
int uart_poll_in(const struct device *dev, unsigned char *p_char)
从设备中读取一个字符作为输入,这个接口检查接收方是否存在有效数据。当接收方存在有效数据时,该接口从设备中读取一个字符,存储到 p_char 指向的地址,并将调用线程返回0,否则返回 -1,该接口是一个非阻塞调用。
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
p_char | 字符指针 |
串口发送数据
void uart_poll_out(const struct device *dev, unsigned char out_char)
写一个字符到设备输出,这个接口会检查发射的transmitter是否已经填满数据,当transmitter未满时,该接口会向数据寄存器写入一个字符。否则,该接口将等待并阻塞调用它的线程,是一个阻塞调用。
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
out_char | 等待发送的字符 |
更多 UART Polling API接口描述请查阅zephyr官网Polling UART API。
设置 IRQ(中断) 回调函数指针
void uart_irq_callback_user_data_set(const struct device *dev,
uart_irq_callback_user_data_t cb,
void *user_data)
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
cb | 指向回调函数的指针 |
user_data | 要传递给回调函数的数据 |
在 ISR(中断服务) 中开始处理中断
int uart_irq_update(const struct device * dev)
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
检查是否有 IRQ(中断) 挂起。
int uart_irq_is_pending(const struct device * dev)
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
检查 UART RX 缓冲区是否有接收到字符
int uart_irq_rx_ready(const struct device * dev)
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
从 FIFO 读取数据
int uart_fifo_read(const struct device *dev, uint8_t *rx_data,
const int size)
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
rx_data | 数据容器 |
size | 数据容器大小 |
用数据填充 FIFO
int uart_fifo_fill(const struct device *dev,
const uint8_t *tx_data,
int size)
参数说明
字段 | 说明 |
---|---|
dev | UART 设备实例 |
tx_data | 要发送的数据 |
size | 要发送的数据长度 |
更多 UART 中断 API接口描述请查阅zephyr官网Interrupt-driven UART API。
{SDK}\.sdk\csk\samples\driver\uart\uart_configure
演示串口配置的动态切换。
适用开发板:大模型开发套件
编译版型:csk6_duomotai_devkit
在 SDK 根目录(duomotai_ap
)下可通过执行以下指令进行对该示例工程的编译:
lisa zep build -b csk6_duomotai_devkit .sdk\csk\samples\driver\uart\uart_configure -p
编译完成后,编译产物二进制文件位于 build\zephyr\zephyr.bin
使用 Type-C 数据线连接开发套件的 DAP_USB
接口,选中以下其中一种方式对固件进行烧录:
cskburn desktop
是一款聆思推出的桌面烧录工具,在下载并安装 cskburn desktop 烧录工具后,双击图标运行软件:
1.点击串口下拉框,选择连接开发套件后识别到的串口编号;
2.将编译输出的.bin
文件拖拽进烧录区域;
3.点击开始烧录,等待烧录完成。
若您已按照 《环境搭建》 教程完成开发环境的安装,可在编译完成后执行 lisa zep exec cskburn
指令完成烧录。
lisa zep exec cskburn -s \\.\COMxx -C 6 -b 1500000 0x000000 --verify-all .\build\zephyr\zephyr.bin
请将命令行中的的 COMx 替换为开发套件在 PC 上对应的串口号(可通过设备管理器查看)。例如:
COM3
。
lisa zep exec cskburn -s PORT -C 6 0x000000 --verify-all ./build/zephyr/zephyr.bin -b 1500000
请将命令行中的 PORT 替换为开发套件连接在 PC 上对应的串口号。例如:
/dev/ttyUSB0
。
烧录完成后,连接串口终端,按下开发板复位按钮,可看到串口有对应的信息输出。
示例代码流程如下:
hello_world
。{SDK}\.sdk\csk\samples\driver\uart\uart_interrupt
演示串口终端模式下,串口0和串口1之间进行数据收发。
适用开发板:大模型开发套件
编译版型:csk6_duomotai_devkit
在 SDK 根目录(duomotai_ap
)下可通过执行以下指令进行对该示例工程的编译:
lisa zep build -b csk6_duomotai_devkit .sdk\csk\samples\driver\uart\uart_interrupt -p
编译完成后,编译产物二进制文件位于 build\zephyr\zephyr.bin
烧录方法同示例1
。
以下代码与注释已省略一部分非关键接口代码,主要呈现示例的主业务流程与主要接口的使用。
#define UART0 DT_NODELABEL(uart0)
#define UART1 DT_NODELABEL(uart1)
uint8_t rx_buffer[100];
// 串口中断回调函数
static void uart_rx_callback(const struct device *dev, void *user_data)
{
/* 进入中断处理调用 */
uart_irq_update(dev);
while (uart_irq_is_pending(dev)) { /*查询是否有中断挂起*/
if (uart_irq_rx_ready(dev)) {
int recv_len;
recv_len = uart_fifo_read(dev, rx_buffer, 1); /* 读取字符串*/
uart_fifo_fill(dev, rx_buffer, recv_len); /* 发送字符串*/
}
}
}
int main(void)
{
// 从设备树中获取UART0设备
const struct device *uart0 = DEVICE_DT_GET(UART0);
…
// 从设备树中获取UART1设备
const struct device *uart1 = DEVICE_DT_GET(UART1);
…
// 定义两个串口中断的回调函数
uart_irq_callback_user_data_set(uart0, uart_rx_callback, NULL);
uart_irq_callback_user_data_set(uart1, uart_rx_callback, NULL);
// 使能中断
uart_irq_rx_enable(uart0);
uart_irq_rx_enable(uart1);
while (1) {
k_msleep(20);
}
return 0;
}
在本实例的 boards\csk6_duomotai_devkit.overlay
文件中,可看到本示例中 UART 串口使用到的引脚定义:
&pinctrl {
pinctrl_uart0_rx_default: uart0_rx_default{
pinctrls = <UART0_RXD_GPIOA_02>; //rx pin
};
pinctrl_uart0_tx_default: uart0_tx_default{
pinctrls = <UART0_TXD_GPIOA_03>; //tx pin
};
pinctrl_uart1_rx_default: uart1_rx_default{
pinctrls = <UART1_RXD_GPIOA_13>; //rx pin
};
pinctrl_uart1_tx_default: uart1_tx_default{
pinctrls = <UART1_TXD_GPIOA_12>; //tx pin
};
};
可参照以上示例修改 UART 串口引脚,支持的引脚配置组合可参照
duomotai_ap\.sdk\csk\dts\arm\csk
文件。
{SDK}\.sdk\csk\samples\driver\uart\uart_polling
演示串口终端模式下,串口0和串口1之间进行数据收发。
适用开发板:大模型开发套件
编译版型:csk6_duomotai_devkit
在 SDK 根目录(duomotai_ap
)下可通过执行以下指令进行对该示例工程的编译:
lisa zep build -b csk6_duomotai_devkit .sdk\csk\samples\driver\uart\uart_polling -p
编译完成后,编译产物二进制文件位于 build\zephyr\zephyr.bin
烧录方法同示例1
。
以下代码与注释已省略一部分非关键接口代码,主要呈现示例的主业务流程与主要接口的使用。
…
// 定义一个宏 UART0,表示设备树中标记为 uart0 的节点
#define UART0 DT_NODELABEL(uart0)
int main(void)
{
char rx_char;
// 从设备树中获取 UART 设备 uart0
const struct device *uart = DEVICE_DT_GET(UART0);
…
// 轮询模式下进行 UART 自收自发通信
while (1) {
if (uart_poll_in(uart, &rx_char) == 0) { /*read a char*/
uart_poll_out(uart, rx_char); /*send a char*/
}
}
return 0;
}
在本实例的 boards\csk6_duomotai_devkit.overlay
文件中,可看到本示例中使用到的引脚定义:
&pinctrl {
pinctrl_uart0_rx_default: uart0_rx_default{
pinctrls = <UART0_RXD_GPIOA_02>; //rx pin
};
pinctrl_uart0_tx_default: uart0_tx_default{
pinctrls = <UART0_TXD_GPIOA_03>; //tx pin
};
};
{SDK}\.sdk\csk\samples\driver\uart\uart_async
演示如何使用 UART 进行异步收发。
适用开发板:大模型开发套件
编译版型:csk6_duomotai_devkit
使用引脚:
在 SDK 根目录(duomotai_ap
)下可通过执行以下指令进行对该示例工程的编译:
lisa zep build -b csk6_duomotai_devkit .sdk\csk\samples\driver\uart\uart_async -p
编译完成后,编译产物二进制文件位于 build\zephyr\zephyr.bin
烧录方法同示例1
。
以下代码与注释已省略一部分非关键接口代码,主要呈现示例的主业务流程与主要接口的使用。
在工程目录 prj.conf
文件中需使能以下模块:
CONFIG_UART_ASYNC_API=y
CONFIG_LOG=y
CONFIG_DMA=y
CONFIG_DMA_CSK6=y
CONFIG_NOCACHE_MEMORY=y
可通过boards
目录下 overlay 文件的方式完成信号线的设备树配置。
设备树文件csk6_duomotai_devkit.overlay
配置如下:
&pinctrl {
pinctrl_uart1_rx_default: uart1_rx_default{
pinctrls = <UART1_RXD_GPIOA_13>;
};
pinctrl_uart1_tx_default: uart1_tx_default{
pinctrls = <UART1_TXD_GPIOA_12>;
};
};
&dma0 {
status = "okay";
};
&uart1 {
pinctrl-0 = <&pinctrl_uart1_rx_default &pinctrl_uart1_tx_default>;
pinctrl-names = "default";
dmas = <&dma0 0>, <&dma0 1>;
dma-names = "tx", "rx";
status = "okay";
current-speed = <115200>;
};
1.从设备树中获取设备的句柄
const struct device *uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart1));
2.UART 回调函数设置
uart_callback_set(uart_dev, uart_async_callback, NULL);
3.启用 UART 接收
uart_rx_enable(uart_dev, rx_buf, sizeof(rx_buf), 50 * USEC_PER_MSEC);
4.启动 UART 传输
uart_tx(uart_dev, tx_buf, sizeof(tx_buf), 100 * USEC_PER_MSEC);
5.等待传输完成信号量 tx_done,如果超时则记录错误并返回
k_sem_take(&tx_done, K_MSEC(100)) != 0
6.等待接收准备信号量 rx_rdy,如果超时则记录错误并返回。
k_sem_take(&rx_rdy, K_MSEC(100)) != 0
7.比较并打印结果
按下开发板复位按钮,连接 PA12 和 PA13 引脚,可通过串口查看输出结果
int main(void)
{
const struct device *uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart1));
if (!device_is_ready(uart_dev)) {
LOG_ERR("uart device not ready!");
return -1;
}
printk("Uart async tx-rx sample\n");
memset(rx_buf, 0, sizeof(rx_buf));
uart_callback_set(uart_dev, uart_async_callback, NULL);
uart_rx_enable(uart_dev, rx_buf, sizeof(rx_buf), 50 * USEC_PER_MSEC);
uart_tx(uart_dev, tx_buf, sizeof(tx_buf), 100 * USEC_PER_MSEC);
if (k_sem_take(&tx_done, K_MSEC(100)) != 0) {
LOG_ERR("TX_DONE timeout\n");
return -1;
}
if (k_sem_take(&rx_rdy, K_MSEC(100)) != 0) {
LOG_ERR("RX_RDY timeout\n");
return -1;
}
if (memcmp(tx_buf, rx_buf, sizeof(rx_buf)) != 0) {
printk("Data check failed\n");
return -1;
}
printk("Uart async tx-rx complete\n");
return 0;
}