straighter 0 30 марта, 2016 Опубликовано 30 марта, 2016 · Жалоба Уважаемые форумчане, помогите разобраться... ATmega8 работает с кварцем 8 Мгц. На вход встроенного компаратора подаю синусоиду 20 кГц от стабильного генератора. По положительному перепаду на выходе компаратора происходит захват значения в Timer/Counter1. Возникает прерывание, значение f увеличивается на 1. Когда f достигает значения d, то PORTC.2 устанавливается в 1 на время 10мкс, а затем снова устанавливается в 0. После этого в основном цикле программы PORTC.3 устанавливается в 1 на время 10мкс, а затем снова устанавливается в 0. Когда я задаю значение d меньше или равное 255, то все работает нормально, после каждого импульса на выходе PORTC.2 возникает импульс выходе PORTC.3, как и должно быть. Но если я задаю значение d больше или равное 256, то импульсы на выходе PORTC.3 так и продолжают идти, но не каждому из них предшествует импульс на выходе PORTC.2. Пробовал разные частоты синусоиды - то же самое. В чем причина? Фрагмент кода: unsigned int d=255; // Заданное кол-во НЧ-периодов счета (кол-во сработок компаратора) unsigned int f; // Текущее кол-во НЧ-периодов счета (кол-во сработок компаратора) // Timer1 input capture interrupt service routine interrupt [TIM1_CAPT] void timer1_capt_isr(void) { // Place your code here PORTC.0=1; delay_us(10); PORTC.0=0; if (f==1) { PORTC.1=1; delay_us(10); PORTC.1=0; }; if (f==(d)) { PORTC.2=1; delay_us(10); PORTC.2=0; }; f=f+1; } void main(void) { f=0; // Global enable interrupts #asm("sei") while (1) { // Place your code here while (f<(d+1)); #asm("cli") PORTC.3=1; delay_us(10); PORTC.3=0; delay_us(100); PORTC.4=1; delay_us(10); PORTC.4=0; f=0; #asm("sei") }; } Осциллограммы : d=255; синий цвет - вых. РС2; желт. цвет - вых. РС3; Здесь все правильно и понятно: d=256; синий цвет - вых. РС2; желт. цвет - вых. РС3; Видно, что нет импульса на РС2, а соответствующий импульс на РС3 есть: Желт. цвет - сигнал на входе компаратора; синий цвет - на вых. РС0 (смотри код программы): Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aiwa 0 31 марта, 2016 Опубликовано 31 марта, 2016 · Жалоба Попробуйте заменить delay_us на что-то более конкретное, чтобы убедиться, что ее реализация не разрешает прерывания. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
straighter 0 31 марта, 2016 Опубликовано 31 марта, 2016 · Жалоба Попробую сделать без delay_us, например так: PORTC.1=1; PORTC.1=0; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aiwa 0 1 апреля, 2016 Опубликовано 1 апреля, 2016 (изменено) · Жалоба Тогда осцилограммы не будет. Если используете IAR, в нем есть удобная псевдофункция delay_cycles(const unsigned long ) - задержка на заданное количество тактов. Либо просто пустые вложенные циклы. Тут два возможных варианта: Первый: 1. При выполнении оператора while (f<(d+1)); когда значение f = 255 = [00,FF] 1. процессор загружает младший байт FF в регистр срабатывает прерывание и увеличивает значение (f = f+1) f = 256 = [01,00] 2. процессор загружает старший байт в регистр, а теперь он уже 01, а не 00. поэтому значение в регистах стало [01,FF] = 511, а не 256 как на самом деле. Происходит выход из цикла while на стадии когда счетчик равен 256 Второй: и второй вариант если delay_us разрешает прерывания, а на компараторе происходит шумок. Какой из них нужно судить по частоте возникновения эффекта. Наверное быстрее переделать сравнение для первого варианта while (1) { #asm("cli") unsigned int f_test=f; #asm("sei") if(f_test<(d+1)) break; }; Изменено 1 апреля, 2016 пользователем aiwa Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
straighter 0 1 апреля, 2016 Опубликовано 1 апреля, 2016 · Жалоба Спасибо за ответ. Только я не совсем понял, в какое место моей программы вставить приведенный фрагмент кода: #asm("cli") unsigned int f_test=f; #asm("sei") if(f_test<(d+1)) break; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aiwa 0 1 апреля, 2016 Опубликовано 1 апреля, 2016 · Жалоба вместо while (f<(d+1)); вставить while(1) { #asm("cli") unsigned int f_test=f; #asm("sei") if(f_test<(d+1)) break; } чтобы сделать вычитку двухбайтной переменной f "атомарной" операцией. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
straighter 0 1 апреля, 2016 Опубликовано 1 апреля, 2016 · Жалоба Сделал так и почему-то исчезли импульсы на PORTC.2 (d = 256). Но идею насчет "атомарной" операции понял. Буду думать... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aiwa 0 1 апреля, 2016 Опубликовано 1 апреля, 2016 · Жалоба Сделал так и почему-то исчезли импульсы на PORTC.2 (d = 256). Но идею насчет "атомарной" операции понял. Буду думать... Извините, я забыл поменять знак в теле замене цикла c '<' на '>=' while(1) { #asm("cli") unsigned int f_test=f; #asm("sei") if(f_test>=(d+1)) break; } Из-за этого выход из цикла происходит сразу же. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
straighter 0 1 апреля, 2016 Опубликовано 1 апреля, 2016 · Жалоба Большое спасибо, получилось! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться