RK3568平台(UART篇)Uart驱动框架

一.TTY子系统

在Linux kernel中,tty驱动不像于spi,iic等那么架构简单,它是一个庞大的系统,它的框架大体如下图一。我们作为普通的驱动开发移植人员,不会从零写tty驱动,一般都是厂家根据现有的tty驱动和自家芯片修改,拿到板子按照厂家的配置,串口应该使能直接使用的。但是开发的过程中也有可能需要用到串口,一般会修改serial驱动,这样我们不会动tty_core层。

Linux tty子系统包含:tty核心(tty_core),tty线路规程(tty_line_discipine)和tty驱动(tty_driver)。tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱动。其整体框架如下图所示:

 二.UART 驱动关键数据结构

 同 I2C、SPI 一样,Linux 也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱 动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也 已经由 原厂编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信 息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成 /dev/ttymxcX(X=0….n)文件。

struct uart_driver:

uart_driver 包含了串口设备名,串口驱动名,主次设备号,串口控制台(可选))等信息,还封装了tty_driver (底层串口驱动无需关心tty_driver)

struct uart_driver {
        struct module    *owner; /*拥有该uart_driver的模块,一般为THIS_MODULE*/
        const char        *driver_name; /*驱动串口名,串口设备名以驱动名为基础*/
        const char        *dev_name; /*串口设备名*/
        int                 major; /*主设备号*/
        int                 minor; /*次设备号*/
        int                 nr; /*该uart_driver支持的串口数*/
        struct console    *cons; /*其对应的console,若该uart_driver支持serial console,
*否则为NULL*/
/*
* 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; /*tty相关*/
 

struct console

实现控制台打印功能必须要注册的结构体

struct console {
      char name[16];
      void(*write)(struct console *,const char *, unsigined);
      int (*read)(struct console *, char *, unsigned);
      struct tty_driver *(struct console *,int*);
      void (*unblank)(void);
      int  (*setup)(struct console *, char *);
      int  (*early_setup)(void);
      short  flags;
      short  index; /*用来指定该console使用哪一个uart port (对应的uart_port中的line),如果为-1,kernel会自动选择第一个uart port*/
      int   cflag;
      void  *data;
      struct   console *next;
};
 

struct uart_state:

每一个uart端口对应着一个uart_state,该结构体将uart_port与对应的circ_buf联系起来。uart_state有两个成员在底层串口驱动会用到:xmit和port。

用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。串口接收中断处理函数需要通过port将接收到的数据传递给线路规程层。

struct uart_state {
       struct  tty_port  port;
       
       enum uart_pm_state   pm_state;
       struct circ_buf     xmit;
       
       struct uart_port     *uart_port; /*对应于一个串口设备*/
};
 

struct uart_port:

uart_port用于描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实现对应一个串口设备。

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);
        int                     (*handle_irq)(struct uart_port *);
        void                    (*pm)(struct uart_port *, unsigned int state,
                                      unsigned int old);
        void                    (*handle_break)(struct uart_port *);
        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               (0)
#define UPIO_HUB6               (1)
#define UPIO_MEM                (2)
#define UPIO_MEM32              (3)
#define UPIO_AU                 (4)                     /* Au1x00 and RT288x type IO */
#define UPIO_TSI                (5)                     /* Tsi108/109 type IO */
 
        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
 
        upf_t                   flags;
 
#define UPF_FOURPORT            ((__force upf_t) (1 << 1))
#define UPF_SAK                 ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK            ((__force upf_t) (0x1030))
#define UPF_SPD_HI              ((__force upf_t) (0x0010))
#define UPF_SPD_VHI             ((__force upf_t) (0x0020))
#define UPF_SPD_CUST            ((__force upf_t) (0x0030))
#define UPF_SPD_SHI             ((__force upf_t) (0x1000))
#define UPF_SPD_WARP            ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST           ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ            ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD          ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY         ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART          ((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST        ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER    ((__force upf_t) (1 << 16))
/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
#define UPF_HARD_FLOW           ((__force upf_t) (1 << 21))
/* 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         ((__force upf_t) (0x17fff))
#define UPF_USR_MASK            ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))
 
        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 */
        resource_size_t         mapbase;                /* for ioremap */
        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];
        void                    *private_data;          /* generic platform data pointer */
};
 

struct uart_ops:

