; Ultrasonic Sonar Sensor Interface ; Copyright 2007, Paul Stoffregen .equ reg0, 0 .equ reg1, 1 .equ reg2, 2 .equ reg3, 3 .equ reg4, 4 .equ locat, 0x0000 ;Location for this program ;internal ram usage: .equ input_buf_size, 64 .equ input_buf_ptr, 0x20 ;points past end of data in input_buf .equ my_address, 0x21 ;holds 2's complement of the address .equ sonar_value, 0x22 ;most recent reading of sonar .equ sonar_count, 0x23 ;reading counter (1 to 255), 0=none yet .equ input_buf, 0x24 .equ stack, (input_buf + input_buf_size) .org locat ljmp standalone_begin .org locat+3 ;at 11.0592 MHz, each count is 1.08507 us ;speed of sound is 73.746 microsounds for one inch ;0: for 22 MHz, div by 2, discard lsb ;1: check if 15127 or higher, clip to 223 ;2: mult by 241 (0.028% error from ideal scale) ;3: shift left 2 bits, keep top 8 bits (round up if necessary) ;this results in an 8 bit number from 0 to 223, which is the total ;inches of sound travel. divide by 2 for distance to target. ;this interrupt happens when the sonar sensor pulse ends ;and timer0 has counted its length. so all we need to do ;is read timer0, scale it properly and store the reading. ;a counter is also incremented (roll over to 1, not 0) ;so the PC can tell if this is a new reading or an old one int0_isr: push psw push acc push b push reg3 push reg4 mov r4, th0 ;read timer0 pulse width count mov a, tl0 ;clr c ;divide by 2 if using 22 MHz crystal ;xch a, r4 ;rrc a ;xch a, r4 ;rrc a mov r3, a ;check if value is 15127 or higher add a, #(65536 - 15127) & 255 mov a, r4 addc a, #(65536 - 15127) & 255 jnc int0_isr_1 mov a, #223 ;clip result to 223 max if too high sjmp int0_isr_2 int0_isr_1: ;xch a, r4 ;lcall phex ;xch a, r4 ;lcall phex mov a, r3 mov b, #241 ;multiply by 241, keeping upper 16 bits mul ab mov r3, b mov a, r4 mov b, #241 mul ab add a, r3 mov r3, a mov a, b addc a, #0 mov r4, a mov a, r3 ;multiply result by 4 rlc a mov r3, a mov a, r4 rlc a mov r4, a mov a, r3 rlc a mov b, a mov a, r4 ;keep upper 8 bits rlc a jnb b.7, int0_isr_2 inc a ;round off if necessary int0_isr_2: mov sonar_value, a ;lcall phex ;mov a, sonar_count ;add a, #1 ;increment count ;jnc int0_isr_3 ;mov a, #1 ;roll over to 1 (not zero) inc sonar_count mov a, sonar_count add a, #(256 - 200) jnc int0_isr_3 mov sonar_count, #1 int0_isr_3: ;mov sonar_count, a pop reg4 pop reg3 pop b pop acc pop psw reti .org locat+0x100 .db 0xA5,0xE5,0xE0,0xA5 ;signiture bytes .db 254,'P',0,0 ;id (35=prog) .db 0,0,0,0 ;prompt code vector .db 0,0,0,0 ;reserved .db 0,0,0,0 ;reserved .db 0,0,0,0 ;reserved .db 0,0,0,0 ;user defined .db 255,255,255,255 ;length and checksum (255=unused) .db "Sonar Interface",0 ;max 31 characters, plus the zero .org locat+0x100+64 ;executable code begins here ;for 8051 dev board ;.equ rxen_pin, 0x90 ;P1.0 low to enable receiver ;.equ txen_pin, 0x91 ;P1.1 low to enable transmitter ;.equ trigger_pin, 0x97 ;P1.7 pulse low to trigger sonar ping ;for actual board .equ rxen_pin, 0xB5 ;P3.5 low to enable receiver, high to enable transmitter .equ txen_pin, 0xB7 ;P3.7 - not connected .equ trigger_pin, 0x97 ;P1.7 pulse low to trigger sonar ping start: jnb ti, * clr p3.4 ;switch to aux serial port (rs-485) standalone_begin: mov sp, #stack mov input_buf_ptr, #input_buf acall read_dip_switches mov sonar_value, #0 mov sonar_count, #0 orl PCON,#10000000b ; set double baud rate mov tmod, #0x2B ;timer0: measure pulse width, timer1: baud rate MOV SCON,#01010000b ; Set Serial for mode 1 ORL TCON,#01010010b ; Start timer 1 both timer mov th1, #255 ;max baud rate (115200 @ 22.1184 MHz) clr ri setb ti setb tr1 setb txen_pin clr rxen_pin ; setb rxen_pin ; clr txen_pin ; mov dptr, #msg_bootup ; lcall pstr ; mov a, my_address ; cpl a ; inc a ; lcall phex ; lcall newline ; jnb ti, * ; setb txen_pin ; clr rxen_pin ; sjmp wait_input_loop ;msg_bootup: ; .db "Begin Code, Addr=",0 wait_input_loop: jnb ri, wait_input_loop clr ri mov a, sbuf ;receive byte from serial port ;lcall phex ;cjne a, #27, got_data ;ljmp 0 got_data: add a, #32 jc got_control_byte got_data_byte: ;push acc ;mov a, #'.' ;lcall cout ;pop acc ;if we get here, the byte was 0 to 223 (ordinary data), so store ;it into the buffer and return to waiting for more data. add a, #224 mov r0, a mov a, input_buf_ptr cjne a, #input_buf+input_buf_size, store_data_byte ;input buffer is full, so simply discard this byte sjmp wait_input_loop store_data_byte: xch a, r0 mov @r0, a inc r0 mov input_buf_ptr, r0 sjmp wait_input_loop got_control_byte: ;if we get here, the byte was 224 to 255 (control). The code ;above already adjusted it to 0 to 31. Now we can just use a ;jump table ;push acc ;mov a, #'_' ;lcall cout ;pop acc mov dptr, #command_jump_table mov b, #3 mul ab jmp @a+dptr command_jump_table: ljmp cmd_flush_input_buf ;224 ljmp cmd_flush_input_buf ;225 ljmp cmd_ping ;226 ljmp cmd_read ;227 ljmp cmd_flush_input_buf ;228 ljmp cmd_flush_input_buf ;229 ljmp cmd_flush_input_buf ;230 ljmp cmd_flush_input_buf ;231 ljmp cmd_flush_input_buf ;232 ljmp cmd_flush_input_buf ;233 ljmp cmd_flush_input_buf ;234 ljmp cmd_flush_input_buf ;235 ljmp cmd_flush_input_buf ;236 ljmp cmd_flush_input_buf ;237 ljmp cmd_flush_input_buf ;238 ljmp cmd_flush_input_buf ;239 ljmp cmd_flush_input_buf ;240 ljmp cmd_flush_input_buf ;241 ljmp cmd_flush_input_buf ;242 ljmp cmd_flush_input_buf ;243 ljmp cmd_flush_input_buf ;244 ljmp cmd_flush_input_buf ;245 ljmp cmd_flush_input_buf ;246 ljmp cmd_flush_input_buf ;247 ljmp cmd_flush_input_buf ;248 ljmp cmd_flush_input_buf ;249 ljmp cmd_flush_input_buf ;250 ljmp cmd_flush_input_buf ;251 ljmp cmd_flush_input_buf ;252 ljmp cmd_flush_input_buf ;253 ljmp cmd_flush_input_buf ;254 ljmp cmd_flush_input_buf ;255 cmd_flush_input_buf: mov input_buf_ptr, #input_buf acall read_dip_switches ajmp wait_input_loop cmd_ping: ;mov a, #'*' ;lcall cout ;this is a command to do the sonar ping, but first we need ;to see if the data has our address on the list. otherwise ;this is a command for some other sonar to ping and we're ;supposed to remain quiet mov a, input_buf_ptr add a, #(256 - input_buf) ;acc = number of bytes of data jz cmd_flush_input_buf mov r2, a mov r0, #input_buf cmd_ping_loop1: mov a, @r0 inc r0 add a, my_address jz cmd_ping_ok djnz r2, cmd_ping_loop1 ;our address wasn't on the list, so just ignore this command ajmp cmd_flush_input_buf cmd_ping_ok: ;ok, it's definitely time to transmit that ping! mov ie, #0 nop clr trigger_pin ;begin the trigger pulse clr tr0 clr tf0 orl tmod, #00001001b ;set up timer0 to measure response pulse anl tmod, #11111001b clr a mov tl0, a mov th0, a setb trigger_pin nop setb tr0 setb it0 ;int0, falling edge trigger clr ie0 ;int0 flag clear mov ie, #10000001b ;enable interrupt ajmp cmd_flush_input_buf cmd_read: ;mov a, #'#' ;lcall cout ;first check if we are the one who is supposed to respond. Only ;one can reply, so only check the first byte of data. mov a, input_buf_ptr ;lcall phex cjne a, #input_buf, cmd_read2 ajmp cmd_flush_input_buf ;no data, so ignore cmd_read2: mov r0, #input_buf mov a, @r0 add a, my_address jz cmd_read3 ajmp cmd_flush_input_buf ;not our address, so ignore cmd_read3: mov r0, #input_buf ;mov @r0, #224 ;inc r0 mov @r0, sonar_value inc r0 mov @r0, sonar_count inc r0 mov @r0, #228 inc r0 mov input_buf_ptr, r0 ajmp transmit_response read_dip_switches: clr a sw1: jb p1.5, sw2 setb acc.0 sw2: jb p1.4, sw4 setb acc.1 sw4: jb p1.3, sw8 setb acc.2 sw8: jb p1.2, sw_calc setb acc.3 sw_calc: cpl a inc a mov my_address, a ;mov my_address, #256 - 5 ret ;transmit a response on the RS485 network. The complete response ;must be in "input_buffer" and "input_buf_ptr" must point to the ;byte after the end of the response. transmit_response: ;first, delay 2 character times before turning on the transmitter, ;to allow time for the controller that send up a request to turn ;off its transmitter. acall byte_delay acall byte_delay ;now let's transmit setb rxen_pin clr txen_pin mov r0, #input_buf ;r0 point to buffer data mov a, input_buf_ptr add a, #(256 - input_buf) mov r2, a ;r2 is number of bytes in buffer transmit_response_loop: jnb ti, transmit_response_loop clr ti mov sbuf, @r0 ;mov a, @r0 ;lcall phex inc r0 djnz r2, transmit_response_loop ;lcall newline transmit_wait_done: jnb ti, transmit_wait_done ;todo: is any additional delay required here? transmit_response_end: setb txen_pin clr rxen_pin ajmp cmd_flush_input_buf ;this code delays exactly one byte time (assuming no interrupts) byte_delay: ;2 mov a, th1 ;1 read baud const cpl a ;1 inc a ;1 a = # cycles per 1/16 bit mov b, #20 ;1 mul ab ;4 a/b has number of cycles / 8 add a, #254 ;1 xch a, b ;1 reduce by 2 for the 16 cycles of addc a, #255 ;1 to compute all this, and the ret xch a, b ;1 byte_delay_loop: nop ;1 now delay exactly 8 cycles for nop ;1 each count in a/b add a, #255 ;1 xch a, b ;1 addc a, #255 ;1 xch a, b ;1 jc byte_delay_loop ;2 ret ;2 cout: jnb ti, cout clr ti ;clr ti before the mov to sbuf! mov sbuf, a ret phex: phex8: acall phex_b phex_b: swap a ;SWAP A will be twice => A unchaInged phex1: push acc anl a, #15 add a, #0x90 ; acc is 0x9X, where X is hex digit da a ; if A to F, C=1 and lower four bits are 0..5 addc a, #0x40 da a acall cout pop acc ret newline:push acc ;print one newline mov a, #13 acall cout mov a, #10 acall cout pop acc ret pstr: push acc pstr1: clr a movc a, @a+dptr inc dptr jz pstr2 mov c, acc.7 anl a, #0x7F acall cout jc pstr2 sjmp pstr1 pstr2: pop acc ret