Reinicie Rx USART + DMA en STM32L1

1

Estoy usando un STM32L1 en una placa Nucleo-L152RE. Tengo dispositivos que controlo a través de una serie que funciona a velocidades bastante altas, por lo que estoy tratando de habilitar DMA en el USART. Con el siguiente código, puedo lanzar un Rx DMA, pero el segundo, que comencé desde el ISR nunca se completa:

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);
}

La primera transferencia de DMA funciona bien, así que supongo que no estoy limpiando algo antes de iniciar la segunda, pero no puedo averiguar de qué se trata.

Dependiendo de algunas condiciones externas, el código real no siempre reinicia la transferencia DMA desde DMA1_Channel5_IRQHandler sino que la reinicia desde otro lugar, por eso no puedo usar el DMA en modo circular.

    
pregunta Genís

1 respuesta

5

Para iniciar otra transacción DMA debe programar una duración de transacción. Solo se puede programar cuando un canal DMA está deshabilitado. Entonces, en su caso, el código podría verse así:

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);
}

O puede usar su función uart_receive_dma (), pero debe desactivar un canal DMA antes de llamarlo.

void DMA1_Channel5_IRQHandler(void) { 
    DMA_ClearITPendingBit(DMA1_IT_TC5);
    DMA_Cmd(DMA1_Channel5, DISABLE);
    uart_receive_dma();
}

La segunda variante hará lo mismo que la primera, pero tomará mucho más tiempo.

    
respondido por el LonelyWolf

Lea otras preguntas en las etiquetas