我是靠谱客的博主 知性魔镜,最近开发中收集的这篇文章主要介绍基于Stm32f103利用模拟iic驱动LM75A温度传感器,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这两天一直在搞模拟iic,模拟iic相较于硬件iic的优势在于更稳定,io口的选择更灵活。

这次编写模拟iic驱动程序还是有点坎坷,其中模拟iic的时序不是难点,直接说我遇到的问题1.io口模式的选择(一开始我是使用固定的io口模式,这中间也看到有人说io口模式不影响,经过我的实践是会影响的)2.LM75A驱动时序图的理解不到位。

下面分析遇到这两个拦路虎的原因:第一个问题是对硬件电路的一些知识不掌握,没有搞清io口模式具体的含义,第二个问题我认为还是对iic协议的不熟悉。

下面具体说说模拟iic驱动编写的过程(这里和我写的驱动TM1640的地方有异曲同工之处)

首先我们还是要看一下iic通讯协议的时序图

这张图就很清楚明白了,我们可以把上图分解成几个部分:启动信号部分、8位数据发送(数据接受与发送其本质是一样的)部分、应答(这里应答又可以分为主机发送应答信号,主机接受从机应答信号)部分、停止信号部分。

这个是LM75A中读取temp寄存器的步骤,这里解释一下器件应答和主机应答,器件应答为从机向主机发送应答信号0(正常工作)、1(非正常工作),而主机应答为主机向从机发送应答信号0(应答)、1(非应答),为什么要解释这一点呢?这是与后面io口SDA脚的初始化有关,因为器件应答需要读取SDA引脚的电平,故要将io口设置为浮空输入,而主机应答需要向从机发送信号,故需要设置成推挽输出(这里建议明白各种io口模式的作用,你可以搞完后设置成其他模式看看能不能正常工作)

下面是.h文件,有一些宏定义,在.c文件中编写方便一些

#ifndef __SOFT_IIC_H
#define __SOFT_IIC_H
#include "headfile.h"

#define SCL GPIO_Pin_6 //GPIOB
#define SDA GPIO_Pin_7
#define SCL_High GPIO_WriteBit(GPIOB,SCL,Bit_SET);
#define SDA_High GPIO_WriteBit(GPIOB,SDA,Bit_SET);
#define SCL_Low GPIO_WriteBit(GPIOB,SCL,Bit_RESET);
#define SDA_Low GPIO_WriteBit(GPIOB,SDA,Bit_RESET);

void Soft_IIC_Mode(uint8_t mode);
void Soft_IIC_Start(void);
void Soft_IIC_End(void);
void Soft_IIC_Ack(void);
void Soft_IIC_NoAck(void);
void Soft_IIC_WaitAck(void);
void Soft_IIC_BasicSend(uint8_t data);
uint8_t Soft_IIC_BasicRead(void);
uint8_t Soft_IIC_ReadByte(uint8_t SlaveAddr,uint8_t ReadAddr);
void Soft_IIC_ReadBuffer(uint8_t SlaveAddr,uint8_t ReadAddr,uint8_t* ReadBuffer,uint8_t Num);


#endif

由于我们上面说到不同部分用到的SDA脚io口模式不一样,所以我们编写一个引脚模式选择函数

void Soft_IIC_Mode(uint8_t mode)
{
    switch(mode)
    {
        case 0: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//输出模式
        
                GPIO_InitTypeDef GPIOB_InitStrcture;
                GPIOB_InitStrcture.GPIO_Mode=GPIO_Mode_Out_PP;
                GPIOB_InitStrcture.GPIO_Pin= SDA | SCL;
                GPIOB_InitStrcture.GPIO_Speed=GPIO_Speed_50MHz;
    
                GPIO_Init(GPIOB,&GPIOB_InitStrcture);break;
        
        case 1: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
        
                GPIOB_InitStrcture.GPIO_Mode=GPIO_Mode_IN_FLOATING;//输入模式
                GPIOB_InitStrcture.GPIO_Pin=SDA;
                GPIOB_InitStrcture.GPIO_Speed=GPIO_Speed_50MHz;
    
                GPIO_Init(GPIOB,&GPIOB_InitStrcture);break;
    }
}

下面我们结合iic时序图编写程序

启动程序代码

void Soft_IIC_Start(void)
{
    Soft_IIC_Mode(0);
    SDA_High;
    SCL_High;
    Delay_us(5);//确保信号正确
    SDA_Low;
    Delay_us(5);//先拉低SDA
    SCL_Low;
}

对照上面启动的时序图,为了确保信号的正确,我们先将SDA,SCL都拉高,延时5us是为了信号的稳定(5us应该可以更短,可以查询数据手册修改,提高通讯效率),然后拉低SDA,到这里其实已经发出了启动信号,但是我为了后续发送数据部分,SCL操作对称且好理解,在启动信号后将SCL拉低,这样在后续发送一字时只需改变SDA电平(数据,在SCL为低店电平时才能改变),再将SCL拉高(发送出去)

所以在后面任何部分的代码,你都会发现最后一步操作都是将SCL拉低,这样可以使每部分衔接更加清晰,不会出现SCL混乱的情况。

然后就是发送数据部分(8位)代码

void Soft_IIC_BasicSend(uint8_t data)
{
    Soft_IIC_Mode(0);
    for(uint8_t i=0;i<8;i++)
    {
        if(data & 0x80)
        {
           SDA_High;
           Delay_us(5);
        }
        else
        {
           SDA_Low;
           Delay_us(5);
        }
        data<<=1;
        SCL_High;
        Delay_us(5);
        SCL_Low;
        Delay_us(5);
    }
}

