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.

[参考译文] CCS/MSP430F2112:I2C读取有时返回0x00

Guru**** 2524260 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/578411/ccs-msp430f2112-i2c-reads-sometimes-return-0x00

部件号:MSP430F2112

工具/软件:Code Composer Studio

大家好,

我正在编写一个中断驱动的MSP430 I2C从器,它具有用于记录的实时时钟和电池监视器。 它似乎在"大多数时间"都起作用,但偶尔,当我读取大量数据突发时,一些寄存器返回零,即使它们具有有效数据。 我从DCO运行主环路,频率为1MHz。 如果我将DCO速度提高到8,12或16MHz,我总是得到零....???

我已经实施了一系列可以通过I2C读取或写入的寄存器。

同时,我还在使用LFXTAL来驱动计时器,从连接的32.768kHz xtal生成精确的0.5秒tick。

有什么想法,可能会发生什么?

代码。

谢谢,

Mike。

#include <MSP4S.h>

#在   1上定义
#define off   0
#define true   1.
#define false   0


typedef unsigned char REG_SUBADDR_TYPE;
typedef unsigned char bool;

#define REG_STATUS         (REG_SUBADDR_TYPE) 0x00
#define REG_control1      (REG_SUBADDR_TYPE) 0x01
#define REG_Control2      (REG_SUBADDR_TYPE) 0x02
#define REG_BATTERY_HIGH   (REG_SUBADDR_TYPE) 0x03
#define REG_BATTERY_LOW      (REG_SUBADDR_TYPE) 0x04
#define REG_SW_VERSION      (REG_SUBADDR_TYPE) 0x05
#define REG_year         (REG_SUBADDR_TYPE) 0x06
#define REG_MONTH         (REG_SUBADDR_TYPE) 0x07
#define REG_DATE         (REG_SUBADDR_TYPE) 0x08
#define REG_Hour         (REG_SUBADDR_TYPE) 0x09
#define REG_MINUTE         (REG_SUBADDR_TYPE) 0x0A
#define REG_second         (REG_SUBADDR_TYPE) 0x0B
#define REG_ALARM_year      (REG_SUBADDR_TYPE) 0x0C
#define REG_ALARM_MONTH      (REG_SUBADDR_TYPE) 0x0D
#define REG_ALARM_DATE      (REG_SUBADDR_TYPE) 0x0E
#define REG_ALARM_Hour      (REG_SUBADDR_TYPE) 0x0F
#define REG_ALARM_MINUTE   (REG_SUBADDR_TYPE) 0x10
#define REG_ALARM_seconds   (REG_SUBADDR_TYPE) 0x1A


typedef枚举

   IIC_SLAVE_IDLE,
   IIC_SLAVE_RXED_SLAVE_ADDR,
   IIC_SLAVER_SLAVER_TX_BLOCK,
   IIC_SLAVER_SLAVER_RX_BLOCK
} IIC_SLAVER_STANE_TYPE;

静态IIC_SLAVE_STATE_TYPE   IIC_SLAVE_STATE;   //I2C状态
REG_SUBADDR_TYPE         IIC_REG_ADDRESS;   //当前寄存器指针


static unsigned char   battery_leve_high = 0x00;
static unsigned char   battery_leve_low = 0x00;

静态无符号int   reg_year =17;
静态无符号int   reg_month =2;
静态无符号int   reg_date =28;
静态无符号int   reg_hours =15;
静态无符号int   reg_minutes =30;
静态无符号int   reg_seconds =30;

静态无符号int   reg_alarm_year = 17;
静态无符号int   reg_alarm_month=2;
静态无符号int   reg_alarm_date = 28;
静态无符号int   reg_alarm_hours =16;
静态无符号int   reg_alarm_minutes =30;
静态无符号int   reg_alarm_seconds = 30;


//声明函数原型
void delay ( unsigned int );
void Update_Half_seconds (void);
void Update_Clock_24hr (void);
void Update_Display(void);
void Update_Mode (void);
作废Check_Alarm (void);
作废Check_Battery (作废);
int REG_READ_REGISTER (REG_SUBADDR_TYPE);
int REG_WRITE注册(REG_SUBADDR_TYPE,int);
REG_SUBADDR_TYPE REG_INC_REGISTER_REG(REG_SUBADDR_TYPE);


