TMS320C6748_I2C

此程序实现C6748的IIC模块对IICEEPROM设备的数据读写功能,地址是0x50(高7位)。程序先写入并读出一个字节数据,然后写入并读出一页数据,接着对比写入和读出的数据,根据结果判断IICEEPROM设备读写是否成功,主函数如下:

int main(void)
{
  int i,result;
  unsigned char buf_send[8];
  unsigned char buf_recv[8];
  
  //I2C管脚配置
  I2CPinMuxSetup(0);
  
  // 初始化串口终端使用串口2
  UARTStdioInit();
  
  // 打印串口终端信息
  UARTPuts("Tronlong IIC EEPROM Application......\r\n\r\n", -1);
  
  // 中断初始化
  InterruptInit();
  IICInterruptInit();
  
  // IIC初始化
  IICInit();
 
  // 写一个字节
  UARTPuts("Write single byte to address 0x0, value is 0x55.\r\n\r\n", -1);
  EEPROMByteWrite(0x0, 0x55);
  
  // 等待写完成
  EEPROMAckPolling();
   
  // 读一个字节
  memset(buf_recv,0,8);
  buf_recv[0] = EEPROMRandomRead(0);
  UARTPuts("Read one byte at a address 0x0, the value is ", -1);
  UARTPutHexNum(buf_recv[0]);
      UARTPuts(".\r\n\r\n", -1);
   
  // 读取当前地址值
  buf_recv[1] = EEPROMCurrentAddressRead();
      UARTPuts("Read one byte at current address 0x0, the value is ", -1);
  UARTPutHexNum(buf_recv[1]);
      UARTPuts(".\r\n\r\n", -1);
       
  for(i=0;i<8;i++)
  {
      if(i%2 ==0)
          buf_send[i]=0xaa;
      else
          buf_send[i]=0x55;
  }
  // 连续写指定长度(一个页面)
  UARTPuts("Write one page (8 bytes) to address 0x0.\r\n\r\n", -1);
  EEPROMPageWrite(0x00,buf_send,8);
  // 等待写完成
  EEPROMAckPolling();
  memset(buf_recv,0,8);
 
  // 连续读指定长度(一个页面)
  UARTPuts("Read one page (8 bytes) at address 0x0.\r\n\r\n", -1);
  EEPROMSequentialRead(0x00,buf_recv,8);
   
  result = 1;
  for (i=0;i<8;i++)
  {
      if (buf_send[i]!=buf_recv[i])
      {
          result = 0;
          break;
      }
  }
 
  // 如果 result 等于 1 ,说明写入的字节值与读出的字节值全部相同。否则,写入与读取的不一致
  if (result)
	  UARTPuts("Verify successfully.\r\n", -1);
  else
	  UARTPuts("Verify failed.\r\n", -1);
   
  for(;;)
  {
   
  }
}

I2CPinMuxSetup(0);函数对C6748的IIC0模块所在复用引脚的功能配置为IIC引脚。设置SYSCFG0模块的PINMUX4寄存器的PINMUX4_15_12和PINMUX4_11_8字段都为2,则设置了引脚功能为I2C0_SDA和I2C0_SCL。

(手册P22)

(指南P204)

(指南P226)

函数如下:

void I2CPinMuxSetup(unsigned int instanceNum)
{
  unsigned int savePinMux = 0;
   
  if(0 == instanceNum)
  {
   
    savePinMux = HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) & \
    ~(SYSCFG_PINMUX4_PINMUX4_15_12 | \
    SYSCFG_PINMUX4_PINMUX4_11_8);
     
    HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) = \
    (PINMUX4_I2C0_SDA_ENABLE | \
    PINMUX4_I2C0_SCL_ENABLE | \
    savePinMux);
   
  }
   
  else if(1 == instanceNum)
  {
    savePinMux = HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) & \
    ~(SYSCFG_PINMUX4_PINMUX4_23_20 | \
    SYSCFG_PINMUX4_PINMUX4_19_16);
     
    HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) = \
    (PINMUX4_I2C1_SDA_ENABLE | \
    PINMUX4_I2C1_SCL_ENABLE | \
    savePinMux);
   
  }
   
  else
  {
   
  }
 
}

