Перейти к содержанию
    

stm32 DMA в режиме DMA_Mode_Normal

здравствуйте.

столькнулся с тем что не понимаю как это работает.

настроенный dma канал читает данные с уарта в буффер озу в режиме DMA_Mode_Normal.

после заполнения буфера как и положено становится как вкопаный. указатель позиции соответствует размеру буфера.

как сказать ему чтоб он повторил действие. сброс указателя позиции и включение и выключение уарта и самого канала dma не приводит к началу записи в буфер.

он явно чтото друго ждет от меня

 

спасибо

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Обычно описанные действия помогают. Может проще использовать 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);
        }
    }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

спасибо. так у меня работает с кольцом. но хочу соптимизировать. я знаю что момент когда точно прийдет пакет и заню его длинну. поэтому чтоб не анализировать состояние кольца хочется перед приходом пакета оп уарт включить на оду запись буфера канал dma. я также знаю момент времени когла он уже пришел без прерываний. хочу обработать пакет, и сново поставить канал dma запись следующего.

данные приходят четко и синхронно - на этом хочу выйграть в коде. только вот перезапустить канал дма на следующую запись не получается :smile3046:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Понял. Я подобным образом работаю с 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);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

если в се переинициализировать - это косяг, на это время тратится... наверно мы не понимаем как правильно. для передатчика я пробывал - кажется достаточно по окончании передачи сбросить разрешение работы для канала и сразу установить его и он зановов выдает буфер в переферию...... с приемником также должно быть по идее

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

может тогда причина не в DMA, а в УАРТ? с другой периферией не пробовали?

ну мне же уарт нужен ;)

 

с полной выключением переинициализацией и включением работает - но это явно через жёппу. если так работает у stm работает dma - то он хуже чем я думал, но не думаю что там дураки сидять.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот здесь вроде что-то по теме. (последний пост)

Вроде всё логично: отключили DMA, сбросили адрес и количество байт, запустили DMA.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

коечто наковырял...

делать нада так;

1. выключить канал dma

2. записать в CNDTR размер буфера

3. включить dma

 

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

 

1a. выключить uart

1. выключить канал dma

2. записать в CNDTR размер буфера

2.a записать CCR то что писалось при инициализации

3. включить dma

3a. включить uart

 

возможно это ревизия кристала такая или еще чтото но поведение меняется если отладчик останавливате и потом пускает процессор. ктонибудб знает как эту 'фичу' помножить на ноль?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...