Ведь таймер в это время тикает.
И может быть ситуация, что к тому времени, когда все уже считано и собираетесь вернуться внутрь, поступает новый запрос от таймера - и оно там так и висит в том прерывании.
Код: Выделить всё
#include <stdio.h>
#include <avr\interrupt.h>
volatile unsigned char counter = 0, counter2 = 0; // можно int, long
volatile unsigned int adc_data = 0;
// Timer 0 по совпадению с регистром сравнения А
ISR(TIMER0_COMPA_vect)
{
ADCSRA |= (1<<ADSC);
}
// ADC по готовности результата
ISR(ADC_vect)
{
adc_data = ADCW; // получает результат 1 раз. выдает постоянно только его
PORTA = adc_data; // (для контроля изменений результата измерения - будет отображать только первое измеренное значение)
counter++;
PORTB = counter; // Отображает младший байт счетчика. (0x01, значение не меняется)
}
int main (void)
{
// Инициализация
{
// Скобка, чтобы можно было свернуть и не мозолило глаза
// Предделитель тактовой частоты - 8
// CLKPR=0x80;
asm ("push r20"); // Предварительно сохраняем регистр. Но обычно студия его не использует
asm ("ldi r20, 0x80");
asm ("sts 0x61, r20");
//CLKPR=0x03;
asm ("ldi r20, 3");
asm ("sts 0x61, r20");
asm ("pop r20"); // Загружаем обратно в регистр его исходное значение
// сделано через ассемблер потому, что на низких уровнях оптимизации бывает после первой команды
// проходит более 4 тактов и предделитель не устанавливается
// Отключаем сторожевой таймер
asm ("eor r1,r1"); // очистка регистра r1. Ни на что не влияет. студия всегда держит в нем 0
MCUCR = 0; //сброс флагов регистра (необходим для отключения стор. таймера) (см. даташит)
asm ("wdr");
WDTCSR |= 0b11000; // собственно, отключение стор. таймера (фузы установлены "как надо") ;)
asm ("sts 0x60,r1"); //WDTCSR
// Порты на выход
DDRA = 0xFF;
DDRB = 0xFF;
DDRC = 0xFF;
// Timer 0
TCCR0A = 0b00000010; // COM0A1 | COM0A0 | COM0B1 | COM0B0 | --- | --- | WGM01 | WGM00 Режим сброс по совпадению
TCCR0B = 0b00000010; // FOC0A | FOC0B | --- | --- | WGM02 | CS02 | CS01 | CS00 Предделитель 8 (125 кГц)
TCNT0 = 0;
OCR0A = 15; // Сброс на 15 такт. деление частоты таймера на (15+1). Зона значений появления бага от 12 до 19. (если память не изменяет)
OCR0B = 0;
// Timer 0 Инициализация прерывания
TIMSK0 = 0b10; // по совпадению с А. OCIE0B | OCIE0A | TOIE0
// Инициализация АЦП
ADMUX = 0; // REFS1 | REFS0 | ADLAR | MUX4 - MUX0 источник питания АЦП - питание контроллера,
// выравнивания вправо нет, вход ADC0 (порт PF0)
ADCSRA=0b11001011; // ADEN | ADSC | ADATE | ADIF | ADIE | ADPS2-ADPS0 вкл, первый запуск, одиночный режим, разрешить прерывания
// при установке ADATE в 1 (авто режим) баг не проявляется.
while (ADCSRA & (1<<ADIF)); //Ожидание конца первого измерения (25 тактов)
ADCSRA |= (1<<ADIF); // Сброс флага прерывания
TIFR0 = 0xFF; // Сброс флагов от таймера
}
asm ("sei"); // разрешаем прерывания
while (1) // Основной цикл программы. Выполняется всегда
{
counter2++;
PORTC = counter2; // Младший байт counter в порт. Также будет отображать только 0x01
}
}
urry писал(а):Я, конечно, извиняюсь, но раз там все до тактов рассчитано - вы учитываете время входа в прерывание ?
Ведь таймер в это время тикает.
И может быть ситуация, что к тому времени, когда все уже считано и собираетесь вернуться внутрь, поступает новый запрос от таймера - и оно там так и висит в том прерывании.
Код: Выделить всё
unsigned char counter2 = 0; // можно int, long
volatile unsigned char counter = 0;
volatile unsigned int adc_data = 0;
И учтите ещё пролог/эпилог Си-шной функции.6.7.1
Interrupt Response Time
The interrupt execution response for all the enabled AVR interrupts is four clock cycles mini-
mum.
Не выдает постоянно, а выдал один раз.adc_data = ADCW; // получает результат 1 раз. выдает постоянно только его
Потому что в это прерывание больше ни разу не попали.PORTB = counter; // Отображает младший байт счетчика. (0x01, значение не меняется)
В протеусе счетчик нормально крутится. В железе нет?PORTC = counter2; // Младший байт counter в порт. Также будет отображать только 0x01
я говорил без учета пролога. Именно время входа в прерывание. точно не помню, но не более 10 тактов 100% там идет дальний джамп и сохранение счетчика.Kavka писал(а):Да, конечно, волатайл! )) Забыл просто. Но это не суть. в той программе было указано. поправилА может вот так?
Добавил
И учтите ещё пролог/эпилог Си-шной функции.Про 2-3 такта это вы погорячились.
6.7.1
Interrupt Response Time
The interrupt execution response for all the enabled AVR interrupts is four clock cycles mini-
mum.
Читал. Но это довольно специфический регистр и назначение его битов от модели к модели меняется. К тому же, заглянул в даташит и не нашел там его вовсе для 2560. Что там должно быть в нем? ))Kavka писал(а):ИС-пытатель, а про SFIOR (Special FunctionIO Register) не читали? Может так будет проще запускать АЦП?
Как ни странно, да! ))) Меня по началу тоже это смутило. И я долго искал, где накосячил. Не нашел.. )) К тому же, не люблю Протеус. Он тоже БАГ на БАГе весь. Железо надежней!a_skr писал(а):Не выдает постоянно, а выдал один раз.adc_data = ADCW; // получает результат 1 раз. выдает постоянно только егоПотому что в это прерывание больше ни разу не попали.PORTB = counter; // Отображает младший байт счетчика. (0x01, значение не меняется)
pps. поставьте точку останова в протеусе. Один раз попали и все. Протеус тоже баг эмулирует?![]()
в 2560 крутится, а в 168 нетps.В протеусе счетчик нормально крутится. В железе нет?PORTC = counter2; // Младший байт counter в порт. Также будет отображать только 0x01![]()
А вот и нет. Это раз.ppps. Запускаете АЦП чаще, чем заканчивается преобразование.
Код: Выделить всё
.cseg
__vectors:
jmp __ctors_end
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
jmp __vector_21 ; Таймер
reti
reti
reti
reti
reti
reti
reti
jmp __vector_29 ; АЦП
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
__ctors_end: // установка стека
ldi r28, 0xFF ; 255
ldi r29, 0x21 ; 33
out SPH, r29 ; 62
out SPL, r28 ; 61
jmp main ; 0x17c <main>
__vector_21:
;volatile unsigned char counter = 0, counter2 = 0; // можно int, long
;volatile unsigned int adc_data = 0;
;// Timer 0 по совпадению с регистром сравнения А
;ISR(TIMER0_COMPA_vect)
;{
push r1 ; Вот эти нехорошие команды, которые блочат контроллер!!! Если закомментировать
push r0 ; все команды Push и Pop, то все работает ОК
in r0, 0x3f ; 63
push r0 ; причем, это не обязательно Push и Pop
in r0, 0x3b ; 59
push r0 ; блокирует любое обращение к регистрам SPL и SPH!!!!!!!!
eor r1, r1
push r24
push r30
push r31
;ADCSRA |= (1<<ADSC);
ldi r30, 0x7A ; 122
ldi r31, 0x00 ; 0
ld r24, Z
ori r24, 0x40 ; 64
st Z, r24
;}
pop r31
pop r30
pop r24
pop r0
out 0x3b, r0 ; 59
pop r0
out 0x3f, r0 ; 63
pop r0
pop r1
reti
__vector_29:
;// ADC по готовности результата
;ISR(ADC_vect)
;{
push r1 ; А вот здесь команды Push и Pop на баг не влияют.
push r0 ; Хотя они здесь не особо нужны и их тоже можно выкинуть
in r0, 0x3f ; 63
push r0
eor r1, r1
push r24
push r25
;adc_data = ADCW; // получает результат 1 раз. выдает постоянно только его
lds r24, 0x0078
lds r25, 0x0079
sts 0x0203, r25
sts 0x0202, r24
;PORTA = adc_data; // (для контроля изменений результата измерения - будет отображать только первое измеренное значение)
lds r24, 0x0202
lds r25, 0x0203
out 0x02, r24 ; 2
;counter++;
lds r24, 0x0200
subi r24, 0xFF ; 255
sts 0x0200, r24
;PORTB = counter; // Отображает младший байт счетчика. (0x01, значение не меняется)
lds r24, 0x0200
out 0x05, r24 ; 5
;}
pop r25
pop r24
pop r0
out 0x3f, r0 ; 63
pop r0
pop r1
reti
main:
;{
;// Скобка, чтобы можно было свернуть и не мозолило глаза
;// Предделитель тактовой частоты - 8
;// CLKPR=0x80;
;asm ("push r20"); // Предварительно сохраняем регистр. Но обычно студия его не использует
push r20
;asm ("ldi r20, 0x80");
ldi r20, 0x80 ; 128
;asm ("sts 0x61, r20");
sts 0x0061, r20
;//CLKPR=0x03;
;asm ("ldi r20, 3");
ldi r20, 0x03 ; 3
;asm ("sts 0x61, r20");
sts 0x0061, r20
;asm ("pop r20"); // Загружаем обратно в регистр его исходное значение
pop r20
;// сделано через ассемблер потому, что на низких уровнях оптимизации бывает после первой команды
;// проходит более 4 тактов и предделитель не устанавливается
;// Отключаем сторожевой таймер
;asm ("eor r1,r1"); // очистка регистра r1. Ни на что не влияет. студия всегда держит в нем 0
eor r1, r1
;MCUCR = 0; //сброс флагов регистра (необходим для отключения стор. таймера) (см. даташит)
out 0x35, r1 ; 53
;asm ("wdr");
wdr
;WDTCSR |= 0b11000; // собственно, отключение стор. таймера (фузы установлены "как надо") ;)
lds r24, 0x0060
ori r24, 0x18 ; 24
sts 0x0060, r24
;asm ("sts 0x60,r1"); //WDTCSR
sts 0x0060, r1
;// Порты на выход
;DDRA = 0xFF;
ldi r24, 0xFF ; 255
out 0x01, r24 ; 1
;DDRB = 0xFF;
out 0x04, r24 ; 4
;DDRC = 0xFF;
out 0x07, r24 ; 7
;// Timer 0
;TCCR0A = 0b00000010; // COM0A1 | COM0A0 | COM0B1 | COM0B0 | --- | --- | WGM01 | WGM00 Режим сброс по совпадению
ldi r24, 0x02 ; 2
out 0x24, r24 ; 36
;TCCR0B = 0b00000010; // FOC0A | FOC0B | --- | --- | WGM02 | CS02 | CS01 | CS00 Предделитель 8 (125 кГц)
out 0x25, r24 ; 37
;TCNT0 = 0;
out 0x26, r1 ; 38
;OCR0A = 15; // Сброс на 15 такт. деление частоты таймера на (15+1). Зона значений появления бага от 12 до 19. (если память не изменяет)
ldi r25, 0x0F ; 15
out 0x27, r25 ; 39
;OCR0B = 0;
out 0x28, r1 ; 40
;// Timer 0 Инициализация прерывания
;TIMSK0 = 0b10; // по совпадению с А. OCIE0B | OCIE0A | TOIE0
sts 0x006E, r24
;// Инициализация АЦП
;ADMUX = 0; // REFS1 | REFS0 | ADLAR | MUX4 - MUX0 источник питания АЦП - питание контроллера,
sts 0x007C, r1
;// выравнивания вправо нет, вход ADC0 (порт PF0)
;ADCSRA=0b11001011; // ADEN | ADSC | ADATE | ADIF | ADIE | ADPS2-ADPS0 вкл, первый запуск, одиночный режим, разрешить прерывания
ldi r24, 0xCB ; 203
sts 0x007A, r24
;// при установке ADATE в 1 (авто режим) баг не проявляется.
;while (ADCSRA & (1<<ADIF)); //Ожидание конца первого измерения (25 тактов)
metka1:
lds r24, 0x007A
sbrc r24, 4
rjmp metka1
;ADCSRA |= (1<<ADIF); // Сброс флага прерывания
lds r24, 0x007A
ori r24, 0x10 ; 16
sts 0x007A, r24
;TIFR0 = 0xFF; // Сброс флагов от таймера
ldi r24, 0xFF ; 255
out 0x15, r24 ; 21
;}
;asm ("sei"); // разрешаем прерывания
sei
;while (1) // Основной цикл программы. Выполняется всегда
;{
; counter2++;
metka2:
lds r24, 0x0201
subi r24, 0xFF ; 255
sts 0x0201, r24
; PORTC = counter2; // Младший байт counter в порт. Также будет отображать только 0x01
lds r24, 0x0201
out 0x08, r24 ; 8
rjmp metka2
Что его мешает запустить? Он запускается в момент записи единицы в ADSC. А что там было до этого - не имеет никакого значения.А вот и нет. Это раз.
А если бы и так, то это не должно никак мешать. Потому что запустить его раньше не смогу чисто физически. (бит ADCS сбрасывается аппаратно по завершению обработки результата). Это два.
Вообще-то помочь мне я не просил. Это разkobzar писал(а):Послушай ТС!
Веди себя нормально а не как какоето быдло!
Ты попросил помочь. А когда тебя просят предоставить какие либо данные или дают совет
ты всех отправляешь писать код самому и ведешь диалог как будто ты самый умный а вокруг стадо баранов!
То что у тебя вторая стадия зведной болезни - мы уже поняли.
Просто либо веди себя уважительно по отношению к другим участникам форума - либо раз ты такой ахуенно умный - то не позорься задавая вопросы. Умные вопросы не задают - они на них отвечают.
В какой топик с твоим участием не глянешь - везде ты "самый агурец"
У 2560 смотрите "ADCSRB – ADC Control and Status Register B"ИС-пытатель писал(а):К тому же, загялнул в даташит и не нашел там его вовсе для 2560. Что там должно быть в нем? ))
Укоротив код в ассемблерном представлении вы "впихнули" код во временнЫе рамки и оно заработало.Reset and Interrupt Handling
...
The list also determines the priority levels of the different interrupts. The lower the address the higher is the priority level.
...
да, заработает. и если на большую - тоже. вообще это не должно иметь значения. очередность прерываний и все такое. )) а вы читали топик? этот вопрос уже рассматривался.ploop писал(а):У вас накладываются прерывания.
Если таймер на меньшую частоту настроить - заработает?
там речь про SFIOR велась.Kavka писал(а):У 2560 смотрите "ADCSRB – ADC Control and Status Register B"ИС-пытатель писал(а):К тому же, загялнул в даташит и не нашел там его вовсе для 2560. Что там должно быть в нем? ))
Не совсем так. АЦП == такту таймера == такт CPU/8"Предделитель тактовой частоты - 8"
Т.е. такт АЦП == такту таймера == такту CPU
В принципе, дело и не в частоте. А в том, что контроллер "вылетает"Загнал Си-шный код в симулятор.
ISR(TIMER0_COMPA_vect) получилась около 45 тактов.
ISR(ADC_vect) получилась около 50 тактов.
Т.е. чаще 90-100 тактов у вас измерять не получиться!
Допустим. А при чем здесь тогда операции Push и Pop? Именно из-за них происходит сбой. Причем добавлять/удалять можно любые операции. Они ничего не меняют. А вот эти - меняют. Замените все операции Push/Pop на Nop - и все заработает. И второе. Почему тогда переменные 1 раз все-таки инкрементируются? Значит, в прерывание АЦП хотя бы раз, но входит! иначе они бы и раза не инкрементировались. Причем, это не первое измерение АЦП (25 тактов). Потому что первое я провел в цикле инициализиции (можно хоть 10 там сделать - ничего не изменится). Кстати, если мне не изменяет память, то переменные в обработчике Таймера тоже "не работают".Более того, "таймерный" флаг на прерывание будет уже стоят снова к моменту последующему выходу из ISR(TIMER0_COMPA_vect).
Первый раз попали в прерывание по таймеру - запустили АЦП. Выходим - уже стоит флаг на "таймерное" прерывание. Ну, одна команда CPU выполнилась и снова в обработчик прерывания таймера.
А что же с прерыванием АЦП?! А ничего! Он не будет выполняться вообще!Укоротив код в ассемблерном представлении вы "впихнули" код во временнЫе рамки и оно заработало.Reset and Interrupt Handling
...
The list also determines the priority levels of the different interrupts. The lower the address the higher is the priority level.
...
У вас АЦП = такт ЦПУ/32Не совсем так. АЦП == такту таймера == такт CPU/8