I'm trying to rewrite the I2C gyro (MPU6050) read operation of an STM32F0xx based flight controller (Silverware) to partially TIM_Update/DMA based I2C readout for accurate looptime reasons (the project is NOT my work, I'm just trying to improve the codebase where possible).
This works, but only if I keep reading only the gyro and stick to DMA. For altitude hold however I want to read out the barometer in mainloop as well and hope to stay under TIM_Update refresh TIM_Period. What I did was generate a TIM_Update event, and in the TIM_Update interrupt handler, start a fresh I2C DMA read process for 14 bytes, and wait for DMA_TC. In DMA_TC handler I process the new gyro values and trigger mainloop.
The I2C hw readout lib that comes with the package guards for I2C jams by checking I2C_FLAG_BUSY before commencing a new I2C read operation (in the i2c sendheader function that is shared with all i2c read operations).
The problem is: this works if I stick to non-DMA I2C and for DMA-only I2C, but as soon as gyro-DMA is done and I start a new hw_i2c_readdata for 1 byte I end up in an endless I2C_FLAG_BUSY loop that only ends because of a timeout.
I have tried every combination of disabling I2C DMA and clearing pending DMA TC/IC2 TC bits but nothing helps:
// Handle I2C TC
DMA_ClearFlag( DMA1_FLAG_GL3 );
DMA_Cmd( DMA1_Channel3, DISABLE );
I2C_DMACmd( I2C1, I2C_DMAReq_Rx, DISABLE );
// RCC->APB1ENR |= (1UL << 21);
// I2C1->CR1 |= 0x8000;
// I2C1->CR1 &= (uint16_t) ~((uint16_t) 0x8000);
// while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == SET);
// while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET);
// while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET);
// TIM_SetCounter( TIM17, 0 );
// TIM_Cmd( TIM17, ENABLE );
sixaxis_read(1); // 1 = DMA mainloop
// I2C_GenerateSTOP(I2C1, ENABLE);
liberror = 0;
i2c_readreg(0x76, 0x08); <- This 'normal' I2C read is pestered with I2C_FLAG_BUSY timeouts.
// TIM_Cmd( TIM17, ENABLE );
liberror is supposed to be 0 all times, if it's nonzero, some sort of I2C BUSY/TC/TXE timeout has occured. I've inserted the liberror=0 for debug reasons, to prevent the code from bailing out on i2c problems and to have it available just before i2c_readreg while it contains last iteration errors.
The DMA setup routine looks like this:
TIM_ClearITPendingBit( TIM17, TIM_IT_Update );
DMA1_Channel3->CNDTR = 14;
DMA_Cmd( DMA1_Channel3, ENABLE );
I2C_DMACmd( I2C1, I2C_DMAReq_Rx, ENABLE );
hw_i2c_sendheader(SOFTI2C_GYRO_ADDRESS, 59 , 1 );
//send restart + readaddress
I2C_TransferHandling(I2C1, (SOFTI2C_GYRO_ADDRESS)<<1 , 14, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);
As can be seen, DMA CNDTR is set to 14 and I2C_TransferHandling asks for 14 bytes. I expect everyting to be done on DMA TC and I2C bus to be available for anything if I disable DMA Channel3 and I2C_DMAReq_Rx?
As can be seen, the project is based on Std Periperal lib.
Hope there's enough information to start discussing the problem, if not please let me know.
I deliberately didn't include the DMA /I2C initialisation because they both work seperately. It's only the transition from DMA to normal that's not working?