roboforum.ru

Технический форум по робототехнике.

Вопрос по чтению АЦП в цикле while (1)

Вопрос по чтению АЦП в цикле while (1)

Петруччо » 07 сен 2011, 20:30

Здравствуйте, уважаемые кибернетики!

Заранее извиняюсь, если не в тот раздел...
Давненько почитываю ваш форум, вот наконец-таки решился прописаться здесь. Сам я к вам с "соседнего" rcdesign.ru, ибо аз есм моделист, но паяльник люблю не меньше моделек :)
вобщем-то идея такая:
на 32й меге (пока, на ней прототипирование) сделано меню из N пунктов, меню перещелкивает кольцом по инкременту от п.1 до N от кнопки на прерывании INT2. в пункте 1 прописана функция чтения и записи АЦП на 2хстрочный ЖКИ 2х16. Проблема в следующем: хоть и ф-ия меню и чтения/записи в ЖКИ прописана в цикле while (1), у меня проц наотрез не хочет непрерывно читать. если прощелкать 3 раза (пройти по кругу), то уже можно увидить новое значение, кароче говоря не обновляется. Код прилагаю. Сам когда-то давно на сях рубился, сейчас все вспоминаю, так что не ругайте, местами он-таки кривоват.
Пишу под AVRStudio 5.0 GCC

Заранее благодарю!
Код: Выделить всёРазвернуть
#include <avr/io.h>
#include <avr/interrupt.h>
#define RS 2 // RS - PORTC 2
#define E 3 // E - PORTC 3
unsigned char mode=0; // индекс меню
unsigned char flag=0; // флаг установки меню
unsigned int u; //показание ацп

void delay (unsigned long int a) // программа задержки
{
    unsigned long int b;
   for (b=a;b>0;b--);
}

void lcd_command (unsigned char lcd) // функция отправки командв в ЖКИ
{
   unsigned char temp;
   temp=(lcd&~(1<<RS))|(1<<E); // старшая тетрада
   PORTC=temp;
   asm ("nop");
   PORTC=temp&~(1<<E);
   
   temp=((lcd*16)&~(1<<RS))|(1<<E); // младшая тетрада
   PORTC=temp;
   asm ("nop");
   PORTC=temp&~(1<<E);
   
   delay (100);
}

void lcd_data (unsigned char lcd) // отправка данных в ЖКИ
{
   unsigned char temp;
   temp=lcd|(1<<RS)|(1<<E); // старшая тетрада
   PORTC=temp;
   asm ("nop");
   PORTC=temp&~(1<<E);
   
   temp=(lcd*16)|(1<<RS)|(1<<E); // младшая тетрада
   PORTC=temp;
   asm ("nop");
   PORTC=temp&~(1<<E);
   
   delay(100);
}   
   
   void lcd_init (void) // инициализация ЖКИ
   {
      lcd_command(0x2c);
      delay (1000);
      lcd_command(0x0c);
      delay (1000);
      lcd_command(0x01);
      delay (1000);
   }
   
      
   void clean_lcd (void) // очистка ЖКИ
   {
      lcd_command(0x01);
   }
   
      
   ISR (INT2_vect) //  обработчик прерывания по нажатию кнопки
   {      
   if (mode++ == 2) mode=0; // инкремент индекса меню, если ==2, то сбрасываем в 0
   flag=0; // разрешаем флаг записи
   GIFR=0x00; // сбрасываем флаг прерывания
   return;
   }   
      unsigned int ADC_read (void) // чтение регисра АЦП
   {
      unsigned int v;
      ADCSRA|=(1<<ADSC);
      while ((ADCSRA&_BV(ADIF))==0x00);
      v=(ADCL|ADCH<<8);
      return v;
   }
   
   void write_adc (unsigned int u) // функция записи результата АЦП (пока)
   {
      lcd_command(0xC0); // перевод на 2ю строку
      unsigned int j; // переменная
      j=u/1000; // вычисляем тысячи
      if (j==0)
      {
          lcd_data(' '); // если 0 то пришем пусто
      }
      else
      {
         lcd_data(0x30+j); // если нет, то 0+j
      }
      u=u-j*1000; // остаток сотен
      j=u/100; // вычисляем...
      lcd_data(0x30+j); // то же самое и так до едениц
      u=u-j*100;
      j=u/10;
      lcd_data(0x30+j);
      u=u-j*10;
      j=u;
      lcd_data(0x30+j);
   }
   
      void menu(void) // функция меню
{
      
      if (mode==0) // если индекс == 0
      {
         if (flag==0) // и запись разрешена
         {
            clean_lcd(); // чистим ЖКИ
            lcd_data('1'); // пишем "1."
            lcd_data('.');   
            flag=1; // запрещяем инкремент индекса на всякий случай
            write_adc(u);   // пишем в 1м разделе меню рез-т АЦП
         }
      }
      else
      if (mode==1) // тоже самое, но п. "2."
      {
         if (flag==0)
         {   
            clean_lcd();
            lcd_data('2');
            lcd_data('.');
            flag=1;
         }
      }
      else
      if (mode==2) // тоже самое, но п. "3."
      {
         if (flag==0)
         {   
            clean_lcd();
            lcd_data('3');
            lcd_data('.');
            flag=1;
         }
      
    }   
   }      

int main(void)
{

DDRB&=~(1<<2);
PORTB=0x04;
DDRC|=0xfc;
delay(3000);
lcd_init();




GICR|=(1<<INT2); // разрешить прерывания от INT2
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0); // ВКЛ АЦП
ADMUX=(1<<REFS1)|(1<<REFS0)|(0<<MUX0)|(0<<MUX1)|(0<<MUX2)|(0<<MUX3); // настраиваем АЦП...
sei();

    while(1)
    {
    u=ADC_read(); // бесконечно преобразуем и пишем в меню
    menu();
    delay(6000);
   
    }
    }

Re: Вопрос по чтению АЦП в цикле while (1)

avr123.nm.ru » 07 сен 2011, 21:03

Проверьте что в правильном порядке читаются v=(ADCL|ADCH<<8);

и наверно volatile модификатор нужен переменным раз речь о прерывании.

Re: Вопрос по чтению АЦП в цикле while (1)

Vooon » 07 сен 2011, 21:11

А как он что-нибудь запишет, если flag=1?

Re: Вопрос по чтению АЦП в цикле while (1)

Петруччо » 07 сен 2011, 21:44

Авр123, все там правильно, этот же кусок работает без ф-ии меню...
Vooon, flag используется исключительно с индексом меню как разрешающий переключение пунктов, не более того. Но все равно спс, попробую!

Добавлено спустя 3 минуты 43 секунды:
З.ы.
volatile не обязательно, все без нее работает.
Еще раз, проблема в вечном цикле, сам код-то работает и все переключает.

Re: Вопрос по чтению АЦП в цикле while (1)

avr123.nm.ru » 07 сен 2011, 22:21

тогда после читки АЦП поставь присвоение значения результату из простого счетчика ctr++;

и посмотри буду ли меняться значения. Т.е. поэтапно тести гле затык.

Re: Вопрос по чтению АЦП в цикле while (1)

boez » 09 сен 2011, 13:02

Без volatile все может работать с вероятностью 99.99%. А потом на 10000-й раз сбойнуть. Если переменная более чем однобайтная, или если она модифицируется и в прерывании, и в коде, она обязана быть volatile.

А что касается
Код: Выделить всёРазвернуть
v=(ADCL|ADCH<<8) 
- опять же, может работать, может не работать, зависит от левой ноги компилятора. Правильно -
Код: Выделить всёРазвернуть
v=ADC

Re: Вопрос по чтению АЦП в цикле while (1)

Петруччо » 16 сен 2011, 10:18

без volatile все работает 100%, проверяно.
отвечаю на свой же вопрос:

поменять в цикле функции местами, сначала menu() затем ADC_read() и все заработало без проблем.

Re: Вопрос по чтению АЦП в цикле while (1)

avr123.nm.ru » 16 сен 2011, 10:29

Хорошо бы понять почему так.

Re: Вопрос по чтению АЦП в цикле while (1)

Петруччо » 19 сен 2011, 16:13

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


Rambler\'s Top100 Mail.ru counter