这里就把刚刚启动信号完成后将SCL拉低的优势体现出来了,便于理解这部分代码。启动信号之后SCL保持在低电平,进for循环直接可以改变SDA,注意的是:iic中数据的传输都是先传输的高位,故这里用了向左位移,一位一位的从高位将数据传输出去。

一开始说到,发送与读取本质是一样,发送是将SCL拉低后,改变SDA,再拉高SCL发送出去;而读取则是拉低SCL再拉高,当SCL为高电平时,读取SDA的电平(即为从机发送的数据)

代码如下

特别注意的是,要读取器件SDA时,都需要将SDA拉高释放总线,这样才能准确读取从机SDA信号。

并且整个iic中只要涉及读取器件SDA的都需要将SDA的io口模式设置成输入模式!!!

uint8_t Soft_IIC_BasicRead(void)
{
    uint8_t BasicRead=0;
    
    Soft_IIC_Mode(1);
    SDA_High;//释放SDA总线
    for(uint8_t i=0;i<8;i++)
    {
        SCL_High;
        Delay_us(5);
        BasicRead<<=1;
        if(GPIO_ReadInputDataBit(GPIOB,SDA))
        {
            BasicRead++;
            Delay_us(1);
        }
        SCL_Low;
        Delay_us(5);
    }

    return BasicRead;
}

接下来就是应答代码

首先是主机应答与不应答(这里是由主机产生的,故SDA仍为输出模式)

主机应答相当于在8位数据发送后,再发送第九位应答位。

void Soft_IIC_Ack(uint8_t Ack)
{
    Soft_IIC_Mode(0);
    switch(Ack)
    {
        case 0:SDA_Low;Delay_us(5);break;//0为发送应答信号
        case 1:SDA_High;Delay_us(5);break;//1为发送非应答信号
    }
    SCL_High;
    Delay_us(5);
    SCL_Low;
}

其次是器件应答(这里是由从机产生的信号,故SDA需要设置为输入模式)

void Soft_IIC_WaitAck(void)
{
    uint8_t timer=0;
    
    Soft_IIC_Mode(1);
    SDA_High;
    SCL_High;//释放总线,由从机发SDA信号
    while( GPIO_ReadInputDataBit(GPIOB,SDA) )
    {
        if(timer++>100)
        {
            Soft_IIC_End();
        }
        
    }
    SCL_Low;
}

其中如果从机应答一直发回的是1(即不正常工作,则结束本次通讯)。

最后就是结束通讯代码(与启动信号类似)

void Soft_IIC_End(void)
{
    Soft_IIC_Mode(0);
    SDA_Low;
    SCL_Low;
    Delay_us(5);//确保信号正确
    SCL_High;
    Delay_us(5);
    SDA_High;
}

以上就是全部模拟iic部分的代码了,后续驱动LM75A温度传感器只需要调用他们即可,在上一篇中讲了利用硬件iic驱动(基于标准库函数)

接下来讲一下利用模拟iic驱动以及遇到的问题

我们还是只讲一下温度寄存器的读取(不讲读取值处理成实际温度,也很简单,有需要可以参考上一篇)

只需要看图10读取temp寄存器的2字节数据,结合下面解释耐心看一下,对整个步骤有所了解

void Soft_IIC_ReadBuffer(uint8_t SlaveAddr,uint8_t ReadAddr,uint8_t* ReadBuffer,uint8_t Num)
{
    Soft_IIC_Start();
    Soft_IIC_BasicSend(SlaveAddr);
    Soft_IIC_WaitAck();
    Soft_IIC_BasicSend(ReadAddr);
    Soft_IIC_WaitAck();
    Soft_IIC_Start();
    Soft_IIC_BasicSend( (SlaveAddr+1) );
    Soft_IIC_WaitAck();
    while(Num)
    {
        if(Num == 1)//只剩下最后一个数据时进入 if 语句
        { 
            *ReadBuffer = Soft_IIC_BasicRead();//调用库函数将数据取出到 Buffer
            Num--; //字节数减 1 
            Soft_IIC_Ack(1); //最后有一个数据时关闭应答位
            Soft_IIC_End();    //最后一个数据时使能停止位
        }
        else//读取数据
        { 
             *ReadBuffer = Soft_IIC_BasicRead();//调用库函数将数据取出到 Buffer
             ReadBuffer++; //指针移位
             Num--; //字节数减 1 
             Soft_IIC_Ack(0);
        }
    }
}

上面就是将图10翻译过来的代码,你可能会有一些疑问,从图10可以看出我们的器件地址实际只有7位,但是我们需要发送8位(因为你会发现7位地址后面还有一位决定读写位(0为写,1为读)这里说明其中器件地址SlaveAddr我们默认第八位读写为0(写),所以在下面我们需要读的时候只需要+1第八位读写位就变成了1(这里就是卡了我一天的坑),因为在硬件iic中调用库函数,没有考虑这一点。

这样就完完整整把这个时序图翻译过来了,从而就可以顺利驱动LM75A,亲眼看到数码管上读取的错误值到正确的温度值,这是很欣慰的,其中还经历了很多查错才发现这个问题。

有些东西必须知道它为什么存在(存在即合理),有些人说不必要重复造车轮,我同意这个观点,但是我还是认为知道车轮是怎么造的更好。可能这次写了模拟iic的代码后,之后都是Ctrl+C了,用起来心里也有底些。。

其次很多关于iic通讯的专业术语我可能表达的并不准确,如有错误,敬请指正,在错误中进步嘛!

最后

以上就是知性魔镜为你收集整理的基于Stm32f103利用模拟iic驱动LM75A温度传感器的全部内容,希望文章能够帮你解决基于Stm32f103利用模拟iic驱动LM75A温度传感器所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部