内部主(无效)

   WDTCTL = WDTPW | WDTHOLD;         //停止看门狗计时器

   IF (CALC1_1MHz == 0xFF || CALDCO_1MHz == 0xFF)
   {
    while (1);                              //如果校准常量被擦除,则停止

   }
   //1MHz
   BCSCTL1 = CALC1_1MHz;             //设置范围
   DCOCTL = CALDCO_1MHz;              //设置DCO步进+调制*

   //BCSCTL2 |= DIVS1 + DIVS0;         // SMCLK /8除法器
   BCSCTL3 = 0x0C;                   // 32.768kHz,12.5pF负载盖

   P3SEL |= 0x06;                   //将I2C引脚分配给USI_B0
   UCB0CTL1 || UCSWRST;   //将USCI逻辑置于重置状态,SMCLK
   UCB0CTL0 = UCMODE_3 + UCSYNC;      // USCI = I2C,同步模式
   UCB0I2COA = 0x18;                //自有地址为0x18
   UCB0CTL1 &=~UCSWRST;             //释放USCI逻辑重置
   UCB0I2CIE || UCSTPIE + UCSTTIE;      //启用STT和STP中断
   IE2 |= UCB0RXIE + UCB0TXIE;         //启用RX和TX中断
   IIC_SLAVE_STATE = IIC_SLAVE_IDLE;   //初始化I2C状态机

   P1DIR = 0x3D;
   P1OUT = 0x00;

   P2DIR = 0x20;                   //WFP 2.5 作为OP
   P2IE |= 0x18;                   //已启用WFP 2.3 ,WFP 2.4 中断
   P2IES |= 0x18;                   //高/低边缘
   P2IFG &=~0x18;                   // IFG已清除
   P2OUT |= BIT5;                   //debug -打开5V_AUX_EN以启用DC-DC

   P3DIR |= 0xF9;
   P3OUT = 0x00;

   TA1CTL = tassel_1 + MC_1;         // ACLK (32.678kHz),上行模式
   TA1CCR0 = 0x3FFF;                // 3FFF相当于32.768kHz时的0.5秒
   TA1CCTL0 = CCIE;                //已启用TACR0中断

   __enable_interrupit();
   //__bis_sr_register(CPUOFF + GIE);       //输入带中断的LPM0

   同时(1);
}


//------------------
//此处处理I2C总线状态更改中断
//当UCB0TXBUF加载了一个字节时,Tx IFG被清除
//读取UCB0RXBUF时清除Rx IFG
//------------------
#pragma vector = USCIAB0TX_vector
__interrupt void USCIAB0TX_ISR(void)

 如果(IFG2和UCB0RXIFG)   //主中继正在给我们写信
 {
    如果(IIC_SLAVE_STA= IIC_SLAVE_IDLE)         //如果我们处于空闲状态,则注册主机要读取或写入
    {
       IIC_REG_ADDRESS = UCB0RXBUF;                //将是子地址
       IIC_SLAVE_STANE = IIC_SLAVE_RXED_SLAVE_ADDR;   //移至下一状态
    }
    否则,如果(IIC_SLAVE_STA= IIC_SLAVER_RXED_SLAVER_ADDR)
    {
       REG_WRITE_REGISTER (IIC_REG_ADDRESS,UCB0RXBUF);
       IIC_REG_ADaddress =(REG_SUBADDR_TYPE) REG_INC_REGISTER (IIC_REG_ADDRESS);   //计算下一个地址
    }
 }
 else   //else,Master要读取数据
 {
    UCB0TXBUF = REG_READ_REGISTER (IIC_REG_ADDRESS);                      //加载先前设置的子地址的内容
    IIC_REG_ADaddress =(REG_SUBADDR_TYPE) REG_INC_REGISTER (IIC_REG_ADDRESS);   //计算下一个地址
 }
}


//------------------
//此处处理I2C总线状态更改中断
//------------------
#pragma vector = USCIAB0RX_vector
__interrupt void USCIAB0RX_ISR(void)

   IF (UCB0STAT和UCSTPIFG)//如果停止INT
   {
      IIC_SLAVER_STANE = IIC_SLAVE_IDLE;
      IIC_REG_ADDRESS = 0x00;
      UCB0STAT &=~UPSTPIFG;
   }

   否则,如果(UCB0STAT和UCSTIFG)//如果启动INT
   {
      UCB0STAT &=~UCSTIFG;
   }
}



空延迟(无符号int ms)

 同时(毫秒--)
   {
       __DELAY周期(1000);//设置为16MHz,将其更改为1000,设置为1 MHz
   }
}



// Timer1_A0中断服务例程
#pragma vector=Timer1_A0_vector
__interrupt void Timer1_A0 (void)

 update_half_seconds();
}



/*此函数应每隔1/512秒从interupt*/中调用一次
/*此中断中的所有功能应在1毫秒内完成,因此不禁用中断!*/
作废Update_Half_Seconds (void)

   static char half_seconds = 0;

   P3OUT ^= BIT6;   //调试:闪烁绿色LED

   半秒++;

   如果(Half_seconds>=2)
   {
      UPDATE_Clock_24hr();
      Half_seconds=0;
   }
}



