一、SPI 协议的简单概括
SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设 备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间, 要求通讯速率较高的场合。
SPI 协议的更多说明请参考:
① SPI协议详解
② 野火的 《零死角玩转STM32—F103指南者》
二、准备工作
(1) 实验说明
-
单片机:STM32F103系列单片机(本人用的时野火的指南者)
-
温湿度传感器:AHT20
-
0.96寸OLED显示屏模块:0.96寸SPI
-
实现功能:
① 显示自己的个人简介
② 显示 AHT20 的温度和湿度
③ 上下或左右显示长字符
如果 AHT20 温湿度采集还没有实现,请参考上篇博文提前实现该功能:基于I2C通信协议和AHT20温湿度传感器的数据采集
(2) 代码移值
淘宝🔗中已经给出了写好了的OLED驱动,我们只需移值这驱动即可。
解压后,然后找到 0.96inch_OLED_Demo_STM32F103RCT6_Software_4-wire_SPI 工程文件
" 1-Demo ——> Demo_STM32 ——> 0.96inch_OLED_Demo_STM32F103RCT6_Software_4-wire_SPI "
可根据 main.c 文件中的连线说明,把代码烧录到STM32中,看 oled 屏是否是好的 (确保接线方式是正确的)
基于 AHT20 温湿度采集 的工程,移值 OLED 的驱动
主要是移值 0.96inch_OLED_Demo_STM32F103RCT6_Software_4-wire_SPI 工程中的 HARDWARE 和 SYSTEM 文件,还有 USER 中的 test.h、test.c、gui.h 和 gui.c 文件
三、主要代码说明
(1) 添加点阵汉字
在 oledfont.h 文件中可以添加自己的点阵汉字,有16×16,24×24、32×32。
点阵字体实现的原理可参考:百度文库
如构造的16×16的 “在下摸鱼怪”
(2) 显示温湿度
我们只需把每次采集的温湿度,通过传参的方式传递给显示函数
/*****************************************************************************
显示温湿度
******************************************************************************/
void Temp_Hum(float temp, float hum)
{
char data1[4],data2[4];
sprintf(data1, "%.1f", temp);
GUI_DrawLine(0, 10, WIDTH-1, 10,1);
GUI_DrawLine(WIDTH/2-1,11,WIDTH/2-1,HEIGHT-1,1);
GUI_DrawLine(WIDTH/2-1,10+(HEIGHT-10)/2-1,WIDTH-1,10+(HEIGHT-10)/2-1,1);
GUI_ShowString(0,1,"2020-12-20",8,1);
GUI_ShowString(78,1,"Sunday",8,1);
GUI_ShowString(14,HEIGHT-1-10,"Cloudy",8,1);
//温度
GUI_ShowString(WIDTH/2-1+2,13,"TEMP",8,1);
GUI_DrawCircle(WIDTH-1-19, 25, 1,2);
GUI_ShowString(WIDTH-1-14,20,"C",16,1);
GUI_ShowString(WIDTH/2-1+9,20,(u8 *)data1,16,1);
//湿度
GUI_ShowString(WIDTH/2-1+2,39,"HUM",8,1);
GUI_DrawBMP(6,16,51,32, BMP5, 1);
sprintf(data2, "%.1f", hum);
GUI_ShowString(WIDTH/2-1+9,46,(u8 *)data2,16,1);
GUI_ShowString(WIDTH-1-14,46,"%",16,1);
}
(3) main.c
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "delay.h"
#include "bsp_aht20.h"
#include "bsp_led.h"
#include "gui.h"
#include "oled.h"
#include "test.h"
extern u32 flag;//标志是否开启延迟
int main(void)
{
u32 CT_data[2]={0};
volatile float hum=0,temp=0;
u32 i=1;
u32 num = 0;//记录AHT20采集次数
USART_Config(); //USART1初始化
LED_GPIO_Config(); //LED端口初始化
delay_init(); //延时函数初始化
temphum_init(); //初始化温湿度传感器
OLED_Init(); //初始化OLED
OLED_Clear(0); //清屏(全黑)
while(1)
{
flag = i;
/* 采集3次温湿度 */
if(i<4)
{
AHT20_Read_CTdata(CT_data); //不经过CRC校验,直接读取AHT20的温度和湿度数据
hum = CT_data[0]*100*10/1024/1024; //计算得到湿度值(放大了10倍)
temp = CT_data[1]*200*10/1024/1024-500;//计算得到温度值(放大了10倍)
printf("湿度:%.1f%%\r\n",(hum/10));
printf("温度:%.1f度\r\n",(temp/10));
printf("\r\n");
Temp_Hum(temp/10, hum/10);
num++;
green_led_on;
delay_ms(1000);
green_led_off;
delay_ms(1000);
}
/* 显示个人简介 */
if(i==4){
OLED_Clear(0);
Display_introduction();
delay_ms(1000);
}
/* 显示个人座右铭*/
if(i==5)
{
OLED_Clear(0);
Display_motto();
delay_ms(1000);
OLED_Clear(0);
i = 0;
num=0;
}
if(num == 3)
{
num = 0;
i = 3;
}
i++;
printf("i=%d,num=%d\r\n",i,num);
}
}
变量 flag 作用是判断是否开启延迟,在oled显示函数中添加延迟(oled.c文件中的OLED_Display函数),将会起到一个刷屏的效果;当我们多次显示温湿度时,将不需要刷屏,所以定义一个变量进行判断。
变量 num 作用是记录温湿度的采集次数,因为这里有个bug,每次采集温湿度后变量 i 中会被置0,所以在额外增添一个变量来规避这个bug😂😂。
四、效果展示
显示 AHT20 的温度和湿度和个人简介
可发现,采集温湿度数据时,变量 i 没有发生改变;可能原因是I2C采集温湿度的影响,去掉采集部分,逻辑就正常了。
如果要实现长字符左右滑动功能,可参考 SSD1306的芯片手册 (基于硬件实现)
实现字符的左右移动:
OLED_WR_Byte(0x2E,OLED_CMD); //关闭滚动
OLED_WR_Byte(0x29,OLED_CMD); //水平向左或者右滚动 26/27
OLED_WR_Byte(0x00,OLED_CMD); //虚拟字节
OLED_WR_Byte(0x00,OLED_CMD); //起始页 0
OLED_WR_Byte(0x07,OLED_CMD); //滚动时间间隔
OLED_WR_Byte(0x07,OLED_CMD); //终止页 7
OLED_WR_Byte(0x00,OLED_CMD); //虚拟字节
OLED_WR_Byte(0xFF,OLED_CMD); //虚拟字节
OLED_WR_Byte(0x2F,OLED_CMD); //开启滚动
注意:开始滚屏之前一定要确保数据已经传输完毕,如果在开始滚屏后在传输数据,可能会导致显示数据被破坏。