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

news2024/11/23 20:57:14

一、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;
};

每个串口驱动都需要定义一个 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 */
};

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
};

四、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、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
	>;
};

4、在 uart3 下追加配置

&uart3 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart3>;
	status = "okay";
};

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$ 

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#
#

七、驱动编写

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
#

通过以上消息,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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <termios.h>

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、数据接收

# ./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 ]

#

2、数据发送

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/20939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python自动化之——获取钉钉群所有人的昵称

python自动化之——获取钉钉群所有人的昵称 楔子 精神小伙沙大柱入职了一家新公司&#xff0c;该公司所有成员都在钉钉群。 一天&#xff0c;沙大柱的上级沙小牛布置了任务&#xff1a;大柱&#xff0c;你把群里所有人的名称导出来吧&#xff0c;我不会操作。 大柱表示&…

【PyCharm中PIL/Pillow的安装】

&#x1f935;‍♂️ 个人主页老虎也淘气 个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f44d;&#x1f3fb; 收藏…

操作系统 - 进程

文章目录操作系统1.操作系统的定位2.进程2.1 PCB的一些属性2.3 进程调度相关属性 &#xff1a;本文小结操作系统 操作系统是一个软件   用途 &#xff1a;管理   1.对下 &#xff1a;管理硬键设备 2.对上 : 为软件提供稳定的运行环境 进一步来说 &#xff1a; 操作系统是软件…

多重背包问题

多重背包也是 0-1 背包的一个变式。与 0-1 背包的区别在于每种物品有ki个&#xff0c;而非一个。 一个很朴素的想法就是&#xff1a;把「每种物品选ki次」等价转换为「有ki个相同的物品&#xff0c;每个物品选一次」。这样就转换成了一个 0-1 背包模型&#xff0c;套用上文所述…

智慧民政解决方案-最新全套文件

智慧民政解决方案-最新全套文件一、建设背景二、建设思路三、建设方案四、获取 - 智慧民政全套最新解决方案合集一、建设背景 在城市信息化建设的大浪潮中&#xff0c;民政信息化建设关系就业、收入、教育、文体、健康、养老和社保等民间社会事务的管理与服务&#xff0c;在智…

Kettle运行Spoon.bat出现命令框然后闪退【BUG已解决】

文章目录项目场景&#xff1a;问题描述原因分析&#xff1a;解决方案&#xff1a;项目场景&#xff1a; 在内科大数据处理课程中&#xff0c;要求安装Kettle。 Kettle&#xff1a; Pentaho Data Integration以Java开发&#xff0c;支持跨平台运行&#xff0c;其特性包括&#…

【算法入门搜索法】走迷宫|单源最短路径1

✅作者简介&#xff1a;热爱后端语言的大学生&#xff0c;CSDN内容合伙人 ✨精品专栏&#xff1a;C面向对象 &#x1f525;系列专栏&#xff1a;算法百炼成神 文章目录&#x1f525;前言1、AB20 走迷宫1.1、解题思路1.2、代码实现与注释2、AB19 【模板】单源最短路12.1、单源最…

CMake中while/continue/break的使用

CMake中的while命令用于在条件为true时评估(evaluate)一组命令&#xff0c;其格式如下&#xff1a; while(<condition>)<commands> endwhile() 在while和匹配的endwhile之间的所有命令都被记录下来而不被调用。一旦评估了endwhile&#xff0c;只要<condition&g…

MIT 6.S081 Operating System Lecture4 (随意的笔记)

系列文章目录 文章目录系列文章目录xv6 中的内存页是如何分配的RISC-V 是多级页表对page table的理解xv6 中的内存页是如何分配的 在本课中&#xff0c;内存也相关源码路径为&#xff1a; kernel/kallo.c // Physical memory allocator, for user processes, // kernel stack…

uni-app入门:wxs基本使用

