zheka 1 22 января, 2010 Опубликовано 22 января, 2010 (изменено) · Жалоба Все господа.... мысли кончились. Не гонясь за точностью, пытаюсь сделать хоть что-то. Завел Timer0 на 16 МГц. ПО переполнению инкрементирую OVF_count - счетчик переполнений. Timer1 в режиме PWM настроен на период 20 мс, выход поступает на вход INT0, фронту импульса запускается обработчик, который умножает OVF_count на 256 , прибавляет текущее значение TCNT0, после чего делит на 16 - получаем период в микросекундах. У меня же проказывает не 20 000 мкс, а 1610 либо 1594. Вот код, где я не прав? #include <mega32.h> #include <delay.h> #include <stdio.h> #asm .equ __lcd_port=0x15;PORTC #endasm #include <lcd.h> #define FAN PORTB.4 #define FINEUP PIND.7 #define FINEDOWN PIND.4 #define SPEED_MINUS PIND.6 #define SPEED_PLUS PIND.1 #define SPEED_MIN 0x0490 #define SPEED_MAX 0x07D0 #define SPEED OCR1A char lcd_buffer[33]; unsigned char s[8]; unsigned int a,b; unsigned int OVF_count; unsigned long int time; // External Interrupt 0 service routine interrupt [EXT_INT0] void ext_int0_isr(void) { a=TCNT0; time=(OVF_count*256+a)/16; OVF_count=0; lcd_clear(); lcd_gotoxy(0,0); sprintf(s,"%u", time); lcd_puts(s); } interrupt [TIM0_OVF] void timer0_ovf_isr(void) { OVF_count++; } main() { unsigned char i,j, devices; int temp; OVF_count=0; // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 16000,000 kHz // Mode: Normal top=FFh // OC0 output: Disconnected TCCR0=0x01; TCNT0=0x00; OCR0=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 2000,000 kHz // Mode: Ph. & fr. cor. PWM top=ICR1 // OC1A output: Non-Inv. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x80; TCCR1B=0x12; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x4E; ICR1L=0x00; OCR1BH=0x00; OCR1BL=0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0b00000001; // External Interrupt(s) initialization // INT0: On // INT0 Mode: Rising Edge // INT1: Off // INT2: Off GICR|=0x40; MCUCR=0x03; MCUCSR=0x00; GIFR=0x40; SPEED=SPEED_MIN; DDRA=0xFF; DDRD=0x00; PORTD=0xFF; DDRD.5=1; DDRB.4=1; FAN=1; delay_ms(500); FAN=0; DDRD.2=0; PORTD.2=0; DDRD.0=0; PORTD.0=1; lcd_init(8); lcd_clear(); #asm sei #endasm while (1) { if (!SPEED_PLUS) { if (SPEED<SPEED_MAX) SPEED++; delay_ms(2); } if (!SPEED_MINUS) { if (SPEED>SPEED_MIN) SPEED--; delay_ms(2); } } } Изменено 22 января, 2010 пользователем rezident Нарушение п.3.4 Правил форума. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Палыч 8 22 января, 2010 Опубликовано 22 января, 2010 · Жалоба Вот код, где я не прав? Прикиньте: какое значение переменной OVF_count ? А, если умножить это значение на 256 ? А, поместиться ли полученное значение в int ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zheka 1 22 января, 2010 Опубликовано 22 января, 2010 (изменено) · Жалоба Хм.. а вот тут уже незнание мной матчасти, вопрос вообще можно было бы выделить в отдельную тему. Формула у меня такая: time=(OVF_count*256+a)/16; time имеет тип long int. Я думал этого достаточно. Разве умножаемая величина должна быть подходящего типа? Я думал, что только произведение... Да, кстати, отключил таймер1, заменил сигнал с него на цикл PORTD.5=1;PORTD.5=0; с задержкой в 1 мс (период 2 мс). На осциллографе длительность периода составила 2,1 мс. Запретил обработку прерываний (внешнего и переполнения T0) - стало ровно 2 мс. Кроме того с вычислениями все в порядке стало, в пределах 2 мс показывает отлично, остальные диапазоны не проверял. Может быть таймер1 вносит в работу программы какую-то разножопицу? Хотя прерывания с него запрещены и работает он в режиме phase & frequency correction PWM. Изменено 22 января, 2010 пользователем zheka Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kool 0 22 января, 2010 Опубликовано 22 января, 2010 · Жалоба Timer1 в режиме PWM настроен на период 20 мс, выход поступает на вход INT0 А почему не используете одно из прерываний таймера? зачем такой обходной путь? time=(OVF_count*256+a)/16; Установите предделитель таймера на 8 и Вам для 20 мс хватит разрядности unsigned int. Тогда time=(OVF_count*256+a)/2; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Палыч 8 23 января, 2010 Опубликовано 23 января, 2010 · Жалоба Установите предделитель таймера на 8 и Вам для 20 мс хватит разрядности unsigned int.Разрядности и так хватит. Можно записать выражение, например, так : time=OVF_count*16+a/16; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zheka 1 24 января, 2010 Опубликовано 24 января, 2010 · Жалоба А почему не используете одно из прерываний таймера? зачем такой обходной путь? Александр, если вы мне подскажете способ, как сделать на 8-битном таймере PWM периодом 20 мс, чтобы освободить Timer1, или как на одном Timer1 сделать одновременно и PWM и измерение периода импульсов, то я откажусь от INT0 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 24 января, 2010 Опубликовано 24 января, 2010 · Жалоба PWM периодом 20 мс Какая дискретность нужна? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zheka 1 24 января, 2010 Опубликовано 24 января, 2010 (изменено) · Жалоба Дискретность? думаю, что той что обеспечит 8-битный таймер хватит. ДЛина импульса от 0.8 до 2 мс. Насколько я понял, на 8-битном таймере максимальный период при наибольшем коэффициенте деления будет 16 мс. БОльше не сделаешь? МОжет добавить в обработчик переполнения задержку в 4 мс? Изменено 24 января, 2010 пользователем zheka Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zheka 1 24 января, 2010 Опубликовано 24 января, 2010 (изменено) · Жалоба В общем, конфликтуют у меня два таймера. Переделал на TImer1 - все нормально, генерирую в цикле импульс периодом 500 мкс (через delay_ms) показывает 504 мкс, и даже не знаю, может быть именно delay_us вносит эту погрешность. Но, как только разрешаю прерывание по переполнению Timer0 - сразу скачет на 553 мкс, причем показания нестабильны. Даже запрет прерываний на время обработки и вывода на экран не помогают... Изменено 24 января, 2010 пользователем zheka Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
fantex 0 25 января, 2010 Опубликовано 25 января, 2010 (изменено) · Жалоба Вот пример реализации измерения частоты импульсов на CPU188-MX5. Основная идея способа - считать количество импульсов и время между передними фронтами импульсов. Если время между фронтами превышает заданное (в данном случае 3 секунды), то полученые значения кол-ва импульсов и время между фронтами копируется в соответствующие переменные, из которых внешняя функция вычисляет уже частоту импульсов. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ namespace FI { word timeMS = -1; word freqCount = 0; void __declspec(naked) interrupt IntHandler(); }; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void __declspec(naked) interrupt FI::IntHandler() { static word sMS = 0; static word count = 0; __asm { push ax // 14 push dx // 14 push ds // 13 mov ax, seg millisecondsCount // 4 mov ds, ax // 2 inc [count] // 19 mov ax, [word ptr millisecondsCount] // 2 mov dx, [sMS] // 2 sub ax, dx // 3 cmp ax, 3000 // 3 jnc loc2 // 4, 13 locret: pop ds pop dx pop ax iret loc2: add dx, ax mov [byte ptr sMS], dl // 2 mov [byte ptr sMS+1], dh // 2 mov [byte ptr timeMS], al // 2 mov [byte ptr timeMS+1], ah // 2 mov al, [byte ptr count] // 2 mov ah, [byte ptr count+1] // 2 mov [byte ptr freqCount], al // 2 mov [byte ptr freqCount+1], ah // 2 xor al, al // 3 mov [byte ptr count], al // 2 mov [byte ptr count+1], al // 2 pop ds // 12 pop dx // 14 pop ax // 14 iret // 28 } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ float FI::GetFreq() { TRACE float f1; float f2; _cli(); f1 = FI::freqCount; f2 = FI::timeMS; FI::freqCount = 0; FI::timeMS = -1; _sti(); return 1000.0 * f1 / f2; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Изменено 25 января, 2010 пользователем fantex Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zheka 1 25 января, 2010 Опубликовано 25 января, 2010 · Жалоба Fantex, спасибо, но, с алгоритмом я разобрался, у меня не получаются детали и конкретная реализация на конкретном камне. КРоме того, я пишу на С. Метод подсчета импульсов мне ну никак не подходит. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILYAUL 0 25 января, 2010 Опубликовано 25 января, 2010 · Жалоба ... реализация на конкретном камне....... . Вот кстати , а камень то какой Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zheka 1 25 января, 2010 Опубликовано 25 января, 2010 (изменено) · Жалоба Камень ATMega32. Вот, господа, упростил до безобразия: По прерыванию INT0 сразу запоминаю значение TCNT0 и количество переполнений таймера0 (всего-то 256 тиков, вдруг чего...) Затем обнуляю таймер: если импульс первый, то начинаем жихнь с чистого листа, если второй, то пофиг, мы то уже запомнили его значение. Далее, чтобы свести к минмимуму влияние вывода на LCD на время, проверяю, первый ли импусьс? Если первый, то обнуляю счетчик переполнений и сбрасываю флаг "первого импульса", дабы при следующем срабатывании он считался вторым. // External Interrupt 0 service routine interrupt [EXT_INT0] void ext_int0_isr(void) { a=TCNT0; b=OVF_count; TCNT0=0x00; if (!first_pulse) { // time=((OVF_count*256+a)/16); lcd_clear(); lcd_gotoxy(1,0); sprintf(s,"%u", b); lcd_puts(s); lcd_gotoxy(1,1); sprintf(s,"%u", a); lcd_puts(s); OVF_count=0; first_pulse=1; TCNT0=0x00; } else { first_pulse=0; OVF_count=0; } } interrupt [TIM0_OVF] void timer0_ovf_isr(void) { OVF_count++; } Генерирую импульсы с периодом 1024 мкс. По идее таймер должен переполниться 64 раза на частоте 16 МГц. Показывает 80. Ну да ладно. Далее - запускаю таймер1. События на нем не обрабатываются, да и вообще, прерывания с него запрещены: TIMSK=0b00000001; Тем не менее, когда я его запускаю: / Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 2000,000 kHz // Mode: Ph. & fr. cor. PWM top=ICR1 // OC1A output: Non-Inv. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x80; TCCR1B=0x12; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x4E; ICR1L=0x00; OCR1BH=0x00; OCR1BL=0x00; на экран выводится число переполнений 1248. Если упрощаю поведение таймера : TCCR1A=0x00;TCCR1B=0x01;, то ничего такого нет, показывает 80. Господа, мысли кончились. Почему таймеры не уживаются? кстати, чем больше делитель TIMER1, тем больше улетает величина OVF_count. Если ее не делить, то показывает 96. А уж если включаю TOIE1, то контроллер начинает ресетиться. В TIFR при проверке все время висит 0x3F. Изменено 25 января, 2010 пользователем zheka Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Палыч 8 25 января, 2010 Опубликовано 25 января, 2010 · Жалоба Почему таймеры не уживаются? Вы предлагаете погадать? Ну, например, разрешены прерывания от таймера 1, а процедура обработки прерывания не написана. При прерываниях от таймера 1 МК попадает на вектора 7-10, но там не команды перехода. МК добирается до векторов таймера 0 (на вектор 12, например), и благополучно попадает на функцию обработки его (таймера 0) прерывания по переполнению... Совет: выбросить вывод на LCD из обработчика прерывания таймера: поставьте взведение флага вывода в прерывании - перенесите сам вывод в main во внутрь бесконечного цикла с проверкой флага. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zheka 1 25 января, 2010 Опубликовано 25 января, 2010 · Жалоба Палыч, вообще-то прерывания тапймера1 запрещены. ПОпробую ваш совет про вывод в main Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться