Обсуждаем контроллеры компании Atmel.
Ответить

Тахометр для токарника

Пт дек 27, 2019 11:44:06

Здравствуйте, уважаемые.
Решил сделать тахометр для своего настольного токарного станочка. Вроде ничего сложно, а обороты в минуту вычисляются некорректно. Прошу тех, кому не лень читать чужой код, подсказать где затаилась ошибка. А чтобы было проще разобраться с моей писаниной, опишу само устройство и алгоритм. Пишу в Codevision AVR.
Тахометр на Atmega8 работает от внутреннего генератора на 8МГЦ, для вывода значений используется 4-значный 7-сегментный индикатор. В качестве датчика - инфракрасная оптопара работающая на отражение. Импульсы с датчика снимаются через INT0 по спадающему фронту.
Алгоритм работы:
Измеряется время между срабатываниями прерывания INT0. При срабатывании прерывания INT0 сбрасывается счетчик импульсов от предыдущего измерения. Счетчик импульсов выполнен на Timer1 и переменной kw_over, которая хранит число переполнений Timer1. Timet1 настроен на 1МГц.
Timer0 используется для отсчета времени вывода на каждый разряд индикатора, а так же для выжидания примерно 1 секунды (переменные pora_obnovit и obnovka) для пересчета значений оборотов в минуту (переменная taho). Пересчет значения "оборотов в минуту" производится один раз в секунду. Если обороты менее одного в секунду, то на индикатор выводится четыре минуса "----".
Функция вывода числа работает корректно. Импульсы с датчика тоже поступают. Прерывание INT0 отрабатывает. А вот расчитывается показание криво. При срабатывании датчика примерно 1 раз в секунду значение "оборотов в минтуту" получается от 23 до 45. Если датчик срабатывает чаще, например 5 раз в секунду, то показания вычислений околонулевые.
Уже мозг кипит :(
Листинг программы с коментариями:
Спойлер/*******************************************************
Chip type : ATmega8A
AVR Core Clock frequency: 8,000000 MHz
*******************************************************/

#include <mega8.h>
#include <delay.h>

unsigned char razr; // на какой разряд индикатора выводить (0-3)
unsigned char print_en; // разрешение вывода очередного разряда
unsigned char error; // =1 если меньше одного оборота в секунду
volatile unsigned long kw; // число тиков таймера
volatile unsigned char kw_over, kw_over_buf; // число переполнений таймера
unsigned int pora_obnovit; // через какой интервал времени будут пересчитываться показания
unsigned char obnovka; // пора ли вычислить новое значение?

#define DIG_1 PORTD.0
#define DIG_2 PORTD.1
#define DIG_3 PORTD.3
#define DIG_4 PORTD.4
#define SEG_A PORTB.2
#define SEG_B PORTB.1
#define SEG_C PORTB.6
#define SEG_D PORTB.7
#define SEG_E PORTD.5
#define SEG_F PORTD.6
#define SEG_G PORTD.7
#define SEG_P PORTB.0

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
if (error==0) // если крутились быстрее 1 оборота в секунду
{
kw=TCNT1L; // сохраняю тики Таймера1
kw+=(TCNT1H<<8);
kw_over_buf=kw_over; // сохраняю количество переполнений Таймера1
}
else
{
kw_over_buf=0;
}
kw_over=0;
error=0;
TCNT1H=0;
TCNT1L=0;
}

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0=0xA2; // 3мс на вывод каждого разряда
print_en=1; // разрешаю вывод
pora_obnovit++;
if (pora_obnovit>332) // 1сек интервал пересчета оборотов
{
pora_obnovit=0;
obnovka=1; // пора пересчитать выводимое значение
}
}

// Timer1 overflow interrupt service routine
// если попали сюда, обороты менее 1 в секунду
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
if (kw_over<16)
{
kw_over++;
}
else
{
error=1; // если обороты меньше 1 в секунду, то отображать "минусы"
}
}

