根据方向位的状态(读/ 写),在I2C 总线上可能:
有两种类型的数据传输:
I2C 总线接口通过以下寄存器进行配置:
LPC_IOCON->PIO0_4 = 0x01;//管脚复用选择i2c
LPC_IOCON->PIO0_5 = 0x01;//管脚复用选择i2c
/*I2CMODE默认为00---->标准模式/快速I2C模式*/
电源和外设时钟配置
系统 AHB 时钟控制寄存器(SYSAHBCLKCTRL) 寄存器第5位置1,允许I2C时钟
LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 5;//i2c时钟允许位
复位设置
//在访问SPI和I2C外设之前,要写1到相应位中用以确保外设复位无效,I2C复位无效
LPC_SYSCON->PRESETCTRL |= 1 << 1;
频率设置和使能
LPC_I2C->SCLH = 480;//决定i2c时钟高电平时间
LPC_I2C->SCLL = 480;//决定i2c时钟低电平时间
LPC_I2C->CONCLR |= (1 << 2) | (1 << 3) | (1 << 5) | (1 << 6);//i2c清除寄存器(写1清除 )
LPC_I2C->CONSET |= (1 << 6);//i2c接口允许
void i2c_init(void)
{
//基本配置
LPC_SYSCON->PRESETCTRL |= 1 << 1;//在访问SPI和I2C外设之前,要写1到相应位中用以确保外设复位无效,I2C复位无效
LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 5;//i2c时钟允许位
LPC_IOCON->PIO0_4 = 0x01;//管脚复用选择i2c
LPC_IOCON->PIO0_5 = 0x01;//管脚复用选择i2c
LPC_I2C->SCLH = 480;//决定i2c时钟高电平时间
LPC_I2C->SCLL = 480;//决定i2c时钟低电平时间
LPC_I2C->CONCLR |= (1 << 2) | (1 << 3) | (1 << 5) | (1 << 6);//i2c清除寄存器(写1清除 )
LPC_I2C->CONSET |= (1 << 6);//i2c接口允许
}
在主接收模式,从一个从发送器接收数据。传输以与主发送模式同样的方式启动。当START 条件已经传送,必须加载从地址和数据方向位到I2C 数据寄存器(I2DAT)中,然后清除的SI 位。在这种情况下,数据方向位(读/ 写)应为1,表示读,下图为主接收模式数据格式。
bool i2c_start(void)
{
LPC_I2C->CONSET = (1 << 5); //设置start标志位
while (!(LPC_I2C->CONSET & (1 << 3)));//等待中断
if (LPC_I2C->STAT != 0x08 && LPC_I2C->STAT != 0x10) //判断STAT状态标志
{
/*0x08(状态码)已发送START 条件
0x10(状态码)已发重复START 条件
如果不是这两种状态,说明出了问题,这时主设备发送stop条件
*/
i2c_stop();//状态标志不为0x08或者0x10表示发送start失败
return false;
}
return true;
}
bool i2c_stop(void)
{
LPC_I2C->CONSET = (1 << 4); //设置stop标志位
LPC_I2C->CONCLR = (1 << 3); //清除中断标志
while(LPC_I2C->CONSET & (1 << 4));//stop位清零,发送stop完成
return true;
}
bool i2c_send_sla(unsigned char addr)
{
LPC_I2C->DAT = addr; //将地址写入数据寄存器,准备发送给从设备
LPC_I2C->CONCLR = (1 << 5) | (1 << 3); //清除中断和start标志
while (!(LPC_I2C->CONSET & (1 << 3))); //等待从设备返回ACK,产生中断
if (LPC_I2C->STAT != 0x18 && LPC_I2C->STAT != 0x40) //判断STAT状态标志
{
/*0x18(状态码)主机接收模式SLA(设备地址)+R(读)传输,已收到ACK(应答)
0x40(状态码)主机发送模式SLA(设备地址)+W(写)传输,已收到ACK(应答)
如果不是这两种状态,说明出了问题,这时主设备发送stop条件
*/
i2c_stop();
return false;
} else
return true;
}
bool i2c_send_dat(unsigned char dat)
{
LPC_I2C->DAT = dat;
LPC_I2C->CONCLR = (1 << 5) | (1 << 3); //清除中断标志位和start标志位
while (!(LPC_I2C->CONSET & (1 << 3))); //等待中断位置1
if (LPC_I2C->STAT != 0x28) {
/*0x28(状态码)主机发送模式 DAT中数据字节已传输,已收到ACK(应答)
如果不是这种状态,说明出了问题,这时主设备发送stop条件
*/
i2c_stop();
return false;
} else
return true;
}
bool i2c_recv_dat(unsigned char *dat, bool ack)
{
/* bool ack
数据不是最后一字节,开启应答位,发送应答位(ACK)
数据为最后一字节时,关闭应答位,发送非应答位(NOT ACK)
*/
if (!ack)
LPC_I2C->CONCLR = 1 << 2;//关应答位
else
LPC_I2C->CONSET = 1 << 2;//开启应答位
LPC_I2C->CONCLR = 1 << 3;//清除中断标志
while (!(LPC_I2C->CONSET & (1 << 3))); //等待中断标志置1
if (LPC_I2C->STAT != 0x50 && LPC_I2C->STAT != 0x58)
{
/*0x50(状态码)主机接收模式 已收到数据字节,收到应答(ACK)
0x58(状态码)主机接收模式 已收到数据字节,收到非应答(NOT ACK)
如果不是这两种状态,说明出了问题,这时主设备发送stop条件
*/
i2c_stop();
return false;
}
*dat = LPC_I2C->DAT;
return true;
}
根据上述流程,可以编写I2C的读数据i2c_read函数。
int i2c_read(unsigned char addr, unsigned char reg, unsigned char *buf, int len)
{
int i = len;
/* 1.发送Start条件 */
if (!i2c_start())
return -1;
/* 2.发送设备地址和数据方向 */
/* 设备地址左移一位,数据方向位为0,为写方向*/
if (!i2c_send_sla(addr << 1))
return -1;
/* 3. 发送寄存器地址 */
if (!i2c_send_dat(reg))
return -1;
/* 4. 发送STOP条件 */
i2c_stop();
/* 5. 发送Start条件 */
if (!i2c_start())
return -1;
/* 5. 发送设备地址和数据方向 */
/* 设备地址左移一位,数据方向位为1,为读方向*/
if (!i2c_send_sla((addr << 1) | 1))
return -1;
/* 6. 接受光照数据 */
while (i)
{
if (i == 1) {
if (!i2c_recv_dat(buf + len - i, false))
return len - i;
} else {
if (!i2c_recv_dat(buf + len - i, true))
return len - i;
}
i--;
}
/* 7. 发送STOP条件 */
i2c_stop();
return len;
}
int i2c_write(unsigned char addr, unsigned char reg, unsigned char *buf, int len)
{
int i;
/* 1. 发送Start条件 */
if (!i2c_start())
return -1;
/* 2. 发送设备地址和数据方向,写方向*/
if (!i2c_send_sla(addr << 1))
return -1;
/* 3. 发送寄存器地址 */
if (!i2c_send_dat(reg))
return -1;
/* 4. 发送FUNCTION */
for (i = 0; i < len; i++) {
if (!i2c_send_dat(buf[i]))
return i;
}
/* 5. 发送Stop条件 */
i2c_stop();
return len;
}
#define ISL29003_ADDR 0x44 //设备地址左移1位,| 数据方向位
#define COMMAND 0
#define CONTROL 1
#define THRHI 2
#define THRLO 3
#define LUXLSB 4
#define LUXMSB 5
#define TMRLSB 6
#define TMRMSB 7
void light_init(void)
{
/*
* 时钟周期数2^16, 二极管的电流为无符号16bit,
* 集成内部定时,正常运行,启用adc内核
*/
unsigned char cmd = (1 << 7);
/*
* 设置范围为 0~64000 lux (勒克斯)
* 中断在8个集成周期后触发
*/
unsigned char ctl = (3 << 2) | (2 << 0);
i2c_write(ISL29003_ADDR, COMMAND, &cmd, 1);//设定COMMAND寄存器的值
i2c_write(ISL29003_ADDR, CONTROL, &ctl, 1);//设定CONTROL寄存器的值
}
unsigned short light_get(void)
{
unsigned short lux;
/*从LUXLSB寄存器读取光照值*/
i2c_read(ISL29003_ADDR, LUXLSB, (unsigned char *)&lux, 2);
return lux;
}