Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples of I2C DMA #41

Open
jonnor opened this issue Apr 20, 2024 · 2 comments
Open

Examples of I2C DMA #41

jonnor opened this issue Apr 20, 2024 · 2 comments

Comments

@jonnor
Copy link

jonnor commented Apr 20, 2024

Hi,
first of all: thank you for this fantastic repository. The build system setup is super pragmatic, easy to work with - and everything I have tried so far works nicely. And there is an impressive amount of examples.

I have one project with an accelerometer, where I would like to read at high rates (from the built-in FIFO). And it is also doing computationally intensive processing (some DSP and machine learning). So I would be very interested in using the DMA support to enable doing work on the CPU while the transfers are ongoing. I see that there is a DMA_CHANNEL_MAP_I2C* in the code. So there does seem to be support for this. There are DMA examples for the ADC for example, but I have not seen anything for I2C+DMA. Does anyone have such examples and/or tips&tricks? It is basically just receive that would benefit from DMA. SPI+DMA would also be relevant

@IOsetting
Copy link
Owner

I haven't tried I2C DMA, in my work I use timer to trigger I2C reading.
Have you tried the I2C DMA examples in Puya's SDK? https://github.com/OpenPuya/PY32F0xx_Firmware/tree/master/Projects/PY32F030-STK/Example_LL/I2C

@deividAlfa
Copy link
Contributor

deividAlfa commented Jul 2, 2024

An example sending a 128 Byte buffer to an I2C device using DMA:

example.c

#define I2C_ADDRESS 0x3C

#define SCL_Port        GPIOF
  #define SCL_Pin       LL_GPIO_PIN_1
  #define SCL_AF        LL_GPIO_AF12_I2C

#define SDA_Port        GPIOA
  #define SDA_Pin       LL_GPIO_PIN_7
  #define SDA_AF        LL_GPIO_AF12_I2C

volatile uint8_t dma_busy;
__attribute__((aligned(4))) uint8_t buffer[128];

const LL_DMA_InitTypeDef DMAInit = {
    .Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH,
    .Mode = LL_DMA_MODE_NORMAL,
    .PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT,
    .MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT,
    .PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE,
    .MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE,
    .Priority = LL_DMA_PRIORITY_HIGH,
};

void i2c_dma_init(void){
  LL_GPIO_Init(SCL_Port, &(LL_GPIO_InitTypeDef){ SCL_Pin, LL_GPIO_MODE_ALTERNATE, LL_GPIO_SPEED_FREQ_VERY_HIGH, LL_GPIO_OUTPUT_OPENDRAIN, LL_GPIO_PULL_NO, SCL_AF });
  LL_GPIO_Init(SDA_Port, &(LL_GPIO_InitTypeDef){ SDA_Pin, LL_GPIO_MODE_ALTERNATE, LL_GPIO_SPEED_FREQ_VERY_HIGH, LL_GPIO_OUTPUT_OPENDRAIN, LL_GPIO_PULL_NO, SDA_AF });
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
  LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SYSCFG);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
  LL_SYSCFG_SetDMARemap_CH1(LL_SYSCFG_DMA_MAP_I2C_TX);
  LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, (LL_DMA_InitTypeDef*)&DMAInit);
  LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_1, LL_I2C_DMA_GetRegAddr(I2C1));
  LL_I2C_DeInit(I2C1);
  LL_I2C_Init(I2C1, &(LL_I2C_InitTypeDef){ 400000, LL_I2C_DUTYCYCLE_2, 0, I2C_CR1_ACK }); //LL_I2C_DUTYCYCLE_16_9
  LL_I2C_DisableBitPOS(I2C1);
  LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
  NVIC_SetPriority(DMA1_Channel1_IRQn, 1);
  NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

void send_i2c_dma(void){
  while(dma_busy){ ; }
  LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer);
  LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, sizeof(buffer));
  LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);

  LL_I2C_GenerateStartCondition(I2C1);              // Start condition
  while(!LL_I2C_IsActiveFlag_SB(I2C1));            

  LL_I2C_TransmitData8(I2C1, I2C_ADDRESS);          // Device address
  while(!LL_I2C_IsActiveFlag_ADDR(I2C1));
  LL_I2C_ClearFlag_ADDR(I2C1);

  LL_I2C_TransmitData8(I2C1, modeData);             // Device memory register address (If applicable)
  while(!LL_I2C_IsActiveFlag_TXE(I2C1));

  dma_busy = 1;                                     // Set busy flag
  LL_I2C_EnableDMAReq_TX(I2C1);                     // Enable DMA request, transfer starts inmediately
}

py32f0xx_it.c

void DMA1_Channel1_IRQHandler(void){
  extern volatile uint8_t dma_busy;
  LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
  LL_DMA_ClearFlag_GI1(DMA1);
  dma_busy = 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants