使用STM32F103 I2C驱动SHT30

SHT30的官方代码见《Sensirion_Humidity_Sensors_SHT3x_Sample_Code_V2》,不过官方是用GPIO模拟的I2C时序,现在修改为STM32F103的硬件I2C驱动SHT30.

使用STM32F103的I2C1实现该功能,相关头文件定义如下:

#define SHT30_I2C                            I2C1#define SHT30_I2C_ADDR                     (0x44) #define SHT30_I2C_SCL_PIN                  (GPIO_Pin_6)           /* PB.06 */
#define SHT30_I2C_SCL_GPIO_PORT            (GPIOB)#define SHT30_I2C_SDA_PIN                  (GPIO_Pin_7)           /* PB.07 */
#define SHT30_I2C_SDA_GPIO_PORT            (GPIOB)#define I2C_SPEED               100000U

官方代码在while(1)里调用SHT3X_GetTempAndHumi函数获取温湿度值(时钟延伸模式):
//-----------------------------------------------------------------------------

while(1){SHT3X_GetTempAndHumi(&temperature, &humidity, REPEATAB_HIGH, MODE_CLKSTRETCH, 50);printf("temperature=%.1f℃\thumidity=%.1f%%RH\terror=%d\r\n",(float)temperature,(float)humidity,(u8)error);}

根据产品实际需求,在需要获取温湿度时调用SHT3X_GetTempAndHumi()函数即可。

//-----------------------------------------------------------------------------
1:获取温湿度函数SHT3X_GetTempAndHumi()

