
文章目录一、通信协议1.1基本概念1.2数据帧格式1.3数据位与校验位的组合关系二、USART模块内部结构2.1模块使能与禁止总开关2.2波特率计算2.3标志位2.4数据发送与接收1初始化串口2IO引脚配置3发送4接收三、格式化打印四、常用功能封装一、通信协议1.1基本概念不同硬件设备之间传输数据需要统一的通信协议。USART串口是STM32最常用的通信外设硬件接线牢记交叉连接TX接对方RXRX接对方TXGND接对方GND即可完成双向数据收发。1.2数据帧格式一帧数据 起始位1位 数据位8~9位 校验位可选 停止位0.5/1/1.5/2位图为起始位 8位有校验 停止位起始位1bit拉低电平通知接收方开始发送数据数据位8~9bit实际传输的数据通常为8位1字节校验位0~1bit可选奇校验/偶校验/无校验停止位0.5/1/1.5/2bit拉高电平标志一帧数据结束通常为1位1.3数据位与校验位的组合关系一帧数据实际有效数据说明起始位 8位无校验 停止位1字节8bit1字节常用起始位 8位有校验 停止位7bit数据 1bit校验不足1字节较少用起始位 9位无校验 停止位9bit多于1字节较少用起始位 9位有校验 停止位1字节8bit 1bit校验1字节常用数据传输通常以1字节为单位因此最常用的是8位无校验和9位有校验。二、USART模块内部结构内部结构框图及分析2.1模块使能与禁止总开关2.2波特率计算波特率通过波特率寄存器(BRR)设置。波特率每秒钟最多传输多少位(bit/s)通信双方必须完全一致。常用波特率9600、115200、921600。公式波特率 时钟频率 ÷ 分频系数 ÷ 16举例72MHz时钟目标波特率1152002.3标志位标志位全称置1条件TxETransmit Data Register Empty - 发送数据寄存器空当TDR空时TxE1TCTransmit Complete - 发送完成当TDR空且移位寄存器空时TC1RxNEReceive Data Register Not Empty - 接收数据寄存器非空当RDR非空时,RxNE1PEParity Error - 奇偶校验错如果接收到的数据有校验错误则PE1FEFrame Error -帧格式错误接收到了无效的数据帧则FE1NENoise Error - 噪声错误接收的数据中检测到了噪声则NE1OREOverrun Error - 过载错误由于过载造成了数据丢失则ORE1查询标志位的值使用标志位TXE判断发送数据寄存器是否为空if(USART_GetFlagStatus(USART1,USART_FLAG_TXE)SET){...}TC判断数据发送是否完成if(USART_GetFlagStatus(USART1,USART_FLAG_TC)SET){...}RXNE等待接收数据寄存器RDR非空while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)RESET);2.4数据发送与接收1初始化串口例USART1波特率115200数据位8位停止位1位无校验RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);// 开启时钟USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate115200;// 波特率115200USART_InitStruct.USART_ModeUSART_Mode_Tx|USART_Mode_Rx;//双向USART_InitStruct.USART_WordLengthUSART_WordLength_8b;// 8位数据位USART_InitStruct.USART_ParityUSART_Parity_No;// 无校验USART_InitStruct.USART_StopBitsUSART_StopBits_1;// 1位停止位USART_Init(USART1,USART_InitStruct);2IO引脚配置在上一篇GPIO笔记中我们已经了解IO引脚初始化时需要配置引脚编号、最大输出速度、工作模式等参数的代码写法。下面就来讲解这些参数该如何分析、如何确定。①引脚参数的确定在进行串口USART初始化配置前需要精准确定对应TX、RX功能的引脚。单片机每一个外设功能都被厂家固定映射在指定的GPIO引脚上不能随意挑选普通IO口充当串口收发引脚。我们需要对照芯片引脚分布表查询当前芯片对应的专用TX发送引脚、RX接收引脚确认引脚编号为后续GPIO初始化、串口参数配置提供准确依据。引脚分布表查阅芯片引脚分布表后可以得到串口TX、RX对应的物理引脚分别为PA9、PA10。重映射表以上为串口默认复用引脚。在实际硬件设计中当默认的PA9、PA10引脚被其他外设占用时我们就无法继续使用这一组引脚。此时便可以查阅串口引脚重映射表使用重映射功能切换到芯片的其他GPIO引脚实现串口TX、RX功能。IO配置表确定串口所使用的引脚后还需要对引脚配置最大输出速度以及工作模式相关配置参数可查阅IO配置表得到。②引脚配置代码TX、RX对应PA9、PA10GPIO_InitTypeDef GPIO_InitStruct;//Tx PA9复用输出推挽RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStruct.GPIO_PinGPIO_Pin_9;GPIO_InitStruct.GPIO_ModeGPIO_Mode_AF_PP;//模式GPIO_InitStruct.GPIO_SpeedGPIO_Speed_10MHz;//最大速度GPIO_Init(GPIOA,GPIO_InitStruct);//Rx PA10输入浮空输入上拉RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStruct.GPIO_PinGPIO_Pin_10;GPIO_InitStruct.GPIO_ModeGPIO_Mode_IPU;//模式GPIO_Init(GPIOA,GPIO_InitStruct);TX、RX对应PB6、PB7重映像RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);// 使能AFIO模块的时钟GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);// USART1_REMAP1GPIO_InitTypeDef GPIO_InitStruct;//Tx PB6复用输出推挽RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_PinGPIO_Pin_6;GPIO_InitStruct.GPIO_ModeGPIO_Mode_AF_PP;// 模式GPIO_InitStruct.GPIO_SpeedGPIO_Speed_10MHz;// 最大速度GPIO_Init(GPIOB,GPIO_InitStruct);//Rx PB7输入浮空输入上拉RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_PinGPIO_Pin_7;GPIO_InitStruct.GPIO_ModeGPIO_Mode_IPU;// 模式GPIO_Init(GPIOB,GPIO_InitStruct);3发送发送多个字节//作用:使用串口一次性发送多个字节//参数:pData - 要发送的数据Size -字节的数量voidMy_USART_SendBytes(USART_TypeDef*USARTx,uint8_t*pData,uint16_tSize){for(uint32_ti0;iSize;i){//#1.等待发送数据寄存器空while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)RESET);// #2.将要发送的数据写入到发送数据寄存器USART_SendData(USARTx,pData[i]);}//#3.等待数据发送完成while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)RESET);}/*调用 uint8_t bytesToSend[]{1, 2, 3, 4, 5}; My_USART_SendBytes(USART1, bytesToSend, 5) */4接收串口控制LED:while(1){//#1.等待接收数据寄存器RDR非空while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)RESET);//#2.读取数据uint8_tbyteRcvdUSART_ReceiveData(USART1);//#3.对数据进行处理if(byteRcvd0)GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);// 亮灯elseif(byteRcvd1)GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//灭灯}三、格式化打印重写fputc函数intfputc(intch,FILE*f){// #1.等待发送数据寄存器为空while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET);// #2.写入发数据寄存器当中USART_SendData(USART1,(uint8_t)ch);returnch;}//调用//printf(Hello world. \r\n);格式化时间字符串while(1){uint32_tcurrentTickGetTick();uint32_tmiliSecondscurrentTick%1000;currentTickcurrentTick/1000;uint32_tsecondscurrentTick%60;currentTickcurrentTick/60;uint32_tminutescurrentTick%60;currentTickcurrentTick/60;uint32_thourcurrentTick;printf(%02u:%02u:%02u.%03u\r\n,hour,minutes,seconds,miliSeconds);Delay(100);}四、常用功能封装