记录一下IIC比较典型应用

传感器相关介绍

//引脚定义   硬件IC--》复用开漏   普通IO---》通用推挽#define B_LUX_V20_SCL0_O    {\GPIO_InitTypeDef  GPIO_ST; \GPIO_ST.GPIO_Pin = GPIO_Pin_1;\GPIO_ST.GPIO_Mode = GPIO_Mode_Out_PP; \GPIO_ST.GPIO_Speed = GPIO_Speed_50MHz;\GPIO_Init(GPIOB, &GPIO_ST); }//GPIOB10 开漏输出#define B_LUX_V20_SCL0_H    GPIO_SetBits(GPIOB, GPIO_Pin_1)#define B_LUX_V20_SCL0_L    GPIO_ResetBits(GPIOB, GPIO_Pin_1)#define B_LUX_V20_SCL0_I    {\GPIO_InitTypeDef  GPIO_ST; \GPIO_ST.GPIO_Pin = GPIO_Pin_1;\GPIO_ST.GPIO_Mode = GPIO_Mode_IN_FLOATING; \GPIO_ST.GPIO_Speed = GPIO_Speed_50MHz;\GPIO_Init(GPIOB, &GPIO_ST); }//GPIOB10 浮空输入#define B_LUX_V20_SCL0_DAT  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)#define B_LUX_V20_SDA0_O    {\GPIO_InitTypeDef  GPIO_ST; \GPIO_ST.GPIO_Pin = GPIO_Pin_2;\GPIO_ST.GPIO_Mode = GPIO_Mode_Out_PP; \GPIO_ST.GPIO_Speed = GPIO_Speed_50MHz;\GPIO_Init(GPIOB, &GPIO_ST); }    //GPIOB11 开漏输出#define B_LUX_V20_SDA0_H    GPIO_SetBits(GPIOB, GPIO_Pin_2)#define B_LUX_V20_SDA0_L    GPIO_ResetBits(GPIOB, GPIO_Pin_2)#define B_LUX_V20_SDA0_I    {\GPIO_InitTypeDef  GPIO_ST; \GPIO_ST.GPIO_Pin = GPIO_Pin_2;\GPIO_ST.GPIO_Mode = GPIO_Mode_IN_FLOATING; \GPIO_ST.GPIO_Speed = GPIO_Speed_50MHz;\GPIO_Init(GPIOB, &GPIO_ST); }    //GPIOB11 浮空输入#define B_LUX_V20_SDA0_DAT  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2)#define B_LUX_V20_ADDR_O    {\GPIO_InitTypeDef  GPIO_ST; \GPIO_ST.GPIO_Pin = GPIO_Pin_13;\GPIO_ST.GPIO_Mode = GPIO_Mode_Out_PP; \GPIO_ST.GPIO_Speed = GPIO_Speed_50MHz;\GPIO_Init(GPIOC, &GPIO_ST); }    //GPIOC13 推免输出#define B_LUX_V20_ADDR_H    GPIO_SetBits(GPIOC, GPIO_Pin_13)#define B_LUX_V20_ADDR_L    GPIO_ResetBits(GPIOC, GPIO_Pin_13)

