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 periodI2C0->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的驱动了~~~
整个过程,最大的功臣就是逻辑分析仪,太好用了?