// вывод четырехзначного числа
void print_number(unsigned char num, unsigned char pos_out)
{
if ((pos_out<4)||(num<10)) // проверка на лимиты
{
// выключу все разряды
DIG_1=1;
DIG_2=1;
DIG_3=1;
DIG_4=1;

if (error==0)
{
switch (num)
{
case 0:
{
SEG_A=0; SEG_B=0; SEG_C=0; SEG_D=0; SEG_E=0; SEG_F=0; SEG_G=1;
break;
}
case 1:
{
SEG_A=1; SEG_B=0; SEG_C=0; SEG_D=1; SEG_E=1; SEG_F=1; SEG_G=1;
break;
}
case 2:
{
SEG_A=0; SEG_B=0; SEG_C=1; SEG_D=0; SEG_E=0; SEG_F=1; SEG_G=0;
break;
}
case 3:
{
SEG_A=0; SEG_B=0; SEG_C=0; SEG_D=0; SEG_E=1; SEG_F=1; SEG_G=0;
break;
}
case 4:
{
SEG_A=1; SEG_B=0; SEG_C=0; SEG_D=1; SEG_E=1; SEG_F=0; SEG_G=0;
break;
}
case 5:
{
SEG_A=0; SEG_B=1; SEG_C=0; SEG_D=0; SEG_E=1; SEG_F=0; SEG_G=0;
break;
}
case 6:
{
SEG_A=0; SEG_B=1; SEG_C=0; SEG_D=0; SEG_E=0; SEG_F=0; SEG_G=0;
break;
}
case 7:
{
SEG_A=0; SEG_B=0; SEG_C=0; SEG_D=1; SEG_E=1; SEG_F=1; SEG_G=1;
break;
}
case 8:
{
SEG_A=0; SEG_B=0; SEG_C=0; SEG_D=0; SEG_E=0; SEG_F=0; SEG_G=0;
break;
}
case 9:
{
SEG_A=0; SEG_B=0; SEG_C=0; SEG_D=0; SEG_E=1; SEG_F=0; SEG_G=0;
break;
}
}// switch (num)
}//if (error==0)
else
{
SEG_A=1; SEG_B=1; SEG_C=1; SEG_D=1; SEG_E=1; SEG_F=1; SEG_G=0;
}

switch (pos_out)
{
case 0:
{
DIG_1=0;
break;
}
case 1:
{
DIG_2=0;
break;
}
case 2:
{
DIG_3=0;
break;
}
case 3:
{
DIG_4=0;
break;
}
}//switch (pos_out)
}
}

void main(void)
{
unsigned char number[4]; //выводимое значение поразрядно
unsigned char i;
unsigned int decimal; // используется для поразрядной разбивки числа для вывода
unsigned int taho; // вычисленное значение оборотов в минуту

// Input/Output Ports initialization
// Port B initialization
// Function: Bit7=Out Bit6=Out Bit5=In Bit4=In Bit3=In Bit2=Out Bit1=Out Bit0=Out
DDRB=(1<<DDB7) | (1<<DDB6) | (0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (1<<DDB2) | (1<<DDB1) | (1<<DDB0);
// State: Bit7=0 Bit6=0 Bit5=T Bit4=T Bit3=T Bit2=0 Bit1=0 Bit0=0
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

// Port C initialization
// Function: Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRC=(0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
// State: Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTC=(0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

// Port D initialization
// Function: Bit7=Out Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=In Bit1=Out Bit0=Out
DDRD=(1<<DDD7) | (1<<DDD6) | (1<<DDD5) | (1<<DDD4) | (1<<DDD3) | (0<<DDD2) | (1<<DDD1) | (1<<DDD0);
// State: Bit7=0 Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=0 Bit1=0 Bit0=0
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 31,250 kHz
TCCR0=(1<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0xA2;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: Normal top=0xFFFF
// OC1A output: Disconnected
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 65,536 ms
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0<<AS2;
TCCR2=(0<<PWM2) | (0<<COM21) | (0<<COM20) | (0<<CTC2) | (0<<CS22) | (0<<CS21) | (0<<CS20);
TCNT2=0x00;
OCR2=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (1<<TOIE1) | (1<<TOIE0);

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Falling Edge
// INT1: Off
GICR|=(0<<INT1) | (1<<INT0);
MCUCR=(0<<ISC11) | (0<<ISC10) | (1<<ISC01) | (0<<ISC00);
GIFR=(0<<INTF1) | (1<<INTF0);

// USART initialization
// USART disabled
UCSRB=(0<<RXCIE) | (0<<TXCIE) | (0<<UDRIE) | (0<<RXEN) | (0<<TXEN) | (0<<UCSZ2) | (0<<RXB8) | (0<<TXB8);

// Analog Comparator initialization
// Analog Comparator: Off
// The Analog Comparator's positive input is
// connected to the AIN0 pin
// The Analog Comparator's negative input is
// connected to the AIN1 pin
ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0);
SFIOR=(0<<ACME);

// ADC initialization
// ADC disabled
ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADFR) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);

// SPI initialization
// SPI disabled
SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);

// TWI initialization
// TWI disabled
TWCR=(0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWEN) | (0<<TWIE);


// Global enable interrupts
#asm("sei")

DIG_1=1; DIG_2=1; DIG_3=1; DIG_4=1;
SEG_A=1; SEG_B=1; SEG_C=1; SEG_D=1; SEG_E=1; SEG_F=1; SEG_G=1; SEG_P=1;
razr=0;
print_en=0;
error=0;
obnovka=0;
pora_obnovit=0;

