[导读] 单片机开发串口是应用最为广泛的通信接口,也是最为简单的通信接口之一,但是其中的一些要点你是否明了呢?来看看本人对串口的一些总结,当然这个总结并不能面面俱到,只是将个人认为具有共性以及相对比较重要的点做了些梳理。 啥是串口?首先这玩意儿分两种: 通用异步收发器(UART)是用于异步串行通信的一种物理层标准,其中数据格式和传输速度是可配置的。 通用同步收发器(USART)是一种串行接口设备,可以对其进行编程以进行异步或同步通信。 * O/ z9 x2 ]+ V" l数据格式 线上空闲、无数据状态为常高电平,故逻辑低定义为起始位。 起始位:总是 1 位 数据位:常见的有 8 位或 9 位。 校验位 % A; R; H; {- I5 Q奇校验 / X( F1 W' O# d# E- y" Q0 N偶校验 & m5 i, G* ^+ C$ Y" B9 W无校验 停止位: 9 I/ T9 l+ h1 o4 [3 M1 |& Q1 位 2 位 , y2 X3 b# p& ^4 X9 w0 z9 t1 l1 g/ \波特率:bit rate 就是位/秒的概念,就是 1 秒传送多少位的概念。常见的波特率有哪些呢? 这里须注意的要点: 一个有效字节的传输时间怎么算? 比如 9600 下,1 位起始位,8 位数据位,奇校验,1 位停止位,则 7 a9 {& G9 F j( c8 h为什么要理解清楚这个概念呢,因为在应用中需要计算数据吞吐率问题,就比如一个应用是数据采集串口传输问题,需要计算采集的位速率需要小于或等于传输波特率,否则数据就来不及传。当然如果说你有足够大的缓冲区可以临时存储,但是如果进来太快,而传出速度跟不上,多大的缓冲都会满! 校验位有用吗?当你的传输介质处于一个有干扰的场景下,校验位就可以从物理层检测出错误。 8 E7 S! m% F) H; w理解数据编码方式有啥意义呢?比如在调试中你可以利用逻辑分析直接去解析收发线上的数据报文。 应用电路设计的时候 RX-TX 相连,很多初学者容易在这里踩坑! 常见的传输位序为低有效位在前。 对于波特率而言需要注意波特率发生器有可能带来误码问题 啥是 UART? 2 i4 w0 L! q f7 f0 E两边分别代表两个通信的设备,单从 UART 编程的角度讲收发不需要物理同步握手,想发就发。图中箭头代表数据信息流向。RX 表示接收数据,TX 表示发送数据。数据总是从发送端传递到接收端,这就是为啥 RX 连接 TX,TX 连 RX 的原因。 , J \! J d* Z) ^0 g啥是 USART? $ Z: |+ u: O% F# W* B- ?同步简单说,收发不可自如,不可以想发就发,收发需要利用硬件 IO 口进行握手,RTS/CTS 就是用于同步的握手信号: p. p! M5 U. h6 M/ GRTS:Ready to send,请求发送,用于在当前传输结束时阻止数据发送。 CTS:clear to send,清除发送,用于指示 USART 已准备好接收数据。 这个对于普通应用而言并不常见,这里不做详细展开,需要用到的时候只需要对应收发时控制握手信号即可。 8 N' p6 V9 w" {: f9 Y, u编程策略对于不同的单片机,其硬件体系各异,寄存器也差异很大,但是从收发编程策略角度而言,常见有下面三种方式: - A3 d" [( V4 M* T7 Z6 Y( B& Q9 R查询发送/中断接收模式 & ^$ Q1 [ |$ N, X0 p8 h( {收发中断模式 DMA 模式 1 C& j% a1 H, x0 O. k查询发送/中断接收模式这里以伪代码方式描述一下: - D- n6 `( m+ L' z/*查询发送字节*/ void uart_send_byte( uint8 ch ) 2 B( A$ u% C5 p0 Z1 B/ P{ ' K/ [! u" \$ y# y) ]7 G/*如果当前串口状态寄存器非空闲,则一直等待*/ ]3 c G& I; S/ e5 K/*注意while循环后的分号,表示循环体为空操作*/ . n9 ]& I9 n) `$ A6 [4 c! p- pwhile( !UART_IS_IDLE() ); /*此时将发送字节写入发送寄存器*/ UART_TX_REG = ch; . ~$ A5 D8 ]7 H2 d8 K/ a} /*发送一个缓冲区*/ 7 P. i4 {7 w' C: m; w0 Yvoid uart_send_buffer( uint8 *pBuf,uint8 size ) { uint8 i = 0; $ [' {) h! }( i* ^5 F3 d) Y/* 异常参数处理*/ if( pBuf == NULL ) ! E+ s1 r% Q4 U" Hreturn; for( i=0; i《size;i++ ) { send_byte( pBuf[i] ); ) K+ i4 j% y7 c- M} - l. f$ [% `7 W( U4 O6 ?1 o} " i3 N) }& q* n- S* a5 V9 J对于接收而言,如采用查询模式则几乎是没有任何应用价值,因为外部数据不知道什么时候会到来,所以查询接受就不描述了,这里描述一下中断接收。 2 L, i( p1 o+ h z; s! g; q5 W: mstatic uint8 rx_index = 0; void uart_rx_isr( void ) { 3 X% b0 E8 N& c) m+ f9 v7 \2 p/* 接收报文处理 */ rx_buffer[rx_index++] = UART_RX_REG; 5 i- p3 R$ l9 v6 p4 K} 5 n1 N2 s/ N( c' Y. J- n# N中断接收需要考虑的几个要点: 断帧:这就取决于协议怎么制定了,比如应用协议定义的是 ASCII 码方式,就可以定义同步头、同步尾,比如 AT 指令的解析,做逻辑判断帧头、帧尾即可。但是如果传输的是 16 进制数据,比如 MODBUS-RTU 其断帧采用的是 3.5 个字节时间没有新的字节接收到,则认为收到完整的帧了。 0 C; J+ q8 H$ ^2 T: s如何保证帧的完整性,一般会在报文尾部加校验,比较常用的校验模式有 CRC 校验算法。 $ \: x1 q9 U. \: l不同的单片机开发环境对于中断向量的处理方式略有不同,需要根据各自芯片的特点进行处理。比如 51 单片机,其发送/接收都共享一个中断向量号。 收发中断模式#define FRAME_SIZE (128u) ' A* o9 F8 [( B( N! GstaTIc uint8 tx_buffer[FRAME_SIZE]; staTIc uint8 tx_index = 0; : [ h6 R$ C" ]1 sstaTIc uint8 tx_length = 0; staTIc uint8 rx_buffer[FRAME_SIZE]; . _8 g2 |8 |: A0 |2 ustatic uint8 rx_index = 0; $ T# j, ^5 v; v, Q( I/ @static bool rx_frame_done = false; void prepare_frame( uint8 * pBuf, uint8 size ) 9 k% N0 c( W8 U& G5 g{ /*将待传的报文按照协议封装*/ /*可能需要处理的事情,比如帧头、帧尾、校验等*/ - W4 c0 l, h `- x, H1 {} # N# m5 J3 U. X, \0 s5 b l3 N) Hbool uart_start_sending( uint8 * pBuf, uint8 size ) { if( pBuf == NULL ) return false; 8 @( u: E) a) qmemcpy( tx_buffer,pBuf,size ); tx_index = 0; $ h& G6 o6 e2 t I8 T3 utx_length = size; / ?4 r; R1 L3 d, i0 A) o, F9 P$ ]/ J/*使能发送中断,向发送寄存器写入一个字节,进入连续发送模式*/ ENABLE_TX_INT = 1; 9 I! c' W; J; `7 U8 TUART_TX_REG = tx_buffer[tx_index++]; } void uart_tx_isr( void ) { + G% J( }2 b- w2 d5 p: ^& [: f. Fif( tx_index《tx_length ) { UART_TX_REG = tx_buffer[tx_index++]; - L% E3 d9 }& H; W8 f/ x7 g' X% x} else { + |$ p0 D; M, D* z/*发送完毕,关闭发送中断*/ DISABLE_TX_INT = 1; 5 I. D3 ]$ y' f/ S b+ [: w+ _} + O" v$ c, Z9 M. |# S7 t} - M% }" X$ h: }- F* mvoid uart_rx_isr( void ) { ( G4 ~1 S2 s$ Y9 O: L9 Z) Y/*处理接收,待接收到完整的帧就设置帧完成标记*/ $ L- F: f- x M! `5 u, ]! W/*由于应用各有不同,这里就无法描述实现了*/ 3 B: b6 N9 I: c% {2 q" p} 还需要考虑的是,对于 UART 硬件层面的出错处置,以 STM32 为例,就可能有下面的错误可能发生: % z, s3 }% c) k/ \* o) \溢出错误 噪声检测 帧错误 ; Y1 d; _5 @% u" A奇偶校验错误 另外不同的单片机其底层硬件实现差异也不较大,比如有的硬件发送缓冲是单字节的缓冲,有的则具有 FIFO,这些在选型编程时都需要综合考虑。 7 c% ^' z$ i) Z9 J5 ] IDMA 模式DMA 发送模式而言,大致分这样几步: 初始化 UART 为 DMA 发送模式,开启 DMA 结束中断,并写好 DMA 传输结束中断处理函数 准备待发送报文,帧头、帧尾、校验处理 , |- `8 m& _+ |7 I将待发送报文缓冲区首地址赋值给 DMA 源地址,DMA 目标地址设置为 UART 发送寄存器,设置好发送长度。 ! Q. g/ p# H5 _) F9 h启动 DMA 传输,剩下传输完成就会进入传输结束中断处理函数。 DMA 接收模式而言,大致分这样几步: 初始化 UART 为 DMA 接收模式,开启 DMA 结束中断,并写好 DMA 传输结束中断处理函数 中断处理函数中标记接收到帧,对于使用 RTOS 而言,还可以使用的机制是利用 RTOS 的事件机制、消息机制进行通知有新的帧接收到了。 : z: B4 a/ h" }对于 DMA 接收模式而言,对于变长帧的处理较为不利,所以如果想使用 DMA 接收,制定协议时尽量考虑将帧长度固定,这样处理会方便些。 总结一下单片机串口是一个需要好好掌握的内容,这里总结了一些个人经验,尽量将一些个人共性的东西总结出来。至于实际实现而言,由于芯片体系差异较多,具体代码各异。但个人认为处置的思路方法却是基本一致。所以本文除了描述串口本身的细节而言,想表达的一个额外的观点是: % ]! w$ y6 I T" r对于一些技术点尽量学会将其共性的东西剥离总结出来。 G4 ]! Q. m8 L9 p- w总结、概括、剥离抽象是一个比较好的学习思路,不用对具体的硬件死记,万变不离其宗。 |
【银杏科技ARM+FPGA双核心应用】STM32H7系列10——ADC
【银杏科技ARM+FPGA双核心应用】STM32H7系列57——MDK_FLM
【STM32图书分享之九】—《STM32F 32位ARM微控制器应用设计与实践》
无刷直流电机控制应用+基于STM8S系列单片机---电子书
STM32 USB的程序,包含固件、驱动和测试用的应用程序
工业以太网总线ETHERCAT驱动程序设计及应用(扫描版)
【实战经验】STM32 DFSDM测量温度应用
【实战经验】基于STM32 I2S的音频应用开发介绍
【中文文档】AN4112_使用STM32F05xx模拟比较器的六个应用案例
[连载]STM32F103ZET6 uCGUI3.90a控件应用教程(第五节 复选框和单选按钮)