SOC系统经典IP介绍以及使用方法说明之dw_i2c

2023-12-23 06:24:16

系列文章目录


第三章 DW_i2c功能及使用流程介绍


文章目录

前言

一、功能介绍

1.1 i2c写操作:

1.1.1 详细flow介绍

1.2 i2c读操作:

1.2.1 详细flow介绍

二、软件流程介绍

2.1 定义WDT中断服务子程序

2.2 定义main函数

三、总结



前言

? ? ?按照i2c的协议,i2c的时钟由master方提供,master可以向slave发送数据,也可以向slave请求数据。对应的slave既可以接收来自master发送的数据,也可以响应master请求数据。关于i2c协议,后续我们会单独讲解里面的细节和注意事项,这里我们主要接收使用过程中的一些基本配置flow。


一、功能介绍

? ? ? ?这里简单介绍一下i2c读写过程涉及到flow:

1.1 i2c写操作:

1.1.1 详细flow介绍

step1:首先master发起i2c start,即在sclk高电平拉低sda;

step2:发送slave设备地址,与之通信,这个设备地址按照协议可以是7bit,也可以是10bit,这里介绍最简单的7bit模式。

step3:紧接着发送一个bit的写控制位,表示当前传输为一笔写操作。

step4:然后后续跟着两个byte的数据,这两个byte数据一般用于软件解析命令用,通常情况下收发双方可以利用这两个byte自定义一些私有协议,比如最简单的就是两个纯粹的mem地址0x1000,表示master即将向0x1000地址写数据,因此后续master发送过来的Data,需要软件将其搬运到系统mem 0x1000地址去。当然也可以将这两个byte拆分成多个不同的命令,方便系统灵活运用。

step5:然后是发送过来的真正的payload数据

step6:当master完成数据写传输后,发送stop,即在scl的高电平拉高sda,结束当前传输。

1.2 i2c读操作:

1.2.1 详细flow介绍

step1:首先master发起i2c start,即在sclk高电平拉低sda;

step2:发送slave设备地址,与之通信,这个设备地址按照协议可以是7bit,也可以是10bit,这里介绍最简单的7bit模式。

step3:紧接着发送一个bit的写控制位,表示当前传输为一笔写操作。

step4:然后后续跟着两个byte的数据,这两个byte数据一般用于软件解析命令用,通常情况下收发双方可以利用这两个byte自定义一些私有协议,比如最简单的就是两个纯粹的mem地址0x1000,表示master即将向0x1000地址写数据,因此后续master发送过来的Data,需要软件将其搬运到系统mem 0x1000地址去。当然也可以将这两个byte拆分成多个不同的命令,方便系统灵活运用。

step5:这里master发送了一个re_start操作,即在scl的高电平拉低了sda,而并非发送payload数据,这里其实是一个读的转身,当slave接收到re_start时,会认为master其实想读数据,访问的数据地址体现在2byte信息里。因此,此时软件需要从内存地址里取数据并加载到i2c tx fifo中;

step6:按照协议重新发送设备地址以及R控制bit,这里配合step5供软件解析读过程。

step7:slave会根据软件填写的tx fifo值,把数据发送出去。

step8:当master接收完数据后,发送stop,即在scl的高电平拉高sda,结束当前传输。

二、软件流程介绍

2.1 定义WDT中断服务子程序

中断handler定义:

