一直对stm32的硬件iic敬而远之,好像说是有问题的,就一直用的模拟iic实现,今天用sht30试了一下,发现基于cubemx来配置iic还挺方便的。现做如下记录
首先是硬件平台:stm32f103vet6指南者
先总结一下步骤
cubemx配置->SHT30_Init(选择模式)->reset->利用Init的返回值判断是否成功->读取->数据拼接
涉及到的自己写的函数有
Init,reset,read,Dat_To_Float
先说配置cubemx:
基础的时钟配置略
单纯打开iic
下面的参数顺便讲解一下
1.speedmode可以选择标准和快速
时钟速度可调,快速的话可以到40000,还要调占空比
2.clock stretching
clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,很多没有,像sht30是有的,我看有个博主发的单词读取的代码里面就用到了这个,但我这里没有用
3.地址长度
7位10位可选,这里因为后面要加读取或者写入后面相当于要加一位,就是8位或者11位
后面的不经常用(可能单纯我不常用)不说了
keil代码部分:
我是基于https://blog.csdn.net/BearPi/article/details/104311539这个代码写的,我来说说我的理解
/* USER CODE BEGIN 2 */
SHT30_Init();
SHT30_reset();
if(SHT30_Init() == HAL_OK)
printf("sht30 init ok.\n");
else
printf("sht30 init fail.\n");
/* USER CODE END 2 */
我们来剖析一下
首先是SHT30_Init
/*****/
这里要说一下题外话,基于iic发送的函数比如在dma模式中有两种
一个是用于存储设备的比如eeprom,还有一个就是一般的传感器呀这种
传感器用transmit,存储器的话是mem_write
/****/
好,继续,这里传的cmd:MEDIUM_2_CMD我们打开来看看
初始化相当于选择模式,
对应的是中等重复性(和精度有关)的每秒两次测量
好,我们再来说说这个的函数构造,还是比较常见
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);
}
每次只能发8位,总共是16位的指令(16进制有四位,那就是16位的指令了),所以要分为两次发送,cmd>>8这个指令就是在提取高位cmd[0],然后iic是高位先行,那个transmit的函数里面要放入cmd的地址,所以这个cmd[0]放高位
另外这个函数除了初始化之外还有返回值
/---------reset-------------------------/
同理只是加了一个延迟
/---------while循环------------/
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(1000);
if(SHT30_Read_Dat(recv_dat) == HAL_OK)
{
if(SHT30_Dat_To_Float(recv_dat, &temperature, &humidity)==0)
{
printf("temperature = %.2f*c, humidity = %.2f%%\n", temperature, humidity);
}
else
{
printf("crc check fail.\n");
}
}
else
{
printf("read data from sht30 fail.\n");
}
}
来细看SHT_Read_Dat
/**
* @brief 从SHT30读取一次数据
* @param dat —— 存储读取数据的地址(6个字节数组)
* @retval 成功 —— 返回HAL_OK
*/
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);
}
其中
对应手册的周期模式读取
recv_dat这个是存储的数组地址
其中
HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR_READ, dat, 6, 0xFFFF);
SHT30_ADDR_READ是七位地址加一个1,dat是存的位置,6表示存多少个
6个的原因是
温度两个,湿度两个,还有两个CRC
0xFFFF表示一个Timeout
/--------------------数据拼接
if(SHT30_Dat_To_Float(recv_dat, &temperature, &humidity)==0)
{
printf("temperature = %.2f*c, humidity = %.2f%%\n", temperature, humidity);
}
-------------------/
接收完过后就是数据的拼接
/**
* @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;
}
先是CRC校验,然后是
分析完成,成功输出!