RISC-V MCU中文社区

【分享】 E203外设——IIC工作原理与软件应用

发表于 开源蜂鸟E203 2023-05-18 21:23:43
0
1804
4

E203外设——IIC工作原理与软件应用

队伍编号:CICC2136
队伍名称:芯如止水

1.工作原理

I2C总线全称为Inter-Integrated Circuit(集成电路互联总线),是MCU中常用的接口模块。该协议规定了2条串行总线线路:一条串行数据线SDA用于收发数据、和一条串行时钟线SCL用于通信双方时钟的同步。SDA传输数据是大端传输,每次传输8bit即1字节。每个挂载在I2C总线上的从设备使用一个7bit 的地址进行唯一识别,当总线上传来的地址与自身符合时,返回一个ACK信号表示匹配。通过SCL与SDA的电平及电平跳变关系可以表示不同的协议标志位。如下图所示:

1.1发送数据

I2C的基本功能就是使用SDA串行数据线和SCL时钟线进行主从设备间的数据传输。
在主设备master向从设备slave发送数据的过程中,对于发送端,cpp代码中对i2c_master_top模块中相应txr、cr寄存器配置好并执行完后,i2c_master_top模块负责将txr中的数据以cr配置的I2C模式一一发送,其格式如上述图片所示。

具体地,初始sda和scl都处于高电平状态,发送一个起始信号然后将sda拉低,使得slave设备知道传输即将开始;接着是主设备发送从设备的地址和一个读写标志位,其中写标志是0,如果有某个从设备的地址与之匹配,从设备会返回一个应答信号ack,则接下来的通信就在主设备和此从设备之间进行;主设备继续发送一个要写入数据的寄存器地址,接着是一个从设备的应答信号;再紧接着发送8位数据,从设备再进行一次应答,如果数据通信就此结束,那么终止信号是在sda置于低电平时,将scl拉高并保持高电平,然后将sda拉高。可见波形图与这一过程一一对应。

可以很明显地看出scl和sda信号分为了三段:传输从设备地址、传输寄存器号、以及传输一连串的数据,根据数据段sda信号也可以1bit、1bit地读出二进制发送数据。

对于接收端,在i2c_slave_model模块中完成的是发送数据的接收和存储。使用一个移位寄存器[7:0]sr来接收串行的sda数据。

当处于idle状态下,若发送从设备号(sr[7:0]即a0)等于内置从设备号,转入slave_ack状态;然后进入get_mem_adr状态,mem_adr从sr寄存器中读入发送的寄存器地址,作为保存数据的mem起始地址;接着进入gma_ack状态下,再进入data状态。状态机处于data状态时,mem_adr自动加1,依次将每次接收的sr中8 bit数据保存到mem中;每次8bit数据存完进入data_ack状态决定是否终止数据传输,若是,则回到idle,传输完成。

1.2接收数据

主设备master接收从设备slave传过来的数据,分为两个过程:master向slave发送从设备号、写标志位(0)以及起始寄存器号;master向slave发送从设备号、读标志位(1)然后从i2c_master_top模块中rxr接收寄存器取数。其中,第一部分发送寄存器号与1.1节所述完全一致,第二部分也与1.1节非常相似,区别在于data传输阶段是从设备向主设备发送8bit数据。在这一部分,当slave中状态为data(以及idle)时,mem中相应mem_adr地址指针指向的数据会赋值给[7:0]mem_do的输出数据寄存器,而mem_do会自行移位并赋值mem_do[7]给sda_o进行I2C输出。

而主设备先向从设备发送数据、从设备再向主设备发送数据的波形图如下所示,波形后半段显示了将保存在slave. mem中的学号再传输回来的过程,可以看到scl和sda的时序关系仍然与前述理论图示相同。

可根据sda的拉高信号依次看到rxr中正确显示了所需要的数据,如以下波形图中白色圆圈所示:

2.软件应用

定义了SLAVE_ADDR宏来指明唯一的slave的基地址;定义了OV_WriteReg、OV_ReadReg两个函数分别用于master向slave的写、和master从slave中读:

#define SLAVE_ADDR      0x50 // 7'b1010_000
uint8_t OV_WriteReg(uint8_t regID, uint8_t *regDat, uint8_t length);
uint8_t OV_ReadReg(uint8_t regID, uint8_t *regDat, uint8_t length);

对I2C外设的控制通过配置预设寄存器来完成。I2C模块的基地址base_addr在hbird-sdk/SoC/hbirdv2/Common/Include/hbirdv2.h中声明:

#define I2C_CTRL_ADDR           _AC(0x10042000,UL)
#define _REG8(p, i)             (*(volatile uint8_t *) ((p) + (i)))
#define I2C_REG(offset)         _REG8(I2C_CTRL_ADDR, offset)

通过volatile对特定地址进行读写并封装成函数,通过封装好的函数对I2C模块进行寄存器读写,具体的内部寄存器如TXR、RXR等偏置则定义在i2c.h中。

2.1 OV_WriteReg函数实现

OV_WriteReg函数实现master向slave的写,传入的形式参数包括8位slave保存数据的起始寄存器号regID、放置传输数据数组的首地址regDat、和传输数据长度length。该函数完成以下4个动作:
① Phase 1:
发送数据{SLAVE_ADDR, 1’b0}装入TXR,其中1’b0表示写;CR配置为8’b1001_0000,其中第7位的1表示开始STA命令,第4位的1表示向从设备发起写命令WR;待SR[1]=0时转入phase 2,其中SR第1位TIP=1表示正在传输数据,TIP=0表示传输数据完成;

② Phase 2:
发送数据regID装入TXR;CR配置为8’b0001_0000,其中第4位的1表示向从设备发起写命令WR;待SR[1]=0时转入phase 3;

③ Phase 3:
依次发出数组regDat中length个元素:发送数据regDat[i]装入TXR;CR配置为8’b0001_0000,其中第4位的1表示向从设备发起写命令WR;待SR[1]=0时发送下一个数据或转入phase 4;

④ Phase 4:
CR配置为8’b0100_0000,其中第6位的1表示STOP,产生停止命令。

2.2 OV_ReadReg函数实现

OV_ReadReg函数实现master向slave的读,传入的形式参数包括8位slave读取数据的起始寄存器号regID、放置传回数据数组的首地址regDat、和传回数据长度length。该函数完成以下4个动作:
① Phase 1:
发送数据{SLAVE_ADDR, 1’b0}装入TXR,其中1’b0表示写;CR配置为8’b1001_0000,表示STA和WR命令;待SR[1]=0时进行下一步,SR[1]=0表示传输数据完成;
然后发送数据regID装入TXR;CR配置为8’b0001_0000,表示WR命令;待SR[1]=0时转入phase 2;

② Phase 2:
发送数据{SLAVE_ADDR, 1’b1}装入TXR,其中1’b1表示读;CR配置为8’b1001_1000,三个1从高位到低位分别表示STA、WR(此时仍是向从设备写入从设备地址)、ACK应答命令;;待SR[1]=0时转入phase 3;

③ Phase 3:
依次读入length个元素到数组regDat:CR配置为8’b0010_0000,其中第5位的1表示向从设备发起读命令RD;待SR[1]=0时从RXR中读入数据到regDat[i],接着读下一个数据或转入phase 4;

④ Phase 4:
CR配置为8’b0110_1000,其中三个1从高位到低位分别表示STOP停止命令、RD和ACK。

喜欢4
用户评论
zzzjy

zzzjy 实名认证

懒的都不写签名

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板