STM32F103 硬件I2C读取温度传感器
上篇写了STM32F103使用硬件IIC读取经典外设EEPROM,只是设计到一个写数据与读数据的过程,这篇主要介绍来读写一个温度传感器EMC1314型号的案例。
读写传感器的数据有一个重要的步骤就是学会看懂它的手册(datasheet),从手册中我们需要获取几类关键信息,一是该传感器的物理地址(特别重要),因为同一个传感器可能因为容量或者型号细小的差别会导致物理地址也有很大的差别。二是该传感器的初始化配置的寄存器。三是储存数据的寄存器,这里存储着你要读取的传感器数据,有些是可以直接读取,而有些是需要换算与计算,这些都需要仔细读取寄存器部分才能弄明白。
1.温度传感器读取前的初始化
针对EMC温度传感器的初始化,主要是出了I2C2口引脚的一些初始化配置外,还有该传感器本身的一些寄存器配置,这些配置是与I2C2口的配置是在初始化的时候一起进行的。EMC寄存器初始化的配置主要包括是每次读取数据前的EMC重置和EMC开启读取这两个寄存器的操作。
用封装的EMC写一字节的接口对该两个寄存器的地址进行操作,例如
u16 EMC_Rst()
{
printf("EMC_Rst\r\n");
EMC_WriteByte(0x22, 0xF0); //EMC重置
delay_ms(50);
return RET_SUCC;
}
u16 EMC_Start()
{
printf("EMC_Start\r\n");
EMC_WriteByte(0x03,0x80); //EMC开启读取
printf("EMC Start end");
return RET_SUCC;
}
u16 EMC_Initializes(void)
{
I2C2_GPIO_Configuration();
I2C2_Configuration();
delay_ms(50);
return RET_SUCC;
}```
u16 EMC_Init()
{
EMC_Initializes();
EMC_Rst();
printf("EMC Init end");
return RET_SUCC;
}
```c
//EMC写一字节
u16 EMC_WriteByte(uint16_t Addr, uint16_t Data)
{
I2C_AcknowledgeConfig(I2C2,ENABLE);
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY));
/* 1.开始 */
I2C_GenerateSTART(I2C2, ENABLE);
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* 2.设备地址/写 */
I2C_Send7bitAddress(I2C2, EMC_DEV_ADDR, I2C_Direction_Transmitter); //数据地址
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/* 3.数据地址 */
I2C_SendData(I2C2, Addr); //寄存器数据地址(8位)
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* 4.写一字节数据 */
I2C_SendData(I2C2, Data); //发送数据
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* 5.停止 */
I2C_GenerateSTOP(I2C2, ENABLE);
return RET_SUCC;
}
//EMC读一字节
u16 EMC_ReadByte(uint16_t Addr, uint16_t *Data)
{
I2C_AcknowledgeConfig(I2C2,ENABLE);
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY));
/* 1.开始 */
I2C_GenerateSTART(I2C2, ENABLE);
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* 2.设备地址/写 */
I2C_Send7bitAddress(I2C2, EMC_DEV_ADDR, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/* 3.数据地址 */
I2C_SendData(I2C2, Addr); //数据地址(8位)
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* 4.重新开始 */
I2C_GenerateSTART(I2C2, ENABLE);
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* 5.设备地址/读 */
I2C_Send7bitAddress(I2C2, EMC_READ_ADDR, I2C_Direction_Receiver);
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
/* 6.读一字节数据 */
I2C_AcknowledgeConfig(I2C2, DISABLE); //产生非应答
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_RXNE) == RESET);
*Data = I2C_ReceiveData(I2C2); //读取数据
/* 7.停止 */
I2C_GenerateSTOP(I2C2, ENABLE);
return RET_SUCC;
}
2.读取EMC寄存器的数据
这部分在读取温度数据之前,为了检测硬件I2C读取接口的准确性,我们可以先在EMC寄存器中找到存有固定值的寄存器读一读,将读出的数据与真实数据比较一下是否一致。比如存有该设备ID的寄存器,先读取该寄存器来检验一下接口的准确性。
读取的过程也很简单,传入对应的寄存器地址直接读取该值,比如
u16 EMC_ID_Get(u16 *ID)
{
EMC_Init();
EMC_Start();
delay_ms(50);
EMC_ReadByte(0xFE, ID);
printf("EMC_ID = %d\r\n", *ID);
return RET_SUCC;
}
读取之后发现读取出来的数值为85与实际存储的值相同,说明读取的接口没有问题。
接下来就是直接去读取存有温度数据的寄存器了,EMC温度传感器跟其他的温度传感器又不一样。一是它读取出来的数据是由两部分组成,分别要读取整数位和小数位,其中整数部分是一个寄存器的八位数,小数部分则是另一个寄存器的三位数,且存在一字节的高三位,这里我们读取的该小数位寄存器的时候需要左移五位并上0x7才能正确取到小数位的值,然后将这两部分拼成一个11位的数才是正确的温度数据;二是该11位的数还不是最后的温度值,需要再进行换算,乘以一个换算系数,寄存器手册里面读取的温度表格清楚划分了11位数中每一位对应代表0.125,所以说明换算系数为0.125,因为还需要将二进制的11位数化为十进制后乘以0.125;三是该温度传感器中没有引入正负位,有的温度传感器特地留出高位用来表示数据的正负情况,这边该温度传感器是没有表现正负的需求。
//标准读取温度的接口
u16 EMC_Temp_Get(float *temperature)
{
u16 TL,TH;
u32 tem,tem1;
EMC_Init();
EMC_Start();
delay_ms(50);
EMC_ReadByte(0x01, &TH); //外部温度整数部分
EMC_ReadByte(0x10, &TL); //外部温度小数部分
tem = (TL>>5)&0x7;
printf("tem = %d\n", tem);
tem1=(TH<<3)|tem;//获得低三位 共十一位
printf("tem1 = %d\n", tem1);
*temperature = tem1*0.125; // 十进制乘以0.125得到最终度数。
if(*temperature>=LIMIT_MAX)
{
*temperature = LIMIT_MAX;
}
printf("temperature = %f\n", *temperature);
return RET_SUCC;
}
这里还加上了一个判断就是温度传感器读取的阈值最大为127.875,如果超过那显示读取的数据只能是该值,当然一般用温度传感器来读取的数值不会超过,温度读取的量程完全够用。最后读取出来的数据打印出来,浮点数是三十多度,长期读下来也是正常的稳定变高,没有出现突变的形况。说明读取的数据准确可靠。
总结
读取这种需要数据换算的传感器,有两点比较重要,一是要看懂它的手册,手册里面包含比较终于的物理地址和相关操作的寄存器,二是该温度的换算机制一定得弄清楚,原始数据是什么,经过怎么样的处理然后得到最后的真实数据,这两点至关重要。欢迎大家留言讨论,有任何问题可以积极探讨。