跳到主要内容

UART

1. UART 简介

UART(Universal Asynchronous Receiver-Transmitter,通用异步收发器)是一种广泛应用于嵌入式系统和计算机之间的串行通信协议。UART 通过将数据一位一位地传输,采用异步通信方式,数据传输和接收之间无需共享时钟信号。这使得UAR T成为短距离低速设备之间数据传输的常用选择。在 Linux 系统中通常被注册为串口终端设备。这些串口设备在 Linux 系统中以/dev/tty*/dev/serial/*的形式出现,具体设备名取决于硬件和驱动程序的设置。

2. 串口引脚图

  • 在 Lyra Ultra 中,UART0 被指定为调试串口,调试串口的波特率默认是 1500000。
  • Luckfox Lyra Ultra/Ultra W 引脚图:

3. 串口测试(shell)

  • 以 UART1 为例,打开成功后,我们使用命令查看 UART1:

    root@luckfox:/home/luckfox# ls /dev/ttyS*
    /dev/ttyS1
  • 用 stty 工具查询其通信参数:

    root@luckfox:/home/luckfox# stty -F /dev/ttyS1
    speed 9600 baud; line = 0;
    -brkint -imaxbel
  • 修改波特率,其中i speed 为输入速率,ospeed 为输出速率:

    iomux 0 12 16 
    iomux 0 13 17
    stty -F /dev/ttyS1 ispeed 115200 ospeed 115200
  • 关闭回显:

    stty -F /dev/ttyS1 -echo

3.1 与Windows主机通讯

将串口转 TTL 模块一端连接电脑,另一端连接 Lyra 开发板的引脚 3(GND)、4(RMIO12)和 5(RMIO13)上。

  1. 下载并打开 MobaXterm,选择串口,设置波特率(默认为9600,请根据自己实际修改过的数值设置)。

  2. 在开发板上的终端执行以下指令,使用 echo 命令向终端设备文件写入字符串"Hello"和"world !":

    echo Hello > /dev/ttyS1
    echo "world !" > /dev/ttyS1
  3. Windows 上的串口调试助手会接收到内容:

4. 串口通信(Python程序)

  1. 使用 Python 的serial库,实现收发数据的完整代码如下:

    #!/usr/bin/python3

    from periphery import Serial

    # 使用上下文管理器,自动关闭串口
    with Serial("/dev/ttyS1", baudrate=115200, databits=8, parity="none", stopbits=1, xonxoff=False, rtscts=False) as serial:
    try:
    # 发送数据
    serial.write(b"Hello World!\n")

    # 读取数据
    buf = serial.read(128, 1)
    if buf:
    data_string = buf.decode("utf-8")
    print(f"Read {len(buf)} bytes:\n{data_string}")
    else:
    print("No data received.")

    except Exception as e:
    print(f"An error occurred: {e}")
  2. 运行程序。

    root@luckfox:/home/luckfox# sudo python3 uart.py
    Read 13 bytes:
    Hello World!

5. 串口通信(C程序)

  1. 打开串口。

    int open_serial(const char *device) {
    int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
    perror("Error opening serial port");
    return -1;
    }
    return fd;
    }
  2. 配置串口参数。

    int configure_serial(int fd, speed_t baudrate) {
    struct termios tty;

    if (tcgetattr(fd, &tty) != 0) {
    perror("Error getting tty attributes");
    return -1;
    }

    cfsetospeed(&tty, baudrate); // 设置输出波特率
    cfsetispeed(&tty, baudrate); // 设置输入波特率

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8位数据位
    tty.c_cflag |= (CLOCAL | CREAD); // 启用接收器,忽略调制解调器线状态
    tty.c_cflag &= ~(PARENB | PARODD); // 无奇偶校验
    tty.c_cflag &= ~CSTOPB; // 1个停止位
    tty.c_cflag &= ~CRTSCTS; // 不使用RTS/CTS硬件流控

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
    tty.c_iflag &= ~(ICRNL | INLCR); // 关闭CR-LF转换

    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 关闭规范模式,关闭回显
    tty.c_oflag &= ~OPOST; // 关闭输出处理

    tty.c_cc[VMIN] = 0; // 非阻塞模式
    tty.c_cc[VTIME] = 10; // 1秒超时

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
    perror("Error setting tty attributes");
    return -1;
    }

    return 0;
    }
  3. 发送数据。

    int send_data(int fd, const char *data) {
    int len = strlen(data);
    int bytes_written = write(fd, data, len);

    if (bytes_written < 0) {
    perror("Error writing to serial port");
    return -1;
    }
    return bytes_written;
    }
  4. 接受数据。

    int receive_data(int fd, char *buffer, int buffer_size) {
    int bytes_read = read(fd, buffer, buffer_size);

    if (bytes_read < 0) {
    perror("Error reading from serial port");
    return -1;
    }
    return bytes_read;
    }
  5. 关闭串口。

    void close_serial(int fd) {
    if (close(fd) != 0) {
    perror("Error closing serial port");
    }
    }
  6. 使用 C 程序来实现收发数据完整代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h> // open()
    #include <termios.h> // tcgetattr(), tcsetattr()
    #include <unistd.h> // read(), write(), close()
    #include <errno.h> // errno

    #define SERIAL_PORT "/dev/ttyS1"
    #define BAUDRATE B115200
    #define BUFFER_SIZE 128

    // 打开串口
    int open_serial(const char *device) {
    int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
    perror("Error opening serial port");
    return -1;
    }
    return fd;
    }

    // 配置串口参数
    int configure_serial(int fd, speed_t baudrate) {
    struct termios tty;

    if (tcgetattr(fd, &tty) != 0) {
    perror("Error getting tty attributes");
    return -1;
    }

    cfsetospeed(&tty, baudrate); // 设置输出波特率
    cfsetispeed(&tty, baudrate); // 设置输入波特率

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8位数据位
    tty.c_cflag |= (CLOCAL | CREAD); // 启用接收器,忽略调制解调器线状态
    tty.c_cflag &= ~(PARENB | PARODD); // 无奇偶校验
    tty.c_cflag &= ~CSTOPB; // 1个停止位
    tty.c_cflag &= ~CRTSCTS; // 不使用RTS/CTS硬件流控

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
    tty.c_iflag &= ~(ICRNL | INLCR); // 关闭CR-LF转换

    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 关闭规范模式,关闭回显
    tty.c_oflag &= ~OPOST; // 关闭输出处理

    tty.c_cc[VMIN] = 0; // 非阻塞模式
    tty.c_cc[VTIME] = 10; // 1秒超时

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
    perror("Error setting tty attributes");
    return -1;
    }

    return 0;
    }

    // 发送数据
    int send_data(int fd, const char *data) {
    int len = strlen(data);
    int bytes_written = write(fd, data, len);

    if (bytes_written < 0) {
    perror("Error writing to serial port");
    return -1;
    }
    return bytes_written;
    }

    // 接收数据
    int receive_data(int fd, char *buffer, int buffer_size) {
    int bytes_read = read(fd, buffer, buffer_size);

    if (bytes_read < 0) {
    perror("Error reading from serial port");
    return -1;
    }
    return bytes_read;
    }

    // 关闭串口
    void close_serial(int fd) {
    if (close(fd) != 0) {
    perror("Error closing serial port");
    }
    }

    int main() {
    int serial_fd;
    char recv_buffer[BUFFER_SIZE];

    // 打开串口
    serial_fd = open_serial(SERIAL_PORT);
    if (serial_fd < 0) return 1;

    // 配置串口
    if (configure_serial(serial_fd, BAUDRATE) != 0) {
    close_serial(serial_fd);
    return 1;
    }

    // 发送数据
    const char *message = "Hello World!\n";
    if (send_data(serial_fd, message) < 0) {
    close_serial(serial_fd);
    return 1;
    }
    printf("Sent: %s", message);

    // 接收数据
    int bytes_received = receive_data(serial_fd, recv_buffer, BUFFER_SIZE - 1);
    if (bytes_received > 0) {
    recv_buffer[bytes_received] = '\0'; // 添加字符串终止符
    printf("Received: %s\n", recv_buffer);
    } else if (bytes_received == 0) {
    printf("No data received.\n");
    }

    // 关闭串口
    close_serial(serial_fd);

    return 0;
    }
  7. 交叉编译运行程序,搭建交叉编译环境请参考《程序编译》或《GPIO》部分。

    arm-none-linux-gnueabihf-gcc uart.c -o uart

6. 设备树简介

  1. 设备文件路径位于kernel-6.1/arch/arm/boot/dts/rk3506g-luckfox-lyra.dts,开启uart的代码片段如下:

    &uart1 {
    pinctrl-0 = <&rm_io12_uart1_tx &rm_io13_uart1_rx>;
    status = "okay";
    pinctrl-names = "default";
    };