Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: stm32 DMA в режиме DMA_Mode_Normal
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
klen
здравствуйте.
столькнулся с тем что не понимаю как это работает.
настроенный dma канал читает данные с уарта в буффер озу в режиме DMA_Mode_Normal.
после заполнения буфера как и положено становится как вкопаный. указатель позиции соответствует размеру буфера.
как сказать ему чтоб он повторил действие. сброс указателя позиции и включение и выключение уарта и самого канала dma не приводит к началу записи в буфер.
он явно чтото друго ждет от меня

спасибо
prm
Обычно описанные действия помогают. Может проще использовать DMA в режиме записи по кольцу, организовав тем самым подобие двойного буфера?

В своем проекте использую FreeRTOS. Сделал отдельную задачу на прием информации по УАРТ. В обработчике прерывании от DMA в очередь событий (создаю отдельно) помещаю код возникшего события (буфер заполнен на половину, заполнен полностью, ошибка). В задаче приема вызываю xQueueReceive(..., ..., таймаут_ожидания_окончания_передачи). Если событие принято, обрабатываю данные буфера, запоминаю смещение (первая/вторая половина буфера, зависит от кода события). Если событие не принято (xQueueReceive завершилась по таймауту), определяю количество принятых байт с момента возникновения последнего события (знаю смещение в буфере, знаю сколько еще пришло DMA_GetCurrDataCounter(...)) и снова обрабатываю буфер.

Код
//============================================================================
/// Задача по приему данных от USART_DBG
//=============================================================================
void vDebugReceive(void *pvParameters)
{
    const portTickType  xTicksToWait = USART_DBG_RX_TIMEOUT_MS / portTICK_RATE_MS;
    uint8_t             event_code;
    uint16_t            last_data_cnt = USART_DBG_RX_BUF_SIZE;
    uint16_t            cnt = 0;
    uint8_t             offset = 0;

    //вечный цикл
    for(;;)
    {
        // ожидаю прихода события (данные считаны либо таймаут)
        if( xQueueReceive(dbg_rx_events, &event_code, xTicksToWait) == pdPASS )
        {
            //получил событие
            switch(event_code)
            {
                case DBG_RX_HALF:
                {
                    // заполнена первая половина приемного буфера
                    for (; offset < (USART_DBG_RX_BUF_SIZE+1)/2; ++offset)
                        xQueueSendToBack(dbg_rx_data, (const void*)&dbg_in_buf[offset],0);

                    last_data_cnt = (USART_DBG_RX_BUF_SIZE+1)/2;
                    break;
                }
                case DBG_RX_FULL:
                {
                    // заполнена вторая половина приемного буфера
                    for (; offset < USART_DBG_RX_BUF_SIZE; ++offset)
                        xQueueSendToBack(dbg_rx_data, (const void*)&dbg_in_buf[offset],0);

                    // смещение на начало буфера
                    offset = 0;
                    last_data_cnt = USART_DBG_RX_BUF_SIZE;
                    break;
                }
                case DBG_RX_ERR:
                {
                    // произошла ошибка при приеме данных. Заново включаем канал DMA
                    DMA_Cmd(USART_DBG_Rx_DMA_Ch, ENABLE);
                    offset = 0;
                    break;
                }
            }
        }
        else
        {
            // проверяю, может пришла часть данных
            if ( last_data_cnt != DMA_GetCurrDataCounter(USART_DBG_Rx_DMA_Ch))
            {

                // т.к. счетчик в DMA декрементируется, то вычитаем из last_data_cnt
                cnt = last_data_cnt - DMA_GetCurrDataCounter(USART_DBG_Rx_DMA_Ch);

                for (; cnt && offset < USART_DBG_RX_BUF_SIZE; ++offset, --cnt)
                    xQueueSendToBack(dbg_rx_data,(const void*)&dbg_in_buf[offset],0);

                //ОБНУЛЯТЬ offset НЕЛЬЗЯ!!! Должно генерироваться прерывание по заполнению буфера
            }

            last_data_cnt = DMA_GetCurrDataCounter(USART_DBG_Rx_DMA_Ch);
        }
    }
}
klen
спасибо. так у меня работает с кольцом. но хочу соптимизировать. я знаю что момент когда точно прийдет пакет и заню его длинну. поэтому чтоб не анализировать состояние кольца хочется перед приходом пакета оп уарт включить на оду запись буфера канал dma. я также знаю момент времени когла он уже пришел без прерываний. хочу обработать пакет, и сново поставить канал dma запись следующего.
данные приходят четко и синхронно - на этом хочу выйграть в коде. только вот перезапустить канал дма на следующую запись не получается smile3046.gif
prm
Понял. Я подобным образом работаю с DS18b20. Вот рабочий кусок кода(лишнее можно выбросить, я подстраховался):

