目录
前言
一、代码位置
二、硬件原理图
三、设备树配置
四、设备注册
五、串口调试功能
六、驱动调试
总结
前言
本文主要讲解如何移植RK3568的串口并且测试连通性
一、代码位置
在Linux kernel 中,使用8250串口通用驱动,以下为主要驱动文件:
drivers/tty/serial/8250/8250_core.c # 8250串口驱动核心
drivers/tty/serial/8250/8250_dw.c # Synopsis DesignWare 8250串口驱动
drivers/tty/serial/8250/8250_dma.c # 8250串口DMA驱动
drivers/tty/serial/8250/8250_port.c # 8250串口端口操作
drivers/tty/serial/8250/8250_early.c # 8250串口early console驱动
在SDK代码下面的kernel目录下输入make ARCH=arm64 menuconfig进入到图形化配置页面,在界面输入“/”,在框内输入8250然后回车,会看下如下页面:
输入对应的数字2可以去8250的配置页面,由于Rockchip SDK中提供的UART默认配置已经使用了8250驱动我们就不需要修改。
二、硬件原理图
如下是硬件上通过高低电平转换将uart转化为rs232,rs232与网口进行连接。
三、设备树配置
rk平台的设备树修改路径都是在kernel\arch\arm64\boot\dts\rockchip下面,具体哪个文件根据对应开发板来决定,通常描述设备硬件配置在rkxxxx.dtsi中,比如在rk3568.dtsi中:
uart4: serial@fe680000 {
compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";
reg = <0x0 0xfe680000 0x0 0x100>;
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_UART4>, <&cru PCLK_UART4>;
clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
dmas = <&dmac0 8>, <&dmac0 9>;
pinctrl-names = "default";
pinctrl-0 = <&uart4m0_xfer>;
status = "disabled";
};
我们需要修改的参数是pinctrl-0和status
uart4有两组数据线
uart4 {
uart4m0_xfer: uart4m0-xfer {
rockchip,pins =
<1 4 2 &pcfg_pull_up>,
<1 6 2 &pcfg_pull_up>;
};
uart4m1_xfer: uart4m1-xfer {
rockchip,pins =
<3 9 4 &pcfg_pull_up>,
<3 10 4 &pcfg_pull_up>;
};
};
我们根据硬件的接法来选择对应的数据线,最后设备树的修改是:
&uart4 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart4m1_xfer>;
};
四、设备注册
在dts中使能UART后,能在系统启动的log中看到以下对应的打印,表示设备正常注册:
root@linaro-alip:/# dmesg |grep ttyS4
[ 0.235933] fe680000.serial: ttyS4 at MMIO 0xfe680000 (irq = 58, base_baud = 1500000) is a 16550A
普通串口设备将会根据dts中的aliase来对串口进行编号,对应注册成ttySx设备。注册的节点为/dev/ttyS4,命名规则是通过dts中的aliases来的。
aliases {
serial0 = &uart0;
serial1 = &uart1;
serial2 = &uart2;
serial3 = &uart3;
}
对应uart0注册为ttyS0,uart0注册为ttyS1,如果需要把uart3注册成ttyS1,可以进行以下修改
serial1 = &uart3;
serial3 = &uart1;
五、串口调试功能
Rockchip UART作为控制台,使用fiq_debugger流程。Rockchip SDK一般会将uart2配置为ttyFIQ0设备。使用以下驱动文件:
drivers/staging/android/fiq_debugger/fiq_debugger.c drivers/soc/rockchip/rk_fiq_debugger.c
arch/arm/mach-rockchip/rk_fiq_debugger.c
在dts中fiq_debugger节点配置如下。由于fiq_debugger和普通串口互斥,
在使能fiq_debugger节点后必须禁用对应的普通串口uart节点。c
hosen: chosen {
bootargs = "earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0";
};
fiq-debugger {
compatible = "rockchip,fiq-debugger";
rockchip,serial-id = <2>;
rockchip,wake-irq = <0>;
/* If enable uart uses irq instead of fiq */
rockchip,irq-mode-enable = <1>;
rockchip,baudrate = <1500000>; /* Only 115200 and 1500000 */
interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&uart2m0_xfer>;
status = "okay";
};
&uart2 {
status = "disabled";
};
rockchip,serial-id:使用的UART编号。修改serial-id到不同UART,fiq_debugger设备也会注册成
ttyFIQ0设备。
pinctrl-0:修改为对应的串口数据脚。
六、驱动调试
现在我们已经得到的串口四的驱动节点,这之后要测试硬件的连通性,比较简单直观的测试方法是对硬件上的TX、RX进行短接,然后输入命令 cat /dev/ttyS3 & 后台监控串口,收到会立即打印输出,现在执行命令 echo “helloworld” > /dev/ttyS3如果观察到执行完命令 立即输出helloworld 则表明收发都是正常的。
还有一种通用的测试方法是在开发板上跑一套应用程序,可以发送数据,可以接收数据。比如如下测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termio.h>
#include <time.h>
#include <pthread.h>
int read_data(int fd, void *buf, int len);
int write_data(int fd, void *buf, int len);
int setup_port(int fd, int baud, int databits, int parity, int stopbits);
void print_usage(char *program_name);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
int data_available = 0;
void *read_thread(void *arg) {
int fd = *(int *)arg;
char buffer[1024]; // 存储读取的数据
while (1) {
int bytes_read = read_data(fd, buffer, sizeof(buffer));
if (bytes_read > 0) {
printf("Read Thread: Read %d bytes: %s\n", bytes_read, buffer);
} else {
// 处理读取错误或设备关闭的情况
break;
}
}
pthread_exit(NULL);
}
void *write_thread(void *arg) {
int fd = *(int *)arg;
char input[1024]; // 存储用户输入的数据
while (1) {
printf("Enter data to write (or 'q' to quit): ");
fgets(input, sizeof(input), stdin);
if (strcmp(input, "q\n") == 0 || strcmp(input, "Q\n") == 0) {
// 用户输入 'q' 或 'Q',退出循环
break;
}
int len = strlen(input);
int bytes_written = write_data(fd, input, len);
if (bytes_written > 0) {
printf("Write Thread: Wrote %d bytes: %s\n", bytes_written, input);
}
}
pthread_exit(NULL);
}
int main(int argc, char *argv[]) //./a.out /dev/ttyS4 115200 8 0 1
{
int fd;
int baud;
int len;
int count;
int i;
int databits;
int stopbits;
int parity;
if (argc != 6) {
print_usage(argv[0]);
return 1;
}
baud = atoi(argv[2]);
if ((baud < 0) || (baud > 921600)) {
fprintf(stderr, "Invalid baudrate!\n");
return 1;
}
databits = atoi(argv[3]);
if ((databits < 5) || (databits > 8)) {
fprintf(stderr, "Invalid databits!\n");
return 1;
}
parity = atoi(argv[4]);
if ((parity < 0) || (parity > 2)) {
fprintf(stderr, "Invalid parity!\n");
return 1;
}
stopbits = atoi(argv[5]);
if ((stopbits < 1) || (stopbits > 2)) {
fprintf(stderr, "Invalid stopbits!\n");
return 1;
}
fd = open(argv[1], O_RDWR, 0);
if (fd < 0) {
fprintf(stderr, "open <%s> error %s\n", argv[1], strerror(errno));
return 1;
}
if (setup_port(fd, baud, databits, parity, stopbits)) {
fprintf(stderr, "setup_port error %s\n", strerror(errno));
close(fd);
return 1;
}
pthread_t read_tid, write_tid;
int ret;
// 创建读取线程
ret = pthread_create(&read_tid, NULL, read_thread, &fd);
if (ret != 0) {
fprintf(stderr, "Failed to create read thread\n");
return 1;
}
// 创建写入线程
ret = pthread_create(&write_tid, NULL, write_thread, &fd);
if (ret != 0) {
fprintf(stderr, "Failed to create write thread\n");
return 1;
}
// 等待读取线程和写入线程结束
pthread_join(read_tid, NULL);
pthread_join(write_tid, NULL);
close(fd);
return 0;
}
static int baudflag_arr[] = {
B921600, B460800, B230400, B115200, B57600, B38400,
B19200, B9600, B4800, B2400, B1800, B1200,
B600, B300, B150, B110, B75, B50
};
static int speed_arr[] = {
921600, 460800, 230400, 115200, 57600, 38400,
19200, 9600, 4800, 2400, 1800, 1200,
600, 300, 150, 110, 75, 50
};
int speed_to_flag(int speed)
{
int i;
for (i = 0; i < sizeof(speed_arr)/sizeof(int); i++) {
if (speed == speed_arr[i]) {
return baudflag_arr[i];
}
}
fprintf(stderr, "Unsupported baudrate, use 9600 instead!\n");
return B9600;
}
static struct termio oterm_attr;
int setup_port(int fd, int baud, int databits, int parity, int stopbits)
{
struct termio term_attr;
if (ioctl(fd, TCGETA, &term_attr) < 0) {
return -1;
}
memcpy(&oterm_attr, &term_attr, sizeof(struct termio));
term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL | ISTRIP);
term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL);
term_attr.c_lflag &= ~(ISIG | ECHO | ICANON | NOFLSH);
term_attr.c_cflag &= ~CBAUD;
term_attr.c_cflag |= CREAD | speed_to_flag(baud);
term_attr.c_cflag &= ~(CSIZE);
switch (databits) {
case 5:
term_attr.c_cflag |= CS5;
break;
case 6:
term_attr.c_cflag |= CS6;
break;
case 7:
term_attr.c_cflag |= CS7;
break;
case 8:
default:
term_attr.c_cflag |= CS8;
break;
}
switch (parity) {
case 1:
term_attr.c_cflag |= (PARENB | PARODD);
break;
case 2:
term_attr.c_cflag |= PARENB;
term_attr.c_cflag &= ~(PARODD);
break;
case 0:
default:
term_attr.c_cflag &= ~(PARENB);
break;
}
switch (stopbits) {
case 2:
term_attr.c_cflag |= CSTOPB;
break;
case 1:
default:
term_attr.c_cflag &= ~CSTOPB;
break;
}
term_attr.c_cc[VMIN] = 1;
term_attr.c_cc[VTIME] = 0;
if (ioctl(fd, TCSETAW, &term_attr) < 0) {
return -1;
}
if (ioctl(fd, TCFLSH, 2) < 0) {
return -1;
}
return 0;
}
int read_data(int fd, void *buf, int len)
{
int count;
int ret;
ret = 0;
count = 0;
//while (len > 0) {
ret = read(fd, (char*)buf + count, len);
if (ret < 1) {
fprintf(stderr, "Read error %s\n", strerror(errno));
//break;
}
count += ret;
len = len - ret;
//}
*((char*)buf + count) = 0;
return count;
}
int write_data(int fd, void *buf, int len)
{
int count;
int ret;
ret = 0;
count = 0;
while (len > 0) {
ret = write(fd, (char*)buf + count, len);
if (ret < 1) {
fprintf(stderr, "Write error %s\n", strerror(errno));
break;
}
count += ret;
len = len - ret;
}
return count;
}
void print_usage(char *program_name)
{
fprintf(stderr,
"*************************************\n"
" A Simple Serial Port Test Utility\n"
"*************************************\n\n"
"Usage:\n %s <device> <baud> <databits> <parity> <stopbits> \n"
" databits: 5, 6, 7, 8\n"
" parity: 0(None), 1(Odd), 2(Even)\n"
" stopbits: 1, 2\n"
"Example:\n %s /dev/ttyS4 115200 8 0 1\n\n",
program_name, program_name
);
}
使用交叉编译将可执行程序拷贝进开发板
prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc tx_rx.c -pthread
然后执行如下命令测试:
chmod 777 a.out
./a.out /dev/ttyS4 115200 8 0 1
这是一个自发自收程序,这样就不用与PC端建立连接了,对于测试连通性还是没问题的,当你将tx、rx短接之后你会看到如下打印:
而不进行短接就是如下打印:
文章来源:https://www.toymoban.com/news/detail-594753.html
总结
串口的调试并不复杂,都是在SDK上已有的基础上进行修改,网上的例子也有很多,有兴趣的朋友可以在开源的基础上自己写一份串口测试的应用程序,这个过程才会有点收获,这里只是简单记录一下整个调试和测试过程。文章来源地址https://www.toymoban.com/news/detail-594753.html
到了这里,关于RK3568串口调试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!