沈阳公司做网站的,wordpress 安装 此网页包含重定向循环,上海佐兹设计公司官网,网站左侧悬浮导航一、原理说明
STM32自带通讯接口 通讯目的 通信方式#xff1a;
全双工#xff1a;通信时可以双方同时通信。
半双工#xff1a;通信时同一时间只能一个设备发送数据#xff0c;其他设备接收。
单工#xff1a;只能一个设备发送到另一个设备#xff0c;例如USART只有…一、原理说明
STM32自带通讯接口 通讯目的 通信方式
全双工通信时可以双方同时通信。
半双工通信时同一时间只能一个设备发送数据其他设备接收。
单工只能一个设备发送到另一个设备例如USART只有TX线。
时钟类型
异步约定传输速率例如波特率相同约定起始信号和结束信号、帧头帧尾等进行约束和校验进行传输。
优点是节省了硬件资源缺点是在传输过程中停止会传输失败不能中途暂停而且难以用软件模拟只能使用硬件传输。
同步有一根单独的时钟线通过时钟信号控制传输时的速率。
优点是在传输过程中可以中断停止时钟信号即可停止停止后可以继续传输并且可以使用软件模拟时钟信号和通信无硬件资源时可以牺牲引脚来使用。
传输类型
单端通过信号和GND的电平差来区分高低电平需要把通信双方的GND接到一起否则容易受到干扰。
差分通过两根线的差分电平来判断高低电平通信抗干扰信号更强传输距离更远。
USB尽量共地
设备拓扑类型
点对点设备1-1通信可能需要寻址以确定
点对多设备1-n通信
多对多设备n-n通信
串口协议 CH340可以将串口转换为USB协议可以直接插在电脑上。
陀螺仪传感器可以测量角速度加速度等姿态参数可以接串口和I2C。
蓝牙串口模块可以和蓝牙设备互联实现手机遥控单片机的功能。 串口接线图 电平标准 串口协议的软件定义 RB8和TB8是奇偶校验位可选择是否使用。一般需要校验位则选择9bit数据不需要校验位则选择8bit。
波特率每秒传输码元的个数可能每个码元包含信息量不止1bit单位为码元/s或者为bund。
比特率每秒传输bit的速率。单位bit/s或者bps。在二进制调制的情况下一个码元就是一个bit。
奇偶校验
奇校验表示包括校验位和数据位发送的1为奇数个。例如发送0x0F则发送 111100001
串口波形实例
起始位为低电平默认线上高电平结束位为高电平方便区分两个连续数据包。 STM32的USART外设 UART和USART类似但是只支持时钟输出不支持时钟输入。
同步模式是支持CLK时钟输出、硬件流控制表示传输时可通过从设备反馈 来控制主设备的发送 防止从设备处理慢导致数据丢失。DMA是串口支持DMA数据转运。STM32的串口协议也可支持智能卡、IrDA红外通信、LIN局域网等设备。
STM32-USART内部原理 发送数据寄存器TDR和接收数据寄存器RDR使用同一块存储区域。nRTS高速主机当前是否可以接收数据。nCTS用于接收从机nRTS信号的引脚判断从机当前是否可以接收。n代表低电平有效。串口使用SCLK可以兼容SPI。若不知道对方波特率可以通过SCLK计算得到并传出输出。
唤醒单元用来实现串口挂载多设备可以给串口分配地址。总线上对应发送的地址设备会进行通信。
TXE发送寄存器空。
RXNE接收寄存器非空。 USART波形及配置 一般配置9bit字长都会使用校验位。若不是用校验位则第九位为载荷bit。
选择8bit字长最好无校验位若有校验位那么有效载荷7bit。每次发送不够1byte。 停止位高电平一般用1bit。 USART结构图 USART起始位侦测原理 波特率分频分频后还有16倍分频 例如USART1波特率9600那么9600 72000000/(16*DIV);DIV 468.785。放入寄存器为111010100.11
USB转串口模块的内部电路图 不同数据模式 二、实例
1、串口发送OLED接收
接线图 引脚定义 重构printf函数 printf输出汉字需要增加--no-multibyte-chars配置防止中文乱码 --no-multibyte-chars 接收时需要用相同的编码方式 若utf-8兼容性不好可更换GB2312编码方式。串口接收汉字需要收发的两方编码方式相同。
main.c
#include stdio.h
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
uint8_t SendDataA[3] {0x01,0x02,0x03};
char *Char Hello Word!!!;
int32_t zNum 23213;
int main(void){OLED_Init();Serial_Init();OLED_ShowString(1,1,AD0:0000);OLED_ShowString(2,1,AD1:0000);while(1){Delay_ms(1000);//Serial_SendByte(0x41);//发送十六进制0x41//Serial_SendByte(A);//发送字符A同0x41.逻辑为发送A-底层0x41发送-串口底层接收0x41-可现实字符或十六进制数//Serial_SendByte(SendDataA[0]);//发送1byte数据//Serial_SendByteArray(SendDataA[0],3);//发送指定长度字节数组//Serial_SendString(Char);发送字符串//Serial_SendSignedNum(zNum,4);//printf打印串口方法1//printf(Num %d\r\n , 666);// \r\n为字符换行printf通过重定向打印串口//printf打印串口方法2//不使用重定向格式化字符串来串口打印所有的串口都有可以用//char String[100];//sprintf(String,Num %d\r\n , 666);//使用sprintf进行格式化然后使用串口进行输出//Serial_SendString(String);//打印Num 666\r\n//printf打印串口方法3//Serial_printf(Num %d\r\n , 666);//发送汉字字符串-接收需要和发送时相同的编码方式Serial_printf(你好);}return 0;
}Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include stm32f10x.h // Device header
#include stdio.h
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendByteArray(uint8_t *ByteArray,uint16_t length);
void Serial_SendString(char *Char);
void Serial_SendSignedNum(int32_t Num,uint16_t length);
void Serial_SendNum(int32_t Num,uint16_t length);
void Serial_printf(char *format,...);
#endif
Serial.c
#include stm32f10x.h // Device header
#include math.h
#include stdio.h
#include stdarg.h
/*** brief 初始化USART1通过USART1进行收发* param * arg * param * arg * retval None*/
void Serial_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;//上拉输入.GPIO_InitStructure.GPIO_Pin GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,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_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate 9600;USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None;//硬件流控制USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx;USART_InitStructure.USART_Parity USART_Parity_No;USART_InitStructure.USART_WordLength USART_WordLength_8b;USART_InitStructure.USART_StopBits USART_StopBits_1;USART_Init(USART1,USART_InitStructure);USART_Cmd(USART1,ENABLE);
}
/*** brief 串口发送1byte* param * arg * param * arg * retval None*/
void Serial_SendByte(uint8_t Byte){USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET);//发送缓冲不为空则等待
}/*** brief 发送字节数组* param 数组指针* arg * param 长度* arg * retval None*/
void Serial_SendByteArray(uint8_t *ByteArray,uint16_t length){for(int i 0 ; ilength ; i){Serial_SendByte(ByteArray[i]);}
}/*** brief 发送一个字符串* param * arg * param * arg * retval None*/
void Serial_SendString(char *String){int i 0;for(i0;String[i]!\0;i){Serial_SendByte(String[i]);}
}/*** brief 以字符形式发送有符号数字* param * arg * param * arg * retval None*/
void Serial_SendSignedNum(int32_t Num,uint16_t length){if(Num0){Serial_SendByte(0x2B);//加号}else{Serial_SendByte(0x2D);//减号Num -Num;}//Num abs(Num); abs处理int数据所以不适用for(int i1;ilength;i){Serial_SendByte((Num/(uint32_t)pow(10,length-i)%10)0);}
}/*** brief 以字符形式发送无符号数字* param * arg * param * arg * retval None*/
void Serial_SendNum(int32_t Num,uint16_t length){for(int i1;ilength;i){Serial_SendByte((Num/(uint32_t)pow(10,length-i)%10)0);}
}/*** brief 重定向fputc函数到串口1即Serial_SendByte函数使用micor提供的精简库* arg fputc是printf函数的底层printf打印时就是调用fputc单个打印通过重定向可以在串口打印* param * arg * param * arg * retval None*/
int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}/*** brief 重构v sprintf完成printf打印串口* arg sprintf只能接收直接写的参数vsprintf可以接收封装格式参数* param format接收格式化字符串* arg * param ...:可变参数列表用来接收剩余参数* arg * retval None*/
void Serial_printf(char *format,...){char String[100];va_list arg;//定义一个参数列表变量va_start(arg,format);//从format位置开始接收参数列表放在arg中vsprintf(String,format,arg);//将打印参数arg格式化为format格式字符串最好放入打印字符串变量Stringva_end(arg);//释放参数表Serial_SendString(String);//发送格式化后的字符串
}
2、串口发送接收串口收到数据后将数据返回
main.c
#include stdio.h
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.hint main(void){OLED_Init();Serial_Init();OLED_ShowString(1,1,Rx:00);while(1){Serial1Rx();OLED_ShowHexNum(1,4,Serial1_RxData,2);//显示1byte接收的十六进制数}return 0;
}Serial.c
#include stm32f10x.h // Device header
#include math.h
#include stdio.h
#include stdarg.h
uint8_t Serial1_RxData;
uint8_t Serial1_Flag;
/*** brief 初始化USART1通过USART1进行收发* param * arg * param * arg * retval None*/
void Serial_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;//上拉输入.GPIO_InitStructure.GPIO_Pin GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,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_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate 9600;USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None;//硬件流控制USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx;USART_InitStructure.USART_Parity USART_Parity_No;USART_InitStructure.USART_WordLength USART_WordLength_8b;USART_InitStructure.USART_StopBits USART_StopBits_1;USART_Init(USART1,USART_InitStructure);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC配置-中断优先级和中断对应通道使能 stm32f10x.hNVIC_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);
}
/*** brief 串口发送1byte* param * arg * param * arg * retval None*/
void Serial_SendByte(uint8_t Byte){USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET);//发送缓冲不为空则等待
}/*** brief USART1中断函数,接收的数据返回* param * arg * param * arg * retval None * arg 中断函数名在startup_stm32f10x_md.s中*/
void USART1_IRQHandler(void){if(USART_GetITStatus(USART1,USART_IT_RXNE) SET){Serial1_RxData USART_ReceiveData(USART1);Serial1_Flag 1;USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}/*** brief 配合USART1_IRQHandler串口1中断中断后处理行为* param * arg * param * arg * retval None*/
void Serial1Rx(void){if(Serial1_Flag){Serial_SendByte(Serial1_RxData);Serial1_Flag 0;}
}/*** brief 发送字节数组* param 数组指针* arg * param 长度* arg * retval None*/
void Serial_SendByteArray(uint8_t *ByteArray,uint16_t length){for(int i 0 ; ilength ; i){Serial_SendByte(ByteArray[i]);}
}/*** brief 发送一个字符串* param * arg * param * arg * retval None*/
void Serial_SendString(char *String){int i 0;for(i0;String[i]!\0;i){Serial_SendByte(String[i]);}
}/*** brief 以字符形式发送有符号数字* param * arg * param * arg * retval None*/
void Serial_SendSignedNum(int32_t Num,uint16_t length){if(Num0){Serial_SendByte(0x2B);//加号}else{Serial_SendByte(0x2D);//减号Num -Num;}//Num abs(Num); abs处理int数据所以不适用for(int i1;ilength;i){Serial_SendByte((Num/(uint32_t)pow(10,length-i)%10)0);}
}/*** brief 以字符形式发送无符号数字* param * arg * param * arg * retval None*/
void Serial_SendNum(int32_t Num,uint16_t length){for(int i1;ilength;i){Serial_SendByte((Num/(uint32_t)pow(10,length-i)%10)0);}
}/*** brief 重定向fputc函数到串口1即Serial_SendByte函数使用micor提供的精简库* arg fputc是printf函数的底层printf打印时就是调用fputc单个打印通过重定向可以在串口打印* param * arg * param * arg * retval None*/
int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}/*** brief 重构v sprintf完成printf打印串口* arg sprintf只能接收直接写的参数vsprintf可以接收封装格式参数* param format接收格式化字符串* arg * param ...:可变参数列表用来接收剩余参数* arg * retval None*/
void Serial_printf(char *format,...){char String[100];va_list arg;//定义一个参数列表变量va_start(arg,format);//从format位置开始接收参数列表放在arg中vsprintf(String,format,arg);//将打印参数arg格式化为format格式字符串最好放入打印字符串变量Stringva_end(arg);//释放参数表Serial_SendString(String);//发送格式化后的字符串
}/*** brief 串口1接收查询方式,接收到的数据返回* param * arg * param * arg * retval None*/
void Serial_Get(void){if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) SET){uint8_t Get_Data USART_ReceiveData(USART1);Serial_SendByte(Get_Data);};
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include stm32f10x.h // Device header
#include stdio.h
extern uint8_t Serial1_Flag;
extern uint8_t Serial1_RxData;
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendByteArray(uint8_t *ByteArray,uint16_t length);
void Serial_SendString(char *Char);
void Serial_SendSignedNum(int32_t Num,uint16_t length);
void Serial_SendNum(int32_t Num,uint16_t length);
void Serial_printf(char *format,...);
void Serial_Get(void);
void Serial1Rx(void);
#endif
3、数据包收发收发Hex数据包 程序功能和逻辑
1、按钮按下对应STM32发送Hex数据包OLED显示发送Hex数据。每次按按钮数据包递增。
2、串口发送Hex数据包对应OLED显示接收的Hex数据当接收到正确的数据包会进行处理把接受的数据包也发送接受的错误的数据包不会发送但OLED也会显示。。 Hex数据包格式 可变长数据包可能导致数据和包尾重合导致误判。
对于这种情况 可对数据进行限幅然后包头包尾在限幅之外。或者尽量使用定长数据包然后尽量增加包头包尾的数量并尽量让数据不能呈现包头包尾的状态。
对于Hex数据包最好选择定长带包头包尾可以避免接收错误。
如果载荷不会和包头包尾重复那么选择可变包长。
Hex通信协议为
发送
按下按键STM32发送0xFF 0x01 0x02 0x03 0x04 0xFE以0xFF为包头0xFE为包尾中间4byte为数据每次发送数据字节递增。
接收
通过串口助手发送0xFF 0xXX 0xXX 0xXX 0xXX 0xFE共6byte其中以0xFF为包头0xFE为包尾中间4byte为数据。STM32接收后通过OLED显示数据字节。
Hex数据包接收 main.c
#include stdio.h
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
#include Button.h
int main(void){OLED_Init();Serial_Init();Button1_Init();OLED_ShowString(1,1,Tx:);OLED_ShowString(3,1,Rx:);while(1){Serial1Rx();GetButton_Send();OLED_ShowHexNum(4,1,Serial1_RxPacket[0],2);OLED_ShowHexNum(4,4,Serial1_RxPacket[1],2);OLED_ShowHexNum(4,7,Serial1_RxPacket[2],2);OLED_ShowHexNum(4,10,Serial1_RxPacket[3],2);OLED_ShowHexNum(2,1,Serial1_TxPacket[0],2);OLED_ShowHexNum(2,4,Serial1_TxPacket[1],2);OLED_ShowHexNum(2,7,Serial1_TxPacket[2],2);OLED_ShowHexNum(2,10,Serial1_TxPacket[3],2);}return 0;
}Serial.c
#include stm32f10x.h // Device header
#include math.h
#include stdio.h
#include stdarg.h
uint8_t Serial1_RxData;
uint8_t Serial1_RxFlag;//接收数据包标志位
uint8_t Serial1_RxPacket[4];//接收数据包
uint8_t Serial1_TxPacket[4] {0x01,0x02,0x03,0x04};//发送数据包
/*** brief 初始化USART1通过USART1进行收发* param * arg * param * arg * retval None*/
void Serial_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;//上拉输入.GPIO_InitStructure.GPIO_Pin GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,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_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate 9600;USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None;//硬件流控制USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx;USART_InitStructure.USART_Parity USART_Parity_No;USART_InitStructure.USART_WordLength USART_WordLength_8b;USART_InitStructure.USART_StopBits USART_StopBits_1;USART_Init(USART1,USART_InitStructure);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC配置-中断优先级和中断对应通道使能 stm32f10x.hNVIC_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);
}
/*** brief 串口发送1byte* param * arg * param * arg * retval None*/
void Serial_SendByte(uint8_t Byte){USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET);//发送缓冲不为空则等待
}/*** brief 发送字节数组* param 数组指针* arg * param 长度* arg * retval None*/
void Serial_SendByteArray(uint8_t *ByteArray,uint16_t length){for(int i 0 ; ilength ; i){Serial_SendByte(ByteArray[i]);}
}/*** brief 根据数据包发送数据数组* param * arg * param * arg * retval None*/
void Serial1Tx_HexPacket(void){Serial_SendByte(0xFF);//发送包头Serial_SendByteArray(Serial1_TxPacket,4);Serial_SendByte(0xFE);//发送包尾
}/*** brief 配合USART1_IRQHandler串口1接收中断对接收到的数据进行处理* param * arg * param * arg * retval None*/
void Serial1Rx(void){if(Serial1_RxFlag){for(int i0;i4;i){Serial1_TxPacket[i] Serial1_RxPacket[i];}Serial1Tx_HexPacket();Serial1_RxFlag 0;}
}/*** brief 配合USART1_IRQHandler串口1接收中断接收到的Hex数据包进行判断* param * arg * param * arg * retval None*/
void Serial1Rx_HexPacket(void){static uint8_t RxState 0;//接收状态机static uint8_t RxDataFlag 0;//接收数据下标/*0/1 包头1 数据2 包尾*/Serial1_RxData USART_ReceiveData(USART1);//接收数据if(RxState 0){//等待接收包头if(Serial1_RxData 0xFF){//如果获取包头RxState 1;RxDataFlag 0;//在每次接收数据前清0更稳定}}else if(RxState 1){//等待数据Serial1_RxPacket[RxDataFlag] Serial1_RxData;RxDataFlag;if(RxDataFlag4){RxState 2;}}else if(RxState 2){//等待包尾if(Serial1_RxData 0xFE){//等待包尾RxState 0;Serial1_RxFlag 1;//标志接收到了正确的数据包标志位}else{RxState 0;//如果接收的不是包尾则丢弃数据包}}}/*** brief USART1中断函数,接收的数据返回* param * arg * param * arg * retval None * arg 中断函数名在startup_stm32f10x_md.s中*/
void USART1_IRQHandler(void){if(USART_GetITStatus(USART1,USART_IT_RXNE) SET){Serial1Rx_HexPacket();//开始接收数据USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include stm32f10x.h // Device header
#include stdio.h
extern uint8_t Serial1_RxData;
extern uint8_t Serial1_RxFlag;
extern uint8_t Serial1_RxPacket[];
extern uint8_t Serial1_TxPacket[];
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendByteArray(uint8_t *ByteArray,uint16_t length);
void Serial1Rx(void);
void Serial1Rx_HexPacket(void);
void Serial1Tx_HexPacket(void);
#endif
Button.h
#ifndef __BUTTON_H
#define __BUTTON_H
#include stm32f10x.h // Device headervoid Button1_Init(void);
void GetButton_Send(void);
#endif
Button.c
#include stm32f10x.h // Device header
#include Delay.h
#include Serial.h
/*** brief 初始化引脚B1地开信号接收ButtonA1推挽输出控制LED* param * arg * param * arg * retval None*/
void Button1_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOB,GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_1);//高电平
}/*** brief * param * arg * param * arg * retval None*/
void GetButton_Send(void){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) RESET){Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) RESET);Delay_ms(20);Serial1_TxPacket[0];Serial1_TxPacket[1];Serial1_TxPacket[2];Serial1_TxPacket[3];Serial1Tx_HexPacket();}
}
4、数据包收发收发文本数据包 程序功能逻辑
1、按钮按下灯开关对应STM32发送开关灯文本数据包OLED显示发送的文本数据
2、串口发送开关灯Hex数据包对应开关灯对应OLED显示接收的Hex数据STM32发送开关灯文本数据包包反馈 文本数据包格式 使用文本数据包中间数据基本上都是字母不会和包头包尾重复。可使用不定长数据包。
通常用换行做包尾这样在打印时就是一行一行显示相对Hex数据包解析效率低。
文本数据包通信协议为
发送
按下按键STM32交替发送 LED_ON\r\n和LED_OFF\r\n对应LED开关以为包头\r\n为包尾(换行)LED_ON和LED_OFF为数据。并在OLED显示
接收
通过串口助手发送 LED_ON\r\n和LED_OFF\r\n对应LED开关其中以为包头\r\n为包尾(换行)LED_ON和LED_OFF为数据。并通过OLED显示数据字节。当STM32接收到错误指令时报故。
文本数据包接收 main.c
#include stdio.h
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
#include Button.h
int main(void){OLED_Init();Serial_Init();Button1_Init();OLED_ShowString(1,1,Tx:);OLED_ShowString(2,1,LED_OFF);OLED_ShowString(3,1,Rx:);OLED_ShowString(4,1,LED_OFF);while(1){GetButton_LED();Serial1TextRx();}return 0;
}代码讲解 Serial.c
#include stm32f10x.h // Device header
#include math.h
#include stdio.h
#include stdarg.h
#include OLED.h
#include Button.h
#include String.h
uint8_t Serial1_RxData;//接收数据字节
uint8_t Serial1_RxFlag;//接收完整数据包标志位
char Serial1_RxTextPacket[100];//接收文本数据包
/*** brief 初始化USART1通过USART1进行收发* param * arg * param * arg * retval None*/
void Serial_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;//上拉输入.GPIO_InitStructure.GPIO_Pin GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,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_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate 9600;USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None;//硬件流控制USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx;USART_InitStructure.USART_Parity USART_Parity_No;USART_InitStructure.USART_WordLength USART_WordLength_8b;USART_InitStructure.USART_StopBits USART_StopBits_1;USART_Init(USART1,USART_InitStructure);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC配置-中断优先级和中断对应通道使能 stm32f10x.hNVIC_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);
}
/*** brief 串口发送1byte* param * arg * param * arg * retval None*/
void Serial_SendByte(uint8_t Byte){USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) RESET);//发送缓冲不为空则等待
}/*** brief 发送一个字符串* param * arg * param * arg * retval None*/
void Serial_SendString(char *String){int i 0;for(i0;String[i]!\0;i){Serial_SendByte(String[i]);}
}/*** brief 根据数据包发送文本数据数组* param * arg * param * arg * retval None*/
void Serial1Tx_TextPacket(void){Serial_SendByte();//发送包头if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)RESET){Serial_SendString(LED_ON);}else{Serial_SendString(LED_OFF);}Serial_SendByte(\r);//发送包尾Serial_SendByte(\n);//发送包尾}/*** brief 配合USART1_IRQHandler串口1接收中断对接收到的文本数据进行处理* param * arg * param * arg * retval None*/
void Serial1TextRx(void){if(Serial1_RxFlag){OLED_ShowString(4,1, );OLED_ShowString(4,1,Serial1_RxTextPacket);if(strcmp(Serial1_RxTextPacket,LED_OFF) 0){LED_OFF(GPIOA,GPIO_Pin_1);}else if(strcmp(Serial1_RxTextPacket,LED_ON) 0){LED_ON(GPIOA,GPIO_Pin_1);}else{OLED_ShowString(2,1,Rx_Error);Serial_SendString(Rx_Error\r\n);}Serial1_RxFlag 0;}
}/*** brief 配合USART1_IRQHandler串口1接收中断接收到的Text数据包进行判断* param * arg * param * arg * retval None*/
void Serial1Rx_TextPacket(void){static uint8_t RxState 0;//接收状态机static uint8_t RxDataFlag 0;//接收数据下标/*0/1 包头1 数据2 包尾*/Serial1_RxData USART_ReceiveData(USART1);//接收数据if(RxState 0){//等待接收包头if(Serial1_RxData ){//如果获取包头RxState 1;RxDataFlag 0;//在每次接收数据前清0更稳定}}else if(RxState 1){//等待数据if(Serial1_RxData ! \r){Serial1_RxTextPacket[RxDataFlag] Serial1_RxData;RxDataFlag;}else{RxState 2;} }else if(RxState 2){//等待包尾if(Serial1_RxData \n){//等待包尾RxState 0;Serial1_RxTextPacket[RxDataFlag] \0;Serial1_RxFlag 1;//标志接收到了正确的数据包标志位}else{RxState 0;//如果接收的不是包尾则丢弃数据包}}
}/*** brief USART1中断函数,接收的数据返回* param * arg * param * arg * retval None * arg 中断函数名在startup_stm32f10x_md.s中*/
void USART1_IRQHandler(void){if(USART_GetITStatus(USART1,USART_IT_RXNE) SET){//Serial1Rx_HexPacket();//开始接收数据Serial1Rx_TextPacket();USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}代码讲解 Serial1TextRx未放在中断内减少消耗软件资源减少中断时间占用
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include stm32f10x.h // Device header
#include stdio.h
extern uint8_t Serial1_RxData;
extern uint8_t Serial1_RxFlag;
extern char Serial1_RxTextPacket[];
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char *Char)void Serial1TextRx(void);
void Serial1Tx_TextPacket(void);
void Serial1Rx_TextPacket(void);
#endif
Button.c
#include stm32f10x.h // Device header
#include Delay.h
#include Serial.h
#include OLED.h
/*** brief 初始化引脚B1地开信号接收ButtonA1推挽输出控制LED* param * arg * param * arg * retval None*/
void Button1_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOB,GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_1);//高电平
}
/*** brief LED关闭OLED显示LED_OFF,并且串口发送LED_OFF\r\n* param * arg * param * arg * retval None*/
uint8_t LED_OFF(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin){GPIO_WriteBit(GPIOx,GPIO_Pin,Bit_SET);//关闭LEDOLED_ShowString(2,1,LED_OFF );Serial1Tx_TextPacket();//串口发送return 1;
}/*** brief LED打开OLED显示LED_ON,并且串口发送LED_ON* param * arg * param * arg * retval None*/
uint8_t LED_ON(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin){GPIO_WriteBit(GPIOx,GPIO_Pin,Bit_RESET);//打开LEDOLED_ShowString(2,1,LED_ON );Serial1Tx_TextPacket();//串口发送return 0;
}/*** brief 获取B1的Button是否按下按下则控制LED反转* param * arg * param * arg * retval None*/
void GetButton_LED(void){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) RESET){Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) RESET);Delay_ms(20);if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) RESET){LED_OFF(GPIOA,GPIO_Pin_1);}else{LED_ON(GPIOA,GPIO_Pin_1);}}
} 代码讲解 Button.h
#ifndef __BUTTON_H
#define __BUTTON_H
#include stm32f10x.h // Device headervoid Button1_Init(void);
uint8_t LED_OFF(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
uint8_t LED_ON(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
void GetButton_LED(void);
#endif
程序逻辑
按下按钮GetButton_LED - GetButton_LED - LED_ON/LED_OFF - GPIO_WriteBit/OLED_ShowString/Serial1Tx_TextPacket
发送控制指令:USART1_IRQHandler-Serial1Rx_TextPacket-Serial1TextRx-LED_ON/LED_OFF - GPIO_WriteBit/OLED_ShowString/Serial1Tx_TextPacket 反思
本节程序模块之间的耦合性太高应该针对不同的模块进行不同的功能封装然后统一调用即可。不应该在一个模块程序中调用另一个模块程序。
例如串口模块中尽量只写串口函数若需要串口相关返回和处理增加标志位返回函数或者全局变量即可。LED控制和按钮采集应该只写LED控制和按钮采集。在主函数中应该进行采集控制和通信函数的调用。每个模块库应该保持整洁和低耦性方便理解和使用。