【STC库函数】I2C的使用(拿SGP30测试一下)

写在开头,为什么我文章里写的不是I2C而是IIC,因为它们俩是一样的东西写成啥都可以,虽然大家平时看到的更多的是I2C,但是写成IIC的话,会比较方便我码字。

今天我们来看看在STC库函数中IIC如何使用。

一共有三组引脚可以供我们选择,我们照例是要包含头文件STC32G_Switch.h

可以使用下面的函数来选择引脚,把里面的参数改了就可以换其他引脚了。

I2C_SW(I2C_P24_P25);

STC的IIC没有总线仲裁功能,所以如果需要的话,我们就得使用软件IIC然后自己去写了。

有主机和从机两种模式,但是一般来说都是拿来作为主机的,所以就不讲从机方面的内容了。

当我们配置完IIC之后,只需要去改写IIC主机控制寄存器就可以发出相对应的IIC时序了。

 可能有小伙伴有疑问,我这个系列不是只用库函数,能不讲寄存器就不讲寄存器的嘛?

关于这一点,马上揭晓为什么,我们直接来看库函数。

我们把文件STC32G_I2C.cSTC32G_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之间,都是正常现象。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值