uart使用的过程为: - 1. 使能GPIO口和UART对应的总线时钟
- 2. 配置GPIO口的输出模式
- 3. 配置uart口相关的基本信息
- 4. 使能uart口的相关的中断,如接收中断、空闲中断等
- 5. 编写中断接收函数
# g$ y, O" t4 B' N4 B5 S 配置对应的GPIO口对于STM32F4_Discovery开发板而言共有五个,选择UART5作为实验串口,其对应的IO口为PC12、PD2。 - UART5_TX: PC12
- UART5_RX: PD2. n% v' Q' D5 p& |- _
首先需要将对应的GPIO口配置为复用功能,如下所示: - GPIO_InitTypeDef gpioInitStructure;
, O8 J' a9 {2 ~4 j - //
' [1 Q' V6 e' I1 L, G6 J# n4 A - // 使能对应的GPIO口时钟
7 B9 q+ C2 P7 z1 M - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
3 k* R I3 d- H" p* p. I - //4 \2 \% @3 P9 L3 L
- // UART5 TX:PC12 RX:PD2
+ X+ P' o4 w0 X2 }* X) p2 { - //4 M& D% V2 x# @4 J' z
- GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
5 s$ w2 e) d8 d* ~ - GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);3 j: o3 x: Z; T, _# U8 k3 V) K
- //
! B$ l; _3 \- F. W) X - // PC12
0 U9 w6 C( A4 {! D - gpioInitStructure.GPIO_Pin = GPIO_Pin_12;2 H) m, |/ G: z8 {: Y; R5 ~2 T8 C
- gpioInitStructure.GPIO_Mode = GPIO_Mode_AF;
( C0 o: R5 p$ w - gpioInitStructure.GPIO_OType = GPIO_OType_PP;' ^- Q- h" A0 W) F
- gpioInitStructure.GPIO_PuPd = GPIO_PuPd_UP;: ]" j& Z; ]! R3 |: M+ M1 R6 `6 W& j
- gpioInitStructure.GPIO_Speed = GPIO_Speed_2MHz; 9 o3 @4 C+ s( n; g- ]
- GPIO_Init(GPIOC, &gpioInitStructure);
7 m7 U2 s/ J8 W; [! L - // PD29 f, Q# w, W, h+ A6 o9 X$ h/ p
- gpioInitStructure.GPIO_Pin = GPIO_Pin_2;
, F1 M, t/ `3 S6 ?# l( I1 g - GPIO_Init(GPIOD, &gpioInitStructure);
复制代码 6 ] ^- g/ y+ {
对于GPIO口的配置,实际上是对GPIO各个寄存器的配置。GPIO_PinAFConfig()函数作用是配置GPIOx_AFR寄存器,每个口的复用功能选择由四个位来配置(具体参考STM32F4XX参考手册),因此16个GPIO空需要两个32位寄存器,在STM32F4处理器中,分别是GPIOx_AFRL、GPIOx_AFRH。而在库函数中,将寄存器的地址对应到相应的结构体上,与这两个寄存器放在一个AFR[2]数组中,如下所示。 - typedef struct
" y$ E4 J4 k3 L% p7 g6 { - {9 c7 }, _2 Q: y; F5 ]/ ?, M
- __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */, H, R2 ]6 m) { B
- __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */+ N8 Q" C/ [0 P7 R- Z
- __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */! O( f1 O: F9 l6 R0 @; @
- __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
; i* G1 ~1 j# I* j! y9 G - __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */+ {( A- u1 @4 t1 _7 T
- __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */% a& E5 x( @, l/ U* T6 X
- __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
2 R) g" M( i' K* B' ?& n5 f - __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */& | K9 [$ }7 o, j
- __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */# _ ?& _7 u9 K8 }2 T. `
- __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */+ J( Z2 p! O1 I5 R- l& Z z$ O
- } GPIO_TypeDef;
复制代码 % s5 J2 Y( `) w' H
GPIO_Init()函数作用是配置GPIO口的输出输入功能,将GPIO_TypeDef结构体中的寄存器依次配置为相应的模式。 GPIO口配置完成之后,需要配置UART口。 # S! Z3 x" O, h8 i
UART口配置配置UART口的同时,需要配置对应的接收中断,对应代码如下: - NVIC_InitTypeDef nvicInitStructure;
$ D+ h8 ?3 V/ k" ~ - USART_InitTypeDef uartInitStructure;
Z6 n! [9 R; n+ r/ q; J' M |9 N - //使能uart5时钟
3 l; {( ]* n8 e - RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5 , ENABLE);1 ~: N& W5 o, y$ e/ [
- //
5 f# w3 E6 e8 A; @. e% W. i - // 配置UART5
/ i8 ?, W& D; P. t4 C - uartInitStructure.USART_BaudRate = 9600;
; S9 s9 q5 |/ i. ` - uartInitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;% |% h3 C: ? k! N3 ~, U
- uartInitStructure.USART_WordLength = USART_WordLength_8b;- W& E3 g, B8 L4 \, M# Y& H! i
- uartInitStructure.USART_StopBits = USART_StopBits_1;8 a8 R/ X6 V% ~1 J4 w9 a' k$ ?
- uartInitStructure.USART_Parity = USART_Parity_No;8 B3 _5 z; @; g1 S& G: B
- uartInitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
8 R' y0 s: T. ?! z - USART_Init(UART5, &uartInitStructure);3 P3 q1 M8 Z8 @7 g2 I
- //
3 e& k% V( I1 h1 z" ]) ` - // 配置外设接受中断
0 c" D) o% M- b$ @5 E0 w( {1 _ - nvicInitStructure.NVIC_IRQChannel = UART5_IRQn; ' y- ]/ A/ T u! Z% {
- nvicInitStructure.NVIC_IRQChannelPreemptionPriority = 0;8 J9 J* ~! b! Q2 n: {! n D! O
- nvicInitStructure.NVIC_IRQChannelSubPriority = 0;
0 i3 h3 r, U' [3 U- R$ J - nvicInitStructure.NVIC_IRQChannelCmd = ENABLE;
& T& h1 s1 m7 `8 F0 T J" P0 c- U - NVIC_Init(&nvicInitStructure);
1 f: y6 z3 y( ?' O" c. S6 Q$ { T - //& X3 y& t1 A' h( ]+ ]+ C* }0 t
- USART_Cmd(UART5, ENABLE);
复制代码
- w1 [. y; Z' A" e4 H) N9 ^3 z配置UART5的过程主要由USART_Init()函数实现,该函数完成对UART寄存器的配置。在库函数中,UART寄存器如下所示: - typedef struct
& J: m$ O6 f0 m) ~ - {
7 |; g+ s: e( G0 o - __IO uint16_t SR; /*!< USART Status register, Address offset: 0x00 */
* p4 g& u6 \* Z: e, |5 Y2 B) G - uint16_t RESERVED0; /*!< Reserved, 0x02 */ }) y0 P: K* u/ i
- __IO uint16_t DR; /*!< USART Data register, Address offset: 0x04 */% w& E: k9 a* x( {/ j! A3 x
- uint16_t RESERVED1; /*!< Reserved, 0x06 */
/ k6 E. E- V1 s m. d" S - __IO uint16_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */3 l2 Y- U% K7 G
- uint16_t RESERVED2; /*!< Reserved, 0x0A */' ?9 P P! M7 c
- __IO uint16_t CR1; /*!< USART Control register 1, Address offset: 0x0C */8 B+ M% r& }+ L" g* J
- uint16_t RESERVED3; /*!< Reserved, 0x0E */$ g2 f' Y% o2 J! H$ J% ~
- __IO uint16_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
2 ]4 M# y8 O8 p \% J - uint16_t RESERVED4; /*!< Reserved, 0x12 */
2 y! W4 J. Y7 Y4 o) v, v - __IO uint16_t CR3; /*!< USART Control register 3, Address offset: 0x14 */% R- ]. n4 h, f5 \% g* n
- uint16_t RESERVED5; /*!< Reserved, 0x16 */% ^1 A* F0 ?( c0 R
- __IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
* H0 k5 ?; z' x% c' S# v8 O - uint16_t RESERVED6; /*!< Reserved, 0x1A */
& I$ j2 _# b h, V - } USART_TypeDef;
复制代码
8 u# o$ h, m# C, GUART寄存器详细内容可以参考STM32F4中文手册,这里简单介绍下以下几个寄存器: - SR寄存器:状态寄存器,包含了一些标志位,如TXE(发送数据寄存器为空)、TC(发送完成)、RXNE(读取数据寄存器不为空)
- DR寄存器: 数据寄存器,只用其低9位(DR[8:0])。当发送数据时,将数据写入该寄存器,该寄存器将数据发送到TDR或者移位寄存器发送,当数据写到移位寄存器时,TXE标志置1(数据写入时置0)。TXE为1时可以继续写入数据,否则新写入的数据会把原有数据覆盖。读取数据时,也是从该寄存器读取数据。
- BRR波特率寄存器:波特率寄存器,用来设置波特率的值。
- CR1寄存器:包含使能位UE、字长M、过采样倍率、奇偶校验、接收使能、发送使能等控制信息。
4 W$ ]% p! s9 y: f 数据收发单字符发送 & ]2 v5 q N# {6 c% F
- //发送一个字符,USART_SendData函数实际就是将字符写入USART_DR寄存器
$ U0 p! m, ^$ H& U - USART_SendData(pUSARTx,ch);5 m- i! V/ m0 u0 g2 x& b( I
- //
/ D( H$ D* u2 p% b4 l - //等待发送寄存器为空,只有当USART_FLAG_TXE == 1 时才可以继续想DR寄存器写入数据,否则会将上一个数据覆盖掉。) u# T8 T+ m! {# {3 `
- while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
复制代码 " u% f c: t" z4 M: E$ B* i( L
3 M6 L, l* |, X" Y. O, |字符串发送 2 S; i+ `$ U* S$ e5 B! t7 I, c" q6 s
- /***************** 发送字符串 **********************/' w$ M0 M/ `. i# W4 H
- // 发送字符串是每个字符依次发送,相当于循环执行单字符发送函数。
4 j' v& h# A) A - //
& d4 ?" [" P3 N! @ - void uart_send_str( USART_TypeDef * pUSARTx, char *str)
6 }. U4 B$ q/ H2 Q. @# b7 ~: b - {1 g' s) q5 x1 \8 N
- unsigned int k=0;
9 o# ^/ e: _$ n& w7 _- D/ H - //
* u/ `. W1 v2 _0 z! a - do {
3 v5 O6 B7 I8 v3 @1 q+ B+ B - uart_send_byte( pUSARTx, *(str + k) );
; R4 R- n' V/ g _ - k++;
4 e: X& I! h* p5 e9 b# ?: B, ^2 `. p - } while (*(str + k)!='\0');- ?6 N' {! G& I, C# s: V% N; C: F
- // R4 E- Q' a; ~
- /* 等待发送完成 */
/ z- x$ I7 [% `: E1 n8 x, | - while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) ;
1 F [$ O* v, D% M; V9 c - }
( _" K" ^& F3 Q. z - //3 ~; h' G! l# z, t6 I7 }
- // USART_FLAG_TC为1的条件是,DR寄存器为空并且以为寄存器也为空。相当于所有数据都发送完毕1 D; }6 {, `% Y9 c% k
复制代码 字符接收
0 Y! C/ l7 o- K' V9 q; c* i- uint8_t ucTemp; 4 J* d6 p9 d6 k" L0 `3 q
- if (USART_GetITStatus(UART5, USART_IT_RXNE)!=RESET){
3 d! r2 s) I# R& B! ` - ucTemp = USART_ReceiveData( UART5 );5 b+ P/ e5 w W8 t$ i
- // USART_SendData(UART5,ucTemp);
# a- u, y% h! C( Z% W7 l3 X - }+ m/ [9 @1 ~+ b ?3 M
- // USART_ReceiveData()函数是将DR寄存器读取并返回; ^$ m4 t, T T" x
- // USART_IT_RXNE不为0表示数据寄存器中有数据,需要将其读出。$ o: [! J/ c7 E0 X3 j
- // 每次收一个字符,USART_IT_RXNE会被设置为1,直至数据被读出,USART_IT_RXNE再次被设置为0.
7 a1 R2 y- j6 ] - // 同时,USART_IT_RXNE也被设置为中断的标志,即接收到数据时,进入中断。
! A0 ^$ E+ }4 ^* `0 T5 d' Y
复制代码
$ q" n7 S# N/ o8 i5 i# [, }& Z- f0 e& u5 u, z- p: T
\/ N& w. t( h
|