DSPF2833x:IIC配置[DSP I2C]


一、IIC简介

如图所示,IIC总线由SDA数据线和SCL时钟线组成,同时总线上外接上拉电阻确保信号的稳定性,设备对应挂载到总线上。
IIC硬件连接图

1.1 IIC总线频率

IIC总线时钟频率经过两次分频可得,如下图所示。
时钟频率的计算公式如下:

  1. I2C模块时钟(I2C Module Clock) = 系统时钟(SYSCLK) / (I2CPSC + 1)
  2. I2C总线时钟(I2C Master Clock) = I2C模块时钟(I2C Module Clock) / (ICCL + d) + (ICCH + d)

为了满足所有I2C协议时序规范,I2C模块时钟必须在7-12MHz之间。

I2C模块时钟分频
其中d的取值取决于I2CPSC寄存器位IPSC的值,如下图所示:

IPSC = 0, d = 7; IPSC = 1, d = 6; IPSC > 1, d = 5.

IPSCValue

I2CPSC寄存器的IPSC位介绍:

I2CPSC
从下图可以看到IIC总线的时钟频率,当IIC模块时钟频率(Tmod)固定时,ICCH和ICCL设置的值越大,对应SCL总线上高低电平占比越长,因此SCL(IIC总线上)的时钟频率越小,对IIC设备的读取速度越慢

SCL占空比

二、寄存器说明

2.1 相关寄存器概览

IIC相关寄存器概览

2.2 I2COAR

设置I2C自身地址(主机地址)寄存器

I2COAR

  • 寄存器I2CMDRXA位控制地址的位数

2.3 I2CIER

设置I2C中断请求使能

I2CIER

功能
ARBL仲裁丢失中断使能
NACK无应答中断使能
ARDY寄存器访问就绪中断使能
RRDY接收数据就绪中断使能
XRDY传输数据就绪中断使能
SCD停止条件检测到中断使能
ASS寻址为从机中断使能
  • 寄存器位值为1代表使能,0代表不使能。

2.4 I2CSTR

I2C状态寄存器,用于确定发生了哪个中断并存储状态信息。

I2CSTR

bit说明bit说明
15保留7-6保留
14 SDIR从机方向标志位5 SCD停止条件发生位
13 NACKSNTNACK发送位4 XRDY发送数据就绪中断标志位
12 BB总线忙3 RRDY接收数据就绪中断标志位
11 RSFULL接收移位寄存器满2 ARDY寄存器访问就绪中断标志位
10 XSMT发送移位寄存器空1 NACK无响应中断标志位
9 AAS0 ARBL仲裁丢失中断标志位
8 AD0

2.5 I2CCLKL

I2C时钟低分时器

  • 该值不能为0

2.6 I2CCLKH

I2C时钟高分时器

  • 该值不能为0

2.7 I2CCNT

数据计数寄存器,用于表示用发送和接收多少个数据。

I2CCNT

2.8 I2CDRR

数据接收寄存器

每次一位从SDA引脚移入接收移位寄存器(I2CRSR)。当接收到完整的数据字节时,I2C模块将数据字节从I2CRSR复制到I2CDRR。CPU无法直接访问I2CRSR。I2CMDR寄存器的BC位可设置接收数据的位长,如果数据设置接收长度低于8位,进行右对齐,比如设置3位,I2CDRR寄存器的0-2位地址有效,当处于接收FIFO模式时,I2CDRR寄存器充当接收FIFO缓冲器。
I2CDRR

2.9 I2CSAR

I2C从机地址设置寄存器
配置与I2COAR一样

2.10 I2CDXR

I2C数据发送寄存器

2.11 I2CMDR

I2C模式配置寄存器

I2CMDR

bitnamefuntion
14Free置1自由运行,不受调试断点影响
13STT置1发送起始信号
11STP置1发送停止信号在内部数据计数为0的时候
10MST置1设置为主机模式
9TRX0:接收模式,1:发送
8XA0:7bit地址,1:10位地址
7RM重复模式
5IRS1:使能I2C模块,0:重启模块

2.12 I2CISRC

I2C中断源寄存器(I2CISRC)是一个16位寄存器,由CPU用来确定哪个事件生成了I2C中断。

I2CISRC
INTCODE:
中断状态
通过读寄存器值获取中断源状态:

    Uint16 IntSource;
	// Read interrupt source
    IntSource = I2caRegs.I2CISRC.all;

对应程序定义:

// Interrupt Source Messages
#define I2C_NO_ISRC             0x0000
#define I2C_ARB_ISRC            0x0001
#define I2C_NACK_ISRC           0x0002
#define I2C_ARDY_ISRC           0x0003
#define I2C_RX_ISRC             0x0004
#define I2C_TX_ISRC             0x0005
#define I2C_SCD_ISRC            0x0006
#define I2C_AAS_ISRC            0x0007