etError SHT3X_GetTempAndHumi(ft* temperature, ft* humidity,etRepeatability repeatability, etMode mode, u8t timeout)
{etError error;switch(mode){case MODE_CLKSTRETCH: // get temperature with clock stretching mode error = SHT3X_GetTempAndHumiClkStretch(temperature, humidity,repeatability, timeout);break;case MODE_POLLING:  // get temperature with polling mode error = SHT3X_GetTempAndHumiPolling(temperature, humidity,repeatability, timeout);break; default:error = PARM_ERROR; break;}return error;
}

由于我们使用的是MODE_CLKSTRETCH时钟延伸模式,因此只修改SHT3X_GetTempAndHumiClkStretch函数。

2:用时钟延伸模式获取温湿度函数SHT3X_GetTempAndHumiClkStretch();

//-----------------------------------------------------------------------------
etError SHT3X_GetTempAndHumiClkStretch(ft* temperature, ft* humidity,etRepeatability repeatability, u8t timeout)
{etError error; // error codeu16t   rawValueTemp; // temperature raw value from sensor u16t rawValueHumi; // humidity raw value from sensorerror = SHT3X_StartWriteAccess();// if no error ...if(error == NO_ERROR){// start measurement in clock stretching mode// use depending on the required repeatability, the corresponding commandswitch(repeatability){case REPEATAB_LOW:error = SHT3X_WriteCommand(CMD_MEAS_CLOCKSTR_L); break;case REPEATAB_MEDIUM:error = SHT3X_WriteCommand(CMD_MEAS_CLOCKSTR_M); break;case REPEATAB_HIGH:error = SHT3X_WriteCommand(CMD_MEAS_CLOCKSTR_H); break;default:error = PARM_ERROR; break;}}// if no error, start read accessif(error == NO_ERROR) error = SHT3X_StartReadAccess();// if no error, read temperature raw valuesif(error == NO_ERROR) error = SHT3X_Read2BytesAndCrc(&rawValueTemp, ACK, timeout);// if no error, read humidity raw valuesif(error == NO_ERROR) error = SHT3X_Read2BytesAndCrc(&rawValueHumi, NACK, 0); //SHT3X_StopAccess();// if no error, calculate temperature in °C and humidity in %RHif(error == NO_ERROR){*temperature = SHT3X_CalcTemperature(rawValueTemp);*humidity = SHT3X_CalcHumidity(rawValueHumi);}else{SHT3X_StopAccess();}return error;
}

与官方源代码相比,修改了函数SHT3X_StopAccess()的定义及使用方式。官方源代码在接收到6个字节的温湿度数据后,调用SHT3X_StopAccess()模拟产生I2C通信停止条件。而硬件I2C(主接收模式)关闭通信则需要以下操作(RM0008中文参考手册P499):

  主设备在从 从设备 接收到最后一个字节后发送一个NACK。接收到NACK后,从设备释放对SCL和SDA线的控制;主设备就可以发送一个停止/重起始条件.1:为了在收到最后一个字节后产生NACK脉冲,在读倒数第二个字节之后必须清除ACK位.2:为了产生一个停止/重起始条件,软件必须在读第二个数据字节之后设置STOP/START位.

主接收器传送序列图:

因此,应在接收最后一个字节之前添加通信停止代码,该代码会在I2c_ReadByte()函数里描述。

3:开始写入访问函数SHT3X_StartWriteAccess()。该函数先判断总线是否busy,不忙的话产生开始条件,再发送从机地址,然后等待从机应答。

//-----------------------------------------------------------------------------
static etError SHT3X_StartWriteAccess(void)
{etError error = NO_ERROR; // error code/*!< While the bus is busy */SHT30_Timeout = SHT30_LONG_TIMEOUT;while(I2C_GetFlagStatus(SHT30_I2C, I2C_FLAG_BUSY)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_ERROR;return error;}}I2C_AcknowledgeConfig(SHT30_I2C, ENABLE);//I2c_StartCondition();// write a start condition I2C_GenerateSTART(SHT30_I2C, ENABLE);/*!< Test on EV5 and clear it (cleared by reading SR1 then writing to DR) *//** * @brief  Communication start* * After sending the START condition (I2C_GenerateSTART() function) the master * has to wait for this event. It means that the Start condition has been correctly * released on the I2C bus (the bus is free, no other devices is communicating).* */SHT30_Timeout = SHT30_FLAG_TIMEOUT;while(!I2C_CheckEvent(SHT30_I2C, I2C_EVENT_MASTER_MODE_SELECT)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_EV5_ERROR;return error;}}// write the sensor I2C address with the write flag //error = I2c_WriteByte(_i2cAddress << 1);//发送地址后,等待从机确认 EV6I2C_Send7bitAddress(SHT30_I2C, (SHT30_I2C_ADDR << 1), I2C_Direction_Transmitter);/*!< Test on EV6 and clear it */ SHT30_Timeout = SHT30_LONG_TIMEOUT;while(!I2C_CheckEvent(SHT30_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_EV6_ERROR;return error;}}return error;
}//-----------------------------------------------------------------------------

4:写命令函数SHT3X_WriteCommand()
从机应答主机地址后,SHT3X_WriteCommand()函数开始写命令,先发送高字节,再发生低字节。每个字节发送后均要等待从机应答。发送完成后,主机产生停止信号。

//-----------------------------------------------------------------------------
static etError SHT3X_WriteCommand(etCommands command)
{etError error = NO_ERROR; // error code// write the upper 8 bits of the command to the sensor //error = I2c_WriteByte(command >> 8);I2C_SendData(SHT30_I2C,command >> 8);/* --EV8 */SHT30_Timeout = SHT30_FLAG_TIMEOUT;while(!I2C_CheckEvent(SHT30_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_EV8_ERROR;return error;}}// write the lower 8 bits of the command to the sensor //error |= I2c_WriteByte(command & 0x00FF);I2C_SendData(SHT30_I2C,command & 0x00FF);SHT30_Timeout = SHT30_FLAG_TIMEOUT;while(!I2C_CheckEvent(SHT30_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))//EV8_2{if((SHT30_Timeout--) == 0) {error = TIMEOUT_EV8_ERROR;return error;}}I2C_GenerateSTOP(SHT30_I2C, ENABLE);//主发送器通信完成return error;
}

5:开始读访问SHT3X_StartReadAccess()

//-----------------------------------------------------------------------------
static etError SHT3X_StartReadAccess(void)
{etError error; // error code// write a start condition //I2c_StartCondition();/*!< While the bus is busy */SHT30_Timeout = SHT30_LONG_TIMEOUT;while(I2C_GetFlagStatus(SHT30_I2C, I2C_FLAG_BUSY)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_ERROR;return error;}}// write a start condition //I2c_StartCondition();//EV5:After sending the START condition (I2C_GenerateSTART() function) the master //has to wait for this event.I2C_GenerateSTART(SHT30_I2C, ENABLE);/*!< Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */SHT30_Timeout = SHT30_FLAG_TIMEOUT;while(!I2C_CheckEvent(SHT30_I2C, I2C_EVENT_MASTER_MODE_SELECT)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_EV5_ERROR;return error;}}// write the sensor I2C address with the write flag //error = I2c_WriteByte(_i2cAddress << 1);//发送地址后,等待从机确认 EV6I2C_Send7bitAddress(SHT30_I2C, (SHT30_I2C_ADDR << 1), I2C_Direction_Receiver);/*!< Test on EV6 and clear it */ SHT30_Timeout = SHT30_FLAG_TIMEOUT;while(!I2C_CheckEvent(SHT30_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_EV6_ERROR;return error;}}// write the sensor I2C address with the read flag //error = I2c_WriteByte(_i2cAddress << 1 | 0x01);return error;
}

6:读取两个字节数据和Crc:SHT3X_Read2BytesAndCrc()和官方代码一样。

//-----------------------------------------------------------------------------
static etError SHT3X_Read2BytesAndCrc(u16t* data, etI2cAck finaleAckNack,u8t timeout)
{etError error; // error codeu8t    bytes[2]; // read data array u8t    checksum; // checksum byte// read two data bytes and one checksum byteerror = I2c_ReadByte(&bytes[0], ACK, timeout);if(error == NO_ERROR) error = I2c_ReadByte(&bytes[1], ACK, 0);if(error == NO_ERROR) error = I2c_ReadByte(&checksum, finaleAckNack, 0);// verify checksumif(error == NO_ERROR) error = SHT3X_CheckCrc(bytes, 2, checksum);// combine the two bytes to a 16-bit value*data = (bytes[0] << 8) | bytes[1];return error;
}

7:读去字节I2c_ReadByte()
为了防止读取数据时,任务被中断打断导致数据出错,读取时先关闭中断,读取完成后,再开启中断。

etError I2c_ReadByte(u8t *rxByte, etI2cAck ack, u8t timeout)
{etError error = NO_ERROR; __set_PRIMASK(1);//关总中断if(ack == ACK){I2C_AcknowledgeConfig(SHT30_I2C, ENABLE);}else{//关闭通信:/* 主设备在从 从设备 接收到最后一个字节后发送一个NACK。接收到NACK后,从设备* 释放对SCL和SDA线的控制;主设备就可以发送一个停止/重起始条件.* 1:为了在收到最后一个字节后产生NACK脉冲,在读倒数第二个字节之后必须清除ACK位.* 2:为了产生一个停止/重起始条件,软件必须在读第二个数据字节之后设置STOP/START位.*/I2C_AcknowledgeConfig(SHT30_I2C, DISABLE);(void)SHT30_I2C->SR2;I2C_GenerateSTOP(SHT30_I2C, ENABLE);}//从机端接收到主机地址后,开始向主机发送数据/*!< Test on EV7 */ SHT30_Timeout = SHT30_LONG_TIMEOUT;while(!I2C_CheckEvent(SHT30_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)){if((SHT30_Timeout--) == 0) {error = TIMEOUT_EV7_ERROR;return error;}}*rxByte  = I2C_ReceiveData(SHT30_I2C);if(ack == NACK){/* Wait to make sure that STOP control bit has been cleared *///STOP位:监测到停止条件时,由硬件清除。SHT30_Timeout = SHT30_FLAG_TIMEOUT;while(SHT30_I2C->CR1 & I2C_CR1_STOP){if((SHT30_Timeout--) == 0) {error = TIMEOUT_ERROR;return error;}}}__set_PRIMASK(0);//开总中断return error;      //return with no error
}

8:SHT3X_CheckCrc()和官方代码一样。

//-----------------------------------------------------------------------------
static etError SHT3X_CheckCrc(u8t data[], u8t nbrOfBytes, u8t checksum)
{u8t crc;   // calculated checksum// calculates 8-Bit checksumcrc = SHT3X_CalcCrc(data, nbrOfBytes);// verify checksumif(crc != checksum) return CHECKSUM_ERROR; else return NO_ERROR;
}

9:SHT3X_StopAccess()
修改了SHT3X_StopAccess函数的内容及使用方式,当检测到有ERROR时,调用该函数,重新初始化I2C。

static void SHT3X_StopAccess(void)
{I2C_DeInit(SHT30_I2C);SHT30_Init(SHT30_I2C_ADDR);
}

10: I2C配置:SHT30_Init()

void SHT30_Init(uint8_t i2cAddress)
{I2C_InitTypeDef  I2C_InitStructure;GPIO_InitTypeDef  GPIO_InitStructure;/*配置时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);GPIO_InitStructure.GPIO_Pin = SHT30_I2C_SCL_PIN|SHT30_I2C_SDA_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_Init(SHT30_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);/*!< SHT30_I2C Periph clock enable */RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); I2C_DeInit(I2C1);/*!< I2C configuration */I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 = i2cAddress;I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;/* Apply I2C configuration after enabling it */I2C_Init(I2C1, &I2C_InitStructure);/* I2C Peripheral Enable */I2C_Cmd(I2C1, ENABLE);
}

