AM437x——I2C裸机

CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2017/08/28/AM437x——I2C裸机/#more

记录AM437x的I2C裸机调试,包含GPIO模拟和寄存器控制。


0.I2C协议关键点

0.1 I2C协议

想象有两个设备,他们之间要传输数据。发数据的叫主设备,接收数据的叫从设备。
两个设备之间,只有两根线。一根线得作为时钟信号线,这样两个设备才能统一一个信号标准,另外一个自然只有作为信号线传输数据。

主设备要发送数据前,得发出一个特殊的信号,告诉从设备,这个信号就是开始信号。
同理,发送完后,有个特殊的结束信号。以及从机在接收到数据后,给个响应信号表示自己收到了。

开始信号s:SCL为高,SDA由高变低,开始传输数据
结束信号p:SCL为高,SDA由低变高,结束传输数据
响应信号a:接收器在收到8位数据后,在第9个时钟周期,拉低SDA电平。
SDA的信号,只能在SCL为低的时候发生变化。


如图,

  • SDA和SCL开始都为高。然后SDA拉低,表示开始信号。
  • 在接下来的8个时间周期里,主机控制SDA的高低,发出一个 包含从机地址是读操作还是写操作 的数据。第9个时钟周期,主设备拉高后释放掉SDA,再去读取它。从设备应该拉低SDA,给出响应信号。
  • 再接下来的8个时间周期里,主机控制SDA的高低,传达数据。从机就读取电平高低,得到数据。第9个时钟周期,从设备给主设备一个响应信号。
  • 最后,主机给个停止信号,表示传输结束。

0.2 EEPROM

以CAT24C256这个EEPROM作为从设备为例。

  • 先看看CAT24C256的引脚有哪些:

    SCL和SDA,时钟和数据。
    A0,A1和A2,用来表示从设备的地址,在硬件电路上,设置这3个引脚的值。
    WP,写保护引脚。高电平的时候写保护,因此在写EEPROM的时候得将该引脚置低。

  • 再看看,前面说的 “一个 包含从机地址是读操作还是写操作 的数据”:

    可以看到前4位固定为"1010",然后是3位地址,最后位表示是读操作还是写操作。

  • 写操作的时序

    先来个开始信号,然后是从机地址(写),然后是要写地址的高8位,再是低8位,再是要写的8位数据,最后是停止信号。
    每次传输的后面都得有个响应信号。

  • 读操作的时序

    先来个开始信号,然后是从机地址(写),然后是要写地址的高8位,再是低8位。再来个开始信号,然后是从机地址(读),获取8位数据,最后是停止信号。
    每次传输的后面都得有个响应信号,但如果是最后一次接收数据可不要。

1.GPIO模拟版本

1.1硬件结构

TI EVM开发板的EEPROM部分电路图:

米尔科技开发板的EEPROM部分电路图:

两个板子都是用的I2C0,器件地址都是000,唯一不同的是米尔的板子用了EMU0引脚控制EEPROM的写保护。
后面以米尔板子为例。这三个引脚分别是:

I2C0_SDA->gpio3_5;
I2C0_SCL->gpio3_6;
WP->gpio3_7;

1.2相关函数

  • GPIO相关

1.启用GPIO3时钟,引脚控制默认为GPIO,再设置为输出,拉高引脚,做好准备;
{% codeblock lang:c%}
void i2c_init(void)
{

PRCM_CM_PER_GPIO3_CLKCTRL  |= (0x02<<0);

//gpio3_5->I2C0_SDA;gpio3_6->I2C0_SCL;gpio3_7->WP
//CTRL_CONF_I2C0_SDA ;//GPIO模式I2C,默认配置即可
//CTRL_CONF_I2C0_SCL ;//GPIO模式I2C,默认配置即可

GPIO3->OE      &= ~(0x01<<7 | 0x01<<6 | 0x01<<5);//输出
GPIO3->DATAOUT |=  (0x01<<7 | 0x01<<6 | 0x01<<5);//拉高

}
{% endcodeblock %}

2.封装好SDA和SCL的输出高低电平函数,以及获取SDA引脚数据函数,最后还有使能写保护引脚函数;
{% codeblock lang:c%}
static void SDA(char x)
{
GPIO3->OE &= ~(0x01<<5);

if(x) {
    GPIO3->DATAOUT |=  (0x01<<5);
} 
else {
    GPIO3->DATAOUT &= ~(0x01<<5);
}     

}

