文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基于Linux的tty架构及UART驱动详解

2024-12-03 11:31

关注

一、模块硬件学习

1.1. Uart介绍

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称为UART,是一种异步收发传输器,是电脑硬件的一部分。它将要传输的资料在串行通信与并行通信之间加以转换。

作为把并行输入信号转成串行输出信号的芯片,UART 通常被集成于其他通讯接口的连上。

UART 是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设备中,UART 用于主机与辅助设备通信,如汽车音与外接AP 之间的通信,与PC 机通信包括与监控调试器和其它器件,如EEPOM通信。

1.1.1. 通信协议

UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。其中各位的意义如下:

由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

Uart传输数据如图2-1所示:


1.1.2. 波特率

波特率是衡量资料传送速率的指标。表示每秒钟传送的符号数(symbol)。一个符号代表的信息量(比特数)与符号的阶数有关。例如传输使用256阶符号,每8bit代表一个符号,数据传送速率为120字符/秒,则波特率就是120 baud,比特率是120*8=960bit/s。这两者的概念很容易搞错。

UART 的接收和发送是按照相同的波特率进行收发的。波特率发生器产生的时钟频率不是波特率时钟频率,而是波特率时钟频率的16倍,目的是为在接收时进行精确的采样,以提取出异步的串行数据。根据给定的晶振时钟和要求的波特率,可以算出波特率分频计数值。

1.1.3. 工作原理

发送数据过程:空闲状态,线路处于高电位;当收到发送数据指令后,拉低线路一个数据位的时间T,接着数据位按低位到高位依次发送,数据发送完毕后,接着发送奇偶检验位和停止位(停止位为高电位),一帧数据发送结束。

接收数据过程: 空闲状态,线路处于高电位;当检测到线路的下降沿(线路电位由高电位变为低电位)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶检验位是否正确,如果正确则通知则通知后续设备准备接收数据或存入缓存。

由于UART是异步传输,没有传输同步时钟。为了能保证数据传输的正确性,UART采用16倍数据波特率的时钟进行采样。每个数据有16个时钟采样,取中间的采样值,以保证采样不会滑码或误码。

一般UART一帧的数据位为8,这样即使每一个数据有一个时钟的误差,接收端也能正确地采样到数据。

UART的接收数据时序为:当检测到数据下降沿时,表明线路上有数据进行传输,这时计数器CNT开始计数,当计数器,当计数器为8时,采样的值为“0”表示开始位;当计数器为24=161+8时,采样的值为bit0数据;当计数器的值为40=162+8时,采样的值为bit1数据;依次类推,进行后面6个数据的采样。如果需要进行奇偶校验位,则当计数器的值为152=169+8时,采样的值为奇偶位;当计数器的值为168=1610+8时,采样的值为“1”表示停止位,一帧数据收发完成。

1.1.4. RS232与RS485

UART:通常说的UART指的是一种串行通信协议,规定了数据帧格式,波特率等。

RS232和RS485:是两种不同的电气协议,也就是说,是对电气特性以及物理特性的规定,作用于数据的传输通路上,它并不含对数据的处理方式。

对应的物理器件有RS232或者RS485驱动芯片,将CPU经过UART传送过来的电压信号驱动成RS232或者RS485电平逻辑。

RS232使用3-15V有效电平,而UART,因为对电气特性没有规定,所以直接使用CPU使用的电平,即TTL电平(在0-3.3V之间)。

更具体的,电气的特性也决定了线路的连接方式,比如RS232,规定用电平表示数据,因此线路就是单线路的,两根线能达到全双工的目的;RS485使用差分电平表示数据,因此必须用两根线才能达到传输数据的基本要求,要实现全双工,必须使用4根线。

RS232和RS485的区别(1)抗干扰性

1.1.5. 流控

数据在两个串口传输时,常常会出现丢失数据的现象,或者两台计算机的处理速度不同,如台式机与单片机之间的通讯,接收端数据缓冲区以满,此时继续发送的数据就会丢失,流控制能解决这个问题,当接收端数据处理不过来时,就发出“不再接收”的信号,发送端就停止发送,直到收到“可以继续发送”的信号再发送数据。

因此流控制可以控制数据传输的进程,防止数据丢失。PC机中常用的两种流控为:硬件流控(包括RTS/CTS、DTR/CTS等)和软件流控制XON/XOFF(继续/停止)。

硬件流控制常用的有RTS/CTS流控制和DTR/DSR流控制两种。

DTR–数据终端就绪(Data Terminal Ready)低有效,当为低时,表示本设备自身准备就绪。此信号输出对端设备,使用对端设备决定能否与本设备通信。

DSR-数据装置就绪(Data Set Ready)低有效,此信号由本设备相连接的对端设备提供,当为低时,本设备才能与设备端进行通信。

RTS - 请求发送(数据)(Request To Send)低有效,此信号由本设备在需要发送数据给对端设备时设置。当为低时,表示本设备有数据需要向对端设备发送。对端设备能否接收到本方的发送数据,则通过CTS信号来应答。

CTS - 接收发送(请求)(Clear To Send)低有效,对端设备能否接收本方所发送的数据,由CTS决定。若CTS为低,则表示对端的以准备好,可以接收本端发送数据。

以RTS/CTS流控制分析,分析主机发送/接收流程:

物理连接


主机的RTS(输出信号),连接到从机的CTS(输入信号)。主机是CTS(输入信号),连接到从机的RTS(输入信号)。

由于电缆的限制,在普通的控制通讯中一般不采用硬件流控制,而是使用软件流控制。

一般通过XON/XOFF来实现软件流控制。常用方法是:当接收端的输入缓冲区内数据量超过设定的高位时,就向数据发送端发送XOFF字符后就立即停止发送数据。

当接收端的输入缓冲区内数据量低于设定的低位时,就向数据发送端发送XON字符(十进制的17或Control-Q),发送端收到XON字符后就立即开始发送数据。

一般可从设备配套源程序中找到发送端收到XON字符后就立即发送数据。一般可以从设备配套源程序中找到发送的是什么字节。

应注意,若传输的是二进制的数据,标志字符也可能在数据流中出现而引起误操作,这是软件流控的缺陷,而硬件流控不会出现这样的问题。

二、Linux serial框架

在Linux系统中,终端是一种字符型设备,它有多种类型,通常使用tty(Teletype)来简称各种类型的终端设备。

对于嵌入式系统而言,最普遍采用的是Uart(Universal Asynchronous Receiver/Transmitter),串行端口,日常生活中简称端口

2.1. TTY驱动程序框架

2.1.1. TTY概念

串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看做是一个字符设备。这些串行端口所对应的设备名称是/dev/ttySAC*;

在Linux系统中,计算机的输出设备通常被称为控制台终端,这里特指printk信息输出到设备。/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数“console=ttySCA0”就把console映射到了串口0

当用户登录时,使用的是虚拟终端。使用Ctcl+Alt[F1 - F6]组合键时,我们就可以切换到tty1、tty2、tty3等上面去。tty*就称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名。

2.1.2. TTY架构分析

整个 tty架构大概的样子如图3.1所示,简单来分的话可以说成两层,一层是下层我们的串口驱动层,它直接与硬件相接触,我们需要填充一个 struct uart_ops 的结构体,另一层是上层 tty 层,包括 tty 核心以及线路规程,它们各自都有一个 Ops 结构,用户空通过间是 tty 注册的字符设备节点来访问。


图3.1tty架构图

如图3.2所示,tty设备发送数据的流程为:tty核心从一个用户获取将要发送给一个tty设备的数据,tty核心将数据传递给tty线路规程驱动,接着数据被传到tty驱动,tty驱动将数据转换为可以发给硬件的格式。

接收数据的流程为:从tty硬件接收到的数据向上交给tty驱动,接着进入tty线路规程驱动,再进入tty核心,在这里它被一个用户获取。


图3.2 tty设备发送、接收数据流程

2.2. 关键数据结构

2.2.1. Struct uart_driver

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

  1. struct uart_driver { 
  2.         struct module    *owner;  
  3.         const char        *driver_name;  
  4.         const char        *dev_name;  
  5.         int                 major;  
  6.         int                 minor;  
  7.         int                 nr;  
  8.         struct console    *cons;  
  9.  
  10. struct uart_state *state;  
  11. struct tty_driver  *tty_driver;  

2.2.2. struct console

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

  1. struct console { 
  2.       char name[16]; 
  3.       void(*write)(struct console *,const char *, unsigined); 
  4.       int (*read)(struct console *, char *, unsigned); 
  5.       struct tty_driver *(struct console *,int*); 
  6.       void (*unblank)(void); 
  7.       int  (*setup)(struct console *, char *); 
  8.       int  (*early_setup)(void); 
  9.       short  flags; 
  10.       short  index;  
  11.       int   cflag; 
  12.       void  *data; 
  13.       struct   console *next
  14. }; 

2.2.3. struct uart_state

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

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

  1. struct uart_state { 
  2.        struct  tty_port  port; 
  3.         
  4.        enum uart_pm_state   pm_state; 
  5.        struct circ_buf     xmit; 
  6.         
  7.        struct uart_port     *uart_port;  
  8. }; 

2.2.4. struct uart_port

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

  1. struct uart_port { 
  2.         spinlock_t              lock;                    
  3.         unsigned long           iobase;                  
  4.         unsigned char __iomem   *membase;                
  5.         unsigned int            (*serial_in)(struct uart_port *, int); 
  6.         void                    (*serial_out)(struct uart_port *, intint); 
  7.         void                    (*set_termios)(struct uart_port *, 
  8.                                                struct ktermios *new, 
  9.                                                struct ktermios *old); 
  10.         int                     (*handle_irq)(struct uart_port *); 
  11.         void                    (*pm)(struct uart_port *, unsigned int state, 
  12.                                       unsigned int old); 
  13.         void                    (*handle_break)(struct uart_port *); 
  14.         unsigned int            irq;                     
  15.         unsigned long           irqflags;                
  16.         unsigned int            uartclk;                 
  17.         unsigned int            fifosize;                
  18.         unsigned char           x_char;                  
  19.         unsigned char           regshift;                
  20.         unsigned char           iotype;                  
  21.         unsigned char           unused1; 
  22.  
  23. #define UPIO_PORT               (0) 
  24. #define UPIO_HUB6               (1) 
  25. #define UPIO_MEM                (2) 
  26. #define UPIO_MEM32              (3) 
  27. #define UPIO_AU                 (4)                      
  28. #define UPIO_TSI                (5)                      
  29.  
  30.         unsigned int            read_status_mask;        
  31.         unsigned int            ignore_status_mask;      
  32.         struct uart_state       *state;                  
  33.         struct uart_icount      icount;                  
  34.  
  35.         struct console          *cons;                   
  36. #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ) 
  37.         unsigned long           sysrq;                   
  38. #endif 
  39.  
  40.         upf_t                   flags; 
  41.  
  42. #define UPF_FOURPORT            ((__force upf_t) (1 << 1)) 
  43. #define UPF_SAK                 ((__force upf_t) (1 << 2)) 
  44. #define UPF_SPD_MASK            ((__force upf_t) (0x1030)) 
  45. #define UPF_SPD_HI              ((__force upf_t) (0x0010)) 
  46. #define UPF_SPD_VHI             ((__force upf_t) (0x0020)) 
  47. #define UPF_SPD_CUST            ((__force upf_t) (0x0030)) 
  48. #define UPF_SPD_SHI             ((__force upf_t) (0x1000)) 
  49. #define UPF_SPD_WARP            ((__force upf_t) (0x1010)) 
  50. #define UPF_SKIP_TEST           ((__force upf_t) (1 << 6)) 
  51. #define UPF_AUTO_IRQ            ((__force upf_t) (1 << 7)) 
  52. #define UPF_HARDPPS_CD          ((__force upf_t) (1 << 11)) 
  53. #define UPF_LOW_LATENCY         ((__force upf_t) (1 << 13)) 
  54. #define UPF_BUGGY_UART          ((__force upf_t) (1 << 14)) 
  55. #define UPF_NO_TXEN_TEST        ((__force upf_t) (1 << 15)) 
  56. #define UPF_MAGIC_MULTIPLIER    ((__force upf_t) (1 << 16)) 
  57.  
  58. #define UPF_HARD_FLOW           ((__force upf_t) (1 << 21)) 
  59.  
  60. #define UPF_SOFT_FLOW           ((__force upf_t) (1 << 22)) 
  61. #define UPF_CONS_FLOW           ((__force upf_t) (1 << 23)) 
  62. #define UPF_SHARE_IRQ           ((__force upf_t) (1 << 24)) 
  63. #define UPF_EXAR_EFR            ((__force upf_t) (1 << 25)) 
  64. #define UPF_BUG_THRE            ((__force upf_t) (1 << 26)) 
  65.  
  66. #define UPF_FIXED_TYPE          ((__force upf_t) (1 << 27)) 
  67. #define UPF_BOOT_AUTOCONF       ((__force upf_t) (1 << 28)) 
  68. #define UPF_FIXED_PORT          ((__force upf_t) (1 << 29)) 
  69. #define UPF_DEAD                ((__force upf_t) (1 << 30)) 
  70. #define UPF_IOREMAP             ((__force upf_t) (1 << 31)) 
  71.  
  72. #define UPF_CHANGE_MASK         ((__force upf_t) (0x17fff)) 
  73. #define UPF_USR_MASK            ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) 
  74.  
  75.         unsigned int            mctrl;                   
  76.         unsigned int            timeout;                 
  77.         unsigned int            type;                    
  78.         const struct uart_ops   *ops; 
  79.         unsigned int            custom_divisor; 
  80.         unsigned int            line;                    
  81.         resource_size_t         mapbase;                 
  82.         struct device           *dev;                    
  83.         unsigned char           hub6;                    
  84.         unsigned char           suspended; 
  85.         unsigned char           irq_wake; 
  86.         unsigned char           unused[2]; 
  87.         void                    *private_data;           
  88. }; 

2.2.5. struct uart_ops

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

  1.  struct uart_ops { 
  2.         unsigned int    (*tx_empty)(struct uart_port *); 
  3.         void            (*set_mctrl)(struct uart_port *, unsigned int mctrl); 
  4.         unsigned int    (*get_mctrl)(struct uart_port *); 
  5.         void            (*stop_tx)(struct uart_port *); 
  6.         void            (*start_tx)(struct uart_port *); 
  7.         void            (*throttle)(struct uart_port *); 
  8.         void            (*unthrottle)(struct uart_port *); 
  9.         void            (*send_xchar)(struct uart_port *, char ch); 
  10.         void            (*stop_rx)(struct uart_port *); 
  11.         void            (*enable_ms)(struct uart_port *); 
  12.         void            (*break_ctl)(struct uart_port *, int ctl); 
  13.         int             (*startup)(struct uart_port *); 
  14.         void            (*shutdown)(struct uart_port *); 
  15.         void            (*flush_buffer)(struct uart_port *); 
  16.         void            (*set_termios)(struct uart_port *, struct ktermios *new, 
  17.                                        struct ktermios *old); 
  18.         void            (*set_ldisc)(struct uart_port *, int new); 
  19.         void            (*pm)(struct uart_port *, unsigned int state, 
  20.                               unsigned int oldstate); 
  21.         int             (*set_wake)(struct uart_port *, unsigned int state); 
  22.  
  23.          
  24.         const char      *(*type)(struct uart_port *); 
  25.  
  26.          
  27.         void            (*release_port)(struct uart_port *); 
  28.  
  29.          
  30.         int             (*request_port)(struct uart_port *); 
  31.         void            (*config_port)(struct uart_port *, int); 
  32.         int             (*verify_port)(struct uart_port *, struct serial_struct *); 
  33.         int             (*ioctl)(struct uart_port *, unsigned int, unsigned long); 
  34. #ifdef CONFIG_CONSOLE_POLL 
  35.         int             (*poll_init)(struct uart_port *); 
  36.         void            (*poll_put_char)(struct uart_port *, unsigned char); 
  37.         int             (*poll_get_char)(struct uart_port *); 
  38. #endif 
  39. }; 

2.3. 关键流程

2.3.1. 注册流程

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


图3.3uart driver注册流程

注册过程主要做了以下操作:

根据driver支持的最大设备数,申请n个uart_state空间,每一个uart_state都有一个uart_port。

分配一个tty_driver,并将uart_driver->tty_driver指向它。

对tty_driver进行设置,其中包括默认波特率、检验方式等,还有一个重要的ops,结构体tty_operation的注册,它是tty核心与串口驱动通信的接口。

初始化每一个uart_state的tty_port;

注册tty_driver。注册uart_driver实际上是注册tty_driver,与用户空间打交道的工作完全交给tty_driver,这一部分是内核实现好的不需要修改

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

uart driver在调用接口前,要手动设置uart_port的操作uart_ops,使得通过调用uart_add_one_port接口后驱动完成硬件的操作接口注册。uart添加port流程如图3-4所示:


图3-4 uart添加port流程图

2.4. 数据收发流程

2.4.1. 打开设备(open操作)

open设备的大体流程如图3-5所示:


图3-5 open设备流程

2.4.2. 数据发送流程(write操作)

发送数据大体流程如图3-6所示:


图3-6 发送数据流程

2.4.3. 数据接收流程(read操作)

接收数据的大体流程如图3-7所示:


图3-7数据接收流程

2.4.4. 关闭设备(close操作)

close设备的大体流程如图3-8所示:


图3-8 close设备流程

2.4.5. 注销流程

此接口用于从uart driver上注销一个uart port,该接口在uart driver中的remove函数中调用。uart移除port的流程如图3-9所示:


图3.9 uart移除port流程图

此接口在uart driver中调用,用来从kernel中注销uart_driver,调用阶段在uart driver的退出阶段,例如:module_exit(),uart driver的注销流程如图3.10所示


2.5. 使用rs485通信

2.5.1. rs485和rs232的区别

uart(TTL-3.3V)/rs232(工业级 +-12V)是电压驱动,rs485是电流驱动(能传输更远的距离) rS232用电平表示数据,使用2根线可实现全双工,rs485用差分电平表示数据,因此必须用4根线实现全双工rs485;

全双工:uart-tx 1根线变成rs485-A/B 2根线;uart-rx 1根线变成rs485- X/Y两根线;

rs485半双工: 将全双工的A/B和X/Y合并起来分时复用;rs485-de/re是给转换器的一个控制信号,对我们芯片来说,都是输出;

2.5.2. rs485调试方法:

首先保证uart模块和相关gpio,电压转换芯片工作正常:

模式12-gpio-normal-uart-rs485-halfduplex (2个gpio独立控制de/re, enable就是将相关gpio设置到active电平;不用uart控制器的rs485模式;uart控制器处于normal模式)

模式21-gpio-normal-uart-rs485-halfduplex 这个模式的前提条件,外设器件的 de/re必须是相反极性的,比如de是高电平有效,re是低电平有效,则可以用一个gpio,来控制 de/re,此时de/re一定是互斥的。(1个gpio控制de/re, enable就是将相关gpio设置到active电平;不用uart控制器的rs485模式;uart控制器处于normal模式)

模式3rs485-software-halfduplex(de/re 独立输出) (使能uart控制器的rs485模式; 通过uart模块内部reg来控制 de/re 信号)

模式4rs485-hardware-halfduplex(de/re 独立输出) 基本配置同模式3,但是设置 rs485模式为 hardware-halfduplex模式

模式5:使用纯硬件的办法实现RS485半双工功能,电路如图所示:


接收:默认没有数据时,UART_TX为高电平,三极管导通,485芯片RE低电平使能,RO接收数据使能,此时从485AB口收到什么数据就会通过RO通道传到MCU,完成数据接收过程。发送:当发送数据时,UART_TX会有一个下拉的电平,表示开始发送数据,此时三极管截止,DE为高电平发送使能。当发送数据‘0’时,由于DI口连接地,此时数据‘0’就会传输到AB口 A-B<0,传输‘0’,完成了低电平的传输。当发送‘1’时,此时三极管导通,按理说RO使能,此时由于还处在发送数据中,这种状态下485处于高阻态,此时的状态通过A上拉B下拉电阻决定,此时A-B>0传输‘1’,完成高电平的传输。

3. 模块详细设计

3.1. 关键函数接口

3.1.1. uart_register_driver

  1.  
  2. int uart_register_driver(struct uart_driver *drv) 

3.1.2. uart_unregister_driver

  1.  
  2. void uart_unregister_driver(struct uart_driver *drv) 

3.1.3. uart_add_one_port

  1.  
  2. int uart_add_one_port(struct uart_driver *drv,struct uart_port *port) 

3.1.4. uart_remove_one_port

  1.  
  2. int uart_remove_one_port(struct uart_driver *drv,struct uart_port *port) 

3.1.5. uart_write_wakeup

  1.  
  2. void uart_write_wakeup(struct uart_port *port) 

3.1.6. uart_suspend_port

  1.  
  2. int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) 

3.1.7. uart_resume_port

  1.  
  2. int uart_resume_port(struct uart_driver *drv, struct uart_port *port) 

3.1.8. uart_get_baud_rate

  1.  
  2. unsigned int uart_get_baund_rate(struct uart_port *port, struct ktermios *termios, struct ktermios *old,unsigned int min, unsigned int max

3.1.9. uart_get_divisor

  1.  
  2. unsigned int uart_get_divisor(struct uart_port *port, unsigned int baund) 

3.1.10. uart_update_timeout

  1.  
  2. void uart_update_timeout(struct uart_port *port,unsigned int cflag, unsigned int baud) 

3.1.11. uart_insert_char

  1.  
  2. void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun,unsigned int ch, unsigned int flag) 

3.1.12. uart_console_write

  1.  
  2. Void uart_console_write(struct uart_port *port,const char *s, unsigned int count,viod(*putchar)(struct uart_port*, int)) 

4. 模块使用说明

4.1. 串口编程

4.1.1. 串口控制函数

4.1.2. 串口配置流程

(1) 保持原先串口配置,使用tegetatrr(fd, &oldtio);

  1. struct termious newtio, oldtio; 
  2. tegetattr(fd, &oldtio); 

(2) 激活选项有CLOCAL和CREAD,用于本地连接和接收使用

  1. newtio.cflag |= CLOCAL|CREAD; 

(3) 设置波特率

  1. newtio.c_cflag = B115200; 

(4) 设置数据位,需使用掩码设置

  1. newtio.c_cflag &= ~CSIZE; 
  2. Newtio.c_cflag |= CS8; 

(5) 设置停止位,通过激活c_cflag中的CSTOP实现。若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOP

  1. newtio.c_cflag &= ~CSTOPB;  
  2. Newtio.c_cflag |= CSTOPB;  

(6) 设置流控

  1. newtio.c_cfag |= CRTSCTS  
  2. newtio.c_cfag |= (IXON | IXOFF | IXANY);  

(7) 奇偶检验位设置,使用c_cflag和c_ifag. 设置奇校验

  1. newtio.c_cflag |= PARENB; 
  2. newtio.c_cflag |= PARODD; 
  3. newtio.c_iflag |= (INPCK | ISTRIP); 

设置偶校验

  1. newtio.c_iflag |= (INPCK | ISTRIP); 
  2. newtio.c_cflag |= PARENB; 
  3. newtio.c_cflag |= ~PARODD; 

(8) 设置最少字符和等待时间,对于接收字符和等待时间没有什么特别的要求,可设置为0:

  1. newtio.c_cc[VTIME] = 0; 
  2. newtio.c_cc[VMIN]  = 0; 

(9) 处理要写入的引用对象 tcflush函数刷清(抛弃)输入缓冲(终端程序已经接收到,但用户程序尚未读)或输出缓冲(用户程序已经写,但未发送)。

  1. int tcflash(int filedes, int quene) 
  2. quene数应当是下列三个常数之一: 
  3.   *TCIFLUSH 刷清输入队列 
  4.   *TCOFLUSH 刷清输出队列 
  5.   *TCIOFLUSH 刷清输入、输出队列 
  6. 例如: 
  7. tcflush(fd, TCIFLUSH); 

(10) 激活配置,在完成配置后,需要激活配置使其生效。使用tcsetattr()函数:

  1. int tcsetarr(int filedes, const struct termios *termptr); 
  2. opt 指定在什么时候新的终端属性才起作用, 
  3.    *TCSANOW:更改立即发生 
  4.    *TCSADRAIN:发送了所有输出后更改才发生。若更改输出参数则应使用此选项 
  5.    *TCSAFLUSH:发送了所有输出后更改才发生。更进一步,在更改发生时未读的 
  6.                  所有输入数据都被删除(刷清) 
  7. 例如:tcsetatrr(fd, TCSANOW, &newtio); 

4.1.3. 使用流程

(1)打开串口,例如"/dev/ttySLB0"

  1. fd = open("/dev/ttySLB0",O_RDWR | O_NOCTTY | O_NDELAY); 
  2. O_NOCTTY:是为了告诉Linux这个程序不会成为这个端口上的“控制终端”。如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程。 
  3. O_NDELAY:这个标志则是告诉Linux这个程序并不关心DCD信号线的状态,也就是不管串口是否有数据到来,都是非阻塞的,程序继续执行。 

(2)恢复串口状态为阻塞状态,用于等待串口数据的读入,用fcntl函数:

  1. fcntl(fd,F_SETFL,0);  //F_SETFL:设置文件flag为0,即默认,即阻塞状态 

(3)接着测试打开的文件描述符是否应用一个终端设备,以进一步确认串口是否正确打开。

  1. isatty(STDIN_FILENO); 

(4)读写串口

  1. 串口的读写与普通文件一样,使用read,write函数 
  2. read(fd, buf ,8); 
  3. write(fd,buff,8); 

4.1.4. Demo

以下给出一个测温模块收取数据的例子

  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5. #include  
  6. #include  
  7. #include  
  8. #include  
  9. #include  
  10.  
  11. #define UART_DEVICE     "/dev/ttySLB1" 
  12.  
  13. struct temp { 
  14.  float temp_max1; 
  15.  float temp_max2; 
  16.  float temp_max3; 
  17.  float temp_min; 
  18.  float temp_mean; 
  19.  float temp_enviromem; 
  20.  char temp_col[1536]; 
  21. }; 
  22.  
  23. int main(void) 
  24.  int count, i, fd; 
  25.  struct termios oldtio, newtio; 
  26.  struct temp *temp
  27.  temp = (struct temp *)malloc(sizeof(struct temp)); 
  28.  if (!temp) { 
  29.   printf("malloc failed\n"); 
  30.   return -1; 
  31.  } 
  32.  
  33.  char cmd_buf1[] = { 0xAA, 0x01, 0x04, 0x00, 0x06, 0x10, 0x05, 0x00, 0xBB}; 
  34.  char cmd_buf2[] = { 0xAA, 0x01, 0x04, 0x00, 0x00, 0xA0, 0x00, 0x03, 0xBB}; 
  35.  char cmd_buf3[] = { 0xAA, 0x01, 0x04, 0x00, 0x03, 0x10, 0x01, 0x00, 0xBB}; 
  36.  char read_buf[2000]; 
  37.  
  38.  //-----------打开uart设备文件------------------ 
  39.  fd = open(UART_DEVICE, O_RDWR | O_NOCTTY); 
  40.  if (fd < 0) { 
  41.   printf("Open %s failed\n", UART_DEVICE); 
  42.   return -1; 
  43.  } else { 
  44.   printf("Open %s successfully\n", UART_DEVICE); 
  45.  } 
  46.  
  47.  //-----------设置操作参数----------------------- 
  48.  tcgetattr(fd, &oldtio);//获取当前操作模式参数 
  49.  memset(&newtio, 0, sizeof(newtio)); 
  50.  
  51.  //波特率=230400 数据位=8 使能数据接收 
  52.  newtio.c_cflag = B230400 | CS8 | CLOCAL | CREAD | CSTOPB; 
  53.  newtio.c_iflag = IGNPAR; 
  54.  
  55.  tcflush(fd, TCIFLUSH);//清空输入缓冲区和输出缓冲区 
  56.  tcsetattr(fd, TCSANOW, &newtio);//设置新的操作参数 
  57.  
  58.  //printf("input: %s, len = %d\n", cmd_buf, strlen(cmd_buf)); 
  59.  //------------向urat发送数据------------------- 
  60.  
  61.  for (i = 0; i < 9; i++) 
  62.   printf("%#X ", cmd_buf1[i]); 
  63.  
  64.  count = write(fd, cmd_buf1, 9); 
  65.  if (count != 9) { 
  66.   printf("send failed\n"); 
  67.   return -1; 
  68.  } 
  69.  
  70.  usleep(500000); 
  71.  
  72.  memset(read_buf, 0, sizeof(read_buf)); 
  73.  count = read(fd, read_buf, sizeof(read_buf)); 
  74.  if (count > 0) { 
  75.   for (i = 0; i < count; i++); 
  76.   temp->temp_max1 = read_buf[7] << 8 | read_buf[6]; 
  77.   temp->temp_max2 = read_buf[9] << 8 | read_buf[8]; 
  78.   temp->temp_max3 = read_buf[11] << 8 | read_buf[10]; 
  79.   temp->temp_min  = read_buf[13] << 8 | read_buf[12]; 
  80.   temp->temp_mean = read_buf[15] << 8 | read_buf[14]; 
  81.  
  82.   printf("temp->temp_max1 = %f\n"temp->temp_max1 * 0.01); 
  83.   printf("temp->temp_max2 = %f\n"temp->temp_max2 * 0.01); 
  84.   printf("temp->temp_max3 = %f\n"temp->temp_max3 * 0.01); 
  85.   printf("temp->temp_min  = %f\n"temp->temp_min  * 0.01); 
  86.   printf("temp->temp_mean = %f\n"temp->temp_mean * 0.01); 
  87.    
  88.  
  89.  } else { 
  90.   printf("read temp failed\n"); 
  91.   return -1; 
  92.  } 
  93.  
  94.  count = write(fd, cmd_buf3, 9); 
  95.  if (count != 9) { 
  96.   printf("send failed\n"); 
  97.   return -1; 
  98.  } 
  99.  
  100.  usleep(365); 
  101.  memset(read_buf, 0, sizeof(read_buf)); 
  102.  count = read(fd, read_buf, sizeof(read_buf)); 
  103.  if (count > 0) { 
  104.   for (i = 0; i < count; i++); 
  105.   temp->temp_enviromem = read_buf[7] << 8 | read_buf[6]; 
  106.  
  107.   printf("temp->temp_enviromem = %f\n"temp->temp_enviromem * 0.01);  
  108.  } else { 
  109.   printf("read enviromem failed\n"); 
  110.   return -1; 
  111.  } 
  112.    
  113.  
  114.  count = write(fd, cmd_buf2, 9); 
  115.  if (count != 9) { 
  116.   printf("send failed\n"); 
  117.   return -1; 
  118.  } 
  119.  
  120.  usleep(70000); 
  121.  memset(read_buf, 0, sizeof(read_buf)); 
  122.  memset(temp->temp_col, 0, sizeof(temp->temp_col)); 
  123.  count = read(fd, read_buf, sizeof(read_buf)); 
  124.  printf("count = %d\n"count); 
  125.  if (count > 0) { 
  126.   for (i = 0; i < count - 7; i++) 
  127.   temp->temp_col[i] = read_buf[i+6]; 
  128.   for (i = 0; i < 1536; i++) 
  129.   { 
  130.    if (!(i%10)) 
  131.     printf("\n"); 
  132.    printf("%#X "temp->temp_col[i]); 
  133.   } 
  134.  } else { 
  135.   printf("read temp colour failed\n"); 
  136.   return -1; 
  137.  } 
  138.  free(temp);  
  139.  
  140.  close(fd); 
  141.  
  142.  tcsetattr(fd, TCSANOW, &oldtio); //恢复原先的设置 
  143.  
  144.  return 0; 

 

来源: 一口Linux内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