在我们的开发过程中,我们会发现好多外设都是通过I2C来通讯的。(当然啦,还有一些通过spi、uart等的协议进行通讯的)在使用I2C进行通讯的时候会发现有两种i2c的通讯方式,一种是模拟、一种是硬件。
这里就不说I2C的这两种方式的区别和I2C通讯的模式了,这些可以通过查博客进行了解。
在中科蓝讯的SDK代码中也提供了模拟和硬件的两种通讯方式。(在 bsp_i2c.c 文件中)可以自己去查看,这里就不说各个函数的功能了,就只说下我们需要注意的地方。
一、模拟I2C
这种方式就是通过调节IO电平来模拟I2C 通讯时候的波形来进行数据的传输。我们只需要将i2c的初始化放在外设初始化之前,正常的情况下使用读写函数是可以正常进行读写数据的。我们在使用的过程中需要注意外设的寄存器地址是8bit还是16bit的,需要与程序中的进行对应,否则会出现读写错误或者失败。
/***************************************************
我这里的代码是对寄存器地址16bit的进行读写的函数
通过上面传入进来的 addr类型可以知道,该寄存器地址是16bit的。所以我们需要先发送高8bit再发送低8bit。因为内部bsp_i2c_tx_byte 函数是发送8bit的数据,所以16bit的需要分两次进行发送
***************************************************/
// i2c 的写函数
AT(.text.bsp.i2c)
void i2c_write_tx_byte(u8 I2C_ADDR,u16 addr, u8 *dat,u32 len)
{
bsp_i2c_start();
bsp_i2c_tx_byte(I2C_ADDR);
bsp_i2c_rx_ack();
bsp_i2c_tx_byte((u8)(addr >> 8));
bsp_i2c_rx_ack();
bsp_i2c_tx_byte((u8)(addr & 0xff));
bsp_i2c_rx_ack();
for(int i =0;i<len;i++)
{
bsp_i2c_tx_byte(dat[i]);
bsp_i2c_rx_ack();
}
bsp_i2c_stop();
}
// i2c 的读函数
AT(.text.bsp.i2c)
u8* i2c_read_rx_byte(u8 I2C_ADDR,u16 addr,u8 *dat,u32 len)
{
bsp_i2c_start();
bsp_i2c_tx_byte(I2C_ADDR);
bsp_i2c_rx_ack();
bsp_i2c_tx_byte((u8)(addr >> 8));
bsp_i2c_rx_ack();
bsp_i2c_tx_byte((u8)(addr & 0xff));
bsp_i2c_rx_ack();
bsp_i2c_start();
bsp_i2c_tx_byte(I2C_ADDR | BIT(0));
bsp_i2c_rx_ack();
for(int i=0;i<len;i++)
{
dat[i] = bsp_i2c_rx_byte();
if(i ==(len-1))
bsp_i2c_tx_ack();
else
bsp_i2c_tx_ack();
}
bsp_i2c_stop();
return dat;
}
如果你的器件地址是 8bit 那就不需要先发高8bit然后发低8bit了。就直接发送器件地址就可以了。还有一个要注意的,在读写的时候设备地址的bit0 是有区别的(0 写,1 读),所以在读取数据的时候需要在 I2C_ADDR 的 bit0 置1.
二、硬件I2C
原理上和上面模拟的差不多,只是读写函数有内部提供的。
/*
下面两个函数分别是寄存器16bit的读写函数
*/
void bsp_i2c_tx_buf(u16 dev_addr, u16 reg_addr, u8* data, u32 len)
{
int i;
u32 cfg;
if (len == 0) {
return;
}
for (i = 0; i < len; i++) {
cfg = WDATA;
if (i == 0) { //ÊÕµÚ1byte
cfg |= START_FLAG0 | DEV_ADDR0 | START_FLAG1 |REG_ADDR_1 ;
}
if (i == (len - 1)) { //ÊÕ×îºó1byte
cfg |= STOP_FLAG;
}
bsp_i2c_config(cfg, dev_addr, reg_addr, data[i]);
}
}
void bsp_i2c_tx_to_rx_buf(u16 dev_addr, u16 reg_addr, u8 *buf, u16 len)
{
u32 cfg = 0;
u16 i;
if (buf == NULL || len == 0)
{
return;
}
//step1: write reg addr
cfg |= START_FLAG0 | DEV_ADDR0; // cfg = 起始信号 + 设备地址
#if 0 //!I2C_ADDRESSING_16BIT
cfg |= REG_ADDR_0;
#else
cfg |= REG_ADDR_1; // 16位寻址方式
#endif
bsp_i2c_config(cfg, dev_addr, reg_addr, 0);
//step2:read reg addr
for (i = 0; i < (len + 1); i++)
{
cfg = RDATA; // 获取硬件读寄存器里的数据
if (i == 0) //收第1byte
{
cfg |= START_FLAG0 | DEV_ADDR0; // 如果是第一byte数据,则额外加上: 起始信号+设备地址
bsp_i2c_config(cfg, dev_addr | 0x01, reg_addr, 0);
}
else
{
if (i == (len)) //收最后1byte
{
cfg |= STOP_FLAG | NACK; // 如果是最后一byte数据 则额外加上: 停止信号+不需要对方应答信号
}
buf[i-1] = bsp_i2c_config(cfg, dev_addr, reg_addr, 0);
}
}
}
/*
下面的函数是寄存器8bit的写函数
*/
void bsp_i2c_tx_buf1(u16 dev_addr, u16 reg_addr, u8* data, u32 len)
{
int i;
u32 cfg;
if (len == 0) {
return;
}
for (i = 0; i < len; i++) {
cfg = WDATA;
if (i == 0) { //ÊÕµÚ1byte
cfg |= START_FLAG0 | DEV_ADDR0 | REG_ADDR_0 | WDATA | STOP_FLAG;
}
if (i == (len - 1)) { //ÊÕ×îºó1byte
cfg |= STOP_FLAG | NACK;
}
data[i] = bsp_i2c_config(cfg, dev_addr, reg_addr, data[i]);
//printf("==> i2c tx buf[%d] = 0x%x \n",i, data[i]);
}
}
其实硬件的读写函数我们看到的没有什么大的区别,主要区别在于系统提供的static u32 bsp_i2c_config(u32 i2c_cfg, u16 dev_addr, u16 reg_addr, u32 dat) 函数中的 i2c_cfg 参数进行区别读写、寄存器地址8bit还是16bit
这里来说几个关键的值:
WDATA:表示该函数是写数据到寄存器中
RDATA:表示该函数是读寄存器里面的数据
REG_ADDR_0:寄存器地址是8bit
REG_ADDR_1:寄存器地址是16bit
/***************************************** END *****************************************/