串口(UART)是嵌入式设备中比较常用的功能。这篇文章将记录下应用程序中串口操作相关内容。
这篇文章中内容均在下面的开发板上进行测试:
《新唐NUC980使用记录:自制开发板(基于NUC980DK61YC)》
这篇文章是在下面文章基础上进行的:
《新唐NUC980使用记录(5.10.y内核):在用户应用中使用GPIO》
Linux中应用程序对于串口的使用可以参考下面文章: https://digilander.libero.it/robang/rubrica/serial.htm
https://blog.csdn.net/qq_37932504/article/details/121125906
这里简单介绍下相关内容。Linux中应用程序对于串口的使用基本上也就是文件操作,使用 open / write / read
等方法。此外通常使用前需要通过 ioctl
方法来获取与设置串口的波特率、数据位、停止位、流控制等。对于串口而言部分设置在 ioctl
方法之上封装了一些函数,可以方便的进行操作。
串口的设置都集中在下面的结构体中:
struct termio
{
unsigned short c_iflag; /* 输入模式标志 */
unsigned short c_oflag; /* 输出模式标志 */
unsigned short c_cflag; /* 控制模式标志*/
unsigned short c_lflag; /*本地模式标志 */
unsigned char c_line; /* 行规程标志 */
unsigned char c_cc[NCC]; /* 控制字符标志 */
};
结构体中具体内容与设置可以参考上面文章。
首先需要调整设备树来启用串口:
# cd ~/nuc980-sdk/NUC980-linux-5.10.y/
gedit arch/arm/boot/dts/nuc980-dev-v1.0.dts
这里启用了 uart1
:
uart1: serial@b0071000 {
status = "okay";
};
根据 nuc980.dtsi
文件的中的定义 uart1
默认使用引脚为 PA0 - RXD
和 PA1 - TXD
。
# 设置编译工具链
export ARCH=arm; export CROSS_COMPILE=arm-buildroot-linux-gnueabi-
export PATH=$PATH:/home/nx/nuc980-sdk/buildroot-2023.02/output/host/bin
# 编译生成设备树文件
make dtbs
# 编译完成后拷贝到电脑上再拷贝到SD卡中
# sudo cp arch/arm/boot/dts/nuc980-dev-v1.0.dtb /media/sf_common/
# 我这里开发环境和开发板在同一局域网中,所以可以直接通过网络将dtb文件拷贝到开发板上
# 在开发板中挂载boot分区
# mount /dev/mmcblk0p1 /mnt/
# 在ubuntu中使用scp命令拷贝dtb文件到开发板上
# scp arch/arm/boot/dts/nuc980-dev-v1.0.dtb [email protected]:/mnt/
# 拷贝完成后重启开发板即可测试
# reboot
加载新的设备树之后就可以测试新串口的功能了,使用外部的串口模块连接开发板和电脑:
可以看到使用默认波特率发送数据其实很简单,但是实际的使用过程中往往还需要修改串口设置以及接收数据等,所以就需要编写应用程序来处理。
cd ~
# 创建目应用程序录并进入
mkdir -p ~/nuc980-sdk/apps/uart
cd ~/nuc980-sdk/apps/uart
# 创建应用程序文件
gedit uart_test.c
# 应用程序代码见下面章节
# 设置编译工具链
export ARCH=arm; export CROSS_COMPILE=arm-buildroot-linux-gnueabi-
export PATH=$PATH:/home/nx/nuc980-sdk/buildroot-2023.02/output/host/bin
# 编译生成目标应用程序
arm-linux-gcc -o uart_test uart_test.c
# 编译完成后拷贝到电脑上再拷贝到SD卡中
# sudo cp uart_test /media/sf_common/
# 我这里开发环境和开发板在同一局域网中,所以可以直接通过网络将文件拷贝到开发板上
# 在ubuntu中使用scp命令拷贝文件到开发板上
# scp uart_test [email protected]:/root/
#include /* Standard input/output definitions */
#include /* String function definitions */
#include /* UNIX standard function definitions */
#include /* File control definitions */
#include /* Error number definitions */
#include /* POSIX terminal control definitions */
int main(int argc, char **argv)
{
int fd, ret;
struct termios opt;
char c;
if (argc != 2)
{
printf("Usage: uart_test \n");
return -1;
}
/* 打开串口 */
fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
printf("NX applog: can not open %s\n", argv[1]);
return -1;
}
fcntl(fd, F_SETFL, 0); // 读取使用阻塞方式
/* 设置串口属性 */
// tcgetattr(fd, &opt); // 获取现有串口属性
bzero(&opt, sizeof(opt)); // 以0填充结构体
cfsetspeed(&opt, B115200); // 设置串口波特率
opt.c_cflag |= CS8; // 8位数据位
// opt.c_cflag &= ~PARENB; // 没有校验位
// opt.c_cflag &= ~CSTOPB; // 1位停止位
opt.c_cflag |= CREAD | CLOCAL;
opt.c_cc[VMIN] = 1; // 每次读取至少读到一个字节
opt.c_cc[VTIME] = 0; // 读取操作不会超时
tcflush(fd, TCIOFLUSH); // 清空当前输入/输出缓冲区数据
ret = tcsetattr(fd, TCSANOW, &opt); // 使能串口设置
if (ret != 0)
{
printf("NX applog: port set error\n");
return -1;
}
/* 数据收发测试 */
while (1)
{
read(fd, &c, 1); //
printf("NX applog: read 0x%02x %c\n", c, c);
write(fd, &c, 1);
}
close(fd);
return 0;
}
串口操作总体来说并不复杂。另外虽然串口有很多可以设置的属性,但是目前很多属性已经不怎使用了,最常见需要修改的也就波特率了。
/*
* Device Tree Source for NUC980 DEV board
*
* Copyright (C) 2018 Nuvoton Technology Corp.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/dts-v1/;
#include "nuc980.dtsi"
/ {
model = "Nuvoton NUC980 DEV V1.0";
compatible = "nuvoton,nuc980-dev-v1.0", "nuvoton,nuc980";
chosen {
bootargs = "console=ttyS0,115200n8 noinitrd rootfstype=ext4 root=/dev/mmcblk0p2 rw rootwait mem=64M";
};
apb {
uart1: serial@b0071000 {
status = "okay";
};
uart2: serial@b0072000 {
status = "disabled";
};
uart3: serial@b0073000 {
status = "disabled";
};
uart4: serial@b0074000 {
status = "disabled";
};
uart5: serial@b0075000 {
status = "disabled";
};
uart6: serial@b0076000 {
status = "disabled";
};
uart7: serial@b0077000 {
status = "disabled";
};
uart8: serial@b0078000 {
status = "disabled";
};
uart9: serial@b0079000 {
status = "disabled";
};
can0: can@b00a0000 {
status = "disabled";
};
can1: can@b00a1000 {
status = "disabled";
};
rtc: rtc@b0041000 {
status = "disabled";
};
gpio: gpio@b0004000 {
pinctrl-0 = <>;
eint2-config = <0 0 0>;
eint3-config = <0 0 0>;
};
nadc: nadc@b0043000 {
status = "disabled";
};
pwm0: pwm@b0058000 {
status = "disabled";
};
pwm1: pwm@b0059000 {
status = "disabled";
};
etimer0: etimer0@b0050000 {
status = "disabled";
};
etimer1: etimer1@b0050100 {
status = "disabled";
};
etimer2: etimer2@b0051000 {
status = "disabled";
};
etimer3: etimer3@b0051100 {
status = "disabled";
};
i2c0: i2c0@b0080000 {
status = "disabled";
};
i2c1: i2c1@b0081000 {
status = "disabled";
pinctrl-0 = <&pinctrl_i2c1_PB>;
};
i2c2: i2c2@b0082000 {
status = "disabled";
pinctrl-0 = <&pinctrl_i2c2_PB>;
};
};
ahb {
usbh_ehci@b0015000 {
pinctrl-0 = <>; /*disable PWREN and OVC*/
ov_active = <1>;/*disable PWREN and OVC*/
status = "okay";
};
usbh_ohci@b0017000{
status = "okay";
};
usbdev@b0016000 {
status = "okay";
};
fmi@b0019000 {
status = "disabled";
};
sdh@b0018000 {
status = "okay";
};
emac0@b0012000 {
status = "okay";
};
emac1@b0022000 {
status = "disabled";
};
ccap0@b0024000 {
status = "disabled";
};
i2c_gpio0: i2c-gpio-0 {
status = "disabled";
};
ccap1@b0014000 {
status = "disabled";
};
i2c_gpio1: i2c-gpio-1 {
status = "disabled";
};
dma@b0008000 {
status = "okay";
};
i2s: i2s@b0020000 {
status = "disabled";
};
i2s_pcm: i2s_pcm {
status = "disabled";
};
sound {
compatible = "nuvoton,nuc980-audio";
i2s-controller = <&i2s>;
i2s-platform = <&i2s_pcm>;
status = "disabled";
};
ebi: ebi@b0010000 {
status = "disabled";
};
};
nx_node@0 {
compatible = "nx_dts_node";
str = "Naisu 233!";
num = <0x00000000 0x00000020>;
};
nx_node@1 {
compatible = "nx_dts_node";
str = "Hello Naisu!";
num = <0x000000 0x00000040>;
};
};