2018-06-04 06:59 AM
I'm trying to rewrite the I2C gyro (MPU6050) read operation of an STM32F0xx based flight controller (
https://github.com/silver13/BoldClash-BWHOOP-B-03
) 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
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
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
void DMA1_Channel2_3_IRQHandler(void){ DMA_ClearITPendingBit(DMA1_IT_TC3); I2C_ClearITPendingBit(I2C1, I2C_IT_TCI); 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.// mainloop();// 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:
void TIM17_IRQHandler(void)
{ 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?
#i2c-isues #dma