реклама на сайте
подробности

 
 
> Программирование АЦП на atmega16L-16AI, Помогите разобраться с считыванием данных с АЦП
abitwise
сообщение Apr 7 2011, 06:26
Сообщение #1





Группа: Участник
Сообщений: 14
Регистрация: 1-04-11
Пользователь №: 64 056



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

Исходные данные:

Есть - мк Atmega16L-16AI (TQFP)
- два вентилятора Jamicon (JF0825B1H), Evercool (EC6010H12B)
- два дачитка температуры типа NTC

Задача: необходимо считывать информацию с датчиков и выдавать на вентиляторы напряжения, соответствующие температурным диапазонам.
Так как я в этом деле новичок, то разбил задачу на три.
1) Научиться выдавать ШИМ на вентиляторы. С этим я худо бедно разобрался!

Вот код
Код
  Chip type           : ATmega16L
Program type        : Application
Clock frequency     : 8,00000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/


#include <mega16.h>
#include <delay.h>

// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
OCR1A=0x00;
OCR1B=0x00;
TCNT1L=0x00;  
PORTD=0b00110000;
}

//Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
   PORTD=0b00110000;

}

// Timer 1 output compare B interrupt service routine
interrupt [TIM1_COMPB] void timer1_compb_isr(void)

{
  PORTD=0b00110000;
    
}

void main(void)
{


// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTA=0x00;
DDRA=0x00;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;

// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=In Func6=In Func5=Out Func4=Out Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=0 State4=0 State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x30;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8192,000 kHz
// Mode: Fast PWM top=00FFh
// OC1A output: Non-Inv.
// OC1B output: Non-Inv.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: On
TCCR1A=0xA1;
TCCR1B=0x09;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x1C;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// ADC initialization
// ADC Clock frequency: 64,000 kHz
// ADC Voltage Reference: AREF pin
// ADC Auto Trigger Source: Timer1 Overflow
// Only the 8 most significant bits of
// the AD conversion result are used
//ADMUX=0b00000000;
//ADCSRA=0xF7;
//SFIOR&=0x1F;
//SFIOR|=0xC0;
SPCR=0x40;
SPSR=0x01;

#asm("sei")

while (1)
{
   OCR1B=100;
   OCR1A=100;
};
}


По части теории вопросов у меня пока что нет. Перед решением этой задачи были проведены расчёты и получены соответствующие значения регистров сравнения таймера/счётчика1, соответствующих температурным диапазонам. Также согласно формуле из даташита на атмегу получено число ADC.
Вот эти значения (приведу только один диапазон в целях экономии времени):

температура, С измерительное напряжение, В ADC OCR1A OCR1B
25 1.196 299 0 0
26 1.159 290 0 0
27 1.123 281 0 0
28 1.087 272 0 0
29 1.053 263 0 0
30 1.020 255 0 0

и т. д.
Со второй задачей справиться не получается. Заключается она в том, чтобы научиться работать с АЦП.
А именно как правильно считывать информацию из АЦП, где и какие прерывание нужны в данной задаче? То есть я хочу понять структуру кода на Си, которая позволяет это делать и как такую структуру писать.
Дело в том, что я пользуюсь CodeVisionAvr, который генерит код, если использовать Wizard. Это на мой взгляд удобно, но только в том, случае если есть полное понимание сгенеренного кода. У меня такого понимания нету.
Я пытался самостоятельно решить эту проблему и протестировать полученный результат в Протеус, как в решении первой задачи, но ничего не вышло
Вот код (всё тоже самое только с добавлением сгенеренного кода для АЦП):
Код


1-   #define ADC_VREF_TYPE 0x04                                
2-   // Read the 8 low significant bits
3-   // of the AD conversion result
4-   unsigned char read_adc(unsigned char adc_input)
5-   {
6-    ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);  
7-    Delay needed for the stabilization of the ADC input voltage
8-    delay_us(10);
9-    // Start the AD conversion
10-  ADCSRA=0b11100110;
11-  // Wait for the AD conversion to complete
12-  while ((ADCSRA & 0x40)==0);
13-  ADCSRA|=0x10;
14-  return ADCL;
15-  }





