2012-06-15 09:08 AM
Hello guys,
I've got the following problem: I'm trying to read a register of a gyroscope with I²C. Everything works fine as long as I'm using several delays during the I²C communication. This wasn't a problem until yet, where I noticed that exact those delays lock the CPU completely down. So now I need to figure out why this code keeps freezing without delays: uint8_t I2C_Read_Byte(uint8_t __slave_addr, uint8_t __reg_addr) { uint8_t __retval; while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, __slave_addr, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, __reg_addr); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) ; I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, __slave_addr, I2C_Direction_Receiver); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); Delay_us(57); /* freezing without delay */ while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) ; I2C_AcknowledgeConfig(I2C1, DISABLE); __retval = I2C_ReceiveData(I2C1); Delay_us(101); /* same here */ I2C_GenerateSTOP(I2C1, ENABLE); Delay_us(111); /* and here */ I2C_AcknowledgeConfig(I2C1, ENABLE); return __retval; } I'd be very greatful for any help! Best regards and thanks in advance, Wolfram2012-06-16 10:26 AM
It would certainly be helpful to see the source code of your
Delay_us()
function, if you think it is the source of the problem. However, from your description, this is hard to believe. Existing code stops to work only if other/new code interferes with it's resources. If it is timer-based, I would look there first. But maybe it's something trivial like a stack overflow...2012-06-16 10:56 AM
2012-06-16 11:13 AM
------------------ Okay, seing that touching the ''edit'' button is a no-go here, I won't touch it anymore --------------------
Thanks for the reply! My Delay_us funtion is as simple as possible: just a while loop that iterates a variable to ~6000 (I think). But I think I should add some explanation. My code is thought to do the following: Read several I²C device registers and do some calculation. I'm reading the registers everytime a timer running with 200Hz fires an update interrupt. The problem is that the delays in the I²C communication make it impossible to read the devices registers with a frequency any higher than 200Hz. So the CPU is at its limit, and handling a RS232 interrupt interrupts the timer. So that I'm not able anymore to read data from several sensors with 200Hz. So when a RS232 interrupt fires, the timer interrupt is delayed and so also the reading of the sensors. But it is essential for my calculations that the frequency at wich the sensor data is updates is exactly known. I had this problem with I²C from the beginning on, and I was unable to solve it. Basically I should be able to read the sensor with ~1kHz, then the RS232 interrupt wouldn't delay the timer interrupt and everything would be fine. So long story in short, I really need to get rid of the delays in order to read sensor data with exactly 200Hz. Best regards, Wolfram2012-06-16 12:47 PM
Couple of thoughts:
Having unbounded while() loops is about the most fatal thing you can do, and provides no avenue for error detection or recovery. I know ST does this in the examples, but for production code you need to be more careful. You don't want to be doing this I2C code under interrupt. If you want to use interrupts, use a state machine and event drive it.2012-06-17 04:54 AM
Yeah I know, nested interrupts are also a no-go, but for testing purposes this is sufficient.
Any clues why my I²C code doesn't work without delays? Best regards, Wolfram2012-06-17 10:46 AM
Yeah I know, nested interrupts are also a no-go, but for testing purposes this is sufficient.
I was sure you know that, so I didn't comment on that ... And I'm sure you know you waste a tremendous amount of CPU performance in this loops ... I tried an easy solution for a display driver, that worked for me without problems. It uses the SysTick timer. Set it to a higher firing frequency, which defines your delay granularity - say, 10us to 50us. Define yourDelay()
routine to set a count, which is decremented in the SysTick interrupt. And in the SysTick interrupt, decrement those counter(s), and set a flag when it hits zero, which makes yourDelay()
function return to the caller. (Maybe I find this code somewhere - tomorrow...). Of course, you can use another timer, but in this poject I had SysTick at an appropriate rate anyway.
Any clues why my I²C code doesn't work without delays?
This might have to do with your sensor. Possibly it chokes if you drive it too fast - just consult the datasheet. And, to make sure, observe the actual events on the I2C bus in this case, a scope is suggested.
2012-06-19 03:57 AM
That's a relly good idea, thanks! :)
None of the 2 devices I'm using needs a delay in the I²C communication. Both sensors need time to sample new values, but the registers may be read at any time. Actually I start to believe that this is some kind of bug, as its only occuring when I'm trying to read data from a device. Writing to registers - no matter to which of the several I²C devices I'm using - works flawlessly without any delay. I read in the errata sheet that for the mcu I'm using (STM32F103VE) there is a bug in the I²C communication that may occur if you read 1 or 2 bytes from a peripheral. But - as it was described in the errata sheet - this errors should occur elsewhere, and not where my code keeps freezing. I really don't have any clue what is going on here ... =/ Best regards, Wolfram2012-06-19 04:32 AM
The I2C bus is a beast...
I would always check the bus - I guess the sensor (I2C slave) dos not ACK the read access. One must keep in mind that the ST examples are just examples, that's also true for the I2C ones. To go through the send/receive states with while loops, only checking for completion flags, is not fit for a industrial application. Sometimes you need some more error checking, and implement timeouts. If you had pointed out earlier to work with an stm32f10x, you might have been pointed to this bug. After reading the errata sheet, I decided to use available bit-banging code for some test code. Just look around, you are not the only F10x user frustrated by the I2C peripheral...2012-06-19 05:32 AM
Right then I'm going to check the I²C communication with a scope. I thought I2C_AcknowledgeConfig(I2C1, DISABLE); disables ack? What happens if I disable acknowledgement and the slave sends an ack?
When I first tried to get I2C working I failed. I then also switched to bit banging, which wasn't working too good. After all the error was a single misplaced line in then I²C configuration. I guess the solution won't be as simple here ... Best regards, Wolfram