while (1)
{
if (obnovka==1)
{
//пересчитываем новое значение
obnovka=0;
#asm("cli") // запрет прерываний чтобы не запороть значение kw
kw=(long)kw+((long)65536*(long)kw_over_buf); // число тиков таймера1 с учетом количества его переполнений
taho=((long)6*kw)/100000; // обороты в минуту
#asm("sei")
decimal = 1000;
for(i = 0; i < 4; i++)
{
number[i] = (taho / decimal) % 10; // Выделяем первый разряд в числе и сохраняем
decimal /= 10; // уменьшаем делитель для последующего разряда в числе
}
}
if (print_en==1) // вывод разрешен
{
SEG_P=1; // выключу точку
switch (razr)
{
case 0:
{
print_number(number[0],razr);
break;
}
case 1:
{
print_number(number[1],razr);
break;
}
case 2:
{
print_number(number[2],razr);
break;
}
case 3:
{
print_number(number[3],razr);
break;
}
}// switch (razr)
razr++;
if (razr>3) razr=0;
print_en=0;
}// if(print_en==1)
}//while
}//main

Re: Тахометр для токарника

Пт дек 27, 2019 12:10:31

Код:
kw=(long)kw+((long)65536*(long)kw_over_buf); // число тиков таймера1 с учетом количества его переполнений
taho=((long)6*kw)/100000; // обороты в минуту

Думаю, что это некорректное выражение, т.к. скорость вращения S=60*Fo/nox, где Fo-частота тиков таймера, nox-число тиков таймера1 с учетом количества его переполнений за один период вращения.

Re: Тахометр для токарника

Пт дек 27, 2019 12:37:06

скорость вращения S=60*Fo/nox, где Fo-частота тиков таймера, nox-число тиков таймера1 с учетом количества его переполнений за один период вращения.


Т.е. taho=60*1000000/kw, я правильно понял?

Re: Тахометр для токарника

Пт дек 27, 2019 12:50:12

Если
thor_nsk писал(а):Timet1 настроен на 1МГц
, то да, правильно.

Re: Тахометр для токарника

Пт дек 27, 2019 13:17:39

для измерения периода приходящих импульсов вместо прерывания INT0 лучше использовать функцию захвата таймера1 и вычислять в прерывании по захвату.

Re: Тахометр для токарника

Пт дек 27, 2019 15:02:28

если до понедельникк потерпишь, могу скинуть проект тахо. под тини 2313, под своё железо сам допилишь...

Re: Тахометр для токарника

Пт дек 27, 2019 15:25:58

Скорректировал формулу, вроде все работает :)

если до понедельника потерпишь, могу скинуть проект тахо. под тини 2313, под своё железо сам допилишь...

Потерплю :) Буду благодарен. С интересом посмотрю вашу реализацию.

Re: Тахометр для токарника

Пн дек 30, 2019 14:38:05

к сожалению под тини уже нету, только под мегу16, но там меню и прочей мути было многоо...
поэтому сел и быстренько перекинул в тини (в проект от насоса) только требуемое, а насосное всё выкинул (оставил (закоментировав) только обработку придисплейной клавиатуры, в протеусовом проекте эта клавиатура тоже для примера осталась, потом сам уберёшь, если не понадобится)
таймер обработки клавиатуры оставил - т.к. он служит и делителем для обновления показаний на дисплее.
Хоть точность замера итак плавающая (в зависимости от частоты измеряемых импульсов динамически изменяется в 32 раза, поддерживая примерно на одном уровне время и шаг измерения), дополнительно сделал принудительный(кнопкой)/автоматический(по частоте) выбор скорости таймера - он, к сожалению пересчитывает уже не на лету... т.ч. можешь использовать только один из диапазонов (пределы измерения каждого диапазона указаны в коде). (сейчас переключение настроено по скорости, вверх - 1000 rpm, вниз - 500 rpm)

Добавлено after 34 minutes 45 seconds:
аппаратная регулировка яркости реализована, но на кнопки не выведена - если надо, можешь сам вывести... там достаточно OCR0B менять
Вложения
taho2313.zip
(134.43 KiB) Скачиваний: 211
Последний раз редактировалось Ivanoff-iv Пн дек 30, 2019 19:22:13, всего редактировалось 1 раз.

Re: Тахометр для токарника

Пн дек 30, 2019 17:31:48

Ivanoff-iv, благодарю.

Re: Тахометр для токарника

Пн дек 30, 2019 22:23:42

сразу оговорюсь, что я не призываю писать в таком стиле (запихнув весь код в прерывание) но для себя считаю его допустимым при соблюдении некоторых условий, направленных на недопущение многократного наложения прерываний и срыва стека.
правильней вместо этого разместить тяжелый код в цикле майна и выравнивать начало выполнения его зависающим до события циклом или сном.
Ответить