Код
    USART_Cmd(USART_DS18B20, DISABLE);
    //USART_DS18B20
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate            = 115200;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART_DS18B20, &USART_InitStructure);

    // конфигурирую DMA
    DMA_InitTypeDef DMA_InitStructure;

    DMA_DeInit(USART_DS18B20_Rx_DMA_Ch);
    DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)&USART_DS18B20_DR_Base;
    DMA_InitStructure.DMA_MemoryBaseAddr        = (uint32_t)rx_data_buf;
    DMA_InitStructure.DMA_DIR                   = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize            = 1000;
    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_Priority              = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M                   = DMA_M2M_Disable;
    DMA_Init(USART_DS18B20_Rx_DMA_Ch, &DMA_InitStructure);

    DMA_DeInit(USART_DS18B20_Tx_DMA_Ch);
    DMA_InitStructure.DMA_PeripheralBaseAddr   = (uint32_t)&USART_DS18B20_DR_Base;
    DMA_InitStructure.DMA_MemoryBaseAddr       = (uint32_t)tx_data_buf;
    DMA_InitStructure.DMA_DIR                  = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize           = cnt;
    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_Priority             = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M                  = DMA_M2M_Disable;
    DMA_Init(USART_DS18B20_Tx_DMA_Ch, &DMA_InitStructure);

    USART_ClearFlag(USART_DBG,USART_IT_TC);
    USART_Cmd(USART_DS18B20, ENABLE);
    USART_DMACmd(USART_DS18B20,           USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
    DMA_ITConfig(USART_DS18B20_Tx_DMA_Ch, DMA_IT_TC | DMA_IT_TE,             ENABLE);
    DMA_ITConfig(USART_DS18B20_Rx_DMA_Ch, DMA_IT_HT | DMA_IT_TC | DMA_IT_TE, DISABLE);

    // запускаю передачу
    DMA_Cmd(USART_DS18B20_Rx_DMA_Ch, ENABLE);
    DMA_Cmd(USART_DS18B20_Tx_DMA_Ch, ENABLE);

    // засыпаю до завершения передачи

    USART_Cmd(USART_DS18B20, DISABLE);
    DMA_Cmd(USART_DS18B20_Rx_DMA_Ch, DISABLE);
    DMA_Cmd(USART_DS18B20_Tx_DMA_Ch, DISABLE);

    DMA_ClearITPendingBit(DMA1_IT_GL2);
    DMA_ClearITPendingBit(DMA1_IT_GL3);
klen
и так пробывал - работает. но меня сильно напрягает что ВСЕ нада выключить и снова ПРОИНИТИТЬ и ВКЛЮЧИТЬ - этож бред. на мой вггляд достаточно былобы установить позицию записи сбросить флаг завершения передачи как сигнал того что нужно писать заново .....

если в се переинициализировать - это косяг, на это время тратится... наверно мы не понимаем как правильно. для передатчика я пробывал - кажется достаточно по окончании передачи сбросить разрешение работы для канала и сразу установить его и он зановов выдает буфер в переферию...... с приемником также должно быть по идее
prm
может тогда причина не в DMA, а в УАРТ? с другой периферией не пробовали?
klen
Цитата(prm @ Jul 6 2011, 12:38) *
может тогда причина не в DMA, а в УАРТ? с другой периферией не пробовали?

ну мне же уарт нужен wink.gif

с полной выключением переинициализацией и включением работает - но это явно через жёппу. если так работает у stm работает dma - то он хуже чем я думал, но не думаю что там дураки сидять.
AHTOXA
Вот здесь вроде что-то по теме. (последний пост)
Вроде всё логично: отключили DMA, сбросили адрес и количество байт, запустили DMA.
klen
коечто наковырял...
делать нада так;
1. выключить канал dma
2. записать в CNDTR размер буфера
3. включить dma

работает. может возникнуть вопрос а че я тут всем мозги парю? оказывается если по ходу работы отлачик остановится на точке прерывания - то! все отваливается. при отладке с остановами работаеттолько такой код:

1a. выключить uart
1. выключить канал dma
2. записать в CNDTR размер буфера
2.a записать CCR то что писалось при инициализации
3. включить dma
3a. включить uart

возможно это ревизия кристала такая или еще чтото но поведение меняется если отладчик останавливате и потом пускает процессор. ктонибудб знает как эту 'фичу' помножить на ноль?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.