|
一、USART简介 1.1 UART 简介 通用同步异步收发器(USART:Universal Synchronous/Asynchronous Receiver/Transmitter)1 R4 F! V; H( y" ]4 d7 A I 特点:串行、全双工、同步/异步通信(USART可支持同步通信,UART只支持异步通信) 9 e( g1 w2 z: G4 K: K8 v 发送和接收共用的可编程波特率 可编程数据字长度(8位或9位)(常用8位). C: Y4 n* s- H4 u5 Y" E2 q% [ 可配置的停止位-支持1或2个停止位(常用1位停止位) 可配置奇偶校验位(一般不用) 1.2 物理拓补图 接口通过三个引脚与其他设备连接在一起。任何USART双向通信至少需要两个脚 RX:接收数据输入 TX:发送数据输出 GND:双方要共地,双方通过USART进行通信,两个设备必须要共地,因为逻辑1---3.3V电压(对于地的电压),如果两个设备不共地则对地电压的高电平(3.3V就可能不太一样)。共地的意义是让两个设备的通信电平对地电压相等
( |0 h2 {5 N% I/ w/ }* o 1.3 数据帧格式 一帧数据=1bit起始位[低电平]+8bit数据位+1bit奇偶校验位[可以不用]+1bit停止位[高电平] USART总线空闲,即没有数据发生,TX线为高电平 <1>数据位:USART发送的1次数据量是1Byte=8bit;[5、6、7、8]4 l% B( T* [% U) B <2>数据字:数据位+奇偶校验位;如果不用校验位,数据字=数据位=8bit;如果使用校验位,数据字=数据位+校验位=9bit! B# I! {7 M3 z* \9 a5 @1 U% g6 G 奇校验:数据位中的1的个数+校验位中的1的个数为奇数 偶校验:数据位中的1的个数+校验位中的1的个数为偶数. x1 B/ o8 `+ o0 j <3>停止位:可以被配置为1bit(默认);2个停止位(可用于常规USART模式、单线模式以及调制解调器模式);0.5个停止位:在智能卡模式下接收数据时使用;1.5个停止位:在智能卡模式下发送和接收数据时使用, V6 c' Z& f2 z <4>发送顺序:先发送低位,再发送高位 j$ }# L8 Y2 q4 H- k! a8 N* v" H 1.4 框图 4 p1 p2 g5 f6 e
, P" b3 I" W, M$ _% T) s+ P0 g5 a 对于这个框图,最关键的是两个标志位TXE(发送为空标志位),RXNE(接收不为空标志位)4 q" V) f+ ]( o6 g 数据通过RX端口进入MCU的UART的接收移位寄存器,随后进入接收数据寄存器(RDR),进入后RXNE被置1,表明可以读- V, ~$ ~& Y6 V$ Y" X) a8 K# V+ c MCU通过数据总线获得数据,数据再到发送数据寄存器(TDR),若TXE=1,说明数据已经被转移到移位寄存器,表明可以发送 二、串口通信 配置UART & `) T9 T2 a5 W$ D" V 开启了接收中断和空闲中断 /**************************** *@brief uasrt1端口配置函数 *@param void7 x* N/ X6 C" |* u! n( g( f. O [ *@return void$ P' a* D/ S* m9 g( r8 R) v% v ~ *@NOTE PA10->RX PA9->TX7 s. ?7 |5 R* L7 w2 E. i$ w PA10,PA9位于APB2上 *@time 2025-6-3 ******************************/ X4 D9 H U/ G6 |$ U* E6 Z3 G& V void usart1_Init(uint32_t Baud_Rate)( X+ ^1 j' E) p# f! `' m8 ] {5 Q* e5 {. N+ z/ j' N# L //1.打开引脚时钟PA$ B* ?. u% Q. z) D) H& Y RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE) ;0 C' i+ k. \( i) I3 i! Z3 q. P+ c* S' B 1 D1 R, r8 B# F* y( q2 t# i //2.打开usart1时钟8 K O) j8 L) H; _1 Y: M RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) ;5 s8 e% n! L- `; J& P4 R' Q( \6 ? + i5 f2 y7 K, l9 l$ P //PA9->TX推挽复用输出 GPIO_InitTypeDef GPIO_InitStruct_TX ;3 k/ e7 c) h/ f, y) |& ~" @ GPIO_InitStruct_TX.GPIO_Mode = GPIO_Mode_AF_PP ; GPIO_InitStruct_TX.GPIO_Pin = GPIO_Pin_9 ;' T' m" ^ h1 c% X3 ] GPIO_InitStruct_TX.GPIO_Speed = GPIO_Speed_10MHz ;# Q2 |0 B1 L# I* G+ j1 O GPIO_Init(GPIOA,&GPIO_InitStruct_TX) ; & y) D6 l- \+ h# {4 Q0 ^ //3.配置,PA10->RX浮空输入3 f* [6 Z* Y0 l. ^. U7 M GPIO_InitTypeDef GPIO_InitStruct_RX ;* \5 B# w; E4 I- v 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 ;. i* d+ c5 C2 k8 l USART_InitStruct.USART_Parity = USART_Parity_No ; USART_InitStruct.USART_StopBits = USART_StopBits_1 ; USART_InitStruct.USART_BaudRate = Baud_Rate ; 5 |; X: Q3 ^9 [ USART_InitStruct.USART_WordLength = USART_WordLength_8b ;& R5 Y' \& {6 I" A$ n USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ;! r$ a% J8 a: G9 e6 `& k1 A x USART_Init(USART1,&USART_InitStruct) ; USART_ITConfig(USART1,USART_IT_RXNE,ENABLE) ;//开启接收中断 RXNEIE=1 USART_ITConfig(USART1,USART_IT_IDLE,ENABLE) ;//开启空闲中断 IDLE=1 //初始化NVIC NVIC_InitTypeDef NVIC_InitStruct ;4 x% S8 y4 i7 E9 c NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn ;//中断通道 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;//中断使能0 E1 p! H7 x8 \4 n8 o3 z) ] NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2 ;//响应先级 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;//抢占优先级) ]6 j! T0 p& \: m' U NVIC_Init(&NVIC_InitStruct) ;% S. N, C0 L; Y$ o% ] //5.使能usart17 d' a C. O( _' _ USART_Cmd(USART1,ENABLE) ;9 x" j2 D; V# g/ E } / T7 a; y- t8 H+ o; b 接收发送函数1 p R3 }8 {4 f8 L+ R /****************************! n; G! q q/ M1 r* p *@brief 发送字符函数& ~- A' b5 d' l/ H *@param void5 H& S0 u+ m# g! N7 `- u' W *@return void *@note MCU会将数据发送到DR寄存器中的TDR寄存器中, 当TXE==1时,TDR寄存器会将数据加载到移位寄存器中,可以发送" W1 Z" Y) y6 @* v1 m *@time 2025-6-3 ******************************/ void usart1_sendbyte(uint8_t str). [) H1 M! x$ Y B- D/ z* | {* _5 Q: P6 Q5 y: k( M- j% r while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==0); USART_SendData(USART1,str); } & k0 S+ N1 d3 R /****************************6 D6 p8 C5 l; k *@brief 发送字符串函数% D5 c; ?1 p3 Y *@param uint8_t *str *@return uint16_t len9 @( |& S# d! X% A *@note 发送字符串本质上就是发送字符 *@time 2025-6-3 ******************************/ void usart1_sendstr(char *str,int len)& U5 Q0 t7 K$ k {8 x3 D& Y, u+ G; v" c while(len) { usart1_sendbyte(*str++);' K" [) }% Z! p& n& O len--;2 {% F4 V T S7 ]" a3 u8 A }6 H# D7 Z# {# z# m; @5 _2 |" W$ v }3 N. P% n \( b8 E4 v /****************************5 A- J3 u a) b *@brief 接收字符. X: `- d( r( C" _4 ?: j) @ *@param void5 I- W! y, x7 ~' q6 v) s1 { *@return void( {* S7 L% _1 I *@note MCU接收到数据后先放在DR寄存器的接收移位寄存器+ ^0 r1 o: S( i 数据会到RDR寄存器中,当RXNE==1时,说明RDR有数据,可以读( {, O9 ]2 v `$ | *@time 2025-6-3 ******************************/5 n4 E/ f3 \4 E* N( y2 e% t- Z0 S uint16_t usart1_receivebyte(). d$ a$ P9 w" A3 ?& e {: K7 w+ Z- X. e1 ^* v while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==0); return USART_ReceiveData(USART1);$ m- P8 q9 s) x1 j% F8 n+ h: v }1 }- q* Z P/ i6 g /**************************** *@brief 接收字符串函数(idle标志位) *@param void+ z7 F$ m* I3 P {7 j+ y *@return void# o1 A- C* g& a, C* o *@note void *@time 2025-6-3$ h- F% J, M# W: X9 k- \ ******************************/ void usart1_receivestr(char*buff)4 U( T7 S( B6 m7 A/ ]' \3 M) X {- j7 ^5 C' F$ R9 I uint8_t flag;, F9 ]! p, @. I9 ?# ^6 G while(1) { if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1) { *buff=USART_ReceiveData(USART1); buff++; ~: h% i, _, ]# C0 D& R flag=1;# \9 \4 Z7 A" N } if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)==1&&flag)( c6 c5 k z/ e, A2 Q& y2 j' Z {( j& h0 p2 ~6 S7 d *buff='\0'; //当发送时 发送结束标志" W. L* y2 `2 a! O7 | //先读SR,再读DR将置IDLE位置0 USART1->SR;: z8 E5 ~" Y% t5 U- m" I USART1->DR; break;' m3 _; c- I. |. \6 x( S" J% I } } }8 P- \1 n( }3 w 1 D+ T. p/ k- c1 A# K) \$ o ( f3 k, r- K- D' L0 W+ E; _ F 打印函数, S; ?4 ^$ H! z$ n+ s* Y 记得勾选微库,printf函数不涉及到中断,它是将内存中的数据输出到上位机 //printf()的底层借助fputc函数进行输出 int fputc(int ch,FILE*stream)' }6 h& c5 |8 V6 e Z( v {; o2 l- [9 U1 w1 e5 ~, B! F4 k% b usart1_sendbyte(ch);4 j( ~6 }/ V0 P* x6 t return ch; }4 z" E- D7 j p; j* y$ o4 R) y
中断函数' ^) M+ z5 B+ R( q MCU接收到数据时,进入接收中断函数,将数据存到rx_buff[100]数组里;当上位机不发送数据时,说明空闲状态,进入空闲中断,将 rx_buff[100]数组里面的数据发到上位机 ; |% `. X" r6 a /****************************) ~. s4 K3 u( x' c) } *@brief usart1中断实验+ m- E# ], X' H7 n *@param void *@return void *@note void *@time 2025-6-4 ******************************/, B6 Z" \7 h R% h6 W# f char rx_buff[1000]={0};5 }% U' k2 l( h9 V char cnt=0; void USART1_IRQHandler(void) {/ ]9 x8 N5 }& @+ B //当RXNE=1时,有数据接收# `0 i4 Q, m* P7 Q* X if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET). r8 F- a8 _0 \/ ^. |& ?; {- G { J+ J: H. |2 P' @' ^ rx_buff[cnt++]=USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位( ]( i. L2 i% d8 Y }. C" M# Y/ _7 l' Z, Y% i) n0 | & l3 N) V5 i8 g% M" Y //当IDLE=1时,; A0 f6 ], l8 R/ Z6 S if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET) { usart1_sendstr(rx_buff,strlen(rx_buff));/ D9 G, Z) B! G: c: N! _% U cnt=0; USART1->SR;7 V6 p+ h) E6 D$ ?6 W) `7 k8 M! ^3 j USART1->DR; //清除标志位 } } ———————————————— |
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之继电器模块
微信公众号
手机版