AM437x——I2C裸机

TI Cortex-A9 AM437x系列-创龙TL437x-EVM开发板详解 TL437x-EVM是广州创龙基于SOM-TL437x核心板研发的一款TI ARM Cortex-A9开发板,采用核心板+底板方式,尺寸为180mm*130mm,核心板采用4*60pin B2B工业级连接器,稳定、可靠、便捷,可以帮助客户快速评估核心板性能。 SOM-TL437x核心板采用高密度沉金无铅工艺8层板设计,尺寸为58mm*35mm,采用美国德州仪器最新Cortex-A9 CPU AM437x,高性能与低功耗有机结合。采用耐高温、体积小、精度高的B2B连接器,引出了核心板的全部接口资源,帮助开. 阅读详情

优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问: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的驱动了~~~

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

AM437x内存映射详解:从L3/L4总线到外设地址的嵌入式开发指南 内存映射是嵌入式系统开发的核心基础概念,它定义了处理器虚拟地址与物理硬件(如RAM、Flash及外设寄存器)之间的对应关系。其原理是通过内存管理单元(MMU)或总线互连架构,将CPU发出的访问请求路由到正确的物理设备。理解内存映射对于实现稳定、高效的硬件控制至关重要,尤其在裸机编程、驱动开发和系统调试中,它能帮助开发者避免数据异常、系统挂起等常见问题。在ARM Cortex-A9等处理器中,TI的AM437x系列采用了层次化的L3和L4总线设计,以实现性能隔离与功耗优化。L3总线作为高速主干,连接DDR内存 阅读详情

相关推荐

深入解析AM437x内存映射:嵌入式开发的硬件寻址与调试指南

内存映射是计算机系统中CPU访问物理内存和硬件设备的地址转换机制,它定义了处理器可寻址空间内各功能区域的布局与访问规则。其核心原理在于通过地址总线将物理地址空间划分为不同区间,分别映射到RAM、ROM及各类外设寄存器,从而实现统一的寻址访问。这一技术对于嵌入式开发至关重要,它直接关系到系统的稳定性、性能优化与功耗管理。在工业控制、物联网网关等实时性要求高的应用场景中,精准的内存映射配置是确保外设驱动、DMA传输及系统启动流程正确无误的基础。本文以TI AM437x SoC为例,结合**总线架构**与**外设

weixin_29171087的博客 227

课程设计,基于STM32的智能测温系统(程序+电路图+手机APP)!

今天,咱给各位分享一个小项目,即基于STM32F103C8T6单片机的智能测温系统,它不仅能实时监测温度,还能通过手机APP远程管理和查看数据,可以说是温度监测的多面手。比如,一台精密的机床在工作时,如果温度过高,可能会导致零件变形,影响产品质量。使用STM32温度监控系统,可以实时监控运输过程中的温度变化,确保商品在合适的温度下存放,减少损耗,保证商品的新鲜度。通过这些应用场景,我们就能看到,这个系统不仅能够提供精确的温度监测,还能通过智能化的控制和管理,能够切实地为各行各业带来便利和效率的提升的。

weixin_45715650的博客 2050

AM437x嵌入式开发实战:MDIO、USB与MMC/SD寄存器配置与调试指南

在嵌入式系统开发中,寄存器编程是直接与硬件交互、实现底层控制的核心技术。其原理是通过读写映射到内存地址的硬件寄存器,精确配置外设的工作模式、时序与状态。掌握寄存器操作对于驱动开发、性能优化和深度调试具有不可替代的价值,尤其在网络通信、存储管理和高速接口等关键应用场景中。本文聚焦于TI AM437x平台,深入解析**MDIO接口**的PHY管理机制与状态机,并探讨**USB控制器**的双角色配置与PHY初始化流程,通过具体寄存器位操作和避坑指南,帮助开发者从底层理解并操控这些常用外设,解决实际开发中的链路不稳