16-   // ADC interrupt service routine
17-   interrupt [ADC_INT] void adc_isr(void)
18-   {
19-   unsigned char adc_data_l;
20-   // Read the 8 low significant bits
21-   // of the AD conversion result
22-   adc_data_l = ADCL;

23-   if(adc_data_l==0xFF)
24-     {
25-       OCR1A = 255 - adc_data_l;
26-       OCR1B = 255 - adc_data_l;
27-     }
28-   else
29-     {
30-      OCR1A = adc_data_l;
31-      OCR1B = adc_data_l;
32-      };
33-   }


Хотелось бы узнать от опытных людей, для чего необходимо и что означают 1 и 6 строчки кода, а также 12 и 13.
Далее хотелось бы понять правильно ли я сделал, что в процедуре прерывания АЦП написал последовательность действий по присвоению значений регистрам сравнения ?

3) Третья задача пока вообще не рассматриволась. Заключается она в передаче данных из АЦП в процедуру сравнения полученного из АЦП числа.
У меня процедура сравнения выглядит следующим образом:
Код
int range[9]={299, 255, 217, 184, 132, 112, 95, 81};

if (range[0] <= adc_data_l)
  {
   OCR1AL=0;
   OCR1BL=0;
  };
if (range[1] <= adc_data_l)
  {
   OCR1AL=0;
   OCR1BL=0;
  };
if (range[2] <= adc_data_l < range[1])  
  {
   OCR1AL=128;
   OCR1BL=85;
  };
if (range[3] <= adc_data_l< range[2])  
  {
   OCR1AL=149;
   OCR1BL=96;
  };    
if (range[4] <= adc_data_l < range[3])  
  {
   OCR1AL=170;
   OCR1BL=106;
  };    
if (range[5] <= adc_data_l < range[4])  
  {
   OCR1AL=191;
   OCR1BL=138;
  };
if (range[6] <= adc_data_l < range[5])  
  {
   OCR1AL=213;
   OCR1BL=149;
  };  
if (range[7] <= adc_data_l < range[6])  
  {
   OCR1AL=234;
   OCR1BL=170;
  };
if (range[8] <= adc_data_l < range[7])  
  {
   OCR1AL=255;
   OCR1BL=255;
  };

Массив из значений регистров сравнения ввёл для удобства. Хотя может быть оно и сомнительное.
В какой части кода необходимо писать эту процедуру ? В бесконечном цикле while(1){}? Или в прерываниях? Поскольку переменная adc_data_l объявлена внутри функции, то использовать её как глобальную переменную нельзя. Как решается эта проблема?. То есть, допустим я прочитал АЦП и записал число в эту переменную, что нужно сделать чтобы можно было пользоваться этой переменной в процедуре сравнения?

Заранее благодарю за помощь! Если на форуме или в интернете уже есть идентичные темы, в которых предложены решения данных проблем, то прошу поделиться ссылкой. Я обыскался в инете на эту тему, но не нашёл ничего, чтобы мне объяснило или подсказало как решать и что делать.
Надеюсь на вашу помощь!
Go to the top of the page
 
+Quote Post
3 страниц V   1 2 3 >  
Start new topic
Ответов (1 - 14)
Палыч
сообщение Apr 7 2011, 07:06
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(abitwise @ Apr 7 2011, 10:26) *
Хотелось бы узнать от опытных людей, для чего необходимо и что означают 1 и 6 строчки кода, а также 12 и 13.

Строка 1: определяет константу для задания опорного напряжения (в Вашем случае - AVCC with external capacitor at AREF pin)
Строка 6: коммутирует на АЦП один из входов (номер входа определяется значением adc_input) и опорное напряжение
Строка 12: ожидание окончания преобразования (измерения)
Строка 13: сброс флага прерывания

Цитата(abitwise @ Apr 7 2011, 10:26) *
Далее хотелось бы понять правильно ли я сделал, что в процедуре прерывания АЦП написал последовательность действий по присвоению значений регистрам сравнения ?
Непонятно: зачем используете процедуру прерывания, раз в функции измерения (read_adc) дожидаетесь готовности, кроме того функция измерения возвращает код АЦП (регистр ADCL)...
Если Ваша программа только и должна делать, что измерять температуру и по её значению менять параметры ШИМ, то эти действия можно поместить в бесконечный цикл.
Go to the top of the page
 
+Quote Post
abitwise
сообщение Apr 7 2011, 09:19
Сообщение #3





Группа: Участник
Сообщений: 14
Регистрация: 1-04-11
Пользователь №: 64 056



