2024-08-30 08:27 AM - edited 2024-08-30 09:12 AM
Hello,
I am trying to implement the following: a timer (tim3) is set to PWM generation, outputting a pulse periodically. Its TRGO triggers the DMA channel to request I2C to initiate a read transaction, read 8 bytes and generate an event interrupt (TC probably) to process these data bytes. On the next PWM pulse the next 8 bytes should be read. Data should be put to a circular buffer (ideally 8 bytes long, so that data is just overwritten into the same memory buffer. Cycle should continue indefinitely.
I feel like this scenario should be implementable, however I don't seem to grasp how to set it up correctly. I have the timer generating pulses correctly, but no tinkering with GPDMA and I2C settings yields a working procedure, at best I manage to make I2C to trigger, but only read 2 bytes once and then not reacting to consecutive pulses. I don't ask to do my job for me, but at least could you confirm that it is doable and outline the correct way to set it up? Thx.
Here is how everything is set up now (pics attached) and how I start the cycle in the code:
2024-08-30 09:51 AM - edited 2024-08-30 10:00 AM
After some meditating over the reference manual, I got an idea. I think, to initiate a new transaction, I need to update I2C CR2 register each time. So, I think I need to make a linked list DMA queue, with a node writing something into CR2, and the next node to actually request Rx. Does it sound correct?
2024-09-01 03:56 AM
According to AN5593:
A second issue is that, If you want to perform a DMA read from the I2C Bus to memory, you probably want to enable increment address on the destination.
2024-09-02 09:36 AM
Is there any appnote or example that uses EVC and RX requests to control I2C with automatic transaction handling? I couldn't find any, all I found is transaction initiated from code, and only moving data via DMA.
2024-09-02 05:27 PM - edited 2024-09-02 07:06 PM
Oh not at all, not at all. You're very welcome.
2024-09-18 12:52 AM - edited 2024-09-18 12:54 AM
Hi. I have a similar project, where the DMA needs to change the BSRR GPIO register, on TIM15 TRGO.
I almost got it, but the DMA does not get enabled by the timer triggers.
There is a single TC event from the DMA that gets triggered, and that's it.
Only with a workaround, which re-enables the DMA on TC (transaction complete) event, does the project work.
without it, the DMA does not do any work.
with it, the DMA is triggered by TIM15 properly and toggles the GPIOs properly.
Here is the DMA init code
static void MX_GPDMA1_Init(void)
{
/* USER CODE BEGIN GPDMA1_Init 0 */
/* USER CODE END GPDMA1_Init 0 */
/* Peripheral clock enable */
__HAL_RCC_GPDMA1_CLK_ENABLE();
/* GPDMA1 interrupt Init */
#ifdef DMA_INT_EN
HAL_NVIC_SetPriority(GPDMA1_Channel4_IRQn, 14, 14);
HAL_NVIC_EnableIRQ(GPDMA1_Channel4_IRQn);
HAL_NVIC_SetPriority(GPDMA1_Channel5_IRQn, 13, 13);
HAL_NVIC_EnableIRQ(GPDMA1_Channel5_IRQn);
#endif
/* USER CODE BEGIN GPDMA1_Init 1 */
/* USER CODE END GPDMA1_Init 1 */
handle_GPDMA1_Channel5.Instance = GPDMA1_Channel5;
handle_GPDMA1_Channel5.InitLinkedList.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
handle_GPDMA1_Channel5.InitLinkedList.LinkStepMode = DMA_LSM_1LINK_EXECUTION;
handle_GPDMA1_Channel5.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel5.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel5.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel5) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel5, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
handle_GPDMA1_Channel4.Instance = GPDMA1_Channel4;
handle_GPDMA1_Channel4.InitLinkedList.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
handle_GPDMA1_Channel4.InitLinkedList.LinkStepMode = DMA_LSM_1LINK_EXECUTION;
handle_GPDMA1_Channel4.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel4.InitLinkedList.TransferEventMode = DMA_TCEM_EACH_LL_ITEM_TRANSFER;
handle_GPDMA1_Channel4.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel4) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel4, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN GPDMA1_Init 2 */
#ifdef DMA_INT_EN
// Register DMA callbacks
HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel4, HAL_DMA_XFER_CPLT_CB_ID, HAL_DMA_ConvCpltCallback);
HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel5, HAL_DMA_XFER_CPLT_CB_ID, HAL_DMA_ConvCpltCallback);
// HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel4, HAL_DMA_XFER_ERROR_CB_ID, HAL_DMA_ErrorCallback);
// HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel5, HAL_DMA_XFER_ERROR_CB_ID, HAL_DMA_ErrorCallback);
#endif
/* USER CODE END GPDMA1_Init 2 */
}
Here is the ISR with the workaround code
void HAL_DMA_ConvCpltCallback(DMA_HandleTypeDef *hdma)
{
// Workaround - re-enable DMA
// __HAL_DMA_ENABLE(hdma);
__HAL_DMA_ENABLE(&handle_GPDMA1_Channel4);
__HAL_DMA_ENABLE(&handle_GPDMA1_Channel5);
}
Here is TIM15 init code
static void MX_TIM15_Init(void)
{
/* USER CODE BEGIN TIM15_Init 0 */
/* USER CODE END TIM15_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM15_Init 1 */
/* USER CODE END TIM15_Init 1 */
htim15.Instance = TIM15;
htim15.Init.Prescaler = 49999;
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 799;
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.RepetitionCounter = 0;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim15) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM15_Init 2 */
// Enable the update DMA request
__HAL_TIM_ENABLE_DMA(&htim15, TIM_DMA_UPDATE);
// Enable the update interrupt (UIE)
#ifdef TIM_INT_EN
HAL_NVIC_SetPriority(TIM15_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM15_IRQn);
#endif
/* USER CODE END TIM15_Init 2 */
}
Here is the DMA queue code
HAL_StatusTypeDef MX_led_dma_queue_Config(DMA_QListTypeDef * p_led_dma_queue, const uint32_t dst_addr, uint16_t seq_len,
const uint32_t seq[])
{
HAL_StatusTypeDef ret = HAL_OK;
/* DMA node configuration declaration */
DMA_NodeConfTypeDef pNodeConfig = {0};
/* Set node configuration ################################################*/
pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
#ifdef USE_TIM15_AS_TRGO
pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_RISING;
pNodeConfig.TriggerConfig.TriggerSelection = GPDMA1_TRIGGER_TIM15_TRGO;
pNodeConfig.Init.Request = GPDMA1_REQUEST_TIM15_UP;
#else
pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
#endif
pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST; // Single burst per trigger
pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
pNodeConfig.Init.DestInc = DMA_DINC_FIXED;
pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
pNodeConfig.TriggerConfig.TriggerMode = DMA_TRIGM_SINGLE_BURST_TRANSFER;
pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
pNodeConfig.DstAddress = dst_addr;
pNodeConfig.DataSize = seq_len * sizeof(seq[0]);
if ( (node_idx+1) == MAX_NODES)
ret |= HAL_ERROR;
/* Build node */
pNodeConfig.SrcAddress = (uint32_t)seq;
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &dma_queue_node[node_idx]);
/* Insert node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(p_led_dma_queue, &dma_queue_node[node_idx]);
/* Define the first circular node of the queue */
ret |= HAL_DMAEx_List_SetCircularModeConfig(p_led_dma_queue, &dma_queue_node[node_idx]);
node_idx++;
return ret;
}
here is main()
int main(void)
{
/* USER CODE BEGIN 1 */
/* STM32U5xx HAL library initialization:
- Configure the Flash prefetch
- Configure the Systick to generate an interrupt each 1 msec
- Set NVIC Group Priority to 3
- Low Level Initialization
*/
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
#warning uncomment to have logs back
// SWO_Init();
/* USER CODE END Init */
/* Configure the System Power */
SystemPower_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_GPDMA1_Init();
/* Configure the system clock */
SystemClock_Config();
MX_ICACHE_Init();
#ifdef USE_TIM15_AS_TRGO
MX_TIM15_Init();
#endif
/* USER CODE BEGIN 2 */
printf("hello\n");
fflush(stdout);
if ( MX_led_dma_queue_Config(&led_dma_queue_port_0, LED1_GPIO_BSRR, BUFFER_SIZE, led1_toggle_buffer) != HAL_OK )
{
Error_Handler();
}
if ( MX_led_dma_queue_Config(&led_dma_queue_port_1, LED2_GPIO_BSRR, BUFFER_SIZE, led2_toggle_buffer) != HAL_OK )
{
Error_Handler();
}
if ( HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel4, &led_dma_queue_port_0) != HAL_OK )
{
Error_Handler();
}
if ( HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel5, &led_dma_queue_port_1) != HAL_OK )
{
Error_Handler();
}
/* -1- Enable GPIO Clock (to be able to program the configuration registers) */
LED1_GPIO_CLK_ENABLE();
LED2_GPIO_CLK_ENABLE();
/* -2- Configure IO in output push-pull mode to drive external LEDs */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = LED1_PIN;
HAL_GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LED2_PIN;
HAL_GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStruct);
HAL_Delay(100);
start_dma_transfer();
#ifdef USE_TIM15_AS_TRGO
#ifdef TIM_INT_EN
if (HAL_TIM_Base_Start_IT(&htim15) != HAL_OK)
#else
if (HAL_TIM_Base_Start(&htim15) != HAL_OK)
#endif
{
Error_Handler();
}
#endif
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_PIN);
/* Insert delay 100 ms */
// HAL_Delay(100);
// HAL_GPIO_TogglePin(LED2_GPIO_PORT, LED2_PIN);
/* Insert delay 100 ms */
HAL_Delay(1);
// start_dma_transfer();
}
/* USER CODE END 3 */
}