哪些网站推广公司,网站做弹幕广告,企业网站备案需要法人拍照吗,花2w学ui值得吗本篇文章包含的内容 一、STM32的USART外设1.1 STM32的USAER外设简介1.2 USART外设的结构和工作原理1.3 串口通信数据帧1.4 起始位侦测和USART的噪声判断机制1.5 波特率发生器 二、串口发送和接收数据包2.1 HEX数据包2.2 文本数据包2.3 固定包长HEX数据包接收2.4 可变包长文本数… 本篇文章包含的内容 一、STM32的USART外设1.1 STM32的USAER外设简介1.2 USART外设的结构和工作原理1.3 串口通信数据帧1.4 起始位侦测和USART的噪声判断机制1.5 波特率发生器 二、串口发送和接收数据包2.1 HEX数据包2.2 文本数据包2.3 固定包长HEX数据包接收2.4 可变包长文本数据包接收 三、代码实现3.1 常用库函数3.2 使用串口发送数据3.3 使用串口接收数据3.4 串口收发HEX数据包3.5 串口发送文本数据包实现简单的人机交互 本次课程采用单片机型号为STM32F103C8T6。 课程链接江协科技 STM32入门教程 往期笔记链接 STM32学习笔记一丨建立工程丨GPIO 通用输入输出 STM32学习笔记二丨STM32程序调试丨OLED的使用 STM32学习笔记三丨中断系统丨EXTI外部中断 STM32学习笔记四丨TIM定时器及其应用定时中断、内外时钟源选择 STM32学习笔记五丨TIM定时器及其应用输出比较丨PWM驱动呼吸灯、舵机、直流电机 STM32学习笔记六丨TIM定时器及其应用输入捕获丨测量PWM波形的频率和占空比 STM32学习笔记七丨TIM定时器及其应用编码器接口丨用定时器实现编码器测速 STM32学习笔记八丨ADC模数转换器ADC单、双通道转换 STM32学习笔记九丨DMA直接存储器存取DMA数据转运、DMAAD多通道转换 STM32学习笔记十丨I2C通信使用I2C实现MPU6050和STM32之间通信 STM32学习笔记十一丨SPI通信W25Q64芯片简介使用SPI读写W25Q64存储器芯片 STM32学习笔记十二丨RTC实时时钟 一、STM32的USART外设
1.1 STM32的USAER外设简介 USARTUniversal Synchronous/Asynchronous Receiver/Transmitter通用同步/异步收发器。USART是STM32内部集成的硬件外设可根据数据寄存器的一个字节数据自动生成数据帧时序从TX引脚发送出去也可自动接收RX引脚的数据帧时序拼接为一个字节数据存放在数据寄存器里。 自带波特率发生器最高达4.5Mbits/s 可配置数据位长度8/9包含校验位的长度、停止位长度0.5/1/1.5/2 可选校验位无校验/奇校验/偶校验 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN 同步模式STM32的同步模式只有时钟输出没有时钟输入所以并不是完全的同步通信模式。硬件流控制由一根专用的状态线来接收数据流控制信号接收的一方会将准备好信号通过状态线反馈给发送方发送方只在接收方准备好后才开始发送数据可以解决由于接收方处理速度慢而导致的数据丢失的问题。DMA串口支持DMA转运数据。智能卡、IrDA、LIN这些通信协议和串口非常相似在串口的基础上稍作配置即可实现这些协议的通信。 STM32F103C8T6 USART资源 USART1APB2、 USART2、 USART3APB1
1.2 USART外设的结构和工作原理 在USART外设中发送数据寄存器TDR只写和接收数据寄存器RDR只读公用同一个地址。发送数据寄存器TDR将数据转运到发送移位寄存器后会置TxE标志位为1我们检测这个标志位就可以写入下一个数据了这样可以保证数据的连续不间断的传输提高了串口传输数据的工作效率同理当接收移位寄存器接收到一个完整的数据后就会把一个字节的数据整体转移到接收数据寄存器RDR中同时置RxNE标志位为1读取并判断这个标志位就可以及时读走数据。 关于硬件流控制nRTSRequest To Send是请求发送引脚它是一个输出引脚用于告诉对方当前我是否能够接收nCTSClear To Send是清除发送引脚清除发送数据寄存器意思即将数据发送出来是一个输入硬件用来接收对方的nRTS信号。 STM32的时钟输出功能主要有以下用途可以用来兼容别的协议例如SPI还可以实现自适应波特率的传输如果接收方无法确定发送过来的数据是什么波特率就可以计算这个时钟的周期来确定波特率不过这就需要额外的代码来完成功能。 图中的唤醒单元可以实现串口多设备。在开始通信前主机给每个从机设定一个地址当通信地址和从机地址相同时唤醒单元开始工作没有收到地址时就保持沉默这样就可以实现一条总线上挂载多设备的通信。
1.3 串口通信数据帧 上图展示了9位和8位发送/接收一个字节的时序。对于9位数据帧模式可以配置为8位有效载荷1位校验位也可以配置为9位有效载荷前者更加常用对于8位的数据帧可以配置为7位有效载荷1位校验位也可以配置为8位有效载荷后者更常用。可以通过配置选择最后一个数据位是否输出时钟。空闲帧和断开帧是和局域网协议相关的函数这里仅作了解即可。 上图展示了不同停止位的时序波形
1.4 起始位侦测和USART的噪声判断机制 STM32会以波特率的16倍频对输入信号进行采样。它会在第一次侦测到0后开启起始位侦测如果在第3、5、7次采样中至少有2个0就认为收到了起始位否则认为收到的是噪声。如果检测到了两个0和一个1认为收到了起始位但是会置NENoise Error标志位为1来提醒用户可能存在噪声。如果通过了起始位侦测外设会开始采样起始位在第8、9、10次采样并且要求3位中至少有两个0之后的位也都在第8、9、10次进行采样保证采样尽量在一位数据的正中间。如果三次检测不是全部相同则NE会置1来提醒用户可能存在噪声。
1.5 波特率发生器 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定计算公式波特率 f_PCLK2/1 / (16 * DIV)可以通过这个公式计算对应波特率的DIV。用库函数配置可以省去这个过程。
二、串口发送和接收数据包 在实际应用中我们常常需要连续发送或接收数据有时需要对连续的数据进行分割和打包我们才可以正确处理数据使用数据包发送和接收数据还可以实现简单的人机交互设计。 打包和分割数据的方法可以自行设计串口的数据包采用添加包头和包尾的方式实现。
2.1 HEX数据包 HEX数据包适合发送最原始的数据例如一些使用串口通信的陀螺仪、温湿度传感器等。 如果载荷数据可能存在与包头包尾重复的情况可以采用以下的方法解决
规定有效载荷数据的范围增加包头包尾的数量尽量使其产生载荷数据中不会出现的格式尽量采用固定包长发送数据包在接收数据时我们不关心有效数据是否和包头包尾重复我们只关心应该是包头包尾的位置是否是包头包尾。 在实际使用时如果载荷数据不会和包头包尾重复可以二者留其一例如只添加包头或者只添加包尾。
2.2 文本数据包 在字符模式下由于有大量的字符可以使用就可以很好地避免数据和包头包尾重复的问题。使用文本数据包直观易理解非常灵活可以很方便的实现一些人机交互的需求例如蓝牙模块常用的AT指令CNC和3D打印机常用的G代码等但是解析效率低。所以需要根据实际场景来选择数据包格式。
2.3 固定包长HEX数据包接收 如果采用一个一个字节的接收方法每接收一个数据需要进入中断接收结束之后需要退出中断 但是对于数据包来说很显然包头包尾和有效载荷数据之间存在前后的关联性。对于包头、数据和包尾这三种状态我们需要有不同的处理逻辑在程序中就需要设计一个能记住不同状态的机制在不同状态执行不同的操作同时还要进行数据的合理转移。这种程序设计思维就称为“状态机”。
2.4 可变包长文本数据包接收 由于文本传输有两个包尾所以在S1状态下接收数据和等待包尾需要同时进行在S2状态需要等待第二个包尾。
三、代码实现
3.1 常用库函数
// 缺省结构体配置
void USART_DeInit(USART_TypeDef* USARTx);// 初始化函数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);// 结构体初始化
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);// 同步时钟输出配置
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);// 开启USART
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);// 中断配置接收时使用
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);// DMA通道配置
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);// 发送数据
void 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_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);3.2 使用串口发送数据
Setial.h
#ifndef __Serial_H_
#define __Serial_H_#include stdio.hvoid Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);#endif
Serial.c
#include stm32f10x.h // Device header
#include stdio.h
#include stdarg.hvoid Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin GPIO_Pin_9;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_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_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i 0; i Length; i ){Serial_SendByte(Array[i]);}
}void Serial_SendString(char *String)
{uint16_t i;for (i 0; String[i] ! \0; i ){Serial_SendByte(String[i]);}
}uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for (i 0; i Length; i ){Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 0);}
}// printf函数重定向在使用前需要在魔术棒中点击MicroLIB
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}// 封装sprintf()函数
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_SendString(String);
}
main.c
#include stm32f10x.h // Device header
#include OLED.h
#include Serial.hint main()
{OLED_Init();Serial_Init();Serial_SendByte(A);uint8_t MyArray[] {0x42, 0x43, 0x44, 0x45};Serial_SendArray(MyArray, 4);Serial_SendString(\r\nHello world!\r\n);Serial_SendNumber(12345, 5);printf(\r\nNUM %d\r\n, 666);char String[100];// sprintf()函数可以把格式化字符之后的内容转移到对应的字符串中解决了多个串口无法都重定向printf的问题sprintf(String, Num %d\r\n, 333); Serial_SendString(String);Serial_Printf(\r\n你好世界); // UTF-8需要在编译器中添加--no-multibyte-chars指令while(1){}
}
3.3 使用串口接收数据 当接受的数据比较简单时在主循环中可以采用这样的方法来查询标志位以接收数据
while(1)
{if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) SET){RxData USART_ReceiveData(USART1);OLED_ShowHexNum(1, 1, RxData, 2);}
}下面展示利用中断接收数据的方法
Serial.h
#ifndef __Serial_H_
#define __Serial_H_#include stdio.hvoid Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
uint8_t Serial_GetRxFlag(void);
uint8_t Setial_GetRxData(void);#endif
Serial.c
#include stm32f10x.h // Device header
#include stdio.h
#include stdarg.huint8_t Serial_RxData;
uint8_t Serial_RxFlag;void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin GPIO_Pin_9;GPIO_Init(GPIOA, GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin GPIO_Pin_10;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_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;NVIC_Init(NVIC_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_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i 0; i Length; i ){Serial_SendByte(Array[i]);}
}void Serial_SendString(char *String)
{uint16_t i;for (i 0; String[i] ! \0; i ){Serial_SendByte(String[i]);}
}uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for (i 0; i Length; i ){Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 0);}
}// printf函数重定向在使用前需要在魔术棒中点击MicroLIB
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}// 封装sprintf()函数
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_SendString(String);
}uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag 1){Serial_RxFlag 0;return 1;}return 0;
}uint8_t Setial_GetRxData(void)
{return Serial_RxData;
}// 中断服务函数
void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) SET){Serial_RxData USART_ReceiveData(USART1);Serial_RxFlag 1;USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}
main.c
#include stm32f10x.h // Device header
#include OLED.h
#include Serial.huint8_t RxData;int main()
{OLED_Init();OLED_ShowString(1, 1, RxData:);Serial_Init();while(1){if (Serial_GetRxFlag() 1){RxData Setial_GetRxData();Serial_SendByte(RxData);OLED_ShowHexNum(1, 8, RxData, 2);}}
}
3.4 串口收发HEX数据包
Serial.h
#ifndef __Serial_H_
#define __Serial_H_#include stdio.hextern uint8_t Serial_TxPacket[];
extern uint8_t Serial_RxPacket[];void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);void Serial_SendPacket(void);
uint8_t Serial_GetRxFlag(void);#endif
Serial.c
#include stm32f10x.h // Device header
#include stdio.h
#include stdarg.huint8_t Serial_TxPacket[4];
uint8_t Serial_RxPacket[4];
uint8_t Serial_RxFlag;void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin GPIO_Pin_9;GPIO_Init(GPIOA, GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin GPIO_Pin_10;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_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;NVIC_Init(NVIC_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_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i 0; i Length; i ){Serial_SendByte(Array[i]);}
}void Serial_SendString(char *String)
{uint16_t i;for (i 0; String[i] ! \0; i ){Serial_SendByte(String[i]);}
}uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for (i 0; i Length; i ){Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 0);}
}// printf函数重定向在使用前需要在魔术棒中点击MicroLIB
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}// 封装sprintf()函数
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_SendString(String);
}void Serial_SendPacket(void)
{Serial_SendByte(0xFF);Serial_SendArray(Serial_TxPacket, 4);Serial_SendByte(0xFE);
}uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag 1){Serial_RxFlag 0;return 1;}return 0;
}// 中断服务函数
void USART1_IRQHandler(void)
{static uint8_t RxState 0;static uint8_t pRxState 0;if (USART_GetITStatus(USART1, USART_IT_RXNE) SET){uint8_t RxData USART_ReceiveData(USART1);if (RxState 0) // 等待包头{if (RxData 0xFF){RxState 1;pRxState 0;}}else if (RxState 1) // 获取数据{Serial_RxPacket[pRxState] RxData;pRxState ;if (pRxState 4){RxState 2;}}else if (RxState 2) // 等待包尾{if (RxData 0xFE){RxState 0;Serial_RxFlag 1;}}USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 读取DRRXNE会自动清0}
}
main.cKey.c/.h文件略
#include stm32f10x.h // Device header
#include OLED.h
#include Serial.h
#include Key.huint8_t RxData, KeyNum;int main()
{OLED_Init();Key_Init();Serial_Init();OLED_ShowString(1, 1, TxData:);OLED_ShowString(3, 1, RxData:);Serial_TxPacket[0] 0x01;Serial_TxPacket[1] 0x02;Serial_TxPacket[2] 0x03;Serial_TxPacket[3] 0x04;Serial_SendPacket();while(1){KeyNum Key_GetNum();if (KeyNum 1){Serial_TxPacket[0] ;Serial_TxPacket[1] ;Serial_TxPacket[2] ;Serial_TxPacket[3] ;Serial_SendPacket();}OLED_ShowHexNum(2, 1, Serial_TxPacket[0], 2);OLED_ShowHexNum(2, 4, Serial_TxPacket[1], 2);OLED_ShowHexNum(2, 7, Serial_TxPacket[2], 2);OLED_ShowHexNum(2, 10, Serial_TxPacket[3], 2);if (Serial_GetRxFlag() 1){OLED_ShowHexNum(4, 1, Serial_RxPacket[0], 2);OLED_ShowHexNum(4, 4, Serial_RxPacket[1], 2);OLED_ShowHexNum(4, 7, Serial_RxPacket[2], 2);OLED_ShowHexNum(4, 10, Serial_RxPacket[3], 2);}}
}3.5 串口发送文本数据包实现简单的人机交互
Serial.h
#ifndef __Serial_H_
#define __Serial_H_#include stdio.hextern char Serial_RxPacket[];
extern uint8_t Serial_RxFlag;void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);uint8_t Serial_GetRxFlag(void);#endif
Serial.c
#include stm32f10x.h // Device header
#include stdio.h
#include stdarg.hchar Serial_RxPacket[100];
uint8_t Serial_RxFlag;void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin GPIO_Pin_9;GPIO_Init(GPIOA, GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin GPIO_Pin_10;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_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;NVIC_Init(NVIC_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_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i 0; i Length; i ){Serial_SendByte(Array[i]);}
}void Serial_SendString(char *String)
{uint16_t i;for (i 0; String[i] ! \0; i ){Serial_SendByte(String[i]);}
}uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for (i 0; i Length; i ){Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 0);}
}// printf函数重定向在使用前需要在魔术棒中点击MicroLIB
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}// 封装sprintf()函数
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_SendString(String);
}// 中断服务函数
void USART1_IRQHandler(void)
{static uint8_t RxState 0;static uint8_t pRxState 0;if (USART_GetITStatus(USART1, USART_IT_RXNE) SET){uint8_t RxData USART_ReceiveData(USART1);if (RxState 0) // 等待包头{if (RxData Serial_RxFlag 0) // 只有上一个数据处理完才会接收方式指令被覆盖的问题{RxState 1;pRxState 0;}}else if (RxState 1) // 获取数据并等待第二个包尾{if (RxData \r){RxState 2;}else {Serial_RxPacket[pRxState] RxData;pRxState ;}}else if (RxState 2) // 等待包尾{if (RxData \n){RxState 0;Serial_RxPacket[pRxState] \0; // 手动添加字符串结束标志位Serial_RxFlag 1;}}USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 读取DRRXNE会自动清0}
}
main.cLED.c/.h略执行电灯操作即可
#include stm32f10x.h // Device header
#include OLED.h
#include Serial.h
#include LED.h
#include string.huint8_t RxData, KeyNum;int main()
{OLED_Init();Serial_Init();LED_Init();OLED_ShowString(1, 1, TxData:);OLED_ShowString(3, 1, RxData:);while (1){if (Serial_RxFlag 1){OLED_ShowString(4, 1, );OLED_ShowString(4, 1, Serial_RxPacket);if (strcmp(Serial_RxPacket, LED_ON) 0){LED1_ON();Serial_SendString(LED_ON_OK\r\n);OLED_ShowString(2, 1, );OLED_ShowString(2, 1, LED_ON_OK);}else if (strcmp(Serial_RxPacket, LED_OFF) 0){LED1_OFF();Serial_SendString(LED_OFF_OK\r\n);OLED_ShowString(2, 1, );OLED_ShowString(2, 1, LED_OFF_OK);}else {Serial_SendString(ERROR_COMMAND\r\n);OLED_ShowString(2, 1, );OLED_ShowString(2, 1, ERROR_COMMAND);}Serial_RxFlag 0;}}
} 课程链接江协科技 STM32入门教程欢迎大家一起交流学习。 原创笔记码字不易欢迎点赞收藏~ 如有谬误敬请在评论区不吝告知感激不尽博主将持续更新有关嵌入式开发、机器学习方面的学习笔记~