http://www.aosong.com/class-36.html
USB 转 TTL 模块
杜邦线若干
PC 机(Win10)
0.96寸OLED显示屏模块
2. 软件工具:
keil 5 MDK
串口调试助手
烧录软件 mcuisp
1、I2C 协议简介
I2C 通讯协议 (Inter - Integrated Circuit) 是由 Phiilps公司开发的,由于它引脚少,硬件实现简单, 可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路 (IC) 间的通讯。
2、I2C 物理层
(1) 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可 连接多个 I2C通讯设备,支持多个通讯主机及多个通讯从机。
(2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线 (SDA) ,一条串行时钟线(SCL)。 数据线即用来表示数据,时钟线用于数据收发同步。
(3)每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的 访问。
(4) 总线通过上拉电阻接到电源。当 I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
(5)多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
(6) 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达3.4Mbit/s,但目前大多 I 2C 设备尚不支持高速模式。
(7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。
3、协议层
I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
4、I2C的两种方式——硬件I2C和软件I2C
①硬件I2C
直接利用 STM32 芯片中的硬件 I2C 外设。
硬件I2C的使用只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置 1,此时外设就会控制对应的 SCL及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。
②软件I2C
直接使用。
CPU 内核按照 I2C 协议的要求控制GPIO 输出高低电平,从而模拟I2C。 软件I2C的使用 需要在控制产生 I2C 的起始信号时,控制作为 SCL 线的 GPIO引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL线切换为低电平,这样就输出了一个标准的 I2C 起始信号。
③两者的差别
硬件 I2C 直接使用外设来控制引脚,可以减轻 CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为 SCL 和 SDA,软件模拟 I2C 则可以使用任意 GPIO引脚,相对比较灵活。对于硬件I2C用法比较复杂,软件I2C的流程更清楚一些。如果要详细了解I2C的协议,使用软件I2C可能更好的理解这个过程。在使用I2C过程,硬件I2C可能通信更加快,更加稳定。
1 .代码编写
在野火提供的示例代码中,打开一个只包含固件库的空项目。向工程中添加相关代码,添加代码的具体内容请参考下面链接:
https://blog.csdn.net/hhhhhh277523/article/details/111397514
具体的工程项目包下载 链接:https://pan.baidu.com/s/1-uYzWATsvkOYRmazolQAzA
提取码:4xva
2 .部分代码分析
AHT20 芯片的初始化
//初始化AHT20
void AHT20_Init(void)
{
IIC_Init();
IIC_Start();
IIC_Send_Byte(0x70);
IIC_Wait_Ack();
IIC_Send_Byte(0xa8);//0xA8进入NOR工作模式
IIC_Wait_Ack();
IIC_Send_Byte(0x00);
IIC_Wait_Ack();
IIC_Send_Byte(0x00);
IIC_Wait_Ack();
IIC_Stop();
delay_ms(10);//延时10ms左右
IIC_Start();
IIC_Send_Byte(0x70);
IIC_Wait_Ack();
IIC_Send_Byte(0xbe);//0xBE初始化命令,AHT20的初始化命令是0xBE, AHT10的初始化命令是0xE1
IIC_Wait_Ack();
IIC_Send_Byte(0x08);//相关寄存器bit[3]置1,为校准输出
IIC_Wait_Ack();
IIC_Send_Byte(0x00);
IIC_Wait_Ack();
IIC_Stop();
delay_ms(10);//延时10ms左右
}
AHT20 芯片读取并保存数据
//没有CRC校验,直接读取AHT20的温度和湿度数据
void AHT20_Read_CTdata(u32 *ct)
{
volatile u8 Byte_1th=0,Byte_2th=0,Byte_3th=0;
volatile u8 Byte_4th=0,Byte_5th=0,Byte_6th=0;
u32 RetuData = 0;
u16 cnt = 0,flag;
AHT20_SendAC();//向AHT20发送AC命令
delay_ms(80); //大约延时80ms
while(((AHT20_Read_Status()&0x80)==0x80))//直到状态bit[7]为0,表示为空闲状态,若为1,表示忙状态
{
delay_ms(1);
if(cnt++>=100) break;
}
IIC_Start();
IIC_Send_Byte(0x71);
flag=IIC_Wait_Ack();
Byte_1th = IIC_Read_Byte(flag);//状态字
Byte_2th = IIC_Read_Byte(flag);//湿度,发送ACK(继续发送)
Byte_3th = IIC_Read_Byte(flag);//湿度
Byte_4th = IIC_Read_Byte(flag);//湿度/温度
Byte_5th = IIC_Read_Byte(flag);//温度
Byte_6th = IIC_Read_Byte(!flag);//温度,发送NACK(停止发送)
IIC_Stop();
//保存得到的数据到RetuData中
RetuData = (RetuData|Byte_2th)<<8;
RetuData = (RetuData|Byte_3th)<<8;
RetuData = (RetuData|Byte_4th);
RetuData =RetuData >>4;
ct[0] = RetuData;//湿度
RetuData = 0;
RetuData = (RetuData|Byte_4th)<<8;
RetuData = (RetuData|Byte_5th)<<8;
RetuData = (RetuData|Byte_6th);
RetuData = RetuData&0x0fffff;
ct[1] =RetuData; //温度
}
main.c 函数
#include "led.h"
#include "usart.h"
#include "temhum.h"
int main(void)
{
u32 CT_data[2]={0};
volatile float hum=0,tem=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
temphum_init(); //ATH20初始化
while(1)
{
AHT20_Read_CTdata(CT_data); //不经过CRC校验,直接读取AHT20的温度和湿度数据
hum = CT_data[0]*100*10/1024/1024; //计算得到湿度值(放大了10倍)
tem = CT_data[1]*200*10/1024/1024-500;//计算得到温度值(放大了10倍)
printf("湿度:%.1f%%\r\n",(hum/10));
printf("温度:%.1f度\r\n",(tem/10));
printf("\r\n");
//延时2s,LED闪烁提示串口发送状态
LED=0;
delay_ms(1000);
LED=1;
delay_ms(1000);
}
}
3 .编译调试后生成hex文件,然后烧录进STM32,具体烧录过程请参考:
https://blog.csdn.net/lengyuefeng212/article/details/104178386
4 .串口结果显示
打开串口助手,实验结果如下:
如图所示,用手握住传感器即可看到其温度得到了明显升高,环境湿度改变后同样湿度也有了变化。
1、简介
SPI协议是由摩托罗拉公司提出的通讯协议(SerialPeripheralInterface),即串行外围设备接口,是 一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率 较高的场合。2、SPI物理层
SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO,片选线为SS,它们的作 用介绍如下:
(1) SS*(*SlaveSelect):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用NSS 表示。当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCK、MOSI 及 MISO 同时并联到相同的SPI总线上,即无论有多少个从设备,都共同只使用这3条总线;而每 个从设备都有独立的这一条NSS信号线,本信号线独占主机的一个引脚,即有多少个从设 备,就有多少条片选信号线。I2C协议中通过设备地址来寻址、选中总线上的某个设备并与 其进行通讯;而SPI协议中没有设备地址,它使用NSS信号线来寻址,当主机要选择从设 备时,把该从设备的NSS信号线设置为低电平,该从设备即被选中,即片选有效,接着主 机开始与被选中的从设备进行SPI通讯。所以SPI通讯以NSS线置低电平为开始信号,以 NSS线被拉高作为结束信号。
(2) SCK(SerialClock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速 率,不同的设备支持的最高时钟频率不一样,如STM32的SPI时钟频率最大为fpclk/2,两 个设备之间通讯时,通讯速率受限于低速设备。
(3) MOSI(MasterOutput,SlaveInput):主设备输出/从设备输入引脚。主机的数据从这条信号线 输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
(4) MISO(MasterInput,,SlaveOutput):主设备输入/从设备输出引脚。主机从这条信线读入数据, 从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。
3、协议层
与I2C的类似,SPI协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。
这是一个主机的通讯时序。NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机 产生,主机通过该信号线读取从机的数据。MOSI与MISO的信号只在NSS为低电平的时候才有 效,在SCK的每个时钟周期MOSI和MISO传输一位数据。
4、STM32的SPI特性及架构
STM32的SPI外设可用作通讯的主机及从机,支持最高的SCK时钟频率为fpclk/2(STM32F103型 号的芯片默认f:sub:pclk1为72MHz,fpclk2 为36MHz),完全支持SPI协议的4种模式,数据帧长 度可设置为8位或16位,可设置数据MSB先行或LSB先行。它还支持双线全双工(前面小节说 明的都是这种模式)、双线单向以及单线模式。其中双线单向模式可以同时使用MOSI及MISO数 据线向一个方向传输数据,可以加快一倍的传输速度。而单线模式则可以减少硬件接线,当然这 样速率会受到影响。
1.汉字点阵字模
利用取模软件将需要显示的文字用十六进制表示出来,需要用到的取模软件:PCtoLCD2002
打开PCtoLCD2002,软件初始设置如下:
在文字输入区输入目标文字,点击生成字模,得到字模码组
注:将文字进行了“左旋90度”、“垂直翻转”,这样在 OLED 屏上显示的文字才是正向的,这是因为 OLED 竖屏显示的原因。
2.演示程序
需要下载 0.96 寸 OLED 显示屏厂家给出的 Demo 程序。
程序下载链接:
http://www.lcdwiki.com/res/Program/OLED/0.96inch/SPI_SSD1306_MSP096X_V1.0/0.96inch_SPI_OLED_Module_SSD1306_MSP096X_V1.0.zip
3.修改代码
下载资料包之后打开其中的“0.96inch_SPI_OLED_Module_SSD1306_MSP096X_V1.0\1-Demo\Demo_STM32\0.96inch_OLED_Demo_STM32F103RCT6_Software_4-wire_SPI”。
注意:我用的开发板是 STM32F103C8T6 芯片,所以这里我用的是这个文件里的代码,如果不一样,请选择与自己平台相同的实例文件。
1)添加字模
打开上面下载的工程文件“0.96inch_OLED_Demo_STM32F103RCT6_Software_4-wire_SPI\PROJECT\OLED.uvprojx”,打开 gui.c 下的 oledfont.h 头文件,将 cfont16[] 数组内的内容修改成自己的中文文字点阵即可。
2)修改显示函数
将 test.c 里 void TEST_MainPage(void) 函数中的语句注释掉,添加自己的执行语句。
//主界面显示测试
void TEST_MainPage(void)
{
//GUI_ShowString(28,0,"OLED TEST",16,1);
//GUI_ShowString(12,16,"0.96\" SSD1306",16,1);
//GUI_ShowString(40,32,"64X128",16,1);
//GUI_ShowString(4,48,"www.lcdwiki.com",16,1);
GUI_ShowCHinese(45,18,16,"刘慢慢",1);
GUI_ShowString(18,40,"63190703xxxx",16,1);
delay_ms(1500);
delay_ms(1500);
}
函数说明:
① GUI_ShowString() 函数各参数分别对应: X 坐标、Y 坐标、字符串(ASCLL码中的)、bit(表示字符显示格式,这里我用的 16 ,和汉字一样高)、显示样式(1:白字黑底;0:黑字白底)。
② GUI_ShowChinese()
函数各参数分别对应: X 坐标、Y 坐标、汉字点阵大小(这里使用的是 16×16 的,参数应该是16)、要显示的汉字、显示样式(1:白字黑底;0:黑字白底)。
3)修改主函数
将 main.c 代码中的 while 函数里除 TEST_MainPage(); 语句以外的语句全注释掉,如下:
4 .编译调试后生成hex文件,然后烧录进STM32,具体烧录过程请参考:
https://blog.csdn.net/lengyuefeng212/article/details/104178386
1 .添加字模
跟上述方法一样,获取需要显示的字的字模后,向 gui.c 下的 oledfont.h 头文件里的 cfont16[] 数组内的添加中文文字点阵即可。我这里添加了“当、前、温、湿、度”这 5 个字的点阵。
2 .修改代码
1)移植 AHT20 温湿度采集代码
从上文使用的工程文件里面移植下面 4 个文件:
bsp_i2c.h、bsp_i2c.c、
sys.h(移植后更改了名称为 AHT20_sys.h,不然会重名)、
sys.c(移植后更改了名称为 AHT20_sys.c,不然会重名);
并将bsp_i2c.c文件中的串口发送改为 OLED 显示 void Show_OLED(void) 即可。
具体移植过程和修改可以参考这篇博客:
https://blog.csdn.net/ssj925319/article/details/111588662?spm=1001.2014.3001.5506
void Show_OLED(void)
{
GUI_ShowCHinese(28,10,16,"当前温湿度",1);
GUI_ShowCHinese(20,32,16,"温度:",1);
GUI_ShowString(60,32,strTemp1,16,1);
GUI_ShowString(68,32,strTemp2,16,1);
GUI_ShowString(76,32,".",16,1);
GUI_ShowString(84,32,strTemp3,16,1);
GUI_ShowCHinese(92,32,16,"℃",1);
GUI_ShowCHinese(20,48,16,"湿度:",1);
GUI_ShowString(60,48,strHumi1,16,1);
GUI_ShowString(68,48,strHumi2,16,1);
GUI_ShowString(76,48,".",16,1);
GUI_ShowString(84,48,strHumi3,16,1);
GUI_ShowCHinese(92,48,16,"%",1);
}
2)修改主函数
将 main.c 代码中不用的函数注释掉,再修改代码,之后的结果如下:
#include "bsp_i2c.h" //注意添加头文件
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
OLED_Init(); //初始化OLED
IIC_Init(); //初始化IIC
OLED_Clear(0); //清屏(全黑)
while(1)
{
read_AHT20_once(); //读取温度并显示
OLED_Clear(0); //清屏(全黑)
delay_ms(1500);
}
要注意添加头文件,调用温湿度读取并显示函数。
3 .编译调试后生成hex文件,然后烧录进STM32,具体烧录过程请参考:
https://blog.csdn.net/lengyuefeng212/article/details/104178386
4 .STM32F103 开发板与 OLED 模块连接
5 .运行结果如下:
用手握住传感器即可看到其温度得到了明显升高,环境湿度改变后同样湿度也有了变化。
1 .添加字模
跟上述方法一样,获取字模后,向 gui.c 下的 oledfont.h 头文件里的 cfont16[] 数组内的添加中文文字点阵即可。
这里我添加的是“生活不易慢慢叹气”
2 .修改代码
1)修改显示函数
同上,在 test.c 里 void TEST_MainPage(void) 函数中不用的语句注释掉,添加自己的想要显示的字符,如下:
2)修改主函数
将 main.c 代码中的 while 函数注释掉,再添加如下代码:
//从左到右滑动( OLED 屏的滚屏命令)
OLED_WR_Byte(0x2E,OLED_CMD); //关闭滚动
OLED_WR_Byte(0x27,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); //虚拟字节
TEST_MainPage();
OLED_WR_Byte(0x2F,OLED_CMD); //开启滚动
3 .编译调试后生成hex文件,然后烧录进STM32,具体烧录过程请参考:
https://blog.csdn.net/lengyuefeng212/article/details/104178386
4 .STM32F103 开发板与 OLED 模块连接
5 .运行结果如下:
在本次实验中我学习了I2C总线通信协议和SPI 协议,使用STM32F103完成了基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值成功通过串口输出。同时理解了OLED屏显和汉字点阵编码原理,学会了如何使用STM32F103的SPI接口实现显示学号和姓名,显示AHT20的温度、湿度,以及滑动显示长字符。这些我之前未曾接触过,本次实验受益良多。
https://blog.csdn.net/qq_47538417/article/details/121547952
https://blog.csdn.net/m0_58892312/article/details/121410862
https://blog.csdn.net/ssj925319/article/details/111588662
https://blog.csdn.net/qq_43279579/article/details/111678857
《STM32 HAL库开发实战指南——基于野火霸天虎开发板》.pdf