Explorar o código

add uart driver

xuqiang hai 10 meses
pai
achega
f58486e06f
Modificáronse 4 ficheiros con 600 adicións e 0 borrados
  1. 81 0
      Makefile
  2. 24 0
      examples/main.c
  3. 302 0
      src/uart.c
  4. 193 0
      src/uart.h

+ 81 - 0
Makefile

@@ -0,0 +1,81 @@
+target = uart
+lib_name = uart
+
+build_type ?= release
+build_type_dir ?= release
+example_dir ?= examples
+
+ifeq (${build_type}, release)
+	build_type_dir := release
+	DRFLAGS := -O3 -w
+else
+	build_type_dir = debug
+	DRFLAGS := -g3 -DDEBUG=1 -Wall -v
+endif
+
+build_dir = build/${build_type_dir}
+obj_dir := ${build_dir}/obj
+bin_dir := ${build_dir}/bin
+lib_dir	:= ${build_dir}/lib
+
+gcc_std := -std=gnu99
+g++_std := -std=c++11
+includes = src
+
+CFLAGS := -fPIC ${DRFLAGS} ${gcc_std} ${addprefix -I,${includes}}
+CXXFLAGS := -fPIC ${DRFLAGS} ${g++_std} ${addprefix -I,${includes}}
+ARFLAGS := rcsD
+LDFLAGS :=
+LDLIBS	:= 
+
+src_sources := $(shell find src -name '*.cpp' -or -name '*.c')
+example_sources :=  $(shell find examples -name '*.cpp' -or -name '*.c')
+sources := ${src_sources} ${example_sources}
+objects := $(sources:%=$(obj_dir)/%.o)
+
+static_lib = lib${lib_name}.a
+shared_lib = lib${lib_name}.so
+
+define collect_source_object
+$(patsubst ${example_dir}/%,${obj_dir}/${example_dir}/%.o, $(shell find ${example_dir} -name '*.cpp' -or -name '*.c' -or -name '*.s'))
+endef
+
+# 默认目标:构建最终的可执行文件
+all: ${lib_dir}/${static_lib}  $(bin_dir)/$(target)
+# $(foreach m,${binary_name},$(eval ${bin_dir}/$m: $(call collect_source_object,$m)))
+${bin_dir}/$(target): $(call collect_source_object, $(target))
+
+# 链接目标:将所有.o文件链接成可执行文件
+# $(bin_dir)/$(target): $(objects)
+# 	@mkdir -p $(dir $@)
+# 	${CC} $^ -o $@
+$(bin_dir)/$(target):
+	mkdir -p $(dir $@)
+	${CC} ${LDFLAGS} $^ ${LDLIBS} -o $@  -L./$(lib_dir) -l$(lib_name)
+
+# c source
+$(obj_dir)/%.c.o: %.c
+	@mkdir -p $(dir $@)
+	${CC} ${CFLAGS} -c $< -o $@
+
+# c++ source
+$(obj_dir)/%.cpp.o: %.cpp
+	@mkdir -p $(dir $@)
+	${CXX} ${CXXFLAGS} -c $< -o $@
+
+# static lib
+${lib_dir}%.a: $(src_sources:%=$(obj_dir)/%.o)
+	@mkdir -p $(dir $@)
+	${AR} ${ARFLAGS} $@ $^
+
+# shared lib
+$(lib_dir)/%.so: $(src_sources:%=$(obj_dir)/%.o)
+	@mkdir -p $(dir $@)
+	${CC} -shared $^ ${LDFLAGS} ${LDLIBS} -o $@
+	
+.PHONY: clean rebuild
+
+clean:
+	rm -r build
+
+rebuild: clean all

+ 24 - 0
examples/main.c