static void SCL(char x)
{
GPIO3->OE &= ~(0x01<<6);

if(x) {
    GPIO3->DATAOUT |=  (0x01<<6);
} 
else {
    GPIO3->DATAOUT &= ~(0x01<<6);
}     

}

static char GET_SDA(void)
{
GPIO3->OE |= (0x01<<5);

return (GPIO3->DATAIN & (0x01<<5)?1:0);

}

void eeprom_write_protect(char x)
{
if(x)
GPIO3->DATAOUT |= (0x01<<7);
else
GPIO3->DATAOUT &= ~(0x01<<7);
}
{% endcodeblock %}

  • I2C协议相关
    1.三个信号函数:开始信号、停止信号、响应信号(判断是否收到从设备的响应信号)。
    理论上可以通过调节延时的长短,改变传输速度。
    {% codeblock lang:c%}
    static void i2c_delay(volatile int time)
    {
    time = time*1;
    for (; time > 0; time–);
    }

static void i2c_start(void)
{
SCL(1);
i2c_delay(1);

SDA(1);
i2c_delay(1);
SDA(0);
i2c_delay(1);

SCL(0); 
i2c_delay(1);

}

static void i2c_stop(void)
{
SDA(0);
i2c_delay(1);

SCL(1);
i2c_delay(1);

SDA(1);
i2c_delay(1);

}

static int i2c_ack(void)
{
int pin_val;

SCL(0);
i2c_delay(1);

SDA(1);
i2c_delay(1);

//实测中,am437x引脚由输出设置为输入的时候,会有一个高电平
//因此,趁现在SCL为低,先设置为输入
GET_SDA();

SCL(1);
i2c_delay(1);

pin_val = GET_SDA();
i2c_delay(1);

SCL(0);
i2c_delay(1);

return pin_val;

}
{% endcodeblock %}

2.I2C的读写函数
写就是在8个时钟周期里,SCL低的时候,改变SDA,SCL高的时候,稳定SDA,从而发出8位数据。
写就是在8个时钟周期里,SCL低的时候,不读引脚,SCL高的时候,读引脚,从而得到8位数据。
{% codeblock lang:c%}
void eeprom_write(unsigned char addr, unsigned char data)
{
eeprom_write_protect(0);

i2c_start();

i2c_write(0xA0);
while(i2c_ack());

i2c_write(addr>>8);
while(i2c_ack());
i2c_write(addr);
while(i2c_ack());

i2c_write(data);
while(i2c_ack());

i2c_stop();

eeprom_write_protect(1);
}

unsigned char eeprom_read(unsigned char addr)
{
unsigned char data = 0;

eeprom_write_protect(0);

i2c_start();

i2c_write(0xA0);
while(i2c_ack());

i2c_write(addr>>8);
while(i2c_ack());
i2c_write(addr);
while(i2c_ack());

i2c_stop();

i2c_delay(10);

i2c_start();

i2c_write(0xA1);
while(i2c_ack());

data = i2c_read();

i2c_stop();

eeprom_write_protect(1);

return data;

}
{% endcodeblock %}

  • EEPROM相关
    写操作:先关闭写保护,发出开始信号,发出从机地址(写),发出高地址、低地址,发出数据,发出停止信号和打开写保护。
    读操作:先关闭写保护,发出开始信号,发出从机地址(写),发出高地址、低地址。再发出开始信号,发出从机地址(写读),读取数据,发出停止信号和打开写保护。
    {% codeblock lang:c%}
    void eeprom_write(unsigned char addr, unsigned char data)
    {
    eeprom_write_protect(0);

    i2c_start();

    i2c_write(0xA0);
    while(i2c_ack());

    i2c_write(addr>>8);
    while(i2c_ack());
    i2c_write(addr);
    while(i2c_ack());

    i2c_write(data);
    while(i2c_ack());

    i2c_stop();

    eeprom_write_protect(1);
    }

unsigned char eeprom_read(unsigned char addr)
{
unsigned char data = 0;

eeprom_write_protect(0);

i2c_start();

i2c_write(0xA0);
while(i2c_ack());

i2c_write(addr>>8);
while(i2c_ack());
i2c_write(addr);
while(i2c_ack());

i2c_stop();

i2c_delay(10);

i2c_start();

i2c_write(0xA1);
while(i2c_ack());

data = i2c_read();

i2c_stop();

eeprom_write_protect(1);

return data;

}
{% endcodeblock %}

