一、 简介
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模块有四种基本工作模式:
工作模式 | 描述 |
---|---|
从机接收模式 | 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);
主机发送一个字节数据后自动会在数据后检测ACK信号
我看了很久官方的中断程序和中断相关的介绍= =。由于我太菜,看了个寂寞,复刻不出,网上除了官方基本没有例程(反正我没找到例程),因此我自己写了一个利用TMS320F280049C的硬件I2C读写ATC24C02的的程序(非中断)程序运行效率不高,但是吧,能用就行= =。
//初始化
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;
}
如果有幸想通了怎么用中断,再研究分享吧,现在水平不够,就这样吧