struct uart_ops涵盖了驱动可对串口的所有操作

 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 *, int new);
        void            (*pm)(struct uart_port *, unsigned int state,
                              unsigned int oldstate);
        int             (*set_wake)(struct uart_port *, unsigned int state);
 
        /*
         * 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关键流程

注册uart_driver:

此接口在uart driver中调用,用来注册uart_driver到kernel中,调用阶段在uart driver的初始阶段,例如:module_init(), uart_driver的注册流程图。

 添加uart_port:

此接口用于注册一个uart port 到uart driver上,通过注册,uart driver就可以访问对应的uart port,进行数据收发。该接口在uart driver中的probe函数调用,必须保证晚于uart_register_drver的注册过程。

打开设备(open操作):

数据发送流程(write操作):

数据接收流程(read操作) :

关闭设备(close操作): 

除uart_port:

此接口用于从uart driver上注销一个uart port,该接口在uart driver中的remove函数中调用。

四.RS-232通信

(1) 基本概念

RS-232接口符合美国电子工业联盟(EIA)制定的串行数据通信的接口标准,被广泛用于计算机串行接口外设连接,像有些老式PC机上就配置有RS232接口。RS232的工作方式是单端工作方式,这是一种不平衡的传输方式,收发端信号的逻辑电平都是相对于信号地而言的,RS232最初是DTE(数字终端设备)和DCE(数据通信设备)一对一通信,也就是点对点,一般是用于全双工传送,也可以用于半双工传送。

采用负逻辑传送,规定逻辑“1”的电平为-5V~-15 V,逻辑“0”的电平为+5 V~+15 V。选用该电气标准的目的在于提高抗干扰能力,增大通信距离。RS -232的噪声容限为2V,接收器将能识别高至+3V的信号作为逻辑“0”,将低到-3 V的信号作为逻辑“1”。

传输距离短,只有15米,实际应用可以达到50米,再长的距离就须加调制了。

RS232标准物理接口是25个引脚的,因为常用的是9个引脚,后来基本采用DB9连接器了。

RS-232标准主要规定了信号用途、通讯接口和信号电平标准。如下图为常见的设备间串口通讯结构图。

下面介绍下DB9接口。如下图为DB9标准的公头、母头接法(接线口以针式引出信号线为公头,以孔式引出信号线为母头)。

如上图,以公头为例,有9条信号线,其中,

RXD:用于接收DCE发来的数据信号,即输入;
TXD:用于发送DTE的信号,即输出。公头和母头的RXD和TXD应交叉连接;
GND:用于平衡设备双方的地电位,即共地。
其他信号线如DCD、DTR、DSR、RTS、CTS等,使用逻辑1表示有效信号,逻辑0表示无效信号。如DTE端的RTS信号线置1时,是为了告知DCE设备本机已准备好 接收数据,而置0则表示未准备就绪。

(2)通信方式

RS232 可做到双向传输,全双工通讯,最高传输速率 20kbps。

(3)与UART异同点

逻辑电平不同:UART是TTL电平,RS232是RS232电平,不能直接相连,需要电平转换芯片(MAX3232等)。

协议层完全相同:UART和RS232的协议层可以共享,没有较大区别。

(4)缺点

接口电平值较高,易损坏接口电路的芯片;又因为与TTL电平不兼容故需使用电平转换电路方能与TTL电路连接。

传输速率较低,大约为20Kbps;传输距离较短,大约为15米左右。

接口由三根线TX、RX、GND组成,没有构成差分线形式,容易产生共地共模干扰,抗干扰能力弱。

  • 32
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是一个简单的Linux UART驱动程序的框架: ```c #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/serial.h> #define DRIVER_NAME "my_uart" // 定义字符设备结构体 struct my_uart_dev { struct cdev cdev; struct class *class; struct device *device; dev_t devno; struct uart_port uart; }; static int my_uart_open(struct inode *inode, struct file *filp) { struct my_uart_dev *dev = container_of(inode->i_cdev, struct my_uart_dev, cdev); filp->private_data = dev; // 初始化串口 uart_config(&dev->uart); return 0; } static ssize_t my_uart_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct my_uart_dev *dev = filp->private_data; ssize_t ret; // 从串口读取数据到buf中 ret = uart_read(&dev->uart, buf, count); return ret; } static ssize_t my_uart_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct my_uart_dev *dev = filp->private_data; ssize_t ret; // 将buf中的数据写入到串口中 ret = uart_write(&dev->uart, buf, count); return ret; } static const struct file_operations my_uart_fops = { .owner = THIS_MODULE, .open = my_uart_open, .read = my_uart_read, .write = my_uart_write, }; static int __init my_uart_init(void) { int ret; struct my_uart_dev *dev; // 分配设备结构体的内存 dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err_alloc_dev; } // 初始化串口 uart_init_port(&dev->uart); // 注册字符设备 dev->devno = MKDEV(0, 0); ret = alloc_chrdev_region(&dev->devno, 0, 1, DRIVER_NAME); if (ret < 0) { goto err_alloc_chrdev_region; } cdev_init(&dev->cdev, &my_uart_fops); dev->cdev.owner = THIS_MODULE; ret = cdev_add(&dev->cdev, dev->devno, 1); if (ret < 0) { goto err_cdev_add; } // 创建设备节点 dev->class = class_create(THIS_MODULE, DRIVER_NAME); if (IS_ERR(dev->class)) { ret = PTR_ERR(dev->class); goto err_class_create; } dev->device = device_create(dev->class, NULL, dev->devno, NULL, DRIVER_NAME); if (IS_ERR(dev->device)) { ret = PTR_ERR(dev->device); goto err_device_create; } return 0; err_device_create: class_destroy(dev->class); err_class_create: cdev_del(&dev->cdev); err_cdev_add: unregister_chrdev_region(dev->devno, 1); err_alloc_chrdev_region: kfree(dev); err_alloc_dev: return ret; } static void __exit my_uart_exit(void) { struct my_uart_dev *dev = container_of(&my_uart_fops, struct my_uart_dev, cdev); // 删除设备节点 device_destroy(dev->class, dev->devno); class_destroy(dev->class); // 注销字符设备 cdev_del(&dev->cdev); unregister_chrdev_region(dev->devno, 1); // 释放设备结构体的内存 kfree(dev); } module_init(my_uart_init); module_exit(my_uart_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your name"); MODULE_DESCRIPTION("My UART driver"); ``` 需要注意的地方: 1. 在字符设备结构体`my_uart_dev`中,有一个`struct uart_port uart`成员,用来保存串口的状态。 2. 在驱动初始化函数`my_uart_init`中,需要分配`my_uart_dev`结构体的内存,并初始化`struct uart_port uart`成员。 3. 在驱动初始化函数`my_uart_init`中,需要注册字符设备,并创建设备节点。 4. 在驱动退出函数`my_uart_exit`中,需要删除设备节点,并注销字符设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式_笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值