|
一、USART简介 1.1 UART 简介 通用同步异步收发器(USART:Universal Synchronous/Asynchronous Receiver/Transmitter)# Q# |% T7 ~% y3 W& E T3 b3 O 特点:串行、全双工、同步/异步通信(USART可支持同步通信,UART只支持异步通信) 发送和接收共用的可编程波特率$ z* x. S5 k4 ?8 q4 c 可编程数据字长度(8位或9位)(常用8位) 可配置的停止位-支持1或2个停止位(常用1位停止位)2 l; I/ w) O* ~" T5 Q- v5 s$ d! Q 可配置奇偶校验位(一般不用) 1.2 物理拓补图 接口通过三个引脚与其他设备连接在一起。任何USART双向通信至少需要两个脚: ]) S5 h9 v2 c+ P RX:接收数据输入 TX:发送数据输出 GND:双方要共地,双方通过USART进行通信,两个设备必须要共地,因为逻辑1---3.3V电压(对于地的电压),如果两个设备不共地则对地电压的高电平(3.3V就可能不太一样)。共地的意义是让两个设备的通信电平对地电压相等7 g; Z& L4 b5 n) w . @9 }/ V- Z% y( y! O! O8 Z% B
% n( x7 c$ c2 ]6 j# k9 J 1.3 数据帧格式+ R; D2 z. k7 G 一帧数据=1bit起始位[低电平]+8bit数据位+1bit奇偶校验位[可以不用]+1bit停止位[高电平]+ i7 Y5 i1 ~- _! V ) O/ C ~- l" ]7 [) @, \# ^ USART总线空闲,即没有数据发生,TX线为高电平# N* i& z! a5 i2 |; |/ s <1>数据位:USART发送的1次数据量是1Byte=8bit;[5、6、7、8] <2>数据字:数据位+奇偶校验位;如果不用校验位,数据字=数据位=8bit;如果使用校验位,数据字=数据位+校验位=9bit, [) M6 x! g' A& Z( o+ I0 J7 F 奇校验:数据位中的1的个数+校验位中的1的个数为奇数 偶校验:数据位中的1的个数+校验位中的1的个数为偶数6 F$ D j( |% x5 S6 c; U: Y <3>停止位:可以被配置为1bit(默认);2个停止位(可用于常规USART模式、单线模式以及调制解调器模式);0.5个停止位:在智能卡模式下接收数据时使用;1.5个停止位:在智能卡模式下发送和接收数据时使用+ Y. E# z5 m b8 K! J$ b( n b <4>发送顺序:先发送低位,再发送高位. D+ U7 a) t" o3 J 1.4 框图2 Q3 Q$ e. A, o" M) W9 L( _
对于这个框图,最关键的是两个标志位TXE(发送为空标志位),RXNE(接收不为空标志位) 数据通过RX端口进入MCU的UART的接收移位寄存器,随后进入接收数据寄存器(RDR),进入后RXNE被置1,表明可以读" c4 g! D. }7 h7 {) n MCU通过数据总线获得数据,数据再到发送数据寄存器(TDR),若TXE=1,说明数据已经被转移到移位寄存器,表明可以发送- f. a/ J( }$ \7 Y& V+ V- ] 2 g1 X: Y$ {' b3 d$ \' A 二、串口通信' f! s5 w9 b7 O4 E) x# ` 配置UART+ x2 f u( D: c9 I$ k# f3 G 开启了接收中断和空闲中断3 e( s% A1 v# v+ t. b /****************************5 @3 ^4 Y$ V+ d8 G4 { *@brief uasrt1端口配置函数, l) l9 p8 H/ _/ J *@param void9 m7 O |: L- O. ?8 `8 H *@return void *@NOTE PA10->RX PA9->TX PA10,PA9位于APB2上+ m! w" x: S- j7 D7 l- c *@time 2025-6-3 ******************************/ void usart1_Init(uint32_t Baud_Rate) { //1.打开引脚时钟PA/ b) N6 ^# x) f RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE) ; //2.打开usart1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) ; ! ]( P; N- M+ Z: z: w( ? f; W //PA9->TX推挽复用输出 GPIO_InitTypeDef GPIO_InitStruct_TX ;/ z! x- q* N0 ^- Y1 k! m' K9 p( G$ A3 G GPIO_InitStruct_TX.GPIO_Mode = GPIO_Mode_AF_PP ; GPIO_InitStruct_TX.GPIO_Pin = GPIO_Pin_9 ;7 V* Z J# ~9 x& ?/ n& Q8 c0 g" C# j1 @$ u GPIO_InitStruct_TX.GPIO_Speed = GPIO_Speed_10MHz ; GPIO_Init(GPIOA,&GPIO_InitStruct_TX) ; //3.配置,PA10->RX浮空输入 GPIO_InitTypeDef GPIO_InitStruct_RX ; GPIO_InitStruct_RX.GPIO_Mode = GPIO_Mode_IN_FLOATING ;5 U7 @% \& }( t7 W( Z8 p, y GPIO_InitStruct_RX.GPIO_Pin = GPIO_Pin_10 ;7 O6 D2 ^: |& O1 F6 Y' F2 }$ V( i GPIO_Init(GPIOA,&GPIO_InitStruct_RX) ; ! P2 s$ d4 R7 R$ \1 N: B8 N //4.配置usart1,, M7 i. D- B) Q' O. V USART_InitTypeDef USART_InitStruct ;7 Q7 c; }5 B0 n) F7 P7 U2 X USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx ;6 h5 g. v. r0 Z3 _7 Z( P/ ? USART_InitStruct.USART_Parity = USART_Parity_No ;& X, L9 _- t( r2 o7 T USART_InitStruct.USART_StopBits = USART_StopBits_1 ; USART_InitStruct.USART_BaudRate = Baud_Rate ; ) I. q- T2 {$ N& d% a! }3 r4 g- E USART_InitStruct.USART_WordLength = USART_WordLength_8b ; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ; USART_Init(USART1,&USART_InitStruct) ;$ ~9 l `: r% G# y " U. m5 @" e6 \! j, r+ i, p1 N2 { USART_ITConfig(USART1,USART_IT_RXNE,ENABLE) ;//开启接收中断 RXNEIE=1" B2 n: J: I& w$ i USART_ITConfig(USART1,USART_IT_IDLE,ENABLE) ;//开启空闲中断 IDLE=1 //初始化NVIC NVIC_InitTypeDef NVIC_InitStruct ;( `+ |' P5 v& x7 U NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn ;//中断通道 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;//中断使能 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2 ;//响应先级$ K& e& k9 j! n5 R* t8 O NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;//抢占优先级 NVIC_Init(&NVIC_InitStruct) ;( x$ ~' K3 a. D5 u# o+ T! v- b //5.使能usart1 USART_Cmd(USART1,ENABLE) ; } ' r* ` Z$ |6 Q 接收发送函数5 q3 ]1 Y" b4 D7 |* H7 V $ W' ?. Q/ z. W) X /**************************** *@brief 发送字符函数) q2 }* `$ c" n( H *@param void1 E2 V6 M2 E" ~/ ]) D *@return void2 ~5 g) o8 V+ e. Q- c) q *@note MCU会将数据发送到DR寄存器中的TDR寄存器中, 当TXE==1时,TDR寄存器会将数据加载到移位寄存器中,可以发送 *@time 2025-6-33 ~8 L7 Q( V+ P5 B8 U ******************************/+ O6 `. T& L& V8 { void usart1_sendbyte(uint8_t str) {+ N! R- }( r. [7 g6 { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==0); USART_SendData(USART1,str);. a0 K, O0 t4 T& N- j, `8 g4 s }+ u& S% r& a$ f % L; O; Q6 F, a- r: D /**************************** *@brief 发送字符串函数 *@param uint8_t *str *@return uint16_t len3 O/ Q9 P5 c/ L5 \8 Z *@note 发送字符串本质上就是发送字符 *@time 2025-6-3 ******************************/ void usart1_sendstr(char *str,int len) { while(len)) v! R4 [( p" T- w {: M8 o* y+ b% w4 r S usart1_sendbyte(*str++); len--; }2 Y2 X" }% {! _ }+ ~' g8 u# U b" e3 W2 a ! n! _+ A7 s* a: ? /****************************9 Q+ J8 ^5 Z9 V- p; c8 M. {- c *@brief 接收字符3 C7 k0 L7 |; K/ T6 Z/ p0 f; Y *@param void; h# A/ t4 I% l *@return void |0 @, k8 e8 M9 A5 ? *@note MCU接收到数据后先放在DR寄存器的接收移位寄存器 数据会到RDR寄存器中,当RXNE==1时,说明RDR有数据,可以读0 h. \' \+ O" m. C N# y *@time 2025-6-3! q2 \) d# @- s ******************************/# ]( H7 Z ^* K/ ~. Z- p2 A uint16_t usart1_receivebyte() { while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==0); return USART_ReceiveData(USART1); }: I: Z. W1 P% }5 ^* P /****************************+ A5 e/ m8 T. I *@brief 接收字符串函数(idle标志位) *@param void6 P7 m( B3 v. M2 \: U! i *@return void" M4 R B" ~) a; b9 i' P *@note void% I: a9 N. O2 r, J3 C- ?5 ~ *@time 2025-6-3" \! Z# ~: c* W, X2 I) G ******************************/ void usart1_receivestr(char*buff) {: Q6 g0 ~. P7 B; o: p" Y. i5 ] uint8_t flag; while(1) {+ q( i- e) f+ D7 j if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1) { *buff=USART_ReceiveData(USART1); buff++;9 U' t# Y/ v; j; G# A flag=1; }% F0 }1 I6 o Y0 ?! x if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)==1&&flag), J% v4 Z' h2 W2 E: M { *buff='\0'; //当发送时 发送结束标志# X% l+ R1 S* ]9 K& u4 C- y //先读SR,再读DR将置IDLE位置0 USART1->SR; USART1->DR;) X9 B9 M e- p8 D, t, z R break; }! M- K- Z3 ~$ r } }: ]* U7 H$ b* ?: c" p. [ ! V3 L( Q8 T6 I0 _- p$ M) { 6 O' K* _% o; u- C- v4 K 打印函数 记得勾选微库,printf函数不涉及到中断,它是将内存中的数据输出到上位机 5 ?3 H0 i/ c* F+ b //printf()的底层借助fputc函数进行输出* F, J& o* ^4 A! u int fputc(int ch,FILE*stream)+ b% d$ P+ e% }( ^! X7 e {' M! |7 w. h- [* `9 [0 a usart1_sendbyte(ch);" v6 l9 V5 X0 i+ h, N: [4 { \ return ch; }- n9 c) O1 S" l, ^; s& G 5 J; s% |! K* J5 }8 w 5 i2 u; k; m+ u
中断函数 MCU接收到数据时,进入接收中断函数,将数据存到rx_buff[100]数组里;当上位机不发送数据时,说明空闲状态,进入空闲中断,将 rx_buff[100]数组里面的数据发到上位机6 [& g ?7 i, k M1 |; H: d, D , F% l5 p \+ P /****************************6 M6 r: v4 i4 s+ ~ *@brief usart1中断实验 *@param void *@return void *@note void1 d/ d6 }' D' D# m5 y *@time 2025-6-4 ******************************/ char rx_buff[1000]={0}; char cnt=0;" V& z. \1 ^ E: P. w/ |) x void USART1_IRQHandler(void)+ p/ v+ g" K* f( E0 V: u9 z {0 Z. e0 ^5 g( E: J( \) R, c: F //当RXNE=1时,有数据接收/ U$ x/ I& a. `# n7 y; Q U if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)0 M4 k) G( J9 @- q M: L {" a/ p' f& c! ?1 r: N* Q, z. Z& n rx_buff[cnt++]=USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位 } ) }9 }: V) _8 I7 y6 Q //当IDLE=1时, if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)8 ?1 X9 h# y$ Q. ]0 ` {" z2 k8 @, T( K; j4 s/ r" S usart1_sendstr(rx_buff,strlen(rx_buff));, j; T+ Q( t8 Q1 _ cnt=0; USART1->SR; USART1->DR; //清除标志位' {& S8 s" D/ Y6 P+ @1 B0 e } }& R% X1 @6 B4 R* s- [ 7 g0 h& D2 e) e* H ———————————————— |
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之继电器模块
微信公众号
手机版