2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2  След.
 
 видимость глобальной переменной в С для avr
Сообщение23.05.2018, 21:05 


07/10/15

2400
Столкнулся с такой ситуацией: при объявлении глобальной переменной и последующем её изменении в обработчике прерываний, она то ли вовсе не меняется, то ли не видна в основной программе. Вот код:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
int st=0;


ISR (ADC_vect)                   // функция обработки прерываний АЦП
{

st++;

}


int main(void) {  

// -- конфигурирование выводов --
DDRD |= (1 << PORTD0);              // пин PORTD0 порта DDRD как выход
PORTD &= ~(1 << PORTD0);

//       конфигурирование АЦП
ADMUX = 0b11000000;
ADCSRA =0b11101111;  

sei();                                  // ----- разрешить все прерывания  
     
// -- основной цикл программы --
while (1)                              
     {
         
          if (st>5000)                    
               PORTD |= (1 << PORTD0);  // высокий уровень на пин PD0 порта DDRD
          else
                PORTD &= ~(1 << PORTD0); // низкий  уровень на пин PD0 порта DDRD
                           
                                         
         if (st>10000)
                         st=0;
   
     }
return 0;
}
 


К порту PD0 подключен светодиод, по которому я и сужу о работе.
Должно быть так: по каждому прерыванию АЦП значение счётчика увеличивается на 1 ,
по достижении $st=5000$ на PD0 подаётся высокий уровень и диод должен загореться. Через такое же время счётчик, когда $st=10000$, $st$ обнуляется и диод должен гаснуть.

Но диод не загорается совсем.

P.S: При непосредственном переключении внутри бесконечного цикла
Используется синтаксис C
while (1)                              
     {
         
         PORTD |= (1 << PORTD0);
        _delay_ms(3000);
        PORTD &= ~(1 << PORTD0);
               
     }
 

диод мигает.

АЦП тоже работает, так как при изменении состояния PD0 в обработчике прерываний АЦП, диод загорается при прикосновении рукой к выводу ADC0, а при удалении руки - он гаснет.

Используется синтаксис C
ISR (ADC_vect)                   // функция обработки прерываний АЦП
{

       if (ADCW>750)
                 PORTD |= (1 << PORTD0);
        else
         PORTD &= ~(1 << PORTD0);
}
 


Думаю тут всё дело в переменной st, но в чём конкретно - понять не могу ...

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение23.05.2018, 21:40 
Заслуженный участник


20/08/14
11177
Россия, Москва
Andrey_Kireew
На 90% проблема что переменная st не однобайтовая, а значит операции с ней точно не атомарные. Объявите её байтовой и возможно всё заработает, хотя код и останется неправильным.
Правильный код - с атомарными обращениями к переменной вне процедуры прерывания. Вариантов реализации много, самый простой - на время обращения к переменной запрещать вообще все прерывания (и не забывать разрешать после операций с переменной, в том числе и при возникновении ошибок и исключений). Например весь код
Код:
if (st>10000) st=0;
надо забирать в атомарность, а не только отдельно чтение и отдельно запись. Я частенько не парюсь и добавляю две процедуры
Код:
int atomReadInt(int * ptr) {
int temp;
  asm("cli"); temp = * ptr; asm("sei"); return temp;
}
void atomWriteInt(int * ptr, int value) {
  asm("cli"); * ptr = value; asm("sei");
}
//Пример использования: не if(st>5000), а if(atomReadInt(st)>5000).
Но в том if выше даже это не спасёт.
И кстати операция st=0 (как и чтение переменной) тоже не атомарная, это минимум две (или четыре, смотря сколько байтов переменная) команды записи в память - и между ними тоже может придти прерывание и получится бред. Операции чтения и записи атомарные только для байтовых переменных, но для битовых - снова не обязательно, зависит от компилятора, для надёжности лучше считать не атомарными.
В общем изучайте инфу об атомарных операциях и думайте.

Дальше разбираться честно говоря неохота.

-- 23.05.2018, 21:44 --

PS. Так что название топика левое, проблема вовсе не в видимости переменной.
PPS. И почему снова не пользуетесь подсветкой синтаксиса? С ней текст программы понятнее.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение23.05.2018, 23:03 


07/10/15

2400
Спасибо большое Dmitriy40 теперь появилась ясность.
"Засунул" содержимое бесконечного цикла прямо в обработчик прерывания - всё работает как надо.

Пытался делать 2 счётчика char или проверять только верхний байт st через указатель (чтобы было обращение только к атомарным переменным) - всё без толку, диод не светится. Будто бы переменную просто не видно.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение23.05.2018, 23:14 
Заслуженный участник


