4.3.3. UART 驱动调试指南
4.3.3.1. 概述
芯片
UART1 和 UART7 支持
硬件 自动 流控。 这些
串口 支持 多种 比特率,包括 115200 bps、 230400 bps、 460800 bps、 921600 bps、 1500000 bps、 2000000 bps 和 4000000 bps。 支持
基于 中断 或 基于 DMA 的 传输 模式。
4.3.3.2. 功能描述
典型应用
UART 通常
我们
| 参数 | 值 |
|---|---|
| 波特率 | 115200 |
| 数据位 | 8 |
| 校验位 | 无 |
| 停止 |
1 |
| 流控制 | 无 |
功能原理
UART 一般

需要
UART 通过

说明
起始
位:发送 1 位 逻辑 0 (低电平),开始 传输数据。 数据位:可以
是 5~8 位 的 数据,先发 低位,再发 高位,一般 常见 的 就是 8 位( 1 个 字节),其他 的 如 7 位 的 ASCII 码。 校验位:奇偶校验,将
数据位 加上 校验位, 1 的 位数 为 偶数(偶 校验), 1 的 位数 4 为 奇数(奇 校验)。 停止
位:停止 位是 数据传输 结束 的 标志,可以 是 1/1.5/2 位 的 逻辑 1 (高电平)。 空闲
位:空闲 时 数据线 为 高电平 状态,代表 无 数据传输。

如果
如果
工作方式
中断
模式:数据 接收 和 发送 通过 中断 处理,适用 于 数据量 较 小 或 对 实时性 要求 较 高 的 场景。 DMA 模式:数据
通过 DMA 搬运,适用 于 数据量 较大 或 需要 高 吞吐量 的 场景。 除 UART0 作为 内核 的 默认 UART 输出, Linux 禁止 使用 DMA 搬运 ; 其他 UART 均 支持 使用 DMA 搬运。 详细 可以 查看 设备树 节点 部分。配置
4.3.3.3. 驱动代码
uboot 下驱动代码
dtsi 文件
uboot/arch/arm/dts/x5.dtsi
dsp_uart: serial@32120000 {
compatible = "ns16550a"; /*指定 UART 控制器的兼容类型*/
reg = <0x32120000 0x10000>; /*定义 UART 寄存器的基地址和地址空间大小*/
clock-frequency = <UART_CLK_FREQ>; /*指定 UART 的输入时钟频率*/
reg-shift = <2>; /*寄存器访问的偏移量*/
current-speed = <115200>; /*UART 使用的波特率115200*/
u-boot,dm-pre-reloc; /*表示此设备在 u-boot 的早期阶段需要初始化*/
status = "okay"; /*表示启用该设备*/
};
驱动
uboot/drivers/serial/ns16550.c
Kernel 下驱动代码以及配置、设备树
代码路径
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_dwlib.c
drivers/tty/serial/8250/8250_dwlib.h
内核配置
SERIAL_8250_DW
SERIAL_8250_DWLIB