weixin_29196315的博客 290

AT24C256资料下载仓库介绍:便捷的电子资源集成平台

AT24C256资料下载仓库介绍:便捷的电子资源集成平台 去发现同类优质开源项目:https://gitcode.com/ AT24C256资料下载仓库是一个集中提供AT24C256相关资源的平台,为电子爱好者和开发者提供了极大的便利。 项目介绍 AT24C256资料下载仓库的创建初衷是为了解决电子爱好者在学习和开发过程中寻找AT24C256相关资源的难题。该仓库整合了多种资源,包括文档、程序代码...

gitblog_06715的博客 387

创龙SOM-TL437xF工业级核心板简介(2)

创龙SOM-TL437xF是一款基于TI Sitara系列AM4376/AM4379ARM Cortex-A9+ Xilinx Spartan-6 FPGA处理器设计的异构多核工业级核心板。核心板内部AM437x与Spartan-6通过GPMC、I2C通信总线连接。通过工业级B2B连接器引出HDMI、CAMERA、GPMC、CAN等接口。核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。 用户使用核心板进行二次开发时,仅需专注上层运用,降低了开发难度和时间成本,可..

Tronlong_的博客 484

【驱动】TI AM437x(内核调试-02):dynamic 动态打印调试

dynamic 动态控制打印信息:打开、关闭打印;指定驱动模块、文件、函数打印

郭老二 657

AM437x——LED裸机

优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。 最近在玩AM437X,记录下一些学习过程。分为裸机和驱动。 0.准备文件 AM437x EVM Schematic(后称原理图YP) am4378(后称芯片手册SP) spruhl7g(后称参考手册CP) 1.分析原理图 四个LED灯,单片机通过控制三极管的通断,从而控制灯的亮灭。 单片...

hceng_linux 837

科研智能体平台搭建与实战:大模型如何驱动科研自动化

大模型技术的快速发展,正推动AI智能体从单纯对话工具向主动执行任务的科研伙伴演进。智能体的核心原理在于整合模型调用、工具使用、知识库检索与工作流编排,使其能够自主拆解目标、执行操作并迭代优化。在科研场景中,这一技术价值尤为突出:文献初筛、实验数据清洗、代码调试、结果汇总等高频重复劳动均可实现自动化,大幅释放研究者精力。从基础组件配置到多智能体协同,从平台选型到提示词调优,实际落地需要关注模型匹配、工具稳定性、记忆管理与人机校验机制。本文基于真实工程经验,系统梳理科研智能体平台的搭建路径、应用案例与高频问题排

weixin_32597009的博客 282

介绍---TI 公司Sitara 系列 AM437x处理器集成的QSPI接口

介绍TI 公司Sitara 系列 AM437x处理器集成的QSPI接口的基本结构H和工作原理

航天领域资深码农,型号经验丰富,一线干货分享 483

【微信小程序开发与设计】垃圾智能分类小程序

垃圾分类是将来生活可持续发展的必需项。但是很多人由于缺乏垃圾分类知识,或是因为垃圾分类困难,费时费力,所以没有好好贯彻垃圾分类政策,此时设计一款方便于群众进行智能化垃圾识别分类的微信小程序就显得尤为必要。

Markelle_Fultz的博客 8242

Field _II 仿真 学习笔记

参考Field II的使用说明,进行了四个例子的练习。仿真程序和结果如下: PURPOSE1:Calculate the received response from all elements of a linear array and plot the responses and the summed response 【计算来自所有线性阵元的接收响应,并绘制该响应以及叠加响应。】 >>

qq_34018578的博客 1万+

SOM-TL437是高性能低功耗处理器设计的工业级核心板(2)