2.13 I2CEMDR

2.14 I2CPSC

I2C预分配寄存器

2.15 I2CFFTX

I2C发送FIFO寄存器

2.16 I2CFFRX

I2C接收FIFO寄存器


三、代码说明

以官方例程:Example_2833xI2C_eeprom.c为例。

I2C消息结构体:

变量名含义
MsgStatusI2C状态
SlaveAddress从机地址
NumOfBytes写入字节数
MemoryHighAddr内存写入的高位地址
MemoryLowAddr内存写入的低位地址
MsgBuffer[I2C_MAX_BUFFER_SIZE]消息写入队列
//
// I2C Message Structure
//
struct I2CMSG 
{
    Uint16 MsgStatus;	// Word stating what state msg is in:
                        //   I2C_MSGCMD_INACTIVE = do not send msg
                        //   I2C_MSGCMD_BUSY = msg start has been sent,
                        //                     awaiting stop
                        //   I2C_MSGCMD_SEND_WITHSTOP = command to send
                        //       master trans msg complete with a stop bit
                        //   I2C_MSGCMD_SEND_NOSTOP = command to send
                        //       master trans msg without the stop bit
                        //   I2C_MSGCMD_RESTART = command to send a restart
                        //       as a master receiver with a stop bit
    Uint16 SlaveAddress; // I2C address of slave msg is intended for
    Uint16 NumOfBytes;	 // Num of valid bytes in (or to be put in MsgBuffer)
    
    //
    // EEPROM address of data associated with msg (high byte)
    //
    Uint16 MemoryHighAddr;
    
    //
    // EEPROM address of data associated with msg (low byte)
    //
    Uint16 MemoryLowAddr;   
    
    //
    // Array holding msg data - max that MAX_BUFFER_SIZE can be is 16 due to
    // the FIFO's
    Uint16 MsgBuffer[I2C_MAX_BUFFER_SIZE];	
};

I2CA初始化函数:
–将main函数部分初始化定义项也复制到I2CA_Init(void)中
–一般修改I2caRegs.I2CCLKL和I2caRegs.I2CCLKH的值(公式见1.1)

void I2CA_Init(void)
{
   // 初始化I2C GPIO功能
   InitI2CGpio();
   // 初始化I2C
   I2caRegs.I2CSAR = 0x0050;		// EEPROM的从机地址
   // I2C模块频率 = 10MHz
   #if (CPU_FRQ_150MHZ)             
        I2caRegs.I2CPSC.all = 14;   
   #endif
   #if (CPU_FRQ_100MHZ)             
     I2caRegs.I2CPSC.all = 9;
   #endif
   //Master Clock(SCL) = 10MHz / (10 + 5) + (5 + 5) = 400KHz
   I2caRegs.I2CCLKL = 10;			// NOTE: 这两个赋值必须不能等于0
   I2caRegs.I2CCLKH = 5;
   //SCD: 停止条件产生中断,ARDY: 寄存器访问就绪产生中断
   I2caRegs.I2CIER.all = 0x24;		

   I2caRegs.I2CMDR.all = 0x0020;	// 使能I2C(释放总线的效果)

   I2caRegs.I2CFFTX.all = 0x6000;	// 使能FIFO模式和发送FIFO
   I2caRegs.I2CFFRX.all = 0x2040;	// 使能接收FIFO, 清除RXFFINT标志位,
	
   //中断服务函数入口
   EALLOW;
   PieVectTable.I2CINT1A = &i2c_int1a_isr;
   EDIS;
   
	// Enable I2C interrupt 1 in the PIE: Group 8 interrupt 1
   PieCtrlRegs.PIEIER8.bit.INTx1 = 1;

   // Enable CPU INT8 which is connected to PIE group 8
   IER |= M_INT8;
   return;
}

I2CA写数据和读数据函数

Uint16 I2CA_WriteData(struct I2CMSG *msg)
{
   Uint16 i;
   // STP位在发送完数据后会清零
   if (I2caRegs.I2CMDR.bit.STP == 1)
      return I2C_STP_NOT_READY_ERROR;

   // 设置从机地址
   I2caRegs.I2CSAR = msg->SlaveAddress;
   // 检查总线是否忙
   if (I2caRegs.I2CSTR.bit.BB == 1)
      return I2C_BUS_BUSY_ERROR;
   // 设置发送字节:发送的数目+2位地址
   I2caRegs.I2CCNT = msg->NumOfBytes+2;
   // 发送16位地址
   I2caRegs.I2CDXR = msg->MemoryHighAddr;
   I2caRegs.I2CDXR = msg->MemoryLowAddr;
   for (i=0; i<msg->NumOfBytes; i++)
   {
      I2caRegs.I2CDXR = *(msg->MsgBuffer+i);
   }
   // 作为主机开始发送
   // FREE、STT、STP、MST、TRX、IRS
   I2caRegs.I2CMDR.all = 0x6E20; 

   return I2C_SUCCESS;
}


