|
一、USART简介' |* b6 Y) }! k$ ^6 u0 Y 1.1 UART 简介" ]5 `+ p: ^, L" d0 e 通用同步异步收发器(USART:Universal Synchronous/Asynchronous Receiver/Transmitter)( E( N: _' K! F! q5 m+ I u 特点:串行、全双工、同步/异步通信(USART可支持同步通信,UART只支持异步通信) 发送和接收共用的可编程波特率5 J& \3 R$ [ c 可编程数据字长度(8位或9位)(常用8位)" m; D' Y/ e! h) k9 t8 X 可配置的停止位-支持1或2个停止位(常用1位停止位) 可配置奇偶校验位(一般不用) Y& h4 @' l" Y5 }2 D8 Y 6 P2 n3 V' x6 V 1.2 物理拓补图 接口通过三个引脚与其他设备连接在一起。任何USART双向通信至少需要两个脚 RX:接收数据输入: l& d% W4 u4 d* H+ V4 |4 Z TX:发送数据输出 GND:双方要共地,双方通过USART进行通信,两个设备必须要共地,因为逻辑1---3.3V电压(对于地的电压),如果两个设备不共地则对地电压的高电平(3.3V就可能不太一样)。共地的意义是让两个设备的通信电平对地电压相等8 Y3 v. Q( K4 k1 ?/ n7 v 5 o1 d) C, ?2 ]6 U z6 o0 K
1.3 数据帧格式7 H% h+ b8 R2 h% P: w' { 一帧数据=1bit起始位[低电平]+8bit数据位+1bit奇偶校验位[可以不用]+1bit停止位[高电平]6 {& e7 C* e/ C) E USART总线空闲,即没有数据发生,TX线为高电平# I" ]8 d# }) ]7 }% }; B5 n <1>数据位:USART发送的1次数据量是1Byte=8bit;[5、6、7、8] <2>数据字:数据位+奇偶校验位;如果不用校验位,数据字=数据位=8bit;如果使用校验位,数据字=数据位+校验位=9bit. O2 r V! K3 v% ?* ~# Z" Y 奇校验:数据位中的1的个数+校验位中的1的个数为奇数 偶校验:数据位中的1的个数+校验位中的1的个数为偶数 <3>停止位:可以被配置为1bit(默认);2个停止位(可用于常规USART模式、单线模式以及调制解调器模式);0.5个停止位:在智能卡模式下接收数据时使用;1.5个停止位:在智能卡模式下发送和接收数据时使用2 h7 Y5 Z9 |( r+ G <4>发送顺序:先发送低位,再发送高位 1.4 框图- D/ v& }9 b& N! H4 F* x
对于这个框图,最关键的是两个标志位TXE(发送为空标志位),RXNE(接收不为空标志位)' _8 L* M* Y) F9 O% L# ` 数据通过RX端口进入MCU的UART的接收移位寄存器,随后进入接收数据寄存器(RDR),进入后RXNE被置1,表明可以读5 I9 |0 A6 e4 q8 F MCU通过数据总线获得数据,数据再到发送数据寄存器(TDR),若TXE=1,说明数据已经被转移到移位寄存器,表明可以发送5 C W/ c! B3 u. y7 |$ b 二、串口通信 配置UART * X4 P# g6 s8 Q! V4 X7 a3 o 开启了接收中断和空闲中断6 A) t3 H- _0 k1 m5 p0 ]1 B7 y+ S 4 R7 S+ }; Y3 Y+ y$ q) u /****************************" W" k2 Y4 C5 Y *@brief uasrt1端口配置函数, F! {7 u' I' L* d9 ^3 G- D *@param void *@return void! d. B+ I: ?' {4 J9 L6 W. s *@NOTE PA10->RX PA9->TX' I' k( k5 i' I6 | PA10,PA9位于APB2上0 y D0 p A5 _: v% k0 O *@time 2025-6-3& ^/ X3 l7 v" c+ ?7 { ******************************/' s$ q$ _5 n1 q: Q# B0 q: R void usart1_Init(uint32_t Baud_Rate)' N. `4 e t; W- B {0 H: ]9 {* ?7 `4 L$ |# l1 W //1.打开引脚时钟PA RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE) ;1 L* ]/ V+ ]: b, g. G: J 8 S8 @' _1 N( b3 T' D //2.打开usart1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) ;/ _( f F) K# @ //PA9->TX推挽复用输出2 h+ P8 g9 R8 S$ a" j- ^, F6 P6 ?8 z! X/ q GPIO_InitTypeDef GPIO_InitStruct_TX ;+ ?3 b4 r: }+ R GPIO_InitStruct_TX.GPIO_Mode = GPIO_Mode_AF_PP ;' y8 w# D% X; h5 D GPIO_InitStruct_TX.GPIO_Pin = GPIO_Pin_9 ; GPIO_InitStruct_TX.GPIO_Speed = GPIO_Speed_10MHz ;7 d: `8 J4 d8 u+ L* }& Y GPIO_Init(GPIOA,&GPIO_InitStruct_TX) ; 2 }9 n& _" Y/ F: [8 t) t1 _ //3.配置,PA10->RX浮空输入; S q; e) V7 ^! B6 i% k( R GPIO_InitTypeDef GPIO_InitStruct_RX ; GPIO_InitStruct_RX.GPIO_Mode = GPIO_Mode_IN_FLOATING ; GPIO_InitStruct_RX.GPIO_Pin = GPIO_Pin_10 ;+ Y5 r/ [1 q, F" N/ j GPIO_Init(GPIOA,&GPIO_InitStruct_RX) ;0 X ]8 @! |# l$ Y. Z //4.配置usart1,! f. f; a( W3 w4 R) w" C USART_InitTypeDef USART_InitStruct ; USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx ;: @4 n5 T' N9 `* D USART_InitStruct.USART_Parity = USART_Parity_No ; USART_InitStruct.USART_StopBits = USART_StopBits_1 ;; }! e5 o4 ?" x USART_InitStruct.USART_BaudRate = Baud_Rate ; USART_InitStruct.USART_WordLength = USART_WordLength_8b ; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ; USART_Init(USART1,&USART_InitStruct) ;5 l6 y" M8 p0 A& P6 Y6 s. k 8 E; D. B" B1 T. k9 `3 J / ?6 l+ E2 j6 N$ M USART_ITConfig(USART1,USART_IT_RXNE,ENABLE) ;//开启接收中断 RXNEIE=1: L6 U* Q8 k- u) y2 W. C USART_ITConfig(USART1,USART_IT_IDLE,ENABLE) ;//开启空闲中断 IDLE=1 //初始化NVIC5 l* i% o+ y1 G6 W( A% \" T! V! c NVIC_InitTypeDef NVIC_InitStruct ;' i, M, d) v- u9 H: E4 J5 i; x NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn ;//中断通道 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;//中断使能* v$ C, j2 h8 k NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2 ;//响应先级: g* \% S# q5 G. s6 A NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;//抢占优先级: m& q$ w' D2 k NVIC_Init(&NVIC_InitStruct) ; //5.使能usart18 N3 U7 b$ Y* K3 P# Q3 U9 P USART_Cmd(USART1,ENABLE) ; } ) n. m% j/ M7 A$ @- B3 z1 G 接收发送函数 ' B: m+ N) X2 V- p /**************************** *@brief 发送字符函数 *@param void *@return void" B% R6 }0 q, i& a6 K( B *@note MCU会将数据发送到DR寄存器中的TDR寄存器中,: O, k& U1 ^# `& h 当TXE==1时,TDR寄存器会将数据加载到移位寄存器中,可以发送 *@time 2025-6-3 ******************************/3 ~; b# F/ D( w$ x void usart1_sendbyte(uint8_t str) { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==0); USART_SendData(USART1,str);, ~' {( ]. P7 [9 V; L# e }2 Y! t% i% O0 c/ z 5 y9 P/ N* }1 ]2 ~% i7 D /****************************; \+ V+ [( w! U- {1 R/ m/ z *@brief 发送字符串函数" O8 S2 W, k! F J *@param uint8_t *str *@return uint16_t len) d- u- B% J) ]; l( ? A *@note 发送字符串本质上就是发送字符 *@time 2025-6-3. o( `8 E- i( g0 j7 T! [ ******************************/% R9 F1 f Z( H4 U' ^ void usart1_sendstr(char *str,int len)$ O: {; H+ v7 K' ~- ^8 v% t& r {, f4 u+ ~) d1 o9 L. l, I while(len)* t; R7 ?* x5 v$ W2 Q { usart1_sendbyte(*str++); len--; }* U+ U/ J4 ]2 h2 V, I7 d4 ~# a } /**************************** *@brief 接收字符 *@param void *@return void *@note MCU接收到数据后先放在DR寄存器的接收移位寄存器 数据会到RDR寄存器中,当RXNE==1时,说明RDR有数据,可以读 *@time 2025-6-3) F' F$ C3 V2 w ******************************/ uint16_t usart1_receivebyte() {; D% J% u+ Q( N& r! i# K, l: R! J while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==0); return USART_ReceiveData(USART1);! u1 k4 u1 B8 E' j0 h$ K: t4 F9 f1 E' k8 s } /**************************** *@brief 接收字符串函数(idle标志位)4 G) B2 ]2 b3 r# A1 V *@param void7 W3 e( f3 R, D' g/ o6 w *@return void *@note void% X; F$ t1 |/ Q/ M" x *@time 2025-6-3# @1 D1 E3 O5 h3 C ******************************/ void usart1_receivestr(char*buff)4 e8 k5 V# W) i) C9 a n3 Y {0 z) i6 {! H. G uint8_t flag; while(1). J' t' R1 H! J- h: q4 E {8 F3 w: n( E4 X9 ` if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1)6 }/ x | Q x+ Y1 w: { { *buff=USART_ReceiveData(USART1);& C8 C2 p0 K$ x3 w, N5 t+ n/ H( w buff++;/ n6 c. F- p5 f3 j flag=1; }/ I2 E% t6 H. V- ^: p8 x9 e9 R if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)==1&&flag); X! y5 m" c3 ^2 q, c4 L3 k6 z% }* I# @ { *buff='\0'; //当发送时 发送结束标志 //先读SR,再读DR将置IDLE位置0 USART1->SR;8 D. ^5 Y, m- h; u w USART1->DR; break;3 K- V' _# E \# n } }: y) `& }- w/ X8 h9 g( _ } ( M2 T# f {/ }, v& M2 j' b 打印函数 2 l$ q8 D" T/ ] |% h6 h4 n 记得勾选微库,printf函数不涉及到中断,它是将内存中的数据输出到上位机. m7 Z* J. v2 k% x8 _2 }3 v" j3 |4 I //printf()的底层借助fputc函数进行输出+ N/ O$ k2 \0 H+ p0 }. s" M int fputc(int ch,FILE*stream)2 z* t( S5 u+ {% ^8 p {0 i, ]* D) P/ d( u7 ^$ J usart1_sendbyte(ch);2 d9 z" \, }7 p return ch;( X2 P- U) h# v- ~; E( x S( y } . n+ o2 z+ ]9 J' b& o 9 V3 k% B% Q5 ^/ P7 R
中断函数9 [# R! T* ~8 I5 D9 p7 t MCU接收到数据时,进入接收中断函数,将数据存到rx_buff[100]数组里;当上位机不发送数据时,说明空闲状态,进入空闲中断,将 rx_buff[100]数组里面的数据发到上位机 /****************************: l2 w# O1 P4 H7 u8 S *@brief usart1中断实验 *@param void7 d. L3 }( O7 |9 j0 [: A *@return void, R L9 J7 t, ^+ ] *@note void) b, s8 j- T; r: r# X! E *@time 2025-6-4 ******************************/7 F* M. A2 s0 j1 b \- G char rx_buff[1000]={0};: d0 K/ N% G5 l1 Y7 Q char cnt=0;5 G9 d @8 \- @ void USART1_IRQHandler(void) {$ @( H- k! p0 [3 a4 p. M //当RXNE=1时,有数据接收 if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET) {+ h, d; J H! a/ I2 ^7 m$ y( _ rx_buff[cnt++]=USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位 }4 T( L) z' B+ H9 ~ 9 |) Q8 ` h8 V$ O5 T4 w //当IDLE=1时,0 _. s% R0 c- M( R if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)9 y2 Y7 U% o$ L+ i0 R/ M { usart1_sendstr(rx_buff,strlen(rx_buff)); cnt=0;/ f" W( J, p1 g9 b& D% W* U USART1->SR; USART1->DR; //清除标志位 }9 z1 K: H# d/ ^) t8 ^ }& i$ S) p V& |9 ^ D+ O ) E! |: d# W: |; `1 c0 F ! c! l* Z( O1 m' _- a2 e3 u# E ————————————————6 i& H. ]4 F; ^- 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之继电器模块
微信公众号
手机版