此程序实现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函数中设置ICCNT,ICCNT为要接收的数据量。在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)
如果读出的页有一个字节与写进的页不等,则写入与读出不一致,否则一致。