设备树节点配置
UART 控制器arch/arm64/boot/dts/hobot/x5.dtsi 文件
X5 的 UART 控制器
例如
/* arch/arm64/boot/dts/hobot/x5-evb.dts */
...
&uart0 {
status = "okay";
};
&uart2 {
status = "okay";
pinctrl-names = "default"; /* 定义管脚控制的名称组,默认为 "default" */
pinctrl-0 = <&pinctrl_uart2>; /* 将 pinctrl_uart2 的管脚配置应用到 UART2 */
...
};
&uart5 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart5>;
};
...
备注:
x5.dtsi 中
DTS 配置 DMA 绑定
X5 所有 UART 均
以 UART7 为例:
&uart7 {
status = "okay"; /*启用该设备*/
pinctrl-names = "default"; /*定义管脚控制的名称组,默认为 "default*/
pinctrl-0 = <&pinctrl_uart7>; /*将 pinctrl_uart7 的管脚配置应用到 UART7*/
dma-names = "tx", "rx"; /*定义 DMA 通道的名称,分别为 tx 和 rx*/
dmas = <&axi_dmac 1>, <&axi_dmac 0>; /*分别为 UART7 配置发送和接收的 DMA 通道 , 0用于接收,1用于发送*/
}
注意:
UART7 在 EVB 上
默认 功能 为 GPIO,如果 需要 UART7 的话,需要 首先 从 ls_gpio0_porta内将 UART7 对应 的 PIN( lsio_gpio0_0~lsio_gpio0_3)删掉;UART0 作为
内核 的 默认 UART 输出, Linux 禁止 使用 DMA 搬运。
UART DMA 握手
| RX | TX | |
|---|---|---|
| UART1 | 2 | 3 |
| UART2 | 4 | 5 |
| UART3 | 6 | 7 |
| UART4 | 8 | 9 |
| UART5 | 35 | 36 |
| UART6 | 37 | 38 |
| UART7 | 0 | 1 |
4.3.3.4. 功能使用
uboot 阶段使用
查看
printenv baudrate
设置
setenv bootargs "console=tty1 console=ttyS0,115200"
saveenv
kenel 阶段使用
Kernel 阶段
例如:
修改
&uart5 {
status = "disabled"; /*关闭 UART5*/
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart5>;
};
或者console=ttyS0,115200n8 这部分
root@buildroot:~# cat /proc/cmdline
console=ttyS0,115200n8 root=/dev/mmcblk0p9 ro rootwait hobotboot.slot_suffix=_a hobotboot.reason=COLD_BOOT hobotboot.medium=MMC hobotboot.mode=normal hobotboot.ab_switch_reason=normal hobotboot.pmic_type=single-pmic
用户态阶段使用
该
Linux 系统
/dev/ttySx:标准
串口, x 表示 编号,从 0 开始。 /dev/ttyUSBx: USB 转
串口 设备。
我们
常用工具 microcom
首先lsof /dev/ttyS1
检查microcom -s 115200 /dev/ttyS1
然后Hello D-Robotics
观察
在
软件 API(以回环测试举例)
硬件
可以
编译 uart_duplex.c 代码,编译
我们mkdir uart_duplexcd uart_duplex 后
/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc -o uart_duplex uart_duplex.c -lpthread
构建
.
├── uart_duplex
└── uart_duplex.c
将/userdata/uart_duplex
uart_duplex.c 具体
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#define BUFF_SIZE (20 * 1024 * 1024)
pthread_t recv_thread_id;
pthread_t recv_check_thread_id;
pthread_t send_thread_id;
char send_buffer[BUFF_SIZE] = {0};
char recv_buffer[BUFF_SIZE] = {0};
static uint32_t test_size = 1024;
static uint32_t baud = 4000000;
static uint32_t test_count = 0;
int g_fd;
uint64_t recv_total = 0;
sem_t sem_check;
#define FRAME_LEN 512
#if 1
static void dump_recv_data(uint32_t sum, uint32_t len)
{
int ii = 0;
printf("dump receive data:\n");
for (ii = 0; ii < len; ii += 4) {
printf("0x%x: 0x%x, 0x%x, 0x%x, 0x%x\n", sum + ii,
recv_buffer[sum + ii],
recv_buffer[sum + ii + 1],
recv_buffer[sum + ii + 2],
recv_buffer[sum + ii + 3]);
}
}
static void dump_send_data(uint32_t sum, uint32_t len)
{
int ii = 0;
printf("dump send data:\n");
for (ii = 0; ii < len; ii += 4) {
printf("0x%x: 0x%x, 0x%x, 0x%x, 0x%x\n", sum + ii,
send_buffer[sum + ii],
send_buffer[sum + ii + 1],
send_buffer[sum + ii + 2],
send_buffer[sum + ii + 3]);
}
}
#endif
static void set_baudrate(int fd, int nSpeed)
{
struct termios newtio;
tcgetattr(fd, &newtio);
switch (nSpeed) {
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 19200:
cfsetispeed(&newtio, B19200);
cfsetospeed(&newtio, B19200);
break;
case 38400:
cfsetispeed(&newtio, B38400);
cfsetospeed(&newtio, B38400);
break;
case 57600:
cfsetispeed(&newtio, B57600);
cfsetospeed(&newtio, B57600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 230400:
cfsetispeed(&newtio, B230400);
cfsetospeed(&newtio, B230400);
break;
case 921600:
cfsetispeed(&newtio, B921600);
cfsetospeed(&newtio, B921600);
break;
case 1000000:
cfsetispeed(&newtio, B1000000);
cfsetospeed(&newtio, B1000000);
break;
case 1152000:
cfsetispeed(&newtio, B1152000);
cfsetospeed(&newtio, B1152000);
break;
case 1500000:
cfsetispeed(&newtio, B1500000);
cfsetospeed(&newtio, B1500000);
break;
case 2000000:
cfsetispeed(&newtio, B2000000);
cfsetospeed(&newtio, B2000000);
break;
case 2500000:
cfsetispeed(&newtio, B2500000);
cfsetospeed(&newtio, B2500000);
break;
case 3000000:
cfsetispeed(&newtio, B3000000);
cfsetospeed(&newtio, B3000000);
break;
case 3500000:
cfsetispeed(&newtio, B3500000);
cfsetospeed(&newtio, B3500000);
break;
case 4000000:
cfsetispeed(&newtio, B4000000);
cfsetospeed(&newtio, B4000000);
break;
default:
printf("\tSorry, Unsupported baud rate, use previous baudrate!\n\n");
break;
}
tcsetattr(fd,TCSANOW,&newtio);
}
static void set_termios(int fd)
{
struct termios term;
tcgetattr(fd, &term);
term.c_cflag &= ~(CSIZE | CSTOPB | PARENB | INPCK);
term.c_cflag |= (CS8 | CLOCAL | CREAD);
term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
term.c_oflag &= ~(OPOST | ONLCR | OCRNL);
term.c_iflag &= ~(ICRNL |INLCR | IXON | IXOFF | IXANY);
term.c_cc[VTIME] = 0;
term.c_cc[VMIN] = 1;
tcsetattr(fd, TCSAFLUSH, &term);
}
static void *send_test(void *times)
{
/*send thread*/
struct timeval start, end;
int32_t i = 0;
uint32_t j = 0;
uint32_t tmp = 0;
uint32_t exe_count = 0;
int32_t ret = 0;
float ts = 0;
printf("Start send thread\n");
sleep(1);
if (test_count == 0) {
tmp = 10;
} else
tmp = test_count;
for (j = 0; j < tmp; j++) {
if (test_count == 0)
j = 0;
sleep(1);
printf("This is uart send %d times\n", ++exe_count);
gettimeofday(&start, NULL);
for (i = 0; i < test_size * 1024; i = i + FRAME_LEN) {
ret = write(g_fd, &send_buffer[i], FRAME_LEN);
if (ret < FRAME_LEN) {
printf("write ttyS2 error\n");
return NULL;
}
}
#if 1
gettimeofday(&end, NULL);
// printf("start %ld sec, %ld usec, end %ld sec, %ld usec\n", start.tv_sec, start.tv_usec, end.tv_sec, end.tv_usec);
ts = ((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec)) / 1000;
printf("send %dKbytes,time:%fms, BPS:%f\n", test_size, ts, test_size * 1000 / (ts / 1000));
#endif
}
// close(g_fd);
return NULL;
}
static void *recv_test(void *times)
{
int32_t j = 0;
uint32_t exe_count = 0;
int tmp = 0;
int size = 0;
int sum = 0;
int last_count = 0;
int len = 0;
int len_frame = 0; /*use to get correct frame len*/
printf("Start receive thread\n");
memset(recv_buffer, 0, sizeof(recv_buffer));
if (test_count == 0) {
tmp = 10;
} else
tmp = test_count;
for (j = 0; j < tmp; j++) {
sum = 0;
last_count = 0;
if (test_count == 0)
j = 0;
printf("This is receive test %d times\n", ++exe_count);
//gettimeofday(&start, NULL);
size = test_size * 1024;
while (size > 0) {
len = read(g_fd, &recv_buffer[sum], FRAME_LEN);
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
usleep(1000); // 等待 1ms 后重试
continue;
} else {
perror("read error");
return NULL;
}
}
recv_total += len;
len_frame += len;
if (len_frame >= FRAME_LEN) {
len_frame -= FRAME_LEN;
sem_post(&sem_check);
}
#if 0
ret = memcmp(&recv_buffer[sum], &send_buffer[sum], len);
if (ret != 0) {
printf("data compare error\n");
return NULL;
}
#endif
sum +=len;
size -= len;
if ((sum - last_count) > 100 * 1024) {
printf("receive sum:%d bytes\n", sum);
last_count = sum;
}
}
#if 0
gettimeofday(&end, NULL);
printf("start %ld sec, %ld usec, end %ld sec, %ld usec\n", start.tv_sec, start.tv_usec, end.tv_sec, end.tv_usec);
ts = ((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec)) / 1000;
printf("receive %dKbytes,time:%fms, BPS:%f\n", test_size, ts, test_size * 1000 / (ts / 1000));
#endif
}
// close(g_fd);
return NULL;
}
int32_t error_bit(uint64_t *data1, uint64_t *data2, int32_t len)
{
uint64_t c=0;
int32_t sum = 0;
int i = 0;
for(i = 0; i < len / 8; i++) {
c = data1[i] ^ data2[i];
while(c!=0) {
c &= (c - 1);
sum++;
}
}
return sum;
}
static void *recv_check_test(void *times)
{
int32_t check_pos = 0;
uint32_t *cur_frame = NULL;
int32_t error_bit_cnt = 0;
printf("Start recv_check thread\n");
while (1) {
sem_wait(&sem_check);
/*check data*/
cur_frame = (uint32_t *)&recv_buffer[check_pos];
if (*cur_frame != check_pos / FRAME_LEN) {
printf("error: may lost frame, curruent frame is %d, expected frame is %d position: 0x%x\n",
*cur_frame, check_pos / FRAME_LEN, check_pos);
//dump_recv_data(check_pos, FRAME_LEN);
//dump_send_data(check_pos, FRAME_LEN);
error_bit_cnt = 0;
error_bit_cnt = error_bit((uint64_t *)&recv_buffer[check_pos],
(uint64_t *)&send_buffer[check_pos],
FRAME_LEN / 8);
check_pos += FRAME_LEN;
printf("test total data: 0x%lx, error bit count:%d\n", recv_total, error_bit_cnt);
if (check_pos == test_size * 1024) {
//exit(1);
printf("uart: frame head error\n");
}
continue;
}
error_bit_cnt = 0;
error_bit_cnt = error_bit((uint64_t *)&recv_buffer[check_pos],
(uint64_t *)&send_buffer[check_pos],
FRAME_LEN / 8);
if (error_bit_cnt) {
printf("test total data: 0x%lx!!!!!!!, error bit count:%d\n", recv_total, error_bit_cnt);
//dump_recv_data(check_pos, FRAME_LEN);
//dump_send_data(check_pos, FRAME_LEN);
check_pos += FRAME_LEN;
if (check_pos == test_size * 1024) {
//exit(1);
printf("uart: frame data error\n");
}
continue;
}
memset(&recv_buffer[check_pos], 0, FRAME_LEN);
check_pos += FRAME_LEN;
if (check_pos == test_size * 1024) {
check_pos = 0;
printf("### Check the received data is correct ###\n");
}
}
return NULL;
}
static const char short_options[] = "s:u:c:b:d:h";
static const struct option long_options[] = {
{"size", required_argument, NULL, 's'},
{"baudrate", required_argument, NULL, 'b'},
{"count", required_argument, NULL, 'c'},
{"device", required_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0}};
int main(int argc, char *argv[])
{
int ret = 0;
char *pDevice = NULL;
int i = 0;
int32_t cmd_parser_ret = 0;
uint32_t *frame_num = NULL;
uint32_t *frame_value = NULL;
while ((cmd_parser_ret = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
switch (cmd_parser_ret) {
case 's':
test_size = atoi(optarg);
break;
case 'b':
baud = atoi(optarg);
break;
case 'c':
test_count = atoi(optarg);
break;
case 'd':
pDevice = optarg;
break;
case 'h':
printf("**********UART STRESS TEST HELP INFORMATION*********\n");
printf(">>> -s/--size [test size,unit--Kbytes,default is 1M, MAX is 20M]\n");
printf(">>> -b/--baudrate [baud,default is 4M]\n");
printf(">>> -c/--count [test count,default is forever]\n");
printf(">>> -d/--uart [uart device, user must set this]\n");
return 0;
}
}
if (baud > 4000000) {
printf("baud is larger than max baud\n");
return -1;
}
g_fd = open(pDevice, O_RDWR | O_NOCTTY);
if (0 > g_fd) {
printf("open fail\n");
return -1;
}
set_baudrate(g_fd, baud);
set_termios(g_fd);
printf("test size:%d Kbytes, baud:%d\n", test_size, baud);
for (i = 0; i < test_size * 1024; i+=4) {
if (i % FRAME_LEN) {
frame_value = (uint32_t *)&send_buffer[i];
*frame_value = rand();
}
}
for (i = 0; i < test_size * 1024 / FRAME_LEN; i++) {
frame_num = (uint32_t *)&send_buffer[i * FRAME_LEN];
*frame_num = i;
// printf("pos:0x%x, value:0x%x\n", i * FRAME_LEN, *frame_num);
}
sem_init(&sem_check, 0, 0);
ret = pthread_create(&recv_thread_id,
NULL,
recv_test,
NULL);
if (ret < 0) {
printf("create uart1 test thread failed\n");
return -1;
}
ret = pthread_create(&send_thread_id,
NULL,
send_test,
NULL);
if (ret < 0) {
printf("create uart2 test thread failed\n");
return -1;
}
ret = pthread_create(&recv_check_thread_id,
NULL,
recv_check_test,
NULL);
if (ret < 0) {
printf("create receive check thread failed\n");
return -1;
}
pthread_join(recv_thread_id, NULL);
pthread_join(recv_check_thread_id, NULL);
pthread_join(send_thread_id, NULL);
close(g_fd);
return 0;
}
使用
# ./uart_duplex -c 100 -d /dev/ttyS2
test size:1024 Kbytes, baud:4000000
Start receive thread
Start send thread
Start recv_check thread
This is receive test 1 times
This is uart send 1 times
receive sum:102416 bytes
receive sum:204832 bytes
...
receive sum:819328 bytes
receive sum:921744 bytes
receive sum:1024160 bytes
send 1024Kbytes,time:4507.000000ms, BPS:227202.125000
This is receive test 2 times
### Check the received data is correct ###
This is uart send 2 times
receive sum:102416 bytes
receive sum:204832 bytes
...
receive sum:921744 bytes
receive sum:1024160 bytes
send 1024Kbytes,time:4609.000000ms, BPS:222174.000000
This is receive test 3 times
### Check the received data is correct ###
This is uart send 3 times
receive sum:102416 bytes
receive sum:204832 bytes
...
命令
有
这里
该
关键
| 函数 / 代码 |
功能 |
|---|---|
g_fd = open(pDevice, O_RDWR ...) |
打开 |
set_baudrate(int fd, int nSpeed) |
设置 |
set_termios(int fd) |
配置 |
len = read(g_fd, &recv_buffer[sum], FRAME_LEN) |
从recv_buffer 中,每次FRAME_LEN 长度 |
ret = write(g_fd, &send_buffer[i], FRAME_LEN) |
向send_buffer 中FRAME_LEN 长度 |
send_test(void *times) |
发送 |
recv_test(void *times) |
接收 |
recv_check_test(void *times) |
校验 |
error_bit(uint64_t *data1, uint64_t *data2, int32_t len) |
计算 |
主函数 main(int argc, char *argv[]) |
解析 |
4.3.3.5. 常见问题
由于 UART 协议
除了 波特率,有些 模块 或者 通信协议 需要 对 停止 位、校验位 等 都 有 设置,需要 逐一 对齐。 在
使用 串口 调试 的 时候,有些 串口 调试 工具 有 Flow control 的 选项,可以 选择 Xon/Xoff,如果 选择 None,则 可能 敲击 键盘 调试 的 时候 微控器 没有 反应。 使用
前,检查 /dev/ttySx 等 节点 是否 真实 存在。 使用
前,检查 /dev/ttySx 等 节点 是否 被 占用( lsof /dev/ttySx)。