roboforum.ru

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

АЦП Atmega8

АЦП Atmega8

BeInspired » 30 ноя 2011, 21:49

Всем доброго времени суток.
Вопрос - написал программу, которая считывает текущее состояние двух каналов АЦП (на них нацеплены переменные резисторы), вычисляет значение напряжений. По результату выдает направление вращения для моторов и величину ШИМ.
Вот программа:
Код: Выделить всёРазвернуть
#include <avr/io.h>

//определим каналы ШИМа
#define SHIM1 OCR1A
#define SHIM2 OCR1B

//определим входные каналы для джойстика:
/*0000      ADC0
0001      ADC1
0010      ADC2
0011      ADC3
0100      ADC4
0101      ADC5
0110      ADC6
0111      ADC7*/
#define CHANNEL1 0x00
#define CHANNEL2 0x01

//Програма задержки
void pause (unsigned int a)
{
unsigned int i;
for (i=a;i>0;i--);
}


//функция получает в качестве аргумента номер канала и
//возвращает значение на входе
unsigned int ADC_result(unsigned char adc_input)
{
         ADMUX=adc_input | (ADMUX & 0xFF);
//задержка для стабилизации входного напряжения
    pause(100);
//начинаем преобразование (ADSC = 1)
         ADCSRA = ADCSRA | 0x40;
         while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
         ADCSRA|=0x10;//устанавливаем ADIF
         return ADCW;//ADCW - содержит ADCH и ADCL как нам нужно
}

void main(void)
{

   unsigned int i;
   //Массив, хранящий в себе пороговые значения вольтажей от джойстика
   float voltages[3] = {3.5, 1.5, 0};
   //Массив значений мощности ШИМ (без учета знака)
   int power[3] = {0xFF, 0x00, 0xFF};
   int direction[3] = {1, 0, -1};
   //значения вольтажа из соответствующих резисторов
   float vl1, vl2;
   //направление вращения моторов
   // 0 - нет вращения
   // 1 - нормальное направление вращения
   // -1 - обратное вращение
   int dir1, dir2;

DDRB=0x07;  //Инициализация PB1 (OC1A) и PB2 (OC1B) как выход, а также PB0 - светодиод
DDRD=0xFF;  // все ножки порта D сделать выходами

//зажигаем светодиод
PORTB = PORTB | 0x01;

//Инициализируем ШИМ
  TCCR1A=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM10)|(1<<WGM12); //На выводе OC1A единица, когда OCR1A==TCNT1, восьмибитный ШИМ
  TCCR1B=(1<<CS10)|(1<<WGM12);//Делитель=

//Иницилиазируем АЦП
  ADMUX = 0xC0;// Выбрали в качестве опорного напряжения внутренний 2.56В с кондером. 1100.0000 = 0xC0
  ADCSRA = 0x8D;

while (1)
  {

   //////////////////////////////////////////////////////////////////////////
   //       ВОЛЬТАЖ
   //////////////////////////////////////////////////////////////////////////
   vl1 = 0.0049 * ADC_result(CHANNEL1);
   vl2 = 0.0049 * ADC_result(CHANNEL2);
   
   for (i=0; i<2; i++)   
   {
      if (vl1 >= voltages[i])
      {
         //если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
         SHIM1 = power[i];
         dir1 = direction[i];
         break;//сбрасываем цикл, иначе дойдем до самого первого значения
      }
   }
   
   for (i=0; i<2; i++)   
   {
      if (vl2 >= voltages[i])
      {
         //если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
         SHIM2 = power[i];
         dir2 = direction[i];
         break;//сбрасываем цикл, иначе дойдем до самого первого значения
      }
   }
   
   //устанавливаем направление вращения
   //для ножек PD0 и PD1 (первый мотор) PD2 и PD3 (второй мотор)
   
   // ПЕРВЫЙ МОТОР
   // 10 00 - прямое вращение
   // 01 00 - обратное
   // 00 00 - СТОП
   
   // ВТОРОЙ МОТОР
   // 00 10 - прямой
   // 00 01 - обратный
   // 00 00 - СТОП
   
   PORTD = 0x00;//СТОП обоих моторов по умолчанию dir1=dir2=0
   
   if (dir1 == 1)
   {
      PORTD = PORTD | 0x08;
   }
   
   if (dir1 == -1)
   {
      PORTD = PORTD | 0x04;
   }
   
   if (dir2 == 1)
   {
      PORTD = PORTD | 0x02;
   }

   if (dir2 == -1)
   {
      PORTD = PORTD | 0x01;
   }
  }

}

Вопроса два:
1. Почему программа реагирует только на изменения ADC1 (ножка PC1)? На первый АЦП, который ADC0 не реагирует ни один мотор. А вот на вторую почему-то реагируют оба мотора.
2. Почему размер такой маленькой программы огромный? Почти 9 килобайт hex-файл. Как уменьшить?

Во вложениях прошивка и плата.
Вложения
plata-test1.rar
(18.35 КиБ) Скачиваний: 0
AVRGCC1.hex
(8.73 КиБ) Скачиваний: 271
Последний раз редактировалось BeInspired 01 дек 2011, 22:02, всего редактировалось 1 раз.

Re: АЦП Atmega8

Dmitry__ » 30 ноя 2011, 22:05

BeInspired писал(а):Почему размер такой маленькой программы огромный?

зачем для решения такой простой задачи использовать математику с плавающей точкой ? :shock:

Добавлено спустя 3 минуты 16 секунд:
вдумайся, ацп авра 10 бит = 1024 значений :D
ой, или 12 бит?

Re: АЦП Atmega8

boez » 01 дек 2011, 14:28

BeInspired писал(а):1. Почему программа реагирует только на изменения ADC1 (ножка PC1)? На первый АЦП, который ADC0 не реагирует ни один мотор. А вот на вторую почему-то реагируют оба мотора.

Вот поэтому:
Код: Выделить всёРазвернуть
ADMUX=adc_input | (ADMUX & 0xFF);


Подумай, что получится в admux, когда у тебя был включен первый канал, а ты хочешь включить нулевой?

Re: АЦП Atmega8

BeInspired » 01 дек 2011, 16:37

>>Подумай, что получится в admux, когда у тебя был включен первый канал, а ты хочешь включить нулевой?
Точно - спасибо большое! Сегодня поправлю и попробую.

Dmitry__ писал(а):зачем для решения такой простой задачи использовать математику с плавающей точкой ?

Т.е. думаете только из-за этого? Ну не помню я чтобы в С++ при изменении типов данных так кардинально менялся размер программы... :no:

Re: АЦП Atmega8

Dmitry__ » 01 дек 2011, 17:50

про авр не скажу, а в msp430 библиотека плавающей точки занимает около 2-х кб. (это около 4 кб. в hex )
что пишет xx.map ? сколько там твоего кода, а сколько там всяких _Mul32f, _Div32f, _Add32f и прочей нечисти.

шо мешает сделать так:
//Массив, хранящий в себе пороговые значения вольтажей от джойстика
unsigned int voltages[3] = {716, 307, 0};

{3.5, 1.5, 0} - преобразовать:
1024 (разрядность ацп) / 5(вольт питания ацп) * 3.5 (ацкое число) = 716

Добавлено спустя 3 минуты 24 секунды:
есно это выкинуть:
vl1 = 0.0049 * ADC_result(CHANNEL1);
vl2 = 0.0049 * ADC_result(CHANNEL2);

Re: АЦП Atmega8

BeInspired » 01 дек 2011, 19:17

Dmitry__ писал(а):про авр не скажу, а в msp430 библиотека плавающей точки занимает около 2-х кб. (это около 4 кб. в hex )
что пишет xx.map ? сколько там твоего кода, а сколько там всяких _Mul32f, _Div32f, _Add32f и прочей нечисти.

шо мешает сделать так:
//Массив, хранящий в себе пороговые значения вольтажей от джойстика
unsigned int voltages[3] = {716, 307, 0};