int dw_i2c_int_handler()
{
   int rdata;
   rdata = REG32(I2C_INTR_STAT);  //read intr status
   //judge which intr bit trigger
   if((rdata & 0x1) == 0x1) //rx_under
     {
       rdata = REG32(I2C_CLR_RX_UNDER); //read clear
     }
   else if((rdata & 0x2) == 0x2) //rx_over
     {
       rdata = REG32(I2C_CLR_RX_OVER); //read clear
     }
   else if((rdata & 0x4) == 0x4) //rx_full
     {
       rdata = REG32(I2C_DATA_CMD); //read recv data
     }
   else if((rdata & 0x8) == 0x8) //tx_over
     {
       rdata = REG32(I2C_CLR_TX_OVER); //read clear
     }
   else if((rdata & 0x10) == 0x10) //tx_empty
     {
       REG32(I2C_DATA_CMD) = 0x12; //cpu initial data into tx fifo
     }
   else if((rdata & 0x20) == 0x20) //rd_req
     {
       REG32(I2C_CLR_RD_REQ); //clr stop
       REG32(I2C_DATA_CMD) = 0x12; //cpu initial data into tx fifo
     }
   else if((rdata & 0x40) == 0x40) //tx_abrt
     {
       rdata = REG32(I2C_CLR_TX_ABRT); //read clear
     }
   else if((rdata & 0x80) == 0x80) //rx_done
     {
       rdata = REG32(I2C_CLR_RX_DONE); //read clear
     }
   else if((rdata & 0x100) == 0x100) //activity
     {
       rdata = REG32(I2C_CLR_ACTIVITY); //read clear
     }
   else if((rdata & 0x200) == 0x200) //stop_det
     {
       REG32(I2C_CLR_STOP_DET); //clr stop
     }
}

? ? ?这里是一个通用的中断handler,大家可以根据注释以及datasheet上的描述对此进行扩展,比如有些软件需要在tm_empty中断发生的时候CPU填写数据,以及fifo full中断的时候CPU需求将数据取走等等。具体还要根据实际应用情况来做扩展。

? ? 当然,对于EDA验证来说,这里相对来说更灵活,可以根据验证环境情况来自定义中断handler。

2.2 定义main函数

?main函数

int main()
{
   CLK_DW_I2C_EN;   //打开i2c模块的时钟使能,根据设计需求
   
   //may be i2c share gpio with module, this cfg can be used 
   GPIO_FUNC_SEL_I2C;  //配置gpio功能为DW_I2C

   set_irq_vector(NVIC_NUM_DW_I2C, (int)dw_i2c_int_handler); //配置中断服务向量
   __nds32_enable_int(NVIC_NUM_DW_I2C); //使能CPU中断

   //dw i2c initial
   REG32(I2C_ENABLE)      = 0x0;  //在配置i2c之前一定要先disable i2c_en
   REG32(I2C_SDA_HOLD)    = 0x5;  //可选,具体参考datasheet描述
   REG32(I2C_FS_SPKEN)    = 0x2;  //滤除毛刺的深度,建议根据需求配置
   REG32(I2C_TX_TL)       = 0x4;  //tx fifo水线
   REG32(I2C_RX_TL)       = 0x4;  /rx fifo水线
   REG32(I2C_CON)         = (REG32(I2C_CON) & 0xFFFFFF80) | 0x65); //master mode and 400k
   REG32(I2C_FS_SCL_HCNT) = 0x6;  //scl高电平宽度配置,根据需求修改
   REG32(I2C_FS_SCL_LCNT) = 0x6;  //scl低电平宽度配置,根据需求修改

   
   REG32(I2C_ENABLE) = 0x1; //使能i2c
   
   //填写tx fifo数据,硬件会自动将数据发送出去,这里填数据也可以放到int_handler中
   REG32(I2C_DATA_CMD) = 0x12;
   REG32(I2C_DATA_CMD) = 0x34;
   REG32(I2C_DATA_CMD) = 0x56;
   ......

   while(发送中断完成);

}

? ? ?上述main函数中,本文详细介绍了DW_i2c的初始化流程,里面的步骤基本上是必不可少的,当然,有一些配置可以根据实际情况进行调整,同时,如果使用i2c的更多功能,还需要大家自行添加相关寄存器配置。


三、总结

? ? ? 这里只是介绍了master发送的相关case配置,实际上DW_I2C可以配置为master/slave模式,且两个模式可以分别支持收发,即理论上来收还有其他三种case,master收数据,slave发数据以及slave收数据。在实际设计case时,关于数据的收发可以灵活调整,借助每个中断来进行同步收发是一个比较好的编码风格。这里为了让大家更容易理解,因此设计的case简单化了。

? ? ? 在实际验证过程中,不仅仅要根据需求设计严谨的case,还要同时控制收发双方的驱动程序,以及添加数据的自动化checker,这样后期在大量回归测试时,可以更快的完成回归测试。

文章来源:https://blog.csdn.net/luyil/article/details/135146342
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。