【stm32的IIC和SPI协议及其实例】


一、IIC协议和SPI协议

1.1 IIC(I2C)协议

I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。它是一个支持设备的总线。“总线”指多个设备共用的信号线。
在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式。 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
在这里插入图片描述

I2C 的所有硬件架构都是根据图中左侧 SCL 线和 SDA 线展开的STM32芯片有多个 I2C外设,它们的 I2C 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。

在这里插入图片描述
起始信号产生后,所有从机就开始等待主机紧接下来 广播 的从机地址信号(SLAVE_ADDRESS)。在 I2C 总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。根据 I2C 协议,这个从机地址可以是 7 位或 10 位。在地址位之后,是传输方向的选择位,该位为 0 时,表示后面的数据传输方向是由主机传输至从机,即主机向从机写数据。该位为 1 时,则相反,即主机由从机读数据。从机接收到匹配的地址后,主机或从机会返回一个应答(ACK)或非应答(NACK)信号,只有接收到应答信号后,主机才能继续发送或接收数据。

在这里插入图片描述

1.2 SPI协议

SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。
SPI 通讯设备之间的常用连接方式如下:
在这里插入图片描述

SPI 通讯使用 3 条总线及片选线,3 条总线分别为 SCK、MOSI、MISO,片选线为 SS,它们的作用介绍如下:
(1) SS( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS,以下用 NSS 表示。当有多个 SPI从设备与 SPI主机相连时,设备的其它信号线 SCK、MOSI及 MISO同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同只使用这 3条
总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。
(2) SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
(3) MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机
到从机。
(4) MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机

SPI 的所有硬件架构都从图 25-5 中左侧 MOSI、MISO、SCK 及 NSS 线展开的。STM32芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。
在这里插入图片描述

二、 I2C协议下的温湿度检测

2.1 AHT20

AHT20是一种温湿度传感器,可以使用I2C接口。
有如下优点

  1. 高精度,完全校准
  2. 极高的可靠性与卓越的长期稳定性(较上一代aht10有极大的提升)
  3. 抗干扰能力强
  4. 性价比极高
  5. 适用于恶劣的环境条件下
    引脚图如下
    在这里插入图片描述
    (需要注意的是,部分AHT20好像只能采用5v供电才能工作,不知道是不是实验使芯片本身的原因)

2.2代码部分

1.main函数,初始化相应模块

int main(void)
{	
	delay_init();	    	       //延时函数初始化    	  
	uart_init(115200);	 
	IIC_Init();
		  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	
	while(1)
	{
		//printf("温度湿度显示");
		read_AHT20_once();
		
  }
}

2.设置AHT20工作模式

`
void  read_AHT20_once(void)
{
	delay_ms(10);

	reset_AHT20();
	delay_ms(10);

	init_AHT20();
	delay_ms(10);

	startMeasure_AHT20();
	delay_ms(80);

	read_AHT20();
	delay_ms(5);
}

3.读取AHT20发送的数据

void read_AHT20(void)
{
	uint8_t   i;
	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}

	//-------------
	I2C_Start();

	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	readByte[0]= I2C_ReadByte();
	Send_ACK();

	readByte[1]= I2C_ReadByte();
	Send_ACK();

	readByte[2]= I2C_ReadByte();
	Send_ACK();

	readByte[3]= I2C_ReadByte();
	Send_ACK();

	readByte[4]= I2C_ReadByte();
	Send_ACK();

	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();

	I2C_Stop();

	//--------------
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;

		H1 = (H1*1000)/1024/1024;

		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];

		T1 = (T1*2000)/1024/1024 - 500;

		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;

		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;

		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("lyy");

	}
X	printf("\r\n");
	
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");

	
}

上述函数都可以厂家提供的数组包中找到,不需要自定义
完整代码如下提取码mwl4

2.3结果展示

温湿度和串口


可以看到,由于手握住温湿度传感器,温度在不断增高,最后两组数据湿度的

三、SPI协议和OLED显示屏

3.1OLED显示屏

OLED图示
在这里插入图片描述
特点如下

  1. 0.96寸OLED屏,支持黑白、黑蓝或者黄蓝双色显示
  2. 128x64分辨率,显示效果清晰,对比度高
  3. 超大可视角度:大于160°(显示屏中可视角度最大的一种屏幕)
  4. 宽电压供电(3V~5V),兼容3.3V和5V逻辑电平,无需电平转换芯片
  5. 默认为4线制SPI总线,可以选择3线制SPI总线或者IIC总线
  6. 超低功耗:正常显示仅为0.06W(远低于TFT显示屏)
  7. 军工级工艺标准,长期稳定工作
  8. 提供丰富的STM32、C51、Arduino、Raspberry Pi以及MSP430平台示例程序
  9. 提供底层驱动技术支持
    引脚图如下
    在这里插入图片描述
    参考链接 http://www.lcdwiki.com/zh/0.96inch_SPI_OLED_Module
    基础程序链接

3.2代码配置

1.下载上述链接程序
2.打开资料包,选择与自己平台相同的实例,打开Demo的工程(选择spi4线的),使用keil编译
在这里插入图片描述
3.更改部分函数,输出想要的结果
如果输出汉字,文件中的字符库可能没有,我们需要自己找到汉字对应的点阵(这里取16进制的)
我们输入“张三”后按下ctrl+enter
在这里插入图片描述
然后选择c51格式
在这里插入图片描述
生成的即为16进制的点阵
在这里插入图片描述
复制点阵后打开工程文件找到储存oled汉字模的头文件
在这里插入图片描述
如果没有看到的话可以右键单击hardware文件添加进来
在这里插入图片描述
在这里插入图片描述
把复制的数据加入数组
在这里插入图片描述
用自带的函数定义输出函数
void TEST_MainPage(void)
{

GUI_ShowCHinese(28,20,16,"江**",1);//中文姓名
GUI_ShowString(4,48,"6320730412",16,1);//数字详细
delay_ms(1500);		
delay_ms(1500);

}

3.3结果展示

在这里插入图片描述

3.4改成滑动式展示方式

我们来输出点别的东西,我们希望输出的东西可以在屏幕上左右滑动,那该怎么办呢?
首先,还是要在字模库内添加新的汉字,具体步骤如下。
然后再main函数内添加下列代码就可以了

	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)
	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.5oled滑动显示结果展示

oled滑动


完整代码如下 提取码:nsae

四、结合:oled显示温湿度数据

有了前面两个例子做铺垫,我们现在把这两个例子结合起来,是不是就发现不是很难了。
再例一中,我们在read_AHT20()函数中添加串口输出函数就达到了串口输出温湿度的效果,那么如果把这些函数改成输出到oled屏上不久可以了吗?

4.1改动例一代码

首先我们把例二代码的oled基本函数部分复制到例一工程去,如果建立了新的文件,不要忘记添加文件路径哦。
read_AHT20()函数中,我们把接受到的文件输入到数组中,然后再用oled的输出函数输出数组

sprintf(strTemp,"%.1f",a);   //调用Sprintf函数把DHT11的温度数据格式化到字符串数组变量strTemp中  
  sprintf(strHumi,"%.1f",b);    //调用Sprintf函数把DHT11的湿度数据格式化到字符串数组变量strHumi中  
	//printf(strTemp);
	//printf("/r/n");
	GUI_ShowCHinese(16,00,16,"温湿度显示",1);
	GUI_ShowCHinese(16,20,16,"温度",1);
	GUI_ShowString(53,20,strTemp,16,1);
	GUI_ShowCHinese(16,38,16,"湿度",1);
	GUI_ShowString(53,38,strHumi,16,1);

经过更改后,输出温湿度的途径就变成了oled屏了,我们编译烧录看下结果吧
完整代码如下:

提取码 vvwq

数字取模软件提取码:ppmc

4.2结果展示

温湿度oled

五、总结

本次实验有一定的难度,但是网络上有相当齐全的参考资料,硬件厂家也提供了完善的库函数,需要注意的是在如此多的资料中筛选合适的代码。

六、参考链接

1.https://blog.csdn.net/qq_43279579/article/details/111414037
2.https://blog.csdn.net/hhhhhh277523/article/details/111397514
3.https://www.bilibili.com/video/BV1LB4y1A7ah/?spm_id_from=333.337.search-card.all.click
4http://www.lcdwiki.com/zh/0.96inch_SPI_OLED_Module

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值