Uint16 I2CA_ReadData(struct I2CMSG *msg)
{
   // 检查STP
   if (I2caRegs.I2CMDR.bit.STP == 1)
      return I2C_STP_NOT_READY_ERROR;
   // 发送从机地址
   I2caRegs.I2CSAR = msg->SlaveAddress;
   // 发送读取地址
   if(msg->MsgStatus == I2C_MSGSTAT_SEND_NOSTOP)
   {
      // I2C模块已在总线上接收或传输START位时置1
      // 所以执行了发送控制字后,bus busy状态=1,后面else if就没有判断BB状态
      if (I2caRegs.I2CSTR.bit.BB == 1)
         return I2C_BUS_BUSY_ERROR;
      // 发送内存地址
      I2caRegs.I2CCNT = 2;
      I2caRegs.I2CDXR = msg->MemoryHighAddr;
      I2caRegs.I2CDXR = msg->MemoryLowAddr;
      I2caRegs.I2CMDR.all = 0x2620;  //这里比前面发送函数少了一个停止位
   }
   // dsp发送内存地址后,状态切换为:I2C_MSGSTAT_RESTART,开始接收数据
   else if(msg->MsgStatus == I2C_MSGSTAT_RESTART) 
   {
      I2caRegs.I2CCNT = msg->NumOfBytes;	// 设置接收的字节数
      I2caRegs.I2CMDR.all = 0x2C20;			// TRX = 0:接收
   }
   return I2C_SUCCESS;
}

I2C读写函数(读和写整合在一起):
–该函数根据自己所需做修改

//=====================================================================
//
// 读写I2C数据
// mode:RW_I2C_WRITE or RW_I2C_READ
//=====================================================================
Uint16 RwI2cBus(Uint16 mode)
{
    int16 i;
    
    if (I2caRegs.I2CMDR.bit.STP == 1)
    {
        return I2C_STP_NOT_READY_ERROR;
    }
    
    if ((I2caRegs.I2CSTR.bit.BB == 1) && (I2cMsg.status != I2C_MSGSTAT_RESTART)) 
    {
        return I2C_BUS_BUSY_ERROR;
    }

    I2caRegs.I2CSAR = I2C_SLAVE_ADDR;

    if (mode == RW_I2C_WRITE)
    {
        // Setup number of bytes to send
        // buffer + Address
		
        I2caRegs.I2CCNT = I2cMsg.NumOfBytes+ 2;
        I2caRegs.I2CDXR = I2cMsg.MemoryHighAddr; // Setup data to send
        I2caRegs.I2CDXR = I2cMsg.MemoryLowAddr;

        for (i = 0; i < I2cMsg.bytes; i++)
        {
            I2caRegs.I2CDXR = I2cMsg.MsgBuffer[i];
        }

        // Send start as master transmitter
        I2caRegs.I2CMDR.all = 0x6E20;   // S.A.D.P
    }
    else if ((mode == RW_I2C_READ) && (I2C_MSGSTAT_RESTART == I2cMsg.status))
    {
        I2caRegs.I2CCNT = I2cMsg.NumOfBytes; // Setup how many bytes to expect
        I2caRegs.I2CMDR.all = 0x2C20;   // Send restart as master receiver
                                        // S.A.D.P
    }
    else                                // ACK, or start read
    {
        I2caRegs.I2CCNT = 2;
        I2caRegs.I2CDXR = I2cMsg.MemoryHighAddr;
        I2caRegs.I2CDXR = I2cMsg.MemoryLowAddr;
        
        I2caRegs.I2CMDR.all = 0x2620;   // Send data to setup EEPROM address
                                        // S.A.D
    }

    return I2C_SUCCESS;
}

I2CA中断服务函数:
–删除了例程中对比读数据和写数据的操作

