|
一、USART简介, f4 U, }1 W1 @$ t, x( U/ n 1.1 UART 简介, g+ _( c' o' i+ p8 k; Q/ ] 通用同步异步收发器(USART:Universal Synchronous/Asynchronous Receiver/Transmitter)9 ^/ O j" {: Z) m5 Y 特点:串行、全双工、同步/异步通信(USART可支持同步通信,UART只支持异步通信) ! g/ E$ H6 B1 \- U& g 发送和接收共用的可编程波特率 i7 v9 \ G$ m9 N 可编程数据字长度(8位或9位)(常用8位)8 J: A3 v: K2 x 可配置的停止位-支持1或2个停止位(常用1位停止位) 可配置奇偶校验位(一般不用), o5 u) ~# D' a' V* }" p 1.2 物理拓补图 接口通过三个引脚与其他设备连接在一起。任何USART双向通信至少需要两个脚" {0 L7 W) ]4 t, |# Z RX:接收数据输入* d6 n# D3 ^" A; }9 X7 P TX:发送数据输出! q# I, A, g+ p+ l7 ^ GND:双方要共地,双方通过USART进行通信,两个设备必须要共地,因为逻辑1---3.3V电压(对于地的电压),如果两个设备不共地则对地电压的高电平(3.3V就可能不太一样)。共地的意义是让两个设备的通信电平对地电压相等 ; a7 Q; Z: g+ e! y$ m4 P7 i- n
1.3 数据帧格式 一帧数据=1bit起始位[低电平]+8bit数据位+1bit奇偶校验位[可以不用]+1bit停止位[高电平]: q4 x: C; v& K- O0 M USART总线空闲,即没有数据发生,TX线为高电平) V% G& ~. L a; Q <1>数据位:USART发送的1次数据量是1Byte=8bit;[5、6、7、8] <2>数据字:数据位+奇偶校验位;如果不用校验位,数据字=数据位=8bit;如果使用校验位,数据字=数据位+校验位=9bit 奇校验:数据位中的1的个数+校验位中的1的个数为奇数 偶校验:数据位中的1的个数+校验位中的1的个数为偶数 <3>停止位:可以被配置为1bit(默认);2个停止位(可用于常规USART模式、单线模式以及调制解调器模式);0.5个停止位:在智能卡模式下接收数据时使用;1.5个停止位:在智能卡模式下发送和接收数据时使用! }( q0 P6 v( B0 z0 ~: Y <4>发送顺序:先发送低位,再发送高位 1.4 框图2 J! s5 ?# |" k" `( G
4 J; ?, r" {/ F" p 对于这个框图,最关键的是两个标志位TXE(发送为空标志位),RXNE(接收不为空标志位), K/ D+ q& g9 H7 \8 @ 数据通过RX端口进入MCU的UART的接收移位寄存器,随后进入接收数据寄存器(RDR),进入后RXNE被置1,表明可以读 MCU通过数据总线获得数据,数据再到发送数据寄存器(TDR),若TXE=1,说明数据已经被转移到移位寄存器,表明可以发送5 s: y4 E2 V% L ?, W o* J7 m# X 2 K/ a' E: _; s# S1 J# [ 二、串口通信3 J5 n. U9 l- ? 配置UART 3 }2 O+ C/ R7 Q1 d( [" E+ ~/ a 开启了接收中断和空闲中断 ' X& ~5 s$ v3 e# O /**************************** *@brief uasrt1端口配置函数 q0 c+ l- @/ C+ J! \ *@param void3 a- ]2 C0 c' u( S: C7 Q *@return void *@NOTE PA10->RX PA9->TX PA10,PA9位于APB2上) U4 W8 I: Q: P6 `3 U' n0 ]: Q3 p *@time 2025-6-3 z7 {! v) _5 s' p/ ^# t ******************************/3 ]' j. K h- ]1 t& l void usart1_Init(uint32_t Baud_Rate)7 H" F" l N" B6 x3 [' l { //1.打开引脚时钟PA# m; Q# E$ b d! D6 ` RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE) ; 7 y$ I: X4 f" R7 V //2.打开usart1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) ; //PA9->TX推挽复用输出5 j) o8 f6 e/ ^7 J. i, V4 u GPIO_InitTypeDef GPIO_InitStruct_TX ; GPIO_InitStruct_TX.GPIO_Mode = GPIO_Mode_AF_PP ; GPIO_InitStruct_TX.GPIO_Pin = GPIO_Pin_9 ; GPIO_InitStruct_TX.GPIO_Speed = GPIO_Speed_10MHz ; GPIO_Init(GPIOA,&GPIO_InitStruct_TX) ;) b) J, O A/ J! f5 S % t H8 l) L1 t //3.配置,PA10->RX浮空输入+ x5 U |- d) N. b GPIO_InitTypeDef GPIO_InitStruct_RX ; GPIO_InitStruct_RX.GPIO_Mode = GPIO_Mode_IN_FLOATING ;4 `, l! L* F' ^* q2 b9 w4 d# C ^ GPIO_InitStruct_RX.GPIO_Pin = GPIO_Pin_10 ;: s# o8 w9 ?4 B( u8 W GPIO_Init(GPIOA,&GPIO_InitStruct_RX) ; //4.配置usart1,9 |; g7 A) M& g. q" N. I( M1 a* F+ j USART_InitTypeDef USART_InitStruct ;8 t% ?- a" R; i. H/ m% Q8 h8 c USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx ;. Y! O: l0 [6 N* @ USART_InitStruct.USART_Parity = USART_Parity_No ; USART_InitStruct.USART_StopBits = USART_StopBits_1 ; USART_InitStruct.USART_BaudRate = Baud_Rate ; * E! q3 [5 X1 X# L* ?: [% P7 t/ p& J USART_InitStruct.USART_WordLength = USART_WordLength_8b ;3 B/ z9 B+ s6 y x USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ; USART_Init(USART1,&USART_InitStruct) ; USART_ITConfig(USART1,USART_IT_RXNE,ENABLE) ;//开启接收中断 RXNEIE=14 J% Q- D9 G5 o( e5 v5 s USART_ITConfig(USART1,USART_IT_IDLE,ENABLE) ;//开启空闲中断 IDLE=1 //初始化NVIC" t+ W5 B, e) w' D NVIC_InitTypeDef NVIC_InitStruct ; NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn ;//中断通道, P4 G3 E! C% W. h! C: L: ~ NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;//中断使能3 P- ]. D# X# P! m" u! a/ h/ \ NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2 ;//响应先级3 ], Z3 j3 m5 D. R8 T& Q NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;//抢占优先级6 t; t9 v/ C4 r2 c0 l NVIC_Init(&NVIC_InitStruct) ;. j& t" o0 s& ]7 E" | # ~8 Y" j$ j9 R //5.使能usart1$ l6 U' @ ^! q0 J9 I USART_Cmd(USART1,ENABLE) ; } ! G/ Y) d5 Y; |+ E/ W8 M3 Q : T" M) u* s# W6 _- b# I; M6 _ : V* Q5 f3 }# [" g3 i 接收发送函数 & S' v* B/ S# ]. O: _ x2 q; S) D /****************************3 O' w. T" Y6 G *@brief 发送字符函数/ X9 d4 [$ \* ~ *@param void: G% m) o* y7 H0 S *@return void *@note MCU会将数据发送到DR寄存器中的TDR寄存器中,6 t3 b! p6 U. a1 W 当TXE==1时,TDR寄存器会将数据加载到移位寄存器中,可以发送 *@time 2025-6-3/ U7 ^% o, r; @7 x. L ******************************/ void usart1_sendbyte(uint8_t str)0 L4 v1 }. n( D8 s' Q, f {2 z ^. \+ i% S$ G while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==0); USART_SendData(USART1,str);) B3 a# Z i* s }% m6 K% o- X" B- z 5 Y4 P' u8 g9 I( b+ K /****************************4 K! \% {" C3 ~ *@brief 发送字符串函数% s5 ? y. R6 l: a% H, E; j( U2 f *@param uint8_t *str *@return uint16_t len *@note 发送字符串本质上就是发送字符 *@time 2025-6-3 ******************************/- ?, M) R+ Y$ s: q% @1 j2 t+ ^- j- i void usart1_sendstr(char *str,int len)3 r. ^& R; u( q6 j- R2 y4 e8 \$ c { while(len)5 k" [: T) O/ F6 j1 v" i" I* o {+ g! j8 h4 r8 {$ Q- R$ J! I usart1_sendbyte(*str++); len--;; n; ?& j2 C$ g* a+ m `# H }4 b' d4 T% W8 f3 p6 p+ ~3 i1 t# i5 z }3 i8 ], m- n6 q3 ^6 F+ s & D! M9 \9 D& g' }) \# O4 F /**************************** *@brief 接收字符 *@param void! {2 x$ z7 u3 g5 ^+ A *@return void *@note MCU接收到数据后先放在DR寄存器的接收移位寄存器 数据会到RDR寄存器中,当RXNE==1时,说明RDR有数据,可以读 *@time 2025-6-3 r1 b2 i- R9 I ******************************/ uint16_t usart1_receivebyte(); c* K9 R' V. T6 d \& v {* V9 P- \1 x' ?9 f) `7 q1 O while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==0);4 [# q: u! ?6 z6 z" w* }( } return USART_ReceiveData(USART1); } /**************************** *@brief 接收字符串函数(idle标志位) *@param void *@return void *@note void *@time 2025-6-3 ******************************/ void usart1_receivestr(char*buff)' T# |3 I) Y, x { uint8_t flag;5 P m$ v/ ^6 X while(1)4 S! d) D2 R4 P2 k; f: [) L { if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1)( v6 I7 G& t& T- y" H { *buff=USART_ReceiveData(USART1);" }5 X- ~# g; I* O/ g buff++; flag=1;7 }# `& Y1 v, h, U5 s4 N }. |( q8 L+ t2 ]* ~" e% T# d- S& D; s if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)==1&&flag) {' X- H+ G% Q1 M *buff='\0'; //当发送时 发送结束标志 //先读SR,再读DR将置IDLE位置0 USART1->SR; USART1->DR;4 b8 n x& C, F break;" K( j+ w1 w0 A8 m5 s- b9 W1 M }5 @4 \) |9 }, z }- A' M4 s' {% G0 C; V }0 Z |0 ^: O5 s) a1 h ( y- E. A, r+ m7 b1 A. T 打印函数* T$ F/ y+ J5 e- G 记得勾选微库,printf函数不涉及到中断,它是将内存中的数据输出到上位机5 R+ d, Q0 o3 c, p7 m 0 z, _; I* h% G5 l( a- c" e. z //printf()的底层借助fputc函数进行输出 int fputc(int ch,FILE*stream) {! @8 `$ J) g) W4 K- w& ~% ^ usart1_sendbyte(ch); return ch;- f R g- o$ J) z; S5 Q4 D9 [ }' w5 R# |0 v/ D( y. Z% ~& J ) L9 r3 G* W# Z, t5 b4 q2 @ 1 Z$ B1 P! P$ {) E4 m
中断函数8 z3 ]8 T$ P* C( i MCU接收到数据时,进入接收中断函数,将数据存到rx_buff[100]数组里;当上位机不发送数据时,说明空闲状态,进入空闲中断,将 rx_buff[100]数组里面的数据发到上位机 ' K: L) Q, _6 Z; N /**************************** *@brief usart1中断实验6 T' Z6 p+ a- L$ {/ ~ *@param void8 c4 Z* D; B6 z! u *@return void *@note void; F( o& n8 Q# P2 k! { *@time 2025-6-4 ******************************/ char rx_buff[1000]={0}; char cnt=0; void USART1_IRQHandler(void). V5 s, h, j) G: R- M6 h { //当RXNE=1时,有数据接收 if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)3 W' I5 {/ ?- e6 Y { rx_buff[cnt++]=USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位5 ]7 }9 b8 h$ T% d }# ?/ f% {' f& M/ C) J) g4 u/ V) a //当IDLE=1时, if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)9 b& |/ J3 ^: R5 ?9 g { usart1_sendstr(rx_buff,strlen(rx_buff)); cnt=0;: p, j- ?. \- h4 { USART1->SR; USART1->DR; //清除标志位5 I3 m( D' T- [5 S } }- l' z+ w- w$ f, ?! m) d) s ————————————————6 d5 J, Z. C% k( G |
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:雨露均沾·逢7狂欢】之五:STM32系统时钟PLL故障排除1例
【福利三:雨露均沾·逢7狂欢】之三:AI帮我查BUG
福利三:雨露均沾·逢7狂欢】之二:STM32F407VG串口通信乱码故障的排除
基于STM32F103的I2C主从机通信
OpenBLT移植到STM32F103战舰开发板上适用于所有STM32F103系列的Bootloader
TFT LCD 与 FSMC 的硬件连接大容量 STM32F10xxx FSMC 接口
单片机:初学者该了解的STM32F103基础知识
2025软件工具兔哥知道
STM32之继电器模块
微信公众号
手机版