From: ceb Date: Mon, 20 Dec 2004 00:58:10 +0000 (+0000) Subject: added serial receive stuff X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=92be4355274fdc3a427b6af43d6e3b001c8d3811;p=trains.git added serial receive stuff started on transmit to track stuff changed timer to NMRA timer done initialisation of output pins not quite decided on packet format --- diff --git a/cebpic/README.protocol b/cebpic/README.protocol new file mode 100644 index 0000000..87fb3ab --- /dev/null +++ b/cebpic/README.protocol @@ -0,0 +1,12 @@ +NMRA packets from the computer to the PIC should consist of 8-bit bytes. + +bits 7-1 contain the data to be sent to the track +bit 0 is 1 if the byte is the last in the packet and 0 otherwise + +The first 14 data bits in the NMRA packet should be 1s. +(i.e. the first two complete bytes should be 11111110 11111110) +(packets beginning with some other sequence are reserved for non-NMRA messages) + +baud rate 9600 +most significant bit first +8n1 diff --git a/cebpic/nmra-stream.asm b/cebpic/nmra-stream.asm index 69f0e15..7ef1794 100644 --- a/cebpic/nmra-stream.asm +++ b/cebpic/nmra-stream.asm @@ -7,8 +7,28 @@ ; pin 21 (per-pic-led, RD2/PSP2/C1IN) states: high H = green, low L = red, float Z = black + +; *******will need to deal with initial short circuit first + + + + + + include /usr/share/gputils/header/p18f458.inc + +NMRACTRL equ 0x0 ; byte 0: state relevant to NMRA control +TRANSMITBIT equ 0x7 ; bit 7: 0/1 bit currently being transmitted +NEXTACTION equ 0x6 ; bit 7: change polarity on next interrupt y/n + +FROMSERIAL equ 0x1 ; byte 1: from-serial buffer location (in BSR5) +TOTRACK equ 0x2 ; byte 2: to-track buffer location (in BSR5) +TOTRACKBIT equ 0x3 ; byte 3: bit location of pointer within byte + + + + org 0 goto initialise @@ -74,27 +94,42 @@ initialise bcf TRISE,4,0 ; turn off PSPMODE (Data Sheet p100/101) -; timer0 initial config for led flash -; bcf T0CON,6,0 ; p107 Timer0 -> 16bit mode -; bcf T0CON,5,0 ; timer0 use internal clock -; bcf T0CON,3,0 ; use prescaler -; bcf T0CON,2,0 ; } -; bsf T0CON,1,0 ; } prescale value 1:8 (13ms x 8) -; bcf T0CON,0,0 ; } - -; *** setup for timer0 for nmra bits +; timer0 initial config for NMRA timer + bsf T0CON,6,0 ; p107 Timer0 -> 8bit mode + bcf T0CON,5,0 ; timer0 use internal clock + bcf T0CON,3,0 ; use prescaler + bcf T0CON,2,0 ; } + bcf T0CON,1,0 ; } prescale value 1:2 + bcf T0CON,0,0 ; } ;----------------------------------------------------------------------------------------- -; initialise buffers +; initialise buffers (for BSR5, for nmra from-serial/to-track buffers) + + movlw 5 + movwf BSR,0 ; set BRS to 5 + + clrf NMRACTRL,0 ; for bits relevant to control of nmra stream + clrf FROMSERIAL,0 ; for location of write-from-usart pointer within BSR5 + clrf TOTRACK,0 ; for location of send-to-track pointer within BSR5 + ; all in access bank + +;----------------------------------------------------------------------------------------- + +; set booster pwm high + bsf TRISC,1,0 ; make pin 1 (booster pwm) output + bsf PORTC,1,0 ; booster pwm high + +; *** also need to deal with initial short circuit state *** + +; initialise booster direction + bsf TRISC,0,0 ; make pin 0 (booster direction) output + +; initialise next action/transmit bit + bsf NMRACTRL,NEXTACTION,0 + bsf NMRACTRL,TRANSMITBIT,0 - movlw 5 - movwf FSR0H,0 - clrf FSR0L,0 ; set next empty buffer to 500h - movlw 5 - movwf FSR1H,0 - clrf FSR1L,0 ; set current buffer to 500h ;***************************************************************************************** @@ -117,7 +152,7 @@ interrupt_high interrupt_low -; *** check which interrupt. Branch to serial_receive or timer or return +; check which interrupt. Branch to serial_receive or timer or return btfss PIR1,5,0 ; check whether serial receive interrupt bit set goto serial_receive @@ -126,13 +161,46 @@ interrupt_low call led_red call waiting call led_red - retfie ; return from interrupt to main if spurious interrupt + retfie 1 ; return from interrupt to main if spurious interrupt ;***************************************************************************************** serial_receive + movff FROMSERIAL,FSR0L ; set low byte of IND0 pointer + movlw 5 + movwf FSR0H,0 ; set high byte of IND0 pointer + movff RCREG,IND0 ; copy to received register + incf FROMSERIAL,1,0 ; advance FROMSERIAL pointer by 1 byte + btfss RCREG,0,0 ; check if bit 0 is set + call advance_write_buffer ; if so, move to next buffer + call led_red + call waiting + return + +advance_write_buffer + +; check if on 1st byte of new buffer anyway (i.e. last 4 bits = 0) + movff FROMSERIAL,W ; copy FROMSERIAL pointer location to W + andlw 0xF ; investigate last 4 bits + +; if zero (i.e. overflowed to next buffer), branch to overflow check + bz fromserial_overflow + +; if not zero, clear low 4 bits of FROMSERIAL and increment top 4 bits +; aaaabbbb -> bbbbaaaa -> bbbb(aaaa+1) -> 0000(aaaa+1) -> (aaaa+1)0000 + swapf FROMSERIAL,1,0 + incf FROMSERIAL,1,0 + movlw 0xF + andwf FROMSERIAL,1,0 + swapf FROMSERIAL,1,0 + +fromserial_overflow +; clear bit 6 (will set back to buffer 0 if has overflowed to 4) + bcf FROMSERIAL,6,0 + + return ;***************************************************************************************** @@ -140,12 +208,72 @@ serial_receive timer0_interrupt bcf INTCON,2,0 ; clear interrupt-set bit + movlw 0x6E + movwf W,TMR0L ; set timer0 to 0x6E (so that interrupt takes 58us) + + +; check next action - if 0, change to 1 and return + btfsc NMRACTRL,NEXTACTION,0 + goto toggle_output + bsf + retfie 1 + + +; if next action = 1, then toggle output + +toggle_output + btg PORTC,0,0 ; toggle booster output pin + btfss PORTC,0,0 + goto decide_next_bit + goto mid_bit + + +; if transition was 0->1 then we are mid-bit, so copy transmitbit to +; nextaction in preparation for 2nd half of bit and then return + +mid_bit + btfsc NMRACTRL,TRANSMITBIT,0 + bsf NMRACTRL,NEXTACTION,0 + btfss NMRACTRL,TRANSMITBIT,0 + bcf NMRACTRL,NEXTACTION,0 retfie 1 + + +decide_next_bit + +; check whether current to-track buffer = current from-serial buffer +; if yes, transmit 1s (set transmitbit=1, nextaction=1 and return) + + movff FROMSERIAL,W,0 + xorwf TOTRACK,0,0 + andlw 0x30 + bnz read_from_buffer + bsf NMRACTRL,TRANSMITBIT,0 + bsf NMRACTRL,NEXTACTION,0 + retfie 1 + + +read_from_buffer + +; if no, find current to-track byte and bit +; if bit=0, check value of current byte+bit +; set = last byte&bit, so move pointer to next buffer byte 0 +; write na=cb=1 and return + +; if bit=0 not set, advance to bit7 of next byte in buffer +; set na=cb=new bit value, return +; if bit=/=0, +; set na=cb=bit value, advance bit, return + + retfie 1 + -nmra_transmit +; use rotate through carry? +; rotate mask register for anding? would also provide easy check for which bit I'm on +; use top bit for last byte? signed integer arithmetic possible (branch if -ve etc.) diff --git a/cebpic/nmra-test.asm b/cebpic/nmra-test.asm new file mode 100644 index 0000000..76dfc59 --- /dev/null +++ b/cebpic/nmra-test.asm @@ -0,0 +1,126 @@ + +; general initialisation +; set up serial port +; set up 58us timer loop (actually longer for 1st test version + +; receive bytes from computer +; send when computer has sent complete stream (wait for stop, or for 9x1?) +; +~ + + +; pin 21 (per-pic-led, RD2/PSP2/C1IN) states: high H = green, low L = red, float Z = black + + include /usr/share/gputils/header/p18f458.inc + + org 0 + goto initialise_serial + +; interrupt routine call - this needs to be at 00008h (high priority interrupt) + + org 000008h + goto interrupt_high + +; interrupt routine call - this needs to be at 000018h (low priority interrupt) + + org 000018h + goto interrupt_low + + + code + +initialise_serial + +; globally enable interrupts - p77 + bsf RCON,7,0 ; enable priority levels + bsf INTCON,7,0 ; enable high-priority interrupts + bsf INTCON,6,0 ; enable low-priority interrupts + +; initial config - RCSTA register p182 + bsf RCSTA,7,0 ; serial port enable (p182) + bsf RCSTA,6,0 ; 8-bit reception + bsf RCSTA,4,0 ; enable continuous receive + + bcf TXSTA,4,0 ; asynchronous mode + +; set SPBRG to get correct baud rate according to table top right p186 +; (Tosc = 20MHz, desired baud rate = 9600) + bsf TXSTA,2,0 ; set high baud rate + bsf SPBRG,7,0 + bsf SPBRG,0,0 + +; interrupt set-up for serial receive + bsf PIE1,5,0 ; enable USART receive interrupt (p85) + bsf IPR1,5,0 ; set to low-priority interrupt + + +; LED pin (21) initialisation + bcf TRISE,4,0 ; turn off PSPMODE (Data Sheet p100/101) + + +; timer0 (main loop LED flash) initial config + bcf T0CON,6,0 ; p107 Timer0 -> 16bit mode + bcf T0CON,5,0 ; timer0 use internal clock + bcf INTCON,5,0 ; clear TMR0IE => mask interrupt + bcf T0CON,3,0 ; use prescaler + bcf T0CON,2,0 ; } + bsf T0CON,1,0 ; } prescale value 1:8 (13ms x 8) + bcf T0CON,0,0 ; } + +; timer2 (nmra 58us period) initial config (p115) + + + +main + call led_green + call waiting + call led_black + call waiting + goto main + + +led_green + bcf TRISD,2,0 ; make pin RD2 an output (DS100) + bsf LATD,2,0 ; set pin RD2 H (green) + return + +led_black + bsf TRISD,2,0 ; make pin RD2 an input (i.e. set Z, black) (DS100) + return + +led_red + bcf TRISD,2,0 ; make pin RD2 an output (DS100) + bcf LATD,2,0 ; set pin RD2 L (red) + return + + +waiting + bcf INTCON,2,0 ; clear timer0 interrupt bit (p109) + clrf TMR0H,0 ; p107 set high bit of timer0 to 0 (buffered, + ; so only actually set when write to tmr0l occurs) + clrf TMR0L,0 ; set low bit o timer0 - timer now set to 0000h +loop + btfss INTCON,2,0 ; check whethr tiomer0 interrupt has been set - + ; skip next instruction if so + bra loop + return + + +interrupt_high +; needed? + +interrupt_low + btfss PIR1,5,0 ; check whether serial receive interrupt bit set + + btfss PIR1,5,0 ; check whether timer interrupt bit set + call led_red + clrf WREG ; clear working register + movff RCREG,WREG ; read data out of serial receive buffer -> WREG + incf WREG ; increment WREG + movff WREG,TXREG ; write data out of WREG -> serial transmit buffer + retfie +; *** retfie 1 or 0? +; *** also need to clear specific interrupt bits in software + + + end diff --git a/detpic/nmra-stream.asm b/detpic/nmra-stream.asm index 69f0e15..7ef1794 100644 --- a/detpic/nmra-stream.asm +++ b/detpic/nmra-stream.asm @@ -7,8 +7,28 @@ ; pin 21 (per-pic-led, RD2/PSP2/C1IN) states: high H = green, low L = red, float Z = black + +; *******will need to deal with initial short circuit first + + + + + + include /usr/share/gputils/header/p18f458.inc + +NMRACTRL equ 0x0 ; byte 0: state relevant to NMRA control +TRANSMITBIT equ 0x7 ; bit 7: 0/1 bit currently being transmitted +NEXTACTION equ 0x6 ; bit 7: change polarity on next interrupt y/n + +FROMSERIAL equ 0x1 ; byte 1: from-serial buffer location (in BSR5) +TOTRACK equ 0x2 ; byte 2: to-track buffer location (in BSR5) +TOTRACKBIT equ 0x3 ; byte 3: bit location of pointer within byte + + + + org 0 goto initialise @@ -74,27 +94,42 @@ initialise bcf TRISE,4,0 ; turn off PSPMODE (Data Sheet p100/101) -; timer0 initial config for led flash -; bcf T0CON,6,0 ; p107 Timer0 -> 16bit mode -; bcf T0CON,5,0 ; timer0 use internal clock -; bcf T0CON,3,0 ; use prescaler -; bcf T0CON,2,0 ; } -; bsf T0CON,1,0 ; } prescale value 1:8 (13ms x 8) -; bcf T0CON,0,0 ; } - -; *** setup for timer0 for nmra bits +; timer0 initial config for NMRA timer + bsf T0CON,6,0 ; p107 Timer0 -> 8bit mode + bcf T0CON,5,0 ; timer0 use internal clock + bcf T0CON,3,0 ; use prescaler + bcf T0CON,2,0 ; } + bcf T0CON,1,0 ; } prescale value 1:2 + bcf T0CON,0,0 ; } ;----------------------------------------------------------------------------------------- -; initialise buffers +; initialise buffers (for BSR5, for nmra from-serial/to-track buffers) + + movlw 5 + movwf BSR,0 ; set BRS to 5 + + clrf NMRACTRL,0 ; for bits relevant to control of nmra stream + clrf FROMSERIAL,0 ; for location of write-from-usart pointer within BSR5 + clrf TOTRACK,0 ; for location of send-to-track pointer within BSR5 + ; all in access bank + +;----------------------------------------------------------------------------------------- + +; set booster pwm high + bsf TRISC,1,0 ; make pin 1 (booster pwm) output + bsf PORTC,1,0 ; booster pwm high + +; *** also need to deal with initial short circuit state *** + +; initialise booster direction + bsf TRISC,0,0 ; make pin 0 (booster direction) output + +; initialise next action/transmit bit + bsf NMRACTRL,NEXTACTION,0 + bsf NMRACTRL,TRANSMITBIT,0 - movlw 5 - movwf FSR0H,0 - clrf FSR0L,0 ; set next empty buffer to 500h - movlw 5 - movwf FSR1H,0 - clrf FSR1L,0 ; set current buffer to 500h ;***************************************************************************************** @@ -117,7 +152,7 @@ interrupt_high interrupt_low -; *** check which interrupt. Branch to serial_receive or timer or return +; check which interrupt. Branch to serial_receive or timer or return btfss PIR1,5,0 ; check whether serial receive interrupt bit set goto serial_receive @@ -126,13 +161,46 @@ interrupt_low call led_red call waiting call led_red - retfie ; return from interrupt to main if spurious interrupt + retfie 1 ; return from interrupt to main if spurious interrupt ;***************************************************************************************** serial_receive + movff FROMSERIAL,FSR0L ; set low byte of IND0 pointer + movlw 5 + movwf FSR0H,0 ; set high byte of IND0 pointer + movff RCREG,IND0 ; copy to received register + incf FROMSERIAL,1,0 ; advance FROMSERIAL pointer by 1 byte + btfss RCREG,0,0 ; check if bit 0 is set + call advance_write_buffer ; if so, move to next buffer + call led_red + call waiting + return + +advance_write_buffer + +; check if on 1st byte of new buffer anyway (i.e. last 4 bits = 0) + movff FROMSERIAL,W ; copy FROMSERIAL pointer location to W + andlw 0xF ; investigate last 4 bits + +; if zero (i.e. overflowed to next buffer), branch to overflow check + bz fromserial_overflow + +; if not zero, clear low 4 bits of FROMSERIAL and increment top 4 bits +; aaaabbbb -> bbbbaaaa -> bbbb(aaaa+1) -> 0000(aaaa+1) -> (aaaa+1)0000 + swapf FROMSERIAL,1,0 + incf FROMSERIAL,1,0 + movlw 0xF + andwf FROMSERIAL,1,0 + swapf FROMSERIAL,1,0 + +fromserial_overflow +; clear bit 6 (will set back to buffer 0 if has overflowed to 4) + bcf FROMSERIAL,6,0 + + return ;***************************************************************************************** @@ -140,12 +208,72 @@ serial_receive timer0_interrupt bcf INTCON,2,0 ; clear interrupt-set bit + movlw 0x6E + movwf W,TMR0L ; set timer0 to 0x6E (so that interrupt takes 58us) + + +; check next action - if 0, change to 1 and return + btfsc NMRACTRL,NEXTACTION,0 + goto toggle_output + bsf + retfie 1 + + +; if next action = 1, then toggle output + +toggle_output + btg PORTC,0,0 ; toggle booster output pin + btfss PORTC,0,0 + goto decide_next_bit + goto mid_bit + + +; if transition was 0->1 then we are mid-bit, so copy transmitbit to +; nextaction in preparation for 2nd half of bit and then return + +mid_bit + btfsc NMRACTRL,TRANSMITBIT,0 + bsf NMRACTRL,NEXTACTION,0 + btfss NMRACTRL,TRANSMITBIT,0 + bcf NMRACTRL,NEXTACTION,0 retfie 1 + + +decide_next_bit + +; check whether current to-track buffer = current from-serial buffer +; if yes, transmit 1s (set transmitbit=1, nextaction=1 and return) + + movff FROMSERIAL,W,0 + xorwf TOTRACK,0,0 + andlw 0x30 + bnz read_from_buffer + bsf NMRACTRL,TRANSMITBIT,0 + bsf NMRACTRL,NEXTACTION,0 + retfie 1 + + +read_from_buffer + +; if no, find current to-track byte and bit +; if bit=0, check value of current byte+bit +; set = last byte&bit, so move pointer to next buffer byte 0 +; write na=cb=1 and return + +; if bit=0 not set, advance to bit7 of next byte in buffer +; set na=cb=new bit value, return +; if bit=/=0, +; set na=cb=bit value, advance bit, return + + retfie 1 + -nmra_transmit +; use rotate through carry? +; rotate mask register for anding? would also provide easy check for which bit I'm on +; use top bit for last byte? signed integer arithmetic possible (branch if -ve etc.)