|
[导读] 单片机开发串口是应用最为广泛的通信接口,也是最为简单的通信接口之一,但是其中的一些要点你是否明了呢?来看看本人对串口的一些总结,当然这个总结并不能面面俱到,只是将个人认为具有共性以及相对比较重要的点做了些梳理。. d6 b; a0 z9 g4 ^ 首先这玩意儿分两种:
线上空闲、无数据状态为常高电平,故逻辑低定义为起始位。
这里须注意的要点:
两边分别代表两个通信的设备,单从 UART 编程的角度讲收发不需要物理同步握手,想发就发。图中箭头代表数据信息流向。RX 表示接收数据,TX 表示发送数据。数据总是从发送端传递到接收端,这就是为啥 RX 连接 TX,TX 连 RX 的原因。 啥是 USART?
同步简单说,收发不可自如,不可以想发就发,收发需要利用硬件 IO 口进行握手,RTS/CTS 就是用于同步的握手信号:
这个对于普通应用而言并不常见,这里不做详细展开,需要用到的时候只需要对应收发时控制握手信号即可。 编程策略对于不同的单片机,其硬件体系各异,寄存器也差异很大,但是从收发编程策略角度而言,常见有下面三种方式:
这里以伪代码方式描述一下: /*查询发送字节*/) j8 Z$ H J3 l# xvoid uart_send_byte( uint8 ch ) { /*如果当前串口状态寄存器非空闲,则一直等待*/" j( t. B* n( A" `5 i /*注意while循环后的分号,表示循环体为空操作*/' o- I% w% p2 i9 K3 m# ? while( !UART_IS_IDLE() ); ! E! z6 D, Q6 V9 u /*此时将发送字节写入发送寄存器*/ UART_TX_REG = ch;9 A8 W0 {! a7 j } ; @4 S# A/ N" N4 E/ s /*发送一个缓冲区*/$ ~% F3 {. K8 S3 F* x void uart_send_buffer( uint8 *pBuf,uint8 size )! e8 Z. {0 e5 M* c5 G { uint8 i = 0;6 M. O" J4 d+ ]8 W /* 异常参数处理*/ if( pBuf == NULL ), a# J- x" E2 c! H6 |9 N return;1 I B7 y7 Z$ E' F 9 d3 m1 h) j# V ] for( i=0; i<size;i++ )0 v# J; [: p) q6 }& R R0 Y* J5 A6 @ {" y# K" ]( z% l5 @$ l send_byte( pBuf );0 S! q: l3 @$ [6 d1 W2 }1 @ }6 X Q, w! Q) J' v) ?$ Q. ^ }% k/ w u' o4 q# ?/ X4 a) J 对于接收而言,如采用查询模式则几乎是没有任何应用价值,因为外部数据不知道什么时候会到来,所以查询接受就不描述了,这里描述一下中断接收。 static uint8 rx_index = 0;void uart_rx_isr( void )% P T/ j2 t, r! r { /* 接收报文处理 */ rx_buffer[rx_index++] = UART_RX_REG;1 p0 Y" B0 y8 x1 S ]* A8 ^. i } 中断接收需要考虑的几个要点:
static uint8 tx_buffer[FRAME_SIZE]; static uint8 tx_index = 0; static uint8 tx_length = 0; static uint8 rx_buffer[FRAME_SIZE]; static uint8 rx_index = 0; static bool rx_frame_done = false;, K- t J8 p; z! r& |" ]$ u- t' M7 h void prepare_frame( uint8 * pBuf, uint8 size ) {2 b0 O4 g: X1 g: q# M" x% J# {6 X /*将待传的报文按照协议封装*/ /*可能需要处理的事情,比如帧头、帧尾、校验等*/ } bool uart_start_sending( uint8 * pBuf, uint8 size ) W& W* K6 W0 y! @" k1 L {! ^* M; R2 ]! l$ _# } o if( pBuf == NULL ) return false;+ w- {+ L( V' w5 g . F( |: |' d( f8 l- K+ C; w- p memcpy( tx_buffer,pBuf,size );' N/ z2 m1 i7 \ tx_index = 0;; B2 w( m8 @- M; r, M' ?* G6 v! R tx_length = size;! x& k3 M1 B$ K# [: u+ A $ G4 j/ Q+ |' F/ D /*使能发送中断,向发送寄存器写入一个字节,进入连续发送模式*/ ENABLE_TX_INT = 1; UART_TX_REG = tx_buffer[tx_index++]; }% }- Z$ R% b1 J6 C" Z$ `7 j% ` / E8 i, R( P" P void uart_tx_isr( void ) { M U% {; [4 Q* r* ` if( tx_index<tx_length )* `/ d) ~6 V# Q& G% d/ ? {2 h/ [1 `' }" y: \ UART_TX_REG = tx_buffer[tx_index++]; } else { /*发送完毕,关闭发送中断*/ DISABLE_TX_INT = 1; } } & @/ _9 p+ `& _' ]' [* g2 c+ R' O void uart_rx_isr( void ) _* E- \9 Y p& O+ l { /*处理接收,待接收到完整的帧就设置帧完成标记*/ /*由于应用各有不同,这里就无法描述实现了*/ } 还需要考虑的是,对于 UART 硬件层面的出错处置,以 STM32 为例,就可能有下面的错误可能发生:
另外不同的单片机其底层硬件实现差异也不较大,比如有的硬件发送缓冲是单字节的缓冲,有的则具有 FIFO,这些在选型编程时都需要综合考虑。 DMA 模式DMA 发送模式而言,大致分这样几步:
DMA 接收模式而言,大致分这样几步:
单片机串口是一个需要好好掌握的内容,这里总结了一些个人经验,尽量将一些个人共性的东西总结出来。至于实际实现而言,由于芯片体系差异较多,具体代码各异。但个人认为处置的思路方法却是基本一致。所以本文除了描述串口本身的细节而言,想表达的一个额外的观点是:
|
微信公众号
手机版