单片机学习小组
直播中

王桂英

7年用户 1311经验值
私信 关注

I2C模块具有哪几种工作模式?

I2C模块具有哪几种工作模式?

回帖(1)

王志宏

2022-1-24 10:20:55
一、 简介

1、关于中断
能触发的中断事件只有两个:



  • a、I2C中断
  • b、I2C FIFO中断
    其中I2C中断包括:
  • 发送数据就绪
  • 接收数据就绪
  • 寄存器访问就绪
  • 接收到NACK
  • 仲裁了(仲裁只有在一个数据总线上接了多个主机时才有用)
  • 停止位检测
  • 从机地址
    其中I2C FIFO中断包括
  • 发送FIFO中断
  • 接收FIFO中断

2、关于传输模式

传输模式也是有两种:



  • 标准模式:准备发送n个数据值,其中n是您在I2C模块寄存器中编程的值。主要依赖I2CCNT寄存器配置
  • 重复模式:继续发送数据值,直到您使用软件启动停止条件或新的启动条件。主要依赖于I2CMDR寄存器配置

3、关于I2C模块

主要由10个部分组成:



  • a、串行接口:一个数据引脚(SDA),一个时钟引脚(SCL)
  • b、数据寄存器和FIFO,用于临时保存SDA引脚和CPU之间的接收数据和传输数据
  • c、控制和状态寄存器
  • d、外围总线接口,使CPU能够访问I2C模块寄存器和FIFO
  • e、时钟同步器,用于同步I2C输入时钟(来自设备时钟发生器)和SCL引脚上的时钟,并与不同时钟速度的主机同步数据传输
  • f、一个预分频器:用于划分驱动至I2C模块的输入时钟
  • g、SDA和SCL两个引脚上各有一个噪声滤波器
  • h、仲裁器:用于处理I2C模块(主机模式)和另一个主模块之间仲裁
  • I、中断生成逻辑:以便向CPU发送中断
  • j、FIFO中断生成逻辑:以便FIFO访问可以与I2C模块中的数据接收和数据传输同步

二、数据传输流程

非FIFO模式下
CPU将用于传输的数据写入I2CDXR,并从I2CDRR读取接收到的数据。
将I2C模块配置为发送器时,写入I2CDXR的数据将复制到I2CXSR,并在SDA引脚上一次移出一位。
I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);//配置为发送模式 当I2C模块被配置为接收器时,接收到的数据被转移到I2CRSR,然后复制到I2CDRR
I2C_setConfig(I2CA_BASE, I2C_MASTER_RECEIVE_MODE);//配置为接收模式 时钟生成:
为了满足所有的12C协议定时规格,12C模块的时钟必须在7- 12mhz之间。


只有当I2C模块处于复位状态(I2CMDR中的IRS=0)时,才能初始化预分频器。
只有当IRS更改为1时,预定频率才生效。
•I2CCLKL中的ICCL。对于每个主时钟周期,ICCL确定信号低的时间量。
•I2CCKLH中的ICCH。对于每个主时钟周期,ICCH确定信号高电平的时间量。


//对应代码最后一部分I2C_DUTYCYCLE_50可以使SCL的占空比为50%或者33%
I2C_initMaster(I2CA_BASE, DEVICE_SYSCLK_FREQ, 10000, I2C_DUTYCYCLE_50);


引脚配置:
先配置GPyGMUX,配置GPxQSELn为异步模式,配置GPyPUD为内部上拉模式


GPIO_setDirectionMode(DEVICE_GPIO_PIN_SDAA, GPIO_DIR_MODE_OUT);//GPIO_DIR_MODE_IN
GPIO_setPadConfig(DEVICE_GPIO_PIN_SDAA, GPIO_PIN_TYPE_STD);//GPIO_PIN_TYPE_PULLUP
GPIO_setQualificationMode(DEVICE_GPIO_PIN_SDAA, GPIO_QUAL_ASYNC);//异步


GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCLA, GPIO_DIR_MODE_OUT);//GPIO_DIR_MODE_IN
GPIO_setPadConfig(DEVICE_GPIO_PIN_SCLA, GPIO_PIN_TYPE_STD);//GPIO_PIN_TYPE_PULLUP
GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCLA, GPIO_QUAL_ASYNC);//异步


GPIO_setPinConfig(DEVICE_GPIO_CFG_SDAA);
GPIO_setPinConfig(DEVICE_GPIO_CFG_SCLA);


三、I2C模块操作详情

I2C模块有四种基本工作模式:
[tr]工作模式描述[/tr]

从机接收模式I2C模块是从模块,从主模块接收数据。所有从机在此模式下开始。在此模式下,SDA上接收的串行数据位随主设备产生的时钟脉冲移入。作为从机,I2C模块不生成时钟信号,但在接收字节后需要设备干预(I2CSTR中的RSFULL=1)时,它可以将SCL保持在低位。
从机发送模式I2C模块为从模块,向主模块传输数据。该模式只能从从接收器模式进入;I2C模块必须首先接收来自主机的命令。当您使用任何7位/10位寻址格式时,如果从机地址字节与其自身地址(在I2COAR中)相同,且主机已传输R/W=1,则I2C模块将进入其从机发送器模式。作为从发送器,I2C模块随后利用主发送器产生的时钟脉冲将SDA上的串行数据移出。作为从机,I2C模块不生成时钟信号,但在发送字节后需要设备干预(I2CSTR中的XSMT=0)时,它可以将SCL保持在低位。
主机接收模式I2C模块是一个主模块,从一个从模块接收数据。该模式只能从主变送器模式进入;I2C模块必须首先向从机发送命令。当您使用任何7位/10位寻址格式时,I2C模块在传输从属地址字节且R/W=1后进入其主接收器模式。SDA上的串行数据位随SCL上I2C模块产生的时钟脉冲移入I2C模块。当接收到字节后需要设备干预(I2CSTR中的RSFULL=1)时,时钟脉冲被抑制,SCL保持低。
主机发送模式I2C模块是一个主模块,将控制信息和数据传输到从模块。所有母版都在此模式下开始。在此模式下,以任何7位/10位寻址格式组合的数据在SDA上移出。位移位与SCL上I2C模块产生的时钟脉冲同步。在发送一个字节后,当需要设备干预(I2CSTR中的XSMT=0)时,时钟脉冲被抑制,SCL保持低。

起始信号和结束信号


        //刚刚配置的信息特指以下两个函数:
        I2C_setDataCount(I2CA_BASE,2);
        I2C_putData(I2CA_BASE, ADDR);//发送设备地址
       
        I2C_sendStartCondition(I2CA_BASE);//发送起始信号,并将刚刚配置的信息发送出
    I2C_sendStopCondition(I2CA_BASE);//发送终止信号


设置数据长度


I2C_setBitCount(I2CA_BASE, I2C_BITCOUNT_8);//配置数据长度为8位


7位地址模式


I2C_setAddressMode(I2CA_BASE,I2C_ADDR_MODE_7BITS);//设置为7位地址位寻址模式


在该模式下以CAT24C02为例





当A2 = A1 = A0 = 0时,写模式为0xA0,读模式为0xA1,但在TMS320F280049C的I2C模块不需要考虑R/W位只考虑前7位,即输入0x50即可。


        I2C_setSlaveAddress(I2CA_BASE, 0x50);
1
主机发送一个字节数据后自动会在数据后检测ACK信号


我看了很久官方的中断程序和中断相关的介绍= =。由于我太菜,看了个寂寞,复刻不出,网上除了官方基本没有例程(反正我没找到例程),因此我自己写了一个利用TMS320F280049C的硬件I2C读写ATC24C02的的程序(非中断)程序运行效率不高,但是吧,能用就行= =。


