A few posts before I wrote about about the project “Campus Door System “. In our real time environment part of the project we are using the miniMODUL-167 which is equipt with an RTC-8583.
For I²C < -> RTC interfacing we are using sources which can be downloaded from the Phytec ftp servers. For the whole project we have been using this source without problems 🙂
After rolling out the system to one of our customers a few weeks later the system suddenly stopped, only a reset could clear the situation. Our in-house systems have never shown this kind of symptoms. So what the hell was going on?
After some days I found out that the problem might be located on the RTC part. After the crash the RTC delivers such funny things like 56 h 78 m 99 s , lol, ok so the RTC counters only compare for equals thats for sure :-). But what part of our code is setting the RTC ?
To make it short, the problem was a combination of our system architecture and the way the used I²C library is working.
Because the I²C library has a low level layer which is responsible for generating the bit pattern of the I²C data, this part of the code is prone to wrong designed interrupt architecture.
Sample of the low level I²C:
/****************************************************************************/ /* sends one bit to I2C-bus, also used for sending ACKNOWLEDGE condition */ void I2CSendBit(BYTE State) { // I2CDelay(); I2CPutSCL(0); /* setup SCL (redundant) */ // I2CDelay(); I2CPutSDA(State); /* setup SDA */ // I2CDelay(); I2CPutSCL(1); /* enable SDA writing */ // I2CDelay(); /* let SCL get stabilized High */ while(!I2CGetSCL()); /* wait until SCL-line is released */ // I2CDelay(); I2CPutSCL(0); /* disable SDA writing (SDA is now */ /* clocked) */ // I2CDelay(); I2CPutSDA(1); /* release SDA */ } /****************************************************************************/ /* set SCL-line to desired state */ void I2CPutSCL(BYTE State) { if(!State) { I2C_PORT &= ~I2C_SCL_MSK; /* set SCL-Line to ZERO */ #ifdef __C166__ I2C_PORT_DIR |= I2C_SCL_MSK; /* set SCL-Pin for OUTPUT Mode */ #endif /* enable output AFTER setting of */ /* correct level to avoid '1'-level */ /* spikes */ } else { #ifdef __C166__ I2C_PORT_DIR &= ~I2C_SCL_MSK; /* set SCL-Pin for INPUT Mode */ #endif /* this is possible because of the */ /* external pull-up resistor ! */ /* set SCL-line to ONE */ I2C_PORT |= I2C_SCL_MSK; } } /****************************************************************************/ /* read state of SCL-line */ /* WARNING: This function should be used only if SCL-line is in High-level !*/ /* (This function is for used only for realizing the Slow-Down-Mode) */ BYTE I2CGetSCL(void) { #ifdef __C166__ I2C_PORT_DIR &= ~I2C_SCL_MSK; /* set SCL-Pin for INPUT Mode */ #endif if(I2C_PORT&I2C_SCL_MSK) return(1); /* read SCL-line status */ else return(0); }
This is exactly what happend. In-house we have only a certain amount of readers connected to the external buses of the access system. But our customer has over 100 readers installed. So the interrupt load was at a level where the situation could arise that at the moment where the I²C bit pattern is generated a few interrupts suspended the pattern generation. This leads in the worst case to a write instead of a read flag 🙂
A quick fix was to globally disable interrupts in I²C write situations or a better solution would be to move the accourding code to a high priojority interrupt.
I’m also writing this because some of my colleagues also use this lib – and yes they also do not read manuals 🙂
have fun
Mario