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

AVR 5.11 Ошибка при двухбайтном сравнении!

Такой код:

 

volatile unsigned int msCounter = 0;

 

...

 

if (msCounter >= 1000) {

 

msCounter = 0;

 

//...

 

}

 

Иногда сравнение выполняется неправильно и входит в блок при значении msCounter = 768.

Установлено, что при двухбайтном сравнении происходит прерывание, в котором инкрементируется msCounter, что и приводит в последствии к ошибке.

 

if (msCounter >= 1000) {

00014A E9EB LDI R30,0x9B

00014C E0F1 LDI R31,0x01

00014E 8100 LD R16,Z

...тут прерывание и инкрементирование сравниваемой величины...

000150 8111 LDD R17,Z+1

000152 3E08 CPI R16,0xE8

000154 4013 SBCI R17,0x03

000156 F390 BRCS 0x13C

...

 

 

Вопрос: Можно ли заставить компилятор автоматически разруливать такие вещи?? Или это нужно постоянно держать в голове и делать вот так:

 

__disable_interrupt();

 

if (msCounter >= 1000) {

 

msCounter = 0;

 

//...

 

}

 

__enable_interrupt();

 

Заранее благодарен!

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


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

Вопрос: Можно ли заставить компилятор автоматически разруливать такие вещи?

 

 

Нет. Для AVR это Ваши проблемы. Запрет прерываний или другой метод обеcпечения атомарности надо использовать явно.

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


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

Нет. Для AVR это Ваши проблемы. Запрет прерываний или другой метод обеcпечения атомарности надо использовать явно.

 

Для какой платформы это проблемы компилятора?

 

Запрет прерываний или другой метод обеcпечения атомарности надо использовать явно.

 

Другой метод? Какой например?

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


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

Другой метод? Какой например?

 

unsigned int tmp1, tmp2;
do
{ tmp1=msCounter;
  tmp2=msCounter;
} while(tmp1!=tmp2);
if (tmp2 >= 1000) 
{
  ...
}

А вот с обнулением сложнее. :( Это вообще неправильный подход к программированию когда volatile-переменная с неатомарным доступом модифицируется сразу в нескольких частях программы. Либо используйте дублирование этой переменной с семафором доступа, либо используйте семафор для ее обнуления. Вообще это так принципиально ее в майне обнулять?

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


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

unsigned int tmp1, tmp2;
do
{ tmp1=msCounter;
  tmp2=msCounter;
} while(tmp1!=tmp2);
if (tmp2 >= 1000) 
{
  ...
}

А вот с обнулением сложнее. :( Это вообще неправильный подход к программированию когда volatile-переменная с неатомарным доступом модифицируется сразу в нескольких частях программы. Либо используйте дублирование этой переменной с семафором доступа, либо используйте семафор для ее обнуления. Вообще это так принципиально ее в майне обнулять?

 

Как-то уж очень геморно. Интеррапты запрещать как-то читабельней. Хотя, тоже фигня...

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

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

...Дело в том, что таких ситуаций немеренно, когда в функциях происходят сравнения данных, которые могут быть изменены в прерывании. Это ж повсеместно!! И что?? Перед каждым if, case, while, for... запрещать прерывание, чтобы переменная правильно сравнивалась??????....... Ну не гемор разве?...

 

PS. zltigo тему быстренько перенес в ветку для ламеров, а сам как всегда определенного ответа дать не смог. Сколько помню его, все ходит вокруг да около и мало кому реально помогает. Я уж и сомневаюсь что он профессионал.

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


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

PS. zltigo тему быстренько перенес в ветку для ламеров

 

Простите, но это есть очередное "детское открытие" - на втором месте после volatile. 

 

, а сам как всегда определенного ответа дать не смог.

 

??? Ответ был дан более, чем определенный  что может быть определеннее "Нет", если, конечно, это мужчина говорит. Хоть сомнений в моем поле у Вас нет :) ?

 

Сколько помню его, все ходит вокруг да около и мало кому реально помогает.

 

Предпочитаю, что-бы начинающие додумывали сами, иначе, при "делай так" не всегда получается даже "ремесленники".

 

...Дело в том, что таких ситуаций немеренно, когда в функциях...

 

По этой причине нужно заранее продумывать систему при котрой такие "ситуации" минимальны. Лично у меня это считанные случаи, ни разу не было более 2x "ситуаций" в проекте. Чаще всего ни одной. Боритесь с причинами. 

 

Для какой платформы это проблемы компилятора?

 

Для 16bit и выше :). Для некоторых ядер предусмотрены команды обеспечивающие атомарность заданного количества последующих команд. В таких случаях встречается возможность указать компилятору на атомарность с помощью дополнительного квалификатора, но в любом случае компилятор не принимает решение самостоятельно.

 

 

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


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

Можно ли заставить компилятор автоматически разруливать такие вещи?

Для Вашего случая в IAR предусмотрен квалификатор __monitor.

Смотрите в Compiler Reference как это делается в разделе MONITOR FUNCTIONS (стр. 30)

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


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

Для Вашего случая в IAR предусмотрен квалификатор __monitor.

 

 

Автор ведь хочет вообще не прилагая никаких умственных усилий :(, а тут "доп.меры".

 

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

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


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

PS. zltigo тему быстренько перенес в ветку для ламеров, а сам как всегда определенного ответа дать не смог. Сколько помню его, все ходит вокруг да около и мало кому реально помогает. Я уж и сомневаюсь что он профессионал.

Все нормально! Вам верно сказали, что для AVR это не компилятора проблемы: ведь восьмибитник никогда за "один раз" не сможет сделать что-либо с 16-битной переменной. Нужно обеспечить атомарность. Я делаю так:

cli();
uint16_t safe_variable = variable;
sei();

Переменна variable может изменяться где ей будет угодно. Но в переменной safe_variable всегда будет верное значение на момент запрета прерываний.

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


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

Такой код:

volatile unsigned int msCounter = 0;
...
if (msCounter >= 1000) {
      msCounter = 0;
      //...
}

Такой код предполагает, что прерывания таймера происходят с частотой 1 МГц. Оно реально нужно? Есть ли хоть одна задача, требующая такого дискрета по времени? 4 мкс не спасут? Тогда все укладывается в 1-байтовый счетчик.

Ладно, будем считать, что нужно время с разрешением в 1мкс. Что мешает выполнять все модификации счетчика в прерывании? Или наоборот, в прерывании только взводить флажок, а обрабатывать его, инкрементируя или обнуляя не-volatile счетчик, вне прерывания?

 

Вопрос: Можно ли заставить компилятор автоматически разруливать такие вещи?

Можно выбирать контроллер под задачу.

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

Можно ...

Нельзя думать, что компилятор будет читать Ваши мысли.

 

ЗЫЖ имхо, большинство бед человечества от уверенных в себе людей. Я предпочитаю сомневаться в своей уверенности.

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


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

Такой код предполагает, что прерывания таймера происходят с частотой 1 МГц.

Почему же? Автор пишет, что иногда происходит сбой программы. Скорее всего иногда в момент сравнения происходит прерывание, которое модифицирует переменную и нарушает процесс сравнения.

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


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

Почему же? Автор пишет, что иногда происходит сбой программы. Скорее всего иногда в момент сравнения происходит прерывание, которое модифицирует переменную и нарушает процесс сравнения.

Да, спасибо за пинок - глаз замылился, почему-то посчитал мс за мкс.

На остальном продолжаю настаивать :).

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


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

Для Вашего случая в IAR предусмотрен квалификатор __monitor.

если AVR-GCC (WinAVR), то смотрите макросы на подобии ATOMIC_BLOCK

avr-libc-user-manual.pdf (п. 22.25)

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


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

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

А если не в майне обнулять, то где? Для того, чтобы ошибки не было нужно только там же, где приращение происходит, т.е. в прерывании.
А на зачем ее вообще сбрасывать-то? Пущай переменная инкрементируется себе спокойно в прерывании, а вы используйте для отсчета интервалов (вам ведь интервал отмерить нужно?) локальную переменную и беззнаковую разность при проверке. Выглядеть это будет вот так.

volatile unsigned int msCounter;
void main (void)
{ unsigned int tmp1, tmp2;
 (static) uinsigned int period; //static нужен, если эта или аналогичная по назначению переменная 
                                    //будет использована в любой другой функции, кроме main
 ...
 do
 { tmp1=msCounter;
   tmp2=msCounter;
 } while (tmp2!=tmp1); //цикл для считывания msCounter не нужен, если прерывания еще не разрешены
 period=tmp2; //делаем первую засечку для отсчета интервала
 ...
 for (;;)
 {
   ...
   do
   { tmp1=msCounter;
     tmp2=msCounter;
   } while (tmp2!=tmp1); //считываем msCounter без запрета прерываний
   if ((tmp2-period)>=1000)  //событие наступило?
   { period=tmp2;  //делаем новую временнУю засечку для отсчета нового интервала
     ...     //обрабатываем событие по данной временнОй засечке
   }
   ...
 }
}

Тут главный нюанс состоит в том, что период переполнения msCounter должен быть не меньше, чем максимальный требуемый вам интервал и переменная должна быть именно беззнаковая. Причем при таком способе, используя всего лишь одну переменную счетчика, можно в программе завести сколько угодно переменных для временнЫх засечек и счетчиков интервалов. При вашем же способе (сброс где-то вне прерывания) пришлось бы инкрементировать в прерывании именно столько счетчиков, сколько вам потребовалось бы временных засечек/интервалов.

A volatile - это в данном случае и не обязательно как я понимаю. volatile ведь нужно применять для переменных, которые могут быть изменены аппаратно. А тут только софтово, просто в прерывании.
Фигню вы тут написали. :( Переменные аппаратно не модифицируются. Аппаратно только какие-либо регистры периферии МК могут изменятся. Да, в зависимости от архитектуры МК для адресации они могут быть объявлены как volatile, но квалификтор volatile при этом вовсе не обозначает, что переменная аппаратно изменяется. Почитайте стандарт C99. Будет больше пользы, если вы сами найдете, "переварите", осознаете и запомните эту информацию.

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


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

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

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

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

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

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

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

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

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

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