注意

这是最新开发分支配套的文档,可能包含已发布版本中尚未提供的功能。如果您要查看特定版本的文档,请使用左侧的下拉菜单并选择所需要的版本。

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 使用 pinsfunction 属性来配置引脚。

// 在 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()


评论列表
条评论
登录