Я использую STM32L1 на плате Nucleo-L152RE. У меня есть устройства, которыми я управляю через последовательный порт, работающие на довольно высоких скоростях, поэтому я пытаюсь включить DMA на USART. С помощью приведенного ниже кода я могу запустить один Rx DMA, но второй, который я запускаю из ISR, никогда не завершается:
void uart_receive_dma() {
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = UART_PACKET_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_Init(uart_rx_dma_channel[handler->uart_index], &DMA_InitStructure);
/* RX */
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
}
void DMA1_Channel5_IRQHandler(void){
DMA_ClearITPendingBit(DMA1_IT_TC5);
uart_receive_dma();
}
int main(void) {
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
USART_Configuration();
/* First Rx, works and MA1_Channel5_IRQHandler gets called
uart_receive_dma();
while(1);
}
Первая передача по DMA работает хорошо, поэтому я предполагаю, что я что-то не очищаю перед запуском второй, но я не могу понять, что это такое.
В зависимости от каких-то внешних условий реальный код не всегда перезапускает передачу DMA из DMA1_Channel5_IRQHandler, а перезапускает ее откуда-то еще, поэтому я не могу использовать DMA в циклическом режиме.
Чтобы начать другую транзакцию DMA, вы должны запрограммировать длину транзакции. Его можно запрограммировать только при отключенном канале DMA. Итак, в вашем случае код может выглядеть так:
void DMA1_Channel5_IRQHandler(void) {
DMA_ClearITPendingBit(DMA1_IT_TC5);
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA1_Channel5->CNDTR = UART_PACKET_SIZE; // <--- transaction length
DMA_Cmd(DMA1_Channel5, ENABLE);
}
Или вы можете использовать свою функцию uart_receive_dma(), но вы должны отключить канал DMA перед ее вызовом.
void DMA1_Channel5_IRQHandler(void) {
DMA_ClearITPendingBit(DMA1_IT_TC5);
DMA_Cmd(DMA1_Channel5, DISABLE);
uart_receive_dma();
}
Второй вариант будет делать то же самое, что и первый, но займет гораздо больше времени.