@@ -0,0 +1,24 @@
+#include "uart.h"
+
+#include <string.h>
+#include <stdio.h>
+
+int main()
+{
+    uart_handle_st uart;
+    memset(&uart, 0, sizeof(uart));
+
+    uart_handle_set_device(&uart, "/dev/ttyS1");
+    uart_handle_set_baudrate(&uart, 115200);
+    uart_handle_set_databits(&uart, 8);
+    uart_handle_set_stopbits(&uart, 1);
+    uart_handle_set_parity(&uart, PARITY_NONE);
+    uart_handle_set_flow_control(&uart, FLOW_CONTROL_NONE);
+    uart_handle_set_block(&uart, 1);
+
+    if(uart_handle_open(&uart) < 0)
+    {
+        printf("Failed to open UART\n");
+        return -1;
+    }
+}

+ 302 - 0
src/uart.c

@@ -0,0 +1,302 @@
+/**
+ * @file    uart.c
+ * @brief   UART driver source file.
+ *
+ * @details This file implements UART configuration, open/close,
+ *          read/write functions using POSIX termios API.
+ *
+ * @author  jlgwch
+ * @date    2025-06-06
+ * @version 1.0
+ *
+ * @history
+ *  - 2025-06-06: jlgwch - Created the file.
+ *
+ * @copyright Copyright (c) 2025~ jlgwch. All rights reserved.
+ */
+
+
+#include "uart.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+speed_t baudrate(int baudrate)
+{
+    switch (baudrate)
+    {
+        case 0: return B0;
+        case 50: return B50;
+        case 75: return B75;
+        case 110: return B110;
+        case 134: return B134;
+        case 150: return B150;
+        case 200: return B200;
+        case 300: return B300;
+        case 600: return B600;
+        case 1200: return B1200;
+        case 1800: return B1800;
+        case 2400: return B2400;
+        case 4800: return B4800;
+        case 9600: return B9600;
+        case 19200: return B19200;
+        case 38400: return B38400;
+        case 57600: return B57600;
+        case 115200: return B115200;
+        case 230400: return B230400;
+        case 460800: return B460800;
+        case 500000: return B500000;
+        case 576000: return B576000;
+        case 921600: return B921600;
+        case 1000000: return B1000000;
+        case 1152000: return B1152000;
+        case 1500000: return B1500000;
+        case 2000000: return B2000000;
+        case 2500000: return B2500000;
+        case 3000000: return B3000000;
+        case 3500000: return B3500000;
+        case 4000000: return B4000000;
+        default: return 0; // 不支持
+    }
+}
+
+int uart_handle_set_device(uart_handle_st* handle, const char* device)
+{
+    if(!handle || !device)
+        return -1;
+    handle->config.device = device;
+    return 0;
+}
+
+int uart_handle_set_baudrate(uart_handle_st* handle, int baudrate)
+{
+    if(!handle)
+        return -1;
+    handle->config.baudrate = baudrate;
+    return 0;
+}
+
+int uart_handle_set_databits(uart_handle_st* handle, int databits)
+{
+    if(!handle)
+        return -1;
+    if(databits < 5 || databits > 8)
+        return -1;
+    handle->config.databits = databits;
+    return 0;
+}
+
+int uart_handle_set_stopbits(uart_handle_st* handle, int stopbits)
+{
+    if(!handle) 
+        return -1;
+    if(stopbits != 1 && stopbits != 2)
+        return -1;
+    handle->config.stopbits = stopbits;
+    return 0;
+}
+
+int uart_handle_set_flow_control(uart_handle_st* handle, flow_control_t flow_control)
+{
+    if(!handle)
+        return -1;
+    handle->config.flow_control = flow_control;
+    return 0;
+}
+
+int uart_handle_set_parity(uart_handle_st* handle, parity_t parity)
+{
+    if(!handle)
+        return -1;
+    handle->config.parity = parity;
+    return 0;
+}
+
+int uart_handle_set_block(uart_handle_st* handle, int block)
+{
+    if(!handle)
+        return -1;
+    handle->config.block = (block != 0);
+    return 0;
+}
+
+int uart_handle_open(uart_handle_st* handle)
+{
+    int ret;
+    int flags;
+    int fd;
+    struct termios options;
+
+    if(!handle || !handle->config.device)
+        return -1;
+    
+    flags = O_RDWR | O_NOCTTY;
+    if(!handle->config.block)
+        flags |= O_NONBLOCK;
+
+    fd = open(handle->config.device, flags);
+    if(fd < 0) 
+    {
+        perror("open");
+        return -1;
+    }
+
+    // 获取当前串口配置
+    ret = tcgetattr(fd, &options);
+    if (ret != 0) 
+    {
+        perror("tcgetattr");
+        close(fd);
+        return -1;
+    }
+
+    // 设置波特率
+    speed_t speed = baudrate(handle->config.baudrate);
+    if(speed == 0)
+    {
+        fprintf(stderr, "Unsupported baudrate %d\n", handle->config.baudrate);
+        close(fd);
+        return -1;
+    }
+    cfsetispeed(&options, speed);
+    cfsetospeed(&options, speed);
+
+    // 设置数据位
+    options.c_cflag &= ~CSIZE;  // 清除数据位掩码 CSIZE
+    switch (handle->config.databits) 
+    {
+        case 5: 
+            options.c_cflag |= CS5;
+            break;
+        case 6:
+            options.c_cflag |= CS6;
+            break;
+        case 7:
+            options.c_cflag |= CS7;
+            break;
+        case 8:
+            options.c_cflag |= CS8;
+            break;
+        default:
+            fprintf(stderr, "Invalid databits %d\n", handle->config.databits);
+            close(fd);
+            return -1;
+    }
+
+    // 设置停止位
+    if (handle->config.stopbits == 2)
+        options.c_cflag |= CSTOPB;
+    else
+        options.c_cflag &= ~CSTOPB;
+
+    // 设置校验位
+    options.c_cflag &= ~(PARENB | PARODD);
+    options.c_iflag &= ~(INPCK | ISTRIP);
+    switch (handle->config.parity)
+    {
+        case PARITY_NONE: // 无校验
+            // 已清除
+            break;
+        case PARITY_EVEN: // 偶校验
+            options.c_cflag |= PARENB;
+            options.c_iflag |= INPCK;
+            options.c_cflag &= ~PARODD;
+            break;
+        case PARITY_ODD: // 奇校验
+            options.c_cflag |= PARENB;
+            options.c_cflag |= PARODD;
+            options.c_iflag |= INPCK;
+            break;
+        case PARITY_MARK:
+        case PARITY_SPACE:
+            fprintf(stderr, "MARK/SPACE parity is not supported by termios\n");
+            // 需要额外实现
+            return -1;
+        default:
+            fprintf(stderr, "Invalid parity %c\n", handle->config.parity);
+            close(fd);
+            return -1;
+    }
+
+    // 设置流控
+    switch(handle->config.flow_control)
+    {
+    case FLOW_CONTROL_NONE: // 无流控
+        options.c_cflag &= ~CRTSCTS;                // 关闭硬件流控
+        options.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输入)
+        options.c_oflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输出)
+        break;
+    case FLOW_CONTROL_SOFTWARE: // 软件流控
+        options.c_cflag &= ~CRTSCTS;               // 关闭硬件流控
+        options.c_iflag |= (IXON | IXOFF);         // 启用软件流控 (输入)
+        options.c_oflag |= (IXON | IXOFF);         // 启用软件流控 (输出)
+        break;
+    case FLOW_CONTROL_HARDWARE: // 硬件流控
+        options.c_cflag |= CRTSCTS;                 // 启用RTS/CTS硬件流控
+        options.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输入)
+        options.c_oflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输出)
+        break;
+    default:
+        fprintf(stderr, "Unsupported flow control type\n");
+        return -1;
+    }
+
+    // 其他配置
+    // CLOCAL: 忽略调制解调器状态线, 保证程序不被终端控制。
+    // CREAD: 开启接收使能。
+    options.c_cflag |= (CLOCAL | CREAD);
+
+    if (handle->config.block)
+    {
+        options.c_cc[VMIN] = 1;  // 阻塞模式,最少读1字节
+        options.c_cc[VTIME] = 0;
+    }
+    else {
+        options.c_cc[VMIN] = 0;  // 非阻塞读
+        options.c_cc[VTIME] = 1; // 读等待0.1秒
+    }
+
+    // 关闭特殊字符处理和输出处理
+    // ISIG	禁用信号 (如 Ctrl+C 发送 SIGINT、Ctrl+Z 发送 SIGTSTP)。
+    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+    options.c_lflag &= ~(ICANON | ECHO | ECHOE);
+    options.c_oflag &= ~OPOST;
+
+    // 应用配置
+    if (tcsetattr(fd, TCSANOW, &options) != 0)
+    {
+        perror("tcsetattr");
+        close(fd);
+        return -1;
+    }
+
+    // 保存句柄和配置
+    handle->fd = fd;
+    handle->config.options = options;
+    return 0;
+}
+
+void uart_handle_close(uart_handle_st* handle)
+{
+    if(handle && handle->fd >= 0)
+        close(handle->fd);
+}
+
+int uart_write(uart_handle_st* handle, char* data, int size)
+{
+    if(handle && handle->fd >= 0)
+        return write(handle->fd, data, size);
+    else
+        return -1;
+}
+
+int uart_read(uart_handle_st* handle, char* data, int size)
+{
+    if(handle && handle->fd >= 0)
+        return read(handle->fd, data, size);
+    else
+        return -1;
+}

