Всем добрый день! Скажите пожалуйста, влияет ли тактовая частота работы микроконтроллера на работу с внешним прерыванием? Постараюсь описать суть вопроса. Есть atmega 328p и код написанный под неё для работы на частоте 16mHz. В коде используется внешние прерывание (INT0) и два таймера. Я решил убрать кварц на 16mHz, изменил код для работы на 8mHz, изменил фюз биты. И в результате получил какой то хаус в работе кода, судя по выводу в serial бесконечно срабатывает внешние прерывание. Ну это не все странности))) если переписать код на работу 1mHz и естественно в фюз битах поставить делитель на 8, то код работает отлично. Меня конечно устраивает работа и на 1mHz но хотелась бы понять почему не работает на 8. На всякий случай выложу сам код для работы на 8mHz. P.S. На коментарии не обращайте внимание, некоторые не соответствуют действительности)))) Код (Text): #define F_CPU 8000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "main.h" #include "Serial.h" #define _PR pgm_read_byte volatile bool repeat_mode = false; volatile bool start_receiv = true; volatile uint8_t bit_position = 0; uint8_t buffer[BUFFER_SIZE]; volatile uint8_t buffer_position = 0; uint8_t command = 0; volatile bool state_PLAY = false; volatile bool state_FF = false; volatile bool state_REW = false; volatile bool state_CONNECT = false; void play(); void receiv_command(); void parsing_command(); void clear_buffer(); void transmit_command(const radio_command*); void fast_seek(const radio_command*, volatile uint8_t*, uint8_t); ISR(INT0_vect) { //receiv_command(); } ISR(TIMER0_COMPA_vect) { TCCR0B = STOP_TIMER0; // Останавливаем таймер T0. start_receiv = true; parsing_command(); } ISR(TIMER1_COMPA_vect) { //sei(); if (!start_receiv) { return; } switch(command) { case PLAY: transmit_command(&TAPE_playing); break; case FASTFORWARD: transmit_command(&TAPE_fastforward); break; case REWIND: transmit_command(&TAPE_rewind); break; } } void receiv_command() { uint8_t pin = PIND&(1<<PIND2); uint8_t elapsed_time = TCNT0; if (start_receiv) { Serial.println("S"); TCCR1B = STOP_TIMER1; // Останавливаем таймер T1. clear_buffer(); TCCR0B|=(1<<CS02); // Запускаем таймер (Делитель 64). (TCCR0B|=(1<<CS02)|(1<<CS00);Делитель 1024) start_receiv = false; } if (!pin) { TCNT0 = 0; // Обнуляем таймер. GTCCR|=(1<<PSRSYNC); // Сбрасываем предделитель таймера. } else { if (bit_position>7) { buffer_position++; bit_position = 0; buffer[buffer_position] = 0; } if (elapsed_time>37 && elapsed_time<78) // Логическая 1 = 1.2мс-2.5мс (37-78 8mhz), (19-39 16mhz). { buffer[buffer_position] |= 1<<(7-bit_position); } bit_position++; } } void init() { // Прерывание INT0 EICRA|=(1<<ISC00); // Прерывание на INT0, любое изменение логического уровня. EIMSK|=(1<<INT0); // Разрешить прерывание на INT0. // Таймер T0 (8-bit) TIMSK0|=(1<<OCIE0A); // Разрешитm рерывание по совпадению таймера OCR0A TCCR0A|=(1<<WGM01); // Режим CTC (сброс при совпадении) OCR0A = 0xBB; // 6мс (0xBB 8mhz), (0x5D 1-16mhz) // Таймер T1 (16-bit) TCCR1B|=(1<<WGM12); // Режим CTC (сброс при совпадении) TIMSK1|=(1<<OCIE1A); OCR1AH = 0x7A; // 1сек. (0x7A 8mhz), (0x3D 1-16mhz) OCR1AL = 0x12; // (0x12 8mhz), (0x09 1-16mhz) // Pin DDRD|=(1<<PORTD3)|(1<<PORTD7)|(1<<PORTD6); DDRB|=(1<<PORTB0); sei(); } void clear_buffer() { bit_position = 0; buffer_position = 0; buffer[0] = 0; } void parsing_command() { //Serial.println(buffer[0]); switch (buffer[0]) { uint8_t subcomand; case 0x08: transmit_command(&TAPE_power_on); _delay_ms(10); if (state_CONNECT) { transmit_command(&TAPE_cassette_present); } else { transmit_command(&TAPE_no_cassette); } //Serial.println("0x08"); break; case 0x09: if (state_CONNECT) { transmit_command(&TAPE_cassette_present); _delay_ms(10); transmit_command(&TAPE_stopped); } else { transmit_command(&TAPE_no_cassette); _delay_ms(10); transmit_command(&TAPE_stopped); } //Serial.println("0x09"); break; case 0x01: subcomand = buffer[1] >> 4; command = (buffer[1] << 4)|(buffer[2] >> 4); clear_buffer(); switch (subcomand) { case 0x01: switch (command) { case PLAY: play(); if (!state_PLAY) { PORTD|=(1<<PORTD6); _delay_ms(140); PORTD&=~(1<<PORTD6); state_PLAY = true; } if (state_FF) { PORTB&=~(1<<PORTB0); state_FF = false; } if (state_REW) { PORTD&=~(1<<PORTD7); state_REW = false; } break; case FASTFORWARD: transmit_command(&TAPE_fastforward); TCCR1B = START_TIMER1; PORTB|=(1<<PORTB0); state_FF = true; break; case REWIND: transmit_command(&TAPE_rewind); TCCR1B = START_TIMER1; PORTD|=(1<<PORTD7); state_REW = true; break; case STOP: TCCR1B = STOP_TIMER1; transmit_command(&TAPE_stopped); if (state_PLAY) { PORTD|=(1<<PORTD6); _delay_ms(140); PORTD&=~(1<<PORTD6); state_PLAY = false; } break; } break; case 0x04: switch (command) { case 0x00: repeat_mode = false; case RANDOM: play(); break; case REPEAT: transmit_command(&TAPE_repeat_playback); _delay_ms(8); transmit_command(&TAPE_playing); repeat_mode = true; break; case SEEK_UP: //Serial.println("u"); fast_seek(&TAPE_fast_fastforward, &PORTB, PORTB0); break; case SEEK_DOWN: //Serial.println("d"); fast_seek(&TAPE_fast_rewind, &PORTD, PORTD7); break; } break; } break; default: TCCR1B = START_TIMER1; } } void transmit_command(const radio_command *message) { while (!start_receiv); cli(); for (uint8_t nibble = 0; nibble < _PR(&message->length); nibble++) { uint8_t n_message = _PR(&message->command[nibble/2]) >> 4*((nibble+1)%2); for (int8_t i = 3; i> -1; i--) { PORTD|=(1<<PORTD3); if (n_message & _BV(i)) _delay_us(1700); else _delay_us(500); PORTD&=~(1<<PORTD3); if (n_message & _BV(i)) _delay_us(1300); else _delay_us(2500); } } EIFR|=(1<<INTF0); sei(); } void fast_seek(const radio_command *seek, volatile uint8_t *_ddr, uint8_t _pin) { *_ddr|=(1<<_pin); _delay_ms(120); *_ddr&=~(1<<_pin); transmit_command(&TAPE_seeking); _delay_ms(10); transmit_command(seek); _delay_ms(5); transmit_command(seek); _delay_ms(10); play(); } void play() { transmit_command(&TAPE_playing); _delay_ms(8); if (repeat_mode) { transmit_command(&TAPE_repeat_playback); } else { transmit_command(&TAPE_playback); } TCCR1B = START_TIMER1; command = PLAY; } int main(void) { init(); Serial.begin(4800); clear_buffer(); Serial.println("start"); _delay_ms(5000); while(1) { if (PINB & (1<<PORTB1)) { if (state_CONNECT) { state_CONNECT = false; transmit_command(&TAPE_cassette_ejecting); _delay_ms(10); transmit_command(&TAPE_cassette_finally_eject); } } else { if (!state_CONNECT) { state_CONNECT = true; transmit_command(&TAPE_cassette_loaded); _delay_ms(5); transmit_command(&TAPE_cassette_present); } _delay_ms(2000); } } }
Всего лишь любитель и с AVR пока на Вы. Точно влияет на время отклика по прерыванию - http://www.gaw.ru/html.cgi/txt/doc/micros/avr/arh/mega103_27.htm. В документации по atmega328p, в 13 главе, ни чего особенного не увидел. Возможно надо очень внимательно читать 9 главу. По коду и прерываниям. Надеюсь на прерываниях не кнопка, а датчик который не подвержен дребезгу. Когда-то был адский опыт, с фирменным Omron переключателем и огромными функциями в прерываниях. С тех пор стараюсь избегать кнопок, без аппаратного решения дребезга и делать код в обработчике прерываний минимальный, просто выставить параметр а обработку сделать в main. Плюс данного подхода меньше шансов потерять прерывание, ведь пока выполняется 1, все остальные отдыхают. Так же где-то читал, уже не вспомню где, если во время прерывания произошло еще 1, оно запомнится и отработает после завершения уже вызванного, а если еще 2-ое, то первое будет пропущено. У Вас в коде, ISR(TIMER0_COMPA_vect) вызывает parsing_command(), а дальше от условий вызов transmit_command и _delay_ms, которые доходят до 140ms. И пока все это происходит, все остальное стоит. Удачи!
Alex19, спасибо за ответ. Во первых: Нет, не кнопка. Своеобразная однопроводная линия. Когда передача данных не производится на линии высокий уровень, длительностью низкого уровня определяется логические 1 и 0 (1=1,5-1,7мс. 0= 0,5-0,7мс.). Во вторых: ISR(TIMER0_COMPA_vect) это вектор прерывания таймера T0. Он срабатывает когда передача данных закончена. Вектор прерывания INT0 имеет более высокий приоритет. Просто все очень странно это работает, на частоте 1 и 16mhz все работает а на 8 уже нет. Как я уже и писал судя по выводу через UART постоянно срабатывает прерывание INT0 при 8mhz.
Понятно. Приоритет такой, кто первый встал у того и тапки. А если прерывания происходят одновременно, то да, будет выполнено сначала INT0. Более подробно, можно почитать тут - http://easyelectronics.ru/avr-uchebnyj-kurs-programmirovanie-na-si-chast-3.html и тут - http://easyelectronics.ru/avr-uchebnyj-kurs-podprogrammy-i-preryvaniya.html. Так же при входе в прерывание, происходит запрет обработки других прерываний, до тех пор пока прерывание не завершится не важно, какие там приоритеты о чем уже писал. Добавлю что transmit_command, вызывает как из прерывания, так из из main. Что может создать доп. проблемы. Как и sei в transmit_command если она вызовется по прерыванию, могут быть проблемы. Сам пока, не разбираюсь в стеке, просто избегаю sei в прерываниях. Тут увы не знаю, знаний не достаточно. Но сам код, мне кажется не очень надежным, поэтому грешу на него. Это сугубо мое мнение, могу и ошибаться.