页面加载中,请稍候
来源:嵌入式Linux系统开发发布时间:2023-05-084536浏览
询问 AI本文由技术大拿:蒙工投稿!
桂电毕业的资深嵌入式专家。
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称为UART,是一种异步收发传输器,是电脑硬件的一部分。它将要传输的资料在串行通信与并行通信之间加以转换。
作为把并行输入信号转成串行输出信号的芯片,UART 通常被集成于其他通讯接口的连上。
UART 是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设备中,UART 用于主机与辅助设备通信,如汽车音与外接AP 之间的通信,与PC 机通信包括与监控调试器和其它器件,如EEPOM通信。
UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。其中各位的意义如下:
起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。
数据位:紧接着起始位之后。数据位的个数可以是5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。
奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。
由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
Uart传输数据如图2-1所示:
波特率是衡量资料传送速率的指标。表示每秒钟传送的符号数(symbol)。一个符号代表的信息量(比特数)与符号的阶数有关。例如传输使用256阶符号,每8bit代表一个符号,数据传送速率为120字符/秒,则波特率就是120 baud,比特率是120*8=960bit/s。这两者的概念很容易搞错。
UART 的接收和发送是按照相同的波特率进行收发的。波特率发生器产生的时钟频率不是波特率时钟频率,而是波特率时钟频率的16倍,目的是为在接收时进行精确的采样,以提取出异步的串行数据。根据给定的晶振时钟和要求的波特率,可以算出波特率分频计数值。
发送数据过程:空闲状态,线路处于高电位;当收到发送数据指令后,拉低线路一个数据位的时间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”表示停止位,一帧数据收发完成。
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)抗干扰性
数据在两个串口传输时,常常会出现丢失数据的现象,或者两台计算机的处理速度不同,如台式机与单片机之间的通讯,接收端数据缓冲区以满,此时继续发送的数据就会丢失,流控制能解决这个问题,当接收端数据处理不过来时,就发出“不再接收”的信号,发送端就停止发送,直到收到“可以继续发送”的信号再发送数据。
因此流控制可以控制数据传输的进程,防止数据丢失。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(输入信号)。
1.主机的发送过程:主机查询主机的CTS脚信号,此信号连接到从机的RTS信号,受从机控制。如果主机CTS信号有效(为低),表示从机的接收FIFO未满,从机可以接收,此时主机可以向从机发送数据,并且在发送过程中要一直查询CTS信号是否为有效状态。主机查询到CTS无效时,则中止发送。主机的CTS信号什么时候会无效呢?从机在接收到主机发送的数据时,从机的接收模块的FIFO如果满了,则会使从机RTS无效,也即主机的CTS信号无效。主机查询到CTS无效时,主机发送中止。
2.主机接收模式:如果主机接收FIFO未满,那么使主机RTS信号有效(为低),即从机的CTS信号有效。此时如果从机要发送,发送前会查询从机的CTS信号,如果有效,则开始发送。并且在发送过程中要一直查询从机CTS信号的有效状态,如果无效则终止发送。是否有效由主机的RTS信号决定。如果主机FIFO满了,则使主机的RTS信号无效,也即从机CTS信号无效,主机接收中止。
由于电缆的限制,在普通的控制通讯中一般不采用硬件流控制,而是使用软件流控制。
一般通过XON/XOFF来实现软件流控制。常用方法是:当接收端的输入缓冲区内数据量超过设定的高位时,就向数据发送端发送XOFF字符后就立即停止发送数据。
当接收端的输入缓冲区内数据量低于设定的低位时,就向数据发送端发送XON字符(十进制的17或Control-Q),发送端收到XON字符后就立即开始发送数据。
一般可从设备配套源程序中找到发送端收到XON字符后就立即发送数据。一般可以从设备配套源程序中找到发送的是什么字节。
应注意,若传输的是二进制的数据,标志字符也可能在数据流中出现而引起误操作,这是软件流控的缺陷,而硬件流控不会出现这样的问题。
在Linux系统中,终端是一种字符型设备,它有多种类型,通常使用tty(Teletype)来简称各种类型的终端设备。
对于嵌入式系统而言,最普遍采用的是Uart(Universal Asynchronous Receiver/Transmitter),串行端口,日常生活中简称端口
串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看做是一个字符设备。这些串行端口所对应的设备名称是/dev/ttySAC*;
在Linux系统中,计算机的输出设备通常被称为控制台终端,这里特指printk信息输出到设备。/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数“console=ttySCA0”就把console映射到了串口0
当用户登录时,使用的是虚拟终端。使用Ctcl+Alt[F1 - F6]组合键时,我们就可以切换到tty1、tty2、tty3等上面去。tty*就称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名。
整个 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设备发送、接收数据流程uart_driver 包含了串口设备名,串口驱动名,主次设备号,串口控制台(可选))等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)
structuart_driver{
structmodule*owner;/*拥有该uart_driver的模块,一般为THIS_MODULE*/
constchar*driver_name;/*驱动串口名,串口设备名以驱动名为基础*/
constchar*dev_name;/*串口设备名*/
intmajor;/*主设备号*/
intminor;/*次设备号*/
intnr;/*该uart_driver支持的串口数*/
structconsole*cons;/*其对应的console,若该uart_driver支持serialconsole,
*否则为NULL*/
/*
*theseareprivate;thelowleveldrivershouldnot
*touchthese;theyshouldbeinitialisedtoNULL
*/
structuart_state*state;/*下层,窗口驱动层*/
structtty_driver*tty_driver;/*tty相关*/
实现控制台打印功能必须要注册的结构体
structconsole{
charname[16];
void(*write)(structconsole*,constchar*,unsigined);
int(*read)(structconsole*,char*,unsigned);
structtty_driver*(structconsole*,int*);
void(*unblank)(void);
int(*setup)(structconsole*,char*);
int(*early_setup)(void);
shortflags;
shortindex;/*用来指定该console使用哪一个uartport(对应的uart_port中的line),如果为-1,kernel会自动选择第一个uartport*/
intcflag;
void*data;
structconsole*next;
};
每一个uart端口对应着一个uart_state,该结构体将uart_port与对应的circ_buf联系起来。uart_state有两个成员在底层串口驱动会用到:xmit和port。
用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。串口接收中断处理函数需要通过port将接收到的数据传递给线路规程层。
structuart_state{
structtty_portport;
enumuart_pm_statepm_state;
structcirc_bufxmit;
structuart_port*uart_port;/*对应于一个串口设备*/
};
uart_port用于描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实现对应一个串口设备。
structuart_port{
spinlock_tlock;/*portlock*/
unsignedlongiobase;/*in/out[bwl]*/
unsignedchar__iomem*membase;/*read/write[bwl]*/
unsignedint(*serial_in)(structuart_port*,int);
void(*serial_out)(structuart_port*,int,int);
void(*set_termios)(structuart_port*,
structktermios*new,
structktermios*old);
int(*handle_irq)(structuart_port*);
void(*pm)(structuart_port*,unsignedintstate,
unsignedintold);
void(*handle_break)(structuart_port*);
unsignedintirq;/*irqnumber*/
unsignedlongirqflags;/*irqflags*/
unsignedintuartclk;/*baseuartclock*/
unsignedintfifosize;/*txfifosize*/
unsignedcharx_char;/*xon/xoffchar*/
unsignedcharregshift;/*regoffsetshift*/
unsignedchariotype;/*ioaccessstyle*/
unsignedcharunused1;
#defineUPIO_PORT(0)
#defineUPIO_HUB6(1)
#defineUPIO_MEM(2)
#defineUPIO_MEM32(3)
#defineUPIO_AU(4)/*Au1x00andRT288xtypeIO*/
#defineUPIO_TSI(5)/*Tsi108/109typeIO*/
unsignedintread_status_mask;/*driverspecific*/
unsignedintignore_status_mask;/*driverspecific*/
structuart_state*state;/*pointertoparentstate*/
structuart_icounticount;/*statistics*/
structconsole*cons;/*structconsole,ifany*/
#ifdefined(CONFIG_SERIAL_CORE_CONSOLE)||defined(SUPPORT_SYSRQ)
unsignedlongsysrq;/*sysrqtimeout*/
#endif
upf_tflags;
#defineUPF_FOURPORT((__forceupf_t)(1<<1))
#defineUPF_SAK((__forceupf_t)(1<<2))
#defineUPF_SPD_MASK((__forceupf_t)(0x1030))
#defineUPF_SPD_HI((__forceupf_t)(0x0010))
#defineUPF_SPD_VHI((__forceupf_t)(0x0020))
#defineUPF_SPD_CUST((__forceupf_t)(0x0030))
#defineUPF_SPD_SHI((__forceupf_t)(0x1000))
#defineUPF_SPD_WARP((__forceupf_t)(0x1010))
#defineUPF_SKIP_TEST((__forceupf_t)(1<<6))
#defineUPF_AUTO_IRQ((__forceupf_t)(1<<7))
#defineUPF_HARDPPS_CD((__forceupf_t)(1<<11))
#defineUPF_LOW_LATENCY((__forceupf_t)(1<<13))
#defineUPF_BUGGY_UART((__forceupf_t)(1<<14))
#defineUPF_NO_TXEN_TEST((__forceupf_t)(1<<15))
#defineUPF_MAGIC_MULTIPLIER((__forceupf_t)(1<<16))
/*Porthashardware-assistedh/wflowcontrol(iow,auto-RTS*not*auto-CTS)*/
#defineUPF_HARD_FLOW((__forceupf_t)(1<<21))
/*Porthashardware-assisteds/wflowcontrol*/
#defineUPF_SOFT_FLOW((__forceupf_t)(1<<22))
#defineUPF_CONS_FLOW((__forceupf_t)(1<<23))
#defineUPF_SHARE_IRQ((__forceupf_t)(1<<24))
#defineUPF_EXAR_EFR((__forceupf_t)(1<<25))
#defineUPF_BUG_THRE((__forceupf_t)(1<<26))
/*TheexactUARTtypeisknownandshouldnotbeprobed.*/
#defineUPF_FIXED_TYPE((__forceupf_t)(1<<27))
#defineUPF_BOOT_AUTOCONF((__forceupf_t)(1<<28))
#defineUPF_FIXED_PORT((__forceupf_t)(1<<29))
#defineUPF_DEAD((__forceupf_t)(1<<30))
#defineUPF_IOREMAP((__forceupf_t)(1<<31))
#defineUPF_CHANGE_MASK((__forceupf_t)(0x17fff))
#defineUPF_USR_MASK((__forceupf_t)(UPF_SPD_MASK|UPF_LOW_LATENCY))
unsignedintmctrl;/*currentmodemctrlsettings*/
unsignedinttimeout;/*character-basedtimeout*/
unsignedinttype;/*porttype*/
conststructuart_ops*ops;
unsignedintcustom_divisor;
unsignedintline;/*portindex*/
resource_size_tmapbase;/*forioremap*/
structdevice*dev;/*parentdevice*/
unsignedcharhub6;/*thisshouldbeinthe8250driver*/
unsignedcharsuspended;
unsignedcharirq_wake;
unsignedcharunused[2];
void*private_data;/*genericplatformdatapointer*/
};
struct uart_ops涵盖了驱动可对串口的所有操作
structuart_ops{
unsignedint(*tx_empty)(structuart_port*);
void(*set_mctrl)(structuart_port*,unsignedintmctrl);
unsignedint(*get_mctrl)(structuart_port*);
void(*stop_tx)(structuart_port*);
void(*start_tx)(structuart_port*);
void(*throttle)(structuart_port*);
void(*unthrottle)(structuart_port*);
void(*send_xchar)(structuart_port*,charch);
void(*stop_rx)(structuart_port*);
void(*enable_ms)(structuart_port*);
void(*break_ctl)(structuart_port*,intctl);
int(*startup)(structuart_port*);
void(*shutdown)(structuart_port*);
void(*flush_buffer)(structuart_port*);
void(*set_termios)(structuart_port*,structktermios*new,
structktermios*old);
void(*set_ldisc)(structuart_port*,intnew);
void(*pm)(structuart_port*,unsignedintstate,
unsignedintoldstate);
int(*set_wake)(structuart_port*,unsignedintstate);
/*
*Returnastringdescribingthetypeoftheport
*/
constchar*(*type)(structuart_port*);
/*
*ReleaseIOandmemoryresourcesusedbytheport.
*Thisincludesiounmapifnecessary.
*/
void(*release_port)(structuart_port*);
/*
*RequestIOandmemoryresourcesusedbytheport.
*Thisincludesiomappingtheportifnecessary.
*/
int(*request_port)(structuart_port*);
void(*config_port)(structuart_port*,int);
int(*verify_port)(structuart_port*,structserial_struct*);
int(*ioctl)(structuart_port*,unsignedint,unsignedlong);
#ifdefCONFIG_CONSOLE_POLL
int(*poll_init)(structuart_port*);
void(*poll_put_char)(structuart_port*,unsignedchar);
int(*poll_get_char)(structuart_port*);
#endif
};
此接口在uart driver中调用,用来注册uart_driver到kernel中,调用阶段在uart driver的初始阶段,例如:module_init(), uart_driver的注册流程图
图3.3uart 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流程图open设备的大体流程如图3-5所示:
图3-5 open设备流程发送数据大体流程如图3-6所示:
图3-6 发送数据流程接收数据的大体流程如图3-7所示:
图3-7数据接收流程close设备的大体流程如图3-8所示:
图3-8 close设备流程此接口用于从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所示

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是给转换器的一个控制信号,对我们芯片来说,都是输出;
首先保证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’,完成高电平的传输。
/*功能: uart_register_driver用于串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。
*参数:drv:要注册的uart_driver
*返回值:成功,返回0;否则返回错误码
*/
intuart_register_driver(structuart_driver*drv)
/*功能:uart_unregister 用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数,
*参数:drv:要注销的uart_driver
*返回值:成功返回0,否则返回错误码
*/
voiduart_unregister_driver(structuart_driver*drv)
/*功能:uart_add_one_port用于为串口驱动添加一个串口端口,通常在探测到设备后(驱动的设备probe方法)调用该函数
*参数:
*drv:串口驱动
*port:要添加的串口端口
*返回值:成功,返回0;否则返回错误码
*/
intuart_add_one_port(structuart_driver*drv,structuart_port*port)
/*功能:uart_remove_one_port用于删除一个已经添加到串口驱动中的串口端口,通常在驱动卸载时调用该函数
*参数:
*drv:串口驱动
*port:要删除的串口端口
*返回值:成功,返回0;否则返回错误码
*/
intuart_remove_one_port(structuart_driver*drv,structuart_port*port)
/*功能:uart_write_wakeup唤醒上层因串口端口写数据而堵塞的进程,通常在串口发送中断处理函数中调用该函数
*参数:
*port:需要唤醒写堵塞进程的串口端口
*/
voiduart_write_wakeup(structuart_port*port)
/*功能:uart_suspend_port用于挂起特定的串口端口
*参数:
*drv:要挂起的串口端口锁所属的串口驱动
*port:要挂起的串口端口
*返回值:成功返回0;否则返回错误码
*/
intuart_suspend_port(structuart_driver*drv,structuart_port*port)
/*功能:uart_resume_port用于恢复某一已挂起的串口
*参数:
*drv:要恢复的串口端口所属的串口驱动
*port:要恢复的串口端口
*返回值:成功返回0;否则返回错误码
*/
intuart_resume_port(structuart_driver*drv,structuart_port*port)
/*功能:uart_get_baud_rate通过解码termios结构体来获取指定串口的波特率
*参数:
* port:要获取波特率的串口端口
*termios:当前期望的termios配置(包括串口波特率)
*old:以前的termios配置,可以为NULL
*min:可以接受的最小波特率
*max:可以接受的最大波特率
*返回值:串口波特率
*/
unsignedintuart_get_baund_rate(structuart_port*port,structktermios*termios,structktermios*old,unsignedintmin,unsignedintmax)
/*功能:uart_get_divisor 用于计算某一波特率的串口时钟分频数(串口波特率除数)
*参数:
* port:要计算分频数的串口端口
* baud:期望的波特率
*返回值:串口时钟分频数
*/
unsignedintuart_get_divisor(structuart_port*port,unsignedintbaund)
/*功能:uart_update_timeout用于更新(设置)串口FIFO超出时间
*参数:
* port:要更新超时间的串口端口
* cfalg:termios结构体的cflag值
* baud:串口的波特率
*/
voiduart_update_timeout(structuart_port*port,unsignedintcflag,unsignedintbaud)
/*功能:uart_insert_char用于向uart层插入一个字符
*参数:
* port:要写信息的串口端口
* status:RX buffer状态
* overrun:在status中的overrun bit掩码
* ch:需要插入的字符
* flag:插入字符的flag:TTY_BREAK,TTY_PSRIYY, TTY_FRAME
*/
voiduart_insert_char(structuart_port*port,unsignedintstatus,unsignedintoverrun,unsignedintch,unsignedintflag)
/*功能:uart_console_write用于向串口端口写一控制台信息属性说明tcgetatrr取属性(termios结构)tcsetarr设置属性(termios结构)cfgetispeed得到输入速度cfsetispeed得到输出速度cfstospeed设置输出速度tcdrain等待所有输出都被传输tcflow挂起传输或接收tcflush刷请未决输出和/或输入tcsendbreak送BREAK字符tcgetpgrp得到前台进程组IDTcsetpgrp设置前台进程组ID
*参数:
* port:要写信息的串口端口
*s:要写的信息
* count:信息的大小
* putchar:用于向串口端口写字符的函数,该函数有两个参数:串口端口和要写的字符
*/
Voiduart_console_write(structuart_port*port,constchar*s,unsignedintcount,viod(*putchar)(structuart_port*,int))
structtermiousnewtio,oldtio;
tegetattr(fd,oldtio);
newtio.cflag|=CLOCAL|CREAD;
newtio.c_cflag=B115200;
newtio.c_cflag=~CSIZE;
Newtio.c_cflag|=CS8;
newtio.c_cflag=~CSTOPB;/*停止位设置为1*/
Newtio.c_cflag|=CSTOPB;/*停止位设置为2*/
newtio.c_cfag|=CRTSCTS/*开启硬件流控*/
newtio.c_cfag|=(IXON|IXOFF|IXANY);/*开启软件流控*/
newtio.c_cflag|=PARENB;
newtio.c_cflag|=PARODD;
newtio.c_iflag|=(INPCK|ISTRIP);
设置偶校验
newtio.c_iflag|=(INPCK|ISTRIP);
newtio.c_cflag|=PARENB;
newtio.c_cflag|=~PARODD;
newtio.c_cc[VTIME]=0;
newtio.c_cc[VMIN]=0;
inttcflash(intfiledes,intquene)
quene数应当是下列三个常数之一:
*TCIFLUSH刷清输入队列
*TCOFLUSH刷清输出队列
*TCIOFLUSH刷清输入、输出队列
例如:
tcflush(fd,TCIFLUSH);
inttcsetarr(intfiledes,conststructtermios*termptr);
opt指定在什么时候新的终端属性才起作用,
*TCSANOW:更改立即发生
*TCSADRAIN:发送了所有输出后更改才发生。若更改输出参数则应使用此选项
*TCSAFLUSH:发送了所有输出后更改才发生。更进一步,在更改发生时未读的
所有输入数据都被删除(刷清)
例如:tcsetatrr(fd, TCSANOW, newtio);
fd=open("/dev/ttySLB0",O_RDWR|O_NOCTTY|O_NDELAY);
O_NOCTTY:是为了告诉Linux这个程序不会成为这个端口上的“控制终端”。如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程。
O_NDELAY:这个标志则是告诉Linux这个程序并不关心DCD信号线的状态,也就是不管串口是否有数据到来,都是非阻塞的,程序继续执行。
fcntl(fd,F_SETFL,0);//F_SETFL:设置文件flag为0,即默认,即阻塞状态
isatty(STDIN_FILENO);
串口的读写与普通文件一样,使用read,write函数
read(fd,buf,8);
write(fd,buff,8);
以下给出一个测温模块收取数据的例子
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<termios.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<log/log.h>
#include<stdlib.h>
#defineUART_DEVICE"/dev/ttySLB1"
structtemp{
floattemp_max1;
floattemp_max2;
floattemp_max3;
floattemp_min;
floattemp_mean;
floattemp_enviromem;
chartemp_col[1536];
};
intmain(void)
{
intcount,i,fd;
structtermiosoldtio,newtio;
structtemp*temp;
temp=(structtemp*)malloc(sizeof(structtemp));
if(!temp){
printf("mallocfailed\n");
return-1;
}
charcmd_buf1[]={0xAA,0x01,0x04,0x00,0x06,0x10,0x05,0x00,0xBB};
charcmd_buf2[]={0xAA,0x01,0x04,0x00,0x00,0xA0,0x00,0x03,0xBB};
charcmd_buf3[]={0xAA,0x01,0x04,0x00,0x03,0x10,0x01,0x00,0xBB};
charread_buf[2000];
//-----------打开uart设备文件------------------
fd=open(UART_DEVICE,O_RDWR|O_NOCTTY);
if(fd<0){
printf("Open%sfailed\n",UART_DEVICE);
return-1;
}else{
printf("Open%ssuccessfully\n",UART_DEVICE);
}
//-----------设置操作参数-----------------------
tcgetattr(fd,oldtio);//获取当前操作模式参数
memset(newtio,0,sizeof(newtio));
//波特率=230400数据位=8使能数据接收
newtio.c_cflag=B230400|CS8|CLOCAL|CREAD|CSTOPB;
newtio.c_iflag=IGNPAR;
tcflush(fd,TCIFLUSH);//清空输入缓冲区和输出缓冲区
tcsetattr(fd,TCSANOW,newtio);//设置新的操作参数
//printf("input:%s,len=%d\n",cmd_buf,strlen(cmd_buf));
//------------向urat发送数据-------------------
for(i=0;i<9;i++)
printf("%#X",cmd_buf1[i]);
count=write(fd,cmd_buf1,9);
if(count!=9){
printf("sendfailed\n");
return-1;
}
usleep(500000);
memset(read_buf,0,sizeof(read_buf));
count=read(fd,read_buf,sizeof(read_buf));
if(count>0){
for(i=0;i<count;i++);
temp->temp_max1=read_buf[7]<<8|read_buf[6];
temp->temp_max2=read_buf[9]<<8|read_buf[8];
temp->temp_max3=read_buf[11]<<8|read_buf[10];
temp->temp_min=read_buf[13]<<8|read_buf[12];
temp->temp_mean=read_buf[15]<<8|read_buf[14];
printf("temp->temp_max1=%f\n",temp->temp_max1*0.01);
printf("temp->temp_max2=%f\n",temp->temp_max2*0.01);
printf("temp->temp_max3=%f\n",temp->temp_max3*0.01);
printf("temp->temp_min=%f\n",temp->temp_min*0.01);
printf("temp->temp_mean=%f\n",temp->temp_mean*0.01);
}else{
printf("readtempfailed\n");
return-1;
}
count=write(fd,cmd_buf3,9);
if(count!=9){
printf("sendfailed\n");
return-1;
}
usleep(365);
memset(read_buf,0,sizeof(read_buf));
count=read(fd,read_buf,sizeof(read_buf));
if(count>0){
for(i=0;i<count;i++);
temp->temp_enviromem=read_buf[7]<<8|read_buf[6];
printf("temp->temp_enviromem=%f\n",temp->temp_enviromem*0.01);
}else{
printf("readenviromemfailed\n");
return-1;
}
count=write(fd,cmd_buf2,9);
if(count!=9){
printf("sendfailed\n");
return-1;
}
usleep(70000);
memset(read_buf,0,sizeof(read_buf));
memset(temp->temp_col,0,sizeof(temp->temp_col));
count=read(fd,read_buf,sizeof(read_buf));
printf("count=%d\n",count);
if(count>0){
for(i=0;i<count-7;i++)
temp->temp_col[i]=read_buf[i+6];
for(i=0;i<1536;i++)
{
if(!(i%10))
printf("\n");
printf("%#X",temp->temp_col[i]);
}
}else{
printf("readtempcolourfailed\n");
return-1;
}
free(temp);
close(fd);
tcsetattr(fd,TCSANOW,oldtio);//恢复原先的设置
return0;
}
新闻来源:嵌入式Linux系统开发,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!
2026-07-16

2026-07-09

2026-06-22