+ 193 - 0
src/uart.h

@@ -0,0 +1,193 @@
+/**
+ * @file    uart.h
+ * @brief   UART driver header file.
+ *
+ * @details This file contains declarations for UART configuration and
+ *          control functions, as well as data structures for UART handles.
+ *
+ * @author  jlgwch
+ * @date    2025-06-06
+ * @version 1.0
+ *
+ * @history
+ *  - 2025-06-06: jlgwch - Created the file.
+ *
+ * @copyright Copyright (c) 2025~ jlgwch. All rights reserved.
+ */
+
+#ifndef __UART_H__
+#define __UART_H__
+
+#include <termios.h>
+
+typedef enum {
+    PARITY_NONE = 0,  // 无校验
+    PARITY_EVEN,      // 偶校验
+    PARITY_ODD,       // 奇校验
+    PARITY_MARK,      // 恒为1校验 (较少使用)
+    PARITY_SPACE      // 恒为0校验 (较少使用)
+} parity_t;
+
+typedef enum {
+    FLOW_CONTROL_NONE = 0,   // 无流控
+    FLOW_CONTROL_SOFTWARE,   // 软件流控 (XON/XOFF)
+    FLOW_CONTROL_HARDWARE    // 硬件流控 (RTS/CTS)
+} flow_control_t;
+
+typedef struct
+{
+    const char *device;
+    int baudrate;
+    int databits;
+    int stopbits;
+    parity_t parity;
+    flow_control_t flow_control;
+    int block;
+    struct termios options;
+}uart_config_st;
+
+typedef struct
+{
+    uart_config_st config;
+    int fd;     // 串口文件描述符
+}uart_handle_st;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Convert integer baudrate value to termios speed_t constant.
+ *
+ * This function maps standard baudrate integers to the corresponding termios speed_t values.
+ *
+ * @param baudrate Integer baud rate (e.g., 9600, 115200).
+ * @return Corresponding speed_t value; returns 0 if baudrate is unsupported.
+ */
+speed_t baudrate(int baudrate);
+
+/**
+ * @brief Set the UART device path.
+ *
+ * Assigns the device path string to the UART handle.
+ *
+ * @param handle Pointer to the UART handle structure.
+ * @param device UART device path (e.g., "/dev/ttyS0").
+ * @return 0 on success; -1 if handle or device is NULL.
+ */
+int uart_handle_set_device(uart_handle_st* handle, const char* device);
+
+/**
+ * @brief Set the UART baud rate.
+ *
+ * Updates the baud rate configuration in the UART handle.
+ *
+ * @param handle Pointer to the UART handle.
+ * @param baudrate Baud rate to set (e.g., 9600, 115200).
+ * @return 0 on success; -1 if handle is NULL.
+ */
+int uart_handle_set_baudrate(uart_handle_st* handle, int baudrate);
+
+/**
+ * @brief Set the number of data bits per UART frame.
+ *
+ * Valid data bits are from 5 to 8.
+ *
+ * @param handle Pointer to the UART handle.
+ * @param databits Number of data bits (5-8).
+ * @return 0 on success; -1 if invalid databits or handle is NULL.
+ */
+int uart_handle_set_databits(uart_handle_st* handle, int databits);
+
+/**
+ * @brief Set the number of stop bits per UART frame.
+ *
+ * Supported stop bits are 1 or 2.
+ *
+ * @param handle Pointer to the UART handle.
+ * @param stopbits Number of stop bits (1 or 2).
+ * @return 0 on success; -1 if invalid stopbits or handle is NULL.
+ */
+int uart_handle_set_stopbits(uart_handle_st* handle, int stopbits);
+
+/**
+ * @brief Set the UART flow control mode.
+ *
+ * Flow control can be none, software (XON/XOFF), or hardware (RTS/CTS).
+ *
+ * @param handle Pointer to the UART handle.
+ * @param flow_control Flow control mode.
+ * @return 0 on success; -1 if handle is NULL.
+ */
+int uart_handle_set_flow_control(uart_handle_st* handle, flow_control_t flow_control);
+
+/**
+ * @brief Set UART parity mode.
+ *
+ * Parity modes include none, even, odd, mark, and space.
+ *
+ * @param handle Pointer to the UART handle.
+ * @param parity Parity mode to set.
+ * @return 0 on success; -1 if parity is unsupported or handle is NULL.
+ */
+int uart_handle_set_parity(uart_handle_st* handle, parity_t parity);
+
+/**
+ * @brief Set blocking mode for UART I/O.
+ *
+ * Blocking mode affects read/write system calls behavior.
+ *
+ * @param handle Pointer to the UART handle.
+ * @param block Non-zero for blocking mode, 0 for non-blocking mode.
+ * @return 0 on success; -1 if handle is NULL.
+ */
+int uart_handle_set_block(uart_handle_st* handle, int block);
+
+/**
+ * @brief Open the UART device and apply configurations.
+ *
+ * Opens the UART device file, sets baudrate, data bits, parity, stop bits, flow control, and blocking mode.
+ *
+ * @param handle Pointer to UART handle with configuration parameters set.
+ * @return 0 on success; -1 on failure (device open error or invalid settings).
+ */
+int uart_handle_open(uart_handle_st* handle);
+
+/**
+ * @brief Close the UART device.
+ *
+ * Closes the device file descriptor stored in the handle.
+ *
+ * @param handle Pointer to the UART handle.
+ */
+void uart_handle_close(uart_handle_st* handle);
+
+/**
+ * @brief Write data to the UART device.
+ *
+ * Sends data through the UART interface.
+ *
+ * @param handle Pointer to UART handle.
+ * @param data Pointer to data buffer to send.
+ * @param size Number of bytes to write.
+ * @return Number of bytes written on success; -1 on error.
+ */
+int uart_write(uart_handle_st* handle, char* data, int size);
+
+/**
+ * @brief Read data from the UART device.
+ *
+ * Reads incoming data from the UART interface.
+ *
+ * @param handle Pointer to UART handle.
+ * @param data Buffer to store received data.
+ * @param size Maximum number of bytes to read.
+ * @return Number of bytes read on success; -1 on error.
+ */
+int uart_read(uart_handle_st* handle, char* data, int size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif