|
uart使用的过程为: - 1. 使能GPIO口和UART对应的总线时钟
- 2. 配置GPIO口的输出模式
- 3. 配置uart口相关的基本信息
- 4. 使能uart口的相关的中断,如接收中断、空闲中断等
- 5. 编写中断接收函数
- ( T+ ^! a( a- n
配置对应的GPIO口对于STM32F4_Discovery开发板而言共有五个,选择UART5作为实验串口,其对应的IO口为PC12、PD2。 - UART5_TX: PC12
- UART5_RX: PD26 |' z# _4 M7 H7 p* L
首先需要将对应的GPIO口配置为复用功能,如下所示: - GPIO_InitTypeDef gpioInitStructure;/ [4 E' j f! C7 U/ z. T6 t- h1 R
- //
' z [' t: J& ]0 Q7 [. k) y+ N - // 使能对应的GPIO口时钟) [- R C4 I1 m" A5 S
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1 ]" U- N: w- }- k; ~/ X, U - //
4 u9 V. A6 a$ V6 B4 j' m - // UART5 TX:PC12 RX:PD22 [ r7 y3 o; F
- //
) a, c, G4 b }: ^; [' [$ T2 K1 Q - GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
9 L# ?* m2 B! Y) z$ F% d& ]3 H - GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);
& W+ p" n4 r$ S' J( ^, Z' h - //. ~% o8 J- j; m; r5 Y% F
- // PC12 ! Z" e1 ^* Y/ X* |8 c3 `" P
- gpioInitStructure.GPIO_Pin = GPIO_Pin_12;
3 y6 m9 i3 ^+ l$ P, V4 f - gpioInitStructure.GPIO_Mode = GPIO_Mode_AF;* |7 `: O" u: B8 J
- gpioInitStructure.GPIO_OType = GPIO_OType_PP;
- ~& l% j. O: m- ?+ ? - gpioInitStructure.GPIO_PuPd = GPIO_PuPd_UP; t0 o" I; O2 c6 L+ Z8 k) E9 T
- gpioInitStructure.GPIO_Speed = GPIO_Speed_2MHz; 8 Q4 r! M4 e$ i' ]% ~$ ^0 f
- GPIO_Init(GPIOC, &gpioInitStructure); ( d- a+ F5 L& u9 X
- // PD2! M' @- ]% U! J
- gpioInitStructure.GPIO_Pin = GPIO_Pin_2;4 h* O3 r6 k- d; U: N
- GPIO_Init(GPIOD, &gpioInitStructure);
复制代码
# G/ o6 q5 ^1 |7 l% T+ b对于GPIO口的配置,实际上是对GPIO各个寄存器的配置。GPIO_PinAFConfig()函数作用是配置GPIOx_AFR寄存器,每个口的复用功能选择由四个位来配置(具体参考STM32F4XX参考手册),因此16个GPIO空需要两个32位寄存器,在STM32F4处理器中,分别是GPIOx_AFRL、GPIOx_AFRH。而在库函数中,将寄存器的地址对应到相应的结构体上,与这两个寄存器放在一个AFR[2]数组中,如下所示。 - typedef struct0 [& L) S* h8 [' l, i% h( M
- {
0 a" h2 c* u+ B# S - __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
1 S8 E2 d/ w, p" o0 z - __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */9 D2 `8 H2 ^- J. Y! P
- __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */% X! e0 b: F6 {' h* B
- __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
+ v/ b' u! A) g: `; M - __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */1 {1 `: u: ~9 x; d3 m( n+ B
- __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
j* O5 m5 c# |: O - __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */, w9 Z4 l9 h/ _; |
- __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */9 P. J% p$ b6 L) Q# Z
- __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */2 a) f _# F& I5 Z% T, l
- __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
/ `8 ]" z, }% H" [# U, c - } GPIO_TypeDef;
复制代码
6 w6 N! `. M1 eGPIO_Init()函数作用是配置GPIO口的输出输入功能,将GPIO_TypeDef结构体中的寄存器依次配置为相应的模式。 GPIO口配置完成之后,需要配置UART口。 0 f( P. F1 c8 N& L; U
UART口配置配置UART口的同时,需要配置对应的接收中断,对应代码如下: - NVIC_InitTypeDef nvicInitStructure;4 L* D; e9 \! p z
- USART_InitTypeDef uartInitStructure;
. C3 u" K& N% Q7 B/ [' x - //使能uart5时钟
1 ^; V+ |: u; Z" I& V- W - RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5 , ENABLE);3 ~8 E, d, F4 a& r# ?* \3 p2 H ^
- //
, o3 H0 R0 V" I9 W$ r" X - // 配置UART5
) h& W$ \& o4 W& E! P4 U' A - uartInitStructure.USART_BaudRate = 9600;
1 v/ k" q* T5 A2 G0 i. ~- T - uartInitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/ a6 U4 W9 r) p1 G3 u% \7 B, m: E - uartInitStructure.USART_WordLength = USART_WordLength_8b;* V3 s3 ~; s. ~
- uartInitStructure.USART_StopBits = USART_StopBits_1;
7 E1 O8 l' ]" `+ O2 m! Q( W - uartInitStructure.USART_Parity = USART_Parity_No;1 G$ w* g0 i; S x% s: Z! x v
- uartInitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;( ^, y5 b, U- Y& G1 ?
- USART_Init(UART5, &uartInitStructure);& y& A& }" V; P8 J/ Q$ \. F
- //3 N) U6 g( U9 U- S- P- c- x
- // 配置外设接受中断* r f* \, o# c2 q
- nvicInitStructure.NVIC_IRQChannel = UART5_IRQn; ; a1 ~/ c; N& ?" _# A
- nvicInitStructure.NVIC_IRQChannelPreemptionPriority = 0;
2 d) h- z5 u' x, y% Z - nvicInitStructure.NVIC_IRQChannelSubPriority = 0;
% z: c% H3 o$ Z - nvicInitStructure.NVIC_IRQChannelCmd = ENABLE;' G* H( \& L3 H; a/ A% X' i
- NVIC_Init(&nvicInitStructure);# k; b( ~, B0 r& X
- //1 ?9 \# o N( N7 H: X2 B" |) I8 [
- USART_Cmd(UART5, ENABLE);
复制代码
f- L! |+ f& M配置UART5的过程主要由USART_Init()函数实现,该函数完成对UART寄存器的配置。在库函数中,UART寄存器如下所示: - typedef struct- v1 m3 @8 K, g. y4 r2 V6 u) X5 t
- {
: q/ w' l3 P/ I! u R) e" x/ ^ - __IO uint16_t SR; /*!< USART Status register, Address offset: 0x00 */
8 m L. T1 l6 Z, f. {7 L - uint16_t RESERVED0; /*!< Reserved, 0x02 */
# K1 ^& f T, O3 k4 F2 q - __IO uint16_t DR; /*!< USART Data register, Address offset: 0x04 */
" _# u8 l. S' F" {. R% h - uint16_t RESERVED1; /*!< Reserved, 0x06 */# S# i+ d }( ~% w D8 G4 d5 C0 Z
- __IO uint16_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */
- _( \2 i, ^5 `2 q - uint16_t RESERVED2; /*!< Reserved, 0x0A */
' P3 M: H) ]$ w- P2 I. m - __IO uint16_t CR1; /*!< USART Control register 1, Address offset: 0x0C */- b! r5 k$ i1 B$ D( q8 z+ \
- uint16_t RESERVED3; /*!< Reserved, 0x0E */6 s/ H+ i; M" g( l- C
- __IO uint16_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
+ e0 o- A9 d& z) W+ y - uint16_t RESERVED4; /*!< Reserved, 0x12 */2 B& u( o% E) d. }
- __IO uint16_t CR3; /*!< USART Control register 3, Address offset: 0x14 */
8 s4 H) w/ ]6 D! D6 L6 e q - uint16_t RESERVED5; /*!< Reserved, 0x16 */: G* t7 R+ N2 J
- __IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */+ D; B" [* w: n5 c
- uint16_t RESERVED6; /*!< Reserved, 0x1A */
+ f* r1 Z* O& L - } USART_TypeDef;
复制代码 % b% ~1 \1 x4 e! t5 `1 f1 q/ s
UART寄存器详细内容可以参考STM32F4中文手册,这里简单介绍下以下几个寄存器: - SR寄存器:状态寄存器,包含了一些标志位,如TXE(发送数据寄存器为空)、TC(发送完成)、RXNE(读取数据寄存器不为空)
- DR寄存器: 数据寄存器,只用其低9位(DR[8:0])。当发送数据时,将数据写入该寄存器,该寄存器将数据发送到TDR或者移位寄存器发送,当数据写到移位寄存器时,TXE标志置1(数据写入时置0)。TXE为1时可以继续写入数据,否则新写入的数据会把原有数据覆盖。读取数据时,也是从该寄存器读取数据。
- BRR波特率寄存器:波特率寄存器,用来设置波特率的值。
- CR1寄存器:包含使能位UE、字长M、过采样倍率、奇偶校验、接收使能、发送使能等控制信息。: r: s( @& T* x) n* N
数据收发单字符发送
* m1 n4 p% T/ q" k5 p9 b) h- //发送一个字符,USART_SendData函数实际就是将字符写入USART_DR寄存器# a W; t: Q, Z+ \. n" m- M
- USART_SendData(pUSARTx,ch);
6 n/ a# [) Z( O, q# X. x! U# Z! R - //$ l1 I5 P: _7 E) `+ X
- //等待发送寄存器为空,只有当USART_FLAG_TXE == 1 时才可以继续想DR寄存器写入数据,否则会将上一个数据覆盖掉。
2 {2 P: m& J! h K T2 O4 b - while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
复制代码
6 E) H0 q$ J; k3 X& | d1 a
& y7 M6 q# d3 m2 W# z, y) b) u字符串发送 1 ~% \0 H' R2 F; n D6 L
- /***************** 发送字符串 **********************/
& \8 F4 c& u3 R: ] - // 发送字符串是每个字符依次发送,相当于循环执行单字符发送函数。
& |4 {6 l7 V6 F) B1 n. V8 S4 @; a9 i - //
2 O1 R$ E8 e" E/ Y* w& b - void uart_send_str( USART_TypeDef * pUSARTx, char *str)
$ t) C% g9 A3 j: J - {" y* f9 U3 Z- h
- unsigned int k=0;
- l$ G; r# U( o: L8 H7 _ - //, u9 N, ^; l8 a4 m, U! W
- do {" e/ _0 J& y& J2 x7 E; L% a \ C2 u% E
- uart_send_byte( pUSARTx, *(str + k) );
8 v* Z7 l2 c `, m2 K& R - k++;) S) `- ?* `' l4 q) b ?
- } while (*(str + k)!='\0');
7 Y9 _( x# S2 R+ ~; ? - //# r: M5 ~2 S" z/ A+ K# _
- /* 等待发送完成 */
( w. z0 s! P" b( X! s+ q! V - while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) ;; A/ P3 r4 z2 F2 T6 C5 \
- }
" h) s, F1 q: F$ x3 B5 C* R* \ O- y - //6 ^; h' p. z) \+ _+ |
- // USART_FLAG_TC为1的条件是,DR寄存器为空并且以为寄存器也为空。相当于所有数据都发送完毕
3 J: V' u* ~: ?+ ]1 a* E! g) Y
复制代码 字符接收
4 @; i! r& n9 X, M; n" j- uint8_t ucTemp;
r7 t# X4 m# d) W6 n - if (USART_GetITStatus(UART5, USART_IT_RXNE)!=RESET){6 {# u3 h1 N6 f+ h9 I1 I
- ucTemp = USART_ReceiveData( UART5 );1 A* D# h, L; f6 E/ [0 }
- // USART_SendData(UART5,ucTemp);
- r; A, i# I$ j& R: M - }8 e1 }6 H: _# [( ]0 `3 Q
- // USART_ReceiveData()函数是将DR寄存器读取并返回* F2 }- }% f* u1 Z }
- // USART_IT_RXNE不为0表示数据寄存器中有数据,需要将其读出。1 u9 z; O* A5 t1 h; c
- // 每次收一个字符,USART_IT_RXNE会被设置为1,直至数据被读出,USART_IT_RXNE再次被设置为0.8 ~8 ~: v0 R2 `7 v7 y2 M
- // 同时,USART_IT_RXNE也被设置为中断的标志,即接收到数据时,进入中断。) i9 R6 ^* |( c( L9 ]
复制代码
5 X: J# r) e) R! G6 ^% u9 F3 c2 x# P' J! F" ?2 a
6 w1 @! R' k! r
|