CH58x 芯片 I2C 使用(SHT20/SHT40示例)

复制代码
CH58x 芯片 I2C 的使用说明     ...... 矜辰所致

前言

抽空写一篇基础文章,说明一下 CH58x 硬件 I2C,软件I2C 的使用,同时给出常用温湿度传感器 SHT20/SHT40 的示例代码。

简而言之本文就是讲一下 CH58x 芯片 I2C 接口的使用。

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

目录

  • 前言
  • [一、 I2C 协议基础](#一、 I2C 协议基础)
  • [二、硬件 I2C](#二、硬件 I2C)
    • [2.1 硬件 I2C 流程](#2.1 硬件 I2C 流程)
    • [2.2 地址说明](#2.2 地址说明)
    • [2.3 硬件 I2C 示例](#2.3 硬件 I2C 示例)
      • [2.3.1 SHT40 Demo](#2.3.1 SHT40 Demo)
      • [2.3.2 SHT20 Demo](#2.3.2 SHT20 Demo)
  • [三、软件 I2C](#三、软件 I2C)
    • [2.1 软件 I2C 实现](#2.1 软件 I2C 实现)
    • [2.2 软件 I2C 示例](#2.2 软件 I2C 示例)
  • 四、示例代码下载
  • 结语

一、 I2C 协议基础

I2C 的基础知识,不懂的小伙伴自行上网查看,简单说明一下注意点:

  1. I²C 总线只需要两条线,SCL (时钟线)和 SDA(数据线),数据线需要配置为开漏模式,必须接上拉电阻;

  2. 速率:标准 100 kHz 快速 400 kHz;

  3. 数据传输

    起始信号:当SCL为高电平时,SDA从高到低跳变表示开始

    结束信号:当SCL为高电平时,SDA从低到高跳变表示结束

    ACK :第 9 个时钟 SDA 被拉低

    NACK :第 9 个时钟 SDA 保持高

  4. 上拉电阻,一般标准速率模式 3.3V 选 4.7K 左右,10K内都是可以的,速度越高电阻要相对选小一些。

对于 CH585 来说,支持的功能可以直接查看手册:

包括 I2C 的使用方式流程,芯片手册里面也都写了,比如基础的时序图 (具体资料大家自行查看手册):

需要说明的一点就是对于CH58x 系列芯片来说,它不支持手动设置开漏输出,我们需要把它们设置为上拉输入,芯片使用 I2C 的时候会自动开漏输出,但是它的内部上拉是弱上拉,外面还是需要正常接上拉电阻的!

本文我们讨论常用的模式:芯片做主机模式 ,读取温湿度传感器的数据。至于从机模式的话,官方示例起始也实现了,大家需要可以自己测试。

二、硬件 I2C

官方提供的 I2C 例程为硬件 I2C ,我们本文主要看一下主机模式,在示例上需要把主机宏定义打开,从机宏定义注销:

c 复制代码
#define I2C_MODE      IIC_HOST_MODE
// #define I2C_MODE      IIC_SLAVE_MODE

CH585 上默认 I2C 的引脚为:

PB12 SDA

PB13 SCL

可以重映射为:

PB20 SDA

PB21 SCL

2.1 硬件 I2C 流程

这里流程我们根据代码来说明,作为应用来说一般正常流程操作,配置正确,都不会有什么问题(实际应用中常见的问题,有一部分是配置问题,一部分是地址问题,还有一部分是速率问题)。

  1. 配置对应 I2C 引脚为上拉输入:

    ex:"GPIOB_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13, GPIO_ModeIN_PU);

  2. I2C 初始化,使用 I2C_Init 配置模式,速率,地址位等:

    ex:I2C_Init(I2C_Mode_I2C, 400000, I2C_DutyCycle_16_9, I2C_Ack_Enable, I2C_AckAddr_7bit, MASTER_ADDR);

  3. 初始化后,每次操作 I2C 前需要等待标志位,不同的操作等待的标志位不一样,这个具体参照示例:

    ex:while(I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET);

  4. 发送起始信号:

    ex:
    while(I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET); 等待总线处于空闲状态
    I2C_GenerateSTART(ENABLE);

  5. 发送7bit 从机地址设置 I2C 方向,设置为写状态:

    ex:
    while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));等待IIC接口设置为主模式,等待起始信号发送完成
    I2C_Send7bitAddress(SLAVE_ADDR, I2C_Direction_Transmitter);

  6. 开始发送数据:

    ex:
    while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    if(I2C_GetFlagStatus(I2C_FLAG_TXE) != RESET)
    {
    I2C_SendData(SHT40_CMD_HIGH_PRECISION);
    }

  7. 如果不需要接收数据,发送停止信号:

    ex:
    while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    I2C_GenerateSTOP(ENABLE);

  8. 如果需要读取从机返回的数据,等待一定时间(这个时间一般是有从机决定,比如传感器手册上面会写发送数据读取命令以后,需要等待多久才能读取数据)。

    ex:DelayMs(20);

  9. 读数据之前又得重新发送起始信号:

    ex:I2C_GenerateSTART(ENABLE);

  10. 发送7bit 从机地址设置 I2C 方向,设置为读状态:

    ex:
    while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));
    I2C_Send7bitAddress(SHT40_I2C_ADDR, I2C_Direction_Receiver);

  11. 开始接收数据:

    ex:
    while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
    if(I2C_GetFlagStatus(I2C_FLAG_RXNE) != RESET)
    {
    RxData[i] = I2C_ReceiveData();
    }

  12. 接收完毕发送停止信号:

    ex: I2C_GenerateSTOP(ENABLE);

2.2 地址说明

有个关键点需要特殊说明一下:

就是函数 I2C_Send7bitAddress 传入的设备地址的问题,需要自己左移一位。

比如,对于 SHT40 ,手册上它的地址为 0x44:

我们使用硬件 I2C 的函数的时候,需要如下定义:

c 复制代码
#define SHT40_I2C_ADDR      0x44<<1 
I2C_Send7bitAddress(SHT40_I2C_ADDR, I2C_Direction_Transmitter);

这是使用库函数需要注意的一个点,在官方示例中,有应用层自己封装的 i2c_write_to i2c_read_from 函数,里面的地址需要注意一下,这是官方写的应用层的示例,我们自己写的时候可以参考,也可以直接使用库函数,但是记住使用库函数时传入的地址需要左移一位 。

2.3 硬件 I2C 示例

基本说明介绍完毕,下面就直接上一下传感器示例:

2.3.1 SHT40 Demo

SHT40 的代码很简单,传感器手册里面也写了,以前在 STM32 上的代码可以参考我的文章:

KEIL中编译51程序 算法计算异常的疑问

I2C 初始化代码:

c 复制代码
void my_iic_init(uint8_t num){
  if(1== num){
      GPIOB_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13, GPIO_ModeIN_PU);
  }
  else if(2 == num){
      GPIOPinRemap(ENABLE, RB_PIN_I2C);
      GPIOB_ModeCfg(GPIO_Pin_21 | GPIO_Pin_20, GPIO_ModeIN_PU);
  }
  I2C_Init(I2C_Mode_I2C, 100000, I2C_DutyCycle_16_9, I2C_Ack_Enable, I2C_AckAddr_7bit,0);
}

主函数:

c 复制代码
...
PRINT("test begin!\r\n");
my_iic_init(1);
while(1)
 {
     DelayMs(1000);
     sht40_read();
 }

SHT40传感器读取函数:

c 复制代码
void sht40_read()
{
    PRINT("this is  sht40 test\n");
    uint8_t userRegister;

    while(I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET);

    I2C_GenerateSTART(ENABLE);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(SHT40_I2C_ADDR, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    if(I2C_GetFlagStatus(I2C_FLAG_TXE) != RESET)
    {
        I2C_SendData(SHT40_CMD_HIGH_PRECISION);
    }

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    //需不需要 STOP
    DelayMs(20);

    uint16_t tem, hum;
    uint8_t checksum;

    I2C_GenerateSTART(ENABLE);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(SHT40_I2C_ADDR, I2C_Direction_Receiver);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    uint8_t RxData[6],i=0;

    while(i < 6)
    {
        if(I2C_GetFlagStatus(I2C_FLAG_RXNE) != RESET)
        {
            RxData[i] = I2C_ReceiveData();
            i++;
            if(i==5)
            {
//                I2C_AcknowledgeConfig(DISABLE);
                I2C_GenerateSTOP(ENABLE);
            }
        }

    }

    tem = ((RxData[0]<<8)|RxData[1]);
    hum = ((RxData[3]<<8)|RxData[4]);

    float temperature = tem * (175.0 / 65536.0);
    temperature = temperature - 45.0;

    float humidity = hum * (125.0 / 65536.0);
    humidity = humidity - 6.0;

    printf("sht40 humi:%.2f %%\r\n",humidity);
    printf("sht40 tem:%.2f ℃\r\n",temperature);

}

测试结果:

2.3.2 SHT20 Demo

SHT20 的示例如下,资料自行网上查看,这里也上一下代码(SHT 20 的代码是以前学习的时候网上找的):

主函数:

c 复制代码
...
PRINT("test begin!\r\n");
my_iic_init(1);
prepare_sht20(USER_REGISTER_HEATER_ENABLED);
while(1)
 {
     DelayMs(1000);
     sht20_read();
 }

SHT20传感器读取函数:

c 复制代码
void prepare_sht20(uint8_t val)
{

    while(I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET);

    I2C_GenerateSTART(ENABLE);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(SHT20_I2C_ADDR, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    if(I2C_GetFlagStatus(I2C_FLAG_TXE) != RESET)
    {
        I2C_SendData(WRITE_USER_REG);
    }

    if(I2C_GetFlagStatus(I2C_FLAG_TXE) != RESET)
    {
        I2C_SendData(val);
    }

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_GenerateSTOP(ENABLE);

}

/*
 * SHT20_I2C_ADDR 0x40 需要左移一位 变成 0x80 放入函数
 */
uint16_t readValue(uint8_t cmd)
{
    uint8_t userRegister;

    while(I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET);

    I2C_GenerateSTART(ENABLE);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(SHT20_I2C_ADDR, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    if(I2C_GetFlagStatus(I2C_FLAG_TXE) != RESET)
    {
        I2C_SendData(cmd);
    }

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    uint8_t msb, lsb, checksum;

    I2C_GenerateSTART(ENABLE);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(SHT20_I2C_ADDR, I2C_Direction_Receiver);

    while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    uint8_t RxData[3],i=0;

    while(i < 3)
    {
        if(I2C_GetFlagStatus(I2C_FLAG_RXNE) != RESET)
        {
            RxData[i] = I2C_ReceiveData();
            i++;
            if(i==2)
            {
//                I2C_AcknowledgeConfig(DISABLE);
                I2C_GenerateSTOP(ENABLE);
            }
        }

    }

    msb = RxData[0];
    lsb = RxData[1];
    checksum = RxData[2];
    uint16_t rawValue = ((uint16_t) msb << 8) | (uint16_t) lsb;
    if(checkCRC(rawValue, checksum) != 0){
        return (ERROR_BAD_CRC);
    }
    return rawValue & 0xFFFC;
}

float readHumidity(void)
{
    uint16_t rawHumidity = readValue(TRIGGER_HUMD_MEASURE_HOLD);
    if(rawHumidity == ERROR_I2C_TIMEOUT || rawHumidity == ERROR_BAD_CRC){
        return(rawHumidity);
    }
    float tempRH = rawHumidity * (125.0 / 65536.0);
    float rh = tempRH - 6.0;
    return (rh);
}

float readTemperature(void)
{
    uint16_t rawTemperature = readValue(TRIGGER_TEMP_MEASURE_HOLD);
    if(rawTemperature == ERROR_I2C_TIMEOUT || rawTemperature == ERROR_BAD_CRC){
        return(rawTemperature);
    }
    float tempTemperature = rawTemperature * (175.72 / 65536.0);
    float realTemperature = tempTemperature - 46.85;
    return (realTemperature);
}

void sht20_read(){

    float temperature = readTemperature();
    float humidity = readHumidity();

    printf("sht20 humi:%.2f %%\r\n",humidity);
    printf("sht20 tem:%.2f ℃\r\n",temperature);
}

测试结果:

三、软件 I2C

软件 I2C 这个以前使用 STM32 的时候是一直使用的,比如正点原子软件 I2C 的驱动原理就可以用,以前我也有博文说明过:STM32L051测试 (三、I2C协议设备的添加测试)

只要理解 I2C 协议基础知识,软件 I2C 相对来说比硬件 I2C 流程看起来更加直观,完全按照 I2C 协议的通信流程人为的控制进度,软件 I2C 不限制固定的 IO 口,可以使用任意的两个 IO 口实现,而且可以跨平台实现,以前 STM32 ,51单片机上面的驱动直接改一下引脚配置、匹配一下延时函数就可以使用了。

这个具体的基础知识网上太多了,不懂得小伙伴自己补充一下,这里我们测试一下软件 I2C 的效果。

2.1 软件 I2C 实现

软件实现代码可参考博文: https://www.cnblogs.com/risc5-ble/p/18663782

上面博文实现的是 SHT20/21 的驱动代码,博主后面上的代码也是用此驱动测试过 SHT40 的。

只是上面博文中的接收函数,没有回 ACK 和 NACK 部分,测试修改如下:

c 复制代码
/* ack=1 发 ACK,ack=0 发 NACK */
uint8_t IIC_ReadByte(uint8_t ack)
{
    uint8_t i = 8;
    uint8_t ReceiveByte = 0;
    IIC_SDA_H();
    while (i--)
    {
        ReceiveByte <<= 1;
        IIC_SCL_L();
        IIC_Delay();
        IIC_SCL_H();
        IIC_Delay();
        if (SDA_read())
            ReceiveByte |= 0x01;
    }
    IIC_SCL_L();        // 先拉低 SCL
    if (ack) IIC_SDA_L(); else IIC_SDA_H();
    IIC_Delay();
    IIC_SCL_H();        // 拉高 SCL,从设备采样 ACK/NACK
    IIC_Delay();
    IIC_SCL_L();        // 结束
    // IIC_SDA_H();
    return ReceiveByte;
}

其他部分参考原文,当然博主下面也把本文所有示例代码都打包,大家可以下载。

2.2 软件 I2C 示例

大家可自行下载代码查看,代码下载链接在下面。

四、示例代码下载

本问测试的示例工程如下:

示例中有2个宏定义:

大家下载后是需要放到 CH585 EVT 原 I2C 程序目录下面去测试的,因为我没有把此工程独立出来,需要使用 EVT 里面的库文件。

我把代码放到 Gitee ,大家可以自己去下载:

矜辰所致的 Gitee 仓库

或者直接在 CSDN 资源下载也可以,后面等我上传。

结语

本文主要分别介绍了 CH58x 芯片硬件 I2C 和软件 I2C 的使用,给出了两款常用温湿度传感器的使用示例。只要搞清楚流程,所有的 I2C 设备按照规定的协议方式通信,基本都是类似的。

好了,本文就到这里。谢谢大家!

相关推荐
熬夜有啥好4 天前
基于IMXULL的SHT3X 温湿度传感器 I2C 驱动开发调试记录
驱动开发·imx6ull·i2c·sht3x温湿度传感器
宏集科技工业物联网5 天前
告别人工巡检,数据中心无线温湿度监测一步到位实现智能化
经验分享·温湿度传感器·环境监测系统·温湿度监测·无线温湿度传感器·无线环境监测系统
zmj3203245 天前
I2C总线协议详细介绍
单片机·嵌入式硬件·i2c·总线协议
盛世宏博北京12 天前
多协议温湿度传感器技术解析及系统集成方案设计
大数据·人工智能·温湿度传感器
CinzWS15 天前
I2C协议 - 优雅的代价:深入开漏总线、时钟延展与多主仲裁的脆弱平衡
嵌入式·i2c
嵌入式×边缘AI:打怪升级日志15 天前
使用文件 I/O 操作硬件 —— 从 LED 到温湿度传感器
qt·led·温湿度传感器
PegasusYu16 天前
STM32 I2C访问配置霍尔磁角度传感器MT6701
stm32·编码器·i2c·stm32cubeide·mt6701·角度·磁角度传感器
赤南19 天前
从入门到精通的i2c-tools实战手册
i2c·openbmc·linux工具
π同学1 个月前
ESP-IDF+vscode开发ESP32第四讲——I2C
vscode·esp32·i2c
somi71 个月前
ARM-09-I.MX6U-I2C
单片机·嵌入式硬件·i2c·自用