|
STM32 串口详解 1. USART的特点 USART是通用异步收发传输器(UniversalAsynchronousReceiver/Transmitter),通常称作UART,是一种异步收发传输器,是设备间进行异步通信的关键模块。UART负责处理数据总线和串行口之间的串/并、并/串转换,并规定了帧格式;通信双方只要采用相同的帧格式和波特率,就能在未共享时钟信号的情况下,仅用两根信号线(Rx和Tx)就可以完成通信过程,因此也称为异步串行通信。
% `" E) U. g: u$ ?3 A
(1) 全双工异步通信。 (2) 小数波特率发生器系统,提供精确的波特率。 (3) 可配置的16倍过采样或8倍过采样,因而为速度容差与时钟容差的灵活配置提供了可能。 (4) 可编程的数据字长度(8位或者9位); (5) 可配置的停止位(支持1或者2位停止位); (6) 可配置的使用DMA多缓冲器通信。 (7) 单独的发送器和接收器使能位。 (8) 检测标志:①接受缓冲器 ②发送缓冲器空 ③传输结束标志 (9) 多个带标志的中断源。触发中断。 (10) 其他:校验控制,四个错误检测标志。 0 @ |' ]- z6 j/ |. a
通信结构
- @7 E/ _4 \6 i3 r6 J5 t. ]2. USART简介 2.1、数据传输模型 * l8 s% s @3 q% O
2.2、帧结构 串口异步通信需要定义的参数 ① 起始位 ② 数据位(8位或者9位) ③ 奇偶校验位(第9位) ④ 停止位(1,15,2位) ⑤ 波特率设置 带奇偶校验的数据为就是9位 9 Z8 I( P r3 r
(1) 数据包 串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备得RXD接口,在协议层中规定了数据包的内容,具体包括起始位、主体数据(8位或9位)、校验位以及停止位,通讯的双方必须将数据包的格式约定一致才能正常收发数据。
1 b8 S Z- q2 P( |- Y7 N, K
(2) 波特率 由于异步通信中没有时钟信号,所以接收双方要约定好波特率,即每秒传输的码元个数,以便对信号进行解码,常见的波特率有4800、9600、115200等。STM32中波特率的设置通过串口初始化结构体来实现。
# u, q7 A+ l' U
(3) 起始和停止信号 数据包的首尾分别是起始位和停止位,数据包的起始信号由一个逻辑0的数据位表示,停止位信号可由0.5、1、1.5、2个逻辑1的数据位表示,双方需约定一致。STM32中起始和停止信号的设置也是通过串口初始化结构体来实现。
$ q7 f7 j/ o2 F" N3 S8 o' \
(4) 有效数据 有效数据规定了主题数据的长度,一般为8或9位,其在STM32中也是通过串口初始化结构体来实现的。 1 I' o3 J( O. R% [
(5) 数据校验 在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无(noparity)。这些也都可以在串口初始化结构体中实现的。
- [. v6 _6 H+ j) ^3 L/ J
2.3、波特率
! C: o/ u( f( n/ JOVER8,用于配置过采样,通常情况下,OVER8设置为0。
) p% {1 N& A6 M: [" P如果时钟时84M USARTDIV = 84000000/(115200*16) = 45.572 那么得到: DIV_Fraction = 16*0.572 = 0x09; DIV_Mantissa = 45 = 0x2D;
/ \: w E$ j! r0 T: {( B. F
3. STM32的USART 根据STM32F207数据手册,STM32F207一共6个串口 2 P6 a1 f: `& F1 `
下文我们以USART1为例讲解 从STM32F207数据手册的Table10.Alternate functionmapping图中看到USART1的对应管脚,下文我们选择PA9和PA10作为USART1的管脚。
/ |; N- ~" X8 @* V q* c4. 代码配置 配置中断优先级。 - /* Enable the USARTx Interrupt */+ c' B! W. o$ `7 ?! P6 B
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
3 }# z4 i/ ]; u5 A, d$ _5 \, K - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1;
7 U- B0 D0 @6 _; R - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;1 q) X$ D. b, \+ g
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
( y" M, y- ^1 M$ f; G1 h - NVIC_Init(&NVIC_InitStructure);
复制代码
- Q3 L1 w1 ?) ?1 {打开串口与相应的GPIO引脚,配置好相应串口信息与GPIO引脚的工作模式。 - /* Enable GPIO clock */
3 d7 ], W* ~8 o8 ]1 x) B - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);/ @' B- \+ X- U4 [
- /* Enable UART1 clock */
$ m+ u2 J" V3 Z$ v - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);% F# o; T- _! x& E
- /* Connect PXx to USARTx_Tx*/
( I& C+ F, I+ o! ?' m$ Q - GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);# L( j& e2 H1 F; F) o
- p1 w4 {( W) J+ V4 ?' B
- /* Connect PXx to USARTx_Rx*/+ s) m* |6 x& b$ v
- GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);
7 Q8 f. Q+ C1 Y* \8 K - ) P) D1 \+ ^( b) o
- /* Configure USART Tx as alternate function */- t& ]. r B9 ?" G0 m V1 S
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;8 D/ b' E8 p' d% ?, y% J
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;/ ?, h& g$ X8 j" E* [+ o
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
- ]1 t( n2 t7 Y, b7 v6 e$ Y -
6 C/ u1 {4 S* W - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;, _: [6 W1 I$ g; p& @* C# ]3 w
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/ l0 |$ a% X4 T) ? - GPIO_Init(GPIOA, &GPIO_InitStructure);2 ]! y8 ~9 X! _8 `1 O1 m4 j
- ( X% m- l! X2 R- k f
- /* Configure USART Rx as alternate function */
% _ r4 S7 B; J* C - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;0 _! u8 a" G" j
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
1 u9 m/ m, ~+ w - GPIO_Init(GPIOA, &GPIO_InitStructure);
复制代码
+ M1 v3 r7 s9 Z; d9 ?# A配置USART1。 - USART_InitStructure.USART_BaudRate = 115200;//配置波特率& B4 y. n+ d0 q+ V* N
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//配置数据字长4 _( G4 r& i+ g
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//配置停止位
( u+ K- j* l6 u8 }" y% F. w# u8 V - USART_InitStructure.USART_Parity = USART_Parity_No;//配置校验位: A8 v% d* v2 k
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//配置硬件流控制
' p* M1 @# H4 A/ j5 h - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//配置工作模式,收发一起
/ e* m- s! m, d -
6 H( Q% N% q8 y# o- i7 I7 ~9 D - /* USART configuration */
' o( U8 {$ v9 L0 `0 e2 I - USART_Init(USART1, &USART_InitStructure);// 完成串口的初始化配置
复制代码
! [) D. L& q/ F6 B: } y& t使能中断配置。 - USART_ITConfig(USART1, USART_IT_TC, ENABLE);' `$ A5 R) u" e. s2 q
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)
复制代码 7 g8 b" H" a$ r! G& B. B2 d4 ], D
我们配置了发送传输完成中断和接收数据寄存器非空中断。我们可以配置很多类型中断,在ST提供的标准库函数中看到。 - /*** S% v T' s, [& s, u
- * @brief Enables or disables the specified USART interrupts.' o4 I( f, n) o- l5 x
- * @param USARTx: where x can be 1, 2, 3, 4, 5 or 6 to select the USART or
2 t: z9 e: D; r1 U - * UART peripheral.
* [9 R. }2 I+ p: X6 Q% l3 w4 m - * @param USART_IT: specifies the USART interrupt sources to be enabled or disabled.
4 D5 }" M# X: t# V( G - * This parameter can be one of the following values:
5 M( t) \; V j+ Z/ { - * @arg USART_IT_CTS: CTS change interrupt
* a# X1 W. H, t2 f - * @arg USART_IT_LBD: LIN Break detection interrupt; ]* @* U- X, I2 c
- * @arg USART_IT_TXE: Transmit Data Register empty interrupt
$ F2 r2 |* m# {1 _' \$ V - * @arg USART_IT_TC: Transmission complete interrupt
8 p. x) \$ L4 @4 a8 N - * @arg USART_IT_RXNE: Receive Data register not empty interrupt& g7 m( T8 B; ~6 @
- * @arg USART_IT_IDLE: Idle line detection interrupt
( y8 W0 E! H6 A# a - * @arg USART_IT_PE: Parity Error interrupt
* H& M' O( A+ Y+ t4 e - * @arg USART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
% D+ u) X8 W) c/ A - * @param NewState: new state of the specified USARTx interrupts.
3 S9 f# q( X. y$ X" A - * This parameter can be: ENABLE or DISABLE.% n/ s. O/ k& F! l3 \8 L( b9 O% m5 C
- * @retval None
+ \* j9 n- y8 ] - */
复制代码
$ g' e7 e" I% y' b3 y8 Q z最后使能串口。 - /* Enable USART */8 v9 x3 C& {% Z: H w' w- K
- USART_Cmd(USART1, ENABLE);
复制代码 1 m0 b N/ S3 B6 r7 Q' |1 t* a
main主函数,功能是LCD显示串口接收的10个字符(如果不是ascii码则不显示),串口倒序返回接收到的10个字节。 - int main(void)
! `' o# b: U a4 k2 V - {
* L& q0 G- R4 F' ~9 V1 ~ - while (1)# z( O, T' ] ?& r* I. d8 n
- {
5 l% K/ }4 E2 r( d- X - if(LCD_refresh_flg){
. e: @* L1 E/ z. K - LCD_refresh_flg = 0;
0 |4 m/ ^4 }4 D5 j$ k' q: p - LCD_ShowString(0,16,receive_data);5 c' i) Q& {9 i) N3 f4 `
- receive_num--;
1 @9 ]1 k t! K% ^' a2 n- B$ U1 O - USART_SendData(USART1, receive_data[receive_num--]);
& t9 s5 B& h6 A$ [/ q: O - send_flg = 1;
* v: Y( l. A. R" r, m( g1 o- t: C - }- ~% z/ ?! U; M: y* j4 i( }
- }% t* E! B9 J. [. C
- }
复制代码 ' K6 m. J- K% W
因为使能了中断,我们还需要编写中断函数。 - void USART1_IRQHandler(void)0 m6 y+ Q; G8 H: n2 V8 W x
- {
6 a0 i1 `; R4 K+ D& S: `% F' J - if(USART_GetFlagStatus(USART1, USART_FLAG_TC))
4 \6 L0 h1 v( V6 P+ `( `$ w - {
: w1 z, P; }$ T( a5 x) X7 h% ^( b - if(send_flg == 1){: h9 i/ a: K5 N: g6 \
- if(receive_num==0){
+ R5 U( q% h, }5 _9 t3 I- n ^ - USART_SendData(USART1, receive_data[receive_num]);
% J% N- c n/ l" z - send_flg = 0;2 e$ t1 n* a: K/ f# ?% _! @
- receive_flg = 1;3 ]) {7 p* J7 f0 X' ~* d5 \% p
- }else{3 r/ u. R9 Y7 @
- USART_SendData(USART1, receive_data[receive_num--]);
' M/ B8 k, |. Z- F' ]: Z- O: H - }1 C* X' h2 O, j+ s) r- f) r
- }
& r: z/ O+ z8 Z( C - USART_ClearFlag(USART1, USART_FLAG_TC);8 n, ]8 j8 Z- Y: T; A
- }& P- Z6 \% F' ?6 P: S8 K1 P+ L, G0 W
- if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
' c3 V( D3 L2 `5 Z E1 }) S - {! ]2 p1 p8 H1 Z$ q" f8 ?
- if((receive_flg)&&(send_flg == 0)){- u% I& o% w, Q1 s5 [' `
- receive_data[receive_num++] = USART_ReceiveData(USART1);
5 }- `! ]2 Y/ N; r - if(receive_num==10){7 E, ^+ N# W/ h7 Y2 I
- receive_flg = 0;$ E* m7 y) z" E6 x a* {1 a
- LCD_refresh_flg = 1;
0 h" `* E/ v% n( ]$ T1 w3 e - }6 r6 Y8 H! g8 h# C6 F- B
- }
% x: T% `; j& V& ~7 V' b - USART_ClearFlag(USART1, USART_FLAG_RXNE);: r* f9 V7 v$ I
- }4 {% v; L3 k, W
- }
复制代码
$ p) M- n; e3 | o; \( a) _% e3 q' G下载验证 LCD显示屏可以显示接收的10个字符,且PC以100ms为间隔发送数据,发送977包收到977包回复,测试demo就够健壮,没有出现丢包。 6 L" [7 Z w: H2 S" x* ?
" ^, b2 m- e6 h* [( O" B
文章出处: 知晓编程 " C1 ?3 I% I4 V5 J$ }' z
|