1.3测试效果

  • 串口打印:
  • 向0x01地址写数据2:
  • 读0x01地址数据为2:

2.I2C寄存器版本

2.1硬件结构

同上。

2.2相关函数

  • 初始化相关
    1.初始化相关的时钟和配置复用功能;
    {% codeblock lang:c%}
    static void i2c_gpio_init(void)
    {
    PRCM_CM_WKUP_I2C0_CLKCTRL |= (0x02<<0);
    PRCM_CM_WKUP_CLKSTCTRL |= (0x01<<14);
    PRCM_CM_PER_L4LS_CLKSTCTRL|= (0x01<<27);

    PRCM_CM_PER_GPIO3_CLKCTRL |= (0x02<<0);

    //gpio3_5->I2C0_SDA;gpio3_6->I2C0_SCL;gpio3_7->WP

    CTRL_CONF_I2C0_SDA &= ~(0x07<<0 | 0x01<<16 | 0x01<<19);
    CTRL_CONF_I2C0_SCL &= ~(0x07<<0 | 0x01<<16 | 0x01<<19);
    //CTRL_CONF_EMU0
    GPIO3->OE &= ~(0x01<<7);
    GPIO3->DATAOUT |= (0x01<<7);
    }
    {% endcodeblock %}
    2.初始化I2C0,注册中断;
    注意这里的从机地址不含最后的读写操作位。
    {% codeblock lang:c%}
    void i2c_init(void)
    {
    i2c_gpio_init();

    register_irq(IRQ_I2C0, i2c0_irq);

    interrupt_init(IRQ_I2C0);

    I2C0->CON &= ~(0x01<<15);//reset

    I2C0->SYSC &= ~(0x01<<0);//Auto Idle disabled.

    I2C0->PSC = 3;//the module divided by (PSC + 1) -> 48M/(3+1)=12M

    I2C0->SCLL = 63;//tLOW = (SCLL + 7) * ICLK time period
    I2C0->SCLH = 65;//tHIGH = (SCLH + 5) * ICLK time period

    I2C0->SA = 0x50;//Slave address.1010 000

    I2C0->CON |= (0x01<<15);//Module enabled
    }
    {% endcodeblock %}

  • 中断处理函数
    中断处理函数是硬件I2C的核心,根据中断状态标志位去进行相应的读或写操作。
    {% codeblock lang:c%}
    void i2c0_irq(void)
    {
    unsigned int status = 0;

    status = I2C0->IRQSTS;
    I2C0->IRQSTS = (status & (0x01<<3 | 0x01<<4));

    if(status & (0x01<<3))//receive
    {
    I2C0->IRQSTS |= (0x01<<3);

      if(r_count == num)
      {	
          I2C0->IRQEN_CLR |= (0x01<<3);
          I2C0->CON |= (0x01<<1);//stop  
      } 	 
      else
      {
          data_from_slave[r_count++] = (unsigned char)I2C0->DATA;
      }
    

    }

    if (status & (0x01<<4))//send
    {
    I2C0->DATA = data_to_slave[t_count++];

      I2C0->IRQSTS |= (0x01<<4);		 
      				
      if(t_count == num)
      {
          I2C0->IRQEN_CLR |= (0x01<<4);
      }
    

    }
    }
    {% endcodeblock %}

  • EEPROM相关
    写操作:去写保护,分解地址高低位,设置传输数据的位数(地址是两位+数据一位),清除中断,设置为主机发送模式,使能发送中断,开始传输,直到地址数据传完,开启写保护。
    读操作:去写保护,分解地址高低位,设置传输数据的位数(地址是两位),清除中断,设置为主机发送模式,使能发送中断,开始传输,直到地址数据传完。再设置传输位(接收数据一位),清除中断,设置位主机接收模式,开始传输,直至传输完成,开启写保护。
    {% codeblock lang:c%}
    void eeprom_write(unsigned char addr, unsigned char data)
    {
    eeprom_write_protect(0);

    data_to_slave[0] = addr>>8;
    data_to_slave[1] = (addr & 0xFF);
    data_to_slave[2] = data;

    t_count = 0;

    I2C0->CNT = 0x03;
    num = I2C0->CNT;

    I2C0->IRQSTS |= 0x7FFF;
    I2C0->IRQEN_CLR |= 0x7FFF;

    I2C0->CON |= (0x01<<9 | 0x01<<10 | 0x01<<15); //MST=1 TRX=1

    I2C0->IRQEN_SET |= (0x01<<4);

    I2C0->CON |= (0x01<<0);

    while(i2c_master_bus_busy() == 0);

    while(t_count != num);

    I2C0->CON |= (0x01<<1); //stop

    eeprom_write_protect(1);
    }