20/08/14
11177
Россия, Москва
Если бы её было не видно - компилятор ругнулся бы на неизвестное ему имя. Вероятно под "не видно" Вы подразумевали отличие адресов переменной в прерывании и главной программе, но это можно проверить по асм тексту или в отладчике или просто поверить компилятору что всё правильно.
Деление счётчика на два байта атомарность операций над всем счётчиком не даст разумеется, лишь над байтами, да и то, не полную (конструкция if(st>50) st=0; целиком атомарной не станет).

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 00:10 


07/10/15

2400
Dmitriy40 в сообщении #1314450 писал(а):
Деление счётчика на два байта атомарность операций над всем счётчиком не даст разумеется, лишь над байтами, да и то, не полную (конструкция if(st>50) st=0; целиком атомарной не станет).


я не делил счётчик на 2 байта, а делил на 2 счётчика, да и условный оператор этот вынес в обработчик, так - тоже не работает:

код: [ скачать ] [ спрятать ]
Используется синтаксис C
unsigned char st=0, st2=0;


ISR (ADC_vect)                   // функция обработки прерываний АЦП
{

st++;
if(st==255)
        st2++;

if (st2>40)
        st2=0;
 

}


int main(void) {                 // начало программы

................................
     
// -- основной цикл программы --
while (1)                              
     {
         
                 if (st2>20)                     // сравнение с порогом    
                         PORTD |= (1 << PORTD0);  // высокий уровень на пин PD0 порта DDRD
                  else
                          PORTD &= ~(1 << PORTD0);

     }
return 0;
}
 


теперь то точно всё атомарное.

На счёт несоответствия адресов переменной - спасибо за подсказку, может оно и так.
Я вот ещё думаю, может проблема в том, что я использую режим автоматического преобразования. Там флажок ADSC "преобразование запущено" никогда не сбрасывается в "0". Но не знаю как это может повлиять ...
asm - кода у меня нет, сразу hex код генерируется.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 00:25 
Заслуженный участник


04/05/09
4582
Проблема в том, что компилятор не знает, что переменная может измениться за пределами функции main() и оптимизирует чтение из переменной только один раз. Чтобы этого не было, добавьте ключевое слово volatile к объявлению переменной: volatile int st=0;

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 00:40 


07/10/15

2400
Огромное спасибо venco,
действительно , сделал как Вы посоветовали и стало работать как надо!

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 00:48 
Заслуженный участник


20/08/14
11177
Россия, Москва
Andrey_Kireew в сообщении #1314457 писал(а):
так - тоже не работает:
А вот тут я уже не понимаю почему не работает, должно. Возможно причину указал venco и после оптимизации там действительно чёрти что.
Флаг ADSC ни при чём, он и не должен сбрасываться, преобразования же идут. Факт завершения определяется по ADIF, но у вас очевидно ADIE=1 и сразу же вызывается прерывание со сбросом ADIF, так что тут всё работает правильно.
Насчёт hex only, разбираться в нём это изврат разумеется, поищите в настройках своего компилятора галочку типа Generate asm, в некоторых (если не почти всех) она есть (вот тут по первой же ссылке из гугла кажется сразу несколько вариантов получения асм текста из студии: https://www.avrfreaks.net/forum/how-see ... avr-studio ).

-- 24.05.2018, 00:50 --

Хм, таки дело в компиляторе, надо же, получается я пользуюсь слишком старым/кривым, который такое не оптимизирует.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 01:06 


07/10/15

2400
Хорошо, буду иметь в виду про asm код, но так как проблема решена, разбираться в нём пока наверное не стоит, тем более, что я не очень то в нём и понимаю.

-- 24.05.2018, 02:11 --

У меня WinAvr2010, тоже не новый, из консоли работает ... это наверное всё потому, что я не люблю обновления :)

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 01:11 
Заслуженный участник


20/08/14
11177
Россия, Москва
Разумеется. Это лишь мне, извращенцу, интересно во что компилятор превращает такой красивый и понятный С код. :D

-- 24.05.2018, 01:35 --

Что характерно, добавление volatile мой компилятор интерпретировал как-то странно: не стал помещать переменную в регистры, но и никаких признаков атомарности в коде я не увидел.
Вот исходник с сокращениями:
Используется синтаксис C
volatile unsigned short Cnt = 0;
ISR(T0OVF) {//Прерывание по таймеру
  Cnt++;
}