创龙SOM-TL437x是一款基于TI Sitara系列AM4376/AM4379ARM Cortex-A9高性能低功耗处理器设计的工业级核心板,通过工业级B2B连接器引出千兆网口、HDMI、CAMERA、GPMC、CAN等接口。核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。 用户使用核心板进行二次开发时,仅需专注上层运用,降低了开发难度和时间成本,可快速进行产品方案评估与技术预研。 图 1核心板硬件框图 图 2AM437x...

Tronlong_的博客 357

【3D Slicer】阈值法测量肺部病灶体积

利用3D Slicer测量肺容积

SQ的博客 4857

AM437x——I2C驱动

优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。 本博文对应地址: https://hceng.cn/2017/09/11/AM437x——I2C驱动/#more 记录AM437x的I2C适配器驱动和I2C设备驱动,以及去除am437x EEPROM验证ID功能。 1.I2C驱动 在Linux中,I2C驱动有I2C适配器驱动(就是SOC的I...

hceng_linux 1472

在YOLO中扩充新类别并且不影响旧类的识别精度

让我们先从简单的部分开始:用我们的新类训练自定义 YOLOv8 模型。在本教程中,我们将向模型添加车牌类。我们将使用Roboflow 中的一个数据集。这个数据集有两个车牌类。在开始训练之前,我们还必须对库进行一些更改。为了节省时间,我还将本教程中我们将进行的更改转换为补丁该补丁包含多项更改,但大多数更改都用于指南的下一部分。if RANK!= -1:是的,我们将编写一个回调。我们希望将此自定义训练模型的头部与 COCO 预训练模型中的旧头部一起使用。

如果想成为中心,那么就到中心去吧。 1224

【干货分享】高通 SA8295P PCB 设计规范指南

高通SA8295P芯片是第四代骁龙汽车数字座舱平台的核心组件,采用5nm工艺,集成高性能CPU/GPU和30TOPS算力的NPU。文章详细介绍了该芯片各接口的layout设计规范,包括USB3.1/2.0、eDP/DP、MIPI CSI/DSI、PCIe Gen3等接口的阻抗、损耗、延时等关键指标要求,为新能源汽车电子设计提供重要参考。

一只豌豆象的博客 259

解析TI Sitara AM437x ARM + Xilinx Spartan-6 FPGA异构多核工业级核心板

平台简介 图 1 创龙SOM-TL437xF是一款基于TI Sitara系列AM4376/AM4379 ARM Cortex-A9 + Xilinx Spartan-6 FPGA处理器设计的异构多核工业级核心板。 核心板内部AM437x与Spartan-6通过GPMC、I2C通信总线连接。通过工业级B2B连接器引出LCD、CAMERA、GPMC、CAN等接口。 核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。 用户使用核心板进行二次开发时,...

创龙,嵌入式一体化解决方案商 1008

Google Cameraserver 看门狗CameraServiceWatchdog超时crash原理分析

本文分析了Android CameraServiceWatchdog机制的工作原理。该看门狗用于监控相机服务关键操作(如close、flush)的超时情况,通过定时器线程检测阻塞问题。文章详细讲解了Watchdog的初始化流程、使能方式、内部定时器机制(默认周期100ms,650次循环触发超时),并提供了configureStreams阶段的客制化实现方案。当检测到超时,Watchdog会触发abort生成tombstone日志,便于调试分析。

1128

创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 +Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领域。 SOM-TL437xF引出ARM和FPGA全部资源信号引脚,二次开发极其容易,客户只需要专注上层应用,大大降低了开发难度和时间成本,让产品快速上市,及时抢...

Tronlong_的博客 850

零基础学习simulink-- 四旋翼和六旋翼无人机的建模与动力学分析

通过上述代码实现,您可以逐步创建和搭建一个多旋翼无人机(如四旋翼、六旋翼)的动力学模型,并结合自适应控制器进行仿真。该模型不仅考虑了无人机的基本动力学行为,还引入了空气阻力、螺旋桨之间的相互作用以及气流干扰等复杂效应,使得仿真结果更加接近实际情况。Simulink 提供了强大的建模和仿真工具,使得控制系统工程师能够快速构建复杂的控制算法,并将其应用于实际工程中。多旋翼无人机动力学建模:建立四旋翼和六旋翼无人机的动力学模型,包括平移运动和旋转运动方程。升力和扭矩模型。

