--- /dev/null
+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
; 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
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
;*****************************************************************************************
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
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
;*****************************************************************************************
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.)
--- /dev/null
+
+; 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
; 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
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
;*****************************************************************************************
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
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
;*****************************************************************************************
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.)