uart使用的过程为: - 1. 使能GPIO口和UART对应的总线时钟
- 2. 配置GPIO口的输出模式
- 3. 配置uart口相关的基本信息
- 4. 使能uart口的相关的中断,如接收中断、空闲中断等
- 5. 编写中断接收函数
9 P' w) Y! n! U3 V6 n% Q 配置对应的GPIO口对于STM32F4_Discovery开发板而言共有五个,选择UART5作为实验串口,其对应的IO口为PC12、PD2。 - UART5_TX: PC12
- UART5_RX: PD2
0 Q! Q# |# k* \% `( i3 E
首先需要将对应的GPIO口配置为复用功能,如下所示: - GPIO_InitTypeDef gpioInitStructure;
- e) Z, Z$ t$ n - //3 P( J% y+ I2 M
- // 使能对应的GPIO口时钟% r1 C) `- _4 T3 W" h+ Z
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
0 d. m x. r5 b# w - //) H" h0 C& T* y" F( g$ z& K" s; W
- // UART5 TX:PC12 RX:PD2 P! u) `* |3 U" o0 V$ @
- //
- a+ ]4 @* G8 z8 L1 x. J& [, m9 d - GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
# U2 j( N% m D+ D$ } - GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);
+ D* J# L1 I$ B* r( W - //9 n0 h; r# b) l
- // PC12
/ X0 f4 C% U8 l8 @ - gpioInitStructure.GPIO_Pin = GPIO_Pin_12;7 g& W) n( D+ t8 q- x9 ?
- gpioInitStructure.GPIO_Mode = GPIO_Mode_AF;
, N6 ^8 n5 n. T, | n. O8 G - gpioInitStructure.GPIO_OType = GPIO_OType_PP;
|+ w! I! n& X0 s, r s: J - gpioInitStructure.GPIO_PuPd = GPIO_PuPd_UP;
[4 q, `6 t8 b, Q- i, w) D2 u& f - gpioInitStructure.GPIO_Speed = GPIO_Speed_2MHz; 4 e% A& O+ R9 w$ J" [" L
- GPIO_Init(GPIOC, &gpioInitStructure);
% z, R: e6 e5 w* I9 y# V- G - // PD2
+ R' J4 v; f( W1 n5 D5 E) U) C - gpioInitStructure.GPIO_Pin = GPIO_Pin_2;- ~4 ^# t3 |9 u- A! r& G
- GPIO_Init(GPIOD, &gpioInitStructure);
复制代码
: r( m" X: D6 m2 Y' O对于GPIO口的配置,实际上是对GPIO各个寄存器的配置。GPIO_PinAFConfig()函数作用是配置GPIOx_AFR寄存器,每个口的复用功能选择由四个位来配置(具体参考STM32F4XX参考手册),因此16个GPIO空需要两个32位寄存器,在STM32F4处理器中,分别是GPIOx_AFRL、GPIOx_AFRH。而在库函数中,将寄存器的地址对应到相应的结构体上,与这两个寄存器放在一个AFR[2]数组中,如下所示。 - typedef struct) X/ w. m4 G5 u8 u% S
- {: M$ c" A( ~* Z/ z0 g9 r7 e7 h7 C
- __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
2 r$ ]0 c* P U3 o8 B - __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
/ |4 V3 v5 j) `9 T& p# O - __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
E5 Y6 Q3 \: a# x& G. j) c - __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
8 o- u% I* l: |& p4 r - __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */9 H+ W; G) p; V: Y* e. K
- __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */7 U% |% H& g8 K8 _2 \& ^- F
- __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
, m! }3 d' T9 ` - __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
6 g6 }- v+ Q9 I8 w8 [ - __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */$ Q" h6 e2 `$ I% D) Y/ W
- __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */3 \# i! K1 [# y4 `& k9 t' F* n9 C
- } GPIO_TypeDef;
复制代码
U8 A8 Q$ M: p5 O2 z* M& B% xGPIO_Init()函数作用是配置GPIO口的输出输入功能,将GPIO_TypeDef结构体中的寄存器依次配置为相应的模式。 GPIO口配置完成之后,需要配置UART口。
/ ~+ f1 U& c) k: Y% b! ^" HUART口配置配置UART口的同时,需要配置对应的接收中断,对应代码如下: - NVIC_InitTypeDef nvicInitStructure;7 l. m; ^8 z. k$ ?
- USART_InitTypeDef uartInitStructure;! O% _6 t z% y$ t; r8 T1 J
- //使能uart5时钟( l7 C2 c: C4 G0 B4 T; @. ]% {8 ?
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5 , ENABLE);
8 r" B ]: S, F# `" {+ F0 | - // J# }+ G' B4 @" J; _" J9 G
- // 配置UART50 E9 W, y E; X% s+ O' n
- uartInitStructure.USART_BaudRate = 9600;
& g6 o/ x3 g4 g) j( c! W# T% O - uartInitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; y0 F t' l1 C( t! Q& s1 R
- uartInitStructure.USART_WordLength = USART_WordLength_8b;7 Z, P7 G/ o$ ] p/ c
- uartInitStructure.USART_StopBits = USART_StopBits_1;5 E: X4 ]: ^: z6 a
- uartInitStructure.USART_Parity = USART_Parity_No;
( w; {: k( ]" m( E/ {. }+ D6 _& p - uartInitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
6 [ u, Q3 m3 X3 ` - USART_Init(UART5, &uartInitStructure);
, m: f" @2 s! u* f6 m1 C+ Y - //
9 p1 o0 e3 `% h+ u* m - // 配置外设接受中断1 S0 Z. z0 F- `' S6 _# ?) X1 ~
- nvicInitStructure.NVIC_IRQChannel = UART5_IRQn;
* a6 P: }. v; o) b - nvicInitStructure.NVIC_IRQChannelPreemptionPriority = 0;! n4 V4 o+ X8 @" {
- nvicInitStructure.NVIC_IRQChannelSubPriority = 0;
/ v% j. M% v1 U$ h. ~ K, G" ` - nvicInitStructure.NVIC_IRQChannelCmd = ENABLE;! |$ V4 g, R% ?. I. G
- NVIC_Init(&nvicInitStructure);
7 Y; ^* K8 Q0 y: n( J( R2 P+ l - //
$ [4 ]; `. \4 q! l" L - USART_Cmd(UART5, ENABLE);
复制代码 5 J( P; n8 U" y7 l7 Y0 W
配置UART5的过程主要由USART_Init()函数实现,该函数完成对UART寄存器的配置。在库函数中,UART寄存器如下所示: - typedef struct: l+ r% V7 l/ @! K: c0 o
- {
$ L! o' ?2 `. s& _% H- a - __IO uint16_t SR; /*!< USART Status register, Address offset: 0x00 */
8 N X' v: F. a$ d! ?9 x/ h - uint16_t RESERVED0; /*!< Reserved, 0x02 */! _- U7 v( A" d1 B a
- __IO uint16_t DR; /*!< USART Data register, Address offset: 0x04 */& q$ U9 X& R. M9 M, k2 q
- uint16_t RESERVED1; /*!< Reserved, 0x06 */ u3 v+ x/ E; w: t* t" H, ]4 c
- __IO uint16_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */$ S7 }- Q5 d7 Q- {
- uint16_t RESERVED2; /*!< Reserved, 0x0A */4 u- G4 G( K3 x& I* ] M. W% Y
- __IO uint16_t CR1; /*!< USART Control register 1, Address offset: 0x0C */
) A* U* {4 K2 F+ n. t. e7 O - uint16_t RESERVED3; /*!< Reserved, 0x0E */
/ ?, @( e# S: @/ }3 r0 {1 I6 u4 N - __IO uint16_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
2 K" Y; u' F# L; B - uint16_t RESERVED4; /*!< Reserved, 0x12 */' O T5 V% f/ c
- __IO uint16_t CR3; /*!< USART Control register 3, Address offset: 0x14 */
4 E- C1 T+ E3 D9 u5 e - uint16_t RESERVED5; /*!< Reserved, 0x16 */
) h4 w6 @* k4 C4 U - __IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */3 W; t: o" [/ L3 Z W8 D5 _
- uint16_t RESERVED6; /*!< Reserved, 0x1A */
' b" {) u; \! G: L& R& ? - } USART_TypeDef;
复制代码 8 N8 `' N$ M) _% `, V! V) S
UART寄存器详细内容可以参考STM32F4中文手册,这里简单介绍下以下几个寄存器: - SR寄存器:状态寄存器,包含了一些标志位,如TXE(发送数据寄存器为空)、TC(发送完成)、RXNE(读取数据寄存器不为空)
- DR寄存器: 数据寄存器,只用其低9位(DR[8:0])。当发送数据时,将数据写入该寄存器,该寄存器将数据发送到TDR或者移位寄存器发送,当数据写到移位寄存器时,TXE标志置1(数据写入时置0)。TXE为1时可以继续写入数据,否则新写入的数据会把原有数据覆盖。读取数据时,也是从该寄存器读取数据。
- BRR波特率寄存器:波特率寄存器,用来设置波特率的值。
- CR1寄存器:包含使能位UE、字长M、过采样倍率、奇偶校验、接收使能、发送使能等控制信息。# R" s" s1 ?6 ]2 L# r( T$ U3 ~! N
数据收发单字符发送 3 o1 G0 @4 `' y {7 S( {
- //发送一个字符,USART_SendData函数实际就是将字符写入USART_DR寄存器
$ R# i3 W3 a- ^; \; p, y: @$ @ - USART_SendData(pUSARTx,ch);
9 c8 q0 w8 F+ _& m. m - //" @) |5 E- f7 Q: U. O0 E
- //等待发送寄存器为空,只有当USART_FLAG_TXE == 1 时才可以继续想DR寄存器写入数据,否则会将上一个数据覆盖掉。
8 F3 }" S2 @2 X3 r - while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
复制代码
% j; V6 \0 B/ n6 j5 C/ s- q( l0 U8 ? R8 ^3 `8 \
字符串发送 % W% z# {* x( P! L" ^
- /***************** 发送字符串 **********************/
f2 M* p& {) T4 M. `% A - // 发送字符串是每个字符依次发送,相当于循环执行单字符发送函数。- Z6 h+ \4 V( L- {; W; ~( b! ?
- //8 v' V. ?6 e' Y. }6 [( ]) u- N
- void uart_send_str( USART_TypeDef * pUSARTx, char *str)) X, c& S2 n/ w3 e ~( R
- {9 L7 W( E! ?8 f0 ]% L. j R" o8 }' X! d
- unsigned int k=0;4 ]0 D8 z) Y1 E
- //+ P& ?4 X8 Q1 i1 Y- @+ x& t8 A
- do {3 H5 {4 @! W5 Q; {0 b
- uart_send_byte( pUSARTx, *(str + k) );% T8 m% h* P7 P5 V3 A: v6 K# D- w
- k++;. X& s/ [; N" a
- } while (*(str + k)!='\0');
) R9 U" T) i& l( M1 \ - //
r! k# g# x: o5 F a7 N - /* 等待发送完成 */
: p! l8 ^* Z5 @, y0 f - while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) ;
9 B% g. o5 _2 ?/ p, n( M - }. k9 G' |3 t. j+ M9 b) F. u
- //$ P0 \* [/ Q) p
- // USART_FLAG_TC为1的条件是,DR寄存器为空并且以为寄存器也为空。相当于所有数据都发送完毕8 T W* o, }* ~
复制代码 字符接收# R8 \7 l5 |% |' L' C( E1 P+ f
- uint8_t ucTemp;
0 x$ @" e2 ~7 p7 y - if (USART_GetITStatus(UART5, USART_IT_RXNE)!=RESET){
1 f' `% q. w: Y# s! ~5 k! {& C - ucTemp = USART_ReceiveData( UART5 );
. m4 t0 Q% B9 N" I4 _( J - // USART_SendData(UART5,ucTemp);" T+ {1 v7 @7 o5 s. O
- }( X6 L4 O, q, U% v: m) x8 Z1 [* O
- // USART_ReceiveData()函数是将DR寄存器读取并返回
+ r' }, Y( R) d$ K8 s2 W - // USART_IT_RXNE不为0表示数据寄存器中有数据,需要将其读出。
1 W( Q5 d# y4 V4 j( _ - // 每次收一个字符,USART_IT_RXNE会被设置为1,直至数据被读出,USART_IT_RXNE再次被设置为0.
( r' a4 D5 h+ v% L( Z - // 同时,USART_IT_RXNE也被设置为中断的标志,即接收到数据时,进入中断。
- o1 {/ u- _5 R* T
复制代码
. j7 a4 w# K4 A$ z- D" o6 R5 L3 N0 h5 I1 n9 ]1 ~
/ c, s3 M- ]! p |