The 1/150 scale car runs along wire embedded underneath the road. Start, stop, headlights, and turn signals are controlled by remote control. The steering moves along embedded wires, so the car can only run on a set route. The battery is Li-Po 3.7V 30mAh (size:9x9x4mm). The battery is charged by inserting a small self-made charging pin into a hole on the bottom of the car. The dimensions of the car body are 24.5mm(L)x11.4mm(W)x10.5mm(H), the wheelbase is 16.5mm, and the outer diameter of the tire is 4.0mm.
// 1/150 Car System ATtiny1616 20231201 2200
// Microchip Studio Ver:7.0.2542
#include <avr/io.h>
#define F_CPU 3333333
#include <avr/interrupt.h>
uint8_t c1,c2,i,phase,dir,bat,head,tail=8,brk=150;
int8_t acl;
uint16_t ads,cmd,turn,va,ctb;
int16_t spd,top=6000;
ISR(PORTA_PORT_vect){ //IR
switch(phase){ //RTC.CNT:0.122ms
case 0:if((PORTA.IN & 0x80)&&(RTC.CNT>63)&&(RTC.CNT<83)){phase=1;};break; //Leader(9ms)
case 1:if((~PORTA.IN & 0x80)&&(RTC.CNT>27)&&(RTC.CNT<47)){phase=2;i=0;};break; //Blank(4.5ms)
case 2:if(~PORTA.IN & 0x80){
if(i<8 || (i>=16 && i<24)){c1>>=1;if(RTC.CNT>9){c1|=0x80;}} //LSB
if((i>=8 && i<16)||(i>=24 && i<32)){c2>>=1;if(RTC.CNT>9){c2|=0x80;}} //LSB
if(i==15){ads=(c1<<8)+c2;} //ads:Address
if(i==31){cmd=(c1<<8)+c2;} //cmd:Command
i++;
};break;
}
if(i==32){
if(ads==0x10EF){
switch(cmd){
case 0xD827 : //Headlight
head^=1;
if(head){PORTA.OUT|=(1<<2);TCB0.CCMPH=tail;}
else{PORTA.OUT&=~(1<<2);TCB0.CCMPH=0;}
break;
case 0x10EF : dir=2;PORTB.OUT&=~(1<<3);break; //Left Turn Signal
case 0x807F : dir=3;PORTB.OUT&=~(1<<2);break; //Right Turn Signal
case 0x20DF : dir=0;PORTB.OUT&=~(1<<2);PORTB.OUT&=~(1<<3);break; //Turn Signal OFF
case 0x7887 : //Hazard
PORTB.OUT&=~(1<<2);PORTB.OUT&=~(1<<3);
if(dir==2 || dir==3){dir=0;}
if(dir){dir=0;}else{dir=1;}
break;
case 0xA05F : //Acceleration
acl=1;
PORTB.OUT&=~(1<<4);
if(head){TCB0.CCMPH=tail;}else{TCB0.CCMPH=0;}
break;
case 0x00FF : if(spd){acl=-1;TCB0.CCMPH=brk;PORTB.OUT|=(1<<4);}break; //Brake
}
}
phase=0;i=0;
}
RTC.CNT=0;
PORTA.INTFLAGS|=0b10000000; //Clear Flag
}
ISR(RTC_CNT_vect){
phase=0; //Restart
RTC.INTFLAGS=0b00000001; //Clear
}
ISR(TCD0_OVF_vect){ //Interrupt 208Hz
//Acceleration
spd+=acl*10;
if(spd>top){spd=top;acl=0;} //Top Speed
if(spd<0){ //Stop
spd=0;acl=0;
PORTB.OUT&=~(1<<4);
if(head){TCB0.CCMPH=tail;}else{TCB0.CCMPH=0;}
}
TCA0.SINGLE.CMP0=spd;
if(spd<TCA0.SINGLE.CNT && (PORTB.IN & 0x01)){TCA0.SINGLE.CNT=spd;}
//Turn Signal
turn++;
if(!(turn%73)){
if(dir==2){PORTB.OUT^=(1<<2);} //Left
if(dir==3){PORTB.OUT^=(1<<3);} //Right
if(dir==1){PORTB.OUT^=(1<<2)|(1<<3);} //Hazard
if(bat){PORTA.OUT^=(1<<2);} //Low Voltage
if(!bat && !head){PORTA.OUT&=~(1<<2);}
if(!bat && head){PORTA.OUT|=(1<<2);}
turn=0;
}
//Battery Check
ctb++;
if(!(ctb%208)){
ADC1.COMMAND|=0x01; //Start Conversion
while(!(ADC1.INTFLAGS & 0x01));
ADC1.COMMAND|=0x01; //Start Conversion
while(!(ADC1.INTFLAGS & 0x01));
va=ADC1.RES;
if(va<527){bat=1;}else{bat=0;} // 3.3V*(527/1023)=3.4V/2
ctb=0;
}
TCD0.INTFLAGS=0b00000001;
}
int main(void){
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB,0b00010001); //Div6(3.33MHz)
PORTA.DIR =0b00100100; //PA7:IR, PA5:Taillight, PA2:Headlight
PORTB.DIR =0b00011111; //PB4:Third Brake Light, PB3,2:Turn Signal, PB1,PB0:Motor PWM
//External Interrupt
PORTA.PIN7CTRL =0b00001001; //Pull up Enable, Both Edges
PORTA.INTFLAGS =0b10000000; //PA7 INT Enable
//Motor PWM
TCA0.SINGLE.CTRLA =0b00000101; //Div4, ENABLE
TCA0.SINGLE.CTRLB =0b00010011; //CMP0EN, Single-Slope PWM
TCA0.SINGLE.PER =20833; //Top (3333333Hz/Div4/20833=40Hz)
//Taillight
TCB0.CTRLA =0b00000011; //Div2, Enable
TCB0.CTRLB =0b00010111; //CCMPEN, 8bit PWM
TCB0.CCMPL =255; //Top
//Acceleration Interrupt
TCD0.FAULTCTRL =0b00110000; //Compare A,B Enable
TCD0.CTRLB =0b00000000; //WG Mode:ONERAMP
TCD0.CMPBCLR =4000; //Top (3333333Hz/Div4/4000=208Hz)
TCD0.CTRLA =0b01101001; //System Clock, Div4, Enable
TCD0.INTCTRL =0b00000001; //OVF
//IR
RTC.CTRLA =0b00010001; //Div4, RTC Enable
RTC.PER =1000; //RTC.CNT(1count):32768Hz/Div4=8192Hz(0.122ms)
RTC.INTCTRL =0b00000001; //OVF
//Battery Check
ADC1.CTRLA =0b00000001; //RESSEL 10bit, ADC Enable
ADC1.CTRLC =0b01010000; //SAMPCAP, Reference:VDD, DIV2
ADC1.MUXPOS =0b00000111; //AIN7(PC1)
sei();
while(1){}
}