Технический форум по робототехнике.
1. Человек не умеет отлаживать устройства.
HarryStar писал(а):В обрабочике у вас работа с ЖКИ - это не быстрый процесс и опрос ацп - тоже не айс. Работу с ЖКИ надо точно выносить из прерывания.
Про отладку вам правильно написали - посмотрите что происходит в протеусе или вмлабе.
"...
и в конце дебильный вопрос который возник при чтении кучи примеров программ разных авторов:
- в самом прерывании по переполнении - например таймера0 - надо-ли в первой строке писать TCCNT0=число ? Я так понял что если общий коэфф. пересчета мне не нужно менять - то достаточно в начале main процедуры проинитить все три таймера, и больше их не трогать - т.е. для шима я просто в регистр OCR2 ложу нужное число и получаю изменение скважности - и в прерывании по сравнению - просто меняю ногу порта с 1 на 0. Или надо каждый раз при входе в прогу по прерыв.по переполнению опять устанавливать "до куда считаем"?
..."
...
...
unsigned char RA;
...
// Read the AD conversion result
#define FIRST_ADC_INPUT 0
#define LAST_ADC_INPUT 7
unsigned int adc_data[LAST_ADC_INPUT-FIRST_ADC_INPUT+1];
#define ADC_VREF_TYPE 0xC0
// ADC interrupt service routine
// with auto input scanning
interrupt [ADC_INT] void adc_isr(void)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
input_index=0;
ADMUX=(FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff))+input_index;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}
// interrupt [TIM0_OVF] - это основной цикл управления током
// происходит XXX раз в секунду (Гц)
// тут мы управляем данными OCR2 (TIMER2) - который дергает порт PORTB.1
//
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// сначала заново заносим данные в TIMER0 --- !!!
TCNT0=0xFE; // XXX Гц задаем переключение по таймеру
// + включен делитель на ... от 16Мгц
t0Uout=adc_data[7]; // замер напряжения идущего к ...
t0Iout=adc_data[6]; // замер тока идущего к ...
.............моя короткая программа тут
в которой вычисляется значение для переменной RA, которое потом записывается(кем правильно писать???) в OCR2 ................
}
// Timer2 overflow interrupt [TIM2_OVF] service routine
// тут мы дергаем портом PORTB.1
// по сигналу "совпадение"
// частота 16Мгц/(8 делителя TCCR2 * TCNT2)
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
TCNT2=0xFF; // Reinitialize Timer2 value
OCR2=RA;
PORTB.1=0; // выключаем порт Б - ШИМим оптопару. !!!
}
// Timer2 output compare interrupt service routine
interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
//OCR2=RA;
PORTB.1=1; // включаем порт Б - ШИМим оптопару.
}
// interrupt [TIM1_OVF] - это основной цикл прерывания.
// occurs every (=fhz) seconds
// xtal = quartz crystal frquency [Hz]
// frequency [Hz]
// дальше по этих частях секунды уже отсчитывается все остальное что требует учета времени.
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// сначала заново заносим данные в TIMER0 --- !!!
#asm("cli") // Global Interrupt Disable !!!
TCNT1=0x10000-(xtal/1024/fhz); // fhz= Гц задаем переключение по таймеру
// 1024 - коэф. пересчета записан в регистр: TCCR0=0x05;
#asm("sei") // Global Interrupt Enable !!!
Uout1=adc_data[7]; // читаем напряжение
.......тут моя программка короткая....
...переделал прогу под прерывания от АЦП ....
}
...
void main(void)
{
#asm("cli") // Global Interrupt Disable !!!
...
// Timer/Counter 0 initialization
// Clock source: System Clock
//TCCR0=0x05; // == clk/1024 (From prescaler)
//TCCR0=0x01; // == No prescaler
//TCCR0=0x02; // == clk/8 (From prescaler)
TCCR0=0x03; // == clk/64 (From prescaler) == 64+255== 735 Гц
//TCCR0=0x04; // == clk/256 (From prescaler) == 256+255== 183 Гц
//TCCR0=0x05; // == clk/1024 (From prescaler)
TCNT0=0xFE; // 255 + Коэфф.пересчета
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 16000,000 kHz
// Mode: Normal top=0xFFFF
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00; //
TCCR1B=0x05; // clk/1024 (prescaling)
TCNT1H=0xE9; // старший байт 0xE91D==0.5sek(12Mhz)
TCNT1L=0x1D; // это расчет на полсекунды
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Mode: Fast PWM top=0xFF
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x49; // делитель x1 быстрый ШИМ
//TCCR2=0x41; // фазокорректный PWM, делитель x1
//TCCR2=0x42; // фазокорректный PWM, делитель x8
//TCCR2=0x43; // фазокорректный PWM, делитель x32
//TCCR2=0x44; // фазокорректный PWM, делитель x64
TCNT2=0xFF;
OCR2=0xF0;
// Timer(s)/Counter(s) Interrupt(s) initialization
//TIMSK=0x41; // Bit 0=1 -- TOIE0: Timer/Counter0 Overflow Interrupt Enable
// Bit 6=1 -- TOIE2: Timer/Counter2 Overflow Interrupt Enable
TIMSK=0xC5; // разрешено прерывание по Т0 Т1 и Т2+сравнение OCR2
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// ADC initialization
// ADC Clock frequency: 1000,000 kHz
// ADC Voltage Reference: Int., cap. on AREF
ADMUX=FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff);
ADCSRA=0xCC;
...
//WDTCR=0x0E;
WDTCR=0x0F; // 2.1 секунды
// Global enable interrupts
#asm("sei")
...
........моя программа еще.....................
...................
Alex_Sor писал(а):но мои простые опыты "до того" показали что всё прекрасно успевает на 16Мгц.
Alex_Sor писал(а):в самом прерывании по переполнении - например таймера0 - надо-ли в первой строке писать TCCNT0=число ?
Alex_Sor писал(а)::) человек отлаживал устройства на процессоре 1801ВМ1 когда вы еще не родились в этом мире.
...просто прошло много времени и пришлось опять вспоминать "С" и разбираться с Атмегой...
Я бы таймер 1 ликвидировал и вынес это добро в основной цикл программы, где в начале времени цикла засекал бы таймер, а в конце через delay делал паузу, добивающую до 0.5сек, если хочется стабильности.
Michael_K писал(а):Alex_Sor писал(а):в самом прерывании по переполнении - например таймера0 - надо-ли в первой строке писать TCCNT0=число ?
У этого таймера - куча режимов работы. В некоторых он сам перезагружается.
Но в самом простом случае (который вы и используете) - да, надо писать руками.
Это имеет смысл, если прескейлер достаточно большой (как у вас, например).
Alex_Sor писал(а):...я всегда не любил пути "спрыгнуть" с красивого решения на "быструю подпорку" в программировании - вы уж извините, "старая школа" дает знать...
Alex_Sor писал(а):а в Таймере1 и Таймере2 ?
=DeaD= писал(а):Или в AVR-ке есть какие-то более приличные способы?
Michael_K писал(а):Alex_Sor писал(а):а в Таймере1 и Таймере2 ?
В данном случае точно так же.
Что-то так и не понял, как используется (и используется ли вообще) прерывание от АЦП.
..................
// Read the AD conversion result
#define FIRST_ADC_INPUT 0
#define LAST_ADC_INPUT 7
unsigned int adc_data[LAST_ADC_INPUT-FIRST_ADC_INPUT+1];
#define ADC_VREF_TYPE 0xC0
// ADC interrupt service routine
// with auto input scanning
interrupt [ADC_INT] void adc_isr(void)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
input_index=0;
ADMUX=(FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff))+input_index;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}
.................
В вашем случае нужно обязательно не разрешать прерывания T0/T1 внутри прерываний T1/T0.
По-моему, вы совершенно напрасно не хотите использовать аппаратный шим (от T1) на PORTB.1.
Это cамый прямой (и правильный) способ развязать ваш "узел".
Я бы, честно, сделал в основном цикле проверку флага от медленного прерывания и его обработку
Можно завязать все на один таймер.
То есть в обработчике T0 вести счетчик... Ну например 2499 раз измеряем ток-напряжение, а на 2500 - чего-то там еще...
Но это уже "извращения на любителя".
=DeaD= писал(а):Или в AVR-ке есть какие-то более приличные способы?
В АВР-ке по умолчанию прерывания внутри обработчиков прерываний запрещены... Но что там генерит компилятор - вопрос еще тот.
В GCC раньше различались слова SIGNAL и INTERRUPT (различались именно тем, что внутри одного прерывания запрещены, а внутри другого разрешены). Как в CVAVR - не знаю.