2018-02-22 01:48 PM
Hi!
I'm trying to use the SSD1306 (oled screen) with the STM32F091RC and I2C interface, I managed to integrate the library U8G2 (
https://github.com/olikraus/u8g2/wiki
) in Eclipse with GNU MCU Eclipse and the setup provided by CubeMX for I2C. With the 'normal' transmission routines HAL_I2C_Master_Transmit() it works normal, I can draw things there.But when I try to replace it by HAL_I2C_Master_Transmit_DMA(), seems to be some looses between the DMA and the I2C.
If I run this with the debugging interface, put a breakpoint in the HAL_I2C_Master_Transmit_DMA() instruction, and look with the scope, I get what is supposed to be each time (because there is a time in between each iteration while I hit the button again), but if I let him run free while I record with the scope again, the received chain is not the same, many packages are lost. I'm testing this with the initializing chain and only in the 'command' section of the transmission, to have it a bit more under control.
Seems to me like the DMA is not waiting for the transaction to finish on the I2C, instead he sends the next value to the interface while he is not ready, it's this possible? any other ideas?
attached are main.c,
stm32f0xx_hal_msp.c,
stm32f0xx_it.c
and the file from u8g2 where the communications occurs :
u8x8_cad.c
the init chain is:
00 AE
00 D500 8000 A800 3F00 D300 0000 4000 8D00 1400 2000 00 00 A100 C800 DA00 1200 8100 CF00 D900 F100 DB00 4000 2E00 A400 A600 AF00 10...
when I use HAL_I2C_Master_Transmit(), I can see it all in the scope, if I use HAL_I2C_Master_Transmit_DMA() instead, I will only see:
00 00
00 A6
but this also depends on what is happening on main, with a more complicated setup (the rest of the peripherals I want to use for the application, the data is different.
#i2c-dma-ssd1306Solved! Go to Solution.
2018-04-02 10:34 AM
Solved!
Seems like the code generated by CubeMX needs an interrupt when DMA is in the middle, then I needed to tell to CubeMX to generate the code for that interrupt and wait until the data transmission is completed, the relevant part of the code seems like this:
This is part of the file u8x8_cad.c:
static void u8x8_i2c_data_transfer(uint8_t arg_int, void *arg_ptr){ uint8_t buffer[arg_int]; uint8_t *ptr = arg_ptr; buffer[0] = 0x40; datatransmitted = 0;for (int i = 1; i <= arg_int; i++) buffer[i] = *(ptr++);
// HAL_I2C_Master_Transmit(&hi2c1, 0x78, (uint8_t *)buffer, arg_int, HAL_MAX_DELAY); HAL_I2C_Master_Transmit_DMA(&hi2c1, 0x78, buffer, arg_int +1); while (!datatransmitted);}uint8_t u8x8_cad_ssd13xx_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{ switch(msg) { case U8X8_MSG_CAD_SEND_CMD: case U8X8_MSG_CAD_SEND_ARG: ; // esto es una declaracion vacia, para que el compilador permita la declaracion de la variable despues uint8_t buffer[2]; buffer[0] = 0x00; buffer[1] = arg_int; datatransmitted = 0;// HAL_I2C_Master_Transmit(&hi2c1, 0x78, (uint8_t*) buffer, 2, HAL_MAX_DELAY);
HAL_I2C_Master_Transmit_DMA(&hi2c1, 0x78, buffer , 2); while (!datatransmitted); break; case U8X8_MSG_CAD_SEND_DATA: u8x8_i2c_data_transfer(arg_int, arg_ptr); break; default: return 0; } return 1;}datatransmitted is _Bool declared in main.c, then when the transmission is successful datatransmitted is set:
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{ datatransmitted = 1;}
maybe is more 'elegant' (or maybe is even better) if I can check for the ACK of the I2C transmission... but this way it works!
2018-04-02 10:34 AM
Solved!
Seems like the code generated by CubeMX needs an interrupt when DMA is in the middle, then I needed to tell to CubeMX to generate the code for that interrupt and wait until the data transmission is completed, the relevant part of the code seems like this:
This is part of the file u8x8_cad.c:
static void u8x8_i2c_data_transfer(uint8_t arg_int, void *arg_ptr){ uint8_t buffer[arg_int]; uint8_t *ptr = arg_ptr; buffer[0] = 0x40; datatransmitted = 0;for (int i = 1; i <= arg_int; i++) buffer[i] = *(ptr++);
// HAL_I2C_Master_Transmit(&hi2c1, 0x78, (uint8_t *)buffer, arg_int, HAL_MAX_DELAY); HAL_I2C_Master_Transmit_DMA(&hi2c1, 0x78, buffer, arg_int +1); while (!datatransmitted);}uint8_t u8x8_cad_ssd13xx_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{ switch(msg) { case U8X8_MSG_CAD_SEND_CMD: case U8X8_MSG_CAD_SEND_ARG: ; // esto es una declaracion vacia, para que el compilador permita la declaracion de la variable despues uint8_t buffer[2]; buffer[0] = 0x00; buffer[1] = arg_int; datatransmitted = 0;// HAL_I2C_Master_Transmit(&hi2c1, 0x78, (uint8_t*) buffer, 2, HAL_MAX_DELAY);
HAL_I2C_Master_Transmit_DMA(&hi2c1, 0x78, buffer , 2); while (!datatransmitted); break; case U8X8_MSG_CAD_SEND_DATA: u8x8_i2c_data_transfer(arg_int, arg_ptr); break; default: return 0; } return 1;}datatransmitted is _Bool declared in main.c, then when the transmission is successful datatransmitted is set:
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{ datatransmitted = 1;}
maybe is more 'elegant' (or maybe is even better) if I can check for the ACK of the I2C transmission... but this way it works!
2018-04-21 11:29 AM
I didn't do to much more to this code, but after a while I realized this is a non efficient use of the DMA. I'm sending the data to DMA so it can send the data to I2C, here:
HAL_I2C_Master_Transmit_DMA(&hi2c1, 0x78, buffer , 2); (this one has only two bytes but the other is a variable width)
and wait for the I2C to finish the transmission:
while (!datatransmitted);
in this moment the core is making nothing! should be a better way to do this without the need to wait for it, I was thinking about but don't have any solution, anybody knows a better way?