起始条件+地址+写命令+命令码高字节:

命令码低字节+停止条件+起始条件+地址+读命令+时钟延伸(等待转换结果):

获取的温湿度值:

使用STM32F103 I2C驱动SHT30相关推荐

  1. CM3计算板读取SHT30以及I2C驱动

    1.引言 用SHT30测温湿度,SHT30是I2C通信总线,具体信息去看Datasheet文档:https://pdf1.alldatasheet.com/datasheet-pdf/view/897 ...

  2. K_A11_008 基于STM32等单片机驱动SHT30和SHT31 串口与OLED0.96双显示

    K_A11_008 基于STM32等单片机驱动SHT30和SHT31 串口与OLED0.96双显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 时序 对应程序: 四.部分代码说明 1.接 ...

  3. linux i2c核心,总线与设备驱动,Linux2.6.37 I2C驱动框架分析(一)

    最近工作中又使用到了I2C,所以借S3C2440开发板GT2440为硬件平台温习一遍I2C驱动体系. linux内核中IIC驱动的体系框架 linux内核中IIC部分驱动代码位于:/drivers/i ...

  4. linux驱动:i2c驱动(四)流程图之注册驱动

    二.i2c设备的驱动部分 1.i2c驱动i2c_driver 2.通过i2c_add_driver注册 2.注册过程中 比较i2c_device_id数组中各成员的id与i2c_client中的名字, ...

  5. linux驱动:i2c驱动(二)

    3.驱动源码分析 IPNC_RDK_V3.8.0.1/Source/ti_tools/ipnc_psp_arago/kernel/sound/soc/codecs/tlv320aic3x.c 3.1 ...

  6. linux驱动:i2c驱动(一)

    I2C系统框架:I2C核心层.I2C总线驱动.I2C设备驱动 --------------------------------------------------------------------- ...

  7. 设备树与驱动的关系_Linux I2C驱动竟然如此简单?手把手教你写i2c驱动

    Linux中I2C驱动框架分析 I2C核心(i2c_core) I2C核心维护了i2c_bus结构体,提供了I2C总线驱动和设备驱动的注册.注销方法,维护了I2C总线的驱动.设备链表,实现了设备.驱动 ...

  8. 基于WinCE的I2C驱动程序设计

    http://www.mcu123.com/news/Article/rtos/WinCE/200607/88.html 引言 随着以计算机技术.通信技术和软件技术为核心的信息技术的迅速发展,嵌入式系 ...

  9. 【驱动】linux下I2C驱动架构全面分析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

