Jump to content

    
Sign in to follow this  
proga

Проблема снятия флага

Recommended Posts

Здравствуйте. Я разрабатываю программу на базе микроконтроллера STM32F417. У меня возникла проблема следующего характера. В одной части своей программы мне необходимо запретить DMA исполнение. Делаю я это стандартным способом

 

    
        DMA_Cmd(DMA1_Stream5, DISABLE); 
    while(DMA1_Stream5->CR & DMA_SxCR_EN);

Однако программа в цикле застревает навечно. Если же я в отладчике пошагово исполняю этот кусок кода, то без проблем прохожу цикл.

Если же я изменяю уровень оптимизации с O3 на O1, то программа начивает застревать раньше в подобном же блоке

    DMA_Cmd(DMA2_Stream0, DISABLE);
    while(DMA2_Stream0->CR & DMA_SxCR_EN);

, который раннее при оптимизации O3 программа проходила без проблем. Прошу помощи в разрешении данной проблемы.

Share this post


Link to post
Share on other sites

Периферийные регистры

Встраиваемые системы содержат оборудование со сложной периферией. В составе периферии есть регистры, чьи значения могут изменяться асинхронно алгоритму программы. В качестве простого примера рассмотрим 8-битный регистр состояния, отображаемый в памяти по адресу 0х1234. Допустим, нам нужно опрашивать регистр состояния до тех пор, пока он не станет ненулевым. Простая и неправильная реализация может быть такой:

 

uint8_t * pReg = (uint8_t *) 0x1234;

 

// Wait for register to become non-zero

while (*pReg == 0) { } // Do something else

В этом случае, почти наверняка будет сбой, как только вы включите оптимизацию. Компилятор сгенерирует ассемблерный код подобный этому:

 

mov ptr, #0x1234

mov a, @ptr

loop:

bz loop

Логическое обоснование оптимизатора довольно простое: уже считав значение переменной в аккумулятор (вторую строка кода), нет необходимости считывать его заново, поскольку значение всегда будет тем же. Таким образом, в третьей строке мы окажемся в бесконечном цикле. Чтобы заставить компилятор сделать то, что нам нужно, мы изменим описание на:

 

uint8_t volatile * pReg = (uint8_t volatile *) 0x1234;

 

Ассемблерный код теперь выглядит следующим образом:

 

mov ptr, #0x1234

loop:

mov a, @ptr

bz loop

 

Требуемый ход процесса достигнут.

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

 

Применительно к программированию микроконтроллеров AVR, нет никакой необходимости в использовании указателей на периферийные регистры, потому что код в этом случае получается весьма громоздким. Pashgan.

Share this post


Link to post
Share on other sites
Я заранее почти на 100% уверен, что DMA2_Stream0->CR имеет квалификатор volatile. Так что проблема не тут.

 

2ТС: Пальцем в небо: что нибудь изменится если написать так ?

    DMA_Cmd(DMA1_Stream5, DISABLE);
    __DSB();
    while (DMA1_Stream5->CR & DMA_SxCR_EN);

Share this post


Link to post
Share on other sites

    
        DMA_Cmd(DMA1_Stream5, DISABLE); 
    while(DMA1_Stream5->CR & DMA_SxCR_EN);

1. Вопрос: зачем ждать сброса флага? Неужели есть опасение, что DISABLE не сработает?!

2. Идея: смотрится как излишество в контексте С, но мне помогало:

 

while((DMA1_Stream5->CR & DMA_SxCR_EN) !=0);

 

3. Облом лезть в доку за битами, но может стОит проверять не CR, а SR?

Edited by KnightIgor

Share this post


Link to post
Share on other sites

не излишество, а определенность, и помогли вам другие скобки.

while((DMA1_Stream5->CR & DMA_SxCR_EN) !=0);

равносильно

while((DMA1_Stream5->CR & DMA_SxCR_EN));

но не равносильно

while(DMA1_Stream5->CR & DMA_SxCR_EN);

в кейле несколько раз на это нарывался, не совсем корректная работа компилятора, иногда можно объяснить приоритетом действий иногда нет, но за правило для себя принял запись

while((DMA1_Stream5->CR & DMA_SxCR_EN) !=0);

так нагляднее и надежнее работает!

 

Share this post


Link to post
Share on other sites

Спасибо всем за советы. К сожалению они не помогли. Я просмотрел код через дизасемблер. Копилятор отработал нормально. Дело не в том, что некорректна проверка флага, а в том, что флаг изначально не сбрасывается, либо сбрасывается и тут же устанавливается логикой. Как я уже писал, при пошаговом прохождении этой части кода программа исполняется нормально. А вот при выполнении программы налету она застревает в этом цикле. Возникает ощущение, что логика всё же возвращает флаг на место, просто в пошаговом режиме почему-то не может этого сделать. Скорее всего я, что то делаю не так, хоть и ума не приложу в чём я ошибаюсь. Ну что ж, буду думать.

Edited by proga

Share this post


Link to post
Share on other sites
Спасибо всем за советы. К сожалению они не помогли.

Поскольку чудес не бывает, то если подумать над симптомом - под отладчиком все работает, а "на лету" нет (бит всплывает из небытия), - приходит мысль, что бит взводится в каком-то прерывании: под отладчиком с правильным адаптером прерывание при пошаговом проходе не вызывается, а "на лету" скорее - да. Ищите ошибку в логике своего кода.

 

P.S. Вдогонку еще мысль: гдет-то на задворках моей памяти всплывает воспоминание, что, якобы, если выключить DMA во время передачи, то могут быть странные глюки с битами. Рекомендую перед DISABLE сделать еще STOP.

Edited by KnightIgor

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this