主函数配置了I2C管脚后,就调用UARTStdioInit();函数初始化串口终端,使用串口2。UARTStdioInit函数做的事主要是对PSC1(Power and Sleep Controller)进行设置,使能UART2模块,然后设置UART2模块所在复用引脚的功能为TX和RX,并设置UART2的串口参数为8数据位,1停止位,无校验位,UART模式为FIFO模式,然后使能UART的free运行模式,启动UART2,详细细节可参考这篇博文:

C6748_UART_EDMA_YangYuke的博客-CSDN博客

启动UART2之后,串口2就可以进行收发了。先是打印串口终端信息,UARTPuts("Tronlong IIC EEPROM Application......\r\n\r\n", -1);UARTPuts函数判断要发送的字符是否已经是空字符,如果不是,则调用UARTPutc函数将字符写到UART的transmitter FIFO,如果是,就退出。UARTPuts函数如下:

unsigned int UARTPuts(char *pTxBuffer, int numBytesToWrite)
{
  unsigned int count = 0;
  unsigned int flag = 0;
   
  if(numBytesToWrite < 0)
  {
  flag = 1;
  }
   
  while('\0' != *pTxBuffer)
  {
    /* Checks if data is a newline character. */
    if('\n' == *pTxBuffer)
    {
      /* Ensuring applicability to serial console.*/
      UARTPutc('\r');
      UARTPutc('\n');
    }
    else
    {
      UARTPutc((unsignedchar)*pTxBuffer);
    }
    pTxBuffer++;
    count++;
     
    if((0 == flag) && (count == numBytesToWrite))
    {
    break;
    }
   
  }
  /* Returns the number of bytes written onto the transmitter FIFO. */
  return count;
}

UARTPutc函数如下:

void UARTPutc(unsigned char byteTx)
{
  UARTConsolePutc(byteTx);
}

UARTConsolePutc函数如下:

void UARTConsolePutc(unsigned char data)
{
  UARTCharPut(UART_CONSOLE_BASE, data);
}

UART_CONSOLE_BASE在程序中被定义为0x01D0 D000,即UART2模块的基地址。UARTCharPut函数如下:

void UARTCharPut(unsigned int baseAdd, unsigned char byteTx)
{
  unsigned int txEmpty;
   
  txEmpty = (UART_THR_TSR_EMPTY | UART_THR_EMPTY);
   
  /*
  ** Here we check for the emptiness of both the Trasnsmitter Holding
  ** Register(THR) and Transmitter Shift Register(TSR) before writing
  ** data into the Transmitter FIFO(THR for non-FIFO mode).
  */
   
  while (txEmpty != (HWREG(baseAdd + UART_LSR) & txEmpty));
   
  /*
  ** Transmitter FIFO(THR register in non-FIFO mode) is empty.
  ** Write the byte onto the THR register.
  */
  HWREG(baseAdd + UART_THR) = byteTx;
}

函数先等待UART的THR和TSR寄存器为空,等待上次发送完成,然后再写一个字符数据到THR。回到主函数,进行中断初始化工作,InterruptInit();函数使能全局中断,IICInterruptInit();进行IIC中断初始化,IICInterruptInit函数如下:

void IICInterruptInit(void)
{
  IntRegister(C674X_MASK_INT6, IICIsr);
  IntEventMap(C674X_MASK_INT6, SYS_INT_I2C0_INT);
      IntEnable(C674X_MASK_INT6);
}

IICInterruptInit函数注册CPU#6号可屏蔽中断的服务函数为IICIsr,设置interrupt selector的INTMUX1寄存器的INTSEL6字段(INTMUX1_16_22位)为36(SYS_INT_I2C0_INT),将#36号中断事件映射到CPU#6号可屏蔽中断,然后使能CPU#6号可屏蔽中断,IntEnable(C674X_MASK_INT6)

(手册P94)

回到主函数,中断初始化完成后,对IIC初始化,IICInit();。IICInit函数如下:

void IICInit(void)
{
  //IIC 复位 / 禁用
  I2CMasterDisable(SOC_I2C_0_REGS);
   
  // 配置总线速度为 100KHz
  I2CMasterInitExpClk(SOC_I2C_0_REGS, 24000000, 8000000, 100000);
   
  // 设置从设备地址
  I2CMasterSlaveAddrSet(SOC_I2C_0_REGS, ADDRESS);
   
  // IIC 使能
  I2CMasterEnable(SOC_I2C_0_REGS);
}

函数先是复位IIC0模块,I2CMasterDisable函数如下:

void I2CMasterDisable(unsigned int baseAddr)
{
  /* Bring the I2C module out of reset */
  HWREG(baseAddr + I2C_ICMDR) &= ~(I2C_ICMDR_IRS);
}

I2CMasterDisable函数清除I2C0外设的ICMDR寄存器(I2C Mode Register)的IRS位,从而复位并禁用I2C0

(手册P184)

(指南P917)

(指南P929)

然后I2CMasterInitExpClk(SOC_I2C_0_REGS, 24000000,8000000, 100000);函数I2C主模块的操作进行初始化,配置I2C0的总线速度为100KHz,并使能I2C0,I2CMasterInitExpClk函数如下:

void I2CMasterInitExpClk(unsigned int baseAdd, unsigned int inputClk,
unsigned int scaledClk, unsigned int outputClk)
{
  unsigned int prescale = 0;
  unsigned int dValue = 0;
  unsigned int div = 0;
   
  /* Calculate the prescalar value */
  prescale = (inputClk/scaledClk) - 1;
   
  HWREG(baseAdd + I2C_ICPSC) = prescale;
   
  switch (prescale)
  {
    case 0:
      dValue = 7;
      break;
    case 1:
      dValue = 6;
      break;
    default:
      dValue = 5;
      break;
  }
   
  div = scaledClk/outputClk;
  div -= (2*dValue);
   
  HWREG(baseAdd + I2C_ICCLKL)= div/2;
  HWREG(baseAdd + I2C_ICCLKH) = div - HWREG(baseAdd + I2C_ICCLKL);
   
  return;
}

函数的输入参数列表,inputClk为输入I2C模块的时钟,I2C0的输入时钟为PLL旁路时钟,晶振的时钟为24MHz,所以inputClk为24MHz。scaledClk为经过I2C模块预分频后的时钟频率,范围应该在6.7到13.3MHz之间。OutputClk为I2C_SCL时钟线上的频率。函数对ICPSC寄存器的IPSC字段,以及ICCCLKL、ICCLKH寄存器作相应设置,从而得到正确的I2C_SCL频率。IPSC字段必须要在I2C模块处于reset/disable状态时设置,否则设置无效。

(指南P131)

(指南P130)

(指南P906)

(指南P917)

(指南P917)

(指南P934)

然后I2CMasterSlaveAddrSet(SOC_I2C_0_REGS, ADDRESS);设置从机设备地址为ADDRESS(0x50,IICEEPROM地址)。I2C的slave address有7位和10位以及free mode三种形式,在7位和10位从机地址后面会跟一个读写位R/Wn,根据I2C是发送方还是接收方来决定。I2CMasterSlaveAddrSet函数如下:

void I2CMasterSlaveAddrSet(unsigned int baseAddr, unsigned int slaveAddr)
{
  /*Set the address of the slave with which the master will communicate.*/
  HWREG(baseAddr + I2C_ICSAR) = slaveAddr;
}

(指南P917)

(指南P926)

(指南P909)

(指南P910)

完成了对I2C0的设置后,就可以使能I2C了,

I2CMasterEnable(SOC_I2C_0_REGS);函数如下:

void I2CMasterEnable(unsigned int baseAddr)
{
  /* Bring the I2C module out of reset */
  HWREG(baseAddr + I2C_ICMDR) |= I2C_ICMDR_IRS;
 
  /* Set the backward compatibility mode off, for proper interruption */
  HWREG(baseAddr + I2C_ICEMDR) &= ~I2C_ICEMDR_BCM;
}

设置ICMDR的IRS位为1,从而使能I2C,并设置ICEMDR的BCM位为0,当C6748的I2C0作为从设备,并且要往主设备发送数据时,可以产生transmit data ready中断。

(指南P917)

(指南P933)

这样所有的初始化工作就完成了,回到主函数,先往EEPROM写一个字节(0x55)的数据,EEPROMByteWrite(0x0, 0x55);。EEPROMByteWrite函数如下:

void EEPROMByteWrite(unsigned int Address, unsigned char Data)
{
  slaveData[0] = Address;
  slaveData[1] = Data;
 
  IICSend(SOC_I2C_0_REGS,2);
}

IICSend函数为阻塞函数,只有当CPU执行完该函数才能执行其他操作,否则无法执行其他操作,函数如下:

void IICSend(unsigned int iic, unsigned int dataCnt)
{
  txCompFlag = 1;
  dataIdx = 0;
   
  while(I2CMasterBusBusy(iic));
   
  I2CSetDataCount(iic, dataCnt);
   
  I2CMasterControl(iic, I2C_CFG_MST_TX | I2C_CFG_STOP);
   
  I2CMasterIntEnableEx(iic, I2C_INT_TRANSMIT_READY | I2C_INT_STOP_CONDITION | I2C_INT_NO_ACK);
   
  I2CMasterStart(iic);
   
  // 等待数据发送完成
  while(txCompFlag);
   
  while(I2CMasterBusBusy(iic));
}

while(I2CMasterBusBusy(iic));判断IIC设备是否正忙,如果正忙,则一直等待,直到空闲为止。I2CMasterBusBusy函数如下:

unsigned int I2CMasterBusBusy(unsigned int baseAddr)
{
  return ((HWREG(baseAddr + I2C_ICSTR) & I2C_ICSTR_BB));
}

(指南P917)

(指南P920)

I2CSetDataCount(iic, dataCnt);设置I2C的data count寄存器(ICCNT),该寄存器的值表明I2C处于主收发器(master-transmitter-receiver)且重复模式(repeat mode)被关闭(off)的情况下要传输的字节数。I2CSetDataCount函数如下:

void I2CSetDataCount(unsigned int baseAddr, unsigned int count)
{
  HWREG(baseAddr + I2C_ICCNT)= count;
}

(指南P917)

(指南P924)

I2CMasterControl(iic, I2C_CFG_MST_TX | I2C_CFG_STOP);函数配置I2C控制器的运行模式为主发送器模式(Master-transmitter Mode),并对STP位置位,产生STOP条件(condition)。当内部的数据计数器计到0时(所有要发送的数据都已发送),对STP位置位会产生一个STOP condition。I2CMasterControl函数如下:

void I2CMasterControl(unsigned int baseAddr, unsigned int cmd)
{
  /* Since, the IRS bit needs to be set, to bring the module out
  * of local reset, we do that here, every time
  */
  HWREG(baseAddr + I2C_ICMDR) = cmd;
  HWREG(baseAddr + I2C_ICMDR) |= I2C_ICMDR_IRS;
}

(指南P929)

(指南P929)

(指南P929)

