Свежие обсуждения
Микроконтроллеры

Спидометр на Atmega8

1 2

Хочу сделать электронный спидометр на Atmega8. Написал программу, в Протеусе вроде работает , посмотрите кто разбирается может что нибудь не так, лучше обнаружить ошибки до прошивки на реальный МК.
Это первый мой проект на МК, вдруг где то накосячил.
Конструктивная критика приветствуется)

Вот программа в CodeVisionAVR:
Частота МК 8МГц, внутренняя:

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

#define digit1 PORTC.0
#define digit2 PORTC.1
#define digit3 PORTC.2

flash char digits[] = {
0b10000000, //0
0b11110001, //1
0b01001000, //2
0b01100000, //3
0b00110001, //4 for common anode
0b00100010, //5
0b00000010, //6
0b11110000, //7
0b00000000, //8
0b00100000 //9
};

char digit_out[3], cur_dig ;
unsigned int indication;
unsigned int i;
double speed;
unsigned int imp;

void recoding(void)
{
if (indication1000)
{
digit_out[0]=indication%10;
indication=indication/10;
digit_out[1]=indication%10;
indication=indication/10;
digit_out[2]=indication%10;
indication=indication/10;
}
}

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
i++;
}

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
switch (cur_dig)
{
case 0:{digit3=0;digit1=1;break;};
case 1:{digit1=0;digit2=1;break;};
case 2:{digit2=0;digit3=1;break;};

}
PORTD=digits[digit_out[cur_dig]]; // catode-anode

cur_dig++;
if (cur_dig==3) cur_dig=0;

}

// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
imp=i;
i=0;
TCNT1H=0;
TCNT1L=0;
}

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
PORTB=0x00;
DDRB=0x00;

// Port C initialization
PORTC=0x00;
DDRC=0x07;

// Port D initialization
PORTD=0x00;
DDRD=0xFB;

// Timer/Counter 0 initialization
TCCR0=0x04;
TCNT0=0x00;

// Timer/Counter 1 initialization
TCCR1A=0x00;
TCCR1B=0x05;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x0F;
OCR1AL=0x42;
OCR1BH=0x00;
OCR1BL=0x00;

// External Interrupt(s) initialization
GICR|=0x40;
MCUCR=0x02;
GIFR=0x40;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x91;

// Analog Comparator initialization

ACSR=0x80;
SFIOR=0x00;

i=0;

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

while (1)
{
speed=(imp*36)/40; //20 - 4 imp/m, 30 - 6 imp/m, 40 - 8 imp/m
indication=ceil(speed);
recoding();
}
}

 

rtm7777: Написал программу, в Протеусе вроде работает

А в чем вопрос? Если работает - значит работает. Спаяйте в железе и проверьте.

 

Ну вдруг какой то неправильный подход использовал)

 

Не бойся, там больше 1000 циклов записи флеша есть.
главное с фьюзами не напортач

Поход - не подход - не важно
всем за свои первые программы потом бывает стыдно, но это никого не останавливает.
Работает - и чудесно!

 

А как можно добавить снижение яркости когда включены габариты? Читал что надо ШИМ использовать, но пока не понял как его сюда прицепить.

 

Может в нулевом таймере как то проверять текущее значение счетчика ?

 

rtm7777: А как можно добавить снижение яркости когда включены габариты?

Сейчас каждый сегмент горит до момента переключения на следующий. Для снижения яркости нужно гасить разряда заранее.
К примеру, при частоте сканирования 100 Гц (10 мс) время свечения каждого разряда будет 10/3 = 3,33 мс (3 разряда). Теперь чтобы снизить яркость, нужно сократить время свечения сегмента ниже 3,33 мс, т.е. при 50% яркости время будет 1,67 мс.

Можно поступить иначе, ШИМить источник питания или общий провод индикаторов с частотой, как минимум в 10 раз выше частоты сканирования.

Касательно представленной программы в первом посте, можно по "варианту 1" модифицировать код:
switch (cur_dig)
{
case 0:{digit3=0;digit1=1;break;};
case 1:{digit1=0;digit2=1;break;};
case 2:{digit2=0;digit3=1;break;};
}

вдвое увеличив количество case'ов, причем в первом оставить все, как есть, а во втором проверять флаг "якрость 50%" и при необходимости гасить все цифры digit*=0. Так получим яркость 50%.
т.е.:
case 0:{digit3=0;digit1=1;break;};
case 1:{if (NigthMode==1) digit1=0; break;};
case 2:{digit1=0;digit2=1;break;};
case 2:{if (NigthMode==1) digit2=0; break;};
case 3 ...
надеюсь, идея ясна.

 

Немного не понял как работают case с одинаковыми вариантами, но как я понял этим способом можно только -50% яркости убрать. Если же нужно -40 то это не вариант. Придумал вроде другой вариант, перенести отображение на 2 таймер, в нем есть прерывания по переполнению, а также по совпадению. Допустим активная цифра управляется по переполнению, но в момент когда происходит прерывание по совпадению ее нужно погасить. Такой вариант имеет право на жизнь?) Только еще и реализовать это нужно, опять на пары не пойду пока не придумаю)

 

rtm7777: но как я понял этим способом можно только -50% яркости убрать

да. Зато просто .
Этим способом можно сделать и больше ступеней градации яркости, увеличив количество case на каждую щифру, но это будет сильно снижать частоту сканирования и код будет не очень красивый.

rtm7777: Придумал вроде другой вариант, перенести отображение на 2 таймер, в нем есть прерывания по переполнению, а также по совпадению

Можно несколько иначе. Рассчитать текущее время свечения 1 цифры, затем высвечивая цифру взводим второй таймер с таким расчетом, чтобы он переполнился ранее, чем время свечения цифры. В прерывании этого второго таймера и гасим цифру заранее (т.е. снижение времени свечения / яркости).
Чтобы точно настроить время срабатывания таймера, можно задавать ему начальное значение отличное от нуля.

 

case 1:{digit1=0;digit2=1;break;};
case 2:{digit2=0;digit3=1;break;};

}
PORTD=digits[digit_out[cur_dig]]; // catode-anode

здесь получается паразитная засветка следующего разряда значением предыдущего - переключился на следующий, а информация на порте стоит старая. Вывод на порт делайте в том же кейсе.
Для низких значений частоты пойдет, но рекомендовано все-таки использовать метод обратного счета -
например
http://vrtp.ru/index.php?showtopic=21445