amy_mhd的博客 386

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA处理器;

TL437xF-EVM是一款广州创龙基于TI AM437x ARM Cortex-A9 +Xilinx Spartan-6 FPGA设计的开发板,底板采用沉金无铅工艺的4层板设计,它为用户提供了SOM-TL437xF核心板的测试平台,用于快速评估SOM-TL437xF核心板的整体性能。核心板在内部通过GPMC、I2C通信接口将ARM与FPGA结合在一起,组成ARM+FPGA架构,实现了需求独特、...

Tronlong_的博客 609

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

创龙SOM-TL437x工业级核心板简介

核心板简介 创龙SOM-TL437x是一款基于TI Sitara系列AM4376/AM4379ARM Cortex-A9高性能低功耗处理器设计的工业级核心板,通过工业级B2B连接器引出千兆网口、HDMI、CAMERA、GPMC、CAN等接口。核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。 用户使用核心板进行二次开发时,仅需专注上层运用,降低了开发难度和时间成本,可快速进行产品方案评估与技术预研。 图 1核心板正面图 图 2核...

Tronlong_的博客 686

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

创龙SOM-TL437x工业级核心板

核心板简介 创龙SOM-TL437x是一款基于TI Sitara系列AM4376/AM4379ARM Cortex-A9高性能低功耗处理器设计的工业级核心板,通过工业级B2B连接器引出千兆网口、HDMI、CAMERA、GPMC、CAN等接口。核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。 用户使用核心板进行二次开发时,仅需专注上层运用,降低了开发难度和时间成本,可快速进行产品方案评估与技术预研。 图 1核心板正面图 图 2核心板背面...

Tronlong_的博客 393

创龙TL437x-EasyEVM评估板介绍

评估板简介 创龙TL437x-EasyEVM是一款基于TI Sitara系列AM4376/AM4379ARM Cortex-A9高性能低功耗处理器设计的评估板,由核心板与底板组成。核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。 评估板接口资源丰富,引出千兆网口、CAMERA、GPMC、LCD、USB等接口,方便用户快速进行产品方案评估与技术预研。 图 1评估板正面图1 图 2评估板正面图2 图 3评估板侧视图1 ...

Tronlong_的博客 447

创龙SOM-TL437xF 核心板简介(二)

续上一贴,今天介绍SOM-TL437xF 核心板具体参数内容。 3软硬件参数 硬件框图 图一 核心板硬件框图 图二 AM437x资源对比图 图三AM437x处理器功能框图 图四Spartan-6特性 硬件参数 表1 ARM端硬件参数 CPU CPU:TI Sitara AM4376/AM4379 ARM Cortex-A9,主频1GHz 2x PRU-ICSS,每个PRU-ICSS子系统含...

创龙,嵌入式一体化解决方案商 930

基于AM4376/79 ARM+FPGA的GPMC通信案例讲解

本案例主要基于创龙科技的AM4376/AM4379 ARM Cortex-A9 + Xilinx Spartan-6 FPGA处理器设计的异构多核工业级核心板进行讲解: 图 1 SOM-TL437xF是一款基于TI Sitara系列AM4376/AM4379 ARM Cortex-A9 + Xilinx Spartan-6 FPGA处理器设计的异构多核工业级核心板。核心板内部AM437x与Spartan-6通过GPMC、I2C通信总线连接。通过工业级B2B连接器引出LCD、CAMERA、GPMC、.

创龙,嵌入式一体化解决方案商 1852
上一篇: AM437x——RTC裸机
下一篇: QT应用设计——HexoBlogClient
hceng_blog
博客等级 码龄10年 263粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值