This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
工具/软件:Code Composer Studio
您好!
我只是尝试将一个字节写入24AA32AF EEPROM、然后读回该字节、但当我尝试此简单操作时、我无法正确读取和写入。 我已经从 TI 下载了 I2Croutines.c 和 I2Croutines.h 文件、其中包括 EEPROM 读取、写入和确认函数。 我将 SDA 更改为 P3.0、将 SCL 更改为 P3.1。 我还将从器件地址设置为0xA0、以与 b10100000的控制字节一致。 现在、我尝试运行一个简单的主程序、该程序应该写入和读取一个字节、但我没有得到正确的结果。
#include #include "I2Croutines.h" int main (void) { unsigned int i; WDTCTL = WDTPW + WDTHOLD;//停止看门狗计时器 InitI2C (0xA0); EEPROM_ByteWrite (0x0000,0x12); EEPROM_AckPolling(); unsigned char read_val = EEPROM_RandomRead (0x0000); while (1); 返回0; }
我很困惑为什么这不能正确地写入 EEPROM 和从 EEPROM 读取。 我从 TI 获得的用于 I2Croutines 的代码如下所示。
I2CRoutines.c:
#include "msp430f5418a.h" #include "I2Croutines.h" #define MAXPAGEWRITE 64 int PtrTransmit; unsigned char I2CBufferArray[66]; unsigned char I2CBuffer; /*--------------- */ //说明: // I2C 模块的初始化 /*--------------- // 空 InitI2C (unsigned char EEPROM_i2c_address) //对于10100000、I2C 地址= 0xA0? { I2C_PORT_DIR |= SDA_PIN + SCL_PIN; I2C_PORT_SEL |= SDA_PIN + SCL_PIN; //将 I2C 引脚分配给 USCI_B0 //用户指南中显示的 I2C 模块的建议初始化步骤: UCB0CTL1 |= UCSWRST; //启用 SW 复位 UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C 主设备,同步模式 UCB0CTL1 = UCSSEL_2 + UCTR + UCSWRST; //使用 SMCLK、TX 模式、保持软件复位 UCB0BR0 = SCL_CLOCK_DIV; // fSCL = SMCLK/12 =~100kHz UCB0BR1 = 0; UCB0I2CSA = EEPROM_i2c_address; //定义从机地址 //在本例中为从地址 //定义控制字节 //发送到 EEPROM。 //UCB0I2COA = 0x01A5; //自己的地址。 // UCB0IE|=UCTXIE+UCRXIE; UCB0CTL1 &=~UCSWRST; //清除 SW 复位、 如果(UCB0STAT 和 UCBBUSY)则恢复运行 //测试总线是否空 闲{ //否则手动时钟打开 //生成 I2C_PORT_SEL &=~SCL_PIN; //为 SCL 选择端口功能 I2C_PORT_OUT &=~SCL_PIN; // I2C_PORT_DIR |= SCL_PIN; //将 SCL 驱动为低电平 I2C_PORT_SEL |= SDA_PIN + SCL_PIN; //选择的模块功能 //使用的 I2C 引脚 }; __enable_interrupt (); }/*--------------- // 说明: //初始化 I2C 模块以执行写操作。 /*------------------ */ void I2CWriteInit (void) { UCB0CTL1 |= UCTR; // UCTR=1 =>发送模式(R/W 位= 0) UCB0IFG &&~UCTXIFG; UCB0IE &&=~UCRXIE; //禁用接收就绪中断 UCB0IE|= UCTXIE; //启用发送就绪中断}/--><!--kadov_tag{{</spaces>}/--><!--kadov_tag{</spaces>}}--> */ /说明: // I2C 模块初始化以执行读取操作。 /*------------------ */ void I2CReadInit (void) { UCB0CTL1 &=~UCTR; // UCTL=0 =>接收模式(R/W 位= 1) UCB0IFG &&~UCRXIFG; UCB0IE &&~UCTXIE; //禁用发送就绪中断 UCB0IE |= UCRXIE; //启用接收就绪中断}/--><!--kadov_tag{{</spaces>}/--><!--kadov_tag{</spaces>}}--> */ //说明: //字节写入操作。 通过 I2C 总线与 EEPROM //(2465)进行通信。 一个数据字节被写入一个用户定义的地址。 /*------------------ // void EEPROM_ByteWrite (unsigned int Address、unsigned char Data) { unsigned char ADR_hi; unsigned char ADR_lo; while (UCB0STAT 和 UCBUSY); //等待 I2C 模块具有 //完成所有操作。 ADR_HI =地址>> 8; //计算高字节 ADR_LO =地址和0xFF; //和地址 I2CBufferArray[2]的低字节= ADR_HI; //低字节地址。 I2CBufferArray[1]= ADR_lo; //高字节地址。 I2CBufferArray[0]=数据; PtrTransmit = 2; //设置 I2CBufferArray 指针 I2CWriteInit(); UCB0CTL1 |= UCTXSTT; //开始条件生成 //=> I2C 通信已启动 //_ bis_SR_register (LPM0_bits + GIE); //通过中断 while (UCB0CTL1 & UCTXSTP)输入 LPM0; //确保已发送停止条件}/*--><!--kadov_tag{{</spaces>}--><!--kadov_tag{</spaces>}--><!--kadov_tag{</spaces>}--><!--kadov_tag{</spaces>}--><!--kadov_tag{</spaces>}}--> */ //说明: //页写操作。 通过 I2C 总线与 EEPROM //(24xx65)进行通信。 一个数据字节被写入一个用户定义的地址。 /*------------------ // void EEPROM_PageWrite (unsigned int StartAddress、unsigned char * Data、unsigned char size) { volatile unsigned int i = 0; volatile unsigned char counterI2cBuffer; unsigned char ADR_hi; unsigned char currentAddress = StartAddress; unsigned char currentSize = size;unsigned char bufferPtr = 0; unsigned char moreToDataRead = 1 while (UCB0STAT 和 UCBUSY); //等待 I2C 模块具有 //完成所有操作。 //执行直到数据缓冲区中没有更多数据 while (moreDataToRead) { ADR_HI =电流地址>> 8; //计算高字节 ADR_lo =电流地址和0xFF; //和低字节地址 //一次发送低至64字节数据包的斩波数据 //保持当前起始地址的指针 if (currentSize > MAXPAGEWRITE) { bufferPtr = bufferPtr + MAXPAGEWRITE; counterI2cBuffer = MAXPAGEWRITE - 1; PtrTransmit = MAXPAGEWRITE + 1; //设置 I2CBufferArray 指针 currentSize = currentSize - MAXPAGEWRITE; currentAddress = currentAddress + MAXPAGEWRITE; //获取起始地址 I2CBufferArray[MAXPAGEWRITE + 1]= ADR_HI;//高字节地址。 I2CBufferArray[MAXPAGEWRITE]= ADR_LO;//低字节地址 。} 其他 { bufferPtr = bufferPtr + currentSize; counterI2cBuffer =当前大小-1; PtrTransmit = currentSize + 1; //设置 I2CBufferArray 指针。 MoreDataToRead = 0; currentAddress = currentAddress + currentSize; //获取起始地址 I2CBufferArray[currentSize + 1]= ADR_HI;//高字节地址。 I2CBufferArray[电流大小]= ADR_lo;//低字节地址 。} //将数据复制到 I2CBufferArray unsigned char temp; for (i;i < bufferPtr;i++) { 温度=数据[i]; //必需,否则 IAR 抛出 //警告[Pa082] I2CBufferArray[counterI2cBuffer]= temp; counterI2c 缓冲器--; } I2CWriteInit(); UCB0CTL1 |= UCTXSTT; //开始条件生成 //=> I2C 通信已启动 //_ bis_SR_register (LPM0_bits + GIE); //输入带中断的 LPM0 while (UCB0CTL1 & UCTXSTP); //确保发送了停止条件 EEPROM_AckPolling(); //确保数据被写入 EEPROM } }-->----------------- // //说明: //当前地址读取操作。 从 EEPROM 读取数据。 使用来自 EEPROM 的当前//地址。 /*------------------ // unsigned char EEPROM_CurrentAddressRead (void) { while (UCB0STAT 和 UCBUSY); //等待 I2C 模块具有 //完成所有操作 I2CReadInit(); UCB0CTL1 |= UCTXSTT; // I2C 启动条件 while (UCB0CTL1 & UCTXSTT); //起始条件是否已发送? UCB0CTL1 |= UCTXSTP; // I2C 停止条件 //_bis_SR_register (LPM0_bits + GIE); //通过中断 while (UCB0CTL1 & UCTXSTP)输入 LPM0; //确保停止条件已发送 返回 I2CBuffer; }-->--------------- */ //说明: //随机读取操作。 从 EEPROM 读取数据。 EEPROM //地址由参数 Address 定义。 /*------------------ // unsigned char EEPROM_RandomRead (unsigned int Address) { unsigned char ADR_hi; unsigned char ADR_lo; while (UCB0STAT & UCBUSY); //等待 I2C 模块具有 //完成所有操作 ADR_HI =地址>> 8; //计算高字节 ADR_LO =地址和0xFF; //和地址 I2CBufferArray[1]的低字节= ADR_HI; //存储必须 包含 I2CBufferArray[0]= ADR_lo 的单字节; //在 I2CBuffer 中发送。 PtrTransmit = 1; //设置 I2CBufferArray 指针 //写入地址优先 I2CWriteInit(); UCB0CTL1 |= UCTXSTT; //开始条件生成 //=> I2C 通信已启动 //_bis_SR_register (LPM0_bits + GIE); //通过中断 while (UCB0CTL1 & UCTXSTP)输入 LPM0; //确保发送了停止条件 //读取数据字节 I2CReadInit(); UCB0CTL1 |= UCTXSTT; // I2C 启动条件 while (UCB0CTL1 & UCTXSTT); //起始条件是否已发送? UCB0CTL1 |= UCTXSTP; // I2C 停止条件 //_bis_SR_register (LPM0_bits + GIE); //通过中断 while (UCB0CTL1 & UCTXSTP)输入 LPM0; //确保停止条件已发送 返回 I2CBuffer; }-->--------------- */ //说明: //顺序读取操作。 数据从 EEPROM 以顺序 //形式从作为起点的参数地址读取。 指定 要读取的//大小并填充到数据缓冲区。 /*------------------ // void EEPROM_SequentialRead (unsigned int Address、unsigned char * Data、unsigned int size) { unsigned char ADR_hi; unsigned char ADR_lo; unsigned int counterSize; while (UCB0STAT 和 UCBUSY); //等待 I2C 模块具有 //完成所有操作 ADR_HI =地址>> 8; //计算高字节 ADR_LO =地址和0xFF; //和地址 I2CBufferArray[1]的低字节= ADR_HI; //存储必须 包含 I2CBufferArray[0]= ADR_lo 的单字节; //在 I2CBuffer 中发送。 PtrTransmit = 1; //设置 I2CBufferArray 指针 //写入地址优先 I2CWriteInit(); UCB0CTL1 |= UCTXSTT; //开始条件生成 //=> I2C 通信已启动 //_bis_SR_register (LPM0_bits + GIE); //通过中断 while (UCB0CTL1 & UCTXSTP)输入 LPM0; //确保发送停止条件 //读取数据字节 UCB0CTL1 &=~UCTR; // UCTL=0 =>接收模式(R/W 位= 1) UCB0IFG &&~UCRXIFG; UCB0IE &&~UCTXIE; //禁用发送就绪中断 UCB0IE |= UCRXIE; //启用接收就绪中断 UCB0CTL1 |= UCTXSTT; // I2C 启动条件 while (UCB0CTL1 & UCTXSTT); //起始条件是否已发送? for (counterSize = 0;counterSize < size;counterSize++) { //_bis_SR_register (LPM0_bits + GIE); //输入带中断的 LPM0 DATA[counterSize]= I2CBuffer; } UCB0CTL1 |= UCTXSTP; // I2C 停止条件 //_bis_SR_register (LPM0_bits + GIE); //通过中断 while (UCB0CTL1 & UCTXSTP)输入 LPM0; //确保已发送停止条件}/*--><!--kadov_tag{{</spaces>}--><!--kadov_tag{</spaces>}--><!--kadov_tag{</spaces>}--><!--kadov_tag{</spaces>}--><!--kadov_tag{</spaces>}}--> // //说明: //确认轮询。 如果写入周期为 //正在进行、EEPROM 将不会应答。 它可用于确定写入周期何时完成。 /*------------------ * / void EEPROM_AckPolling (void) { while (UCB0STAT 和 UCBUSY); //等待 I2C 模块具有 //完成所有操作 都执行 { UCB0STAT = 0x00; //清除 I2C 中断标志 UCB0CTL1 |= UCTR; // I2CTRX=1 =>发送模式(R/W 位= 0) UCB0CTL1 &=~UCTXSTT; UCB0CTL1 |= UCTXSTT; //起始条件已生成 while (UCB0CTL1和 UCTXSTT) //等待 I2CSTT 位被清零 { if (!(UCNACKIFG & UCB0STAT)) //如果收到 ACK 则分接 中断; } UCB0CTL1 |= UCTXSTP; //停止条件在之后生成 //发送从器件地址=> I2C 通信开始 while (UCB0CTL1 & UCTXSTP); //等待停止位被复位 _delay_cycles (500); //软件延迟 }while (UCNACKIFG & UCB0STAT); }/--><!------------------- */ *中断服务例程 */ * 请注意、编译器版本在以下代码和*/ /*中进行检查 根据编译器版本、正确的中断服务 */ * 例程定义。 // //#if __VER__< 200 //中断[USCIAB0TX_Vector] void TX_ISR_I2C (void) //#else #pragma vector = USCI_B0_Vector __interrupt void USCI_B0_ISR (void) //#endif { UCTXIFG & UCB0IFG} UCB0TXBUF = I2CBufferArray[PtrTransmit];//加载 TX 缓冲区 PtrTransmit ----; //测量 TX 字节计数器 if (PtrTransmit < 0) { while (!(UCB0IFG & UCTXIFG)); UCB0CTL1 |= UCTXSTP; // I2C 停止条件 UCB0IE &=~UCTXIE; //禁用中断。 UCB0IFG &=~UCTXIFG; //清除 USCI_B0 TX int 标志 //__BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0 } } 否则、if (UCRXIFG & UCB0IFG) { I2CBuffer = UCB0RXBUF; //将接收到的数据存储在缓冲区中 //_BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0 }
I2CRoutines.h:
#define I2C_PORT_SEL P3SEL #define I2C_PORT_OUT P3OUT #define I2C_PORT_REN P3REN #define I2C_PORT_DIR P3DIR #define SDA_PIN BIT0 // UCB0SDA 引脚 #define SCL_PIN BIT1 // UCB.S 引脚 #define SCL_CLOCK _DIV 0x12 // SCL 时钟偏差 void InitI2C (unsigned char EEPROM_i2c_address); void EEPROM_ByteWrite (unsigned int Address、unsigned char Data); unsigned char EEPROM_RandomRead (unsigned int Address);void char EEPROM_CurrentRead (unsigned int Data);unsigned char unsigned char ine_RandomRead (unsigned int Data);unsigned char unsigned char unsigned int Resid_CurrentAddress(unsigned int Data);void int Res void EEPROM_AckPolling (void);
除 Harry (正确)推荐的内容外:
>
//__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts
这与 SLAA208A 的代码不匹配。 注释掉所有这些"等待完成"序列会改变流程、或多或少会保证故障。
我建议您刷新原始代码(.pdf 中有一个链接)。
未经请求:虽然" UCBUSY"(与"UCBBUSY"相比)的使用是原始的、但也有一些可疑的用途。 在 I2C 模式下、UCBUSY 始终读为0 [参考用户指南 SLAU208Q)表37-20和38-7]。
Harry 和 Bruce、大家好、
感谢您的回复、您的建议非常有意义、因此我已将我的从地址更新为0x50、并对所有内容进行了取消注释
_bis_SR_register (LPM0_bits + GIE);
和
_BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);
线。 不过,我仍有问题。 当我尝试写入第一个字节时、代码仅进入 ISR 一次、并在执行下面的 PtrTransmit if 语句之前退出。
if (PtrTransmit < 0) { while (!(UCB0IFG & UCTXIFG)); //UCB0CTL1 |= UCTXSTP; // I2C 停止条件 UCB0IE &=~UCTXIE; //禁用中断。 UCB0IFG &=~UCTXIFG; //清除 USCI_B0 TX int 标志 _BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0 }
查看调试器中的寄存器后、每当我填充 TX 缓冲区时、TX 中断标志就会被清除、并且在停止位被置位之前不会再次被置位。 如果我知道我还有更多字节要发送 EEPROM、或者是否有方法在停止位被置位前继续发送字节、我应该在中断中手动设置 TX 中断标志?
再次感谢你的帮助。
您描述的序列听起来可疑、就像您在地址上遇到问题一样。
您将哪种类型的电路板用于 EEPROM? 我对 A0-A2引脚的配置方式特别感兴趣。
我确实得到了一个否定。 我检查了接线、连接到 VCC 的导线未连接到试验电路板上。 写入和读取操作现在工作正常。 感谢您的所有帮助!