最新文章

  1. 下载MySQL数据库
  2. matlab 双音多频 接收端检测到的号码,信号语音论文,关于基于MATLAB的双音多频信号识别相关参考文献资料-免费论文范文...
  3. Nginx服务学习(6)-日志模块
  4. 【模拟】【递归】解压字符串(jzoj 1519)
  5. 扩展Guava缓存溢出到磁盘
  6. windows下RocketMQ下载、安装、部署、控制台
  7. 06-02 Jenkins job 机制
  8. springcloud工作笔记099---springboot集成netty,进行线程管理,socket通讯
  9. 内存:DDR2与DDR
  10. 配置 sql server linux,配置SQL Server on Linux(2)
  11. 这月绩效差点没了!搞了一个“新人拼团”活动遇到黑产,搭进去了8台服务器......
  12. 美媒:中国假冒芯片太假 致驻伊美军频繁坠机
  13. 迅雷界面引擎Bolt中编译失败的问题
  14. Lattice FPGA 开发工具Diamond使用流程总结——安装
  15. 7.1编辑shapefile文件
  16. 有道词典(android)v4.3.3去广告清爽版+离线词,有道词典4.3.3安
  17. 13946 问题 E: 积木游戏
  18. android本地视频 投屏,小米投屏神器
  19. python数列求和_Python 数字求和
  20. Docker 容器安装监控软件 cAdvisor

热门文章

  1. android 4个button 田字布局,android新浪微博客户端开发.docx
  2. 三万字速通JavaScript
  3. 高仿大企业简洁响应电子商务网站模板 带后台 自适应版本 安卓+PC
  4. 仿iphone输入法_如何在iPhone上的法氏温度和摄氏温度之间切换
  5. 人脸检测颜值软件_颜值检测软件下载-颜值检测免费版下载-ROM之家
  6. 游客模式android,Android之游客登录模式
  7. 洛谷P1157组合的输出
  8. 格式工厂转换视频怎样设置才能使视频大小缩小
  9. 自制作的一个手机平衡球游戏!
  10. Javascr面向对象