UART 使用说明#
概述#
K230 平台集成了 5 个 UART 控制器(uart0 - uart4),基于 Synopsys DesignWare APB UART 核心。
硬件特性#
UART |
基地址 |
中断号 |
状态 |
|---|---|---|---|
uart0 |
0x91400000 |
16 |
disabled |
uart1 |
0x91401000 |
17 |
disabled |
uart2 |
0x91402000 |
18 |
disabled |
uart3 |
0x91403000 |
19 |
disabled |
uart4 |
0x91404000 |
20 |
disabled |
时钟配置#
时钟源:
apb_clk(50 MHz)寄存器宽度:32 位 (
reg-io-width = 4)寄存器偏移步进:4 字节 (
reg-shift = 2)
设备树串口配置#
以 01Studio 板子的 UART3 为例:
启用 UART#
默认情况下 UART 设备状态为 “disabled”,需要在设备树中启用。
&uart3 {
status = "okay";
};
配置串口引脚#
K230 的 iomux 使用 pins 和 function 属性来配置引脚。
// 在 01Studio 板子的 DTS 文件中添加
&iomux {
// UART3 引脚配置
// IO50 = UART3_TXD (alt1), IO51 = UART3_RXD (alt1)
uart3_pins: uart3-pins {
pins = <K230_IO50>, <K230_IO51>;
function = "alt1";
bias-pull-up;
drive-strength = <12>;
input-schmitt-enable;
};
};
&uart3 {
pinctrl-names = "default";
pinctrl-0 = <&uart3_pins>;
status = "okay";
};
添加控制器别名#
aliases {
serial0 = &uart0;
serial1 = &uart1;
serial2 = &uart2;
serial3 = &uart3;
serial4 = &uart4;
};
注意事项#
UART3 多引脚可选:
IO50/IO51 (alt1) - 01Studio 板子使用
IO28/IO29 (alt2)
IO62/IO63 (alt1,可作为 DE/RE 用于半双工 RS-485)
其他 UART 引脚配置类似,需要先通过 iomux 工具确认可用引脚组合
使用 iomux 工具查看 IO50 和 IO51 的功能:
# 查看所有管脚功能
python3 -m k230.iomux --info all
# 查看 IO50 的所有功能
python3 -m k230.iomux --info io50
# 输出: alt0(GPIO50), alt1(UART3_TXD), alt2(I2C2_SCL), alt3(QSPI0_CS4), alt4(TEST_PIN24)
# 查看 IO51 的所有功能
python3 -m k230.iomux --info io51
# 输出: alt0(GPIO51), alt1(UART3_RXD), alt2(I2C2_SDA), alt3(QSPI0_CS3), alt4(TEST_PIN25)
Linux 中的设备节点#
启用 UART 后,设备将出现在 /dev/ 目录下:
UART |
设备节点 |
描述 |
|---|---|---|
uart0 |
/dev/ttyS0 |
串口 0 |
uart1 |
/dev/ttyS1 |
串口 1 |
uart2 |
/dev/ttyS2 |
串口 2 |
uart3 |
/dev/ttyS3 |
串口 3 |
uart4 |
/dev/ttyS4 |
串口 4 |
快速验证#
使用 microcom 等工具可以快速验证串口是否正常工作:
# 使用 microcom(默认超时 10 秒,波特率 115200)
microcom -t 10000 -s 115200 /dev/ttyS3
C 语言示例#
基础串口通信#
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/select.h>
// 串口配置结构体
typedef struct {
int fd;
speed_t baud_rate;
char parity;
int data_bits;
int stop_bits;
} uart_config_t;
// 打开串口
int uart_open(const char *device) {
int fd = open(device, O_RDWR | O_NOCTTY);
if (fd < 0) {
fprintf(stderr, "Error opening %s: %s\n", device, strerror(errno));
return -1;
}
// 设置为非阻塞模式
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
fprintf(stderr, "Error fcntl GETFL: %s\n", strerror(errno));
close(fd);
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
fprintf(stderr, "Error fcntl SETFL: %s\n", strerror(errno));
close(fd);
return -1;
}
return fd;
}
// 配置串口参数
int uart_configure(int fd, uart_config_t *config) {
struct termios tty;
// 清空 termios 结构
memset(&tty, 0, sizeof(tty));
// 获取当前串口设置
if (tcgetattr(fd, &tty) != 0) {
fprintf(stderr, "Error tcgetattr: %s\n", strerror(errno));
return -1;
}
// 基本设置:8 位数据,无校验,1 位停止位
tty.c_cflag &= ~PARENB; // 无校验
tty.c_cflag &= ~CSTOPB; // 1 位停止位
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; // 8 位数据
// 设置波特率
cfsetispeed(&tty, config->baud_rate);
cfsetospeed(&tty, config->baud_rate);
// 设置为原始模式
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_oflag &= ~OPOST;
// 禁用软件流控制
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// 禁用硬件流控制
tty.c_cflag &= ~CRTSCTS;
// 设置最小字符数和等待时间 - 用于非阻塞读取
tty.c_cc[VMIN] = 0; // 不等待字符
tty.c_cc[VTIME] = 0; // 不等待
// 应用设置
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
fprintf(stderr, "Error tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
// 发送数据
int uart_write_data(int fd, const char *data, size_t len) {
ssize_t written = write(fd, data, len);
if (written < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return 0; // 写缓冲区满,返回0
}
fprintf(stderr, "Error write: %s\n", strerror(errno));
return -1;
}
return (int)written;
}
// 关闭串口
void uart_close(int fd) {
close(fd);
}
// 主函数示例
int main(int argc, char *argv[]) {
const char *device = "/dev/ttyS3"; // K230 通常使用 ttyS1
int fd;
uart_config_t config;
char buffer[1024];
int bytes_read;
// 处理命令行参数
if (argc > 1) {
device = argv[1];
}
printf("K230 UART Example\n");
printf("Device: %s\n", device);
printf("Baud: 115200, 8N1\n\n");
// 打开串口
fd = uart_open(device);
if (fd < 0) {
return 1;
}
// 配置串口
config.baud_rate = B115200;
config.parity = 'N';
config.data_bits = 8;
config.stop_bits = 1;
if (uart_configure(fd, &config) < 0) {
uart_close(fd);
return 1;
}
printf("UART configured successfully!\n");
printf("Type something and press Enter...\n");
printf("Press Ctrl+C to exit\n\n");
// 主循环:使用同一个 select 同时监听 stdin 和串口
while (1) {
fd_set readfds;
int maxfd = (fd > STDIN_FILENO) ? fd : STDIN_FILENO;
struct timeval tv;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
FD_SET(fd, &readfds);
tv.tv_sec = 0;
tv.tv_usec = 10000; // 10ms
int ret = select(maxfd + 1, &readfds, NULL, NULL, &tv);
if (ret < 0) {
fprintf(stderr, "Error select: %s\n", strerror(errno));
break;
}
// 检查 stdin 是否有输入
if (FD_ISSET(STDIN_FILENO, &readfds)) {
bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
// 发送到串口
if (uart_write_data(fd, buffer, strlen(buffer)) < 0) {
break;
}
printf("Sent: %s", buffer);
}
}
// 检查串口是否有数据
if (FD_ISSET(fd, &readfds)) {
bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("\nReceived: %s", buffer);
fflush(stdout);
}
}
}
uart_close(fd);
return 0;
}
多线程串口通信#
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>
static int uart_fd = -1;
static volatile int running = 1;
// 接收线程
void *uart_read_thread(void *arg) {
char buffer[1024];
int n;
while (running) {
n = read(uart_fd, buffer, sizeof(buffer) - 1);
if (n > 0) {
buffer[n] = '\0';
printf("\n[RX] %s", buffer);
fflush(stdout);
} else if (n < 0) {
perror("read");
break;
}
usleep(1000);
}
return NULL;
}
// 主函数
int main(int argc, char *argv[]) {
const char *device = "/dev/ttyS3";
pthread_t read_thread;
struct termios tty;
char input[256];
if (argc > 1) {
device = argv[1];
}
printf("K230 UART - Multi-thread Example\n");
printf("Device: %s\n", device);
printf("Commands: 'exit' to quit\n\n");
// 打开串口
uart_fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (uart_fd < 0) {
perror("Cannot open device");
return 1;
}
// 配置串口
memset(&tty, 0, sizeof(tty));
tcgetattr(uart_fd, &tty);
tty.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_iflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
tcsetattr(uart_fd, TCSANOW, &tty);
// 设置为非阻塞模式
int flags = fcntl(uart_fd, F_GETFL, 0);
fcntl(uart_fd, F_SETFL, flags | O_NONBLOCK);
// 创建读线程
if (pthread_create(&read_thread, NULL, uart_read_thread, NULL) != 0) {
fprintf(stderr, "Failed to create read thread\n");
close(uart_fd);
return 1;
}
printf("UART initialized. Start typing (Press Enter to send):\n");
// 主循环:读取键盘输入并发送
while (running) {
if (fgets(input, sizeof(input), stdin) != NULL) {
// 移除换行符
size_t len = strlen(input);
if (len > 0 && input[len-1] == '\n') {
input[len-1] = '\0';
}
// 退出命令
if (strcmp(input, "exit") == 0) {
break;
}
// 发送数据
if (strlen(input) > 0) {
ssize_t written = write(uart_fd, input, strlen(input));
if (written < 0) {
perror("write");
}
printf("[TX] %s\n", input);
}
}
usleep(1000);
}
running = 0;
pthread_join(read_thread, NULL);
close(uart_fd);
printf("UART closed.\n");
return 0;
}
Python 示例#
基础收发#
#导入相关模块
import serial,time
# 配置串口
com = serial.Serial("/dev/ttyS3", 115200)
#发送提示字符
com.write(b'Hello WalnutPi!')
while True:
# 获得接收缓冲区字符个数 int
count = com.inWaiting()
if count != 0: #收到数据
# 读取内容并打印
recv = com.read(count)
print(recv)
#发回数据
com.write(recv)
# 清空接收缓冲区
com.flushInput()
# 延时100ms,接收间隔
time.sleep(0.1)
线程接收#
import serial
import sys
import threading
class K230UART:
"""K230 UART 类"""
def __init__(self, port, baudrate=115200, timeout=1):
"""
初始化 UART
Args:
port: 串口设备路径,例如 /dev/ttyS0
baudrate: 波特率,默认 115200
timeout: 超时时间(秒)
"""
self.port = port
self.baudrate = baudrate
self.timeout = timeout
self.serial = None
def open(self):
"""打开串口"""
try:
self.serial = serial.Serial(
port=self.port,
baudrate=self.baudrate,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=self.timeout,
xonxoff=False,
rtscts=False,
dsrdtr=False
)
print(f"UART opened: {self.port} @ {self.baudrate}")
return True
except serial.SerialException as e:
print(f"Failed to open UART: {e}")
return False
def close(self):
"""关闭串口"""
if self.serial and self.serial.is_open:
self.serial.close()
print(f"UART closed: {self.port}")
def write(self, data):
"""
写入数据
Args:
data: 字符串或字节数组
Returns:
实际写入的字节数
"""
if isinstance(data, str):
data = data.encode('utf-8')
return self.serial.write(data)
def read(self, size=1):
"""
读取数据
Args:
size: 要读取的字节数
Returns:
读取的字节数组
"""
return self.serial.read(size)
def read_all(self):
"""读取所有可用数据"""
return self.serial.read_all()
def read_until(self, terminator=b'\n'):
"""读取直到遇到终止符"""
return self.serial.read_until(terminator)
def uart_receiver(uart, stop_event):
"""串口接收线程函数"""
while not stop_event.is_set():
response = uart.read_all()
if response:
print(f"\n[RX] {response.decode('utf-8', errors='ignore').strip()}")
def main():
"""主函数示例"""
port = "/dev/ttyS3"
# 处理命令行参数
if len(sys.argv) > 1:
port = sys.argv[1]
print("K230 UART Python Example")
print("Press Ctrl+C to exit\n")
# 创建 UART 实例
uart = K230UART(port, baudrate=115200)
# 打开串口
if not uart.open():
sys.exit(1)
try:
# 启动接收线程
stop_event = threading.Event()
receiver_thread = threading.Thread(target=uart_receiver, args=(uart, stop_event), daemon=True)
receiver_thread.start()
print("Type your message and press Enter to send")
while True:
message = input("Send> ") + "\n"
uart.write(message)
print(f"[TX] {message.strip()}")
except KeyboardInterrupt:
print("\nExiting...")
finally:
stop_event.set()
uart.close()
if __name__ == "__main__":
main()
