文章目录
下载工程文件:
https://gitee.com/Joseph_Cooper/stm32-cube-mx-stm32-l151-c8-t6
MCU:STM32L151C8T6-A
IDE:MDK-Keil5
固件库:STM32Cube FW_L1 V1.10.1
0、前言
关于传感器的介绍比较长,对其有一定了解的同学可以直接从第三部分看起。
1、传感器介绍
1.1、传感器简介
SHT30是盛世瑞出品的低端低价温湿度传感器,使用I2C协议进行数据传输,具有两个可选地址,宽电源电压输入支持从2.4V到5.5V。
▲ SHT3x传感器简介
1.2、传感器板原理图
▲SHT3x 传感器板原理图
1.3、传感器引脚定义
▲ SHT3x传感器官方文档引脚定义
Pin | Name | Comments |
---|---|---|
1 | SDA | 数据引脚,输入/输出 |
2 | ADDR | 地址引脚,输入 |
3 | ALENT | 报警引脚,输出;不使用时悬空 |
4 | SCL | 时钟引脚,输入/输出 |
5 | VDD | 电源引脚,输入 |
6 | nRESET | 复位引脚,低电平有效,输入 |
7 | R | 无用引脚,接地 |
8 | VSS | 接地引脚 |
▲传感器引脚定义翻译
通过改变ADDR
引脚的电平值可以改变器件的地址,通过1.2中的原理图可以发现该板上传感器的地址为0x44
▲ SHT3x传感器官方文档地址设定
1.4、数据采集工作流程
1.4.1、单次数据采集模式
数据传输流程:
(1)STM32
发出START
,并发出I2C
的地址(7位)+读(1)/写(0)
标志位,然后等待SHT30
的ACK
(2)发出命令高8位数据
,等待SHT30的ACK
(3)发出命令低8位数据
,等待SHT30的ACK
(4)STM32
发送STOP
停止通信。
(5)STM32
发出START
,并发出I2C的地址(7位)+读(1)/写(0)标志位
,然后等待SHT30的ACK
(6)传感器将接收读头的接收,并发送两个字节的数据(温度),接着是一个字节的CRC
校验和
(7)传感器发送两个字节的数据(相对湿度),然后是一个字节的CRC
校验和。
值得注意的是如果IIC主机
对任何数据字节不感兴趣,I2C数据字节之后以NACK条件中止
读取传输在随后的数据中。
关于命令发送:
▲ 单次数据采集模式的测量命令
通过配置发送的指令我们可以控制传感器的工作模式和数据精度,单次数据采集模式的可配置选项有重复性和有无时钟延展。例如:0x2C06
: 表示高重复精度和时钟延展的使用。
什么是时钟延展?
如果使用时钟延展模式意味着要主机要做超时等待。
值得注意的是在写方向上,必须传输校验和,因为SHT3x-DIS
只接受数据,如果数据后面跟着正确的校验和。
1.4.2、周期型数据采集模式
(1)设置周期型数据采集模式
(2)发送中断命令中断采集/发送数据采集命令
(3)进入单次数据采集模式/传回数据(无数据传回NACK)
▲ 周期性数据采集模式的测量命令
周期数据采集模式的测量命令(白色块由微控制器控制,灰色块由传感器控制)。 N.B:在最高的MPS设置传感器的可能存在发热现象。
mps
:每秒发送数据的次数,举个栗子:当我需要高精度数据且需要每秒发送两次数据时我需要发送0x2236命令给从机。
1.5、其他指令
其余命令仅作简单介绍,欲知详情可查阅官方数据手册。
1.5.1、ART (accelerated response time)指令
在发出ART命令后,传感器将开始获取频率为4Hz的数据。
▲ ART指令
1.5.2、中断命令/停止周期性数据采集模式
当接收到中断命令时,传感器进入单次数据采集模式,在完成正在进行的测量之后。 这可能需要15ms,这取决于所选择的重复性。
▲ 中断命令/停止周期性数据采集模式
1.5.3、复位
软复位:
迫使系统进入定义良好的状态,而不移除电源。 当系统处于空闲状态时,可以将软复位命令发送到SHT3x-DIS。 这将触发传感器重置其系统控制器并从内存中重新加载校准数据。
▲ 软件重置指令
通过IIC呼叫复位:
▲ 通过IIC呼叫复位指令
通过n重置销复位:
将nRESET
引脚拉低(见表6)会产生类似于硬复位的复位。 该n复位引脚是内部连接到VDD通过一个拉起电阻,因此有源低。 必须将n重置引脚拉低至少350ns以产生传感器的重置。
硬复位:
一个硬复位是通过切换电源电压到VDD引脚关闭,然后再打开。 为了防止传感器在ESD二极管上供电,还需要去除引脚1(SDA)、4(SCL)和2(ADDR)的电压。
1.5.4、加热
加热器可按命令开关,见下表。 状态列在状态寄存器中。 重置后,加热器被禁用(默认条件)。
▲ 加热指令
1.5.5、状态寄存器
▲ 状态寄存器读取指令
▲ 状态寄存器的组成
▲ 清空寄存器读取指令
1.6、使用注意事项
▲ 数据手册原文
该传感器在推荐的正常温度和湿度范围内分别为5°C-60°C
和20%RH-80%RH
时显示出最佳的性能。 长期暴露在正常范围以外的条件下,特别是在高湿度下,可能会暂时抵消RH信号(例如:在保持在>80%RH的环境下60小时后测量数据会+3%RH)
。 在恢复到正常的温度和湿度范围后,传感器将自行缓慢地恢复到校准状态。 长期暴露在极端条件下可能会加速衰老。
2、程序实现注意事项
2.1、关于CRC校验
每个数据字后传输的8位CRC校验和由CRC算法生成。 其属性如表19所示。 CRC包含两个先前传输的数据字节的内容。 为了计算校验和,只使用这两个先前传输的数据字节。
▲ IIC CRC属性
2.1、关于传感器原始数据转化为温湿度
测量数据总是以16位值(无符号整数)的形式传输)。 这些值已经线性化,并补偿了温度和电源电压的影响。 将这些原始值转换为物理标度可以使用以下公式实现。
▲ 数据转化公式
S R H S_{RH}SRH和S T S_TST分别表示湿度和温度的原始传感器输出。 只有在十进制表示中使用S R H S_{RH}SRH和S T S_TST时,公式才能正确工作。
3、CubeMX上的I2C
3.1、I2C配置
在配置IIC之前要先配置串口用以查看数据输出,可参考上一篇文章的2.1部分。
在时钟配置和串口配置完成之后选择I2C1
按下图配置。
▲ I2C1 Parameter Settings
主机模式:
I2C速度模式
:标准模式和快速模式
I2C时钟频率(Hz)
:默认100000Hz
从机模式:
保持默认即可,并未用到。
值得注意的是如果你的外接设备无上拉电阻的话需要将内部上拉开启。
▲ I2C1 GPIO Settings
时钟设置和工程管理设置参见系列文章中前三章的设置。
3.2、IIC收发函数
在这里将介绍一下四种函数的用法。
(1)HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
(2)HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
(3)HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
(4)HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
(1)、(2):
函数名称:
HAL_StatusTypeDef HAL_FMPI2C_Master_Transmit();
HAL_StatusTypeDef HAL_FMPI2C_Master_Receive();
函数描述:
在主模式下传输阻塞模式下的大量数据。
在主模式下接收阻塞模式下的大量数据。
参数:
hfmpi2c
:指向包含指定FMPI2C的配置信息的FMPI2C_HandleTypeDef结构的指针。
DevAddress
:目标设备地址,值得注意的是这个地址是加入读写指令后的地址。
pData
:指向数据缓冲区的指针。
Size
:要发送的数据数量
Timeout
:超时时间
返回值:
HAL status
(3)、(4):
函数名称:
HAL_StatusTypeDef HAL_FMPI2C_Mem_Write();
HAL_StatusTypeDef HAL_FMPI2C_Mem_Read();
函数描述:
将阻塞模式下的大量数据写入特定的内存地址。
从特定内存地址读取阻塞模式下的大量数据。
参数:
hfmpi2c
: 目标设备地址
DevAddress
: 内部内存地址
MemAddress
: 内部内存地址的大小
MemAddSize
: 内部内存地址的大小
pData
: 指向数据缓冲区的指针
Size
: 要发送的数据数量
Timeout
: 超时时间
返回值:
HAL status
在这里SHT30不像AT24C02之类的存储元件操作时需要发送子地址,因此使用(1)、(2)函数便可以。
4、软件设计
实现于单次数据采集模式下采集SHT30
数据并换算为摄氏度℃和相对湿度输出至串口。
main.c
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "Stdio.h"
#include "String.h"
#include "SHT30_I2C_Driver.h"
/* USER CODE END Includes */
...
/* Private variables ---------------------------------------------------------*/
uint8_t I2CRXBuffer[6];
/* USER CODE BEGIN PV */
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t Error = 0;
float Temperature = 0,Humidity = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
memset(I2CRXBuffer,0,sizeof(I2CRXBuffer));//接收数组初始化
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(SHT30_ValGet(I2CRXBuffer) == HAL_OK)
{
Error = SHT30_Dat_To_Float(I2CRXBuffer,&Temperature,&Humidity);
if(!Error)
{
printf("Temperature:%.2fC,Humidity:%.2f%%\r\n",Temperature,Humidity);
HAL_Delay(1000);
}
else
{
printf("CRC Error\r\n");
}
}
else
{
printf("SHT30_ValGet Error\r\n");
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
SHT30_I2C_Driver.c
#include "SHT30_I2C_Driver.h"
/**
* @brief 向SHT30发送一条指令(16bit)
* @param cmd —— SHT30指令(在SHT30_MODE中枚举定义)
* @retval 成功返回HAL_OK
*/
static uint8_t SHT30_Send_Cmd(SHT30_CMD cmd)
{
uint8_t cmd_buffer[2];
cmd_buffer[0] = cmd >> 8;
cmd_buffer[1] = cmd;
return HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR_WRITE, (uint8_t*) cmd_buffer, 2, 0xFFFF);
}
/**
* @brief 单次测量数据获取
* @param dat —— 存储读取数据的地址(6个字节数组)
* @retval 成功 —— 返回HAL_OK
*/
uint8_t SHT30_ValGet(uint8_t* dat)
{
uint8_t Error;
Error = SHT30_Send_Cmd(HIGH_ENABLED_CMD);
HAL_Delay(50);
if(Error != HAL_OK)
{
return Error;
}
return HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR_READ, dat, 6, 0xFFFF);
}
/**
* @brief 复位SHT30
* @param none
* @retval none
*/
void SHT30_reset(void)
{
SHT30_Send_Cmd(SOFT_RESET_CMD);
HAL_Delay(20);
}
/**
* @brief 初始化SHT30
* @param none
* @retval 成功返回HAL_OK
* @note 周期测量模式
*/
uint8_t SHT30_Init(void)
{
return SHT30_Send_Cmd(MEDIUM_2_CMD);
}
/**
* @brief 从SHT30读取一次数据
* @param dat —— 存储读取数据的地址(6个字节数组)
* @retval 成功 —— 返回HAL_OK
* @note 周期测量模式
*/
uint8_t SHT30_Read_Dat(uint8_t* dat)
{
SHT30_Send_Cmd(READOUT_FOR_PERIODIC_MODE);
return HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR_READ, dat, 6, 0xFFFF);
}
#define CRC8_POLYNOMIAL 0x31
uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
uint8_t remainder; //余数
uint8_t i = 0, j = 0; //循环变量
/* 初始化 */
remainder = initial_value;
for(j = 0; j < 2;j++)
{
remainder ^= message[j];
/* 从最高位开始依次计算 */
for (i = 0; i < 8; i++)
{
if (remainder & 0x80)
{
remainder = (remainder << 1)^CRC8_POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
}
/* 返回计算的CRC码 */
return remainder;
}
/**
* @brief 将SHT30接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
* @param dat —— 存储接收数据的地址(6个字节数组)
* @retval 校验成功 —— 返回0
* 校验失败 —— 返回1,并设置温度值和湿度值为0
*/
uint8_t SHT30_Dat_To_Float(uint8_t* const dat, float* temperature, float* humidity)
{
uint16_t recv_temperature = 0;
uint16_t recv_humidity = 0;
/* 校验温度数据和湿度数据是否接收正确 */
if(CheckCrc8(dat, 0xFF) != dat[2] || CheckCrc8(&dat[3], 0xFF) != dat[5])
return 1;
/* 转换温度数据 */
recv_temperature = ((uint16_t)dat[0]<<8)|dat[1];
*temperature = -45 + 175*((float)recv_temperature/65535);
/* 转换湿度数据 */
recv_humidity = ((uint16_t)dat[3]<<8)|dat[4];
*humidity = 100 * ((float)recv_humidity / 65535);
return 0;
}
SHT30_I2C_Driver.h
#ifndef __SHT30_I2C_DRIVER_H
#define __SHT30_I2C_DRIVER_H
#include "I2C.h"
/* ADDR Pin Conect to VSS */
#define SHT30_ADDR_WRITE 0x44<<1 //10001000
#define SHT30_ADDR_READ (0x44<<1)+1 //10001011
typedef enum
{
/* 软件复位命令 */
SOFT_RESET_CMD = 0x30A2,
/*
单次测量模式
命名格式:Repeatability_CS_CMD
CS: Clock stretching
*/
HIGH_ENABLED_CMD = 0x2C06,
MEDIUM_ENABLED_CMD = 0x2C0D,
LOW_ENABLED_CMD = 0x2C10,
HIGH_DISABLED_CMD = 0x2400,
MEDIUM_DISABLED_CMD = 0x240B,
LOW_DISABLED_CMD = 0x2416,
/*
周期测量模式
命名格式:Repeatability_MPS_CMD
MPS:measurement per second
*/
HIGH_0_5_CMD = 0x2032,
MEDIUM_0_5_CMD = 0x2024,
LOW_0_5_CMD = 0x202F,
HIGH_1_CMD = 0x2130,
MEDIUM_1_CMD = 0x2126,
LOW_1_CMD = 0x212D,
HIGH_2_CMD = 0x2236,
MEDIUM_2_CMD = 0x2220,
LOW_2_CMD = 0x222B,
HIGH_4_CMD = 0x2334,
MEDIUM_4_CMD = 0x2322,
LOW_4_CMD = 0x2329,
HIGH_10_CMD = 0x2737,
MEDIUM_10_CMD = 0x2721,
LOW_10_CMD = 0x272A,
/* 周期测量模式读取数据命令 */
READOUT_FOR_PERIODIC_MODE = 0xE000,
} SHT30_CMD;
uint8_t SHT30_ValGet(uint8_t* dat);
void SHT30_reset(void);
uint8_t SHT30_Dat_To_Float(uint8_t* const dat, float* temperature, float* humidity);
//周期测量模式
uint8_t SHT30_Init(void);
uint8_t SHT30_Read_Dat(uint8_t* dat);
#endif