`
这个是引脚定义,这里有一个点是比较奇怪的,可能是第一次遇见,我平常都是用没有硬件集成的IIC引脚,也就是用普通的IO口模拟IIC去通信,这里的代码是用PB10和PB11,这两个引脚复用功能都是有IIC的功能,我移植到普通的IO的时候就直接不行了(虽然代码用的是硬件IIC口,但是没有用32的库,也是软件模拟的)后来检查了一下是IO口的模式设置的问题,我的最后测试结果就是:硬件IC–》复用开漏 普通IO—》通用推挽输出,(模块已经有上拉电阻)。

模块时序

起始信号代码:

/*--------------------------------------------------------------------- 功能描述: 起始信号 参数说明: 无 函数返回: 无 ---------------------------------------------------------------------*/vid B_LUX_V20_Start(){  B_LUX_V20_SDA0_H;                         //拉高数据线  B_LUX_V20_SCL0_H;                         //拉高时钟线  B_LUX_V20_Delay5us();                     //延时  B_LUX_V20_SDA0_L;                         //产生下降沿  B_LUX_V20_Delay5us();                     //延时  B_LUX_V20_SCL0_L;                         //拉低时钟线}

起始信号的定义:SCL处于高电平的时候,SDA拉低,出现一个下降沿,这个时候生成一个开始信号
代码一开始就是要把两根线拉高,(一般来说,三个信号我都会将SCL拉高,才开始去做延时),延时的时间一般来说按照100K的速度来延时就可以了,也就是5US。

停止信号代码

/*--------------------------------------------------------------------- 功能描述: 停止信号 参数说明: 无 函数返回: 无 ---------------------------------------------------------------------*/vid B_LUX_V20_Stop(){  B_LUX_V20_SDA0_L;                         //拉低数据线  B_LUX_V20_SCL0_H;                         //拉高时钟线  B_LUX_V20_Delay5us();                     //延时  B_LUX_V20_SDA0_H;                         //产生上升沿  B_LUX_V20_Delay5us();                     //延时  B_LUX_V20_SCL0_L;  B_LUX_V20_Delay5us();}

停止信号的定义是SCL处于高电平期间,SDA有一个上升沿的变化(我一开始是分不清楚的,但是后面想想IIC的两根线空闲状态处于高电平状态,而停止信号就是停止通信,所以这下就记清楚了)。

应答信号

/*--------------------------------------------------------------------- 功能描述: 发送应答信号 参数说明: ack - 应答信号(0:ACK 1:NAK) 函数返回: 无 ---------------------------------------------------------------------*/vid B_LUX_V20_SendACK(uint8 ack){  if (ack&0x01)B_LUX_V20_SDA0_H;//写应答信号  elseB_LUX_V20_SDA0_L;    B_LUX_V20_SCL0_H;                         //拉高时钟线  B_LUX_V20_Delay5us();                     //延时  B_LUX_V20_SCL0_L;                         //拉低时钟线  B_LUX_V20_SDA0_H;  B_LUX_V20_Delay5us();                     //延时}/*--------------------------------------------------------------------- 功能描述: 接收应答信号 参数说明: 无 函数返回: 返回应答信号 ---------------------------------------------------------------------*/uint8 B_LUX_V20_RecvACK(){  uint8 CY = 0x00;uint16 vConter = 1000;  B_LUX_V20_SDA0_H;    B_LUX_V20_SDA0_I;    B_LUX_V20_SCL0_H;                         //拉高时钟线  B_LUX_V20_Delay5us();                     //延时  while (vConter){vConter--;CY |= B_LUX_V20_SDA0_DAT;                 //读应答信号if(!CY)break;}    B_LUX_V20_Delay5us();                     //延时    B_LUX_V20_SCL0_L;                         //拉低时钟线    B_LUX_V20_SDA0_O;    return CY;}

应答信号需要设置SDA的IO口另一个模式:浮空输入,用来检测IIC器件是否向主机发送了一个应答信号,如果发送了主机就可以检测到,

发送一个字节

/*--------------------------------------------------------------------- 功能描述: 向IIC总线发送一个字节数据 参数说明: dat - 写字节 函数返回: 无 ---------------------------------------------------------------------*/uint8 B_LUX_V20_SendByte(uint8 dat){uint8 vRval = 0x00;  uint8 i;    for (i=0; i<8; i++)         //8位计数器  {    if (dat&0x80)B_LUX_V20_SDA0_H;    elseB_LUX_V20_SDA0_L;                   //送数据口        B_LUX_V20_Delay5us();             //延时    B_LUX_V20_SCL0_H;                //拉高时钟线    B_LUX_V20_Delay5us();             //延时    B_LUX_V20_SCL0_L;                //拉低时钟线    B_LUX_V20_Delay5us();             //延时    dat <<= 1;              //移出数据的最高位  }    vRval = B_LUX_V20_RecvACK();return vRval;}

根据IIC的定义,数据变化必须发生在SCL的低电平时候
解释一下:
if (dat&0x80)
{B_LUX_V20_SDA0_H;}
else
{B_LUX_V20_SDA0_L; }
这个是用位操作进行的,一个字节的数据和0X80相与可以得到最高的位一个二进制数是1还是0,如果是1的话SDA输出1,如果不是(也就是0)SDA输出0。
B_LUX_V20_Delay5us(); //延时
B_LUX_V20_SCL0_H; //拉高时钟线
B_LUX_V20_Delay5us(); //延时
B_LUX_V20_SCL0_L; //拉低时钟线
B_LUX_V20_Delay5us(); //延时
dat <<= 1; //移出数据的最高位

这几行代码比较关键
首先就是上一步已经确定了数据线输出的数据是1或者是0,这个时候必须时SCL保持高电平才能保证数据正确传输,延时之后就开始拉低SCL,这个时候数据右移,下一个送出的数据就是次高位,这个时候SCL是低电平,数据可以变化,依次循环八次,就可以发送完一个字节,最后进行应答部分。

主机从IIC器件读取数据

/*--------------------------------------------------------------------- 功能描述: 从IIC总线接收一个字节数据 参数说明: 无 函数返回: 接收字节 ---------------------------------------------------------------------*/uint8 B_LUX_V20_RecvByte(){  uint8 i;  uint8 dat = 0;  B_LUX_V20_SDA0_I;    B_LUX_V20_SDA0_H;                         //使能内部上拉,准备读取数据,  for (i=0; i<8; i++)                 //8位计数器  {    B_LUX_V20_SCL0_H;                       //拉高时钟线    B_LUX_V20_Delay5us();             //延时    dat |= B_LUX_V20_SDA0_DAT;              //读数据                   B_LUX_V20_SCL0_L;                       //拉低时钟线    B_LUX_V20_Delay5us();             //延时        if (i<7) dat <<= 1;  }  B_LUX_V20_SDA0_O;    return dat;}

这个接收和上面发送差不多,这里不在累赘。


上面的是这个模块的通信协议
数据格式:地址+读写位+应答+数据位+应答
地址有两个的7位数据,看模块的ADR接线,如果是往模块写数据(发指令、配置寄存器)就让W置位,不然就是让R置位。然后主机接收一个应答信号,接下来就是一个字节的数据,最后是一个应答
地址一般是7位,加上读写位就是八位,应答信号是独立的,最后的数据也是八位的数据。

/*--------------------------------------------------------------------- 功能描述: 写BH1750 参数说明: REG_Address - 寄存器地址 函数返回: 无 ---------------------------------------------------------------------*/uint8 B_LUX_V20_Single_Write(uint8 REG_Address){uint8 vRval = 0;  B_LUX_V20_Start();                                //起始信号  vRval += B_LUX_V20_SendByte(B_LUX_V20_SlaveAddress);           //发送设备地址+写信号  vRval += B_LUX_V20_SendByte(REG_Address);                  //内部寄存器地址,  //  BH1750_SendByte(REG_data);                //内部寄存器数据,  B_LUX_V20_Stop();                                 //发送停止信号  return vRval;}/*--------------------------------------------------------------------- 功能描述: 连续读出BH1750内部数据 参数说明: 无 函数返回: 无 ---------------------------------------------------------------------*/uint8 B_LUX_V20_Multiple_read(vid){ uint8 vRval = 0;  uint8 i;  B_LUX_V20_Start();                                //起始信号  vRval += B_LUX_V20_SendByte(B_LUX_V20_SlaveAddress+1);         //发送设备地址+读信号    for (i=0; i<3; i++)                           //连续读取6个地址数据,存储中BUF  {    m_LUX_V20_BUF[i] = B_LUX_V20_RecvByte();                //BUF[0]存储0x32地址中的数据    if (i == 0x02)    {            B_LUX_V20_SendACK(1);                         //最后一个数据需要回NOACK    }    else    {      B_LUX_V20_SendACK(0);                         //回应ACK    }  }    B_LUX_V20_Stop();                                 //停止信号  //B_LUX_V20_Delay5ms();return vRval;}

只需要记住每一次通信都是:起始信号、收发数据、应答信号、停止信号,只要按着这个步骤写,并且地址和读写位和数据位都正确一般都可以写得出来。

和BH1750FVI相近的模块是MAX44009,两个模块基本差不多。
(欠解决问题:IO口的模式应该如何正确配置)

这里附上完整的代码工程和数据手册(提取码:37go)
BH1750FVI代码和手册资料,

©著作权归作者所有:来自51CTO博客作者Oo_。oO的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 高可用数据库主从复制延时的解决
  2. 从计算机视觉到人脸识别:一文看懂颜色模型、信号与噪声
  3. 手把手教Linux驱动5-自旋锁、信号量、互斥体概述
  4. 手把手教Linux驱动7-内核互斥锁
  5. linux驱动程序中的并发控制
  6. ipcs、ipcrm、sysresv、kernel.shmmax
  7. 多线程安全-iOS开发要注意咯!
  8. 0-5V转0-5V模拟信号转换器,信号隔离器,分配器
  9. 0-10V转0-10V隔离器,信号转换器,信号分配模块

随机推荐

  1. android Aidl 实现进程间通讯
  2. Android(安卓)性能分析案例
  3. Android 升级问题
  4. android 关于截屏
  5. android.widget.Toast——快显信息
  6. Ubuntu下adb在不到Android设备(windows的
  7. [Androidstudio]的坑之继承ListActivity
  8. Android开发中WebView与js互相调用
  9. 高仿android微信源码下载
  10. 2011.08.12(2)——— android MediaPlayer