写在开头,为什么我文章里写的不是I2C而是IIC,因为它们俩是一样的东西写成啥都可以,虽然大家平时看到的更多的是I2C,但是写成IIC的话,会比较方便我码字。
今天我们来看看在STC库函数中IIC如何使用。
一共有三组引脚可以供我们选择,我们照例是要包含头文件STC32G_Switch.h。
可以使用下面的函数来选择引脚,把里面的参数改了就可以换其他引脚了。
I2C_SW(I2C_P24_P25);
STC的IIC没有总线仲裁功能,所以如果需要的话,我们就得使用软件IIC然后自己去写了。
有主机和从机两种模式,但是一般来说都是拿来作为主机的,所以就不讲从机方面的内容了。
当我们配置完IIC之后,只需要去改写IIC主机控制寄存器就可以发出相对应的IIC时序了。
可能有小伙伴有疑问,我这个系列不是只用库函数,能不讲寄存器就不讲寄存器的嘛?
关于这一点,马上揭晓为什么,我们直接来看库函数。
我们把文件STC32G_I2C.c和STC32G_I2C_Isr.c包含进工程。
用下面这个函数来配置IIC。
用结构体变量的指针来配置IIC。
结构体成员从上到下依次是
IIC速度,说是说速度,实际上我们这边填的是一个分频系数。
IIC的速率就是系统时钟/2 再除上这个系数的两倍再加4,比如说我们系统时钟是24MHz,我要把IIC的速率控制到100k,那么我这个值就填58。
下一个是使能位,给个ENABLE。
模式选择主机模式,I2C_Mode_Master。
是否自动发送数据,就是说我们一把要发送的数据写入对应寄存器,就帮我们自动发送并且接收ACK应答,这个看情况吧,我是DISABLE了。
从机地址,这边是填写如果我们作为从机,那么我们的地址是多少,可以填写一个七位的地址,如果我们就是主机模式的话,那么我们可以不填。
从机地址匹配,我没有看很懂,那我都是从机了,还匹配什么从机地址?所以把官方文档里的解释放下面了。不过好在一般用的主机模式,不需要配置从机相关的。
接下来还有俩函数,分别是发送数据和接收数据的。
但是我不打算用。
因为用它俩的话,我没办法读取SGP30的数据(可以,但是很麻烦)。
小伙伴可以先回顾一下我之前关于SGP30的文章看看如何与SGP30通信。
【硬件模块】SGP30气体传感器-优快云博客文章浏览阅读2.5k次,点赞21次,收藏23次。这是SGP30官方文档里开头的介绍,简单来说就是SGP30是一个数字多像素气体传感器,然后具有长期稳定性和低漂移。这些我们都不用管,我们只需要知道SGP30是通过I2C来通信的,并且可以采集的数据有CO2和TVOC的含量。TVOC是“Total Volatile Organic Compounds”,意思是总挥发性有机化合物。可以来看一下它的参数。TVOC的输出范围是0~60000ppb,而CO2的范围是400~60000ppm。一开始没注意范围,我看CO2一直都在400以上还以为出了啥问题。_sgp30https://blog.youkuaiyun.com/m0_63235356/article/details/140060957简单概括一下就是,我们一开始先发送0x20和0x03两个字节的命令来让SGP30初始化。然后要获取气体数据的时候就发送0x20和0x08两个字节的命令让它采集数据,接着再接收6个字节的数据,其中第一第二个字节是二氧化碳的数据,第三字节是前俩字节的CRC校验,第四第五字节是TVOC的数据,第六字节是TVOC数据的CRC校验。
要求没那么高的话我们可以直接忽略CRC校验(但是要接收,否则时序对不上)。
然后我们再来看看STC库函数里IIC如何发送和接收数据(忽略乱码,官方示例代码好像用的编码是GB2312)。
通过函数内的函数名应该可以看得出它在干什么。
首先是发送起始位,然后发送从机地址,接收ACK应答,发送寄存器地址。问题开始出现了,SGP30它发送的寄存器地址(命令)是16位两个Byte的。
不过我们也有解决的办法,那就是把SGP30的命令的后一个字节放到要发送的数据里,效果是一样的。
所以我们给SGP30发送数据的话,还是可以实现的。
但更大的问题来了,那就是接收数据。
接收函数里咋一看没啥问题,问题就在于我们SGP30不一样,它的命令以及寄存器地址都是16位的,所以我们的SGP30用不了官方的库函数……吗?
我们仔细看看Start,RecvACK,SendData这些函数,不就是IIC的时序吗?
大不了我们不用库函数给我们集成好的完整的流程,我们拿这些小时序自己拼凑一下不就可以了嘛?
由于这些小时序没有声明在头文件里,所以我们可以声明在头文件里,或者直接把SGP30的操作封装一下写在IIC的文件里。
明显是后者更好,因为这些时序的名字过于简单粗暴,声明在头文件里的很容易就和外面的函数重复定义了,所以与其把它们“搬出来”,不如我们“进去”。
函数我已经写好了,我们直接把下面的函数复制进STC32G_I2C.c就行。记得在头文件里声明一下。
void Sgp30_Init(void){
Start();
SendData(0xB0);
RecvACK();
SendData(0x20);
RecvACK();
SendData(0x03);
RecvACK();
Stop();
delay_ms(10);
}
void Sgp30_GetVal(uint8* val){
Start();
SendData(0xB0);
RecvACK();
SendData(0x20);
RecvACK();
SendData(0x08);
RecvACK();
delay_ms(10); //需要等待10ms,保险起见延时久一点
Start();
SendData(0xB1);
RecvACK();
val[0] = RecvData();
SendACK();
val[1] = RecvData();
SendACK();
val[2] = RecvData();
SendACK();
val[3] = RecvData();
SendACK();
val[4] = RecvData();
SendACK();
val[5] = RecvData();
SendNAK(); //send no ACK
Stop();
}
然后我们是主机模式的话,基本用不上中断函数。
经过我实测,没有使能中断也能用,所以我们可以不开中断。
我们给IIC配置使能之后,把对应的GPIO配置好就能使用了。
#include "STC32G_GPIO.h"
#include "STC32G_Delay.h"
#include "STC32G_I2C.h"
#include "STC32G_UART.h"
#include "STC32G_NVIC.h"
#include "STC32G_Switch.h"
void GPIO_Init(void){
P0_MODE_OUT_PP(GPIO_Pin_1);
P0_MODE_IN_HIZ(GPIO_Pin_0);
P2_MODE_IO_PU(GPIO_Pin_4);
P2_MODE_IO_PU(GPIO_Pin_5);
}
void UART_Init(void){
COMx_InitDefine initer;
initer.BaudRateDouble = DISABLE;
initer.Morecommunicate = DISABLE;
initer.UART_BaudRate = 115200;
initer.UART_BRT_Use = BRT_Timer3;
initer.UART_Mode = UART_8bit_BRTx;
initer.UART_RxEnable = ENABLE;
UART_Configuration(UART3, &initer);
NVIC_UART3_Init(ENABLE, Priority_1);
UART3_SW(UART3_SW_P00_P01);
}
void IIC_Init(void){
I2C_InitTypeDef initer;
initer.I2C_Enable = ENABLE; // 使能
initer.I2C_Mode = I2C_Mode_Master; // 主机模式
initer.I2C_MS_WDTA = DISABLE; // 自动发送
initer.I2C_Speed = 58; // 分频控制IIC速度为100k
I2C_Init(&initer);
// NVIC_I2C_Init(I2C_Mode_Master,ENABLE,Priority_0); //主从模式, I2C_Mode_Master, I2C_Mode_Slave; 中断使能, I2C_ESTAI/I2C_ERXI/I2C_ETXI/I2C_ESTOI/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
I2C_SW(I2C_P24_P25);
}
void main(void){
uint8 i = 0;
uint8 Sgp30_ReceiveDate[6];
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXSFR(); //扩展SFR(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
GPIO_Init();
UART_Init();
IIC_Init();
EA = 1;
Sgp30_Init();
printf("Hello\r\n");
while(1){
Sgp30_GetVal(Sgp30_ReceiveDate);
printf("%#x %#x %#x\t%#x %#x %#x\r\n",Sgp30_ReceiveDate[0], Sgp30_ReceiveDate[1],\
Sgp30_ReceiveDate[2], Sgp30_ReceiveDate[3], Sgp30_ReceiveDate[4], Sgp30_ReceiveDate[5]);
delay_ms(3000);
}
return ;
}
一开始SGP30的数据固定是CO2为400,TVOC为0,等待大概十几二十秒之后就数据正常了。
一般来说CO2的浓度会在400~1000ppm之间,都是正常现象。