Идея примерно такая.
Заводим один канал на вход внешнего прерывания INT0. Второй - на соседний порт (просто вход). В прерывании добавляем единицу к счетчику импульсов и смотрим, что на входе второго сигнала. Если 0 - направление вращения одно, 1 - другое.
Берем 10-битный таймер/счетчик Т1 (Т0 занят динамической индикацией). Используем прерывание по переполнению Т1. В прерывании записываем значение того, что насчиталось в счетчике импульсов и обнуляем счетчик импульсов.
Зная период, за который происходит переполнение Т1, и количество импульсов на оборот энкодера, считаем обороты в минуту.
Код:
Спойлер
Код: Выделить всё
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#define F_CPU 8000000UL
#define T0_START 220 //настройка частоты динамической индикации
//инициализация переменных и констант
volatile int clk_pulse; //счетчик импульсов энкодера
volatile int clk_pulse_ms; //количество импульсов за опр. время
int rpm; //обороты в минуту
volatile unsigned char data[5]; //разряды
const unsigned char number[12] = //символы
{
0x3f, //0
0x06, //1
0x5b, //2
0x4f, //3
0x66, //4
0x6d, //5
0x7d, //6
0x07, //7
0x7f, //8
0x6f, //9
0x40, //-
0x00 //
};
//функция преобразования числа
void CONVERT_num(int value)
{
int row_local;
//разбиваем число на разряды
if ((value >> 15) == 0) data[4] = 11;
else data[4] = 10;
row_local = abs(value);
data[0] = row_local % 10;
row_local = row_local / 10;
data[1] = row_local % 10;
row_local = row_local / 10;
data[2] = row_local % 10;
data[3] = row_local / 10;
}
//вектор прерывания по переполнению Т0
ISR(TIMER0_OVF_vect)
{
static unsigned char count; //номер разряда
TCNT0L = T0_START;
PORTB |= 0x1f; //гасим все
//индикация с 0-го разряда
PORTA = number[data[count]];
PORTB &= ~(1 << count);
count++;
if (count == 5) count = 0;
}
//вектор внешнего прерывания 0
ISR(INT0_vect)
{
clk_pulse = abs(clk_pulse) + 1; //счетчик импульсов
//определение направления вращения
if ((PINB & 0x20) == 0x20) clk_pulse = clk_pulse * (-1);
}
//вектор прерывания по переполнению Т1
ISR(TIMER1_OVF_vect)
{
clk_pulse_ms = clk_pulse; //импульсов время счета Т1
clk_pulse = 0;
}
void main(void)
{
//инициализация портов
DDRA = 0x7f; //сегменты индикации
PORTA = 0x00;
DDRB = 0x1f; //разряды индикации и входа энкодера
PORTB = 0xff;
//инициализация таймера 0
TCCR0B |= (1 << CS02); //предделитель 256
TIMSK |= (1 << TOIE0); //разрешение прерывания по переполнению Т0
TCNT0L = T0_START;
//иннициализация внешнего прерывания 0
MCUCR |= ((1 << ISC01) | (1 << ISC00)); //по переднему фронту
GIMSK |= (1 << INT0); //разрешение прерывания INT0
//инициализация таймера 1
TCCR1B |= ((1 << CS13) | (1<<CS12) | (1 << CS11) | (1 << CS10)); //предделитель Т1
TIMSK |= (1 << TOIE1); //разрешение прерывания по переполнению Т1
sei(); //разрешение прерываний
while (1)
{
rpm = clk_pulse_ms; //вычисление об/мин
CONVERT_num(rpm); //преобразование для вывода на индикацию
}
}Моя логика.
Берем частоту МК f_cpu = 8000000 Гц
Предделитель Т1 k_t1 = 16384
Количество тиков Т1 n_t1 = 1024
Период одного тика Т1 t_1_t1 = (1 / f_cpu) * k_t1 = 2.048 мс
Время до переполнения Т1 t_t1 = t_1_t1 * n_t1 = 2.097 с
Период импульса энкодера t_en = 2 мс (примерно при 80 оборотах и 360 импульсах на оборот)
Количество импульсов за время до переполнения n_p = t_t1 / t_en = 1049
А у меня на индикаторе только 6-8. В вышеприведенном коде я просто вывожу счет импульсов за время до переполнения Т1 без всяких преобразований. Вывод на дисплей верный - проверял выводом констант. Счет импульсов в прерывании по входу верный - пихул туда инверсию порта А - частота на выходах А совпадает с поступающей на вход. В свойствах МК в протеусе фьюзы CKSEL 0010 - встроенный генератор на 8 МГц.
Где ошибка?