1.wxs相关介绍 2.wxs标签内嵌在wxml中使用 3.在.wxs文件中外联使用 4.wxs与JavaScript区别 1.wxs相关介绍wxs(weixin script),是小程序的一套脚本语言&#xff0c;结合 WXML&#xff0c;可以构建出页面的结构。可以编写在 wxml 文件中的 标签内&#xff0c;或以…

Spring 项目的创建和 “使用“

目录 1. 创建 Spring 项目 1.1 创键一个 Maven 项目【无需模板】 1.2 添加 Spring 依赖【Spring-context/Spring-beans】 1.3 创建一个启动类 2. 将对象存储到 Spring 中 2.1 创建一个 bean 对象 2.2 将 bean 注册到 Spring 中【使用 Spring 配置文件进行注册】 3. 从 …

【树莓派不吃灰】命令篇⑧ 校准树莓派时间

目录1. systemd-timesyncd1.1 systemd-timesyncd 客户端1.2 systemd-timesyncd 服务1.3 systemd-timesyncd 配置文件1.4 timedatectl命令2. 校准时间2.1 查看时间状态2.2 校准时区2.3 没有时钟同步服务器&#xff0c;手工设置时间2.3.1 禁止ntp自动同步2.3.2 设置时间2.3.3 设置…

敏感词检测库ToolGood.Words中IllegalWordsSearch类使用简介

C#开源敏感词检测库ToolGood.Words中的类IllegalWordsSearch为过滤非法词&#xff08;敏感词&#xff09;专用类&#xff0c;可设置跳字长度&#xff0c;支持全角转忽略大小、跳词、重复词、黑名单等功能&#xff0c;本文对照参考文献1&#xff0c;对该类的用法进行简要介绍。 …

k8s资源对象service-四层负载均衡详解

理论 工作原理如图: service的定义:是一组pod的逻辑组合,通过clusterIP和服务端口接收请求,并将这些请求代理至使用标签选择器来过滤符合条件的pod对象。 作用:服务发现和服务访问,为弹性变动且存在生命周期的pod对象提供了一个固定的访问接口。 service的代理类型:…

Vue动态切换class属性:数组法、对象法

需求&#xff1a;在style里创建好不同的属性&#xff0c;后期可以给标签动态绑定这些属性&#xff0c;也可以实现属性的切换方法&#xff1a;对象法、数组法事先创建好class属性&#xff1a; <style>.aa{}.bb{}.cc{} </style> 对象法&#xff1a; <body><…

矩阵(加速)。。。

我限定你在明天中午之前搞定这东西&#xff01;毕竟之前做过了欸。矩阵&#xff0c;一个看起来很神奇的东西&#xff0c;不过我不打算花太多的时间做这个&#xff0c;还是图论和数论好点儿&#xff0c;还要复习一下之前的数据结构和dp呢。那么先谈谈定义&#xff0c;定义一个矩…

kubernetes介绍与资源管理

#kubernetes介绍与资源管理 1应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;…

CMSC5713-IT项目管理之人力资源管理Human Resources Management

文章目录9.1. Introduction9.1.1. Motivation Theories9.1.2. Influence and Power9.1.3. Motivating Team9.2. Project Human Resource Management9.3. Human Resource Planning9.3.1. Project Organization Charts9.3.2. Responsibility Assignment Matrices9.3.2.1. RACI Ch…

锐捷BFD基础实验配置

目录 BFD与静态路由联动 BFD与静态路由联动&#xff08;单跳&#xff09; BFD与静态路由联动&#xff08;多跳&#xff09; BFD与动态路由联动 配置BFD与OSPF联动 配置BFD与BGP联动 BFD与静态路由联动 BFD联动静态路由注意事项 1、配置需要联动的静态路由时&#xff0c…

Java线程池

自定义线程池 1. 简介 1.1 引入原因 1. 一个任务过来&#xff0c;一个线程去做。如果每次过来都创建新线程&#xff0c;性能低且比较耗费内存 2. 线程数多于cpu核心&#xff0c;线程切换&#xff0c;要保存原来线程的状态&#xff0c;运行现在的线程&#xff0c;势必会更加耗…