На STM32F7 DMA, запускаемый таймерами APB1, не работает

У меня есть плата STM32F7 Disco с MCU STM32F723IEK. Попытка инициировать запрос DMA от таймера вызывает ошибку DMA, но только для таймеров из группы APB1 (от TIM2 до TIM7 и других), которые подключены к DMA1. То же самое с TIM1 и TIM8, которые подключены к DMA2, работает нормально. Ошибка проявляется в том, что флаг TEIFx устанавливается в соответствующем регистре DMA LISR или HISR, а DMA немедленно отключается после первой транзакции. Регистр NDTR уменьшается на единицу.

Согласно даташиту, ошибка TEIF может быть вызвана «ошибкой шины». Я понимаю это, например, пытается получить доступ к периферийному устройству, которое недоступно с шины DMA. Однако та же самая установка хорошо работает с DMA2 и TIM1 / TIM8, без изменения адреса DMA. Таким образом, проблема, похоже, связана с запросом DMA, а не с самой транзакцией данных. Учитывая, что для DMA1 определено много таймерных каналов, это, безусловно, должно работать.

Я пытался изменить настройки DMA, но это не имело значения. Соответствующая часть программы тестирования приведена ниже. Полная версия https://github.com/ak-hard/stm32-dma-tim/blob/master/main.c лишь немного больше и не имеет зависимостей, кроме заголовков устройств CMSIS и STM32.

Интересно, может ли кто-нибудь прокомментировать или воспроизвести эту проблему.

const struct
{
    TIM_TypeDef *tim;
    DMA_TypeDef *dma;
    DMA_Stream_TypeDef *stream;
    unsigned channel;
} CFG = {
// uncomment the needed combination below, only TIM1 and TIM8 work
//    TIM1, DMA2, DMA2_Stream5, 6
    TIM8, DMA2, DMA2_Stream1, 7
//    TIM2, DMA1, DMA1_Stream1, 3
//    TIM2, DMA1, DMA1_Stream7, 3
//    TIM3, DMA1, DMA1_Stream2, 5
//    TIM4, DMA1, DMA1_Stream6, 2
//    TIM5, DMA1, DMA1_Stream0, 6
//    TIM5, DMA1, DMA1_Stream6, 6
//    TIM6, DMA1, DMA1_Stream1, 7
//    TIM7, DMA1, DMA1_Stream2, 1
//    TIM7, DMA1, DMA1_Stream4, 1
};

enum
{
    DMA_SxCR_DIR_P2M = 0,
    DMA_SxCR_PSIZE_WORD = DMA_SxCR_PSIZE_1,
    DMA_SxCR_MSIZE_WORD = DMA_SxCR_MSIZE_1,
};

#define DMA_SxCR_CHSEL_NUM(ch) ((ch) << DMA_SxCR_CHSEL_Pos)

uint32_t buf;

void start(void)
{
    SysTick->LOAD = 0xffffffu;
    SysTick->VAL = 0;
    SysTick->CTRL = 5;

    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM8EN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM4EN | RCC_APB1ENR_TIM5EN
            | RCC_APB1ENR_TIM6EN | RCC_APB1ENR_TIM7EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMA2EN;

    LED_PORT->MODER   |= 1 << (2 * LED_PIN);
    LED_PORT->OSPEEDR |= 3 << (2 * LED_PIN); // fastest speed

    CFG.tim->CR1 |= TIM_CR1_ARPE;
    CFG.tim->ARR = 16;
    CFG.tim->PSC = 1000;
    CFG.tim->EGR = TIM_EGR_UG; // Generate Update Event to copy ARR to its shadow
    CFG.tim->DIER |= TIM_DIER_UDE;
    CFG.stream->CR |= DMA_SxCR_CHSEL_NUM(CFG.channel) | DMA_SxCR_DIR_P2M | DMA_SxCR_PSIZE_WORD | DMA_SxCR_MSIZE_WORD;
    CFG.stream->NDTR = 16;
    CFG.stream->PAR = (uint32_t) &GPIOA->IDR;
    CFG.stream->M0AR = (uint32_t) &buf;
    CFG.stream->CR |= DMA_SxCR_EN;
    CFG.tim->CR1 |= TIM_CR1_CEN;

    // wait until DMA state changes
    while (CFG.dma->LISR == 0 && CFG.dma->HISR == 0)
        delay_ms(1);

    // check for any TEIFx bits
    int error = (CFG.dma->LISR | CFG.dma->HISR) & 0x02080208;

    while (1)
    {
        LED_PORT->ODR ^= 1 << LED_PIN;
        delay_ms(error ? 100 : 500);
    }
}

person A.K.    schedule 26.07.2017    source источник
comment
Удалено - не люблю такого рода обсуждения.   -  person 0___________    schedule 26.07.2017


Ответы (1)


Раньше здесь был ответ, но его почему-то удалили. Но спасибо его автору.

Глядя на матрицу шин, становится ясно, что периферийная шина DMA1 только подключена к APB1. На самом деле это вообще не часть матрицы. Это, вероятно, означает, что DMA1 может обрабатывать только передачи от / к периферийным устройствам APB1. Поскольку GPIO является периферийным устройством AHB, он недоступен из DMA1. Это также должно относиться к другим периферийным устройствам APB2 (например, SPI1) и AHB (например, OTGFS). Обычно нет смысла обращаться к периферийным устройствам AHB или APB2 из DMA1, потому что их запросы не маршрутизируются в DMA1. Однако это может понадобиться для запутанных случаев, таких как GPIO по таймеру.

Я лично считаю, что этот момент можно было бы сделать более очевидным в документации.

person A.K.    schedule 26.07.2017
comment
Полностью согласен, что документация оставляет желать лучшего. У меня такая же проблема с STM32H743, но я получаю ее даже при передаче из памяти в память и не понимаю, как ее обойти. (В конечном итоге я хочу записать 16 бит в GPIOE->ODR, когда срабатывает событие сравнения из HRTIM - таймер работает и событие срабатывает, но возникает ошибка передачи, и я вижу такое же поведение при запуске из программного обеспечения.) Рисунок 1. Системная архитектура ... выглядит так, будто DMA1 может добраться практически до всего, поэтому я все еще чешу в затылке и просто слепо пробую разные вещи: / - person jacobq; 30.03.2019
comment
Ага! Оказывается, нужно быть осторожным, где расположена память. Когда я определил глобальный массив в своем коде, он был помещен в DTCM (0x20000000 - 0x2001FFFF). Эта статья помогла мне решить проблему: community.st.com/s/article/ - person jacobq; 30.03.2019