;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; cruise.asm ; cruise.asm controls the car. Fool! Runs on an ATMEL ATmega103 Flash chip ; ; Michael Jeffrey Dobbs ; Spring 2002 .include "M103def.inc" .include "cruise.inc" .cseg ;Set the interrupt vectors .org 0x0000 jmp reset ;reset jmp brake ;IRQ0 jmp clutch ;IRQ1 jmp nullInt ;IRQ2 jmp nullInt ;IRQ3 jmp off ;IRQ4 jmp nullInt ;IRQ5 jmp nullInt ;IRQ6 jmp nullInt ;IRQ7 .org 0x0012 jmp nullInt ;Timer2 Comp jmp nullInt ;Timer2 Overflow jmp tach_int ;Timer1 Capture jmp nullInt ;Timer1 CompA jmp nullInt ;Timer1 CompB jmp nullInt ;Timer1 Overflow jmp Time0 ;Timer0 Compare jmp nullInt ;Timer0 Overflow .org 0x0022 jmp nullInt ;SPI Transfer jmp laser_read ;UART Rx jmp nullInt ;UDR Empty jmp nullInt ;UART Tx jmp nullInt ;ADC Conversion jmp nullInt ;EEPROM Ready jmp nullInt ;Analog Comparator .def lower=r0 .def higher=r1 .def active=r3 .def extrap_count=r4 .def dx=r5 .def distance_readh=r10 .def desired_read=r11 .def distance_read_oldh=r12 .def neg_numb=r13 .def deltatach=r14 .def desired_distance=r15 .def temp=r16 .def rs232_out=r17 .def temp3=r18 .def distance_readl=r19 .def temp2=r20 .def distance_read_oldl=r21 .def tach_oldl=r22 .def tach=r23 .def desired_tach=r24 .def tach_oldh=r25 ;r26-r31 are x.y.z ;ascii .equ comma=44 ;ascii code .equ cr=13 .equ light_on=14 .equ light_off=15 .equ period=46 .equ clrlcd=12 .equ lf=10 ;rs232 .equ clock=6000000 .equ baudrate = 9600 .equ baudkonstat= (clock/(16*baudrate))-1 ;lcd .equ poscmd=16 .equ first_line=64 .equ second_line=20+64 .equ third_line=40+64 .equ fourth_line=60+64 .equ lcd_distance_read=second_line+14 .equ lcd_distance_desired=third_line +14 .equ lcd_speed_read=second_line+7 .equ lcd_speed_desired=third_line+7 ;calc tach .equ tolerance=5 ;10 AKA 2MPH ;speed=(1/3)tach tolerance is in tach units .equ slope_speed=10 ;(3*32)/speed_damp ;49 ;MIKE 9.6 .equ slope_distance=2 ;16 ; (((3*32)/dist_damp) *.7)/2 ;26 ; MIKE 16 .equ speed_damp=10 .equ dist_damp=2 ;laser .equ laser_trigger=0 ;switch .equ yellow=2 .equ green=5 ;buttons .equ set_count=13 ;13/25 of a second to accept set ;valves .equ release1=1 .equ release2=2 .equ vacuum=3 reset: ldi r16,low(RAMEND) ;set up the stack out SPL,r16 ;pointer for the ldi r16,high(RAMEND) ;end of SRAM. out SPH,r16 rjmp start start: rcall IOSetup rcall TimerSetup rcall LCD_Setup ldi ZH,high(2*init_message) ldi ZL,low(2*init_message) rcall outstr rcall TurnOnLaser ldi ZH,high(2* main_message) ldi ZL,low(2* main_message) rcall outstr ldi temp,0x00 ;start off inactive mov active,temp cbi porta,release1 cbi porta,release2 cbi porta,vacuum clr extrap_count sei ;allow Global Interrupts jmp main main: ;match tach is all ;no pushes or pulls! jmp main adj_speed: sbrs active,0 jmp no_valve ;if were inactive turn off mov deltatach,desired_tach sub deltatach,tach BRPL adj_pos ;take 2 comp if we have neg! neg deltatach adj_pos: ;rcall display_desired_distance ;ldi rs232_out,48 ;add rs232_out,temp ;rcall rs_send mov temp,deltatach cpi temp,tolerance brmi hold ;if less than tolerance then loop ;not within tolerance so faster or slower ;check for slower cp desired_tach,tach brmi faster jmp slower ;active, not hold, or slower so must be faster ret ;inf loop no_valve: cbi porta,release1 cbi porta,release2 cbi porta,vacuum ret faster: sbi porta,release1 sbi porta,release2 sbi porta,vacuum ret slower: sbi porta,release1 cbi porta,release2 cbi porta,vacuum ret hold: sbi porta,release1 sbi porta,release2 cbi porta,vacuum ret nullInt: reti IOSetup: ldi temp, 0xFF out DDRA, r16 ;Set Port A for output ldi temp, 0xFF ;Set for pullup resistors ldi temp, 0x00 ;No pullups - hiZ instead out PORTB, r16 ;Set Port B for Pull-Up Resistors out PORTD, r16 ;Set Port D for Pull-Up Resistors out PORTE, r16 ;Set Port E for Pull-Up Resistors ldi temp, 0x00 out DDRB, r16 ;Set Port B for input out DDRD, r16 ;Set Port D for input out DDRE, r16 ;Set Port E for input ;set up interupts on int4,1,0 ldi temp,0x01 ;0x13 out EIMSK,temp ;set int 7-4 to acitvate on rise ldi temp,0xFF out EICR,temp ;set uart up for send/recv and in on recv ldi temp,0x98 out UCR,temp ret ;return from io setup TurnOnLaser: ;initialize laser cbi PORTA, Laser_trigger ;low ldi temp, 0x00 ;set delay rcall delay sbi PORTA, Laser_trigger ;high ldi temp, 0x00 ;set delay rcall delay cbi PORTA, Laser_trigger ;low again to fire away ;init laser reading Y ldi yh,high(2*raw_laser_reading) ldi yl,low(2*raw_laser_reading) ret TimerSetup: ldi r16, 0x0F ;Set to reset & Prescale = 1024 80 set clear on match out TCCR0, r16 ldi r16, 0x20 ;Set Compare to 32d ;test ldi r16,0x50 ;TEST set compare to slow val ;test ldi r16,0xFF out OCR0, r16 ;number were counting to ldi r16, 0x22 ;Enable bit OCIE0 for Timer0 compare int out TIMSK, r16 ;set no compare stuff ldi temp,0x0 out TCCR1A, temp ; set tccr to clk/1024 ;catpure on rise ldi temp,0x45 out TCCR1B, temp ldi temp,0x05 out TCCR2,temp ; init TCNT1L ldi temp,48 out TCNT1L,temp ret Time0: ;runs @23hz push temp in temp,SREG push temp push rs232_out ;pushed temp then SREG,re232_out sbis pinf,yellow ;don't call buttons if yellow high rcall buttons ; check for button press ; mov temp,extrap_count ; cpi temp,3 ; brne noextrap_fun ; ; call extrap ;noextrap_fun: inc extrap_count ;pushed temp then SREG,rs232_out pop rs232_out pop temp out SREG,temp pop temp reti buttons: ;on button is pressed since yellow is low ;if set pressed then set ;ldi rs232_out,'B' ;rcall rs_send sbic pinf,green jmp set sbis pinf,green jmp resume ;error ldi rs232_out,'E' rcall rs_send ret set: ;set distance_desired mov desired_tach,tach mov desired_distance,distance_readl ldi temp,0xff mov active,temp ldi ZH,high(2* set_pressed_message) ldi ZL,low(2* set_pressed_message) rcall outstr rcall display_desired_distance ret resume: ;activate ldi ZH,high(2* resume_pressed_message) ldi ZL,low(2* resume_pressed_message) rcall outstr tst desired_distance breq no_resume ldi temp,0xff mov active,temp no_resume: ret delay: ;set temp to delay amount smaler=longer ;1024 instructions bellow ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop ldi temp2,0 call delay_inner_loop inc temp cpi temp,0 brne delay ret delay_inner_loop: inc temp2 nop nop nop nop nop nop nop nop nop nop nop cpi temp2,0 brne delay_inner_loop ret brake: push temp in temp,SREG push temp push rs232_out ;pushed temp then SREG,re232_out ; SBRS active,0 ; jmp end_brake ;don't refresh screen if we're already disabled fool cbi porta,release1 cbi porta,release2 cbi porta,vacuum clr active ldi ZH,high(2* brake_pressed_message) ldi ZL,low(2* brake_pressed_message) rcall outstr end_brake: ;pushed temp then SREG,rs232_out pop rs232_out pop temp out SREG,temp pop temp reti clutch: push temp in temp,SREG push temp push rs232_out ;pushed temp then SREG,re232_out SBRS active,0 jmp end_clutch ;don't refresh screen if we're already disabled fool clr active ldi ZH,high(2* clutch_pressed_message) ldi ZL,low(2* clutch_pressed_message) rcall outstr end_clutch: ;pushed temp then SREG,rs232_out pop rs232_out pop temp out SREG,temp pop temp reti Lcd_Setup: ;init rs232 ldi temp,baudkonstat out ubrr,temp ;load baudrate ret ;set up screen yo rs_send: ;send rs232_out ;destroys temp ; sbi ucr,txen ;enable transmitter sbis usr,udre ;wait till udre is cleared rjmp rs_send out udr,rs232_out ; send out ; cbi ucr,txen ; clear send enable ret outstr: ; set z to point will print till null aka 00 lpm mov rs232_out,r0 adiw ZL,1 ;inc cpi rs232_out,0 ; see if were done breq outstr_done rcall rs_send jmp outstr outstr_done: ret laser_read: ;sets distance_read ;sets raw_laser_reading push temp in temp,SREG push temp push rs232_out ;pushed temp,SREG,rs232_out in temp,udr st Y+,temp ;store cpi temp,cr breq laser_measurement ;poped temp,SREG,rs232_out pop rs232_out pop temp out SREG,temp pop temp reti ;return if we dont' have all the data laser_measurement: ;temp ;recived cr, branched from laser_read ldi yh,high(2*raw_laser_reading) ;reset Y ldi yl,low(2*raw_laser_reading) ;reset Y ; find period find_period: ld temp,Y+ cpi temp,cr ;if end the we got an error breq bad_laser cpi temp,'.' ; check for . brne find_period sbiw YL,1 ;dec Y by one because of post inc above ;Y is now period ;move and intialitze distance_read for new reading mov distance_read_oldl,distance_readl ;move old value mov distance_read_oldh,distance_readh ;move old value clr distance_readl clr distance_readh clr dx ;initialize dx ;load distance read ld distance_readl,-Y ; Y-1 singles subi distance_readl,48 ;ascii to decimal ;tens ld temp,-Y ; Y-2 tens ;check for comma cpi temp,',' breq laser_read_done ;comma found return subi temp,48 ;ascii to decimal ;set up for mikes mul ldi rs232_out,10 ;intialize ten mov r0,rs232_out mov r1,temp rcall mul ;mul outputs to lower add distance_readl,r0 adc distance_readh,r1 ;hundreds ld temp,-Y ; Y-3 tens ;check for comma cpi temp,',' breq laser_read_done ;comma found return subi temp,48 ;ascii to decimal ;set up for mikes mul ldi rs232_out,100;intialize hindred mov r0,rs232_out mov r1,temp rcall mul ; mul outputs to lower add distance_readl,r0 adc distance_readh,r1 laser_read_done: ldi yh,high(2*raw_laser_reading) ;reset Y ldi yl,low(2*raw_laser_reading) ;reset Y ;check for bigger than 227 aka 0xE3 ;check for 3 most sig bits SBRS distance_readl,7 ;if 128 is clear the ok jmp laser_read_ok SBRS distance_readl,6 ; 64 clear ok jmp laser_read_ok SBRS distance_readl,5 ; 32 clear then ok jmp laser_read_ok ;ok were about maxed 224,225,226,227 so force it to 227 ldi distance_readl,227 laser_read_ok: rcall calc_tach rcall display_distance_read ;popped temp,SREG,rs232_out pop rs232_out pop temp out SREG,temp pop temp reti ;return from laser_read int bad_laser: ;eventually report error ldi yh,high(2*raw_laser_reading) ;reset Y ldi yl,low(2*raw_laser_reading) ;reset Y ; ldi rs232_out,cr ; rcall rs_send ; ldi rs232_out,lf ; rcall rs_send ldi rs232_out,'B' rcall rs_send ldi rs232_out,'L' rcall rs_send ; ldi rs232_out,cr ; rcall rs_send ; ldi rs232_out,lf ; rcall rs_send ;poped temp,SREG,rs232_out pop rs232_out pop temp out SREG,temp pop temp reti ;return from laser read int display_distance_read: push temp push temp2 ;position cursor ldi rs232_out,poscmd rcall rs_send ;send position ldi rs232_out,lcd_distance_read rcall rs_send ;cr for tera ; ldi rs232_out,cr ; rcall rs_send ; ;cr for tera ; ldi rs232_out,lf ; rcall rs_send ;send hundred ldi temp2,47 ;digit_counter asci -1 mov temp,distance_readl display_distance_read_hund: ; check if we have 100 inc temp2 cpi temp,100 brpl display_distance_read_hund_sub ;if where above 100 then cont loop ;send hund mov rs232_out,temp2 rcall rs_send ldi temp2,47 ;digit_counter asci -1 jmp display_distance_read_tens display_distance_read_hund_sub: subi temp,100 jmp display_distance_read_hund ; continue with loop display_distance_read_tens: inc temp2 cpi temp,10 brpl display_distance_read_tens_sub ;if where under 10 then next ;send tens mov rs232_out,temp2 rcall rs_send jmp display_distance_read_single display_distance_read_tens_sub: subi temp,10 jmp display_distance_read_tens ; continue with loop display_distance_read_single: ;rest is single digit idiot ldi rs232_out,48 add rs232_out,temp ;send singles rcall rs_send ;cr for tera ; ldi rs232_out,cr ; rcall rs_send ;cr for tera ; ldi rs232_out,lf ; rcall rs_send pop temp2 pop temp ret ;display_distance_read return back from call display_desired_distance: push temp push temp2 ;position cursor ldi rs232_out,poscmd rcall rs_send ;send position ldi rs232_out,lcd_distance_desired rcall rs_send ;cr for tera ;ldi rs232_out,cr ;rcall rs_send ;cr for tera ;ldi rs232_out,lf ;rcall rs_send ;send hundred ldi temp2,47 ;digit_counter asci -1 mov temp,desired_distance display_desired_distance_hund: ; check if we have 100 inc temp2 cpi temp,100 brpl display_d_distance_hund_sub ;if where above 100 then cont loop ;send hund mov rs232_out,temp2 rcall rs_send ldi temp2,47 ;digit_counter asci -1 jmp display_d_distance_tens display_d_distance_hund_sub: subi temp,100 jmp display_desired_distance_hund ; continue with loop display_d_distance_tens: inc temp2 cpi temp,10 brpl display_d_distance_tens_sub ;if where under 10 then next ;send tens mov rs232_out,temp2 rcall rs_send jmp display_d_distance_single display_d_distance_tens_sub: subi temp,10 jmp display_d_distance_tens ; continue with loop display_d_distance_single: ;rest is single digit idiot ldi rs232_out,48 add rs232_out,temp ;send singles rcall rs_send ;cr for tera ; ldi rs232_out,cr ; rcall rs_send ;cr for tera ; ldi rs232_out,lf ; rcall rs_send pop temp2 pop temp ret ;display_desired_distance return back from call tach_int: ;tachold is last reading ;modifies tach, and tach_old push temp ;push sreg in temp,SREG push temp ;pushed temp,SREG ;tach_old is raw ICR1L from last reading in temp,ICR1L in temp2,ICR1H push temp push temp2 sub temp,tach_oldl sbc temp2,tach_oldh BRPL tach_int_pos ;take 2 comp if we have neg! NEG temp tach_int_pos: mov tach,temp pop temp2 pop temp mov tach_oldl,temp mov tach_oldh,temp2 rcall display_mph_read rcall adj_speed ;pushed temp,SREG ; so poped SREG,temp pop temp out SREG,temp pop temp reti ; return from tach int calc_tach: ;calulates new tach ;modifies desired_tach push temp push temp2 ; mov desired_tach,tach ;compare last measurments mov temp,distance_readl sub temp,distance_read_oldl mov temp2,distance_readh sbc temp2,distance_read_oldh brpl calc_tach_pos_diff neg temp calc_tach_pos_diff: cpi temp,20 brsh calc_tach_done ;now do distance term ; prepare for mul can't do neg clr NEG_NUMB mov temp,desired_distance sub temp,distance_readl ;now sub dx ; sub temp,dx ;neg numb stuff brpl calc_tach_mul_dist ser temp2 mov NEG_NUMB,temp2 neg temp calc_tach_mul_dist: ;prepare for mike mul ldi temp2,slope_distance mov r0,temp2 mov r1,temp rcall mul ;shift right by 5 ldi temp3,0x80 ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ;if it was neg make it neg again sbrc neg_numb,0 neg r0 ;now add in distance term add desired_tach,r0 ;end calcs calc_tach_done: ;display it ;testinfg change rcall display_dmph_read pop temp2 pop temp ret ;return from calc_tach mul: ; put first in r0 ; second in r1 ;result in r0 push temp push temp2 push temp3 ; step 0 ;move mult to temp mov temp,r1 clr r1 ldi temp3,0x80 ;r1 is high ;r0 is low ;mult in temp ; step 1 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ; STEP 2 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ; Step 3 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ; STEP 4 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ; step 5 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ; STEP 6 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ; Step 7 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ; STEP 8 sbrc r0,0 ; if bit0 is zero skip next add add r1,temp ;shift mov temp2,r1 ; temp2 is now the carry LSR r1 LSR r0 sbrc temp2,0 add r0,temp3 ;set carry over if temp2(0) is one ;done ; rcall display_desired_distance ; mov rs232_out,tach ; rcall rs_send pop temp3 pop temp2 pop temp ret ;end of mul off: push temp in temp,SREG push temp push rs232_out ;pushed temp then SREG,re232_out SBRS active,0 jmp end_off ;don't refresh screen if we're already disabled fool clr active ldi ZH,high(2* off_pressed_message) ldi ZL,low(2* off_pressed_message) rcall outstr end_off: ;pushed temp then SREG,rs232_out pop rs232_out pop temp out SREG,temp pop temp reti set_press: ;already pushing and pulling temp,SREG,rs232_out in int ;check if we're active sbrc active,0 jmp closer ;were not set so set this guy up ldi temp,0xff mov active,temp ;if were not set then set ret closer: ret extrap: clr extrap_count mov temp,distance_readl sub temp,distance_read_oldl ;temp is now slope ;mul mov r0,temp ldi temp,6 mov r1,temp rcall mul LSR r0 ;1 LSR r0 ;2 LSR r0 ;3 LSR r0 ;4 LSR r0 ;5 ;aka divide by 32 add dx,r0 ret .include "mph.asm" .include "mphd.asm" init_message: .db clrlcd,"Team Laser Chaser",cr,lf,"Cruise Control",cr,10,"V1.0b Initializing",cr,10,"750 Hours Free",light_on,0 main_message: .db 65,65,clrlcd,"Team Laser Chaser",cr,lf,"Actual:XXXMPH XXXFT",cr,lf,"Target:XXXMPH XXXFT",cr,lf,"Ready",0 ;don't know why I have to pad this brake_pressed_message: .db 65,65,65,65,65,65,poscmd,fourth_line,"Brake Pressed",0 clutch_pressed_message: .db poscmd,fourth_line,"Clutch Pressed",0 off_pressed_message: .db 65,65,65,65,65,65,poscmd,fourth_line,"Deactivated ",0 resume_pressed_message: .db 65,65,65,65,65,65,poscmd,fourth_line,"Resume Pressed!",0 set_pressed_message: .db 65,65,65,65,65,65,poscmd,fourth_line,"Set Pressed!",0 .dseg raw_laser_reading: .byte 20