uart使用的过程为: - 1. 使能GPIO口和UART对应的总线时钟
- 2. 配置GPIO口的输出模式
- 3. 配置uart口相关的基本信息
- 4. 使能uart口的相关的中断,如接收中断、空闲中断等
- 5. 编写中断接收函数
- 7 |, {6 I1 ?( S3 {& r
配置对应的GPIO口对于STM32F4_Discovery开发板而言共有五个,选择UART5作为实验串口,其对应的IO口为PC12、PD2。 - UART5_TX: PC12
- UART5_RX: PD2. l3 U- P) Y+ m
首先需要将对应的GPIO口配置为复用功能,如下所示: - GPIO_InitTypeDef gpioInitStructure;6 U, F) N h; B1 G% Q
- //7 m' u- U s7 f& s, |# v; r
- // 使能对应的GPIO口时钟
5 n" ^8 ]/ c1 G - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
& x" n8 S; z" x6 C - //
# H# z2 r1 I% ?5 c5 D% R# _* x - // UART5 TX:PC12 RX:PD2
( \2 p; d- r: v- Y& q; B: I% Z - //
0 C3 V% m1 a6 C8 H N- W. ^ - GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);& }- s p2 H4 v
- GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);6 f7 x% J6 G: {& n$ d _$ d+ q; q
- //
( S* P2 l! l9 p8 b, s; t - // PC12 4 `9 ], @: }9 E0 {
- gpioInitStructure.GPIO_Pin = GPIO_Pin_12;- c* y1 y6 @+ F; l
- gpioInitStructure.GPIO_Mode = GPIO_Mode_AF;
) w5 x# U3 M+ z% U8 I- J - gpioInitStructure.GPIO_OType = GPIO_OType_PP;# k, C3 P+ y+ U f# N
- gpioInitStructure.GPIO_PuPd = GPIO_PuPd_UP;
8 {' h: o5 c% ~; I - gpioInitStructure.GPIO_Speed = GPIO_Speed_2MHz; $ \- t% U- @, G8 d. W
- GPIO_Init(GPIOC, &gpioInitStructure);
1 U) F1 X& u, G, G% _ w3 N - // PD2
: i I7 G$ }4 o$ c d3 U - gpioInitStructure.GPIO_Pin = GPIO_Pin_2;+ g. a/ Q2 y; p
- GPIO_Init(GPIOD, &gpioInitStructure);
复制代码
9 s9 K: G. n0 P5 w5 }对于GPIO口的配置,实际上是对GPIO各个寄存器的配置。GPIO_PinAFConfig()函数作用是配置GPIOx_AFR寄存器,每个口的复用功能选择由四个位来配置(具体参考STM32F4XX参考手册),因此16个GPIO空需要两个32位寄存器,在STM32F4处理器中,分别是GPIOx_AFRL、GPIOx_AFRH。而在库函数中,将寄存器的地址对应到相应的结构体上,与这两个寄存器放在一个AFR[2]数组中,如下所示。 - typedef struct
2 l+ Q2 T N# W3 g- c- d - {
) O: U/ W% q. V3 R1 C1 V, D - __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 *// }0 S2 C4 x0 x+ [* L$ L# q( d# c
- __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
) B9 N: q% w0 Z. G. W" t. z% Z3 `: q - __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
9 T' H; J: b: V$ |) y. E! O# L - __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
9 \4 |4 b- t$ a. A( V3 i - __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */5 { \1 Y' U5 l* V2 D6 @ ~
- __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */1 W, Y$ ^; d+ U4 b5 I, X% l# Y
- __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
0 \$ C2 G+ m4 Y9 M: T - __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
% }* ?9 l! {/ Z4 D& D0 F - __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
- T/ @8 n" z% a$ E6 n. |- d% L - __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */9 w$ {+ t# U; o5 [0 Y3 t
- } GPIO_TypeDef;
复制代码 ( E9 r7 V% X- Z0 k6 J
GPIO_Init()函数作用是配置GPIO口的输出输入功能,将GPIO_TypeDef结构体中的寄存器依次配置为相应的模式。 GPIO口配置完成之后,需要配置UART口。
/ W- n$ y5 L( b8 V& W) Z( Q% ?- \UART口配置配置UART口的同时,需要配置对应的接收中断,对应代码如下: - NVIC_InitTypeDef nvicInitStructure;, S. [- ]# t4 x- E+ Q7 ~
- USART_InitTypeDef uartInitStructure;( c9 i$ Q# P+ K5 s
- //使能uart5时钟 {5 X% m4 ^) {% k- f
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5 , ENABLE);0 R+ @1 J0 c1 k8 n9 {
- //% G" f- t! ]5 f7 j6 v
- // 配置UART5' X' c4 K2 C2 M3 f6 V! p4 }
- uartInitStructure.USART_BaudRate = 9600;
" x) G5 `! U$ Y* _$ }; ?1 p, t. ? - uartInitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
2 U$ C- ^6 v' K6 L: @- c - uartInitStructure.USART_WordLength = USART_WordLength_8b;# X1 D2 n! H( [2 v5 V4 k
- uartInitStructure.USART_StopBits = USART_StopBits_1;0 q, }, i7 X5 }6 a( N
- uartInitStructure.USART_Parity = USART_Parity_No;: c# C) B) H9 ?6 h' ^0 @% b# ]
- uartInitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;0 C J! g) o% b4 G. I
- USART_Init(UART5, &uartInitStructure);
% I, X+ E+ _- t4 i- E5 _9 S4 Z - //
5 t! C: R& {& b& M( t9 w - // 配置外设接受中断* a. P' b& e6 W! o
- nvicInitStructure.NVIC_IRQChannel = UART5_IRQn;
& x; c- i9 ?% J6 N) p - nvicInitStructure.NVIC_IRQChannelPreemptionPriority = 0;. f* }) Y4 A. C) ?# y
- nvicInitStructure.NVIC_IRQChannelSubPriority = 0;
+ A0 e' T- N2 L" |' e' J: I( @0 @ - nvicInitStructure.NVIC_IRQChannelCmd = ENABLE;
t, O1 Q C% }% u5 | - NVIC_Init(&nvicInitStructure); J, P2 x2 v6 I) L$ e. B
- //( C) n4 C6 V w! s9 O- X7 u
- USART_Cmd(UART5, ENABLE);
复制代码
" b, T0 x$ b' _: N7 q: V5 W配置UART5的过程主要由USART_Init()函数实现,该函数完成对UART寄存器的配置。在库函数中,UART寄存器如下所示: - typedef struct/ K! |% p& f% T( W6 [
- {
. a2 M9 a0 @7 g# {5 W - __IO uint16_t SR; /*!< USART Status register, Address offset: 0x00 */
$ i. [& A% I5 r5 P - uint16_t RESERVED0; /*!< Reserved, 0x02 */
4 N, Z9 g% H; G& `' o# _6 Q9 B - __IO uint16_t DR; /*!< USART Data register, Address offset: 0x04 */6 {$ X; U, \' K( P; S
- uint16_t RESERVED1; /*!< Reserved, 0x06 */4 h" g) i g" S: ~: X7 c* Z
- __IO uint16_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */
9 m+ O7 w; f: N% z- A - uint16_t RESERVED2; /*!< Reserved, 0x0A */) j) ~2 C* Y1 A/ v- A; ?7 I
- __IO uint16_t CR1; /*!< USART Control register 1, Address offset: 0x0C */+ g$ v9 e/ n/ D( S% r
- uint16_t RESERVED3; /*!< Reserved, 0x0E */
1 j0 s3 D8 a7 ?& [) L - __IO uint16_t CR2; /*!< USART Control register 2, Address offset: 0x10 */% _& w' A p. M$ [. D9 Y; }
- uint16_t RESERVED4; /*!< Reserved, 0x12 */
# a# N! ~, I8 P7 S& R7 z - __IO uint16_t CR3; /*!< USART Control register 3, Address offset: 0x14 */2 q0 a) ]8 U6 |) W1 D
- uint16_t RESERVED5; /*!< Reserved, 0x16 */
0 v9 }4 q ?4 W8 h - __IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
4 w/ p# ^8 T, m: |4 i - uint16_t RESERVED6; /*!< Reserved, 0x1A */
, Y. M: w4 k4 o- u. P* k( u/ d J' ]+ l1 H - } USART_TypeDef;
复制代码
" U6 y$ `9 |7 K$ ]6 ZUART寄存器详细内容可以参考STM32F4中文手册,这里简单介绍下以下几个寄存器: - SR寄存器:状态寄存器,包含了一些标志位,如TXE(发送数据寄存器为空)、TC(发送完成)、RXNE(读取数据寄存器不为空)
- DR寄存器: 数据寄存器,只用其低9位(DR[8:0])。当发送数据时,将数据写入该寄存器,该寄存器将数据发送到TDR或者移位寄存器发送,当数据写到移位寄存器时,TXE标志置1(数据写入时置0)。TXE为1时可以继续写入数据,否则新写入的数据会把原有数据覆盖。读取数据时,也是从该寄存器读取数据。
- BRR波特率寄存器:波特率寄存器,用来设置波特率的值。
- CR1寄存器:包含使能位UE、字长M、过采样倍率、奇偶校验、接收使能、发送使能等控制信息。/ K2 [" z: k6 L$ U/ J7 e, @! X
数据收发单字符发送
8 h" t2 p8 m# f' J8 x# [( `- //发送一个字符,USART_SendData函数实际就是将字符写入USART_DR寄存器
* h6 F0 j, S8 z; M( K - USART_SendData(pUSARTx,ch);
/ I+ d/ G b& s& z$ w - //
O/ W _1 i& l, M3 a! K - //等待发送寄存器为空,只有当USART_FLAG_TXE == 1 时才可以继续想DR寄存器写入数据,否则会将上一个数据覆盖掉。
' m( a: v- m/ ? - while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
复制代码 8 C! |) o# l' P; [
* i( c2 n! V1 H0 S; B, t
字符串发送 3 p8 O' q: f$ I/ q: P7 G: U; y
- /***************** 发送字符串 **********************/
/ z" E# r8 `0 P: [, W* s# z - // 发送字符串是每个字符依次发送,相当于循环执行单字符发送函数。3 N7 _5 `* c% W
- //, J. R' V1 A2 K4 H8 X
- void uart_send_str( USART_TypeDef * pUSARTx, char *str)
$ x& a6 I2 P+ h/ _9 J' Y9 k& h+ O - {) d' x% i" w8 v# k+ i3 G
- unsigned int k=0;
J2 U* x) p6 x" p$ [6 x - //
2 A5 x9 l1 U3 \: C& Z. H% p - do {
" ` Q; V4 d+ k - uart_send_byte( pUSARTx, *(str + k) );
h: a7 _5 e, l& p/ v4 O# I - k++;% \; R" K! Z: i/ Q. U0 a
- } while (*(str + k)!='\0');
5 P+ y5 z2 ]! [, ~# q - //
' X1 C( F, q- N6 X( ~- p - /* 等待发送完成 */. o3 f, k ?0 ^3 w3 M: k" H) C
- while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) ;
4 ?5 g; A5 k* W! t+ W - }4 t3 i/ j7 D' V( |
- //
5 \7 |; ?8 c: H N - // USART_FLAG_TC为1的条件是,DR寄存器为空并且以为寄存器也为空。相当于所有数据都发送完毕7 {' w- [( u" f) z9 S$ i( W0 N0 u
复制代码 字符接收$ n" U, h+ l3 c+ }& H( j
- uint8_t ucTemp; - y! O6 y* c0 R) R
- if (USART_GetITStatus(UART5, USART_IT_RXNE)!=RESET){/ D+ _! [2 U5 T9 S6 o( j
- ucTemp = USART_ReceiveData( UART5 );
7 @* o: J- b0 c: w8 ]5 t - // USART_SendData(UART5,ucTemp);) l: W' G6 \# Q1 M( u ~
- }3 X; `. Y; K8 M# K0 ?: j( A3 y x4 w
- // USART_ReceiveData()函数是将DR寄存器读取并返回
1 c% Z- Z% R& j+ i/ O4 U, E2 ]" Y - // USART_IT_RXNE不为0表示数据寄存器中有数据,需要将其读出。
) y* }" \* W6 h$ O: b) f+ W - // 每次收一个字符,USART_IT_RXNE会被设置为1,直至数据被读出,USART_IT_RXNE再次被设置为0.1 ?1 d6 [) `1 K+ [( d g1 K! N
- // 同时,USART_IT_RXNE也被设置为中断的标志,即接收到数据时,进入中断。
% e+ a$ ~ m- F, x8 S" x
复制代码 & n4 s% H/ V+ l
+ |% e* Y z# P( l. [$ n& i0 Z! g9 L; |
' W7 }2 w, @& f1 ~2 P* [/ k; [" P |