STM32F103: странная проблема с DMA (исправлено, но не решено)

Пытаюсь заюзать DMA для пересылки данных из фиксированного массива в регистр, так же находящийся в памяти по фиксированному адресу (на него отображается шина SSD1963).

Ежели адрес источника данных не инкрементируется (т.е. чтение идёт всё время из одного и того же адреса), то всё ок - на экране видим однотонную заливку (на картинке ниже синий фон именно так и создан).

Если же адрес инкрементируется (в соответствии с флагом в соответствующем регистре контроллера DMA), то каждые первые n посылок (в терминологии референс-мануала -- "токенов") портятся, но каким-то не очень случайным образом:

Ширина испорченной зоны подозрительно похожа на 32. Посылки все 16-битные (16-битных массив пересылается в 16-битный регистр). Эффект не зависит ни от стартового адреса, ни от количества данных в одной транзакции. Скорость работы шины вряд ли виновата (т.к. равномерная заливка всё-таки работает и тактовая частота микроконтроллера более чем в два раза ниже, чем тактовая на видеочипе).

Код:

    SSD1963_SetArea (sx, ex, y, y);
    RCC->AHBENR |= RCC_AHBENR_DMA1EN; 

    DMA1_Channel1->CMAR = (uint32_t)(&buffer[0]); 
    DMA1_Channel1->CPAR = (uint32_t)(&SSD1963.Data); 
    DMA1_Channel1->CNDTR = Count;

    DMA1_Channel1->CCR = DMA_CCR1_MEM2MEM | /* memory-to-memory */
            (0*DMA_CCR1_PL_1) | (0*DMA_CCR1_PL_0) | /* 11: very high priority mode, 00: low priority */
            DMA_CCR1_MSIZE_0 | /* memory size: 16bit */
            DMA_CCR1_PSIZE_0 | /* periph size: 16bit */
            DMA_CCR1_MINC | /* memory increment enabled */
            DMA_CCR1_DIR ; /* direction: read from memory */

    SSD1963.Cmd = (SSD1963_WRITE_MEMORY_START);

    DMA1_Channel1->CCR |= DMA_CCR1_EN; 
    while (!(DMA1->ISR & DMA_ISR_TCIF1)) {}; 
    DMA1_Channel1->CCR &= ~DMA_CCR1_EN; 
    DMA1->IFCR = DMA_IFCR_CTCIF1; 

    RCC->AHBENR &= ~RCC_AHBENR_DMA1EN;



Если я пересылаю ровно точно тот же массив в точно тот же регистр "вручную", но всё пишется нормально, просто медленнее. Если я запускаю программу под отладчиком (опции компиляции все точно те же) и смотрю на регистры, то там записанно именно то, что надо - и глюка пересылки данных НЕТ.

Вопрос - чё за хренотень и как с ней бороться?

Upd: нашёл. Виновата оптимизация -O3. Если её не так туго затягивать, то на -O1 всё работает как и должно работать. Я понял это, когда в отладчике стал смотреть инициализацию DMA по шагам. В сишном коде все операции записаны в том порядке, в котором они ДОЛЖНЫ быть - сначала устанавливаем регистры адресов, затем настраиваем транзакцию, затем включаем... а оптимизатор решил, что всё это неправильно и в итоге получилось так: сначала блок DMA включается на передачу, успевает что-то передать (тот самый мусор!), а затем уже исполняются инструкции, которые его настраивают и дальше высылаются корректные данные.
А с другими каналами вообще не работает - функция исполняется без видимых ошибок, но на экране ничего нет совсем! фантастика

Запускаю под отладчиком - всё нормально рисуется. Без отладчика - ничего не рисуется.

Edited at 2017-01-04 07:26 pm (UTC)