Спасибо за ответ.
Если я вас правильно понял, то процедура прерывания не нужна, и достаточно функции read_adc, чтобы прочитать данные из АЦП?
В даташите сказано, что регистры ADCL и ADCH можно только читать. Следовательно, чтобы работать с полученным числом надо создать переменную, так ?
Где и как её объявить, чтобы я мог пользоваться ей в процедуре сравнения?


Не очень понял
Цитата
Строка 1: определяет константу для задания опорного напряжения (в Вашем случае - AVCC with external capacitor at AREF pin)
Строка 6: коммутирует на АЦП один из входов (номер входа определяется значением adc_input) и опорное напряжение

То есть вот здесь
Код
6- ... (ADC_VREF_TYPE & 0xff)

мы получаем число 0000 0100;
вот здесь
Код
adc_input | (ADC_VREF_TYPE & 0xff);

происходит побитовая логическая операция ИЛИ. И потом получившийся результат записывается в ADMUX?
И это число зависит от значения adc_input?

Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 7 2011, 09:50
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(abitwise @ Apr 7 2011, 13:19) *
Если я вас правильно понял, то процедура прерывания не нужна, и достаточно функции read_adc, чтобы прочитать данные из АЦП?
В процедуре прерывания в Вашем случае не вижу необходимости.

Цитата(abitwise @ Apr 7 2011, 13:19) *
В даташите сказано, что регистры ADCL и ADCH можно только читать. Следовательно, чтобы работать с полученным числом надо создать переменную, так ?
Где и как её объявить, чтобы я мог пользоваться ей в процедуре сравнения?
Примерно, как-то так:
Код
for(;;) // бесконечный цикл в main
{
  unsigned char adc_data_l;
  adc_data_l =  read_adc(_код_канала_первого_датчика_);
  // ......... в этом месте анализ значения adc_data_l и выставляем параметры ШИМ первого вентилятора (не написанная часть 3 Вашей программы)....
  adc_data_l =  read_adc(_код_канала_второго_датчика_);
  // ......... в этом месте анализ значения adc_data_l и выставляем параметры ШИМ второго вентилятора....
}

Цитата(abitwise @ Apr 7 2011, 13:19) *
Не очень понял...
Подозреваю, что должно быть
Код
#define ADC_VREF_TYPE 0x40


Еще хочу обратить внимание на то, что использованная Вами функция read_adc возвращает только младшие 8 бит результата преобразования (измерения) АЦП, а в намётках третьей части Вашей программы для сравнения используются целые числа, бОльшие чем 255. Наверное, нужно внести изменения:
Код
unsigned int read_adc(unsigned char adc_input)
{
..............
  return ADC;
}

и, тогда
Код
for(;;) // бесконечный цикл в main
{
  unsigned  int  adc_data;
  adc_data =  read_adc(_код_канала_первого_датчика_);
  // .........
  adc_data =  read_adc(_код_канала_второго_датчика_);
  // .........
}
Go to the top of the page
 
+Quote Post
abitwise
сообщение Apr 7 2011, 10:51
Сообщение #5





Группа: Участник
Сообщений: 14
Регистрация: 1-04-11
Пользователь №: 64 056



С каждым вашим ответом, ситуация в голове проясняется всё больше.) Спасибо!
Цитата
Подозреваю, что должно быть
Код
#define ADC_VREF_TYPE 0x40

Схема питается от внешнего источника опорного напряжения, сигналы с датчиков поступает на входы ADC4 ADC5, выравнивание вправо, соответственно биты Ref1=0; Ref0=0. Бит ADLAR=0.

Сообщение отредактировал abitwise - Apr 7 2011, 10:52
Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 7 2011, 14:42
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(abitwise @ Apr 7 2011, 14:51) *
биты Ref1=0; Ref0=0
Тогда
Код
#define ADC_VREF_TYPE  0
Собственно - эта константа и содержит эти два бита

Цитата(abitwise @ Apr 7 2011, 14:51) *
сигналы с датчиков поступает на входы ADC4 ADC5
Параметр функции read_adc следует задавать 4 и 5 соответственно.

При разработке Вами части 3 программы, вероятно, следует предусмотреть гистерезис. Иначе, при температуре близкой к границе участка возможны "интересные" эффекты при управлении вентиляторами.
Go to the top of the page
 
