• i.MX 6ULL 驱动开发 二十三:UART


    一、UART 协议

    UART详解_sternlycore的博客-CSDN博客

    二、UART 和 TTY 关系

    基于Linux的tty架构及UART驱动详解 - 一口Linux - 博客园 (cnblogs.com)

    三、Linux UART 驱动框架中重要对象

    1、UART 驱动

    struct uart_driver {
    	struct module		*owner;
    	const char		*driver_name;
    	const char		*dev_name;
    	int			 major;
    	int			 minor;
    	int			 nr;
    	struct console		*cons;
    
    	/*
    	 * these are private; the low level driver should not
    	 * touch these; they should be initialised to NULL
    	 */
    	struct uart_state	*state;
    	struct tty_driver	*tty_driver;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    每个串口驱动都需要定义一个 uart_driver,加载驱动时向系统注册这个 uart_driver,注销驱动时注销掉注册的 uart_driver

    2、UART 控制器

    struct uart_port {
    	spinlock_t		lock;			/* port lock */
    	unsigned long		iobase;			/* in/out[bwl] */
    	unsigned char __iomem	*membase;		/* read/write[bwl] */
    	unsigned int		(*serial_in)(struct uart_port *, int);
    	void			(*serial_out)(struct uart_port *, int, int);
    	void			(*set_termios)(struct uart_port *,
    				               struct ktermios *new,
    				               struct ktermios *old);
    	void			(*set_mctrl)(struct uart_port *, unsigned int);
    	int			(*startup)(struct uart_port *port);
    	void			(*shutdown)(struct uart_port *port);
    	void			(*throttle)(struct uart_port *port);
    	void			(*unthrottle)(struct uart_port *port);
    	int			(*handle_irq)(struct uart_port *);
    	void			(*pm)(struct uart_port *, unsigned int state,
    				      unsigned int old);
    	void			(*handle_break)(struct uart_port *);
    	int			(*rs485_config)(struct uart_port *,
    						struct serial_rs485 *rs485);
    	unsigned int		irq;			/* irq number */
    	unsigned long		irqflags;		/* irq flags  */
    	unsigned int		uartclk;		/* base uart clock */
    	unsigned int		fifosize;		/* tx fifo size */
    	unsigned char		x_char;			/* xon/xoff char */
    	unsigned char		regshift;		/* reg offset shift */
    	unsigned char		iotype;			/* io access style */
    	unsigned char		unused1;
    
    #define UPIO_PORT		(SERIAL_IO_PORT)	/* 8b I/O port access */
    #define UPIO_HUB6		(SERIAL_IO_HUB6)	/* Hub6 ISA card */
    #define UPIO_MEM		(SERIAL_IO_MEM)		/* 8b MMIO access */
    #define UPIO_MEM32		(SERIAL_IO_MEM32)	/* 32b little endian */
    #define UPIO_AU			(SERIAL_IO_AU)		/* Au1x00 and RT288x type IO */
    #define UPIO_TSI		(SERIAL_IO_TSI)		/* Tsi108/109 type IO */
    #define UPIO_MEM32BE		(SERIAL_IO_MEM32BE)	/* 32b big endian */
    
    	unsigned int		read_status_mask;	/* driver specific */
    	unsigned int		ignore_status_mask;	/* driver specific */
    	struct uart_state	*state;			/* pointer to parent state */
    	struct uart_icount	icount;			/* statistics */
    
    	struct console		*cons;			/* struct console, if any */
    #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
    	unsigned long		sysrq;			/* sysrq timeout */
    #endif
    
    	/* flags must be updated while holding port mutex */
    	upf_t			flags;
    
    	/*
    	 * These flags must be equivalent to the flags defined in
    	 * include/uapi/linux/tty_flags.h which are the userspace definitions
    	 * assigned from the serial_struct flags in uart_set_info()
    	 * [for bit definitions in the UPF_CHANGE_MASK]
    	 *
    	 * Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable
    	 * except bit 15 (UPF_NO_TXEN_TEST) which is masked off.
    	 * The remaining bits are serial-core specific and not modifiable by
    	 * userspace.
    	 */
    #define UPF_FOURPORT		((__force upf_t) ASYNC_FOURPORT       /* 1  */ )
    #define UPF_SAK			((__force upf_t) ASYNC_SAK            /* 2  */ )
    #define UPF_SPD_HI		((__force upf_t) ASYNC_SPD_HI         /* 4  */ )
    #define UPF_SPD_VHI		((__force upf_t) ASYNC_SPD_VHI        /* 5  */ )
    #define UPF_SPD_CUST		((__force upf_t) ASYNC_SPD_CUST   /* 0x0030 */ )
    #define UPF_SPD_WARP		((__force upf_t) ASYNC_SPD_WARP   /* 0x1010 */ )
    #define UPF_SPD_MASK		((__force upf_t) ASYNC_SPD_MASK   /* 0x1030 */ )
    #define UPF_SKIP_TEST		((__force upf_t) ASYNC_SKIP_TEST      /* 6  */ )
    #define UPF_AUTO_IRQ		((__force upf_t) ASYNC_AUTO_IRQ       /* 7  */ )
    #define UPF_HARDPPS_CD		((__force upf_t) ASYNC_HARDPPS_CD     /* 11 */ )
    #define UPF_SPD_SHI		((__force upf_t) ASYNC_SPD_SHI        /* 12 */ )
    #define UPF_LOW_LATENCY		((__force upf_t) ASYNC_LOW_LATENCY    /* 13 */ )
    #define UPF_BUGGY_UART		((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )
    #define UPF_NO_TXEN_TEST	((__force upf_t) (1 << 15))
    #define UPF_MAGIC_MULTIPLIER	((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
    
    /* Port has hardware-assisted h/w flow control */
    #define UPF_AUTO_CTS		((__force upf_t) (1 << 20))
    #define UPF_AUTO_RTS		((__force upf_t) (1 << 21))
    #define UPF_HARD_FLOW		((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
    /* Port has hardware-assisted s/w flow control */
    #define UPF_SOFT_FLOW		((__force upf_t) (1 << 22))
    #define UPF_CONS_FLOW		((__force upf_t) (1 << 23))
    #define UPF_SHARE_IRQ		((__force upf_t) (1 << 24))
    #define UPF_EXAR_EFR		((__force upf_t) (1 << 25))
    #define UPF_BUG_THRE		((__force upf_t) (1 << 26))
    /* The exact UART type is known and should not be probed.  */
    #define UPF_FIXED_TYPE		((__force upf_t) (1 << 27))
    #define UPF_BOOT_AUTOCONF	((__force upf_t) (1 << 28))
    #define UPF_FIXED_PORT		((__force upf_t) (1 << 29))
    #define UPF_DEAD		((__force upf_t) (1 << 30))
    #define UPF_IOREMAP		((__force upf_t) (1 << 31))
    
    #define __UPF_CHANGE_MASK	0x17fff
    #define UPF_CHANGE_MASK		((__force upf_t) __UPF_CHANGE_MASK)
    #define UPF_USR_MASK		((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))
    
    #if __UPF_CHANGE_MASK > ASYNC_FLAGS
    #error Change mask not equivalent to userspace-visible bit defines
    #endif
    
    	/*
    	 * Must hold termios_rwsem, port mutex and port lock to change;
    	 * can hold any one lock to read.
    	 */
    	upstat_t		status;
    
    #define UPSTAT_CTS_ENABLE	((__force upstat_t) (1 << 0))
    #define UPSTAT_DCD_ENABLE	((__force upstat_t) (1 << 1))
    #define UPSTAT_AUTORTS		((__force upstat_t) (1 << 2))
    #define UPSTAT_AUTOCTS		((__force upstat_t) (1 << 3))
    #define UPSTAT_AUTOXOFF		((__force upstat_t) (1 << 4))
    
    	int			hw_stopped;		/* sw-assisted CTS flow state */
    	unsigned int		mctrl;			/* current modem ctrl settings */
    	unsigned int		timeout;		/* character-based timeout */
    	unsigned int		type;			/* port type */
    	const struct uart_ops	*ops;
    	unsigned int		custom_divisor;
    	unsigned int		line;			/* port index */
    	unsigned int		minor;
    	resource_size_t		mapbase;		/* for ioremap */
    	resource_size_t		mapsize;
    	struct device		*dev;			/* parent device */
    	unsigned char		hub6;			/* this should be in the 8250 driver */
    	unsigned char		suspended;
    	unsigned char		irq_wake;
    	unsigned char		unused[2];
    	struct attribute_group	*attr_group;		/* port specific attributes */
    	const struct attribute_group **tty_groups;	/* all attributes (serial core use only) */
    	struct serial_rs485     rs485;
    	void			*private_data;		/* generic platform data pointer */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134

    Linux 中使用 struct uart_port 描述硬件信息。

    3、UART 操作集

    /*
     * This structure describes all the operations that can be done on the
     * physical hardware.  See Documentation/serial/driver for details.
     */
    struct uart_ops {
    	unsigned int	(*tx_empty)(struct uart_port *);
    	void		(*set_mctrl)(struct uart_port *, unsigned int mctrl);
    	unsigned int	(*get_mctrl)(struct uart_port *);
    	void		(*stop_tx)(struct uart_port *);
    	void		(*start_tx)(struct uart_port *);
    	void		(*throttle)(struct uart_port *);
    	void		(*unthrottle)(struct uart_port *);
    	void		(*send_xchar)(struct uart_port *, char ch);
    	void		(*stop_rx)(struct uart_port *);
    	void		(*enable_ms)(struct uart_port *);
    	void		(*break_ctl)(struct uart_port *, int ctl);
    	int		(*startup)(struct uart_port *);
    	void		(*shutdown)(struct uart_port *);
    	void		(*flush_buffer)(struct uart_port *);
    	void		(*set_termios)(struct uart_port *, struct ktermios *new,
    				       struct ktermios *old);
    	void		(*set_ldisc)(struct uart_port *, struct ktermios *);
    	void		(*pm)(struct uart_port *, unsigned int state,
    			      unsigned int oldstate);
    
    	/*
    	 * Return a string describing the type of the port
    	 */
    	const char	*(*type)(struct uart_port *);
    
    	/*
    	 * Release IO and memory resources used by the port.
    	 * This includes iounmap if necessary.
    	 */
    	void		(*release_port)(struct uart_port *);
    
    	/*
    	 * Request IO and memory resources used by the port.
    	 * This includes iomapping the port if necessary.
    	 */
    	int		(*request_port)(struct uart_port *);
    	void		(*config_port)(struct uart_port *, int);
    	int		(*verify_port)(struct uart_port *, struct serial_struct *);
    	int		(*ioctl)(struct uart_port *, unsigned int, unsigned long);
    #ifdef CONFIG_CONSOLE_POLL
    	int		(*poll_init)(struct uart_port *);
    	void		(*poll_put_char)(struct uart_port *, unsigned char);
    	int		(*poll_get_char)(struct uart_port *);
    #endif
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    四、UART 驱动编写思路

    LinuxUART 一般都编写好,根据设备树找到相关驱动分析即可。

    五、imx6ull 下 UART 驱动分析

    文档路径:drivers\tty\serial\imx.c

    1、驱动框架

    static struct uart_driver imx_reg = {
    	.owner          = THIS_MODULE,
    	.driver_name    = DRIVER_NAME,
    	.dev_name       = DEV_NAME,
    	.major          = SERIAL_IMX_MAJOR,
    	.minor          = MINOR_START,
    	.nr             = ARRAY_SIZE(imx_ports),	// imx_ports 封装 struct uart_port
    	.cons           = IMX_CONSOLE,
    };
    
    static struct platform_driver serial_imx_driver = {
    	.probe		= serial_imx_probe,
    	.remove		= serial_imx_remove,
    
    	.suspend	= serial_imx_suspend,
    	.resume		= serial_imx_resume,
    	.id_table	= imx_uart_devtype,
    	.driver		= {
    		.name	= "imx-uart",
    		.of_match_table = imx_uart_dt_ids,
    	},
    };
    
    static int __init imx_serial_init(void)
    {
    	int ret = uart_register_driver(&imx_reg);
    
    	if (ret)
    		return ret;
    
    	ret = platform_driver_register(&serial_imx_driver);
    	if (ret != 0)
    		uart_unregister_driver(&imx_reg);
    
    	return ret;
    }
    
    static void __exit imx_serial_exit(void)
    {
    	platform_driver_unregister(&serial_imx_driver);
    	uart_unregister_driver(&imx_reg);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    1、UART 驱动使用 platform 驱动框架。

    2、在加载驱动时注册 UART 驱动。

    2、初始化

    serial_imx_probe 函数。

    3、注销

    serial_imx_remove 函数。

    六、添加设备树

    1、UART 设备树相关说明

    见文档:Documentation\devicetree\bindings\serial\fsl-imx-uart.txt

    2、确定使用 UART

    通过原理图可以确定,使用接口为 uart3

    3、添加 pinctrl 子系统相关配置

    pinctrl_uart3: uart3grp {
    	fsl,pins = <
    		MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX		0X1b0b1
    		MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX		0X1b0b1
    	>;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4、在 uart3 下追加配置

    &uart3 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_uart3>;
    	status = "okay";
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5、编译设备树

    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ make dtbs
      CHK     include/config/kernel.release
      CHK     include/generated/uapi/linux/version.h
      CHK     include/generated/utsrelease.h
    make[1]: 'include/generated/mach-types.h' is up to date.
      CHK     include/generated/bounds.h
      CHK     include/generated/asm-offsets.h
      CALL    scripts/checksyscalls.sh
      DTC     arch/arm/boot/dts/imx6ull-alientek-emmc.dtb
      DTC     arch/arm/boot/dts/imx6ull-alientek-nand.dtb
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6、测试

    # pwd
    /proc/device-tree/soc/aips-bus@02100000
    # ls
    #address-cells       lcdif@021c8000       serial@021f0000
    #size-cells          mmdc@021b0000        serial@021f4000
    adc@02198000         name                 serial@021fc000
    compatible           ocotp-ctrl@021bc000  usb@02184000
    csi@021c4000         pxp@021cc000         usb@02184200
    csu@021c0000         qspi@021e0000        usbmisc@02184800
    ethernet@02188000    ranges               usdhc@02190000
    i2c@021a0000         reg                  usdhc@02194000
    i2c@021a4000         romcp@021ac000       weim@021b8000
    i2c@021a8000         serial@021e8000
    i2c@021f8000         serial@021ec000
    # cd serial@021ec000/
    # ls
    clock-names    dma-names      name           reg
    clocks         dmas           pinctrl-0      status
    compatible     interrupts     pinctrl-names
    # cat compatible
    fsl,imx6ul-uartfsl,imx6q-uartfsl,imx21-uart#
    #
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    七、驱动编写

    uart 相关驱动 Linux 内核已添加,不需要我们编写。
    在这里插入图片描述

    # ls /dev/ttymxc* -l
    crw-------    1 root     root      207,  16 Jan  1 05:59 /dev/ttymxc0
    crw-rw----    1 root     root      207,  18 Jan  1 00:00 /dev/ttymxc2
    #
    
    • 1
    • 2
    • 3
    • 4

    通过以上消息,uart3 驱动加载成功,uart3 在应用层映射为 ttymxc2

    八、应用编写

    1、概述

    LinuxUART 驱动和 tty 关系密切,在应用层使用 UART 是需特别注意,否则一些特殊字符传输可能有问题。

    2、程序

    /***************************************************************
     Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
     文件名 : uart_test.c
     作者 : 邓涛
     版本 : V1.0
     描述 : 串口在原始模式下进行数据传输--应用程序示例代码
     其他 : 无
     论坛 : www.openedv.com
     日志 : 初版 V1.0 2021/7/20 邓涛创建
     ***************************************************************/
    
    #define _GNU_SOURCE     //在源文件开头定义_GNU_SOURCE宏
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    typedef struct uart_hardware_cfg {
        unsigned int baudrate;      /* 波特率 */
        unsigned char dbit;         /* 数据位 */
        char parity;                /* 奇偶校验 */
        unsigned char sbit;         /* 停止位 */
    } uart_cfg_t;
    
    static struct termios old_cfg;  //用于保存终端的配置参数
    static int fd;      //串口终端对应的文件描述符
    
    /**
     ** 串口初始化操作
     ** 参数device表示串口终端的设备节点
     **/
    static int uart_init(const char *device)
    {
        /* 打开串口终端 */
        fd = open(device, O_RDWR | O_NOCTTY);
        if (0 > fd) {
            fprintf(stderr, "open error: %s: %s\n", device, strerror(errno));
            return -1;
        }
    
        /* 获取串口当前的配置参数 */
        if (0 > tcgetattr(fd, &old_cfg)) {
            fprintf(stderr, "tcgetattr error: %s\n", strerror(errno));
            close(fd);
            return -1;
        }
    
        return 0;
    }
    
    /**
     ** 串口配置
     ** 参数cfg指向一个uart_cfg_t结构体对象
     **/
    static int uart_cfg(const uart_cfg_t *cfg)
    {
        struct termios new_cfg = {0};   //将new_cfg对象清零
        speed_t speed;
    
        /* 设置为原始模式 */
        cfmakeraw(&new_cfg);
    
        /* 使能接收 */
        new_cfg.c_cflag |= CREAD;
    
        /* 设置波特率 */
        switch (cfg->baudrate) {
        case 1200: speed = B1200;
            break;
        case 1800: speed = B1800;
            break;
        case 2400: speed = B2400;
            break;
        case 4800: speed = B4800;
            break;
        case 9600: speed = B9600;
            break;
        case 19200: speed = B19200;
            break;
        case 38400: speed = B38400;
            break;
        case 57600: speed = B57600;
            break;
        case 115200: speed = B115200;
            break;
        case 230400: speed = B230400;
            break;
        case 460800: speed = B460800;
            break;
        case 500000: speed = B500000;
            break;
        default:    //默认配置为115200
            speed = B115200;
            printf("default baud rate: 115200\n");
            break;
        }
    
        if (0 > cfsetspeed(&new_cfg, speed)) {
            fprintf(stderr, "cfsetspeed error: %s\n", strerror(errno));
            return -1;
        }
    
        /* 设置数据位大小 */
        new_cfg.c_cflag &= ~CSIZE;  //将数据位相关的比特位清零
        switch (cfg->dbit) {
        case 5:
            new_cfg.c_cflag |= CS5;
            break;
        case 6:
            new_cfg.c_cflag |= CS6;
            break;
        case 7:
            new_cfg.c_cflag |= CS7;
            break;
        case 8:
            new_cfg.c_cflag |= CS8;
            break;
        default:    //默认数据位大小为8
            new_cfg.c_cflag |= CS8;
            printf("default data bit size: 8\n");
            break;
        }
    
        /* 设置奇偶校验 */
        switch (cfg->parity) {
        case 'N':       //无校验
            new_cfg.c_cflag &= ~PARENB;
            new_cfg.c_iflag &= ~INPCK;
            break;
        case 'O':       //奇校验
            new_cfg.c_cflag |= (PARODD | PARENB);
            new_cfg.c_iflag |= INPCK;
            break;
        case 'E':       //偶校验
            new_cfg.c_cflag |= PARENB;
            new_cfg.c_cflag &= ~PARODD; /* 清除PARODD标志,配置为偶校验 */
            new_cfg.c_iflag |= INPCK;
            break;
        default:    //默认配置为无校验
            new_cfg.c_cflag &= ~PARENB;
            new_cfg.c_iflag &= ~INPCK;
            printf("default parity: N\n");
            break;
        }
    
        /* 设置停止位 */
        switch (cfg->sbit) {
        case 1:     //1个停止位
            new_cfg.c_cflag &= ~CSTOPB;
            break;
        case 2:     //2个停止位
            new_cfg.c_cflag |= CSTOPB;
            break;
        default:    //默认配置为1个停止位
            new_cfg.c_cflag &= ~CSTOPB;
            printf("default stop bit size: 1\n");
            break;
        }
    
        /* 将MIN和TIME设置为0 */
        new_cfg.c_cc[VTIME] = 0;
        new_cfg.c_cc[VMIN] = 0;
    
        /* 清空缓冲区 */
        if (0 > tcflush(fd, TCIOFLUSH)) {
            fprintf(stderr, "tcflush error: %s\n", strerror(errno));
            return -1;
        }
    
        /* 写入配置、使配置生效 */
        if (0 > tcsetattr(fd, TCSANOW, &new_cfg)) {
            fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));
            return -1;
        }
    
        /* 配置OK 退出 */
        return 0;
    }
    
    /***
    --dev=/dev/ttymxc2
    --brate=115200
    --dbit=8
    --parity=N
    --sbit=1
    --type=read
    ***/
    /**
     ** 打印帮助信息
     **/
    static void show_help(const char *app)
    {
        printf("Usage: %s [选项]\n"
            "\n必选选项:\n"
            "  --dev=DEVICE     指定串口终端设备名称, 譬如--dev=/dev/ttymxc2\n"
            "  --type=TYPE      指定操作类型, 读串口还是写串口, 譬如--type=read(read表示读、write表示写、其它值无效)\n"
            "\n可选选项:\n"
            "  --brate=SPEED    指定串口波特率, 譬如--brate=115200\n"
            "  --dbit=SIZE      指定串口数据位个数, 譬如--dbit=8(可取值为: 5/6/7/8)\n"
            "  --parity=PARITY  指定串口奇偶校验方式, 譬如--parity=N(N表示无校验、O表示奇校验、E表示偶校验)\n"
            "  --sbit=SIZE      指定串口停止位个数, 譬如--sbit=1(可取值为: 1/2)\n"
            "  --help           查看本程序使用帮助信息\n\n", app);
    }
    
    /**
     ** 信号处理函数,当串口有数据可读时,会跳转到该函数执行
     **/
    static void io_handler(int sig, siginfo_t *info, void *context)
    {
        unsigned char buf[10] = {0};
        int ret;
        int n;
    
        if(SIGRTMIN != sig)
            return;
    
        /* 判断串口是否有数据可读 */
        if (POLL_IN == info->si_code) {
            ret = read(fd, buf, 8);     //一次最多读8个字节数据
            printf("[ ");
            for (n = 0; n < ret; n++)
                printf("0x%hhx ", buf[n]);
            printf("]\n");
        }
    }
    
    /**
     ** 异步I/O初始化函数
     **/
    static void async_io_init(void)
    {
        struct sigaction sigatn;
        int flag;
    
        /* 使能异步I/O */
        flag = fcntl(fd, F_GETFL);  //使能串口的异步I/O功能
        flag |= O_ASYNC;
        fcntl(fd, F_SETFL, flag);
    
        /* 设置异步I/O的所有者 */
        fcntl(fd, F_SETOWN, getpid());
    
        /* 指定实时信号SIGRTMIN作为异步I/O通知信号 */
        fcntl(fd, F_SETSIG, SIGRTMIN);
    
        /* 为实时信号SIGRTMIN注册信号处理函数 */
        sigatn.sa_sigaction = io_handler;   //当串口有数据可读时,会跳转到io_handler函数
        sigatn.sa_flags = SA_SIGINFO;
        sigemptyset(&sigatn.sa_mask);
        sigaction(SIGRTMIN, &sigatn, NULL);
    }
    
    int main(int argc, char *argv[])
    {
        uart_cfg_t cfg = {0};
        char *device = NULL;
        int rw_flag = -1;
        unsigned char w_buf[10] = {0x11, 0x22, 0x33, 0x44,0x55, 0x66, 0x77, 0x88};    //通过串口发送出去的数据
        int n;
    
        /* 解析出参数 */
        for (n = 1; n < argc; n++) {
    
            if (!strncmp("--dev=", argv[n], 6))
                device = &argv[n][6];
            else if (!strncmp("--brate=", argv[n], 8))
                cfg.baudrate = atoi(&argv[n][8]);
            else if (!strncmp("--dbit=", argv[n], 7))
                cfg.dbit = atoi(&argv[n][7]);
            else if (!strncmp("--parity=", argv[n], 9))
                cfg.parity = argv[n][9];
            else if (!strncmp("--sbit=", argv[n], 7))
                cfg.sbit = atoi(&argv[n][7]);
            else if (!strncmp("--type=", argv[n], 7)) {
                if (!strcmp("read", &argv[n][7]))
                    rw_flag = 0;        //读
                else if (!strcmp("write", &argv[n][7]))
                    rw_flag = 1;        //写
            }
            else if (!strcmp("--help", argv[n])) {
                show_help(argv[0]); //打印帮助信息
                exit(EXIT_SUCCESS);
            }
        }
    
        if (NULL == device || -1 == rw_flag) {
            fprintf(stderr, "Error: the device and read|write type must be set!\n");
            show_help(argv[0]);
            exit(EXIT_FAILURE);
        }
    
        /* 串口初始化 */
        if (uart_init(device))
            exit(EXIT_FAILURE);
    
        /* 串口配置 */
        if (uart_cfg(&cfg)) {
            tcsetattr(fd, TCSANOW, &old_cfg);   //恢复到之前的配置
            close(fd);
            exit(EXIT_FAILURE);
        }
    
        /* 读|写串口 */
        switch (rw_flag) {
        case 0:  //读串口数据
            async_io_init();	//我们使用异步I/O方式读取串口的数据,调用该函数去初始化串口的异步I/O
            for ( ; ; )
                sleep(1);   	//进入休眠、等待有数据可读,有数据可读之后就会跳转到io_handler()函数
            break;
        case 1:   //向串口写入数据
            for ( ; ; ) {   		//循环向串口写入数据
                write(fd, w_buf, 8); 	//一次向串口写入8个字节
                sleep(1);       		//间隔1秒钟
            }
            break;
        }
    
        /* 退出 */
        tcsetattr(fd, TCSANOW, &old_cfg);   //恢复到之前的配置
        close(fd);
        exit(EXIT_SUCCESS);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329

    程序使用正点原子提供。

    九、测试

    1、数据接收

    # ./uart_app --dev=/dev/ttymxc2 --type=read
    default baud rate: 115200
    default data bit size: 8
    default parity: N
    default stop bit size: 1
    
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    [ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
    
    #
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、数据发送

    在这里插入图片描述

  • 相关阅读:
    【水质预测】基于matlab模糊神经网络水质预测【含Matlab源码 1923期】
    个性化联邦学习-综述
    [100天算法】-目标和(day 79)
    关于 在Qt中的timerEvent信号中设置QCustomplot的日期时间轴范围乱蹿(编译器优化变量volatile) 的解决方法
    SpringCloud 微服务全栈体系(二)
    SwiftUI的认识与使用
    vue3后台管理框架之API接口统一管理
    如何使用Arduino构建3D打印的电子动画眼睛
    功能解剖学重点
    闲谈工业企业全厂信息化规划
  • 原文地址:https://blog.csdn.net/OnlyLove_/article/details/127950130