Когда-то для друга, который хотел начать программировать авр_ки, делал подобное.
Упор делал не на оптимальность работы прошивки, а на максимальность понимания процессов,
происходящих в контроллере на примере понятного ему (другу) задания.
Но тем не менее, оно хоть и учебное, но работало.
Поскольку у этого контроллера только 2 внешних прерывания, третьим выбран вход компаратора.
Код: Выделить всё
#include <inavr.h>
#include <iom8.h>
/* Set BIT in ADDRESS */
#define setbit(ADDRESS,BIT) ((ADDRESS) |= (1<<(BIT)))
/* Clear BIT in ADDRESS */
#define clearbit(ADDRESS,BIT) ((ADDRESS) &= ~(1<<(BIT)))
/* Test BIT in ADDRESS */
#define testbit(ADDRESS,BIT) ((ADDRESS) & (1<<(BIT)))
volatile unsigned char worktime1,worktime2,worktime3;
volatile unsigned char oldstatus_motor1,oldstatus_motor2,oldstatus_motor3;
void port_init(void)
{
PORTB = 0x00;//all pin=0
DDRB = 0xFF;//all for output
PORTC = 0x00;//all pin=0
DDRC = 0x7F;//C0-C6 for output
PORTD = 0x00;//all output pinD=0, input pin D5 without pullup_s
DDRD = 0x20;//D5-output, all other-input
}
//TIMER0 initialize (On system clock=8MHZ, overflow interrupt every 32768us.)
void timer0_init(void)
{
TCCR0 = 0x00; //stop
TCCR0 = 0x05; //start timer prescale:1024
}
//Comparator initialize
void comparator_init(void)
{
ACSR = ACSR & 0xF7; //ensure interrupt is off before changing
ACSR = 0x4B; //Use Analog Comparator+Compare interrupt
//fixed bandgap reference voltage replaces the positive input to the Analog Comparator.
//Interrupt trigger level - Rising output edge
//interrupt from 1 to 0 on pin D7
}
void sleep_motor1(void) //мотор 1 ложим в спящее состояние
{
clearbit(PORTD,5); // установили в 1 PORTB,5 договоримся, что лог 1 уменьшает питание этого двигателя до состояния удержания
}
void sleep_motor2(void)
{
clearbit(PORTB,7);
}
void sleep_motor3(void)
{
clearbit(PORTB,6);
}
void wakeup_motor1(void)//мотор 1 выводим из спящего состояния
{
setbit(PORTD,5); //установили в 0 PORTB,5 договоримся, что лог 0 питание этого двигателя на всю катушку
worktime1=0; //раз двигатель проснулся, то переменная, содержащая количество тактов таймера когда двигатель1 неактивен, =0
} //грубо говоря, считаем с нуля.
void wakeup_motor2(void)
{
setbit(PORTB,7);
worktime2=0;
}
void wakeup_motor3(void)
{
setbit(PORTB,6);
worktime3=0;
}
#pragma vector = TIMER0_OVF_vect
__interrupt void timer0_ovf_handler(void)
{
//32786 us time left
worktime1++; //таймер клацнул. увеличиваем переменные неактивности двигателей
worktime2++;
worktime3++;
if (worktime1>=30){sleep_motor1();worktime1=0;}//30 клацаний это около 1 секунды
if (worktime2>=30){sleep_motor2();worktime2=0;}//если двигатель неактивен более секунды,
if (worktime3>=30){sleep_motor3();worktime3=0;}//то ему пора баеньки
}//а чтобы не часто его укладывать(32786 us), счетчики обнулим. повторно уложим через 1 секунду.
//тутычки процедура вычисления статуса, если нужно вращать влево
unsigned char workleft(unsigned char data)
{
unsigned char resultat=1;// промежуточная переменная
switch (data)// смотрим, что нам принесло число, переданное в эту процедуру
{
case 1: {resultat=2;break;} //в зависимости от предыдущих состояний
case 2: {resultat=3;break;} //присваиваем промежуточной переменной различные состояния
case 3: {resultat=4;break;}
case 4: {resultat=5;break;}
case 5: {resultat=6;break;}
case 6: {resultat=7;break;}
case 7: {resultat=8;break;}
case 8: {resultat=1;break;}
default: break; // так нужно...
}
return resultat;// говорим то, что вернет нам эта процедура
}
//а здеся процедура вычисления статуса, если нужно вращать вправо
unsigned char workright(unsigned char data)
{
unsigned char resultat=1;// все, как и выше, только вправо
switch (data)
{
case 1: {resultat=8;break;}
case 2: {resultat=1;break;}
case 3: {resultat=2;break;}
case 4: {resultat=3;break;}
case 5: {resultat=4;break;}
case 6: {resultat=5;break;}
case 7: {resultat=6;break;}
case 8: {resultat=7;break;}
default: break;
}
return resultat;
}
// и вот, наконец само воздействие на состояния выходных линий
void left_motor1(void)
{
unsigned char newstatus; // все, как и раньше. обьявляем промежуточную переменную
newstatus=workleft(oldstatus_motor1); //выясняем, какой статус будет, если раньше у мотора был статус-oldstatus_motor1
clearbit(PORTB,0); //а вращать нужно влево.
clearbit(PORTB,1);
clearbit(PORTB,2); //наитупейшим образом обнуляем выходные линии
clearbit(PORTB,3);
if (newstatus==1) {setbit(PORTB,0);}
if (newstatus==2) {setbit(PORTB,0);setbit(PORTB,1);}
if (newstatus==3) {setbit(PORTB,1);}
if (newstatus==4) {setbit(PORTB,1);setbit(PORTB,2);}
if (newstatus==5) {setbit(PORTB,2);}
if (newstatus==6) {setbit(PORTB,2);setbit(PORTB,3);}
if (newstatus==7) {setbit(PORTB,3);}
if (newstatus==8) {setbit(PORTB,3);setbit(PORTB,0);}
oldstatus_motor1=newstatus; // и напоследок, ведь статус выходных линий уже поменяли, запоминаем новый статус, как старый.
} // это пригодится при повторном вычислении этого статуса
void right_motor1(void)
{
unsigned char newstatus;
newstatus=workright(oldstatus_motor1);// все, как и раньше. Только вправо.
clearbit(PORTB,0);
clearbit(PORTB,1);
clearbit(PORTB,2);
clearbit(PORTB,3);
if (newstatus==1) {setbit(PORTB,0);}
if (newstatus==2) {setbit(PORTB,0);setbit(PORTB,1);}
if (newstatus==3) {setbit(PORTB,1);}
if (newstatus==4) {setbit(PORTB,1);setbit(PORTB,2);}
if (newstatus==5) {setbit(PORTB,2);}
if (newstatus==6) {setbit(PORTB,2);setbit(PORTB,3);}
if (newstatus==7) {setbit(PORTB,3);}
if (newstatus==8) {setbit(PORTB,3);setbit(PORTB,0);}
oldstatus_motor1=newstatus;
}
void left_motor3(void)
{
unsigned char newstatus;
newstatus=workleft(oldstatus_motor3);
clearbit(PORTC,2);
clearbit(PORTC,3);
clearbit(PORTC,4);
clearbit(PORTC,5);
if (newstatus==1) {setbit(PORTC,2);}
if (newstatus==2) {setbit(PORTC,2);setbit(PORTC,3);}
if (newstatus==3) {setbit(PORTC,3);}
if (newstatus==4) {setbit(PORTC,3);setbit(PORTC,4);}
if (newstatus==5) {setbit(PORTC,4);}
if (newstatus==6) {setbit(PORTC,4);setbit(PORTC,5);}
if (newstatus==7) {setbit(PORTC,5);}
if (newstatus==8) {setbit(PORTC,5);setbit(PORTC,2);}
oldstatus_motor3=newstatus;
}
void right_motor3(void)
{
unsigned char newstatus;
newstatus=workright(oldstatus_motor3);
clearbit(PORTC,2);
clearbit(PORTC,3);
clearbit(PORTC,4);
clearbit(PORTC,5);
if (newstatus==1) {setbit(PORTC,2);}
if (newstatus==2) {setbit(PORTC,2);setbit(PORTC,3);}
if (newstatus==3) {setbit(PORTC,3);}
if (newstatus==4) {setbit(PORTC,3);setbit(PORTC,4);}
if (newstatus==5) {setbit(PORTC,4);}
if (newstatus==6) {setbit(PORTC,4);setbit(PORTC,5);}
if (newstatus==7) {setbit(PORTC,5);}
if (newstatus==8) {setbit(PORTC,5);setbit(PORTC,2);}
oldstatus_motor3=newstatus;
}
#pragma vector = ANA_COMP_vect// Сюда мы попадем, если вход AIN1 установится в 0
__interrupt void ana_comp_handler(void)
{
//analog comparator compare event
wakeup_motor3(); // договорились, что рабочий фронт =0, значит двигатель просят подвинуться. Нужно просыпаться
if (!testbit(PIND,1)>0) left_motor3();// а в зависимости, от того, в какую сторону просят крутиться,
else right_motor3(); //вызываем и соответствующие подппрограмки
}
void left_motor2(void)// все, как и раньше. Только для двигателя 2.
{
unsigned char newstatus;
newstatus=workleft(oldstatus_motor2);
clearbit(PORTB,4);
clearbit(PORTB,5);
clearbit(PORTC,0);
clearbit(PORTC,1);
if (newstatus==1) {setbit(PORTB,4);}
if (newstatus==2) {setbit(PORTB,4);setbit(PORTB,5);}
if (newstatus==3) {setbit(PORTB,5);}
if (newstatus==4) {setbit(PORTB,5);setbit(PORTC,0);}
if (newstatus==5) {setbit(PORTC,0);}
if (newstatus==6) {setbit(PORTC,0);setbit(PORTC,1);}
if (newstatus==7) {setbit(PORTC,1);}
if (newstatus==8) {setbit(PORTC,1);setbit(PORTB,4);}
oldstatus_motor2=newstatus;
}
void right_motor2(void)
{
unsigned char newstatus;
newstatus=workright(oldstatus_motor2);
clearbit(PORTB,4);
clearbit(PORTB,5);
clearbit(PORTC,0);
clearbit(PORTC,1);
if (newstatus==1) {setbit(PORTB,4);}
if (newstatus==2) {setbit(PORTB,4);setbit(PORTB,5);}
if (newstatus==3) {setbit(PORTB,5);}
if (newstatus==4) {setbit(PORTB,5);setbit(PORTC,0);}
if (newstatus==5) {setbit(PORTC,0);}
if (newstatus==6) {setbit(PORTC,0);setbit(PORTC,1);}
if (newstatus==7) {setbit(PORTC,1);}
if (newstatus==8) {setbit(PORTC,1);setbit(PORTB,4);}
oldstatus_motor2=newstatus;
}
#pragma vector = INT0_vect// Сюда мы попадем, если вход INT0 установится в 0
__interrupt void int0_handler(void)
{
//external interupt on INT0
wakeup_motor1();// все, как и для мотора3, только для первого
if (!testbit(PIND,0)>0) left_motor1();
else right_motor1();
}
#pragma vector = INT1_vect// Сюда мы попадем, если вход INT1 установится в 0
__interrupt void int1_handler(void)
{
//external interupt on INT1
wakeup_motor2();// выше уже обьяснял.
if (!testbit(PIND,4)>0) left_motor2();
else right_motor2();
}
void init_devices(void)
{
//stop errant interrupts until set up
__disable_interrupt(); //disable all interrupts
port_init();// вызываем процедуру инициализации портов
timer0_init();// вызываем процедуру инициализации таймера
comparator_init();// вызываем процедуру инициализации компаратора
MCUCR = 0x0A;// INT0- Falling edge, INT1- Falling edge (interrupt from 1 to 0)
GICR = 0xC0;// Interrupt from INT0+INT1
TIMSK = 0x01; //timer interrupt sources
__enable_interrupt(); //re-enable interrupts
//all peripherals are now initialized
}
main( void )
{
worktime1=0; //обнуляем переменные, содержащие время неактивности двигателей
worktime2=0;
worktime3=0;
oldstatus_motor1=1;// состояния портов двигателя 1.2.3.4
oldstatus_motor2=1;//примем по умолчанию 1
oldstatus_motor3=1;
init_devices();// Думаю, это не вызывает затруднений для понимания....
__delay_cycles (300);
// В соответствии со статусом 1 установим соответствующие линии портов
clearbit(PORTB,2);//M1
clearbit(PORTB,3);
clearbit(PORTB,1);
setbit(PORTB,0);
clearbit(PORTC,0);//M2
clearbit(PORTC,1);
clearbit(PORTB,5);
setbit(PORTB,4);
clearbit(PORTC,4);//M3
clearbit(PORTC,5);
clearbit(PORTC,3);
setbit(PORTC,2);
sleep_motor1();
sleep_motor2();
sleep_motor3();
while(1){}// Все настроено. Дальше работа идет только при возникновении прерываний.
}