при поступлении сигнала на захват,
содержимое регистров TCNT1H:TCNT1L
копируется в регистры ICR1H:ICR1L
Что делать? Справедливости ради замечу, что 4-х пиновые вентиляторы лишены этого недостатка, но сейчас речь не о них. Для 3-х пинового вентилятора можно попробывать сделать "умное" управление.
В 8-см вентиляторе на 3300 RPM, датчик Холла срабатывает 110 раз в секунду. Т.е. он (датчик Холла) работает с периодом в девять миллисекунд. Тогда, если мы на время отключим ШИМ, подадим питание 12 Вольт, замерим длительность одного периода, и затем снова включим ШИМ, то мы получим и управление через ШИМ и достоверный контроль за количеством оборотов. Вентилятор, как и все двигатели, очень инерционные штуки и такой фокус никак не скажется на скорости вентилятора, если его проводить не чаще чем раз в две-три секунд. В остальное время, для простого контроля, чтобы быть уверенным, что вентилятор в принципе крутиться, сгодится подсчет имульсов с датчика Холла и без этого фокуса.
Периоды сигналов в AVR можно измерять с помощью т.н. прерывания по захвату. Основные моменты:
Бегло пройдемся по нужным нам регистрам 16-битного Timer1 ATmega8.
Здесь общий для всех таймеров TIMSK, составнной регистр счетчика TCNT1H:TCNT1L, составной регистр прерывания захвата ICR1H:ICR1L и контрольный регистр TCCR1B.
Установкой бита TICIE1 в регистре TIMSK запускается таймер в "захватывающием" режиме.
Таймер имеет два управляющих регистра: TCCR1A и TCCR1B. В данном случае интерес передставляет только последний. В нём битами CS12, CS11, CS10 устанавливается частота таймера. При установке бита ICES1 срабатывание прерывания осуществляется по растущему фронту, иначе - по падающему.
Исходник, который у меня получился:
#include <util/delay.h> #include <uart.h> #include <avr/io.h> #include <avr/interrupt.h> #define ONE_SECOND 32 #define MAX_TAB 10 static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); volatile uint16_t tab[MAX_TAB]; volatile uint8_t tab_id; volatile uint8_t myTimer; ISR(TIMER1_OVF_vect) { myTimer--; if (!myTimer) { PORTB ^= (1<<PB5); myTimer=ONE_SECOND; } }; ISR(TIMER1_CAPT_vect) { if (tab_id<MAX_TAB) { TCNT1H=0; TCNT1L=0;// clearing cunter uint16_t tmp; tab[tab_id]=(uint16_t)ICR1L; tmp=(uint16_t)ICR1H; tmp<<=8; tab[tab_id]|=tmp; } tab_id++; } int main(void) { //---- init init_uart(); DDRB |= (1<<PB5); // led indicator // init 16-bit timer in capture mode TIMSK =(1<<TICIE1)|(1<<TOIE1); //enable capture, overflow interrupt enable TCCR1B=(1<<ICES1)|(1<<CS11); // prescaler 1/8 TCNT1H=0;TCNT1L=0; // clear counter for(tab_id=0; tab_id<MAX_TAB; tab_id++) // clearing table of a captured values tab[tab_id]=0; myTimer=ONE_SECOND; tab_id=0; //---- ready indication stdout = &mystdout; printf("Ok, I'm ready!\n"); sei(); //---- main body for (;;) { _delay_ms(1000); if (tab_id>0) { cli(); uint8_t k; printf("\n"); for(k=0; k<MAX_TAB; k++) { if (tab[k] > 0) printf("period: %d value: %u\n", k,tab[k]); tab[k]=0; }; tab_id=0; sei(); } else printf("none values. "); }; return 0; }
Таймер работает с предделителем 1/8, т.е. при частоте кварца 16 МГц, один замеряемый период будет равен половине микросекунды. При такой частоте, сечетчик таймера будет переполняться 32 раз в секунду.
Прерывание на переполнение, в данном случае, служит для визуального контроля. Через него мигает светодиод, сигнализируя, что таймер работает. При срабатывании прерывания по захвату, сечетчик таймера сбрасывается, поэтому светодиод будет "подтормаживать". При отсутствии захвата, он будет мигать с частотой 1 раз в секунду.
Работу программы можно проверить просто замкнув пин ICP на питание Vcc и получив т.н. дребезг контактов. Примерно так:
Если же подключить вентилятор по схеме из предыдущего поста, поменяв контакт подключения с INT0 на ICP1, то для для 8-см вентилятора будет такая картинка:
Здесь видим ряд цифр начинающихся на 18 тысяч. Т.к. одно значение равно 1/2 микросекунды, получаем одно срабатывание датчика Холла за 9 миллисекунд. Чтобы совершить один оборот, датчику Холла нужно сработать дважды. Т.е. один оборот проходится за 18 миллисекунд. Т.е. 1000/18=55.5 оборотов в секунду. Или 60*1000/18=3333.3 оборота в минуту.
Для 12-см вентилятора имеем такую картинку:
Этот проходит оборот уже 40 миллисекунд. Т.е. получаем 60*1000/40=1500 оборотов в минуту.
Скачать полный архив с исходниками можно здесь