__interrupt void i2c_int1a_isr(void)     // I2C-A
{
   Uint16 IntSource, i;

   // 读取中断源
   IntSource = I2caRegs.I2CISRC.all;
   if(IntSource == I2C_SCD_ISRC) // 中断源:停止条件产生
   {
      // 如果是在写,将状态清为空闲
      // 开始写的时候,设置状态位为I2C_MSGSTAT_WRITE_BUSY,若产生停止位,代表写结束
      if (CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_WRITE_BUSY)
      {
         CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_INACTIVE;
      }
      else
      {
         // 如果状态是NOSTOP_BUSY,还进入了停止位中断源,重置状态为NOSTOP重新发送
         if(CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_SEND_NOSTOP_BUSY)
         {
            CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_SEND_NOSTOP;
         }
         // 完成读数据,设置状态为空闲并读取参数
         else if (CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_READ_BUSY)
         {
            CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_INACTIVE;
            for(i=0; i < I2C_NUMBYTES; i++)
            {
              CurrentMsgPtr->MsgBuffer[i] = I2caRegs.I2CDRR;
            }
		 }
      }
   }
   else if(IntSource == I2C_ARDY_ISRC) // 中断源:寄存器访问就绪
   {
      if(I2caRegs.I2CSTR.bit.NACK == 1) // 如果没响应
      {
         I2caRegs.I2CMDR.bit.STP = 1;  //发送停止位
         I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT; //无应答位清零
      }
      // 如果发送了NOSTOP,重置状态为RESTART
      else if(CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_SEND_NOSTOP_BUSY)
      {
         CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_RESTART; //重新开始
      }
   } 
   else // 只设置了SCD、ARDY中断源
   {	// 无效中断源
      __asm("   ESTOP0");
   }
   PieCtrlRegs.PIEACK.all = PIEACK_GROUP8;
}

main函数 loop:
–通过例程的main函数可以发现:
–信息队列msg的状态位比较重要,它可以查看写和读队列的当前状态,通过该位状态来执行下一步操作。

   // Application loop
   for(;;)
   {
      //
      // Write data to EEPROM section //
      //
      
      // 该例程将I2cMsgOut1的状态位设置成I2C_MSGSTAT_SEND_WITHSTOP
      // 所以作一个判断进入,进行写操作
      if(I2cMsgOut1.MsgStatus == I2C_MSGSTAT_SEND_WITHSTOP)
      {
         Error = I2CA_WriteData(&I2cMsgOut1);
         //写成功后将CurrentMsgPtr指向I2cMsgOut1,在中断服务函数中进行状态变更
         //将I2cMsgOut1状态设置成I2C_MSGSTAT_WRITE_BUSY
         if (Error == I2C_SUCCESS) 
         {
            CurrentMsgPtr = &I2cMsgOut1;
            I2cMsgOut1.MsgStatus = I2C_MSGSTAT_WRITE_BUSY;
         }
      }  // end of write section

      ///
      // Read data from EEPROM section //
      ///

	  // 当写入完成后,I2cMsgOut1状态设置成I2C_MSGSTAT_INACTIVE表示完成
      if (I2cMsgOut1.MsgStatus == I2C_MSGSTAT_INACTIVE)
      {
		 // 该例程将I2cMsgIn1的状态位初始化为I2C_MSGSTAT_SEND_WITHSTOP
		 // 所以作一个判断进入,然后进行读操作
         if(I2cMsgIn1.MsgStatus == I2C_MSGSTAT_SEND_NOSTOP)
         {
            // 发送控制节
            while(I2CA_ReadData(&I2cMsgIn1) != I2C_SUCCESS)
            {
            }
            // 更新指针和状态
            CurrentMsgPtr = &I2cMsgIn1;
            I2cMsgIn1.MsgStatus = I2C_MSGSTAT_SEND_NOSTOP_BUSY;
         }
         else if(I2cMsgIn1.MsgStatus == I2C_MSGSTAT_RESTART)
         {
            // 读取数据
            while(I2CA_ReadData(&I2cMsgIn1) != I2C_SUCCESS)
            {
            }
            // 更新指针和状态
            CurrentMsgPtr = &I2cMsgIn1;
            I2cMsgIn1.MsgStatus = I2C_MSGSTAT_READ_BUSY;
         }
      }  // end of read section

   }   // end of for(;;)

梳理状态过程

MsgStatus:

// I2C  Message Commands for I2CMSG struct
#define I2C_MSGSTAT_INACTIVE          0x0000	// 空闲
#define I2C_MSGSTAT_SEND_WITHSTOP     0x0010	// 发送带停止位
#define I2C_MSGSTAT_WRITE_BUSY        0x0011	// 写状态忙
#define I2C_MSGSTAT_SEND_NOSTOP       0x0020	// 发送不带停止
#define I2C_MSGSTAT_SEND_NOSTOP_BUSY  0x0021	// 发送状态忙
#define I2C_MSGSTAT_RESTART           0x0022	// 重新开始
#define I2C_MSGSTAT_READ_BUSY         0x0023	// 读状态忙

写数据状态切换:
写数据状态
读数据状态:

读数据状态

四、示例代码

I2C操作EEPROM代码:
i2c_eeprom.c
i2c_eeprom.h


总结

本篇为DSP F28335 I2C配置的学习记录,结合官方例程进一步说明,完整代码去看官方例程。(个人学习记录分享,若侵权删)

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值