东城网站建设工作室,智能硬件开发,中国建材采购网官网,中铁建设集团好进吗目录 一:串口协议
1:通信接口
2:串口通信
3:硬件电路
4:电平标准
5:串口参数及其时序
二:USART介绍
1:简历
2:USART框图
3:USART的基本结构
4:数据帧
5: 波特率发生器
6:数据模式 三:案例
A:串口发送--单发送
1:连接图
2:函数介绍
3:代码
B:串口发送接收
1…目录 一:串口协议
1:通信接口
2:串口通信
3:硬件电路
4:电平标准
5:串口参数及其时序
二:USART介绍
1:简历
2:USART框图
3:USART的基本结构
4:数据帧
5: 波特率发生器
6:数据模式 三:案例
A:串口发送--单发送
1:连接图
2:函数介绍
3:代码
B:串口发送接收
1:函数介绍 2:串口发送接收 -----查询代码
3:函数介绍 4:串口发送接收 -----中断代码
四:USART串口数据包
1:简历
2:HEX数据包
3: 文本数据包
4:HEX数据包接收
5:文本数据包接收
6: 案例
1:连接图
A:发送HEX数据包---固定数据长度
2:连接图
B:发送文本数据包---数据长度 一:串口协议
1:通信接口
通信的目的将一个设备的数据传送到另一个设备扩展硬件系统
通信协议制定通信的规则通信双方按照协议规则进行数据收发 USART通信: TX: 发送数据的引脚 RX : 接收数据的引脚 I2C通信: SCL: 时钟 SDA:数据 SPI通信: SCLK:时钟 MOSl:主机输出数据脚 MISO : 主机输入数据脚 CS : 片选用于指定通信的对象 CAN通信: 是差分数据脚用两个引脚表示一个差分数据 USB通信: 也是 是差分数据脚 双工 全双工:就是指通信双方能够同时进行双向通信, 两个数据线分别负责发送和接收数据 半双工 : 一根数据线负责发送和接收数据, eg:I2C通信的SDA线 时钟 同步: 接收方可以在时钟信号的指引下进行采样 异步 : 没有时钟线, 所以需要双方约定一个采样频率, 还需要加一些帧头帧尾等进行采样位置的对齐 电平 单端: 它们引脚的高低电平都是对GND的电压差, 所以单端信号通信的双方必须要共地就是把GND接在一起 差分 : 差分信号, 它是靠两个差分引脚的电压差来传输信号的, 在通信的时候可以不需要GND, 使用差分信号可以极大地提高抗干扰特性 设备 点对点 : 直接传输数据就可以了 多设备 : 一对多, 需要有一个导址的过程以确定通信的对象 2:串口通信 串口是一种应用十分广泛的通讯接口串口成本低、容易使用、通信线路简单可实现两个设备的互相通信 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信极大地扩展了单片机的应用范围增强了单片机系统的硬件实力
3:硬件电路 简单双向串口通信有两根通信线发送端TX和接收端RX TX与RX要交叉连接 当只需单向的数据传输时可以只接一根通信线 当电平标准不一致时需要加电平转换芯片-----------相同的电平标准才可以通信 VCC的连接 上面的VCG如果两个设备都有独立供电, VCC可以不接 如果一个设备没有独立供电, 需要VCC把他们连接起来 4:电平标准 电平标准是数据1和数据0的表达方式是传输线缆中人为规定的电压与数据的对应关系串口常用的电平标准有如下三种 TTL电平3.3V或5V表示10V表示0 RS232电平-3~-15V表示13~15V表示0 RS485电平两线压差2~6V表示1-2~-6V表示0差分信号
5:串口参数及其时序 波特率串口通信的速率--------波特率表示单位时间内传送的码元符号的个数. 规定串口通信的速率, 串口一般是使用异步通信, 发送和接收必须要约定好速率, 速率参数就是波特率. 双方规定波特率为1000bps, 那就表示1s要发1000位每一位的时间就是1ms 起始位标志一个数据帧的开始固定为低电平------串口的空闲状态是高电平,起始位产生一个下降沿, 告诉接收设备要开始传输数据了 停止位用于数据帧间隔固定为高电平-------停止位固定为1把引脚恢复成高电平,方便下次的数据传输, 可以选择1位、1.5位、2位等 数据位数据帧的有效载荷1为高电平0为低电平低位先行 校验位用于数据验证根据数据位计算得来------校验可以选择3种方式无校验、奇校验和偶校验 串口中每一个字节都装载在一个数据帧里面, 每个数据帧都由起始位、数据位和停止位组成 左边: 这里数据位有8个代表一个字节的8位 (一个字节为8位) 右边 : 数据帧里面还可以在数据位的最后,加一个奇偶校验位 ,这样数据位就9位 奇偶校验位----实际是对高电频1的校验 奇校验 : 发送数据0000 1111 采用右边的数据位为9位, 给第9位补1, 这时候1就为5个为奇数, 接收方一验证发现1的个数不是奇数那就认为传输出错, 就可以选择丢弃或者要求重传 偶校验: 发送数据0000 1111 采用右边的数据位为9位, 给第9位补0, 这时候1就为4个为偶数, 接收方一验证发现1的个数不是偶数那就认为传输出错, 就可以选择丢弃或者要求重传 奇偶校验只能保证一定程度上的数据校验 数据位的2中表示方法 一种是把校验位作为数据位的一部分, 分为8位数据和9位数据, 其中9位数据就是8位有效载荷和1位校验位, 另一种就是把数据位和校验位独立开, 数据位就是有效载荷校验位就是独立的1位 二:USART介绍
1:简历 USARTUniversal Synchronous/Asynchronous Receiver/Transmitter通用同步/异步收发器 USART是STM32内部集成的硬件外设可根据数据寄存器的一个字节数据自动生成数据帧时序从TX引脚发送出去也可自动接收RX引脚的数据帧时序拼接为一个字节数据存放在数据寄存器里 自带波特率发生器最高达4.5Mbits/s 可配置数据位长度8/9、停止位长0.5/1/1.5/2 可选校验位无校验/奇校验/偶校验 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
STM32F103C8T6 USART资源 USART1、 USART2、 USART3 硬件流控制-----------比如A设备有个TX向B设备的RX发送数据, A设备一直在发发的太快了, 如果没有硬件流控制, 那B就只能抛弃新数据或者覆盖原数据了. 如果有硬件流控制在硬件电路上会多出一根线, 如果B没准备好接收就置高电平如果准备好了就置低电平; A接收到了B反馈的准备信号就只会在B准备好的时候才发数据 硬件流控制可以防止因为B处理慢而导致数据丢失的问题 2:USART框图 寄存器 DR寄存器 : TDR和RDR数据寄存器占用同一个地址,在程序上他们表现为一个寄存器DR寄存器, TDR是只写的RDR是只读的, 当你进行写操作时 数据就写入到TDR寄存器. 当你进行读操作时数据就是从RDR读出来的 发送(接收)移位寄存器: 发送移位寄存器的作用就是把个字节的数据一位一位地移出去 标志位-----移位完成产生标志位 TXE : 在存器里就是二进制存储0101 0101, 硬件检测到你写入数据了, 就会检查当前移位寄存器是不是有数据正在移位; 如果没有这个0101 0101就会立刻全部移动到发送移位寄存器, 准备发送. 当数据从TDR移动到移位寄存器时会置一个标志位(TXE置1)叫TXE, 发送寄存器空, 就可以在TDR写入下一个数据了 当TXE标志位置1时, 数据其实还没有发送出去, 只要数据从TDR转移到发送移位寄存器了 , TXE就会置1我们就可以写入新的数据了 while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET); 检查标志位USART1的TEX标志为是否等于0-----TDR寄存器的数据有没有移动到发送移位寄存器里面去 RXNE: 和TXE相同的道理: 当数据从接收移位寄存器, 移动到移位RDR寄存器时会置一个标志位(RXNE置1), 这个也不用手动清除标志位, 和TXE原理相同 3:USART的基本结构 4:数据帧 5: 波特率发生器 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定 计算公式波特率 fPCLK2/1 / (16 * DIV) 配置USART1为9600的波特率 6:数据模式 HEX模式/十六进制模式/二进制模式以原始数据的形式显示 文本模式/字符模式以原始数据编码后的形式显示 三:案例
A:串口发送--单发送
1:连接图 2:函数介绍
在stm32f10x usart.h文件中-----配置USART void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct) 在stm32f10x usart.h文件中-----发送数据 void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 在stm32f10x usart.h文件中-----检查某个寄存器的中断标志位 FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); USART_GetFlagStatus : 获取标志位状态的函数
3:代码
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
#include stdarg.h
void serial_init(void){//开启RCC时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置TI的GPIOGPIO_InitTypeDef GPIO_structinit;GPIO_structinit.GPIO_ModeGPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设GPIO_structinit.GPIO_PinGPIO_Pin_9;GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);//USART的配置USART_InitTypeDef USART_structinit;USART_structinit.USART_BaudRate9600;//通信的波特率USART_structinit.USART_HardwareFlowControlUSART_HardwareFlowControl_None;//硬件流控制--不使用USART_structinit.USART_ModeUSART_Mode_Tx;//配置GPIO为TX发送数据模式USART_structinit.USART_ParityUSART_Parity_No; //选择校验方式---无校验USART_structinit.USART_StopBitsUSART_StopBits_1;//停止的位数USART_structinit.USART_WordLengthUSART_WordLength_8b;//发送或接收的数据位---我们使用8为USART_Init(USART1,USART_structinit);//启动USARTUSART_Cmd(USART1,ENABLE);
}//发送数据
void Serial_SendByte(uint8_t Byte)
{ //发送数据的函数USART_SendData(USART1,Byte);//检查某个寄存器的中断标志位while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET);/*在完成数据发送的时候,TXE置1;下次发送数据自动置0,所以不用手动清除中断标志位.TXE:发送数据寄存器:当TDR寄存器中的数据被硬件转移到移位寄存器的时候该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1则产生中断。对USART DR的写操作将该位清零。0:数据还没有被转移到移位寄存器1:数据已经被转移到移位寄存器。注意:单缓冲器传输中使用该位。*/
} //发送一个数组;Array:传递的数组,Length数组的长度
void Serial_SendArray(uint8_t *Array, uint16_t Length){for(uint16_t i0;iLength;i){Serial_SendByte(Array[i]);}}//发送一个字符串
//发送字符串时自带结束标志位
void Serial_Sendstr(char *str)
{for (uint8_t i0 ; str[i]!0;i){//也可写为str[i]!\0Serial_SendByte(str[i]);}
}//取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉%10就是把左边去掉
//12345取3 12345/100(10^2)%103 x:从左往右数,不包含要取的数字
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}
//传输数字,把数字中的每一位取出,然后发送出去
//Number:传输的数字; Length:传输数字的长度
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函数的底层
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}//spinrf函数的封装
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_Sendstr(String);
}int main(void)
{OLED_Init();serial_init();Serial_SendByte(0x41);uint8_t Array[]{0x01,0x08,0x43,0x45};Serial_SendArray(Array,4);//换行的话需要加上\r\nSerial_Sendstr(Helloword\r\n);Serial_Sendstr(123\r\n);Serial_SendNumber(231,3);//使得printf函数移植方法printf(NUM%d,,123);char String[100];sprintf(String, \r\nNum3%d, 333);Serial_Sendstr(String);Serial_Printf(\r\nNum4%d, 444);Serial_Printf(\r\n);printf(你好世界);while (1){}
}printf函数三中移植的方法----使得可以通过串口通信打印到其他外设: 1:通过重写printf函数的底层,使他通过串口 //printf函数的底层
#include stdio.h
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}2:直接使用sprintf函数 char String[100];sprintf(String, \r\nNum3%d, 333);Serial_Sendstr(String); 3:对sprintf函数进行封装 //spinrf函数的封装
#include stdarg.hvoid Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_Sendstr(String); //调用的Serial_Sendstr函数在代码里面可查询
}Serial_Printf(\r\nNum4%d, 444); B:串口发送接收 对于串口接收来说可以使用查询和中断两种方法 1:函数介绍
在stm32f10x usart.h文件中-----返回外设最近接收的数据 uint16_t USART_ReceiveData(USART_TypeDef* USARTx); USART_ReceiveData : 返回USARTx外设最近接收到的数据 2:串口发送接收 -----查询代码
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
#include stdio.h
#include stdarg.h
void serial_init(void){//开启RCC时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置TI的GPIOGPIO_InitTypeDef GPIO_structinit;GPIO_structinit.GPIO_ModeGPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设GPIO_structinit.GPIO_PinGPIO_Pin_9; //PA9在引脚定义中为TX发送GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);GPIO_structinit.GPIO_ModeGPIO_Mode_IPU;//上拉输入GPIO_structinit.GPIO_PinGPIO_Pin_10; PA9在引脚定义中为RX接收GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);//USART的配置USART_InitTypeDef USART_structinit;USART_structinit.USART_BaudRate9600;//通信的波特率USART_structinit.USART_HardwareFlowControlUSART_HardwareFlowControl_None;//硬件流控制--不使用USART_structinit.USART_ModeUSART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式USART_structinit.USART_ParityUSART_Parity_No; //选择校验方式---无校验USART_structinit.USART_StopBitsUSART_StopBits_1;//停止的位数USART_structinit.USART_WordLengthUSART_WordLength_8b;//发送或接收的数据位---我们使用8为USART_Init(USART1,USART_structinit);//启动USARTUSART_Cmd(USART1,ENABLE);
}//发送数据
void Serial_SendByte(uint8_t Byte)
{ //发送数据的函数USART_SendData(USART1,Byte);//检查某个寄存器的中断标志位while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET);/*在完成数据发送的时候,TXE置1;下次发送数据自动置0,所以不用手动清除中断标志位.TXE:发送数据寄存器:当TDR寄存器中的数据被硬件转移到移位寄存器的时候该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1则产生中断。对USART DR的写操作将该位清零。0:数据还没有被转移到移位寄存器1:数据已经被转移到移位寄存器。注意:单缓冲器传输中使用该位。*/
} //发送一个数组;Array:传递的数组,Length数组的长度
void Serial_SendArray(uint8_t *Array, uint16_t Length){for(uint16_t i0;iLength;i){Serial_SendByte(Array[i]);}}//发送一个字符串
//发送字符串时自带结束标志位
void Serial_Sendstr(char *str)
{for (uint8_t i0 ; str[i]!0;i){//也可写为str[i]!\0Serial_SendByte(str[i]);}
}//取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉%10就是把左边去掉
//12345取3 12345/100(10^2)%103 x:从左往右数,不包含要取的数字
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}
//传输数字,把数字中的每一位取出,然后发送出去
//Number:传输的数字; Length:传输数字的长度
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函数的底层
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}//spinrf函数的封装
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_Sendstr(String);
}uint8_t RXdata;int main(void)
{ OLED_Init();serial_init();while (1){if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)SET){RXdataUSART_ReceiveData(USART1);//返回外设最近接收的数据OLED_ShowHexNum(1,1,RXdata,3);}}
}PA9口和PA10口的GPIO的模式不同 引脚的定义 PA9-----TX发送引脚; PA10--------Rx接收引脚 GPIO工作模式的选择 输入工作模式----------将引脚的信号读取到微型控制器---PA10 输出工作模式---------将微型控制器信号读取到阴极段--PA9 发送输出; 接收输入 3:函数介绍
在stm32f10x usart.h文件中-----使能中断输出信号
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState); USART_ITConfig : 启用或禁用指定的USART中断 配置NVIC在misc.h文件中的函数-----配置NVIC void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); NVIC_PriorityGroupConfig:用来中断分组的参数是中断分组的方式
NVIC_Init: 根据结构体里面指定的参数初始化NMIC 在stm32f10x usart.h文件中-----检查某个寄存器的中断标志位 FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); 在stm32f10x usart.h文件中-----清除中断标志位 void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT) USART_ClearITPendingBit : 清除USARTx的中断挂起位 4:串口发送接收 -----中断代码 在开启USART之前 1: 启动USART的RXNE中断, USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); 2: 配置NVIC #include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
#include stdio.h
#include stdarg.h
uint8_t Serial_RxData;
uint8_t Serial_RxFlag;
void serial_init(void){//开启RCC时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置TI的GPIOGPIO_InitTypeDef GPIO_structinit;GPIO_structinit.GPIO_ModeGPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设GPIO_structinit.GPIO_PinGPIO_Pin_9; //PA9在引脚定义中为TX发送GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);GPIO_structinit.GPIO_ModeGPIO_Mode_IPU;//上拉输入GPIO_structinit.GPIO_PinGPIO_Pin_10; PA9在引脚定义中为RX接收GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);//USART的配置USART_InitTypeDef USART_structinit;USART_structinit.USART_BaudRate9600;//通信的波特率USART_structinit.USART_HardwareFlowControlUSART_HardwareFlowControl_None;//硬件流控制--不使用USART_structinit.USART_ModeUSART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式USART_structinit.USART_ParityUSART_Parity_No; //选择校验方式---无校验USART_structinit.USART_StopBitsUSART_StopBits_1;//停止的位数USART_structinit.USART_WordLengthUSART_WordLength_8b;//发送或接收的数据位---我们使用8为USART_Init(USART1,USART_structinit);//启动USART的RXNE中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_initstruct;NVIC_initstruct.NVIC_IRQChannelUSART1_IRQn; //通道NVIC_initstruct.NVIC_IRQChannelCmdENABLE;NVIC_initstruct.NVIC_IRQChannelPreemptionPriority1;NVIC_initstruct.NVIC_IRQChannelSubPriority1;NVIC_Init(NVIC_initstruct);//启动USARTUSART_Cmd(USART1,ENABLE);
}//发送数据
void Serial_SendByte(uint8_t Byte)
{ //发送数据的函数USART_SendData(USART1,Byte);//检查某个寄存器的中断标志位while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET);/*在完成数据发送的时候,TXE置1;下次发送数据自动置0,所以不用手动清除中断标志位.TXE:发送数据寄存器:当TDR寄存器中的数据被硬件转移到移位寄存器的时候该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1则产生中断。对USART DR的写操作将该位清零。0:数据还没有被转移到移位寄存器1:数据已经被转移到移位寄存器。注意:单缓冲器传输中使用该位。*/
} //发送一个数组;Array:传递的数组,Length数组的长度
void Serial_SendArray(uint8_t *Array, uint16_t Length){for(uint16_t i0;iLength;i){Serial_SendByte(Array[i]);}}//发送一个字符串
//发送字符串时自带结束标志位
void Serial_Sendstr(char *str)
{for (uint8_t i0 ; str[i]!0;i){//也可写为str[i]!\0Serial_SendByte(str[i]);}
}//取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉%10就是把左边去掉
//12345取3 12345/100(10^2)%103 x:从左往右数,不包含要取的数字
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}
//传输数字,把数字中的每一位取出,然后发送出去
//Number:传输的数字; Length:传输数字的长度
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函数的底层
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}//spinrf函数的封装
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_Sendstr(String);
}uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag 1){Serial_RxFlag 0;return 1;}return 0;
}uint8_t Serial_GetRxData(void)
{return Serial_RxData;
}//USART的中断函数----在启动文件中找
void USART1_IRQHandler(){//判断RXNE的中断标志位if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)SET){Serial_RxData USART_ReceiveData(USART1);Serial_RxFlag 1;USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);}
}uint8_t RxData;int main(void)
{OLED_Init();OLED_ShowString(1, 1, RxData:);serial_init();while (1){if (Serial_GetRxFlag() 1){RxData Serial_GetRxData();Serial_SendByte(RxData);OLED_ShowHexNum(1, 8, RxData, 3);}}
}四:USART串口数据包
1:简历 数据包作用 : 把一个个单独的数据给打包起来, 方便我们进行多字节的数据通信 需要把多个字节打包为一个整体进行发送, 极大的方便了我们的使用 2:HEX数据包 数据包的包头和包尾是可以自己进行设定的, 它并不是一个固定的 固定包长, 含包头包尾 : 每个数据包的长度都固定不变 (自己定数据包的长度) , 数据包前面是包头后面是包尾 可变包长含包头包尾 : 每个数据包的长度可以是不一样的, 数据包前面是包头后面是包尾 包头包尾和数据载荷重复的问题 当包头包尾的数据和传输的数据重复时-----定义FF为包头FE为包尾, 如果我传输的数据本身就是FF和FE, 可能会引起误判 解决: A: 限制载荷数据的范围-------可以在发送的时候对数据进行限幅. 比如XYZ3个数据变化范围都可以是0~100. 我们可以在载荷中只发送0~100的数据 B:无法避免载荷数据和包头包尾重复-----尽量使用固定长度的数据包, 由于载荷数据是固定的, 只要通过包头和包尾对齐的数据. 就可以哪个数据应该是包头包尾哪个数据应该是载荷数据. 在接收载荷数据的时候我们并不会判断它是否是包头包尾, 而在接收包头包尾的时候我们会判断它是不是确实是包头包尾 C:增加包头包尾的数量---------尽量呈现出载荷数据出现不了的状态 固定包长和可变包长的选择问题 载荷会出现和包头包尾重复的情况--------最好选择固定包长 反之选择可变包长 3: 文本数据包 在HEX数据包里面数据都是以原始的字节数据本身呈现的, 而在文本数据包里面每个字节就经过了一层编码和译码, 最终表现出来的就是文本格式. 其实都还是一个字节的HEX数据 4:HEX数据包接收 每收到一个字节程序都会进一遍中断 . 状态机: 最开始S0, 收到一个数据进中断, 判断数据是不是包头FF, 如果是FF则代表收到包头, 之后置S1退出中断结束. 下次再进中断根据S1就可以进行接收数据的程序了. 这时再收到数据我们就直接把它存在数组中, 另外再用一个变量记录收了多少个数据, 如果没收够规定的数据就一直是接收状态, 如果收够了就置S2. 下次中断时就可以进入下一个状态了. 最后一个状态就是等待包尾了,判断数据是不是FE, 这样就可以置S0回到最初的状态开始下一个轮回 5:文本数据包接收 6: 案例
1:连接图 A:发送HEX数据包---固定数据长度 #include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
#include Key.h
#include stdio.h
#include stdarg.huint8_t Serial_RxFlag;//一个数据包发送完成的标志位uint8_t Serial_TXPacket[4];//存放发送的数据---STM32向外设发送
uint8_t Serial_RXPacket[4];//存放接收的数据---外设向STM32发送数据void serial_init(void){//开启RCC时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置TI的GPIOGPIO_InitTypeDef GPIO_structinit;GPIO_structinit.GPIO_ModeGPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设GPIO_structinit.GPIO_PinGPIO_Pin_9; //PA9在引脚定义中为TX发送GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);GPIO_structinit.GPIO_ModeGPIO_Mode_IPU;//上拉输入GPIO_structinit.GPIO_PinGPIO_Pin_10; PA9在引脚定义中为RX接收GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);//USART的配置USART_InitTypeDef USART_structinit;USART_structinit.USART_BaudRate9600;//通信的波特率USART_structinit.USART_HardwareFlowControlUSART_HardwareFlowControl_None;//硬件流控制--不使用USART_structinit.USART_ModeUSART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式USART_structinit.USART_ParityUSART_Parity_No; //选择校验方式---无校验USART_structinit.USART_StopBitsUSART_StopBits_1;//停止的位数USART_structinit.USART_WordLengthUSART_WordLength_8b;//发送或接收的数据位---我们使用8为USART_Init(USART1,USART_structinit);//启动USART的RXNE中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_initstruct;NVIC_initstruct.NVIC_IRQChannelUSART1_IRQn; //通道NVIC_initstruct.NVIC_IRQChannelCmdENABLE;NVIC_initstruct.NVIC_IRQChannelPreemptionPriority1;NVIC_initstruct.NVIC_IRQChannelSubPriority1;NVIC_Init(NVIC_initstruct);//启动USARTUSART_Cmd(USART1,ENABLE);
}/*** brief 发送一个字节的数据--8位STM32向外设发送通过USARTx外设传输单个数据* param 需要发送的数据* retval 无*/
void Serial_SendByte(uint8_t Byte)
{ //发送数据的函数USART_SendData(USART1,Byte);//检查某个寄存器的中断标志位while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET);/*在完成数据发送的时候,TXE置1;下次发送数据自动置0,所以不用手动清除中断标志位.TXE:发送数据寄存器:当TDR寄存器中的数据被硬件转移到移位寄存器的时候该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1则产生中断。对USART DR的写操作将该位清零。0:数据还没有被转移到移位寄存器1:数据已经被转移到移位寄存器。注意:单缓冲器传输中使用该位。*/
} /*** brief 发送一个数组* param Array:传递的数组* param Length数组的长度* retval 无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length){for(uint16_t i0;iLength;i){Serial_SendByte(Array[i]);}}/*** brief 发送一个字符串* param 需要发送的字符串* retval 无*/
void Serial_Sendstr(char *str)
{ //发送字符串时自带结束标志位for (uint8_t i0 ; str[i]!0;i){//也可写为str[i]!\0Serial_SendByte(str[i]);}
}/*** brief 取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉%10就是把左边去掉12345取3 12345/100(10^2)%103 x:从左往右数,不包含要取的数字* param X:底数* param Y:指数* retval 无*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}/*** brief 输数字,把数字中的每一位取出,然后发送出去* param 传输的数字* param 传输数字的长度* retval 无*/
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函数的底层
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}//spinrf函数的封装
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_Sendstr(String);
}/*** brief 知道把中断标志位置位0* retval 无*/
uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag 1){Serial_RxFlag 0;return 1;}return 0;
}//------------------HEX包----------------------------
/*** brief 发送HEX数据包---STM32发送到其他外设* retval 无*/
uint16_t Send_HEX()
{Serial_SendByte(0XFF);Serial_SendArray(Serial_TXPacket,4);Serial_SendByte(0XFE);
}/**
* brief 接收HEX数据包---其他外设发送到STM32上面* retval 无*/
void USART1_IRQHandler(){//每收到一个字节程序都会进一遍中断 //static---- 函数进入只会初始化一次,在函数退出后数据仍然有效static uint8_t RxState 0;//状态机的标志位static uint8_t pRxPacket 0;//数据的包长//判断RXNE的中断标志位if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)SET){uint8_t dataUSART_ReceiveData(USART1);//32接收的数据if (RxState0){if (data0XFF){RxState1;} }else if (RxState1){Serial_RXPacket[pRxPacket]data; //右边的值赋值给等号的左边pRxPacket;if (pRxPacket4){pRxPacket0;RxState2;} }else if (RxState2){if (data0xFE){RxState0;Serial_RxFlag1;//一个数据包发送完成的标志位}}USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);}
}uint8_t RxData;
uint8_t num;
int main(void)
{Key_Init();OLED_Init();serial_init();OLED_ShowString(1,1,RX_Data:);OLED_ShowString(3,1,TX_Data:);Serial_TXPacket[0]0x01;Serial_TXPacket[1]0x02;Serial_TXPacket[2]0x03;Serial_TXPacket[3]0x04;while (1){ numKey_GetNum();if (num1){//发送数据Serial_TXPacket[0];Serial_TXPacket[1];Serial_TXPacket[2];Serial_TXPacket[3];Send_HEX();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);}}
}2:连接图 B:发送文本数据包---数据长度 #include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include Serial.h
#include Key.h
#include LED.h
#include string.h
#include stdio.h
#include stdarg.huint8_t Serial_RxFlag;//一个数据包发送完成的标志位uint8_t Serial_TXPacket[4];//存放发送的数据---STM32向外设发送
char Serial_RXPacket[100];//存放接收的数据---外设向STM32发送数据void serial_init(void){//开启RCC时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置TI的GPIOGPIO_InitTypeDef GPIO_structinit;GPIO_structinit.GPIO_ModeGPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设GPIO_structinit.GPIO_PinGPIO_Pin_9; //PA9在引脚定义中为TX发送GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);GPIO_structinit.GPIO_ModeGPIO_Mode_IPU;//上拉输入GPIO_structinit.GPIO_PinGPIO_Pin_10; PA9在引脚定义中为RX接收GPIO_structinit.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_structinit);//USART的配置USART_InitTypeDef USART_structinit;USART_structinit.USART_BaudRate9600;//通信的波特率USART_structinit.USART_HardwareFlowControlUSART_HardwareFlowControl_None;//硬件流控制--不使用USART_structinit.USART_ModeUSART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式USART_structinit.USART_ParityUSART_Parity_No; //选择校验方式---无校验USART_structinit.USART_StopBitsUSART_StopBits_1;//停止的位数USART_structinit.USART_WordLengthUSART_WordLength_8b;//发送或接收的数据位---我们使用8为USART_Init(USART1,USART_structinit);//启动USART的RXNE中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_initstruct;NVIC_initstruct.NVIC_IRQChannelUSART1_IRQn; //通道NVIC_initstruct.NVIC_IRQChannelCmdENABLE;NVIC_initstruct.NVIC_IRQChannelPreemptionPriority1;NVIC_initstruct.NVIC_IRQChannelSubPriority1;NVIC_Init(NVIC_initstruct);//启动USARTUSART_Cmd(USART1,ENABLE);
}/*** brief 发送一个字节的数据--8位STM32向外设发送通过USARTx外设传输单个数据* param 需要发送的数据* retval 无*/
void Serial_SendByte(uint8_t Byte)
{ //发送数据的函数USART_SendData(USART1,Byte);//检查某个寄存器的中断标志位while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET);/*在完成数据发送的时候,TXE置1;下次发送数据自动置0,所以不用手动清除中断标志位.TXE:发送数据寄存器:当TDR寄存器中的数据被硬件转移到移位寄存器的时候该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1则产生中断。对USART DR的写操作将该位清零。0:数据还没有被转移到移位寄存器1:数据已经被转移到移位寄存器。注意:单缓冲器传输中使用该位。*/
} /*** brief 发送一个数组* param Array:传递的数组* param Length数组的长度* retval 无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length){for(uint16_t i0;iLength;i){Serial_SendByte(Array[i]);}}/*** brief 发送一个字符串* param 需要发送的字符串* retval 无*/
void Serial_Sendstr(char *str)
{ //发送字符串时自带结束标志位for (uint8_t i0 ; str[i]!0;i){//也可写为str[i]!\0Serial_SendByte(str[i]);}
}/*** brief 取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉%10就是把左边去掉12345取3 12345/100(10^2)%103 x:从左往右数,不包含要取的数字* param X:底数* param Y:指数* retval 无*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{uint32_t Result 1;while (Y --){Result * X;}return Result;
}/*** brief 输数字,把数字中的每一位取出,然后发送出去* param 传输的数字* param 传输数字的长度* retval 无*/
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函数的底层
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}//spinrf函数的封装
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_Sendstr(String);
}/*** brief 知道把中断标志位置位0* retval 无*/
uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag 1){Serial_RxFlag 0;return 1;}return 0;
}//------------------HEX包----------------------------
/*** brief 发送HEX数据包---STM32发送到其他外设* retval 无*/
uint16_t Send_HEX()
{Serial_SendByte(0XFF);Serial_SendArray(Serial_TXPacket,4);Serial_SendByte(0XFE);
}/**
* brief 接收HEX数据包---其他外设发送到STM32上面* retval 无*/void USART1_IRQHandler(){//每收到一个字节程序都会进一遍中断 //static---- 函数进入只会初始化一次,在函数退出后数据仍然有效static uint8_t RxState 0;//状态机的标志位static uint8_t pRxPacket 0;//数据的包长//判断RXNE的中断标志位if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)SET){uint8_t dataUSART_ReceiveData(USART1);//32接收的数据if (RxState0){if (data){RxState1;pRxPacket0;} }else if (RxState1){if (data\r) //\r 的意思是 回车。将当前位置移到本行的开头。{RxState2;}else{Serial_RXPacket[pRxPacket]data; //右边的值赋值给等号的左边pRxPacket;}}else if (RxState2){if (data\n)//\n 的意思是回车换行。将当前位置移到下一行的开头。{RxState0;Serial_RXPacket[pRxPacket]\0;Serial_RxFlag1;//一个数据包发送完成的标志位}}USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);}
}uint8_t RxData;
uint8_t num;
int main(void)
{OLED_Init();serial_init();LED_Init();OLED_ShowString(1,1,RX_Data:);OLED_ShowString(3,1,TX_Data:);while (1){//接收数据if (Serial_RxFlag1){ OLED_ShowString(4,1, ); OLED_ShowString(4,1,Serial_RXPacket);if (strcmp(Serial_RXPacket, LED_ON)0){LED1_ON();Serial_Sendstr(LED_ON_OK\r\n);//使用32把数据发送到外设中去OLED_ShowString(2, 1, );OLED_ShowString(2, 1, LED_ON_OK);}else if(strcmp(Serial_RXPacket, LED_OFF)0){LED1_OFF();Serial_Sendstr(LED_Off_OK\r\n);//使用32把数据发送到外设中去OLED_ShowString(2, 1, );OLED_ShowString(2, 1, LED_Off_OK);}else{ Serial_Sendstr(ERROR_COMMAND\r\n);//使用32把数据发送到外设中去OLED_ShowString(2, 1, );OLED_ShowString(2,1,ERROR_COMMAND);}Serial_RxFlag 0;}}
}