Поскольку я не профессиональный программист и до этого момента с микроконтроллерами общался постольку поскольку, то обращаюсь к знающим и опытным людям с просьбой помочь разобраться с поставленной передо мной задачей.
Исходные данные:
Есть - мк 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;
};
}
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;
};
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 объявлена внутри функции, то использовать её как глобальную переменную нельзя. Как решается эта проблема?. То есть, допустим я прочитал АЦП и записал число в эту переменную, что нужно сделать чтобы можно было пользоваться этой переменной в процедуре сравнения?
Заранее благодарю за помощь! Если на форуме или в интернете уже есть идентичные темы, в которых предложены решения данных проблем, то прошу поделиться ссылкой. Я обыскался в инете на эту тему, но не нашёл ничего, чтобы мне объяснило или подсказало как решать и что делать.
Надеюсь на вашу помощь!