IP核已经写好,可以通过AXI_LITE总线连接到PS上,接下来就写C语言驱动。
其实硬件底层的I2C驱动很简单,就是以下这四个函数。
void I2C_init(unsigned int base,unsigned int clk,unsigned int speed);
int I2C_start(unsigned int base, unsigned int add, unsigned int read);
unsigned int I2C_read(unsigned int base,unsigned int last);
unsigned int I2C_write(unsigned int base,unsigned char data, unsigned int last);
这些代码是我在N年前搜索到的代码,当实现了24CXX系列的EEPROM的烧写。
/****************************************************************
int I2C_start
Sets the start bit and then sends the first byte which
is the address of the device + the write bit.
inputs
base = the base address of the component
add = address of I2C device
read = 1== read 0== write
return value
0 if address is acknowledged
1 if address was not acknowledged
15-OCT-07 initial release
*****************************************************************/
int I2C_start(unsigned int base, unsigned int add, unsigned int read)
{
#ifdef I2C_DEBUG
printf(" Start I2C at 0x%x, \n\twith address 0x%x \n\tand read 0x%x \n\tand prescale 0x%x\n",base,add,read);
#endif
/* transmit the address shifted by one and the read/write bit*/
IOWR_I2C_OPENCORES_TXR(base, ((add<<1) + (0x1 & read)));
/* set start and write bits which will start the transaction*/
IOWR_I2C_OPENCORES_CR(base, I2C_OPENCORES_CR_STA_MSK | I2C_OPENCORES_CR_WR_MSK );
/* wait for the trnasaction to be over.*/
while( IORD_I2C_OPENCORES_SR(base) & I2C_OPENCORES_SR_TIP_MSK);
/* now check to see if the address was acknowledged */
if(IORD_I2C_OPENCORES_SR(base) & I2C_OPENCORES_SR_RXNACK_MSK)
{
#ifdef I2C_DEBUG
printf("\tNOACK\n");
#endif
return (I2C_NOACK);
}
else
{
#ifdef I2C_DEBUG
printf("\tACK\n");
#endif
return (I2C_ACK);
}
}
这里以I2C_START 为例子,看到有IORD_I2C_OPENCORES_SR这样的宏单元实际上就是对应了底层的寄存器读写,我们跟进去看定义:
#define IORD_I2C_OPENCORES_SR(base) IORD(base, 4)
继续跟进IORD,其实就是底层操作,原来我是使用UART转WISHBONE接口模块UART2BUS,定义对应了一个函数,
#define IORD UART2BUS_RDIO
如果采用内存映射地址就是
#define IORD (base , off ) *(unsigned int *) (base + off <<2 )
base地址就是基地址,可以从VIVADO界面获取。这里是43C0_0000.
驱动层面的编写完毕也就实现了上述了四个函数:
接下来看看一个实现24c02的函数,
unsigned char randm_read02 (unsigned char dev_addr,unsigned char byte_addr ){
unsigned char r;
unsigned char i;
while(1){
r = I2C_start(0,dev_addr,0);
if ( r==0 ) break ;
}
I2C_write(0,byte_addr,0);
while(1){
r = I2C_start(0,dev_addr,1);
if ( r==0 ) break ;
}
r = I2C_read(0,1);
return r;
}
写应用层程序有下属几点考虑:
1,这里直接操作器件的应用层程序其实也牵扯底层操作,应用层编写的时候要对照器件的I2C时序图,以从设备地址等信息自己画出来。
2,实际产品需要加上超时处理机制,这是所有的I2C应用必须考虑的,尤其使用连接线连接传感器。
3,注意设置合适的I2C时钟。一般应用不要超过100K。