unsigned char eeprom_read(unsigned char addr)
{
unsigned char data = 0;

eeprom_write_protect(0);

data_to_slave[0] = addr>>8;
data_to_slave[1] = (addr & 0xFF);

t_count = 0;
r_count = 0;

I2C0->CNT = 0x02;

num = I2C0->CNT;

I2C0->IRQSTS	|=  0x7FFF;
I2C0->IRQEN_CLR |=  0x6FFF;

I2C0->CON |= (0x01<<9 | 0x01<<10 | 0x01<<15); //MST=1  TRX=1  

I2C0->IRQEN_SET |= (0x01<<4);

I2C0->CON |= (0x01<<0);

while(i2c_master_bus_busy() == 0);

while(t_count != num);
while(!(I2C0->IRQSTS_RAW & (0x01<<2)));

//----------------------------------------------------------------//

I2C0->CNT = 0x01;

num = I2C0->CNT;

I2C0->IRQSTS	|=  0x7FFF;
I2C0->IRQEN_CLR |=  0x6FFF;

I2C0->CON |=  (0x01<<10 | (0x01<<15));	
I2C0->CON &= ~(0x01<<9);

I2C0->IRQEN_SET |= (0x01<<3 | 0x01<<8);

I2C0->CON |= (0x01<<0);

while(i2c_master_bus_busy() == 0);

while(r_count != num);

data = data_from_slave[0];

eeprom_write_protect(1);

return data;

}
{% endcodeblock %}

2.3测试效果

  • 向0x01地址写数据2:
  • 读0x01地址数据为2:

3.完整代码

