简介基于STM32标准库主要涉及初始化、收发原理、中断收发、常用配置。STM32F103 有2个USART其中USART1位于APB2总线USART2/3是APB1总线支持同步/异步模式常用异步串口UART模式。参考江科大视频如有错误欢迎指正侵删一、串口核心知识点1、总线时钟USART1挂APB2预分频器不分频72MHz,串口时钟APB2时钟。USART2/3挂APB1APB1最大36MHz串口时钟APB1时钟*2APB1预分频不为1时加倍。STM32标准库自动计算波特率分频值不用手动算。2、串口引脚复用功能默认复用引脚USART1TX-PA9、RX-PA10USART2TX-PA2 、RX-PA3USART3TX-PB10、RX-PB11注必须开启GPIO复用时钟USART外设时钟GPIO配置为复用推挽输出。3、串口1工作模式异步串口只需要TX发送、RX接收2根线无时钟线。帧格式起始位1bit 数据位8/9bit 停止位0.5/1.5/2bit 奇偶检验可选常见配置8N19600/115200波特率。2硬件注RX与Tx交叉连接只需担心数据传输可以只接一根线。电平标准不一致时加电平转换芯片。3常用串口电平标准如下TTL电平3.3V或5V表示10V表示0最常见单片机常用RS232电平-3~-15V表示13~15V表示0RS485电平两线压差2~6V表示1-2~-6V表示4串口参数及时序波特率串口通信速率起始位标志一个数据帧的开始固定为低电平数据位数据帧的有效载荷1为高电平0为低电平校验位数据验证根据数据位计算停止位数据帧间隔固定位高电平建议不需要校验位选8位数据需要选9位数据。二、标准库库函数1、USART初始化结构体 USART_InitTypeDef()typedef struct{ uint32_t USART_BaudRate;//波特率 uint16_t USART_WordLength;/数据位长度 uint16_t USART_StopBits;//停止位 uint16_t USART_Parity; //奇偶校验 uint16_t USART_Mode;//收发模式只发/只收/收发 uint16_t USART_HardwareFlowControl;//硬件流控制一般关闭 }USART_InitTypeDef;2、常用宏定义USART_WordLength_8b //8位数据 USART_StopBits_1 //1位停止位 USART_Parity_No //无校验 USART_Mode_Tx |USART_Mode_Rx //收发全开 USART_HardwareFlowControl_None //关闭硬件流控制3、中断配置结构体USART_ITConfig串口常用中断USART_IT_RXNE接收数据寄存器非空中断收到一个字节就触发中断最常用USART_IT_TXE发送数据寄存器空中断USART_IT_TC发送完成中断4、关键库函数USART_Init(USART_TypeDef* USARTx,USART_InitTypeDef*USART_InitStruct);//串口初始化函数 USART_Cmd(USART_TypeDef*USARTx,FunctionState NewState); //使能/关闭串口外设 USART_SendData(USART_TypeDef*USARTx,uint16_t Data);//发送一个字节 uint16_t USART_ReceiveData(USART_TypeDef*USARTx);//读取接收到的一个字节 FlagStatus USART_GetFlagStatus(USART_TypeDef*USARTx,uint16_t USART_FlAG);//查询标志位 void USART_ITConfig(USART_TypeDef*USARTx ,uint16_t USART_IT,FunctionState NewState)//开启串口对应中断USART_FLAG_RXNE(Read data register not empty)读数据寄存器非空接收标志有数据可读USART_FLAG_TXE(Transmit data register empty)发送数据寄存器空可以写数据USART_FLAG_TC(Transmission complete)一帧数据全部发送完成三、收发过程1、概述单片机串口硬件独立工作CPU只管写数据、读数据收发是USART外设自行移位处理不用CPU一位位搬运。分2部分单片机发送数据、电脑/外设给单片机发送数据单片机接收。2、发送过程Tx接PA9寄存器核心DR数据寄存器、TXE标志位、移位寄存器。步骤1CPU执行 USART_SendData(USART1,data);函数本质把你传入的8位数据写入USART的DR数据保持寄存器。此时移位寄存器是空的硬件自动把DR里的数据搬到移位寄存器开始一位位往外发送。步骤2TXE标志位置1数据从DR搬走后硬件自动置位USART_FLAG_TXE。含义DR寄存器已经空了可以再写下一个字节。查询发送while(!TXE)或者while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET);就是等待这个标志等它置1就可以发送下一个字节。如果开启中断USART_ITConfig(USART1,USART_IT_TXEENABLE)此时会触发发送空中断。步骤3移位寄存器自动串行输出USART硬件自行处理时序先发一位起始位低电平→依次发送8位数据位→1位停止位高电平波特率时钟由外设自己产生CPU不用管。一帧数据全部发送完毕后硬件置位 TC发送完成标志位。整个流程CPU写DR→DR数据送入移位寄存器→TXE置1→硬件串行移位输出引脚TX→全部发送完TC置1。3、单片机接收过程Rx引脚PA10外设主动发送CPU步骤1、外部设备电脑串口助手往Rx引脚发高低电平Rx引脚不断检测电平变化当检测到一个下降沿起始位串口外设开始同步接收。步骤2、硬件移位采样数据USART按照波特率连续采样8位数据存入内部移位寄存器8位数据全部收齐之后硬件自动把移位寄存器的数据复制到DR数据寄存器。步骤3、硬件置位RXNE标志位。DR寄存器装入新数据后硬件自动置位USART_FLAG_RXNE即接收寄存器非空有新数据CPU要来读取。此时2种方式可以实现1)只查询方式没开中断不用USART_ITConfigUSART_GetFlagStatus(USART1,USART_FLAG_RXNE)查询到标志为1就去读DR寄存器。2开启RXNE接收中断使用USART_ITConfigRXNE置1由于开启了中断串口外设立即向NVIC发出中断请求NVIC已经提前配置USART1中断CPU暂停主循环程序跳入固定中断函数USART1_IRQHandler()步骤4、CPU读取DR数据函数USART_ReceiveData()读取DR这个读操作硬件会自动清除RXNE标志位。标志位清零后串口外设才可以继续接收下一个字节。注意如果进入中断没有用USART_ReceiveData()读取DRRXNE标志一直是1会一直不断重复进入中断卡死程序。综上接收过程是外部电平→Rx引脚硬件移位接收→数据存入DR→RXNE置1→开启中断就触发USART1中断→CPU进中断服务函数→读取DR→硬件自动清除RXNE标志位→等待下一个字节。四、代码编写配置STM32F103C8T672MHz9600bit/sPA9-TxPA10-Rx练习1、只发送数据Tx--PA91、开启时钟,GPIO配置为复用推挽输出void Serial_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //GPIO初始化 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); //USART 初始化并使能 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_Init(USART1,USART_InitStructure); USART_Cmd(USART1,ENABLE); }2、发送一个字节和发送字符串函数先发送低位void Serial_SendByte(uint8_t Byte) { USART_SendData(USART1,Byte); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET); } //发送字符串 void Serial_SendString(char* String) { while(*String ) { Serial_SendByte(*String); } }3、测试#include stm32f10x.h #include Delay.h #include OLED.h #include Serial.h int main() { OLED_Init(); Serial_Init(); Serial_SendByte(0x34); Serial_SendString(hello\r\n); while(1) { } }练习2收发数据1、开启时钟,GPIO配置PA9复用推挽输出PA10浮空输入void Serial_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin GPIO_Pin_10 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx |USART_Mode_Rx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_Init(USART1,USART_InitStructure); USART_Cmd(USART1,ENABLE); }2、接收数据并发送在OLED上显示方式1接步骤1的代码采用查询的方式不断检测USART_FLAG_RXNE标志位置1就读取数据。#include stm32f10x.h // Device header #include Delay.h #include OLED.h #include Serial.h uint8_t Data; int main() { OLED_Init(); Serial_Init(); while(1) { if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) SET) { Data USART_ReceiveData(USART1); Serial_SendByte(Data); OLED_ShowHexNum(1,1,Data,2); } } }方法2、中断方式接收项目常用原理外设设备发来一个字节硬件置位RXNE标志触发USART1中断进入中断函数读取数据。完整代码如下Serial.c代码#include Serial.h uint8_t Receive_Flag,Data; void Serial_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin GPIO_Pin_10 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority 2; NVIC_Init(NVIC_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx |USART_Mode_Rx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_Init(USART1,USART_InitStructure); USART_Cmd(USART1,ENABLE); } void Serial_SendByte(uint8_t Byte) { USART_SendData(USART1,Byte); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET); } void Serial_SendString(char* String) { while(*String ) { Serial_SendByte(*String); } } uint8_t Serial_RxFlag1() { if(Receive_Flag 1) { Receive_Flag 0; return 1; } return 0; } uint8_t Rx_GetData1() { return Data; } void USART1_IRQHandler() { if(USART_GetITStatus(USART1,USART_IT_RXNE) SET) { Data USART_ReceiveData(USART1); Receive_Flag 1; USART_ClearITPendingBit(USART1,USART_IT_RXNE); } }main.c代码#include stm32f10x.h // Device header #include Delay.h #include OLED.h #include Serial.h uint8_t GetData; int main() { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); OLED_Init(); Serial_Init(); while(1) { if(Serial_RxFlag1()1) { GetData Rx_GetData1(); Serial_SendByte(GetData); OLED_ShowHexNum(1,1,GetData,2); } } }初始化时也可以将波特率设为变量 uint32_t BaudRate 。总结标准库配置流程中断1、开启GPIO时钟、开串口外设时钟2、GPIO配置Tx复用推挽Rx浮空输入3、USART1结构体初始化波特率、8N1格式4、开启外设中内部断使能USART_ITConfig必写5、NVIC优先级分组设置一次全局即可6、NVIC通道初始化打开CPU中断响应7、使能USART外设8、编写中断服务函数五、关于中断USART_ITConfig1、USART_ITConfig()是单独打开串口某一个中断开关外设本身有中断功能默认是关闭这个函数就是打开对应的中断使能开关不写的话硬件不会触发串口中断。STM32有2道开关都得开启缺一不可开关1外设内部中断使能 USART_ITConfig()有很多中断源默认都关闭。给串口外设开门比如USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);就是告诉USART1外设收到数据RXNE事件发生要向NVIC中断控制器发出中断请求信号。开关2NVIC总开关---NVIC_Init() 3、给CPU开门NVIC是芯片统一中断控制器配置NVIC是允许CPU响应这个中断通道。总之USART_ITConfig()允许外设产生中断请求→NVIC_Init()CPU允许响应这个请求。2、USART_ITConfig()第2个参数参数含义USART_IT_RXNE接收数据寄存器非空中断USART_IT_TXE发送数据寄存器空中断USART_IT_TC一帧全部发送完成中断USART_IT_PE奇偶检验错误中断3、三种情况要不要写USART_ITConfig()1查询方式接收while轮询标志位不用写2开启串口Rx接收中断必须写3只用串口发送不开启任何中断不用写六、printf重定向1、原理printf()底层会调用一个标准库函数fputc()默认这个函数往电脑控制台输出我们要重写这个函数让它把每一个字符通过串口发送出去。2、开启步骤步骤1、在keil里魔术棒→Target勾选UseMircroLIB否则printf无法输出。步骤2、在串口初始化的c文件中编写重定向即可。本案例是Serial.c包含头文件stdio.h#include stdio.h int fputc(int ch,FILE*f) { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET); USART_SendData(USART1,ch); return ch; }这样就定向到了串口1。3、注意事项1printf内部是等待TXE标志逐个发送字符会阻塞CPU允许。2不要在中断服务函数里调用printf。中断里运行printf会造成程序卡死、时序混乱。3主循环里随便使用printf但打印大量数据会占用CPU时间。4、printf重定向什么时候用什么时候不用使用场景1、调试程序查看变量数值程序运行状态比如可直接写printf(温度%d\r\n,temp);串口助手直接看到打印的数值等。场景2快速打印字符串数字混合内容不然打印数字很麻烦要拆分百、十、个位等场景3工程复杂多处需打印日志信息。程序模块多采集、通讯、电机控制等多处需要输出信息统一printf代码简洁。不使用场景1产品已经调试完毕关闭printf接收flash空间。场景2只用串口简单收发指令只接收上位机命令不需要打印日志。场景3单片机资源紧张。
STM32学习10---串口
发布时间:2026/7/5 14:11:30
简介基于STM32标准库主要涉及初始化、收发原理、中断收发、常用配置。STM32F103 有2个USART其中USART1位于APB2总线USART2/3是APB1总线支持同步/异步模式常用异步串口UART模式。参考江科大视频如有错误欢迎指正侵删一、串口核心知识点1、总线时钟USART1挂APB2预分频器不分频72MHz,串口时钟APB2时钟。USART2/3挂APB1APB1最大36MHz串口时钟APB1时钟*2APB1预分频不为1时加倍。STM32标准库自动计算波特率分频值不用手动算。2、串口引脚复用功能默认复用引脚USART1TX-PA9、RX-PA10USART2TX-PA2 、RX-PA3USART3TX-PB10、RX-PB11注必须开启GPIO复用时钟USART外设时钟GPIO配置为复用推挽输出。3、串口1工作模式异步串口只需要TX发送、RX接收2根线无时钟线。帧格式起始位1bit 数据位8/9bit 停止位0.5/1.5/2bit 奇偶检验可选常见配置8N19600/115200波特率。2硬件注RX与Tx交叉连接只需担心数据传输可以只接一根线。电平标准不一致时加电平转换芯片。3常用串口电平标准如下TTL电平3.3V或5V表示10V表示0最常见单片机常用RS232电平-3~-15V表示13~15V表示0RS485电平两线压差2~6V表示1-2~-6V表示4串口参数及时序波特率串口通信速率起始位标志一个数据帧的开始固定为低电平数据位数据帧的有效载荷1为高电平0为低电平校验位数据验证根据数据位计算停止位数据帧间隔固定位高电平建议不需要校验位选8位数据需要选9位数据。二、标准库库函数1、USART初始化结构体 USART_InitTypeDef()typedef struct{ uint32_t USART_BaudRate;//波特率 uint16_t USART_WordLength;/数据位长度 uint16_t USART_StopBits;//停止位 uint16_t USART_Parity; //奇偶校验 uint16_t USART_Mode;//收发模式只发/只收/收发 uint16_t USART_HardwareFlowControl;//硬件流控制一般关闭 }USART_InitTypeDef;2、常用宏定义USART_WordLength_8b //8位数据 USART_StopBits_1 //1位停止位 USART_Parity_No //无校验 USART_Mode_Tx |USART_Mode_Rx //收发全开 USART_HardwareFlowControl_None //关闭硬件流控制3、中断配置结构体USART_ITConfig串口常用中断USART_IT_RXNE接收数据寄存器非空中断收到一个字节就触发中断最常用USART_IT_TXE发送数据寄存器空中断USART_IT_TC发送完成中断4、关键库函数USART_Init(USART_TypeDef* USARTx,USART_InitTypeDef*USART_InitStruct);//串口初始化函数 USART_Cmd(USART_TypeDef*USARTx,FunctionState NewState); //使能/关闭串口外设 USART_SendData(USART_TypeDef*USARTx,uint16_t Data);//发送一个字节 uint16_t USART_ReceiveData(USART_TypeDef*USARTx);//读取接收到的一个字节 FlagStatus USART_GetFlagStatus(USART_TypeDef*USARTx,uint16_t USART_FlAG);//查询标志位 void USART_ITConfig(USART_TypeDef*USARTx ,uint16_t USART_IT,FunctionState NewState)//开启串口对应中断USART_FLAG_RXNE(Read data register not empty)读数据寄存器非空接收标志有数据可读USART_FLAG_TXE(Transmit data register empty)发送数据寄存器空可以写数据USART_FLAG_TC(Transmission complete)一帧数据全部发送完成三、收发过程1、概述单片机串口硬件独立工作CPU只管写数据、读数据收发是USART外设自行移位处理不用CPU一位位搬运。分2部分单片机发送数据、电脑/外设给单片机发送数据单片机接收。2、发送过程Tx接PA9寄存器核心DR数据寄存器、TXE标志位、移位寄存器。步骤1CPU执行 USART_SendData(USART1,data);函数本质把你传入的8位数据写入USART的DR数据保持寄存器。此时移位寄存器是空的硬件自动把DR里的数据搬到移位寄存器开始一位位往外发送。步骤2TXE标志位置1数据从DR搬走后硬件自动置位USART_FLAG_TXE。含义DR寄存器已经空了可以再写下一个字节。查询发送while(!TXE)或者while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET);就是等待这个标志等它置1就可以发送下一个字节。如果开启中断USART_ITConfig(USART1,USART_IT_TXEENABLE)此时会触发发送空中断。步骤3移位寄存器自动串行输出USART硬件自行处理时序先发一位起始位低电平→依次发送8位数据位→1位停止位高电平波特率时钟由外设自己产生CPU不用管。一帧数据全部发送完毕后硬件置位 TC发送完成标志位。整个流程CPU写DR→DR数据送入移位寄存器→TXE置1→硬件串行移位输出引脚TX→全部发送完TC置1。3、单片机接收过程Rx引脚PA10外设主动发送CPU步骤1、外部设备电脑串口助手往Rx引脚发高低电平Rx引脚不断检测电平变化当检测到一个下降沿起始位串口外设开始同步接收。步骤2、硬件移位采样数据USART按照波特率连续采样8位数据存入内部移位寄存器8位数据全部收齐之后硬件自动把移位寄存器的数据复制到DR数据寄存器。步骤3、硬件置位RXNE标志位。DR寄存器装入新数据后硬件自动置位USART_FLAG_RXNE即接收寄存器非空有新数据CPU要来读取。此时2种方式可以实现1)只查询方式没开中断不用USART_ITConfigUSART_GetFlagStatus(USART1,USART_FLAG_RXNE)查询到标志为1就去读DR寄存器。2开启RXNE接收中断使用USART_ITConfigRXNE置1由于开启了中断串口外设立即向NVIC发出中断请求NVIC已经提前配置USART1中断CPU暂停主循环程序跳入固定中断函数USART1_IRQHandler()步骤4、CPU读取DR数据函数USART_ReceiveData()读取DR这个读操作硬件会自动清除RXNE标志位。标志位清零后串口外设才可以继续接收下一个字节。注意如果进入中断没有用USART_ReceiveData()读取DRRXNE标志一直是1会一直不断重复进入中断卡死程序。综上接收过程是外部电平→Rx引脚硬件移位接收→数据存入DR→RXNE置1→开启中断就触发USART1中断→CPU进中断服务函数→读取DR→硬件自动清除RXNE标志位→等待下一个字节。四、代码编写配置STM32F103C8T672MHz9600bit/sPA9-TxPA10-Rx练习1、只发送数据Tx--PA91、开启时钟,GPIO配置为复用推挽输出void Serial_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //GPIO初始化 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); //USART 初始化并使能 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_Init(USART1,USART_InitStructure); USART_Cmd(USART1,ENABLE); }2、发送一个字节和发送字符串函数先发送低位void Serial_SendByte(uint8_t Byte) { USART_SendData(USART1,Byte); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET); } //发送字符串 void Serial_SendString(char* String) { while(*String ) { Serial_SendByte(*String); } }3、测试#include stm32f10x.h #include Delay.h #include OLED.h #include Serial.h int main() { OLED_Init(); Serial_Init(); Serial_SendByte(0x34); Serial_SendString(hello\r\n); while(1) { } }练习2收发数据1、开启时钟,GPIO配置PA9复用推挽输出PA10浮空输入void Serial_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin GPIO_Pin_10 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx |USART_Mode_Rx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_Init(USART1,USART_InitStructure); USART_Cmd(USART1,ENABLE); }2、接收数据并发送在OLED上显示方式1接步骤1的代码采用查询的方式不断检测USART_FLAG_RXNE标志位置1就读取数据。#include stm32f10x.h // Device header #include Delay.h #include OLED.h #include Serial.h uint8_t Data; int main() { OLED_Init(); Serial_Init(); while(1) { if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) SET) { Data USART_ReceiveData(USART1); Serial_SendByte(Data); OLED_ShowHexNum(1,1,Data,2); } } }方法2、中断方式接收项目常用原理外设设备发来一个字节硬件置位RXNE标志触发USART1中断进入中断函数读取数据。完整代码如下Serial.c代码#include Serial.h uint8_t Receive_Flag,Data; void Serial_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin GPIO_Pin_10 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority 2; NVIC_Init(NVIC_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx |USART_Mode_Rx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_Init(USART1,USART_InitStructure); USART_Cmd(USART1,ENABLE); } void Serial_SendByte(uint8_t Byte) { USART_SendData(USART1,Byte); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET); } void Serial_SendString(char* String) { while(*String ) { Serial_SendByte(*String); } } uint8_t Serial_RxFlag1() { if(Receive_Flag 1) { Receive_Flag 0; return 1; } return 0; } uint8_t Rx_GetData1() { return Data; } void USART1_IRQHandler() { if(USART_GetITStatus(USART1,USART_IT_RXNE) SET) { Data USART_ReceiveData(USART1); Receive_Flag 1; USART_ClearITPendingBit(USART1,USART_IT_RXNE); } }main.c代码#include stm32f10x.h // Device header #include Delay.h #include OLED.h #include Serial.h uint8_t GetData; int main() { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); OLED_Init(); Serial_Init(); while(1) { if(Serial_RxFlag1()1) { GetData Rx_GetData1(); Serial_SendByte(GetData); OLED_ShowHexNum(1,1,GetData,2); } } }初始化时也可以将波特率设为变量 uint32_t BaudRate 。总结标准库配置流程中断1、开启GPIO时钟、开串口外设时钟2、GPIO配置Tx复用推挽Rx浮空输入3、USART1结构体初始化波特率、8N1格式4、开启外设中内部断使能USART_ITConfig必写5、NVIC优先级分组设置一次全局即可6、NVIC通道初始化打开CPU中断响应7、使能USART外设8、编写中断服务函数五、关于中断USART_ITConfig1、USART_ITConfig()是单独打开串口某一个中断开关外设本身有中断功能默认是关闭这个函数就是打开对应的中断使能开关不写的话硬件不会触发串口中断。STM32有2道开关都得开启缺一不可开关1外设内部中断使能 USART_ITConfig()有很多中断源默认都关闭。给串口外设开门比如USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);就是告诉USART1外设收到数据RXNE事件发生要向NVIC中断控制器发出中断请求信号。开关2NVIC总开关---NVIC_Init() 3、给CPU开门NVIC是芯片统一中断控制器配置NVIC是允许CPU响应这个中断通道。总之USART_ITConfig()允许外设产生中断请求→NVIC_Init()CPU允许响应这个请求。2、USART_ITConfig()第2个参数参数含义USART_IT_RXNE接收数据寄存器非空中断USART_IT_TXE发送数据寄存器空中断USART_IT_TC一帧全部发送完成中断USART_IT_PE奇偶检验错误中断3、三种情况要不要写USART_ITConfig()1查询方式接收while轮询标志位不用写2开启串口Rx接收中断必须写3只用串口发送不开启任何中断不用写六、printf重定向1、原理printf()底层会调用一个标准库函数fputc()默认这个函数往电脑控制台输出我们要重写这个函数让它把每一个字符通过串口发送出去。2、开启步骤步骤1、在keil里魔术棒→Target勾选UseMircroLIB否则printf无法输出。步骤2、在串口初始化的c文件中编写重定向即可。本案例是Serial.c包含头文件stdio.h#include stdio.h int fputc(int ch,FILE*f) { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET); USART_SendData(USART1,ch); return ch; }这样就定向到了串口1。3、注意事项1printf内部是等待TXE标志逐个发送字符会阻塞CPU允许。2不要在中断服务函数里调用printf。中断里运行printf会造成程序卡死、时序混乱。3主循环里随便使用printf但打印大量数据会占用CPU时间。4、printf重定向什么时候用什么时候不用使用场景1、调试程序查看变量数值程序运行状态比如可直接写printf(温度%d\r\n,temp);串口助手直接看到打印的数值等。场景2快速打印字符串数字混合内容不然打印数字很麻烦要拆分百、十、个位等场景3工程复杂多处需打印日志信息。程序模块多采集、通讯、电机控制等多处需要输出信息统一printf代码简洁。不使用场景1产品已经调试完毕关闭printf接收flash空间。场景2只用串口简单收发指令只接收上位机命令不需要打印日志。场景3单片机资源紧张。