/*这是唯一更新实际实时的函数*/
/*此函数应每秒调用一次*/
作废Update_Clock_24hr (void)

   REG_seconds++;
   Check_Alarm();   //如果位已设置,请检查是否有报警

   如果(REG_seconds >= 60)
   {
      REG_seconds =0;      //重置REG_seconds

      REG_MINUTES ++;

      如果(REG_MINUTES >= 60)
      {
         REG_MINUTES = 0;
         REG_Hours++;
      }
      如果(REG_Hours >= 24)
      {
         REG_Hours = 0;
      }

      CHECK_BATTERY();//   电池电压每分钟更新一次
   }
}


/*每次更新REG_MINUTES计数器时都会调用此函数
 *
 */
作废Check_Battery (作废)

   静态长ADC_Temp = 0.0 ;

   ADC10CTL1 = inch_1;                                        /CH1.
   ADC10CTL0 = SREF_1 + ADC10SHT_2 + RefoN + Ref2_5V + ADC10ON;   //VREF,16 ADC CLK,2.5V REF ON,
   P3OUT|= BIT3;                                        //打开电池ADC路径
   延迟(5);                                           //稳定时间
   ADC10CTL0 |= ENC + ADC10SC;                               //开始采样和转换
   当(ADC10CTL1和占线);                               //等待ADC10完成
   ADC_Temp = ADC10MEM;                                  //指定ADC10MEM中保留的值
   P3OUT &=~BIT3;                                        //关闭电池ADC路径

   BATTERY_LEVE_LOW =(ADC_Temp & 0x00FF);      //存储较低位
   BATTERY_LEVE_HIGH =(ADC_Temp >> 8);         //向右移动并存储高位

   IF (ADC_Temp >= 302)                         //4.2V
   {
      ;
   }
   否则,如果((ADC_Temp >= 259)&&(ADC_Temp <= 301)//3.6V->4.2V
   {
      ;
   }
   否则,如果((ADC_Temp >= 223)&&(ADC_Temp <= 258)//3.1V-->3.6V
   {
      ;
   }
   否则,如果( ADC_Temp <=216)                      //<3.1伏
   {
      ;
   }
}



/*每次更新REG_MINUTES计数器时都会调用此函数
 *
 */
作废Check_Alarm (作废)

   如果(REG_Hours == REG_ALARM_Hours)&&(REG_MINUTES == REG_ALARM_MINUTES)&&
         (REG_seconds == REG_ALARM_seconds)
   {
      ;
   }
}


/*I2C读取寄存器功能
 *
 */
int REG_READ_REGISTER (REG_SUBADDR_TYPE   子地址)

   交换机(subaddr)
   {
       案例REG_BATTERY_LOW:
          返回电池电量低;

       案例REG_BATTERY_HIGH:
          返回电池电量_水平_高;

       案例REG_SW_VERSION:
          返回0x01;

       案例REG_year:
          返回REG_year;

       案例REG_MONTH:
          返回reg_month;

       案例REG_DATE:
          返回reg_date;

       案例REG_Hour:
          返回REG_Hours;

       案例REG_MINUTE:
          返回REG_MINUTES;

       案例REG_ST秒:
          返回REG_seconds;

       案例REG_ALARM_year:
          返回reg_alarm_year;

       案例REG_ALARM_MONTH:
          返回reg_alarm_month;

       案例REG_ALARM_DATE:
          返回reg_alarm_date;

       病例REG_ALARM_Hour:
          返回REG_ALARM_Hours;

       病例REG_ALARM_MINUTE:
          返回REG_ALARM_MINUTES;

       病例REG_ALARM_seconds:
          返回REG_ALARM_seconds;

       默认:
           返回0;
   }
}


/* I2C写入寄存器功能
 *
 */
int REG_WRITE注册(REG_SUBADDR_TYPE子地址,int值)

   交换机(subaddr)
   {
      案例REG_BATTERY_LOW:
         返回false;

      案例REG_BATTERY_HIGH:
         返回false;

      案例REG_SW_VERSION:
         返回false;

      案例REG_year:
         REG_year =值;
         返回true;

      案例REG_MONTH:
         REG_MONTH =值;
         返回true;

      案例REG_DATE:
         REG_DATE =值;
         返回true;

      案例REG_Hour:
         REG_Hours =值;
         返回true;

      案例REG_MINUTE:
         REG_MINUTES =值;
         返回true;

      案例REG_ST秒:
         REG_seconds =值;
         返回true;

      案例REG_ALARM_year:
         REG_ALARM_year =值;
         返回true;

      案例REG_ALARM_MONTH:
         REG_ALARM_MONTH =值;
         返回true;

      案例REG_ALARM_DATE:
         REG_ALARM_DATE =值;
         返回true;

      病例REG_ALARM_Hour:
         REG_ALARM_Hours =值;
         返回true;

      病例REG_ALARM_MINUTE:
         REG_ALARM_MINUTES =值;
         返回true;

      病例REG_ALARM_seconds:
         REG_ALARM_seconds =值;
         返回true;

      默认:
         返回false;
   }
}


/*确定组中的下一个I2C寄存器
 *
 */
REG_SUBADDR_TYPE REG_INC_REGISTER (REG_SUBADDR_TYPE子地址)

   交换机(subaddr)
   {
      案例0x00:RETURN (REG_SUBADDR_TYPE)(0x03); //保留
      案例0x01:RETURN (REG_SUBADDR_TYPE)(0x03); //保留
      案例0x02:RETURN (REG_SUBADDR_TYPE)(0x03); //保留

      案例0x03:返回(REG_SUBADDR_TYPE)(0x04); //电池电量高
      案例0x04:返回(REG_SUBADDR_TYPE)(0x03); //电池电量低

      案例0x05:RETURN (REG_SUBADDR_TYPE)(0x05); // FW版本

      案例0x06:RETURN (REG_SUBADDR_TYPE)(0x07); //年
      案例0x07:RETURN (REG_SUBADDR_TYPE)(0x08); //月
      案例0x08:RETURN (REG_SUBADDR_TYPE)(0x09); //日期
      案例0x09:RETURN (REG_SUBADDR_TYPE)(0x0A); // Hour
      案例0x0A:RETURN (REG_SUBADDR_TYPE)(0x0B); //分钟
      案例0x0B:RETURN (REG_SUBADDR_TYPE)(0x06); //第二个

      案例0x0C:RETURN (REG_SUBADDR_TYPE)(0x0D); // Alarm_Year
      案例0x0D:REG_SUBADDR_TYPE (0x0E); // Alarm_Month
      案例0x0E:REG_SUBADDR_TYPE (0x0F); // Alarm_Date
      案例0x0F:REG_SUBADDR_TYPE (0x10);   // Alarm_Hour
      案例0x10:REG_SUBADDR_TYPE (0x11);   // Alarm_Minute
      案例0x11:RETURN (REG_SUBADDR_TYPE)(0x0C);   // Alarm_Second
    }
    RETURN(REG_SUBADDR_TYPE)(子地址); /*否则我们将返回相同的寄存器! */
}



  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好,Mike:

    快速浏览一下您的代码,我看不到任何引人注目的内容。 您能否确定您从主注册地址收到有效的注册地址? 如果不是,则得到0是有意义的,因为在REG_READ_REGISTER中,如果它通过switch语句,则它始终返回0。 也许作为一个快速的测试,改变它返回0xbeef,看看你是否得到了它。 这至少表明您的方向是IIC_REG_ADDRESS由于某种原因而没有正确的值。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好,Chris -非常感谢您的反馈。 我尝试将值0xAA作为默认返回值,但仍返回0x00。 我正在Pi 3上使用基本Python脚本执行I2C读取测试,它的I2C运行频率为100kHz。 示波器上的波形看起来都很好(每个板上的上拉波形都很低,大约为3k3)。

    我一直在用每个寄存器的离散读/写操作来测试它,例如:-
    YE = i2cBus.read_byte_data (MSP430_ADDR,REG_ALARM_YEY)

    但是,如果我一次在Pi上对所有寄存器执行块读取,它始终工作(我运行了5000块读取测试,所有测试都正常):-
    data_buffer = i2cbus.read_i2c_block_data (MSP430_ADDR,REG_STATUS,18)

    我认为I2C块读取使用重复启动,因此我需要看看接下来我处理停止中断的情况... :-)

    非常感谢您的帮助。

    谢谢,
    Mike。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好,Chris -非常感谢您抽出时间查看我的代码。
    我尝试了您的建议,发现它从未获得默认值(我将其设置为0xAA), 这让我运行了一些类似的测试,我正在研究停止中断的处理,因为我发现如果我使用块一次读取所有寄存器,读取它就能工作100 %。 我认为在处理停止中断时,0x00可能会在我设置IIC_REG_ADDRESS = 0x00;时发出蠕动。 块读取使用重复的启动,因此只生成1个停止中断,而离散读取将始终生成停止。
    谢谢,
    Mike。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    是的,我想你已经明白了。 在收到停止命令时重置寄存器地址肯定会导致错误行为。 我认为您根本不需要重置注册地址。 从设备在多个事务(例如EEPROM)之间跟踪地址的情况并不少见。 您可能会读取特定地址,但随后通常会有一个读取电流,该电流可跟踪上次访问的内存位置,即使是跨事务也是如此。 主中继器将只执行无地址(SO 1字节)的读取,而EEPROM将只返回下一个数据字节。 我想你可以把所有的东西都去掉,然后确保在开始时将寄存器初始化为有效的地址。 如果指针到达有效地址范围的末尾,您可以让它回回到起始处(不是我最喜欢的解决方案),或者直接回复请求。 祝您好运!