` 起始信号代码: 起始信号的定义:SCL处于高电平的时候,SDA拉低,出现一个下降沿,这个时候生成一个开始信号 停止信号代码 停止信号的定义是SCL处于高电平期间,SDA有一个上升沿的变化(我一开始是分不清楚的,但是后面想想IIC的两根线空闲状态处于高电平状态,而停止信号就是停止通信,所以这下就记清楚了)。 应答信号 应答信号需要设置SDA的IO口另一个模式:浮空输入,用来检测IIC器件是否向主机发送了一个应答信号,如果发送了主机就可以检测到, 发送一个字节 根据IIC的定义,数据变化必须发生在SCL的低电平时候 这几行代码比较关键 主机从IIC器件读取数据 这个接收和上面发送差不多,这里不在累赘。 只需要记住每一次通信都是:起始信号、收发数据、应答信号、停止信号,只要按着这个步骤写,并且地址和读写位和数据位都正确一般都可以写得出来。 和BH1750FVI相近的模块是MAX44009,两个模块基本差不多。 这里附上完整的代码工程和数据手册(提取码:37go)
记录一下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拉高,才开始去做延时),延时的时间一般来说按照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(); }
/*--------------------------------------------------------------------- 功能描述: 发送应答信号 参数说明: ack - 应答信号(0:ACK 1:NAK) 函数返回: 无 ---------------------------------------------------------------------*/ vid B_LUX_V20_SendACK(uint8 ack) { if (ack&0x01) B_LUX_V20_SDA0_H; //写应答信号 else B_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; }
/*--------------------------------------------------------------------- 功能描述: 向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; else B_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; }
解释一下:
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总线接收一个字节数据 参数说明: 无 函数返回: 接收字节 ---------------------------------------------------------------------*/ 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; }
(欠解决问题:IO口的模式应该如何正确配置)
BH1750FVI代码和手册资料,
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算