|
一、USART简介; Q: G- b- }( X, o+ k 1.1 UART 简介 通用同步异步收发器(USART:Universal Synchronous/Asynchronous Receiver/Transmitter) 特点:串行、全双工、同步/异步通信(USART可支持同步通信,UART只支持异步通信) % K- W" g& L* _6 H, ~& D 发送和接收共用的可编程波特率2 s% k/ b# z/ G _% n0 W# m( [ 可编程数据字长度(8位或9位)(常用8位); q0 V& l) J3 T+ x 可配置的停止位-支持1或2个停止位(常用1位停止位)1 P" Z u1 k! g4 w 可配置奇偶校验位(一般不用) 0 d3 A: X9 d2 N7 `8 X 1.2 物理拓补图7 b; [3 l! @% ?" ?' N& N. O 接口通过三个引脚与其他设备连接在一起。任何USART双向通信至少需要两个脚: a6 P- n( v5 Z8 |" a }/ r) T. } RX:接收数据输入 TX:发送数据输出* J/ d7 O1 b4 c& v0 y8 A GND:双方要共地,双方通过USART进行通信,两个设备必须要共地,因为逻辑1---3.3V电压(对于地的电压),如果两个设备不共地则对地电压的高电平(3.3V就可能不太一样)。共地的意义是让两个设备的通信电平对地电压相等" G6 T/ Y9 }3 W& P" {1 R+ E 3 ?' W B, x8 S0 S" q$ ]- P
. a8 p+ X. l" ~, V 1.3 数据帧格式# O8 D9 K2 r3 Y3 J 一帧数据=1bit起始位[低电平]+8bit数据位+1bit奇偶校验位[可以不用]+1bit停止位[高电平]- B' l# x3 I: B+ @ 4 d* c% m9 U0 I' Q; d USART总线空闲,即没有数据发生,TX线为高电平 <1>数据位:USART发送的1次数据量是1Byte=8bit;[5、6、7、8]& p( B8 ?0 r! f: u <2>数据字:数据位+奇偶校验位;如果不用校验位,数据字=数据位=8bit;如果使用校验位,数据字=数据位+校验位=9bit 奇校验:数据位中的1的个数+校验位中的1的个数为奇数: ?5 F8 T4 @; J) C9 j" L% g 偶校验:数据位中的1的个数+校验位中的1的个数为偶数 <3>停止位:可以被配置为1bit(默认);2个停止位(可用于常规USART模式、单线模式以及调制解调器模式);0.5个停止位:在智能卡模式下接收数据时使用;1.5个停止位:在智能卡模式下发送和接收数据时使用+ O0 k- `2 q" Y% n9 Q <4>发送顺序:先发送低位,再发送高位' s8 g0 W" } G* {( k- Z8 F% l 4 v! Z' ?4 u+ C1 w" h# @) }- Q 1.4 框图2 k8 L/ t- A# ~7 t4 a$ E
) r$ w' D; l- x+ y! A* p, G 对于这个框图,最关键的是两个标志位TXE(发送为空标志位),RXNE(接收不为空标志位)9 \3 _& A1 U1 R) _* b6 X% ] 数据通过RX端口进入MCU的UART的接收移位寄存器,随后进入接收数据寄存器(RDR),进入后RXNE被置1,表明可以读' f+ X3 P( k$ q/ P8 N MCU通过数据总线获得数据,数据再到发送数据寄存器(TDR),若TXE=1,说明数据已经被转移到移位寄存器,表明可以发送 二、串口通信' \$ Y& E- X& R$ l 配置UART z* j2 Y7 k( k . m7 l" F/ y% p 开启了接收中断和空闲中断 /**************************** }0 A A! Y$ x/ y5 I! a$ V+ } *@brief uasrt1端口配置函数 *@param void *@return void *@NOTE PA10->RX PA9->TX PA10,PA9位于APB2上3 c5 u. }( c+ N/ u. c *@time 2025-6-37 `. R% e% _5 Q ******************************/+ G( J4 n0 a& r3 ]9 u1 o' V void usart1_Init(uint32_t Baud_Rate) {! D5 A2 }& G6 o; y# L% M0 \ //1.打开引脚时钟PA RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE) ; //2.打开usart1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) ;* v, `4 S! |+ V! b8 H6 V$ ]2 U //PA9->TX推挽复用输出 GPIO_InitTypeDef GPIO_InitStruct_TX ; GPIO_InitStruct_TX.GPIO_Mode = GPIO_Mode_AF_PP ;/ l, N4 b, Q' \: I GPIO_InitStruct_TX.GPIO_Pin = GPIO_Pin_9 ; GPIO_InitStruct_TX.GPIO_Speed = GPIO_Speed_10MHz ;+ d+ \8 }' X+ A$ i GPIO_Init(GPIOA,&GPIO_InitStruct_TX) ; //3.配置,PA10->RX浮空输入: z) U; J8 T7 u" \1 I GPIO_InitTypeDef GPIO_InitStruct_RX ; GPIO_InitStruct_RX.GPIO_Mode = GPIO_Mode_IN_FLOATING ; GPIO_InitStruct_RX.GPIO_Pin = GPIO_Pin_10 ;4 J' V% R( w0 v, I' }' u5 x8 v- e GPIO_Init(GPIOA,&GPIO_InitStruct_RX) ; //4.配置usart1, USART_InitTypeDef USART_InitStruct ;4 @/ _9 T5 x& _9 a/ O, I% U# z) f USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx ;, k7 p, O4 S; E! d& N& P USART_InitStruct.USART_Parity = USART_Parity_No ;% }2 I( K! n; b1 ~' K) j1 ?8 t& X9 V USART_InitStruct.USART_StopBits = USART_StopBits_1 ;0 [( o+ \7 _8 }) U USART_InitStruct.USART_BaudRate = Baud_Rate ; 6 u5 W' p2 D9 |% r USART_InitStruct.USART_WordLength = USART_WordLength_8b ; n8 P1 I( |' C# m3 i2 j. r USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ;' d$ R! i: {' O2 L/ L% W( u USART_Init(USART1,&USART_InitStruct) ; ! _* J* h( h/ y$ A* C! m . ]! [$ Y" N. H" D$ T! ^, k7 E USART_ITConfig(USART1,USART_IT_RXNE,ENABLE) ;//开启接收中断 RXNEIE=1: R) `! {. j) y% @ USART_ITConfig(USART1,USART_IT_IDLE,ENABLE) ;//开启空闲中断 IDLE=1 //初始化NVIC NVIC_InitTypeDef NVIC_InitStruct ; NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn ;//中断通道0 g/ e; A! q" e0 ]" N* |; J$ B NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;//中断使能 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2 ;//响应先级6 U: u* ^% ~3 F NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;//抢占优先级 NVIC_Init(&NVIC_InitStruct) ;& L' J" E( J5 r' [: T //5.使能usart1 USART_Cmd(USART1,ENABLE) ;8 w8 k; V9 t, [9 @: Y$ D } . O7 J: y: _/ C7 c4 j 4 f" T* x7 i0 n7 n- f 4 b! m6 o. f/ a& B R 接收发送函数 /**************************** *@brief 发送字符函数. g: |/ Z9 C0 C# ]* H *@param void *@return void *@note MCU会将数据发送到DR寄存器中的TDR寄存器中,+ b5 R9 U; m: t, I: q 当TXE==1时,TDR寄存器会将数据加载到移位寄存器中,可以发送4 {: \# X9 A1 v1 l' v *@time 2025-6-3: ]7 d. K/ p) q& L! x1 I ******************************/1 c: D! O7 J0 S* z void usart1_sendbyte(uint8_t str)- ~0 U$ S$ H- s {- s8 e2 O/ s1 N9 S( v" l while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==0);. }% }8 b m$ v, B USART_SendData(USART1,str);- h2 j x9 B2 a* f9 V }9 A7 R3 T9 ]" v9 r4 j. J- D " l& Y% b! n+ |; B- A' N: l/ C /**************************** *@brief 发送字符串函数+ w: }1 b) T1 c+ p *@param uint8_t *str3 R8 y4 {3 ]: y( V( [! w+ P, D ]) U *@return uint16_t len *@note 发送字符串本质上就是发送字符! t9 l5 x8 b/ h' |: U *@time 2025-6-39 d6 A" k- l& R' q/ Q m ******************************/ void usart1_sendstr(char *str,int len) {4 c2 R' r- O+ V* ~$ h$ }& u) ] while(len); O, F* N! q. T* ~0 G8 G4 S" t { usart1_sendbyte(*str++);8 @, A9 L. }7 i& V0 r; K4 `) H len--; } } 3 _( \, |( f5 ?( b /****************************9 p; n6 i9 N( }, G *@brief 接收字符 *@param void' e3 y* S3 g1 f9 o *@return void9 G. i) {* u) v2 N *@note MCU接收到数据后先放在DR寄存器的接收移位寄存器5 M( Y4 c# M) E3 h2 z 数据会到RDR寄存器中,当RXNE==1时,说明RDR有数据,可以读7 w5 m# k6 ?$ |/ }: \ *@time 2025-6-3, }! y! E% _, S P( j7 Z T ******************************/" |3 P* Q1 g1 `+ M uint16_t usart1_receivebyte()7 _- b( y; H$ x9 C; | { while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==0);* i) t3 u% o) _ return USART_ReceiveData(USART1); }3 \% e9 {/ B& Q# v /**************************** *@brief 接收字符串函数(idle标志位)1 a1 T- V# w# H9 s *@param void! {8 w% h3 u4 s% v; H& c1 M7 x *@return void2 V. Q( p6 I) x/ u# a; K, N% n *@note void+ V7 S: Z: i2 h4 O, b6 H *@time 2025-6-3! ~' g$ O, W- ?5 I4 k& U' w ******************************/5 @; c3 B# n1 v' \) e) I void usart1_receivestr(char*buff)+ C- c( z B5 Z9 j {( i8 y7 F4 H5 M, @. R( G uint8_t flag;/ G! R% z) z: o3 ^( q5 }- M% x while(1) { if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1)2 f- Q+ W3 E8 s. F { *buff=USART_ReceiveData(USART1);3 e! w. x" H+ E; Y& b buff++; flag=1; } if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)==1&&flag)+ R- I' H) I8 B { *buff='\0'; //当发送时 发送结束标志% C0 M/ a6 H2 ^" L3 y& W B0 I { //先读SR,再读DR将置IDLE位置0$ X% X, S$ B1 L USART1->SR; USART1->DR; break;% H9 v+ P, b* S: z" M7 l# u }- R [ c' R% q4 m3 B7 t* I7 a; I } } , r# E& m1 z* Q h 打印函数- }1 G9 D& j" \; c+ d 记得勾选微库,printf函数不涉及到中断,它是将内存中的数据输出到上位机0 ]. ?) U z' M $ }9 T8 p" }( w: ]0 Y- ` //printf()的底层借助fputc函数进行输出 ?) e B2 x1 @) b int fputc(int ch,FILE*stream)3 r: Q; r6 t/ b, M! X: _/ m5 s& W { usart1_sendbyte(ch);( p+ }( a4 b7 j! \4 d8 N return ch; } % _) o+ m: N0 P) I4 ^5 r/ w
中断函数 MCU接收到数据时,进入接收中断函数,将数据存到rx_buff[100]数组里;当上位机不发送数据时,说明空闲状态,进入空闲中断,将 rx_buff[100]数组里面的数据发到上位机 /**************************** *@brief usart1中断实验 *@param void *@return void *@note void *@time 2025-6-49 ^+ W' ?! j% U: N' H ******************************/8 V- \/ [* ]& I. x6 h7 d5 ] char rx_buff[1000]={0}; char cnt=0; void USART1_IRQHandler(void) {/ D: ~% N2 Y, M! N/ u* A' E //当RXNE=1时,有数据接收 if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET); o6 q) {, q# N3 \8 [; V2 A4 P { rx_buff[cnt++]=USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位 }1 y! ~( G5 _# H# l, f //当IDLE=1时," d- l( E+ m! J- t% N: o s" ?8 n6 i if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET) { usart1_sendstr(rx_buff,strlen(rx_buff));) i! F9 d3 g" |( B( y cnt=0;6 P3 w5 n3 y; x* B/ ^- c, E USART1->SR; USART1->DR; //清除标志位 }8 N) X, d+ |; G$ c4 @ } , V6 I: O/ M+ @4 T ^0 ` ` ————————————————( x$ Q* L, F: D$ u3 e( k) v |
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之继电器模块
微信公众号
手机版