这是LM75A温度传感器的概述,本文主要介绍基于Stm32f103的硬件iic驱动LM75A温度传感器。
这是我所使用的硬件电路,很简单。
对于该传感器的使用,主要是读取温度值,查看数据手册我们知道需要利用iic通讯读取。
首先我们要知道什么是iic?简单来说它是一种常用的板级通讯协议,对于详细iic的学习,可以参考iic协议规范,我也不会,暂时只是学会最初步的使用,遇到问题再查看解决吧。
下面来说说,如何使用stm32f103标准库的硬件iic。
首先我们需要初始化iic
void I2C1_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIOB_InitStrcture;
GPIOB_InitStrcture.GPIO_Mode=GPIO_Mode_AF_OD;
GPIOB_InitStrcture.GPIO_Pin=GPIO_Pin_6 | GPIO_Pin_7;
GPIOB_InitStrcture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIOB_InitStrcture);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
I2C_InitTypeDef I2C1_Initstrcture;
I2C1_Initstrcture.I2C_Ack=I2C_Ack_Enable;
I2C1_Initstrcture.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C1_Initstrcture.I2C_ClockSpeed=200000;
I2C1_Initstrcture.I2C_DutyCycle=I2C_DutyCycle_2;
I2C1_Initstrcture.I2C_Mode=I2C_Mode_I2C;
I2C1_Initstrcture.I2C_OwnAddress1=HostAddress;
I2C_Init(I2C1,&I2C1_Initstrcture);
I2C_Cmd(I2C1,ENABLE);
}
Stm32f103的iic1的SDA,SCL与PB6、PB7是复用的,所以要如上初始io口和iic1。(其中有几点说明:一、GPIO.Mode需要设置成复用开漏输出。二、HostAddress为主机地址可自行设置(我使用的0xcc)。三、I2C_ClockSpeed一般不能超过400000。
参照前面数据手册图10(读包含指针字节的temp寄存器)建议查阅一下数据手册,你会明白这些寄存器的作用,这里暂时只会用到temp这个寄存器。由于我们需要读取MS、LS两个字节(实际只有11位见后文),所以我们需要编写一个读取多字节的关于iic的函数。
/I2C读取数据串(器件地址,寄存器,内部地址,数量)
void I2C1_Read_Buffer(uint8_t SlaveAddr,uint8_t ReadAddr,uint8_t* ReadBuffer,uint8_t Num)
{
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
I2C_GenerateSTART(I2C1,ENABLE);//开启信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5
I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //写入器件地址
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除 EV6
I2C_SendData(I2C1,ReadAddr); //发送读的地址
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //清除 EV8
I2C_GenerateSTART(I2C1,ENABLE); //开启信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5
I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Receiver); //将器件地址传出,主机为读
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //清除EV6
while(Num)
{
if(Num == 1)//只剩下最后一个数据时进入 if 语句
{
I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位
I2C_GenerateSTOP(I2C1,ENABLE); //最后一个数据时使能停止位
}
if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED))//读取数据
{
*ReadBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 Buffer
ReadBuffer++; //指针移位
Num--; //字节数减 1
}
}
I2C_AcknowledgeConfig(I2C1,ENABLE);
}
慢慢对照上面图10看,都是一一对应的!!!!(
唯一注意的是:你可能会有疑问为什么最后的while会这样写呢?那是因为应答的原因,仔细看图10最后一次接受数据后,需要关闭应答。对于其中清除EV,在参照下图的情况下打开标准库函数看看,每个事件代表的含义。
有了iic读取函数后就好办了,那就只需要把读取的数据进行处理了。
结合这两张图你就可以清楚明白它的数据是如何处理的了,特别注意,如果是负温度,需要将读取的数据转化成二进制补码后再进行温度值转化。简而言之,就是把后面10位二进制转化为十进制再乘以精度0.125得到的就是实际的温度值,下面是代码。
//读出LM75A的温度值(-55~125摄氏度)
//温度正负号(0正1负),温度整数,温度小数(点后2位)依次放入*Tempbuffer(十进制)
void LM75A_GetTemp(uint8_t *Tempbuffer)
{
uint8_t buf[2]; //温度值储存
uint8_t t=0,a=0;
I2C1_Read_Buffer(LM75A_ADD,0x00,buf,2); //读出温度值(器件地址,子地址,数据储存器,字节数)
t = buf[0]; //处理温度整数部分,0~125度
*Tempbuffer = 0; //温度值为正值
if(t & 0x80)//判断温度是否是负(MSB表示温度符号)
{
*Tempbuffer = 1; //温度值为负值
t = ~t;
t++; //计算补码(原码取反后加1)
}
if(t & 0x01){ a=a+1; } //从高到低按位加入温度积加值(0~125)
if(t & 0x02){ a=a+2; }
if(t & 0x04){ a=a+4; }
if(t & 0x08){ a=a+8; }
if(t & 0x10){ a=a+16; }
if(t & 0x20){ a=a+32; }
if(t & 0x40){ a=a+64; }
Tempbuffer++;
*Tempbuffer = a;
a = 0;
t = buf[1]; //处理小数部分,取0.125精度的前2位(12、25、37、50、62、75、87)
if(t & 0x20){ a=a+12; }
if(t & 0x40){ a=a+25; }
if(t & 0x80){ a=a+50; }
Tempbuffer++;
*Tempbuffer = a;
}
对于温度处理的代码,你可能会有这些疑问:一、第一个if语句是再干嘛,那是在对如果D10为1(即负温度情况下的数据去补码)二、为什么a的值是这样加的,这里特别注意,我这里是将小数部分,整数部分分开处理的,并且a所加的值,已经是对应数据*0.125以后的实际温度值!!!这里可以参照上图我自己注明的数据转化过程。
最后贴上我的主函数,你就会更加明白数据的整数、小数转化过程
#include "headfile.h"
uint8_t buffer[3]={0};
int main()
{
RCC_Init();
TM1640_Init();
I2C1_Init();
while(1)
{
LM75A_GetTemp(buffer); //读取LM75A的温度数据
TM1640_Show(1,buffer[1]/10); //显示数值
TM1640_Show(2,buffer[1]%10+10);
TM1640_Show(3,buffer[2]/10);
TM1640_Show(4,buffer[2]%10);
}
}
我的整数、小数也是分开显示的,相当于只看数,不管大小。
如有错误,敬请指出,在错误中进步。