void main () {
  while(1) Cnt--;
}
И скомплились оба обращения к переменной в почти идентичный (разница в adiw или sbiw) ассемблерный код:
Код:
lds  R30,_Cnt
lds  R31,_Cnt+1
adiw R30,1
sts  _Cnt,R30
sts  _Cnt+1,R31
И никакой защиты от вызова прерывания в середине процедуры уменьшения в основной программе нет!

Andrey_Kireew
Потому я бы не стал утверждать что теперь всё работает. Скорее Вы просто не дождались появления глюка, но при долгой работе он обязательно вылезет (прерывание попадёт ровно между командами обращения к переменной).
Ну или Ваш компилятор сильно умнее моего. ;-)
Но про атомарность операций читать всё равно придётся.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 02:09 


07/10/15

2400
Да, всё верно, я сейчас почитал про volatile. Этот спецификатор заставляет компилятор размещать переменную в ОЗУ, а не в регистрах, так как в регистрах она может "быть не точной" (уж не знаю, что под этим понимается). Как раз рекомендуется использовать в таких случаях, когда переменная меняется в прерываниях. Где то пишут, что вместо этого можно принудительно отключать прерывания.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 02:25 
Заслуженный участник


20/08/14
11177
Россия, Москва
Andrey_Kireew
Скачал WinAvr2010, поставил, скомпилил свой пример им (avr-gcc) - разница несущественная, вот что получилось в асме:
Код:
lds  r24,Cnt
lds  r25,(Cnt)+1
sbiw r24,1
sts  (Cnt)+1,r25
sts  Cnt,r24
Запись обратно в другом порядке, скобочки добавил, регистры другие, но никакой атомарности всё равно не замечено.

Ну а про запрет прерываний на время работы с переменной в основной программе я сразу сказал, как один из возможных вариантов.

По идее volatile используется для адресации аппаратных регистров: данные в них могут меняться независимо от программы (как например ADCW объявлена) и потому хранить копию в регистрах для оптимизации нельзя. Грубо говоря всего лишь запрет кэширования, но не атомарность операций.

PS. Асм текст можно получить в .s файле если добавить компилятору опцию -save-temps. А с опцией -S он на получении асм и остановится, дальше компиляция не пойдёт. Ну просто Вам для информации.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 12:51 


07/10/15

2400
СпасибоDmitriy40 ! можно будет потом asm код оптимизировать.
Только я не совсем понял, как записать команду:
Код:
avr-gcc -s prog_asm.s -mmcu atmega8 -DF_CPU=4000000L -O3 prog.c

так правильно?

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 13:45 


05/09/12
2587
Волатиле и не будет (и не обязано) как-то гарантировать или вообще иметь какое-либо отношение к атомарности. Это всего лишь указание компилятору не умничать и не оптимизировать кот типа
Используется синтаксис C
uint8 a = 1;
while (a) {}

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

А заботы об атомарности ложатся на голову автора кота - в примере выше она достигается автоматически вследствие размещения переменной в одном регистре и чтения/сравнения за одну ассемблерную инструкцию. Если бы, к примеру, переменная занимала 2 байта на 8-битной архитектуре, тогда пришлось бы лепить костыли типа скобок запрета/разрешения прерываний на момент ее чтения (чтобы никакое прерывание ее не изменило) или первое чтение, второе чтение, сравнение результатов двух чтений и если не равны то третье чтение (чтобы если во время первого или второго чтения прерывание испортило значение, третье чтение будет достоверным - реальный кейс по прерыванию по таймеру при достаточном количестве тактов между прерываниями), или какой-либо еще костыль.

 Профиль  
                  
 
 Re: видимость глобальной переменной в С для avr
Сообщение24.05.2018, 16:35 
Заслуженный участник


20/08/14
11177
Россия, Москва
Andrey_Kireew в сообщении #1314550 писал(а):
так правильно?
Нет, -s prog_asm.s лишнее, вместо него надо добавить -save-temps:
Код:
avr-gcc -save-temps -mmcu atmega8 -DF_CPU=4000000L -O3 prog.c
Появится кучка временных файлов (в том числе .i и .o) и среди них и prog.s с асм текстом.

Кстати, я предпочитаю настройки указывать в тексте исходника, а не в параметрах вызова компилятора, и тип контроллера, и его частоту. Пример:
Используется синтаксис C
#define __AVR_ATmega8__ //Имя константы смотреть в io.h
#define F_CPU   4000000UL
И тогда команда вызова сокращается до
Код:
avr-gcc -save-temps -Os prog.c

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 17 ]  На страницу 1, 2  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group