+Quote Post
abitwise
сообщение Apr 7 2011, 17:37
Сообщение #7





Группа: Участник
Сообщений: 14
Регистрация: 1-04-11
Пользователь №: 64 056



Ясно! Уже учёл. Спасибо!

Вот код, который получился с учётом перечисленных выше поправок и подсказок, посмотрите, может быть найдёте ошибки (скорей всего_) ):
Код
/*****************************************************
Chip type           : ATmega16L
Program type        : Application
Clock frequency     : 8,00000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include <mega16.h>
#include <delay.h>

interrupt [TIM1_OVF] void timer1_ovf_isr(void) // Прерывание Т/С1 по переполнению
{
    OCR1A=0x00;
    OCR1B=0x00;
// TCNT1L=0x00;  
// PORTD=0b00110000;
}


interrupt [TIM1_COMPA] void timer1_compa_isr(void) // Прерывание Т/С1 по совпадению события А
{
   PORTD=0b00100000;
}

interrupt [TIM1_COMPB] void timer1_compb_isr(void) // Прерывание Т/С1 по совпадению события Б
{
   PORTD=0b00010000;
}


#define ADC_VREF_TYPE 0x00

unsigned char read_adc(unsigned char adc_input) // Считывание 8 МЗР
                                                                        // преобразования АЦП
{                                              
  ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
  delay_us(10);                                 // Задержка для стабилизации входного напряжения АЦП
  ADCSRA|=0b11100111;                           // Начало АЦП
  while ((ADCSRA & 0x10)==0);                   // Ожидание завершения преобразования
  ADCSRA|=0x10;                                 // Сброс фалга прерывания
  return ADCL;                                  // Возвращает значение 8 МЗР
}


                    
void main(void)
{
int temp_range[9] = {299, 255, 217, 184, 156, 132, 112, 95, 81};
  
PORTA=0x30; // Инициализация порта А;  
DDRA=0x00;  // PA4, РА5 - вход с подтянутым резистором

PORTB=0x00; // Инициализация порта Б
DDRB=0x00;

PORTC=0x00; // Инициализация порта С
DDRC=0x00;

PORTD=0x00; // Инициализация портп Д;  
DDRD=0x30;  // PD4, PD5 выходы с низким логическим уровнем;

TCCR0=0x00; // Инициализация Т/С0
TCNT0=0x00;
OCR0=0x00;


TCCR1A=0xA1; // Инициализация T/C1
TCCR1B=0x0B; // Инициализация T/C1
ICR1H=0x00;  // Тактирование системное
ICR1L=0x00;  // Частота: 8000,000 kHz
                     // Режим: Fast PWM top=00FFh
                     // OC1A : не инвертированный.
                     // OC1B : не инвертированный.
                     // Прерывание по переполнению
                     // Прерывание по совпадению А
                     // Прерывание по совпадению Б



ASSR=0x00;   // Инициализация Т/C2
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

             // Внешние прерывания отключены
              
MCUCR=0x00;
MCUCSR=0x00;

TIMSK=0x1C;  // Инициализация прерываний таймера счётчика

ACSR=0x80;   // Аналоговый компаратор выключен
SFIOR=0x00;  // АЦП в непрерывном режиме





ADMUX=0b00000000;    // Инициализация АЦП
ADCSRA=0b00000000;   // Частота тактирования 125,000 kHz
                                    // Источник питания: AREF pin
                                   // Используются только 8 МЗР


SPCR=0x40;   // Инициализация интерфейса SPI
SPSR=0x01;
#asm("sei")  // Рарешение прерываний

for (;;)     // Бесконечный цикл
{
   unsigned char adc_data_l;
  
   TCNT1H=0x00; // обнуление таймера счётчика на всякий случай
   TCNT1L=0x00;
  
   OCR1AH=0x00; // обнуление регистров сравнения на всякий случай
   OCR1AL=0x00;
   OCR1BH=0x00;
   OCR1BL=0x00;

   adc_data_l=0; // Обнуление переменной на всякий случай                        
  
   adc_data_l=read_adc(5); // Запись данных из АЦП с ноги PA5
  
   if(temp_range[0] <= adc_data_l)  // Процедура сравнения полученного значения АЦП
     {                                             // с температурными диапазонами и
      OCR1A=0;                              // выдача ШИМ на вентилятор
     };
   if(temp_range[1] <= adc_data_l)
     {
      OCR1A=0;
     };
   if(temp_range[2] <= adc_data_l < temp_range[1])
     {
      OCR1A=128;
     };
   if(temp_range[3] <= adc_data_l < temp_range[2])
     {
      OCR1A=149;
     };        
   if(temp_range[4] <= adc_data_l < temp_range[3])
     {
      OCR1A=170;
     };
   if(temp_range[5] <= adc_data_l < temp_range[4])
     {
      OCR1A=191;
     };
   if(temp_range[6] <= adc_data_l < temp_range[5])  
     {
      OCR1A=213;
     };
   if(temp_range[7] <= adc_data_l < temp_range[6])  
     {
      OCR1A=234;
     };
   if(temp_range[8] <= adc_data_l < temp_range[7])  
     {
      OCR1A=255;
     };  
  
   adc_data_l=read_adc(4); // Запись данных из АЦП с ноги PA4

   if(temp_range[0] <= adc_data_l)  // Процедура сравнения полученного значения АЦП
     {                                              // с температурными диапазонами и
      OCR1B=0;                               // выдача ШИМ на вентилятор
     };
   if(temp_range[1] <= adc_data_l)
     {
      OCR1B=0;
     };
   if(temp_range[2] <= adc_data_l < temp_range[1])
     {
      OCR1B=128;
     };
   if(temp_range[3] <= adc_data_l < temp_range[2])
     {
      OCR1B=149;
     };        
   if(temp_range[4] <= adc_data_l < temp_range[3])
     {
      OCR1B=170;
     };
   if(temp_range[5] <= adc_data_l < temp_range[4])
     {
      OCR1B=191;
     };
   if(temp_range[6] <= adc_data_l < temp_range[5])  
     {
      OCR1B=213;
     };
   if(temp_range[7] <= adc_data_l < temp_range[6])  
     {
      OCR1B=234;
     };
   if(temp_range[8] <= adc_data_l < temp_range[7])  
     {
      OCR1B=255;
     };      
};
}

По поводу 10 бит АЦП. Дело в том, что это программа являет собой решение в первом приближении, поэтому высокая точноть преобразования пока не нужна. Поэтому я пользуюсь только данными регистра ADCL. С гистерезисом обязательно подумаю.

















Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 8 2011, 04:04
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Проверку на попадание в диапазон Вы записали в виде
Код
if(A <= X < B)
Такая запись не даст ожидаемый Вами результат. Следует исправить на такой вид
Код
if((A <= X) && (X < B))
Go to the top of the page
 
+Quote Post
XVR
сообщение Apr 8 2011, 08:56
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цепочка из if'ов - это жесть. wacko.gif Для кого в С циклы придумали?
Go to the top of the page
 
+Quote Post
abitwise
сообщение Apr 8 2011, 09:01
Сообщение #10





Группа: Участник
Сообщений: 14
Регистрация: 1-04-11
Пользователь №: 64 056



Можете предложить другой вариант решения ?
Цепочка if-в была выбрана по совету моего товарища, который сослался на то, что циклы типа while, for и др. могут подзагрузить процессор.
Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 8 2011, 09:34
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(abitwise @ Apr 8 2011, 13:01) *
Цепочка if-в была выбрана по совету моего товарища, который сослался на то, что циклы типа while, for и др. могут подзагрузить процессор.
Если Вы описали все функции Вашего устройства, то, собственно, загрузка то и не велика, как не напиши программу... Цикл, возможно, будет выполняться несколько медленнее, но не для того кода, что привели Вы: в Вашем случае будет произведена проверка на попадание в каждый диапазон, когда (надеюсь, что это Вам понятно) значение переменной adc_data_l может лежать внутри только одного диапазона. В случае цепочки if, это решается так
Код
  if(temp_range[0] <= adc_data_l)  // Процедура сравнения полученного значения АЦП
     {                                             // с температурными диапазонами и
      OCR1A=0;                              // выдача ШИМ на вентилятор
     }
   else if(temp_range[1] <= adc_data_l)
     {
      OCR1A=0;
     }
   else if((temp_range[2] <= adc_data_l) && (adc_data_l< temp_range[1]))
     {
      OCR1A=128;
     }
   else if((temp_range[3] <= adc_data_l) && (adc_data_l < temp_range[2]))
................... и т.д.
В случае использования цикла - применяют оператор break, для того, чтобы не перебирать ненужные диапазоны, раз уж нашли подходящий диапазон.

Кстати, проверку в if в виде двух условий (типа А<=X<B), можно заменить на одно условие, беря в расчет то, что границы диапазонов расположены в массиве по убыванию (т.е. второе условие было проверено предыдущим оператором if)
Код
  if(temp_range[0] <= adc_data_l)  // Процедура сравнения полученного значения АЦП
     {                                             // с температурными диапазонами и
      OCR1A=0;                              // выдача ШИМ на вентилятор
     }
   else if(temp_range[1] <= adc_data_l)
     {
      OCR1A=0;
     }
   else if(temp_range[2] <= adc_data_l)
     {
      OCR1A=128;
     }
   else if(temp_range[3] <= adc_data_l)
................... и т.д.
При этом видно, что этот кусок программы хорошо "ложится" под цикл
Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 8 2011, 11:21
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Ещё обратите внимание, что Ваша программа "затупит" при показаниях АЦП менее чем 81, поскольку отсутствует интервал от нуля до 81 (т.е. либо интервалы должны охватывать все возможные значения АЦП, либо должна быть ветвь программы выполнения неких действий, когда значение АЦП не попадает в заданные Вами интервалы).
Go to the top of the page
 
+Quote Post
abitwise
сообщение Apr 8 2011, 11:42
Сообщение #13





Группа: Участник
Сообщений: 14
Регистрация: 1-04-11
Пользователь №: 64 056



Спасибо. Уже обратил)
Буду пробовать прошивать...
По результатам обязательно отпишусь!
Благодарю за советы и помощь!
Go to the top of the page
 
+Quote Post
abitwise
сообщение Apr 9 2011, 18:36
Сообщение #14





Группа: Участник
Сообщений: 14
Регистрация: 1-04-11
Пользователь №: 64 056



Прошил атмегу. Но программа по заданному алгоритму не работает!
После подачи питания происходит включение вентилиторов на полную. На ножках PD4, PD5 никакого ШИМ-а нету, есть напряжение 12 В. Видимо просто выдаётся логическая еденица и всё.
Есть подозрения, что АЦП вообще не включается и ничего не преобразовывает. К сожалению контрольных точек на плате для того, чтобы осцилографом посмотреть, что на ногах PA4, PA5, нету.
Как вы думаете, в чём может быть дело???
Может быть дело в том, что я в программе неправильно или не в том месте инициализировал регистры ADCSRA и ADMUX ?
Насколько я понимаю, в теле бесконечного цикла for(;;) когда я записываю значение АЦП в переменную adc_data_l
Код
adc_data_l=read_adc(5); // Запись данных из АЦП с ноги PA5

должна вызываться функция read_adc() со значением параметра 5, и в ходе вызова должна произойти инициализация регистров ADCSRA и ADMUX.
Может быть я зря вот здесь:
Код
ADMUX=0b00000000;    // Инициализация АЦП
ADCSRA=0b00000000;   // Частота тактирования 125,000 kHz
                                    // Источник питания: AREF pin
                                   // Используются только 8 МЗР

установил все биты в ноль ?
Go to the top of the page
 
+Quote Post
Xenia
сообщение Apr 9 2011, 20:29
Сообщение #15


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(abitwise @ Apr 7 2011, 21:37) *
По поводу 10 бит АЦП. Дело в том, что это программа являет собой решение в первом приближении, поэтому высокая точноть преобразования пока не нужна. Поэтому я пользуюсь только данными регистра ADCL.

Мне кажется, что даже в том случае, если вам достаточно данных младшего регистра ADCL, старший регистр ADCH вы читать тоже обязаны, если хотите, чтобы данные обновлялись.
Цитирую пункт 25.8.3.2 даташита:
Цитата
When ADCL is read, the ADC Data Register is not updated until ADCH is read. Consequently, if the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read ADCH. Otherwise, ADCL must be read first, then ADCH.

Да и не сложное это дело, прочесть обе части регистра - читайте регистр ADC целиком, без указания H или L, и тогда компилятор прочтет оба байта. А присваивать полученное значение можете и однобайтной переменной - тогда старший байт обрежется.
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 15:18
Рейтинг@Mail.ru


Страница сгенерированна за 0.01563 секунд с 7
ELECTRONIX ©2004-2016