目录
SHT30介绍
最近在做项目,在选材的时候要求一种传感器能够测量水温,传统的DHT11,DS18B20等一些可以检测的传感器做不到防水问题,在不断地挑选下,选择了这款性价比较低,且能防水的传感器SHT3X,下面看看它的介绍。
这款传感器淘宝卖10几块,性能方面不用说了,温度,湿度方面基本满足大多项目要求,下面来注入灵魂。首先是IIC协议,如果说是stm32f1x系列网上一整套资源写好,但是我用HT32移植stm32写的时候遇到问题挺多的,你们可以看看和stm32的有什么区别。
IIC协议
IIC.c
#include "iic.h"
#include "delay.h"
#include "usart.h"
//开启时钟
static void I2C_CKCU_Config()
{
CKCU_PeripClockConfig_TypeDef CCLOCK;
CCLOCK.Bit.PA = 1;
CCLOCK.Bit.AFIO = 1;
CKCU_PeripClockConfig(CCLOCK, ENABLE);
}
//引脚配置
static void I2C_GPIO_Config()
{
AFIO_GPxConfig(I2C_SCL_GPIO_ID, I2C_SCL_PIN, I2C_SCL_AFIO_MODE);
AFIO_GPxConfig(I2C_SDA_GPIO_ID, I2C_SDA_PIN, I2C_SDA_AFIO_MODE);
/* Configure the GPIO pin */
GPIO_PullResistorConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_PR_DISABLE);
GPIO_DriveConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_DV_8MA);
GPIO_DirectionConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_DIR_OUT);
GPIO_PullResistorConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_PR_DISABLE);
GPIO_DriveConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DV_8MA);
GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_OUT);
}
//初始化
void I2C_Initia()
{
I2C_CKCU_Config();
I2C_GPIO_Config();
/* 一定要先发一遍停止信号 */
I2C_Stop();
}
//I2C起始信号
void I2C_Start()
{
I2C_SDA_InputConfig();
I2C_SDA_Direction_Output(); //sda线输出
I2C_SDA_1();
I2C_SCL_1();
delay_us(4);
I2C_SDA_0();
delay_us(4);
I2C_SCL_0(); //钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void I2C_Stop()
{
I2C_SDA_Direction_Output(); //sda线输出
I2C_SCL_0();
I2C_SDA_0();
delay_us(4);
I2C_SCL_1();
I2C_SDA_1(); //发送I2C总线结束信号
delay_us(4);
}
/* 返回0表示正确应答,1表示无器件响应 */
u8 I2C_WaitAck()
{
u8 ucErrTime = 0;
I2C_SDA_Direction_Input(); //SDA设置为输入
I2C_SDA_1();
delay_us(2);
I2C_SCL_1();
delay_us(2);
// I2C_SDA_Direction_Input();
// I2C_SDA_InputConfig();
// if( I2C_SDA_READ() ) {
// ack = 1;
// }
// else {
// ack = 0;
// }
while( I2C_SDA_READ() )
{
ucErrTime ++;
if(ucErrTime > 250)
{
I2C_Stop();
return 1;
}
}
I2C_SCL_0();
return 0;
}
产生ACK应答
// void I2C_Ack()
//{
// I2C_SDA_0(); /* CPU驱动SDA = 0 */
// delay_us(DELAYTIME);
//
// I2C_SCL_1(); /* CPU产生1个时钟 */
// delay_us(DELAYTIME);
//
// I2C_SCL_0();
// delay_us(DELAYTIME);
// I2C_SDA_1(); /* CPU释放SDA总线 */
//}
//产生ACK应答
void I2C_Ack()
{
I2C_SCL_0();
I2C_SDA_Direction_Output();
I2C_SDA_0(); /* CPU驱动SDA = 1 */
delay_us(2);
I2C_SCL_1(); /* CPU产生1个时钟 */
delay_us(2);
I2C_SCL_0();
// delay_us(2);
// I2C_SDA_1(); /* CPU释放SDA总线 */
}
//不产生ACK应答
void I2C_NAck()
{
// I2C_SDA_1(); /* CPU驱动SDA = 1 */
// delay_us(DELAYTIME);
//
// I2C_SCL_1(); /* CPU产生1个时钟 */
// delay_us(DELAYTIME);
//
// I2C_SCL_0();
// delay_us(DELAYTIME);
I2C_SCL_0();
I2C_SDA_Direction_Output();
I2C_SDA_1(); /* CPU驱动SDA = 1 */
delay_us(2);
I2C_SCL_1(); /* CPU产生1个时钟 */
delay_us(2);
I2C_SCL_0();
}
void I2C_SendByte(u8 txd)
{
u8 i;
I2C_SDA_Direction_Output();
I2C_SCL_0();//拉低时钟开始数据传输
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if ((txd & 0x80)>>7)
I2C_SDA_1();
else
I2C_SDA_0();
txd<<=1;
delay_us(2);
I2C_SCL_1();
delay_us(2);
I2C_SCL_0();
delay_us(2);
// if (i == 7)
// {
// I2C_SDA_1(); // 释放总线
// }
// _ucByte <<= 1; /* 左移一个bit */
// delay_us(DELAYTIME);
}
}
u8 I2C_ReadByte(unsigned char ack)
{
u8 i,receive=0;
I2C_SDA_Direction_Input();
for(i = 0;i < 8;i++)
{
// data <<= 1;
I2C_SCL_0();
delay_us(2);
I2C_SCL_1();
// I2C_SDA_Direction_Input();
receive <<= 1;
if( I2C_SDA_READ() )
receive++;
delay_us(1);
}
//I2C_SCL_0();
if (!ack)
I2C_NAck();//发送nACK
else
I2C_Ack(); //发送ACK
return receive;
}
IIC.h
#ifndef _IIC_H
#define _IIC_H
#include "ht32f5xxxx_01.h"
/* 定义I2C的SCL连接的GPIO端口 */
#define I2C_SCL_GPIO_ID GPIO_PA /* GPIO端口 */
#define I2C_SCL_PIN GPIO_PIN_0 /* 连接到SCL时钟线的GPIO */
#define I2C_SCL_AFIO_MODE AFIO_FUN_GPIO
#define I2C_SCL_PORT HT_GPIOA
/* 定义I2C的SDA连接的GPIO端口 */
#define I2C_SDA_GPIO_ID GPIO_PA /* GPIO端口 */
#define I2C_SDA_PIN GPIO_PIN_1 /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_AFIO_MODE AFIO_FUN_GPIO
#define I2C_SDA_PORT HT_GPIOA
//IO操作函数
#define I2C_SCL_1() GPIO_SetOutBits(I2C_SCL_PORT, I2C_SCL_PIN) // 高电平
#define I2C_SCL_0() GPIO_ClearOutBits(I2C_SCL_PORT, I2C_SCL_PIN) //低电平
#define I2C_SDA_1() GPIO_SetOutBits(I2C_SDA_PORT, I2C_SDA_PIN)
#define I2C_SDA_0() GPIO_ClearOutBits(I2C_SDA_PORT, I2C_SDA_PIN)
#define I2C_SDA_READ() GPIO_ReadInBit(I2C_SDA_PORT, I2C_SDA_PIN) //输入SDA
//IO方向设置
#define I2C_SDA_Direction_Input() GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_IN)
#define I2C_SDA_Direction_Output() GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_OUT) //SDA线输出
#define I2C_SDA_InputConfig() GPIO_InputConfig(I2C_SDA_PORT, I2C_SDA_PIN, ENABLE) //使能输入输出
#define I2C_SDA_InputDisable() GPIO_InputConfig(I2C_SDA_PORT, I2C_SDA_PIN, DISABLE)
void I2C_Initia(void);
void I2C_Start(void);
void I2C_Stop(void);
u8 I2C_WaitAck(void);
void I2C_SendByte(u8 _ucByte);
u8 I2C_ReadByte(unsigned char ack);
void I2C_Ack(void);
void I2C_NAck(void);
#endif
总的来说,HT32的库还是比较丰富的,至少比stm32库函数多。当然,我这里没有写出IIC时序图,感兴趣的朋友可以去网上搜索自己理解。接下来是驱动SHT30传感器,主要是去淘宝官方拿的代码,然后自己封装一下就行啦。
SHT30驱动
SHT30.c
#include "IIC.h"
#include "sht30.h"
#include "delay.h"
#include "usart.h"
#include "ht32f5xxxx_01.h" // Device header
//*********************************************************************************
//SHT30驱动
//淘宝店铺:https://shop296378919.taobao.com/
//修改日期:2018/6/25
//版本:V1.0
//版权所有,盗版必究。
//All rights reserved
//*********************************************************************************
extern int temp1;
extern u16 humi;
#define POLYNOMIAL_CXDZ 0x31 // X^8 + X^5 + X^4 + 1
//SHT3X CRC校验
unsigned char SHT3X_CRC(u8 *data, u8 len)
{
unsigned char bit; // bit mask
unsigned char crc = 0xFF; // calculated checksum
unsigned char byteCtr; // byte counter
// calculates 8-Bit checksum with given polynomial @GZCXDZ
for(byteCtr = 0; byteCtr < len; byteCtr++) {
crc ^= (data[byteCtr]);
// printf("1");
for(bit = 8; bit > 0; --bit) {
if(crc & 0x80) {
//printf("2");
crc = (crc << 1) ^ POLYNOMIAL_CXDZ;
} else {
//printf("1");
crc = (crc << 1);
}
}
}
return crc;
}
//SHT30命令函数
void SHT30_CMD(u16 cmd)
{
I2C_Start();
I2C_SendByte(SHT30_ADDR+0); //发送设备地址,写寄存器
I2C_WaitAck();
I2C_SendByte((cmd>>8)&0xff); //MSB
I2C_WaitAck();
I2C_SendByte(cmd&0xff); //LSB
I2C_WaitAck();
I2C_Stop();
//printf("1");
delay_ms(50);//命令发完后需要等待20ms以上才能读写
}
//SHT30读取温湿度
//temp:温度,-400~1250,实际温度=temp/10,分辨率0.1℃,精度±0.3℃
//humi:湿度,0~1000,实际湿度=humi/10,分辨率0.1%rh,精度±3
//返回0成功,1失败
u8 SHT30_Read_Humiture(int *temp,u16 *humi)
{
u8 buff[6];
SHT30_CMD(SHT30_READ_HUMITURE);//读温湿度命令
I2C_Start();
I2C_SendByte(SHT30_ADDR+1); //发送设备地址,读寄存器
I2C_WaitAck();
buff[0]=I2C_ReadByte(1);//继续读,给应答
buff[1]=I2C_ReadByte(1);//继续读,给应答
buff[2]=I2C_ReadByte(1);//继续读,给应答
buff[3]=I2C_ReadByte(1);//继续读,给应答
buff[4]=I2C_ReadByte(1);//继续读,给应答
buff[5]=I2C_ReadByte(0);//不继续给停止应答
I2C_Stop();
//printf("buff=%d,%d,%d,%d,%d,%d\r\n",buff[0],buff[1],buff[2],buff[3],buff[4],buff[5]);
//CRC校验
if(SHT3X_CRC(&buff[0],2)==buff[2] && SHT3X_CRC(&buff[3],2)==buff[5])
{
//printf("1");
*temp=(-45+(175.0*((buff[0]<<8)+buff[1])/65535.0));
*humi=100*((buff[3]<<8)+buff[4])/65535.0;
if(*humi>100) *humi=100;
else if(*humi<0) *humi=-0;
if(*temp>125) *temp=125;
else if(*temp<-40) *temp=-40;
return 0;
//printf("temp1=%d\n",temp);
}
else return 1;
}
//SHT30初始化
void SHT30_Init()
{
I2C_Initia();
}
SHT30.h
#ifndef __SHT30_H
#define __SHT30_H
#define SHT30_ADDR (u8)(0x44<<1) //sht30 i2c地址,ADDR管脚接低电平时为0x44,接高电平为0x45
//SHT30命令
#define SHT30_READ_HUMITURE (u16)0x2c06 //读温湿度
//CRC多项式
#define POLYNOMIAL 0x31 // X^8 + X^5 + X^4 + 1
u8 SHT30_Read_Humiture(int *temp,u16 *humi);
void SHT30_Init(void);
#endif
这部分代码就是对读取到的数据进行读取、校验,然后对数据进行处理。
主函数
main.c
#include "ht32.h"
#include "ht32_board.h"
#include "led.h"
#include "delay.h"
#include "USART.h"
#include "IIC.h"
#include "SHT30.h"
//#include "Timer.h"
int temp1;
u16 humi;
volatile u32 milliseconds = 0;
volatile u32 seconds = 0;
u8 led_flag,light_in;
//往小程序发送数据
void Send_Data(void)
{
printf("{\"light\":\"%d\",\n \"led\":\"%d\",\n}",light_in,led_flag);
}
int main()
{
//LED_Init();
SHT30_Init();
I2C_Initia();
//BFTM0_Configuration();
USART_Configuration();
//Send_Data();
while(1)
{
SHT30_Read_Humiture(&temp1,&humi);//获取温湿度
printf("temp1=%2d℃\r\n",temp1);
printf("humi=%2d%\r\n",humi);
delay_ms(1000);
}
}