然后对IRS位置1,使能I2C模块。I2CMasterIntEnableEx(iic, I2C_INT_TRANSMIT_READY | I2C_INT_STOP_CONDITION | I2C_INT_NO_ACK);I2C中断屏蔽寄存器(I2C interrupt mask register,ICIMR)的相应位置位,使能I2C中断的Transmit-data-ready interrupt、Stop condition interrupt、No-acknowledgment interrupt等中断源(interrupt source。当这些I2C中断源事件出现时,就会产生系统中断事件IIC0_INT(EVT#36)。I2CMasterIntEnableEx函数如下:

void I2CMasterIntEnableEx(unsigned int baseAddr, unsigned int intFlag)
{
  /*Enable the master interrupt.*/
  HWREG(baseAddr + I2C_ICIMR) |= intFlag;
}

(指南P917)

(指南P919)

使能I2C中断的中断源之后,就可以启动I2C的数据发送了。I2CMasterStart(SOC_I2C_0_REGS);函数如下:

void I2CMasterStart(unsigned int baseAddr)
{
  HWREG(baseAddr + I2C_ICMDR) |= I2C_ICMDR_STT;
}

(指南P928)

启动I2C传输后,因为默认情况下,发送数据准备好标志(transmit-data-ready interrupt flag,ICXRDY)为1,此时满足transmit ready条件,所以会产生transmit interrupt,程序跳转到中断服务函数IICIsr中。

(指南P916)

中断服务函数voidIICIsr(void)如下:

void IICIsr(void)
{
  volatile unsigned int intCode = 0;
   
  // 取得中断代码
  intCode = I2CInterruptVectorGet(SOC_I2C_0_REGS);
   
  while(intCode!=0)
  {
    // 清除中断事件
    IntEventClear(SYS_INT_I2C0_INT);
   
    if (intCode == I2C_INTCODE_TX_READY)
    {
      I2CMasterDataPut(SOC_I2C_0_REGS, slaveData[dataIdx]);
      dataIdx++;
    }
    
    if(intCode == I2C_INTCODE_RX_READY)
    {
      slaveData[dataIdx] = I2CMasterDataGet(SOC_I2C_0_REGS);
      dataIdx++;
    }
    
    if (intCode == I2C_INTCODE_STOP)
    {
      I2CMasterIntDisableEx(SOC_I2C_0_REGS, I2C_INT_TRANSMIT_READY |
	  								    I2C_INT_DATA_READY |
	  								    I2C_INT_NO_ACK |
	  								    I2C_INT_STOP_CONDITION);
      txCompFlag = 0;
    }
    
    if (intCode == I2C_INTCODE_NACK)
    {
      I2CMasterIntDisableEx(SOC_I2C_0_REGS, I2C_INT_TRANSMIT_READY |
	  								    I2C_INT_DATA_READY |
	  								    I2C_INT_NO_ACK |
	  								    I2C_INT_STOP_CONDITION);
      // 产生停止位
      I2CMasterStop(SOC_I2C_0_REGS);
	  
      I2CStatusClear(SOC_I2C_0_REGS, I2C_CLEAR_STOP_CONDITION);
	  
      // 清除中断
      IntEventClear(SYS_INT_I2C0_INT);
      txCompFlag = 0;
      AckRolling = 1;
    }
    
    if (I2CMasterIntStatus(SOC_I2C_0_REGS) & I2C_ICSTR_NACKSNT)
    {
      I2CMasterIntDisableEx(SOC_I2C_0_REGS, I2C_INT_TRANSMIT_READY |
	  								    I2C_INT_DATA_READY |
	  								    I2C_INT_NO_ACK |
	  								    I2C_INT_STOP_CONDITION);
	  
      // 产生停止位
      I2CMasterStop(SOC_I2C_0_REGS);
	  
      I2CStatusClear(SOC_I2C_0_REGS, (I2C_CLEAR_NO_ACK_SENT |
	  							    I2C_CLEAR_STOP_CONDITION));
	  
      // 清除中断
      IntEventClear(SYS_INT_I2C0_INT);
      txCompFlag = 0;
    }
    
    intCode = I2CInterruptVectorGet(SOC_I2C_0_REGS);
  }
}

 函数中先是获得中断代码intCode,从而获得造成I2C中断的I2C中断源是什么(STOP Condition、No ack acknowledgement等等)。I2CInterruptVectorGet(SOC_I2C_0_REGS);函数如下

unsigned int I2CInterruptVectorGet(unsigned int baseAddr)
{
  return (HWREG(baseAddr + I2C_ICIVR) & I2C_ICIVR_INTCODE);
}

(指南P917)

(指南P932)

如果intCode非0,进入while循环,清除ER(Event flag)寄存器中的事件标志(#36),然后根据intCode作相应处理。因为是发送中断,所以intCode=I2C_INTCODE_TX_READY,I2CMasterDataPut(SOC_I2C_0_REGS, slaveData[dataIdx]);将要发送的数据写到ICDXR寄存器中,I2CMasterDataPut函数如下:

void I2CMasterDataPut(unsigned int baseAddr, unsigned char data)
{
  /*write data to be transmited to Data transmit register */
  HWREG(baseAddr + I2C_ICDXR) = data;
}

(指南P917)

(指南P927)

当发送完最后一个数据后,初值为ICCNT的internal data counter变为0,此时,因为ICMDR寄存器的STP位为1,所以产生STOP condition,从而产生stop condition detected interrupt,读取中断码intCode时为6,进入该中断处理分支,在该处理分支中,

I2CMasterIntDisableEx(SOC_I2C_0_REGS, I2C_INT_TRANSMIT_READY |

                                             I2C_INT_DATA_READY |

                                             I2C_INT_NO_ACK |

                                             I2C_INT_STOP_CONDITION);

函数将I2C中断屏蔽寄存器ICIMR(I2C interrupt mask register)的ICXRDY、NACK、SCD位都清0,从而屏蔽这些I2C中断事件。因为此时没有I2C中断事件产生了(或者被屏蔽了),所有再一次读取I2C中断向量寄存器时,中断码intCode将会变为0,从而程序指针将会跳出I2C中断服务函数,回到II2发送数据程序IICSend,然后while(I2CMasterBusBusy(iic));等待SDL线上的最后一个数据发送完成,完成后跳转回主函数。回到主函数,当EEPROM进入内部写周期之后,就要对其进行Acknowledge Polling写操作,轮询其写周期是否已结束。EEPROMAckPolling();函数不断查询数据是否已经写进EEPROM。EEPROMAckPolling函数如下:

void EEPROMAckPolling(void)
{
  do
  {
  	AckRolling = 0;
  	Delay(0xFFFFF);
  	slaveData[0] = ADDRESS;
  	IICSend(SOC_I2C_0_REGS, 1);
  	Delay(0xFFFFF);
  }while(AckRolling==1);
}

(AT24C02手册P9)

如果EEPROM写操作还没完成,轮询将不会得到回应,因此会产生NCAK中断。IIC中断服务函数会进入该分支段。

if (intCode == I2C_INTCODE_NACK)
{
  I2CMasterIntDisableEx(SOC_I2C_0_REGS, I2C_INT_TRANSMIT_READY |
 								  I2C_INT_DATA_READY |
 								  I2C_INT_NO_ACK |
 								  I2C_INT_STOP_CONDITION);
  // 产生停止位
  I2CMasterStop(SOC_I2C_0_REGS);
  
  I2CStatusClear(SOC_I2C_0_REGS, I2C_CLEAR_STOP_CONDITION);
  
  // 清除中断
  IntEventClear(SYS_INT_I2C0_INT);
  txCompFlag = 0;
  AckRolling = 1;
}

在该函数分支段中,程序关闭相关的I2C中断使能位,然后对ICMDR寄存器的STP位置位,手动产生stop condition,终止I2C数据传输(I2C transaction),I2CMasterStop函数如下:

void I2CMasterStop(unsigned int baseAddr)
{
  HWREG(baseAddr + I2C_ICMDR) |= I2C_ICMDR_STP;
}

再清除ICSTR寄存器的I2C中断标志位,此时的intCode应该是NACK的中断码。最后清除EF(Event flag)寄存器中对应I2C0_INT中断的标志位,然后设置标志变量txComFlag=0表示数据发送完成,AckRolling=1,表明轮询无应答,当回到EEPROMAckPolling函数时,还需继续轮询,直到AckRolling=0为止,主函数再继续向下。

memset(buf_recv,0,8);函数C/C++自带的库函数,其作用是将从buf_recv指向的地址开始,往后8个字节的区别全部赋值为0,利用该函数可以实现对block和array的快速清0. EEPROMRandomRead(0);从EEPROM指定地址处读取一个字节,函数如下:

unsigned char EEPROMRandomRead(unsigned int Address)
{
  slaveData[0] = Address;
  
  IICSend(SOC_I2C_0_REGS,1);
  IICReceive(SOC_I2C_0_REGS, 1);
  
  return slaveData[0];
}

根据手册,读指定地址字节之前,需先往从设备写指定地址,所以先调用函数IICSend(SOC_I2C_0_REGS,1);,然后调用IICReceive(SOC_I2C_0_REGS, 1);函数读取Data。IICReceive函数如下:

void IICReceive(unsigned int iic, unsigned int dataCnt)
{
  txCompFlag = 1;
  dataIdx = 0;
   
  while(I2CMasterBusBusy(SOC_I2C_0_REGS));
   
  I2CSetDataCount(SOC_I2C_0_REGS, dataCnt);
   
  I2CMasterControl(SOC_I2C_0_REGS, I2C_CFG_MST_RX | I2C_CFG_STOP);
   
  I2CMasterIntEnableEx(SOC_I2C_0_REGS, I2C_INT_DATA_READY | I2C_INT_STOP_CONDITION | I2C_INT_NO_ACK);
   
  I2CMasterStart(SOC_I2C_0_REGS);
   
  // 等待数据接收完成
  while(txCompFlag);
   
  while(I2CMasterBusBusy(SOC_I2C_0_REGS));
}

函数与IICSend函数大同小异,在I2CSetDataCount函数中设置ICCNTICCNT为要接收的数据量。在I2CMasterControl函数中,设置ICMDR的RX位为1(不同于IICSend的TX位),从而设置I2C设备为接收模式。在I2CMasterIntEnableEx函数中设置ICIMR寄存器的ICRRDY位(Receive-data-ready interrupt enable bit)为1,使能Receive-data-ready interrupt(不同于IICSend的ICXRDY位,Transmit-data-ready interrupt enable bit)。

(AT24C02手册P11)

当I2C0收到数据时,会产生接收中断,然后程序进入中断服务程序。读取I2C中断码intCode,此时intCode等于I2C_INTCODE_RX_READY。然后I2CMasterDataGet(SOC_I2C_0_REGS);函数读取数据到数组中,当读取了ICCNT个数据后,存储了ICCNT值的internal data counter变为0,而此时ICMDR的STP位为1,所以会产生STOP condition,然后会产生SCD(stop condition detected)中断,再读intCode,会进入I2C_INTCODE_STOP中断的处理分支,操作与IICSend的时候是一样的。I2CMasterDataGet函数如下:

unsigned int I2CMasterDataGet(unsigned int baseAddr)
{
  unsigned int rData;
   
  rData = HWREG(baseAddr + I2C_ICDRR);
  return rData;
}

函数读取ICDRR寄存器的值。

(指南P917)

(指南P925)

回到主函数,打印串口信息,UARTPuts("Read one byte at a address 0x0, the value is ", -1);然后UARTPutHexNum(buf_recv[0]);打印buf_recv[0]数据的16进制形式到串口。UARTPutHexNum函数如下:

void UARTPutHexNum(unsignedint hexValue)
{
  unsigned char num[8] = {0};
  unsigned int quotient = 0;
  unsigned int dividend = 0;
  int count = 0;
 
  dividend = hexValue;
 
  do
  {
    quotient = dividend/16;
    num[count] = (unsignedchar)(dividend % 16);
    if(0 == quotient)
    {
      break;
    }
    count++;
    dividend = quotient;

  }while(count < 8);
 
  if(8 == count)
  {
    count--;
  }
 
  UARTPutc('0');
  UARTPutc('x');
 
  while(count >= 0)
  {
    /* Checking for alphanumeric numbers. */
    if((16 - num[count]) <= 6)
    {
      /* Printing alphanumeric numbers. */
      UARTPutc(num[count--] + 0x37);
    }
    else
    {
      /* Printing numbers in the range 0 to 9. */
      UARTPutc(num[count--] + 0x30);
    }
  }
}

函数将收到的数据转换为16进制,从16进制的最低位开始,每次除16取余数获得该位的数值,并将其存入num数组中,然后除16将数值右移4位,再除16求余数获得第二位的数值,一直重复下去,直到为0,重复次数最多8次,因为unsigned int数据为32位,最大只可能有8位16进制位,然后打印到串口,每打印一位数先判断一下该数是否大于等于10,是就打印成A-E,不是则打印该数的ASCII码。

回到主函数,读取EEPROM当前地址存的字节到buf_recv[1]中,EEPROMCurrentAddressRead函数如下:

unsigned char EEPROMCurrentAddressRead(void)
{
  IICReceive(SOC_I2C_0_REGS, 1);
 
  return slaveData[0];
}

读取EEPROM当前地址的字节不需要先写字节地址(word address)这一步操作了(dummy write),直接发送设备地址就行了。

(AT24C02手册P11)

主函数继续往下,EEPROMPageWrite函数往EEPROM连续写指定长度,EEPROMPageWrite(0x00,buf_send,8);从EEPROM的0x00地址处开始连续写8个字节buf_send数组的数据。EEPROMPageWrite函数如下:

void EEPROMPageWrite(unsignedint Address, unsignedchar *Data, unsignedint Size)
{
  int i;
  
  slaveData[0] = Address;
  
  for (i=0;i<Size;i++)
  	slaveData[i+1] = Data[i];
  
  IICSend(SOC_I2C_0_REGS, Size + 1);
}

(AT24C02手册P10)

然后对EEPROM进行轮询操作,等待数据写完成。EEPROMSequentialRead函数再从指定地址连续读指定长度的数据出来到数组中,

EEPROMSequentialRead(0x00,buf_recv,8);从EEPROM的0x00地址处开始,连续读8个字节的数并存到buf_recv数组中,EEPROMSequentialRead函数如下:

void EEPROMSequentialRead(unsignedint Address , unsignedchar * Data , unsignedint Size)
{
  int i;
  
  slaveData[0] = Address;
  
  IICSend(SOC_I2C_0_REGS,1);
  IICReceive(SOC_I2C_0_REGS, Size);
  
  for (i=0;i<Size;i++)
 	Data[i] = slaveData[i] ;
}

(AT24C02手册P11)

(指南P909)

如果读出的页有一个字节与写进的页不等,则写入与读出不一致,否则一致。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值