#define Delay_MinTime               1*1000
//  I2C PIN
#define DEVICE_GPIO_PIN_SDAA        32U  // GPIO number for I2C SDAA
#define DEVICE_GPIO_PIN_SCLA        33U  // GPIO number for I2C SCLA
#define DEVICE_GPIO_CFG_SDAA        GPIO_32_I2CA_SDA  // "pinConfig" for I2C SDAA
#define DEVICE_GPIO_CFG_SCLA        GPIO_33_I2CA_SCL  // "pinConfig" for I2C SCLA
#define SLAVE_ADDRESS               0x50
//初始化
void HAL_setupI2CA(void)
{
        //IO初始化
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SDAA, GPIO_DIR_MODE_OUT);//GPIO_DIR_MODE_IN
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SDAA, GPIO_PIN_TYPE_STD);//GPIO_PIN_TYPE_PULLUP
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SDAA, GPIO_QUAL_ASYNC);


    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCLA, GPIO_DIR_MODE_OUT);//GPIO_DIR_MODE_IN
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCLA, GPIO_PIN_TYPE_STD);//GPIO_PIN_TYPE_PULLUP
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCLA, GPIO_QUAL_ASYNC);


    GPIO_setPinConfig(DEVICE_GPIO_CFG_SDAA);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCLA);
    //I2C模块初始化
    I2C_disableModule(I2CA_BASE);
    I2C_initMaster(I2CA_BASE, DEVICE_SYSCLK_FREQ, 100000, I2C_DUTYCYCLE_50);
    I2C_setBitCount(I2CA_BASE, I2C_BITCOUNT_8);
    I2C_setSlaveAddress(I2CA_BASE, SLAVE_ADDRESS);
    I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);
    I2C_setEmulationMode(I2CA_BASE, I2C_EMULATION_FREE_RUN);
    //中断源为:检测到停止位,寄存器访问准备就绪
    I2C_enableInterrupt(I2CA_BASE, I2C_INT_STOP_CONDITION | I2C_INT_REG_ACCESS_RDY);


    I2C_enableFIFO(I2CA_BASE);
    I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_RXFF | I2C_INT_TXFF);
    I2C_enableModule(I2CA_BASE);   
}
//写一个字节数据到24C02
int16_t EE24CX_Write_Byte(uint16_t ADDR,uint16_t Data)
{


    I2C_setDataCount(I2CA_BASE,2);


    I2C_putData(I2CA_BASE, ADDR);//发送需要写入的地址
    I2C_putData(I2CA_BASE, Data);//发送需要写入的数据


    I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);//设置为发送模式
    I2C_sendStartCondition(I2CA_BASE);//发送起始信号,并将刚刚配置的信息发送出
    I2C_sendStopCondition(I2CA_BASE);//发送终止信号
    DEVICE_DELAY_US(Delay_MinTime);
    return 1;
}
//读24C02指定地址的一个字节
uint16_t EE24CX_Read_Byte(unsigned char ADDR)
{
    uint16_t Data;
    I2C_setDataCount(I2CA_BASE,1);
    I2C_putData(I2CA_BASE, ADDR);//发送地址信息
    I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);//配置为发送模式
    I2C_sendStartCondition(I2CA_BASE);//发送起始信号


    DEVICE_DELAY_US(Delay_MinTime);


    I2C_setConfig(I2CA_BASE, I2C_MASTER_RECEIVE_MODE);//配置为接收模式
    I2C_sendStartCondition(I2CA_BASE);//发送起始信号


    DEVICE_DELAY_US(Delay_MinTime);
    Data = I2C_getData(I2CA_BASE);//读取数据
    I2C_sendNACK(I2CA_BASE);//发送NACK
    I2C_sendStopCondition(I2CA_BASE);//发送停止信号
    DEVICE_DELAY_US(Delay_MinTime);
    return Data;
}


//写多个字节到24C02(这里没有按照按页写的时序)
//参数ADDR为读取的起始地址,*Data是发送缓存区,Number是发送的字节数
uint16_t IIC_Write_NByte(uint16_t ADDR,uint16_t *Data,uint16_t Number)
{
    uint16_t i = 0;
    for(i = 0;i < Number;i++)
    {
        EE24CX_Write_Byte(ADDR+i,(*Data));
        Data++;
    }
    return 1;
}


//从24C02读取多个字节(这个是按照规格书的多字节读取时序写的)
//参数ADDR为读取的起始地址,*Data是接收缓存区,Number是读取的字节数
uint16_t IIC_Read_NByte(uint16_t ADDR,uint16_t * Data,int16_t Number)
{
    uint16_t i = 0;
    uint16_t i_16 = 0;
    I2C_setDataCount(I2CA_BASE,1);
    I2C_putData(I2CA_BASE, ADDR);//发送地址信息
    I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);//配置为发送模式
    I2C_sendStartCondition(I2CA_BASE);//发送起始信号


    DEVICE_DELAY_US(Delay_MinTime);//该延时必须有否则无法读取I2C数据


    I2C_setDataCount(I2CA_BASE,Number);//设置需要接收的数据量
    I2C_setConfig(I2CA_BASE, I2C_MASTER_RECEIVE_MODE);//配置为接收模式
    I2C_sendStartCondition(I2CA_BASE);//发送起始信号


    DEVICE_DELAY_US(Delay_MinTime);//该延时必须有否则不能正确读取的I2C数据
    while(i < Number)
    {
        i_16 = (i+1) % 16;
        i++;
        *Data = I2C_getData(I2CA_BASE);//读取数据
        Data++;
        if(i != 0 && i_16 == 0)
            DEVICE_DELAY_US(Delay_MinTime);//这个延时必须有,如果没有在读取的数据量超过16字节时读取时序会出错
    }
    if(i == Number)
    {
        I2C_sendStopCondition(I2CA_BASE);//发送停止信号
    }
    DEVICE_DELAY_US(Delay_MinTime);
    return 1;
}


如果有幸想通了怎么用中断,再研究分享吧,现在水平不够,就这样吧
举报

更多回帖

发帖
×
20
完善资料,
赚取积分