{3.5, 1.5, 0} - преобразовать:
1024 (разрядность ацп) / 5(вольт питания ацп) * 3.5 (ацкое число) = 716

Добавлено спустя 3 минуты 24 секунды:
есно это выкинуть:
vl1 = 0.0049 * ADC_result(CHANNEL1);
vl2 = 0.0049 * ADC_result(CHANNEL2);

Ничего не мешает - сейчас поправлю и попробую :)

Апдейт. Попробовал - я в шоке... код уменьшился на 6(!) килобайт. Спасибо! :)

Re: АЦП Atmega8

boez » 01 дек 2011, 19:44

Так ничего удивительного тут нет. На С++ под интел во-первых сущственно больше сами объемы библиотек, во-вторых совсем не факт, что неиспользование плавающей точки в программе влечет ее реальное неиспользование в генерируемом исполняемом файле. Ну и операции с плавающей точкой для интела - это просто вызовы сопроцессора. А здесь все именно так, плавающая точка - эмуляция, под каждую операцию подключается библиотечная функция - для преобразований в целые и обратно, для умножений-сложений и т.п., и все это довольно объемное и жутко медленное к тому же, поскольку даже одинарная точность float - это 32 бита, а процессор 8-битный.

Re: АЦП Atmega8

BeInspired » 01 дек 2011, 21:01

AVRGCC1.hex
Обновленная прошивка
(2.51 КиБ) Скачиваний: 241


Не могу нормально отладить - протеус почему-то наглухо виснет через 30 секунд после начала симуляции :( У вас не виснет?

Переделал программу - посмотрите, пожалуйста - нет ли явных косяков?
Код: Выделить всёРазвернуть
#include <avr/io.h>

//определим каналы ШИМа
#define SHIM1 OCR1A
#define SHIM2 OCR1B

//определим входные каналы для джойстика:
/*0000      ADC0
0001      ADC1
0010      ADC2
0011      ADC3
0100      ADC4
0101      ADC5
0110      ADC6
0111      ADC7*/
#define CHANNEL1 0x00
#define CHANNEL2 0x01

//Програма задержки
void pause (unsigned int a)
{
unsigned int i;
for (i=a;i>0;i--);
}


//функция получает в качестве аргумента номер канала и
//возвращает значение на входе
unsigned int ADC_result(unsigned char adc_input)
{
       ADMUX = 0xC0;
         ADMUX=adc_input | ADMUX ;
//задержка для стабилизации входного напряжения
        pause(100);
//начинаем преобразование (ADSC = 1)
         ADCSRA = ADCSRA | 0x40;
         while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
         ADCSRA|=0x10;//устанавливаем ADIF
         return ADCW;//ADCW - содержит ADCH и ADCL как нам нужно
}

void main(void)
{

   unsigned int i;
   //Массив, хранящий в себе пороговые значения вольтажей от джойстика
   int voltages[7] = {896, 768, 640, 384, 256, 128, 0};
   //Массив значений мощности ШИМ (без учета знака)
   int power[7] = {0xFF, 0xCC, 0x80, 0x00, 0x80, 0xCC, 0xFF};
   int direction[7] = {1, 1, 1, 0, -1, -1, -1};
   //значения вольтажа из соответствующих резисторов
   int vl1, vl2;
   //направление вращения моторов
   // 0 - нет вращения
   // 1 - нормальное направление вращения
   // -1 - обратное вращение
   int dir1, dir2;

DDRB=0x07;  //Инициализация PB1 (OC1A) и PB2 (OC1B) как выход, а также PB0 - светодиод
DDRD=0xFF;  // все ножки порта D сделать выходами

//зажигаем светодиод
PORTB = PORTB | 0x01;

//Инициализируем ШИМ
  TCCR1A=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM10)|(1<<WGM12); //На выводе OC1A единица, когда OCR1A==TCNT1, восьмибитный ШИМ
  TCCR1B=(1<<CS10)|(1<<WGM12);//Делитель= /1

//Иницилиазируем АЦП
//воспользуемся значениями регистров, определенными ранее
  ADMUX = 0xC0;// Выбрали в качестве опорного напряжения внутренний 2.56В с кондером. 1100.0000 = 0xC0
  ADCSRA = 0x8F;

while (1)
  {

   //////////////////////////////////////////////////////////////////////////
   //       ВОЛЬТАЖ
   //////////////////////////////////////////////////////////////////////////
   vl1 = ADC_result(CHANNEL1);
   
   vl2 = ADC_result(CHANNEL2);
   
   for (i=0; i<8; i++)   
   {
      if (vl1 >= voltages[i])
      {
         //если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
         SHIM1 = power[i];
         dir1 = direction[i];
         break;//сбрасываем цикл, иначе дойдем до самого первого значения
      }
   }
   
   for (i=0; i<8; i++)   
   {
      if (vl2 >= voltages[i])
      {
         //если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
         SHIM2 = power[i];
         dir2 = direction[i];
         break;//сбрасываем цикл, иначе дойдем до самого первого значения
      }
   }
   
   //устанавливаем направление вращения
   //для ножек PD0 и PD1 (первый мотор) PD2 и PD3 (второй мотор)
   
   // ПЕРВЫЙ МОТОР
   // 10 00 - прямое вращение
   // 01 00 - обратное
   // 00 00 - СТОП
   
   // ВТОРОЙ МОТОР
   // 00 10 - прямой
   // 00 01 - обратный
   // 00 00 - СТОП
   
   PORTD = 0x00;//СТОП обоих моторов по умолчанию dir1=dir2=0
   
   if (dir1 == 1)
   {
      PORTD = PORTD | 0x08;
   }
   
   if (dir1 == -1)
   {
      PORTD = PORTD | 0x04;
   }
   
   if (dir2 == 1)
   {
      PORTD = PORTD | 0x02;
   }

   if (dir2 == -1)
   {
      PORTD = PORTD | 0x01;
   }


  }

}
Последний раз редактировалось BeInspired 01 дек 2011, 22:01, всего редактировалось 1 раз.

Re: АЦП Atmega8

Dmitry__ » 01 дек 2011, 21:29

ты бы код на форум между [code][/code] вставлял, приятнее читать :)

Re: АЦП Atmega8

BeInspired » 01 дек 2011, 22:02

Добавил тег. Просто сначала его поискал и не заметил.

Re: АЦП Atmega8

Dmitry__ » 01 дек 2011, 23:58

ого, дык это же программа (с тегом) :wink: :)
по мне, так все замечательно, лучшее - враг хорошего.

Ну, может если только:
unsigned int ADC_result(unsigned char adc_input)
int vl1, vl2;
vl1 = ADC_result(CHANNEL1);

Но это я придираюсь :)
Неси зачетку...

Re: АЦП Atmega8

BeInspired » 05 дек 2011, 20:37

Как снизить нагрузку на процессор? Мне джойстик надо опрашивать не чаще 20 раз в секунду, а все остальное время желательно чтобы он спал.. Дайте ссылку где почитать про это или подскажите куда копать (очень желательно с примерами на С).
При этом надо, чтобы не отрубался ШИМ.

Re: АЦП Atmega8

boez » 06 дек 2011, 14:16

Почитать - в даташите Power Modes. Так, чтоб спал и шимил - не уверен, что получится. А зачем собсно, электричества жалко?

Re: АЦП Atmega8

Radist » 06 дек 2011, 20:42

Интересно, зачем нужно усыплять МК? Если надо шимить - значит движки должны крутиться и жрать как минимум десятки мА. Бодрствующий МК жрет единицы мА, спящий - десятки мкА. Экономия единиц мА на фоне десятков - зачем? Обычно в сон МК вгоняют, когда изредка нужно что-то сделать, а питание батарейное и выключатель не вписывается в концепцию. Но вроде это не ваш случай.

Re: АЦП Atmega8

BeInspired » 07 дек 2011, 10:14

Моторы питаются отдельной цепью на 24 Вольта. А плата управления с МК - от 5 вольт (батарейки).


Rambler\'s Top100 Mail.ru counter