|
一、USART简介$ Y& \; [# C% } 1.1 UART 简介 通用同步异步收发器(USART:Universal Synchronous/Asynchronous Receiver/Transmitter)) A; X! ~5 v; i! Z" ^ 特点:串行、全双工、同步/异步通信(USART可支持同步通信,UART只支持异步通信) 发送和接收共用的可编程波特率0 @) L; M0 |7 C: _ 可编程数据字长度(8位或9位)(常用8位) 可配置的停止位-支持1或2个停止位(常用1位停止位) 可配置奇偶校验位(一般不用)# J0 @. Z7 Q& N( A$ Q3 I- Z* d: ^3 N 8 O* [4 l* @" ]1 @; @! b0 \ 1.2 物理拓补图 接口通过三个引脚与其他设备连接在一起。任何USART双向通信至少需要两个脚 RX:接收数据输入 TX:发送数据输出 GND:双方要共地,双方通过USART进行通信,两个设备必须要共地,因为逻辑1---3.3V电压(对于地的电压),如果两个设备不共地则对地电压的高电平(3.3V就可能不太一样)。共地的意义是让两个设备的通信电平对地电压相等+ f) T. _: V q; i' T 3 d& h0 n& t; C q: S; Z2 M4 d+ H* Z
& e" [! G- _% J 1.3 数据帧格式 一帧数据=1bit起始位[低电平]+8bit数据位+1bit奇偶校验位[可以不用]+1bit停止位[高电平]4 [! E9 c% V0 W- }; k+ \" u USART总线空闲,即没有数据发生,TX线为高电平7 D9 Z& j6 ]. K* Z7 I r $ V0 e% x& x8 q/ O K9 q, Q <1>数据位:USART发送的1次数据量是1Byte=8bit;[5、6、7、8] <2>数据字:数据位+奇偶校验位;如果不用校验位,数据字=数据位=8bit;如果使用校验位,数据字=数据位+校验位=9bit1 o$ {3 C; J! a7 x' |3 z: ?7 e# z 奇校验:数据位中的1的个数+校验位中的1的个数为奇数+ O# S% i2 ~. z5 ], \! N8 Z6 K( D7 f 偶校验:数据位中的1的个数+校验位中的1的个数为偶数 <3>停止位:可以被配置为1bit(默认);2个停止位(可用于常规USART模式、单线模式以及调制解调器模式);0.5个停止位:在智能卡模式下接收数据时使用;1.5个停止位:在智能卡模式下发送和接收数据时使用5 t6 t% N; ~* N+ ~7 ? <4>发送顺序:先发送低位,再发送高位 1.4 框图
( F! c! ]. @) g5 b$ J' h) h 对于这个框图,最关键的是两个标志位TXE(发送为空标志位),RXNE(接收不为空标志位) 数据通过RX端口进入MCU的UART的接收移位寄存器,随后进入接收数据寄存器(RDR),进入后RXNE被置1,表明可以读 f: ^$ c5 a/ R( ]8 a. J1 _ MCU通过数据总线获得数据,数据再到发送数据寄存器(TDR),若TXE=1,说明数据已经被转移到移位寄存器,表明可以发送5 ]& e* h1 O" h 二、串口通信 配置UART 开启了接收中断和空闲中断) |3 W4 I& I s3 H6 a 0 [; c2 y' }# t* W /****************************7 C E+ u! ]2 |/ w! ^7 v5 L8 ?$ ?# N *@brief uasrt1端口配置函数( |, j+ E% V; ]; d" s *@param void2 Q2 R: C# ~# S z *@return void *@NOTE PA10->RX PA9->TX PA10,PA9位于APB2上% B5 g5 j( |. A; b1 }. ^ y *@time 2025-6-3% w+ n2 k4 r+ a1 r* D4 d3 y+ d: Q ******************************/ void usart1_Init(uint32_t Baud_Rate)* h' d+ C2 I P$ r! I6 ~ { //1.打开引脚时钟PA4 Q+ `( e8 B( e" @& R. g3 u* X7 [2 X RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE) ;! ^+ z4 K) B9 | t $ e) V; j" C) d //2.打开usart1时钟( W- B+ T5 f/ z9 s/ H4 s8 \ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) ; 9 q0 S0 }2 s9 M+ O) C, U* z! M //PA9->TX推挽复用输出 GPIO_InitTypeDef GPIO_InitStruct_TX ;& y' @* [, W3 y GPIO_InitStruct_TX.GPIO_Mode = GPIO_Mode_AF_PP ;. P1 E+ c' r( o7 y# R GPIO_InitStruct_TX.GPIO_Pin = GPIO_Pin_9 ; GPIO_InitStruct_TX.GPIO_Speed = GPIO_Speed_10MHz ; GPIO_Init(GPIOA,&GPIO_InitStruct_TX) ; 4 _/ {0 {- j' n! Q. R //3.配置,PA10->RX浮空输入 GPIO_InitTypeDef GPIO_InitStruct_RX ; GPIO_InitStruct_RX.GPIO_Mode = GPIO_Mode_IN_FLOATING ; GPIO_InitStruct_RX.GPIO_Pin = GPIO_Pin_10 ; GPIO_Init(GPIOA,&GPIO_InitStruct_RX) ; //4.配置usart1, USART_InitTypeDef USART_InitStruct ; USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx ;: G: _! U4 s2 a2 F USART_InitStruct.USART_Parity = USART_Parity_No ;& }& l: c7 B y' }& h USART_InitStruct.USART_StopBits = USART_StopBits_1 ; USART_InitStruct.USART_BaudRate = Baud_Rate ; 9 u9 m3 F& j5 Q8 d$ a& h: K USART_InitStruct.USART_WordLength = USART_WordLength_8b ;6 ~4 A8 B: N% Q: { USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ;/ I4 i9 t5 h/ [; Q( O! G USART_Init(USART1,&USART_InitStruct) ;3 g. ?: {) B! K- b7 S USART_ITConfig(USART1,USART_IT_RXNE,ENABLE) ;//开启接收中断 RXNEIE=1 USART_ITConfig(USART1,USART_IT_IDLE,ENABLE) ;//开启空闲中断 IDLE=1 //初始化NVIC& C5 S$ f8 i3 t5 w NVIC_InitTypeDef NVIC_InitStruct ;8 ^- |7 T! y0 ?( i NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn ;//中断通道0 ~" _6 s6 E% B NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;//中断使能 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2 ;//响应先级 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;//抢占优先级 NVIC_Init(&NVIC_InitStruct) ;3 x5 \* u/ W" H# D; {# m8 e ( F9 F. o$ Q- M: S2 q //5.使能usart1 USART_Cmd(USART1,ENABLE) ;6 A: ?3 I2 d: O+ H0 Q2 q } 接收发送函数- b" ^* K1 P7 d+ P0 L / d- C7 w: Q% e0 G! D" M6 y /****************************2 n4 K* ?% h: h C) N *@brief 发送字符函数" ~( q5 a/ F% a *@param void *@return void *@note MCU会将数据发送到DR寄存器中的TDR寄存器中, 当TXE==1时,TDR寄存器会将数据加载到移位寄存器中,可以发送 *@time 2025-6-3) D7 a& ?4 {4 X! | ******************************/ void usart1_sendbyte(uint8_t str) {6 ?- V3 {) A n5 K5 t& E! y while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==0); USART_SendData(USART1,str); }3 u; w/ P: @6 R1 h % f) [1 M6 J) \+ H( v /**************************** *@brief 发送字符串函数 *@param uint8_t *str- r) O% H, c7 l. z5 K( ~3 f *@return uint16_t len0 G" r% v) u5 a/ [5 Y *@note 发送字符串本质上就是发送字符' s. o; t* j! f0 C0 V) u O: C *@time 2025-6-3 ******************************/* V/ M; x' J+ R0 ]( c2 D V0 j void usart1_sendstr(char *str,int len)" ^; i a7 G8 g( S& Z {7 N( h! q6 n, ]6 y5 p4 R while(len) {+ [* K, W; N( C9 Y- f usart1_sendbyte(*str++); len--; } }: D# f" B8 \) n. ?; q5 \) L! @ /**************************** *@brief 接收字符 *@param void1 h, {1 A9 ^% j" q *@return void3 ]1 h2 Z) t0 s: } a2 o *@note MCU接收到数据后先放在DR寄存器的接收移位寄存器: ~4 s. x4 C$ [ 数据会到RDR寄存器中,当RXNE==1时,说明RDR有数据,可以读# p; n$ J- O* M0 s1 h' E *@time 2025-6-3 ******************************/ uint16_t usart1_receivebyte()- j, x+ o* s7 H* [% I { while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==0); return USART_ReceiveData(USART1); }; K5 b8 c) m' K' Y1 C! O* n /**************************** *@brief 接收字符串函数(idle标志位)+ q% c; E* ?7 g- y *@param void *@return void *@note void *@time 2025-6-3$ I8 r7 u: ]- C$ z. X ******************************/- G% P: |# l- X void usart1_receivestr(char*buff) { uint8_t flag;! t( M/ s' Z9 k while(1)3 ]9 [( K; d1 W. P3 o { if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1) { *buff=USART_ReceiveData(USART1);+ \/ M( I% Y, z5 N% m# l" x+ A buff++;) L, z( O) ?* N& I' } flag=1;8 u+ T- q0 ?5 @/ z# q2 K& g }. g! {; p6 A7 \' F. {3 q5 K2 ` if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)==1&&flag)" D. f& D/ E1 p2 ]# s9 m* M6 ~; N# c {; u, l5 u. _ q *buff='\0'; //当发送时 发送结束标志 //先读SR,再读DR将置IDLE位置0 USART1->SR; USART1->DR;+ T* E' q# T+ w7 h$ a break; }9 S: b9 |/ L/ W }) n/ y" `! I! X: W+ B) o3 n) O. t }/ F2 d( b l9 S ! _- z E3 c- @% [2 L 3 j1 [0 y0 T& a) v$ q. V+ K: T 打印函数 记得勾选微库,printf函数不涉及到中断,它是将内存中的数据输出到上位机 5 X7 ?4 Z, j: W. n3 w4 s$ p& b //printf()的底层借助fputc函数进行输出5 p/ C# \1 i1 ?# H: N int fputc(int ch,FILE*stream)- H" H- e& Q# l5 D! g. k- g/ n6 j L {# A" f+ E" h7 K" s) u+ n; | usart1_sendbyte(ch);" g& s$ K+ M! g$ C+ {, S return ch; }5 G5 `, O/ S- h( |$ T& R' ` % M3 K5 D% x2 w: [6 f
中断函数 MCU接收到数据时,进入接收中断函数,将数据存到rx_buff[100]数组里;当上位机不发送数据时,说明空闲状态,进入空闲中断,将 rx_buff[100]数组里面的数据发到上位机; v! W. l. }8 J. Q, h8 { % b: s- X! q, Q: R2 ]( Q. } /****************************& h/ E/ A4 u& V, K9 c6 w; [ *@brief usart1中断实验 *@param void *@return void *@note void0 h4 r$ ]" N+ C) c- Q. ~( l *@time 2025-6-4- @$ l+ v0 y e; }6 L; X ******************************/ char rx_buff[1000]={0};. R4 o$ m4 u# _) E J char cnt=0;7 b- P$ N' [9 W void USART1_IRQHandler(void) { //当RXNE=1时,有数据接收 if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)# `) l& ^( B0 n6 P {4 _; B1 H" f {# w% k. X rx_buff[cnt++]=USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位) s+ q. R* z" a+ W$ c: o# j/ n } //当IDLE=1时,0 C5 K C( x: l J if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET) {" Q0 y& H5 B: Y: P5 [2 O usart1_sendstr(rx_buff,strlen(rx_buff));- }- f" X7 \8 Z' ~) T cnt=0;1 t: H6 \9 a- f. s USART1->SR;# o, k' \- Q8 n# A7 A( s/ O USART1->DR; //清除标志位0 b! m! E2 s0 E/ w/ Y } }% K0 a5 _! ^" N. b& @ F ————————————————3 z& |: ~+ H/ w) {0 Y- ~) L |
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之继电器模块
微信公众号
手机版