{% codeblock lang:c [main.c] https://github.com/hceng/am437x/blob/master/hardware/3th_i2c/main.c %}
/*************************************************************************
> File Name: main.c
> Author: hceng
> Description: AM437X裸机i2c
> Created Time: 20170815
*************************************************************************/

#include “AM437X/AM437X_SOC.h”
#include “led.h”
#include “delay.h”
#include “uart.h”
#include “printf.h”
#include “key.h”
#include “clkout.h”
#include “int.h”
#include “timer.h”
#include “i2c.h”

int main()
{
unsigned int i;

uart_init();
gic_init();
timer2_init();

i2c_init();
printf("init ok.\n\r");

//write eeprom.
for(i=0; i<5; i++)
{
    eeprom_write(i,2*i);
    delay_ms(4);//Must be delayed more than 4ms.
}

delay_ms(10);

//read eeprom.
for(i=0; i<5; i++)
{
    printf("read_data%d = %d\n\r",i, eeprom_read(i));
    delay_ms(4);
}

while(1)
{
    
}

return 0;

}
{% endcodeblock %}

{% codeblock lang:c [i2c.c] https://github.com/hceng/am437x/blob/master/hardware/3th_i2c/i2c.c %}
#include “AM437X/AM437X_SOC.h”
#include “i2c.h”
#include “uart.h”
#include “printf.h”
#include “int.h”
#include “delay.h”

#ifdef GPIO_I2C

static void i2c_delay(volatile int time)
{
time = time*1;
for (; time > 0; time–);
}

static void SDA(char x)
{
GPIO3->OE &= ~(0x01<<5);

if(x) {
    GPIO3->DATAOUT |=  (0x01<<5);
} 
else {
    GPIO3->DATAOUT &= ~(0x01<<5);
}     

}

static void SCL(char x)
{
GPIO3->OE &= ~(0x01<<6);

if(x) {
    GPIO3->DATAOUT |=  (0x01<<6);
} 
else {
    GPIO3->DATAOUT &= ~(0x01<<6);
}     

}

static char GET_SDA(void)
{
GPIO3->OE |= (0x01<<5);

return (GPIO3->DATAIN & (0x01<<5)?1:0);

}

void i2c_init(void)
{

PRCM_CM_PER_GPIO3_CLKCTRL  |= (0x02<<0);

//gpio3_5->I2C0_SDA;gpio3_6->I2C0_SCL;gpio3_7->WP
//CTRL_CONF_I2C0_SDA ;//GPIO模式I2C,默认配置即可
//CTRL_CONF_I2C0_SCL ;//GPIO模式I2C,默认配置即可

GPIO3->OE      &= ~(0x01<<7 | 0x01<<6 | 0x01<<5);//输出
GPIO3->DATAOUT |=  (0x01<<7 | 0x01<<6 | 0x01<<5);//拉高

}

static void i2c_start(void)
{
SCL(1);
i2c_delay(1);

SDA(1);
i2c_delay(1);
SDA(0);
i2c_delay(1);

SCL(0); 
i2c_delay(1);

}

static void i2c_stop(void)
{
SDA(0);
i2c_delay(1);

SCL(1);
i2c_delay(1);

SDA(1);
i2c_delay(1);

}

static int i2c_ack(void)
{
int pin_val;

SCL(0);
i2c_delay(1);

SDA(1);
i2c_delay(1);

//实测中,am437x引脚由输出设置为输入的时候,会有一个高电平
//因此,趁现在SCL为低,先设置为输入
GET_SDA();

SCL(1);
i2c_delay(1);

pin_val = GET_SDA();
i2c_delay(1);

SCL(0);
i2c_delay(1);

return pin_val;

}

static void i2c_write(unsigned char data)
{
int i = 0;

for(i=0; i<8; i++) {
    SCL(0);
    i2c_delay(1);
    
    if(data & 0x80) SDA(1);
    else SDA(0);
    data = data<<1; 
    i2c_delay(1);
    
    SCL(1);
    i2c_delay(1);
}
SCL(0);
i2c_delay(1);

}

static unsigned char i2c_read(void)
{
int i = 0;
unsigned char data = 0;

SCL(0);
i2c_delay(1);
SDA(1);

GET_SDA();//同理
i2c_delay(1);

for(i=0; i<8; i++) {
    SCL(1);
    i2c_delay(1);
    
    data = data<<1;
    if(GET_SDA()) data |= 0x01;

    SCL(0);
    i2c_delay(1);
}

SCL(1);//再给eeprom的ack提供一个时钟周期
i2c_delay(1);
SCL(0);
i2c_delay(1);

return data;

}

static void eeprom_write_protect(char x)
{
if(x)
GPIO3->DATAOUT |= (0x01<<7);
else
GPIO3->DATAOUT &= ~(0x01<<7);
}

void eeprom_write(unsigned char addr, unsigned char data)
{
eeprom_write_protect(0);

i2c_start();

i2c_write(0xA0);
while(i2c_ack());

i2c_write(addr>>8);
while(i2c_ack());
i2c_write(addr);
while(i2c_ack());

i2c_write(data);
while(i2c_ack());

i2c_stop();

eeprom_write_protect(1);
}

unsigned char eeprom_read(unsigned char addr)
{
unsigned char data = 0;

eeprom_write_protect(0);

i2c_start();

i2c_write(0xA0);
while(i2c_ack());

i2c_write(addr>>8);
while(i2c_ack());
i2c_write(addr);
while(i2c_ack());

i2c_stop();

i2c_delay(10);

i2c_start();

i2c_write(0xA1);
while(i2c_ack());

data = i2c_read();

i2c_stop();

eeprom_write_protect(1);

return data;

}

#endif

#ifdef REG_I2C

void i2c0_irq(void)
{
unsigned int status = 0;

status = I2C0->IRQSTS;
I2C0->IRQSTS = (status & (0x01<<3 | 0x01<<4));

if(status & (0x01<<3))//receive
{
    I2C0->IRQSTS |= (0x01<<3);

    if(r_count == num)
    {	
        I2C0->IRQEN_CLR |= (0x01<<3);
        I2C0->CON |= (0x01<<1);//stop  
    } 	 
    else
    {
        data_from_slave[r_count++] = (unsigned char)I2C0->DATA;
    }
}

if (status & (0x01<<4))//send
{
    I2C0->DATA = data_to_slave[t_count++];

    I2C0->IRQSTS |= (0x01<<4);		 
					
    if(t_count == num)
    {
        I2C0->IRQEN_CLR |= (0x01<<4);
    }
}

}

void eeprom_write_protect(char x)
{
if(x)
GPIO3->DATAOUT |= (0x01<<7);
else
GPIO3->DATAOUT &= ~(0x01<<7);
}

static unsigned int i2c_master_bus_busy(void)
{
if(I2C0->IRQSTS_RAW & (0x01<<12))
{
return 1;
}
else
{
return 0;
}
}

unsigned char eeprom_read(unsigned char addr)
{
unsigned char data = 0;

eeprom_write_protect(0);

data_to_slave[0] = addr>>8;
data_to_slave[1] = (addr & 0xFF);

t_count = 0;
r_count = 0;

I2C0->CNT = 0x02;

num = I2C0->CNT;

I2C0->IRQSTS	|=  0x7FFF;
I2C0->IRQEN_CLR |=  0x6FFF;

I2C0->CON |= (0x01<<9 | 0x01<<10 | 0x01<<15); //MST=1  TRX=1  

I2C0->IRQEN_SET |= (0x01<<4);

I2C0->CON |= (0x01<<0);

while(i2c_master_bus_busy() == 0);

while(t_count != num);
while(!(I2C0->IRQSTS_RAW & (0x01<<2)));

//----------------------------------------------------------------//

I2C0->CNT = 0x01;

num = I2C0->CNT;

I2C0->IRQSTS	|=  0x7FFF;
I2C0->IRQEN_CLR |=  0x6FFF;

I2C0->CON |=  (0x01<<10 | (0x01<<15));	
I2C0->CON &= ~(0x01<<9);

I2C0->IRQEN_SET |= (0x01<<3 | 0x01<<8);

I2C0->CON |= (0x01<<0);

while(i2c_master_bus_busy() == 0);

while(r_count != num);

data = data_from_slave[0];

eeprom_write_protect(1);

return data;

}

void eeprom_write(unsigned char addr, unsigned char data)
{
eeprom_write_protect(0);

data_to_slave[0] = addr>>8;
data_to_slave[1] = (addr & 0xFF);
data_to_slave[2] = data;

t_count = 0;

I2C0->CNT = 0x03;
num = I2C0->CNT;

I2C0->IRQSTS	|=  0x7FFF;
I2C0->IRQEN_CLR |=  0x7FFF;

I2C0->CON |= (0x01<<9 | 0x01<<10 | 0x01<<15); //MST=1  TRX=1  

I2C0->IRQEN_SET |= (0x01<<4);

I2C0->CON |= (0x01<<0);

while(i2c_master_bus_busy() == 0);

while(t_count != num);

I2C0->CON |= (0x01<<1); //stop

eeprom_write_protect(1);

}

static void i2c_gpio_init(void)
{
PRCM_CM_WKUP_I2C0_CLKCTRL |= (0x02<<0);
PRCM_CM_WKUP_CLKSTCTRL |= (0x01<<14);
PRCM_CM_PER_L4LS_CLKSTCTRL|= (0x01<<27);

PRCM_CM_PER_GPIO3_CLKCTRL  |= (0x02<<0);

//gpio3_5->I2C0_SDA;gpio3_6->I2C0_SCL;gpio3_7->WP

CTRL_CONF_I2C0_SDA &= ~(0x07<<0 | 0x01<<16 | 0x01<<19);
CTRL_CONF_I2C0_SCL &= ~(0x07<<0 | 0x01<<16 | 0x01<<19);
//CTRL_CONF_EMU0
GPIO3->OE      &= ~(0x01<<7);
GPIO3->DATAOUT |=  (0x01<<7);

}

void i2c_init(void)
{
i2c_gpio_init();

register_irq(IRQ_I2C0, i2c0_irq);

interrupt_init(IRQ_I2C0);

I2C0->CON  &= ~(0x01<<15);//reset

I2C0->SYSC &= ~(0x01<<0);//Auto Idle disabled.

I2C0->PSC  = 3;//the module divided by (PSC + 1) -> 48M/(3+1)=12M

I2C0->SCLL = 63;//tLOW = (SCLL + 7) * ICLK time period
I2C0->SCLH = 65;//tHIGH = (SCLH + 5) * ICLK time period

I2C0->SA = 0x50;//Slave address.1010 000

I2C0->CON  |=  (0x01<<15);//Module enabled	

}

void debug_i2c(char *str)
{
printf("-------------------------%s---------------------------\r\n",str);

printf("I2C0->REVNB_HI    = 0x%x\r\n",I2C0->REVNB_HI    );
printf("I2C0->REVNB_LO    = 0x%x\r\n",I2C0->REVNB_LO    );
printf("I2C0->REVNB_HI    = 0x%x\r\n",I2C0->REVNB_HI    );
printf("I2C0->SYSC        = 0x%x\r\n",I2C0->SYSC        );
printf("I2C0->IRQSTS_RAW  = 0x%x\r\n",I2C0->IRQSTS_RAW  );
printf("I2C0->IRQSTS      = 0x%x\r\n",I2C0->IRQSTS      );
printf("I2C0->IRQEN_SET   = 0x%x\r\n",I2C0->IRQEN_SET   );
printf("I2C0->IRQEN_CLR   = 0x%x\r\n",I2C0->IRQEN_CLR   );
printf("I2C0->WE          = 0x%x\r\n",I2C0->WE          );
printf("I2C0->DMARXEN_SET = 0x%x\r\n",I2C0->DMARXEN_SET );
printf("I2C0->DMATXEN_SET = 0x%x\r\n",I2C0->DMATXEN_SET );
printf("I2C0->DMARXEN_CLR = 0x%x\r\n",I2C0->DMARXEN_CLR );
printf("I2C0->DMATXEN_CLR = 0x%x\r\n",I2C0->DMATXEN_CLR );
printf("I2C0->DMARXWAKE_EN= 0x%x\r\n",I2C0->DMARXWAKE_EN);
printf("I2C0->DMATXWAKE_EN= 0x%x\r\n",I2C0->DMATXWAKE_EN);
printf("I2C0->SYSS        = 0x%x\r\n",I2C0->SYSS        );
printf("I2C0->BUF         = 0x%x\r\n",I2C0->BUF         );
printf("I2C0->CNT         = 0x%x\r\n",I2C0->CNT         );
printf("I2C0->DATA        = 0x%x\r\n",I2C0->DATA        );
printf("I2C0->CON         = 0x%x\r\n",I2C0->CON         );
printf("I2C0->OA          = 0x%x\r\n",I2C0->OA          );
printf("I2C0->SA          = 0x%x\r\n",I2C0->SA          );
printf("I2C0->PSC         = 0x%x\r\n",I2C0->PSC         );
printf("I2C0->SCLL        = 0x%x\r\n",I2C0->SCLL        );
printf("I2C0->SCLH        = 0x%x\r\n",I2C0->SCLH        );
printf("I2C0->SYSTEST     = 0x%x\r\n",I2C0->SYSTEST     );
printf("I2C0->BUFSTAT     = 0x%x\r\n",I2C0->BUFSTAT     );
printf("I2C0->OA1         = 0x%x\r\n",I2C0->OA1         );
printf("I2C0->OA2         = 0x%x\r\n",I2C0->OA2         );
printf("I2C0->OA3         = 0x%x\r\n",I2C0->OA3         );
printf("I2C0->ACTOA       = 0x%x\r\n",I2C0->ACTOA       );
printf("I2C0->SBLOCK      = 0x%x\r\n",I2C0->SBLOCK      ); 

printf("******************************************************\r\n");

}

#endif
{% endcodeblock %}

{% codeblock lang:c [i2c.h] https://github.com/hceng/am437x/blob/master/hardware/3th_i2c/i2c.h %}

#ifndef I2C_H
#define I2C_H

#define GPIO_I2C
//#define REG_I2C

#ifdef GPIO_I2C

extern void i2c_init(void);
extern void eeprom_write(unsigned char addr, unsigned char data);
extern unsigned char eeprom_read(unsigned char addr);

#endif

#ifdef REG_I2C

volatile unsigned int t_count;
volatile unsigned int r_count;

volatile unsigned int num;
volatile unsigned char data_to_slave[5];
volatile unsigned char data_from_slave[5];

extern void i2c_init(void);
extern unsigned char eeprom_read(unsigned char addr);
extern void eeprom_write(unsigned char addr, unsigned char data);
extern void debug_i2c(char *str);

#endif

#endif
{% endcodeblock %}

4.心得

GPIO模拟I2C优势是通用性高,移植方便。
利用SOC自带的I2C控制的优势是稳定,高效。

GPIO模拟I2C需要对时序的每个细节都比较清楚才行,非常适合去理解I2C的原理。
硬件I2C几乎不需要考虑时序,按逻辑设置和读取相关寄存器即可。
还有一个重要的点是去看从机I2C设备的时序,不同的设备,可能会不一样的。
下一步,终于要开始I2C的驱动了~~~

整个过程,最大的功臣就是逻辑分析仪,太好用了?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值