文章目录
- 一.半双工,全双工和单工区别
- 二.通信协议基础知识
- 三.串口通信区分
- 四.串口通信的基础知识
- 五.USART串口外设
- 六.USART框图
- 七.USART基本结构
- 八.数据模式
- 九.单片机通过串口发送一个字节,数组,字符串和数字到电脑上
- 9.1 接线
- 9.2 代码
- 十.printf函数的移值方法(单片机输出到电脑上)
- 10.1 方法
- 10.2 写法1代码
- 10.3 写法2代码
- 十一.电脑发送数据到单片机上通过OLED显示
- 11.1 查询方法判断是否显示数据代码
- 11.2 中断的方式来判断是否显示数据代码(一字节)
- 十二.USART数据包
- 12.1 发送HEX数据包和文本数据包
- 12.2 接受HEX数据包和文本数据包
- 12.3 发送数据包和接收数据包以HEX形式的代码
- 12.4 接收数据包以文本形式的代码
一.半双工,全双工和单工区别
二.通信协议基础知识
三.串口通信区分
下图是51单片机串口通信的区分,跟stm32差不多,51单片机的串口通信的uart是只能异步通信,而stm32串口的usart是同步跟异步都可以选择,同步靠时钟线,异步靠比特率,比特率就是通信速度不一样,可以调成一样的。
四.串口通信的基础知识
(1).图一是串口通信的基本介绍。
(2).图二是串口通信的连线。
(3).图三是电平标准的介绍,我们单片机最常见的是TTL电平。
(4).在图四中,每一字节都装载在一个数据帧里面,每个数据帧都由起始位,数据位和停止位组成,这里数据位有8个,代表一个字节有8位,也可以在数据位加一个奇偶校验位,这样数据位就是9位,其中有效载荷是前8位,代表一个字节,校验位跟在有效载荷后面,占1位,1000bps就是表示比特率为1000,就表示,1s要发1000位,就是1/1000=1ms,每一位的时间就是1ms,也就是D0的时间是1ms,没发数据时候(空闲的时候)是高电平,然后就是起始位,发送数据前,发送一个高电平,来打破空闲状态的高电平,产生一个下降沿,告诉接收设备,这一帧数据要开始了,所以相对应的发送完数据后,需要有一个停止位,告诉接收设备发送完了,给高电平,这里数据位的LSB意思是低位在前,MSB意思是高位在后,比如发送0x0F,也就是0000 1111,也就是发送1111 0000,如果奇偶校验可以判断数据传输是不是出错了,如果数据出错了,可以选择丢弃或者要求重新传输,校验可以选择3种方式,无校验,奇校验,偶校验,无校验就是不需要校验位,就是数据位只有8位,如果使用了奇校验,那么包括校验位在内的9位数据就会出现一个奇数个1,比如传输0000 1111,目前总共4个1,是偶数个1,那么校验位就需要在补一个1,连同校验位就是0000 1111 1,保证1为奇数,如果是0000 1110,此时3个1,是奇数1,那么校验位就补一个0,就是0000 1110 0,所以1的个数还是奇数,发送设备先确定这些数据然后给奇数校验位后在开始传输,接收设备接收到数据后,接收到1的个数还是奇数,就认为数据没有出错,如果在传输过程中,有一位数据为干扰变成0或者由0变成1,那么整个数的奇偶特性就会变,就认为传输数据出错,偶校验就是保证1的个数是偶数,如果有两位数据出错,奇偶特性不变,那校验不出来了,所以奇偶校验位只能保证一定程度上的数据校验,如果想要更高的检出率,可以了解一下CRC校验。
(5).在图五中串口通信过程中,在STM32中,这个根据字节数据翻转高低电平,是由USART外设自动完成的,不用我们操心,跟51单片机一样,当然也可以软件模拟产生这样的波形,这里比特率9600,就是1/9600=104us的时间,然后定时器定时一个104us的时间,时间到了之后,按照数据帧要求,调用GPIO_WriteBit置高低电平,产生和这个一模一样的波形,这样也可以完成串口通信,TX引脚发送,就是置高低电平,那么在RX引脚接收,显然就是读取高低电平了,这也是USART外设自动完成的,不用我们操心,如果想软件模拟的话就是定时调用GPIO_ReadInputDataBit的每一位,最终拼成一个字节,当然接收的时候还需要一个外部中断,在起始位的下降沿触发,进入接收状态,并且对齐采样时钟,然后依次采样8次,所以硬件就是我们配置好,然后需要指定发送的数据,硬件外设电路会自动操作完成,软件就是用代码模拟高低电平。
图一
图二
图三
图四
图五
五.USART串口外设
(1).Uart是异步收发器,USART是同步和异步收发器,STM32的USART同步只是多了个时钟输出而已,它只支持时钟输出,不支持时钟输入,这个同步模式更多是为了介入别的协议或者特殊用途而设计的,并不支持两个USART设备之间进行同步通信,所以学习串口还是主要是异步通信。
(2).硬件就是我们配置好,然后需要指定发送的数据,硬件外设电路会自动操作完成,软件就是用代码模拟高低电平。
(3).自带波特率发生器,它就是一个分频器,比如我们APB2总线给个72MHZ的频率,然后波特率发生器进行一个分频,得到我们想要的波特率时钟,在这个时钟下,进行收发,就是我们指定通信的波特率。
(4).同步模式就是多了一个时钟CLK的输出,硬件流控制就是比如A设备的TX向B设备的RX发送数据,A设备一直在发,发的太快了,B处理不过来,如果没有硬件流控制,那么B就只能抛弃新数据或者覆盖原数据了,如果有硬件流控制,在硬件电路上,就会多一根线,如果B没有准备好,就置高电平,如果准备好了,就置低电平。
(5).USART1是APB2总线上的设备,剩下的都是APB1总线的设备。
六.USART框图
(1).图一的红圈位置就是TX和RX引脚,然后黄圈的SW_RX和IRDA_OUT/IN是智能卡和IrDA通信的引脚,这些我们没用,就不用管,TX和RX引脚分别通向绿线和蓝线位置,紫色区域就是串口的数据寄存器了,发送或者接收数据就存在这里,数据寄存器有两个,发送寄存器和接收寄存器,但是寄存器的地址都是一样的,就跟51单片机串口SBUF寄存器一样,在程序上只表现为一个寄存器,就是数据寄存器DR,但是实际硬件中,是分成两个寄存器,一个用于发送TDR,另一个用于接收RDR,TDR是只写,RDR是只读的,然后发送移位寄存器作用就是把一个字节一位一位的移出去,正好对应串口协议波形的数据位,这两个寄存器是怎么工作的呢?比如你在发送寄存器写入0x55,在二进制就是0101 0101,那么此时硬件检测到你写入数据了,它就会检查,当前移位寄存器是不是有数据正在移位,如果没有,这个0101 0101就会立刻全部移动到发送移位寄存器中,准备发送,当数据从TDR移动到发送移位寄存器中,会置一个标志位,叫TXE(发送寄存器空),这个标志位如果置1了,我们就可以在TDR写入下一个数据了,注意一下,TXE置1时,数据其实还没有发送出去,只要数据从TDR转移到发送移位寄存器了,TXE就会置1,我们就可以写入新的数据,然后发送移位寄存器就会在下面(棕线)这里的发送器控制驱动下,向右移位,然后一位一位的向绿线TX输出到引脚,RX引脚也是一样的,在接收器控制下,一位一位的读取RX电平,把接收到的数据先放在最高位,然后一位一位向右移,移位八次后,就可以接收一个字节了,当一个字节移位成功后,这个数据就会一下子全部转移到接收数据寄存器RDR里来,在转移过程也会置一个标志位RXNE(接收数据寄存器非空),当我们检测到RXNE置1后,就可以把数据读走了。
(2).在图二中,硬件数据流控就是防止传输太快了,接收设备还没有来得及处理,就会丢弃或者覆盖数据的现象,有了流控,就可以避免这个问题,这里流控有两个引脚,一个是nRTS,一个是nCTS,nRTS是请求发送,是输出脚,,也就是告诉别人当前能不能接收,nCTS是清除发送,是输入脚,也就是接收别人nRTS的信号,n就是低电平有效,这个流控是怎么玩的呢,首先得找另一个支持流控的串口,如图三,它的TX就接到RX,然后我的nRTS要输出一个能不能接收的反馈信号,接到对方的CTS,,当我能接收的时候,nRTS就置低电平,请求对方发送,对方的nCTS接收到后,就可以一直发送,如果接收寄存器没有及时处理好数据,那么我nRTS就置高电平,对方的nCTS接收到后,就会暂停发送。
(3).然后接着看图四这个模块——SCLK控制,这部分电路用于产生同步的时钟信号,它是配合发送移位寄存器输出的,发送移位寄存器每移位一次,同步时钟电平就跳一个周期,时钟告诉对方,我移出去一位数据了,这个时钟只支持输出,不支持输入,然后就可以用这个同步模式兼容SPI协议等等,然后图四中的唤醒单元,这部分作用是实现串口挂载多设备,串口一般是点对点,点对点只支持两个设备互相通信,而多设备,在一条总线上,可以接多个从设备,每个设备分配一个地址,我想跟某个设备通信,就先进行寻址,确定通信对象再进行数据收发,这个唤醒单元就是可以实现多设备的功能,在图四中的USART地址这里分配一个地址,当你指定地址时,此设备唤醒开始工作,当你发送别的设备地址时,别的设备就唤醒工作,这个设备没有收到地址,就会保持沉默,这样就可以实现多功能通信了。
(4).图五红圈是中断输出控制,中断申请位就是状态寄存器(黄圈)这里的各种标志位,状态寄存器这里有两个重要的标志位比较重要,一个是TXE发送寄存器空,另一个是RXNE接收寄存器空,TXE,这个标志位如果置1了,我们就可以在TDR写入下一个数据了,RXNE置1后,就可以把数据读走了,中断控制这里就是配置能不能通向NVIC。
(5).然后图六是波特率发送器其实就是分频器,APB时钟进行分频,得到发送或者接收移位的时钟,fPCLKx(x=1或者2),USART1挂载在APB2,所以就是PCLK2的时钟,一般是72MHZ,其他的USART都挂载在APB1,所以是PCLK1的时钟,一般是36MHZ,之后这个时钟进行分频,除于一个USARTDIV的分频系数,USARTDIV里面就是在它的右边黄圈的位置,分为整数部分和小数部分,,因为有一些波特率,用72M除一个整数的话,可能除不尽,有误差,这里的分频系数是支持小数点后4位的,分频就更加精准,然后发送器波特率控制(TE)为1,就是发送器使能了,发送部分的波特率就有效,如果接收器波特率控制(RE)为1,就是接收器使能了,接收部分的波特率就有效了。
图一
图二
图三
图四
图五
图六
七.USART基本结构
(1).最左边波特率发生器是用于产生约定的通信速率的,,时钟来源是PCLK2或者1,经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器,发送和接收控制器用来控制发送移位和接收移位,之后发送数据寄存器和发送移位寄存器这两个寄存器的配合,将数据一位一位的移出去,通过GPIO的复用输出,输出到TX引脚,产生串口协议规定的波形,发送移位寄存器这里画了几个右移的符号,就是代表这个移位寄存器是往右移动的,是低位先行,当数据由数据寄存器转移到移位寄存器时,,会置一个TXE的标志位,TXE会置1,我们判断这个标志位就可以知道是不是可以写下一个数据了,接收部分也差不多,RX引脚的波形,通过GPIO输入,在接收控制器的控制下,一位一位的移入接收移位寄存器,,然后也是画了右移符号,所以也是低位先行,所以数据先从左边开始一位一位移进来,然后不断向右移,就是例如第一个给1,第二个给0,就是01,移动完一帧数据帧后,数据就会统一转运到接收数据寄存器,在转运的同时会置一个RXNE标志位,我们会检查这个标志位,就可以知道是不是收到数据了,这个标志位置1,就是收到一帧数据帧了,同时这个标志位可以去申请中断函数,然后快速的读取和保存数据。
(2).但是在软件层面,只有一个DR寄存器可以供我们读写,写入DR寄存器时,数据从图二上面这红线这条路进行,进行发送,读取DR时,数据从图二下面这条红线,进行接收,这就是USART进行串口数据收发的过程,最后右下角,开关控制,就是配置完后,用Cmd开启一下外设。
(3).图三就是波特率发生器就是分频器,发送器和接收器的波特率由波特率寄存器BRR里的DIV确定,图三就是BRR寄存器,里面就是分频系数DIV,DIV分为整数部分和小数部分,可以实现更细腻的分频,波特率和分频系数的关系,可以由图三这个计算公式进行计算,如果要9600的波特率,9600=72M/16*DIV,DIV=468.75,转化成二进制就是11101 0100.11。
图一
图二
图三
八.数据模式
九.单片机通过串口发送一个字节,数组,字符串和数字到电脑上
9.1 接线
9.2 代码
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"int main(void)
{OLED_Init();Serial_Init();Serial_SendByte('A');uint8_t MyArray[] = {0x42,0x43,0x44,0x45};Serial_SendArray(MyArray,4);Serial_SendString("helloworld\r\n");Serial_SendNumber(100,3);while (1){}
}
Serial.c
#include "stm32f10x.h" // Device headervoid Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//由外设USART控制输出,用复用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate=9600;//波特率USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制流,无USART_InitStruct.USART_Mode=USART_Mode_Tx;//发送USART_InitStruct.USART_Parity=USART_Parity_No;//校验位,无USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位1位USART_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度,8位USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);}void Serial_SendByte(uint8_t Byte)//发送一个字节-8位
{USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待数据一位一位传输过去,如果传输过去了标志位就变成1了,就跳出循环,不需要清除标志位,下一次传输的时候会自动清除}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)//发送字符串
{uint8_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+0x30);}}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device headervoid 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);#endif
十.printf函数的移值方法(单片机输出到电脑上)
10.1 方法
10.2 写法1代码
main.c
#include "stm32f10x.h" // Device header
#include "Serial.h"int main(void)
{Serial_Init();printf("Num=%d\r\n",666);while (1){}
}
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//由外设USART控制输出,用复用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate=9600;//波特率USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制流,无USART_InitStruct.USART_Mode=USART_Mode_Tx;//发送USART_InitStruct.USART_Parity=USART_Parity_No;//校验位,无USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位1位USART_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度,8位USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);}void Serial_SendByte(uint8_t Byte)//发送一个字节-8位
{USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待数据一位一位传输过去,如果传输过去了标志位就变成1了,就跳出循环,不需要清除标志位,下一次传输的时候会自动清除}int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device header
#include "stdio.h"//里面有printf
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);#endif
展示效果
10.3 写法2代码
main.c
#include "stm32f10x.h" // Device header
#include "Serial.h"int main(void)
{Serial_Init();char String[100];sprintf(String,"Num=%d\r\n",666);//第一个参数打印输出的位置Serial_SendString(String);while (1){}
}
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//由外设USART控制输出,用复用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate=9600;//波特率USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制流,无USART_InitStruct.USART_Mode=USART_Mode_Tx;//发送USART_InitStruct.USART_Parity=USART_Parity_No;//校验位,无USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位1位USART_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度,8位USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);}void Serial_SendByte(uint8_t Byte)//发送一个字节-8位
{USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待数据一位一位传输过去,如果传输过去了标志位就变成1了,就跳出循环,不需要清除标志位,下一次传输的时候会自动清除}int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device header
#include "stdio.h"//里面有printf
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);#endif
展示效果
十一.电脑发送数据到单片机上通过OLED显示
11.1 查询方法判断是否显示数据代码
main.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "OLED.h"uint8_t RXData;int main(void)
{Serial_Init();OLED_Init();while (1){if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)//查询方法来判断数据是否被单片机接收{RXData=USART_ReceiveData(USART1);OLED_ShowHexNum(1,1,RXData,2);}}
}
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//由外设USART控制输出,用复用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//上拉或者浮空输入模式都可以GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate=9600;//波特率USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制流,无USART_InitStruct.USART_Mode=USART_Mode_Tx | USART_Mode_Rx;//发送和接收USART_InitStruct.USART_Parity=USART_Parity_No;//校验位,无USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位1位USART_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度,8位USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device headervoid Serial_Init(void);#endif
OLED.c
#include "stm32f10x.h"
#include "OLED_Font.h"/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_12, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_13, (BitAction)(x))/*引脚初始化*/
void OLED_I2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOB, &GPIO_InitStructure);OLED_W_SCL(1);OLED_W_SDA(1);
}/*** @brief I2C开始* @param 无* @retval 无*/
void OLED_I2C_Start(void)
{OLED_W_SDA(1);OLED_W_SCL(1);OLED_W_SDA(0);OLED_W_SCL(0);
}/*** @brief I2C停止* @param 无* @retval 无*/
void OLED_I2C_Stop(void)
{OLED_W_SDA(0);OLED_W_SCL(1);OLED_W_SDA(1);
}/*** @brief I2C发送一个字节* @param Byte 要发送的一个字节* @retval 无*/
void OLED_I2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i++){OLED_W_SDA(Byte & (0x80 >> i));OLED_W_SCL(1);OLED_W_SCL(0);}OLED_W_SCL(1); //额外的一个时钟,不处理应答信号OLED_W_SCL(0);
}/*** @brief OLED写命令* @param Command 要写入的命令* @retval 无*/
void OLED_WriteCommand(uint8_t Command)
{OLED_I2C_Start();OLED_I2C_SendByte(0x78); //从机地址OLED_I2C_SendByte(0x00); //写命令OLED_I2C_SendByte(Command); OLED_I2C_Stop();
}/*** @brief OLED写数据* @param Data 要写入的数据* @retval 无*/
void OLED_WriteData(uint8_t Data)
{OLED_I2C_Start();OLED_I2C_SendByte(0x78); //从机地址OLED_I2C_SendByte(0x40); //写数据OLED_I2C_SendByte(Data);OLED_I2C_Stop();
}/*** @brief OLED设置光标位置* @param Y 以左上角为原点,向下方向的坐标,范围:0~7* @param X 以左上角为原点,向右方向的坐标,范围:0~127* @retval 无*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{OLED_WriteCommand(0xB0 | Y); //设置Y位置OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}/*** @brief OLED清屏* @param 无* @retval 无*/
void OLED_Clear(void)
{ uint8_t i, j;for (j = 0; j < 8; j++){OLED_SetCursor(j, 0);for(i = 0; i < 128; i++){OLED_WriteData(0x00);}}
}/*** @brief OLED显示一个字符* @param Line 行位置,范围:1~4* @param Column 列位置,范围:1~16* @param Char 要显示的一个字符,范围:ASCII可见字符* @retval 无*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{ uint8_t i;OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容}OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容}
}/*** @brief OLED显示字符串* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param String 要显示的字符串,范围:ASCII可见字符* @retval 无*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i++){OLED_ShowChar(Line, Column + i, String[i]);}
}/*** @brief OLED次方函数* @retval 返回值等于X的Y次方*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;while (Y--){Result *= X;}return Result;
}/*** @brief OLED显示数字(十进制,正数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~4294967295* @param Length 要显示数字的长度,范围:1~10* @retval 无*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i++) {OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');}
}/*** @brief OLED显示数字(十进制,带符号数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:-2147483648~2147483647* @param Length 要显示数字的长度,范围:1~10* @retval 无*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{uint8_t i;uint32_t Number1;if (Number >= 0){OLED_ShowChar(Line, Column, '+');Number1 = Number;}else{OLED_ShowChar(Line, Column, '-');Number1 = -Number;}for (i = 0; i < Length; i++) {OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');}
}/*** @brief OLED显示数字(十六进制,正数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~0xFFFFFFFF* @param Length 要显示数字的长度,范围:1~8* @retval 无*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i, SingleNumber;for (i = 0; i < Length; i++) {SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;if (SingleNumber < 10){OLED_ShowChar(Line, Column + i, SingleNumber + '0');}else{OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');}}
}/*** @brief OLED显示数字(二进制,正数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~1111 1111 1111 1111* @param Length 要显示数字的长度,范围:1~16* @retval 无*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i++) {OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');}
}/*** @brief OLED初始化* @param 无* @retval 无*/
void OLED_Init(void)
{uint32_t i, j;for (i = 0; i < 1000; i++) //上电延时{for (j = 0; j < 1000; j++);}OLED_I2C_Init(); //端口初始化OLED_WriteCommand(0xAE); //关闭显示OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率OLED_WriteCommand(0x80);OLED_WriteCommand(0xA8); //设置多路复用率OLED_WriteCommand(0x3F);OLED_WriteCommand(0xD3); //设置显示偏移OLED_WriteCommand(0x00);OLED_WriteCommand(0x40); //设置显示开始行OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置OLED_WriteCommand(0xDA); //设置COM引脚硬件配置OLED_WriteCommand(0x12);OLED_WriteCommand(0x81); //设置对比度控制OLED_WriteCommand(0xCF);OLED_WriteCommand(0xD9); //设置预充电周期OLED_WriteCommand(0xF1);OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别OLED_WriteCommand(0x30);OLED_WriteCommand(0xA4); //设置整个显示打开/关闭OLED_WriteCommand(0xA6); //设置正常/倒转显示OLED_WriteCommand(0x8D); //设置充电泵OLED_WriteCommand(0x14);OLED_WriteCommand(0xAF); //开启显示OLED_Clear(); //OLED清屏
}
OLED.h
#ifndef __OLED_H
#define __OLED_H
#include "stm32f10x.h" // Device headervoid OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);#endif
OLED_Font.h
#ifndef __OLED_FONT_H
#define __OLED_FONT_H
#include "stm32f10x.h" // Device header/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 00x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 10x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 20x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 30x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 40xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 50x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 60x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 70x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 80x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 90x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 100x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 110x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 120x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 130x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 140x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 150x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 160x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 170x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 180x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 190x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 200x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 210x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 220x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 230x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 240x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 250x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 260x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 270x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 280x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 290x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 300x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 310xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 320x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 330x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 340xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 350x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 360x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 370x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 380xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 390x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 400x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 410x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 420x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 430x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 440x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 450x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 460xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 470x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 480xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 490x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 500x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 510x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 520x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 530x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 540xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 550x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 560x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 570x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 580x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 590x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 600x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 610x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 620x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 630x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 640x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 650x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 660x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 670x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 680x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 690x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 700x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 710x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 720x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 730x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 740x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 750x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 760x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 770x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 780x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 790x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 800x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 810x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 820x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 830x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 840x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 850x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 860x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 870x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 880x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 890x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 900x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 910x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 920x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 930x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};#endif
11.2 中断的方式来判断是否显示数据代码(一字节)
main.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "OLED.h"uint8_t RXData;int main(void)
{Serial_Init();OLED_Init();OLED_ShowString(1,1,"RXData:");while (1){if(Serial_GetRXFlag() == 1)//中断方法来判断数据是否被单片机接收{RXData=Serial_GetRxData();OLED_ShowHexNum(1,8,RXData,2);}}
}
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>uint8_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_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//由外设USART控制输出,用复用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//上拉或者浮空输入模式都可以GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate=9600;//波特率USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制流,无USART_InitStruct.USART_Mode=USART_Mode_Tx | USART_Mode_Rx;//发送和接收USART_InitStruct.USART_Parity=USART_Parity_No;//校验位,无USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位1位USART_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度,8位USART_Init(USART1,&USART_InitStruct);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStruct);USART_Cmd(USART1,ENABLE);
}uint8_t Serial_GetRXFlag(void)
{if(Serial_RXFlag == 1){Serial_RXFlag = 0;return 1;}return 0;
}uint8_t Serial_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);}
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device headervoid Serial_Init(void);
uint8_t Serial_GetRXFlag(void);
uint8_t Serial_GetRxData(void);#endif
OLED.c
#include "stm32f10x.h"
#include "OLED_Font.h"/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_12, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_13, (BitAction)(x))/*引脚初始化*/
void OLED_I2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOB, &GPIO_InitStructure);OLED_W_SCL(1);OLED_W_SDA(1);
}/*** @brief I2C开始* @param 无* @retval 无*/
void OLED_I2C_Start(void)
{OLED_W_SDA(1);OLED_W_SCL(1);OLED_W_SDA(0);OLED_W_SCL(0);
}/*** @brief I2C停止* @param 无* @retval 无*/
void OLED_I2C_Stop(void)
{OLED_W_SDA(0);OLED_W_SCL(1);OLED_W_SDA(1);
}/*** @brief I2C发送一个字节* @param Byte 要发送的一个字节* @retval 无*/
void OLED_I2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i++){OLED_W_SDA(Byte & (0x80 >> i));OLED_W_SCL(1);OLED_W_SCL(0);}OLED_W_SCL(1); //额外的一个时钟,不处理应答信号OLED_W_SCL(0);
}/*** @brief OLED写命令* @param Command 要写入的命令* @retval 无*/
void OLED_WriteCommand(uint8_t Command)
{OLED_I2C_Start();OLED_I2C_SendByte(0x78); //从机地址OLED_I2C_SendByte(0x00); //写命令OLED_I2C_SendByte(Command); OLED_I2C_Stop();
}/*** @brief OLED写数据* @param Data 要写入的数据* @retval 无*/
void OLED_WriteData(uint8_t Data)
{OLED_I2C_Start();OLED_I2C_SendByte(0x78); //从机地址OLED_I2C_SendByte(0x40); //写数据OLED_I2C_SendByte(Data);OLED_I2C_Stop();
}/*** @brief OLED设置光标位置* @param Y 以左上角为原点,向下方向的坐标,范围:0~7* @param X 以左上角为原点,向右方向的坐标,范围:0~127* @retval 无*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{OLED_WriteCommand(0xB0 | Y); //设置Y位置OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}/*** @brief OLED清屏* @param 无* @retval 无*/
void OLED_Clear(void)
{ uint8_t i, j;for (j = 0; j < 8; j++){OLED_SetCursor(j, 0);for(i = 0; i < 128; i++){OLED_WriteData(0x00);}}
}/*** @brief OLED显示一个字符* @param Line 行位置,范围:1~4* @param Column 列位置,范围:1~16* @param Char 要显示的一个字符,范围:ASCII可见字符* @retval 无*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{ uint8_t i;OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容}OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容}
}/*** @brief OLED显示字符串* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param String 要显示的字符串,范围:ASCII可见字符* @retval 无*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i++){OLED_ShowChar(Line, Column + i, String[i]);}
}/*** @brief OLED次方函数* @retval 返回值等于X的Y次方*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;while (Y--){Result *= X;}return Result;
}/*** @brief OLED显示数字(十进制,正数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~4294967295* @param Length 要显示数字的长度,范围:1~10* @retval 无*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i++) {OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');}
}/*** @brief OLED显示数字(十进制,带符号数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:-2147483648~2147483647* @param Length 要显示数字的长度,范围:1~10* @retval 无*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{uint8_t i;uint32_t Number1;if (Number >= 0){OLED_ShowChar(Line, Column, '+');Number1 = Number;}else{OLED_ShowChar(Line, Column, '-');Number1 = -Number;}for (i = 0; i < Length; i++) {OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');}
}/*** @brief OLED显示数字(十六进制,正数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~0xFFFFFFFF* @param Length 要显示数字的长度,范围:1~8* @retval 无*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i, SingleNumber;for (i = 0; i < Length; i++) {SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;if (SingleNumber < 10){OLED_ShowChar(Line, Column + i, SingleNumber + '0');}else{OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');}}
}/*** @brief OLED显示数字(二进制,正数)* @param Line 起始行位置,范围:1~4* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~1111 1111 1111 1111* @param Length 要显示数字的长度,范围:1~16* @retval 无*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i++) {OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');}
}/*** @brief OLED初始化* @param 无* @retval 无*/
void OLED_Init(void)
{uint32_t i, j;for (i = 0; i < 1000; i++) //上电延时{for (j = 0; j < 1000; j++);}OLED_I2C_Init(); //端口初始化OLED_WriteCommand(0xAE); //关闭显示OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率OLED_WriteCommand(0x80);OLED_WriteCommand(0xA8); //设置多路复用率OLED_WriteCommand(0x3F);OLED_WriteCommand(0xD3); //设置显示偏移OLED_WriteCommand(0x00);OLED_WriteCommand(0x40); //设置显示开始行OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置OLED_WriteCommand(0xDA); //设置COM引脚硬件配置OLED_WriteCommand(0x12);OLED_WriteCommand(0x81); //设置对比度控制OLED_WriteCommand(0xCF);OLED_WriteCommand(0xD9); //设置预充电周期OLED_WriteCommand(0xF1);OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别OLED_WriteCommand(0x30);OLED_WriteCommand(0xA4); //设置整个显示打开/关闭OLED_WriteCommand(0xA6); //设置正常/倒转显示OLED_WriteCommand(0x8D); //设置充电泵OLED_WriteCommand(0x14);OLED_WriteCommand(0xAF); //开启显示OLED_Clear(); //OLED清屏
}
OLED.h
#ifndef __OLED_H
#define __OLED_H
#include "stm32f10x.h" // Device headervoid OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);#endif
OLED_Font.h
#ifndef __OLED_FONT_H
#define __OLED_FONT_H
#include "stm32f10x.h" // Device header/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 00x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 10x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 20x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 30x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 40xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 50x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 60x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 70x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 80x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 90x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 100x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 110x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 120x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 130x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 140x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 150x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 160x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 170x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 180x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 190x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 200x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 210x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 220x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 230x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 240x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 250x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 260x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 270x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 280x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 290x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 300x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 310xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 320x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 330x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 340xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 350x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 360x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 370x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 380xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 390x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 400x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 410x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 420x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 430x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 440x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 450x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 460xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 470x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 480xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 490x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 500x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 510x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 520x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 530x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 540xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 550x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 560x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 570x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 580x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 590x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 600x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 610x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 620x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 630x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 640x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 650x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 660x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 670x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 680x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 690x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 700x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 710x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 720x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 730x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 740x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 750x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 760x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 770x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 780x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 790x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 800x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 810x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 820x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 830x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 840x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 850x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 860x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 870x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 880x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 890x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 900x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 910x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 920x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 930x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};#endif
十二.USART数据包
12.1 发送HEX数据包和文本数据包
(1).发送数据包的时候容易出现重叠不知道哪个数据是开始哪个数据是停止,所以我们加上数据包头包尾,如下图一,固定的包头橙色,固定的包尾蓝色,中间是数据。
(2).文本数据包也是这样,有固定的包头包尾,如图二,固定的包头,然后包尾用\r\n,这样子打印一串数据就可以换行。
图一
图二
12.2 接受HEX数据包和文本数据包
(1).我们知道之前的程序,我们每接收一个字节然后进入中断,在中断函数中拿到这个字节后,就得退出中断了,所以每拿到一个数据都是一个独立的过程,对于数据包来说,很明显它具有前后关联性,包头之后是数据,数据之后是包尾,对于包头,数据,包尾都需要不同的方式来处理一下逻辑,所以在程序中,我们需要设定一个对应不同状态的机制,在不同状态执行不同的操作,同时还要进行合理的转移,这种设计四位就叫状态机。
(2).对于不同的状态我们可以定义三个状态,如图一,HEX接收数据包,第一个状态是等待包头,第二个是等待数据,第三个是等待包尾,每个状态需要用一个变量来标志一下,比如图一中,三个状态分别是S=0,S=1,S=2,只不过标志位,只有0和1,而状态机是多标志位状态的一种方式,执行流程是S=0,收到一个数据,进中断,根据S=0进入第一个状态的程序,判断数据是不是包头FF,如果是FF则代表收到是包头,之后置S=1,然后退出中断,这样下次再进中断,根据S=1,就可以进行接收数据的程序了,直到收到FF才开始接收数据,这时再收到数据,我们就直接把它存在数组中,.另外在用一个变量,记录收了多少个数据,如果没收够4个数据,就一直是接收状态,如果收够了,就置S=2,下一次中断的时候就可以进入下一个状态了,判断是不是FE,如果是FE,就可以置S=0,回到最初的状态,开始下一个轮回,当然也有可能包尾不是FE,比如数据和包头重复,导致包头位置判断错了,包尾就可能不是FE,这时就可以进入重复等待包尾的状态,直到接收到真正的包尾,这样子加入包尾的判断,更能预防因数据和包头重复造成的错误。
(3).文本数据包接收如图二。
图一
图二
12.3 发送数据包和接收数据包以HEX形式的代码
main.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "OLED.h"
#include "Key.h"uint8_t KeyNum;int main(void)
{//单片机要发送的数据的初始值Serial_TxPacket[0]=0x01;Serial_TxPacket[1]=0x02;Serial_TxPacket[2]=0x03;Serial_TxPacket[3]=0x04;Key_Init();Serial_Init();OLED_Init();OLED_ShowString(1,1,"TxPacket:");OLED_ShowString(3,1,"RxPacket:");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);}}
}
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"void Key_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef 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);
}uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0;if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){Delay_ms(20);while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);Delay_ms(20);KeyNum = 1;}return KeyNum;
}
Key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h" // Device headervoid Key_Init(void);
uint8_t Key_GetNum(void);#endif
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>uint8_t Serial_RxPacket[4];//接收数组
uint8_t Serial_TxPacket[4];//发送数组
uint8_t Serial_RXFlag;//接收标志位void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//由外设USART控制输出,用复用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//上拉或者浮空输入模式都可以GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate=9600;//波特率USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制流,无USART_InitStruct.USART_Mode=USART_Mode_Tx | USART_Mode_Rx;//发送和接收USART_InitStruct.USART_Parity=USART_Parity_No;//校验位,无USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位1位USART_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度,8位USART_Init(USART1,&USART_InitStruct);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStruct);USART_Cmd(USART1,ENABLE);
}void Serial_SendByte(uint8_t Byte)//发送一个字节-8位
{USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待数据一位一位传输过去,如果传输过去了标志位就变成1了,就跳出循环,不需要清除标志位,下一次传输的时候会自动清除}void Serial_SendArray(uint8_t *Array,uint16_t Length)//发送数组
{uint16_t i;for(i=0;i<Length;i++){Serial_SendByte(Array[i]);}}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 pRxPacket=0;if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET){uint8_t RxData = USART_ReceiveData(USART1);//接收数据if(RxState == 0)//等待包头{if(RxData == 0xFF){RxState = 1;pRxPacket=0;}}else if(RxState == 1)//等待接收数据程序,这里要用else if,因为如果是if的话上面RxState=0程序里面置1后,就会让=1立马生效,就有两个程序了,如果是else if就会只执行一个{Serial_RxPacket[pRxPacket]= RxData;pRxPacket++;if(pRxPacket >=4){RxState=2;}} else if(RxState == 2)//等待包尾程序{if(RxData == 0xFE){RxState =0;Serial_RXFlag =1;}}USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device headerextern 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_SendPacket(void);
uint8_t Serial_GetRXFlag(void);#endif
12.4 接收数据包以文本形式的代码
main.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "OLED.h"
#include "LED.h"
#include <string.h>//字符串处理函数int main(void)
{Serial_Init();OLED_Init();LED_Init();OLED_ShowString(1,1,"RxPacket:");OLED_ShowString(3,1,"Show:");while (1){if(Serial_RXFlag== 1){OLED_ShowString(2,1," ");//因为oled上面只有16个位置,然后如果先发送16个字符aaaabbbbccccdddd,在发送eeee的话它不会直接显示eeee,而是会显示eeeebbbbccccdddd,所以这里空格就是擦除OLED_ShowString(2,1,Serial_RxPacket);//字符串if(strcmp(Serial_RxPacket,"LED_ON")==0){LED1_ON();Serial_SendString("LED_On_Ok\r\n");OLED_ShowString(4,1," ");OLED_ShowString(4,1,"LED_On_Ok");}else if(strcmp(Serial_RxPacket,"LED_OFF")==0){LED1_OFF();Serial_SendString("LED_On_OFF\r\n");OLED_ShowString(4,1," ");OLED_ShowString(4,1,"LED_On_OFF");}else{Serial_SendString("ERROR\r\n");OLED_ShowString(4,1," ");OLED_ShowString(4,1,"ERROR");}Serial_RXFlag =0;}}
}
LED.c
#include "stm32f10x.h" // Device headervoid LED_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_5);
}void LED1_ON(void)
{GPIO_ResetBits(GPIOB, GPIO_Pin_5);
}void LED1_OFF(void)
{GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
LED.h
#ifndef __LED_H
#define __LED_Hvoid LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);#endif
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>char 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_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//由外设USART控制输出,用复用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//上拉或者浮空输入模式都可以GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate=9600;//波特率USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件控制流,无USART_InitStruct.USART_Mode=USART_Mode_Tx | USART_Mode_Rx;//发送和接收USART_InitStruct.USART_Parity=USART_Parity_No;//校验位,无USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位1位USART_InitStruct.USART_WordLength=USART_WordLength_8b;//数据长度,8位USART_Init(USART1,&USART_InitStruct);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStruct);USART_Cmd(USART1,ENABLE);
}void Serial_SendByte(uint8_t Byte)//发送一个字节-8位
{USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待数据一位一位传输过去,如果传输过去了标志位就变成1了,就跳出循环,不需要清除标志位,下一次传输的时候会自动清除}void Serial_SendString(char * String)//发送字符串
{uint8_t i;for(i=0;String[i]!='\0';i++){Serial_SendByte(String[i]);}
}void USART1_IRQHandler(void)//这里电脑发送的时候不用打\r\n,直接@数据回车就可以了
{static uint8_t RxState=0;static uint8_t pRxPacket=0;if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET){uint8_t RxData = USART_ReceiveData(USART1);//接收数据if(RxState == 0)//等待包头{if(RxData == '@' && Serial_RXFlag ==0)//Serial_RXFlag ==0是防止程序执行太快了,处理完了才执行接收,Serial_RXFlag等于0才开始接收新的数据{RxState = 1;pRxPacket=0;}}else if(RxState == 1)//等待接收数据程序,这里要用else if,因为如果是if的话上面RxState=0程序里面置1后,就会让=1立马生效,就有两个程序了,如果是else if就会只执行一个{if(RxData == '\r')//如果是\r就直接代表到了包尾部分{RxState = 2;}else{Serial_RxPacket[pRxPacket]= RxData;pRxPacket++;}} else if(RxState == 2)//等待包尾程序{if(RxData == '\n'){RxState =0;Serial_RxPacket[pRxPacket]= '\0';Serial_RXFlag =1;}}USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device headerextern char Serial_RxPacket[];void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char * String);
uint8_t Serial_GetRXFlag(void);#endif