Tuesday, March 14, 2000

Queen's Solar Car Display System

Andrew Barrow and I (but mostly Andrew Barrow) designed a display system for the Queen's Solar Car. We used two PIC16F84 microcontrollers, and the module communicated with the rest of the car over a CAN-BUS system. After our breadboard prototype was complete and debugged we used a CAD program to print a scale layout with a laser printer. We used an iron to transfer the ink from the layout to the copper, which allowed us to etch our board.

Queen's Solar Car Display

Queen's Solar Car Display

Queen's Solar Car Display

The system displays the actual speed and target speed on the two dual seven segment displays. These are controlled by one PIC16F84 which multiplexes over the four digits. This PIC receives its data from the master PIC via a SCI routine I wrote. The two LEDs are signal light indicators which also support four way flashing. They are controlled by the master PIC. The master PIC also controls the LCD, again via SCI. On the LCD is displayed the motor's voltage and current and the solar array's voltage and current. All data is retrieved over the CAN-BUS system by the master PIC.

Note: I can't remember, but I think tabs were required in the source files.

bus

; a bus circuit.  push a button on one end and the light turns on on the other end.
; identical code for both ends. check interrupt flag pin, data in pin, data out pin, 
; clock pin, cs pin on both pics, also loan bus oscilator.

LIST P=16F84
errorlevel 0,-305
INCLUDE "P16F84.inc"
__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
;--------------------------------------------------------------------------------------------
poser	equ	0c		;  toggle bit for what the button state was, last time checked
lit	equ	0d		;  toggle bit for what the light was, last time checked

addressbyte	equ	3b	;  var for passing addy byte to wribyte
databyte	equ	3c	;  var for passing data byte to wribyte
IDlow	equ	H'00'		;  low byte of this node's standard CAN ID
IDhigh	equ	H'00'		;  high byte of this node's standard CAN ID
				;  NB above also sets standard mode only
handler equ	3d		;  serial shift register value
cbit	equ	3e		;  for the eight cycle to send an entire byte
swalo	equ	3f		;  delay for the clock
;--------------------------------------------------------------------------------------------
setup	org	0
	clrf	portb		;  clears all the bits in portb
	clrf	porta		;  clears all the bits in porta
	clrf	status		;  clears all the bits in status
	bsf	status, rp0	;  switches to the right hand collum
	movlw	0		;  -->
	movwf	trisb		;  sets portb as output
	movlw	H'ff'		;  -->
	movwf	trisa		;  sets porta as input
	bcf	status, rp0	;  switches back to left hand collum
	bsf	portb, 5	;  taking cs high so bus controller doesn't listen
	movlw	H'0f'		;  -->
	movwf	databyte	;  -->
	movlw	H'0c'		;  -->
	movwf	addressbyte	;  -->
	call	wribyte		;  putting bus into pin interrupt mode
	movlw	H'f0'		;  -->
	movwf	databyte	;  -->
	movlw	H'60'		;  -->
	movwf	addressbyte	;  -->
	call	wribyte		;  setting bus filters such that all bus 
				;  messages show up in buffer 0
	movlw	H'ff'		;  -->
	movwf	databyte	;  -->
	movlw   H'2a'		;  -->
	movwf	addressbyte	;  -->
	call 	wribyte		;  make it the slowest bit rate on the bus

	movlw	H'ff'		;  -->
	movwf	databyte	;  -->
	movlw   H'29'		;  -->
	movwf	addressbyte	;  -->
	call 	wribyte		;  make it the slowest bit rate on the bus

	movlw	H'07'		;  -->
	movwf	databyte	;  -->
	movlw   H'28'		;  -->
	movwf	addressbyte	;  -->
	call 	wribyte		;  make it the slowest bit rate on the bus

	movlw	H'04'		;  -->
	movwf	databyte	;  -->
	movlw   H'0f'		;  -->
	movwf	addressbyte	;  -->
	call 	wribyte		;  set it into normal mode
;--------------------------------------------------------------------------------------------
init	movlw	01		;  -->
	movwf	poser		;  starts knowing that the button is up.  
				;  note: when the button is up i'm gettin a 1
	movwf	H'40'		;  starts knowing that the datalength 
				;  it is going to send will ALLWAYS be 1
	movwf	H'41'		;  starts knowing that the data 
				;  it is going to send will ALLWAYS be 1
	movlw	00		;  -->
	movwf	lit		;  starts knowing that the light is off
loop	btfsc	porta, 0	;  -->
	goto	up		;  -->
	goto	down		;  see if the button is up or down
up	btfss	poser, 0	;  -->
	goto	tellu		;  see if this qualifies as a change of state
	goto	noth		;  ie: now that i am recieving a 0, was i recieving a 1 before
down	btfsc	poser, 0	; -->
	goto	telld		; see if this qualifies as a change of state
	goto	noth		; ie: now that i am recieving a 1, was i recieving a 0 before
tellu	movlw	01		;  note:  when it is up, pin recieves a 1
	movwf	poser		;  record that the state has chaged from down to up
	call	sendmsg		;  goes to subroutine at bottom of document
	goto    noth
telld	movlw	00		; note:  when it is down, pin recievs a 0
	movwf	poser		; record that the state has chaged from up to down
	call	sendmsg		; goes to subroutine at bottom of document
noth	btfsc	porta, 1	;  check if there is an interrupt on the bus
	goto	deth		;  there is no message so go to the bottom
	call	getmsg		;  -->
	btfss	H'33', 0	;  evaluate the message
	goto	deth		;  if message doesn't have a 1 in the zeroth bit, ignore it
	btfss	lit, 0		;  check the current state of the led
	goto	ton		;  if it was off, turn it on
toff	movlw	00		;  -->
	movwf	lit		;  record that we turned it off
	bcf	portb, 3	;  turn it off
	goto	deth		;  done everyting, go to end of program
ton	movlw	01		;  -->
	movwf	lit		;  record that we turned it on
	bsf	portb, 3	;  turn it on
deth	goto	loop		;  back to begining
;--------------------------------------------------------------------------------------------
;--- sendmsg subroutine
sendmsg movlw 	H'30'		;  address of first byte in CAN transmit buffer
	movwf   addressbyte	;  pass to addressbyte param of wribyte
	movlw   0		;  write a 0 data to this (priority 0, don't send yet)
	movwf	databyte	;  pass to databyte param of wribyte
	call	wribyte		;  write 0 to 30h in CAN controller
	movlw   H'31'		;  standard ID high byte register address
	movwf   addressbyte	;  send addy
	movlw	IDhigh		;  standard ID CAN high node address (be very sure to
	  			;  declare these differently for each node
	movwf   databyte	;  send the CAN node byte
	call 	wribyte 	;  SID low byte reg address
	incf	addressbyte	;
	movlw	IDlow		;  <see IDhigh remarks>
	movwf	databyte	;
	call 	wribyte		;  skip regs 32-33h (extended ID bytes)
				;-----------------------
				;  next we have to get our data length value from
				;  40h (passed into sendmsg there) and put it into
				;  the DLC register in the transmit buffer.
				;  then we have to read that number of data bytes
				;  from the param array (41-48h) and save them to
				;  the buffer (36-3dh)
	movlw	H'35'		;  addy of DLC register
 	movwf	addressbyte	;
	movf	40,w		;  get data length
	movwf	databyte	;
	call 	wribyte		;  store it
	incf	addressbyte	;
	movf	41,w		;
	movwf	databyte	;
	call    wribyte		;  store data byte 1
	incf    addressbyte	;
	movf	42,w		;
	movwf   databyte	;
        call    wribyte		;  store data byte 2
        incf	addressbyte	;
	movf	H'43',w		;
	movwf	databyte	;
	call 	wribyte		;  store data byte 3
	incf    addressbyte	;
	movf	H'44',w		;
	movwf   databyte	;
	call 	wribyte		;  store data byte 4
	incf	addressbyte	;
	movf	H'45',w		;
	movwf	databyte	;
	call 	wribyte		;  store data byte 5
	incf    addressbyte	;
	movf	H'46',w		;
	movwf	databyte	;
	call	wribyte		;  store data byte 6
	incf	addressbyte	;
	movf	H'47',w		;
	movwf	databyte	;
	call 	wribyte		;  store data byte 7
	incf	addressbyte	;
	movf	H'48',w		;
	movwf	databyte	;
	call    wribyte		;  store data byte 8

				;  now set transmit flag high
	movlw   H'30'		;  address of buffer byte 1
	movwf	addressbyte	;
	movlw	H'08'		;  turn bit 4 high to transmit
	movwf	databyte	;
	call 	wribyte		;  transmit entire packet.

	movlw	H'81'
	movwf	handler
	call	outbyte

	return			;  --
;--------------------------------------------------------------------------------------------
;--- wribyte subroutine
;--- manages the delivery of three bytes to the bus, one of which is data
;--- addressbyte holds the address to which you write
;--- databyte holds the data you send
;--- portb, 5 is /cs
;--- portb, 6 is clock pin
;--- portb, 7 is data pin
wribyte bcf	portb, 5	;  taking cs low so bus controller listens
	movlw	H'02'		;  -->
	movwf	handler
	call	outbyte		;  sends a byte that tells the bus we want to WRITE
	movf	addressbyte, w	;  you must name a register "addressbyte" and have 
				;  it holding the address you are writing to
	movwf	handler
	call	outbyte		;  sends byte determining the register we are 
				;  writing to on the bus
	movf	databyte, w	;  you must name a register "databyte" 
				;  and have it holding the data you wish to send
	movwf	handler
	call	outbyte		;  sends our data byte
	bsf	portb, 5	;  taking cs high so bus controller stops listening
	return			;  --
;--------------------------------------------------------------------------------------------
;--- outbyte subroutine
;--- sends to the bus the byte that is in the handler
;--- portb, 5 is /cs
;--- portb, 6 is clock pin
;--- portb, 7 is data pin
outbyte	movlw	H'08'		;  -->
	movwf	cbit		;  set up the eight cycle for sending 8 bits
bloop	btfsc	handler, 7	;  check the seventh bit of w
	bsf	portb, 7	;  if it is 1 put a one on the pin
	btfss	handler, 7	;  if it was 1, skip the command for if it was zero
	bcf	portb, 7	;  if it is 0 put a zero on the pin
	bsf	portb, 6	;  clock goes up
	bcf	portb, 6	;  clock goes down and the bus has taken the bit
	rlf	handler,1	;  line up the next bit
	decfsz	cbit, f		;  --
	goto	bloop		;  --
	return			;  --
;--------------------------------------------------------------------------------------------
;--- getmsg subroutine
getmsg	movlw 	H'65'		;  address of first byte in CAN transmit buffer
	movwf   addressbyte	;  pass to addressbyte param of wribyte
	call	reabyte		;  write 0 to 30h in CAN controller
	movf    databyte, w
        andlw   H'0f'
	movwf	H'32'	

	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'33'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'34'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'35'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'36'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'37'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'38'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'39'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'3A'
	movlw	H'2c'		;  -->
	movwf	addressbyte	;  -->
	movlw	H'00'		;  -->
	movwf	databyte	;  -->
	call	wribyte		;  put the interrupt pin back
	return			;  --
;--------------------------------------------------------------------------------------------
;--- reabyte subroutine
;--- manages the sending two bytes to the bus and retreval of one data byte
;--- addressbyte holds the address from which you read
;--- databyte holds the data you read
;--- portb, 5 is /cs
;--- portb, 6 is clock pin
;--- porta, 3 is data pin
reabyte bcf	portb, 5	;  taking cs low so bus controller listens
	movlw	H'03'		;  -->
	movwf	handler		;  -->
	call	outbyte		;  sends a byte that tells the bus we want to READ
	movf	addressbyte, 0	;  you must name a register "addressbyte" and 
				;  have it holding the address you are reading from
	movwf	handler
	call	outbyte		;  sends byte determining the register we are 
				;  reading from on the bus
	call	inbyte		;  gets our data byte
	bsf	portb, 5	;  taking cs high so bus controller stops listening
	return			;  --
;--------------------------------------------------------------------------------------------
;--- inbyte subroutine
;--- gets a byte from the bus
;--- portb, 5 is /cs
;--- portb, 6 is clock pin
;--- porta, 3 is data pin
inbyte	movlw	H'08'		;  -->
	movwf	cbit		;  set up the eight cycle for sending 8 bits
cloop	bsf	portb, 6	;  clock goes up
	btfss	porta, 3	;  check data
	bcf     databyte,0 	;  if data was a 0 record a 0
	btfsc	porta, 3	;  if data was a 0 don't do command for if data was a 1
	bsf     databyte,0	;  if data was a 1 record a 1
	bcf	portb, 6	;  clock goes down and we have taken the bit
	rlf	databyte,1	;  line up the next bit
	decfsz	cbit, f		;  --
	goto	cloop		;  --
	rrf	databyte,1	;  line up the next bit
	return			;  --
;--------------------------------------------------------------------------------------------
	end			;  --

master

;!!!!!!!!!!!speed can't be more than 99!!!!!!!!!!

;=============================================================================================
;  General Setup
;=============================================================================================
LIST P=16C84, F=INHX8M
__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC
include <p16c84.inc>
OSC_FREQ	EQU	D'4000000'	; Oscillator Frequency is 4 MHz
;=============================================================================================
;  Variables for controlling the LCD
;=============================================================================================
LCD_DATA	EQU	PORTA	; LCD data lines interface
LCD_DATA_TRIS	EQU	TRISA
DB3		EQU	3	; LCD dataline 3
DB2		EQU	2	; LCD dataline 2
DB1		EQU	1	; LCD dataline 1
DB0		EQU	0	; LCD dataline 0 (LSB)
LCD_CTRL	EQU	PORTB	; LCD control lines interface
LCD_E		EQU	2	; LCD Enable control line
LCD_RW		EQU	1	; LCD Read/Write control line
LCD_RS		EQU	0	; LCD Register-Select control line
LCD_TEMP	EQU	0c	; LCD subroutines internal use
TABLE_INDEX	EQU	0d	; Index to table strings
DELAY		equ	0e	; Used in DELAYxxx routines
X_DELAY		equ	0f	; Used in X_DELAYxxx routines
data1		equ	10	;  array current
data2		equ	11	;  motor current
data3		equ	12	;  battery current
data4		equ	13	;  battery voltage
datax		equ	14	;  for porcessing the hex data into decimal asci on screen
espace		equ	15	;  empty space for holding toggle values when determining
				;   what decimal asci value to display on the screen from 
				;  given hex data
gspace		equ	23	;  empty space for holding counting values
rolls		equ	21	;  loop counter
slide		equ	24	;  rotating bit tracker
addy		equ	22	;  lcd character on screen position address
;=============================================================================================
;  Variables for SPI to secondary PIC
;=============================================================================================
holder	 	equ	16	;  serial shift register value
ewait		equ	17	;  for the eight cycle to send an entire byte
dwait		equ	18	;  delay for the clock
Pclk		equ	porta	;  -->
PclkP		equ	0	;  clock is porta, 0
Pdat		equ	porta	;  -->  
PdatP		equ	1	;  data is porta, 1
Pcs		equ	portb	;  -->  
PcsP		equ	5	;  cs is portb, 5
tspeed		equ	19	;  Target Speed, delivered by the bus. 
				;  Sent to secondary PIC vis SPI
aspeed		equ	1a	;  Actual Speed, delivered by the bus. 
				;  Sent to secondary PIC vis SPI
;=============================================================================================
;  Variables for signal lights
;=============================================================================================
sig		equ	1b	;  four state value for none, right, left or both lights
Lsig		equ	portb	;  -->
LsigP		equ	7	;  left signal light is attached to portb, 7
Rsig		equ	portb	;  -->
RsigP		equ	6	;  right signal light is attached to portb, 6
;=============================================================================================
;  Variables for SPI to the BUS
;cs		b5	now=	b4	output
;clock		b6	now=	a0	output
;data out	b7	now=	a1	output
;data in	a3	now=	a2	input
;=============================================================================================
addressbyte	equ	1c	;  var for passing addy byte to wribyte
databyte	equ	1d	;  var for passing data byte to wribyte
IDlow		equ     H'00'	;  low byte of this node's standard CAN ID
IDhigh		equ	H'00'	;  high byte of this node's standard CAN ID
				;  NB above also sets standard mode only
handler 	equ	1e	;  serial shift register value
cbit		equ	1f	;  for the eight cycle to send an entire byte
swalo		equ	20	;  delay for the clock
tempx		equ	25	;  prototyping needs (toggling between ones sent over bus)
;=============================================================================================
;  Initialization
;=============================================================================================
	ORG	0
	clrf	portb		;  clears all the bits in portb
	clrf	porta		;  clears all the bits in porta
	clrf	status		;  clears all the bits in status
	CLRF	INTCON		;  Clear int-flags, Disable interrupts
	bsf	status, rp0	;  select bank 1
	movlw	H'08'		;  -->
	movwf	trisb		;  sets portb as output except pin 3 as input
	movlw	H'00'		;  -->
	movwf	trisa		;  sets porta as output 
	bcf	status, rp0	;  switches back to left hand collum
	bsf	portb, 5	;  take cs high so secondary pic doesn't listen.
	bsf	portb, 4	;  taking cs high so bus controller doesn't listen
	CALL	LCDINIT		;  Initialize LCDisplay
	call	binit		;  Initialize the BUS
	call	imsg		;  Initial blank values on lcd
	movlw	H'82'		;  -->
	movwf	tspeed		;  -->
	movwf	aspeed		;  -->
	movwf	data1		;  -->
	movwf	data2		;  -->
	movwf	data3		;  -->
	movwf	data4		;  initial values of zero
	movwf	sig		;  defines signal light state as both off
;=============================================================================================
;  Program Body
;=============================================================================================
	movlw	01		;  -->
	movwf	H'40'		;  starts knowing that the datalength 
				;  it is going to send will ALLWAYS be 1
	movwf	H'41'		;  starts knowing that the data 
				;  it is going to send will ALLWAYS be 1
	movlw	00		;
	movwf	tempx		;for toggling between ones sent by the bus
loop	call	gobyte
	call	sigl
	call	dataput
	movlw	H'ff'
	movwf	gspace
okill	movlw	H'ff'
	movwf	espace
kill	decfsz	espace, f
	goto	kill
	decfsz	gspace, f
	goto	okill
;	bsf	status, rp0	;  select bank 1
;	movlw	H'08'		;  -->
;	movwf	trisb		;  sets portb as output except pin 3 as input
;	bcf	status, rp0	;  select bank 1
	btfsc	portb, 3	;  check if there is an interrupt on the bus
	goto	loop		;  there is no message so go to the bottom
	call	getmsg		;  -->
	btfss	H'33', 0	;  evaluate the message
	goto	loop		;  if the message does not have a 1 
				;  in the zeroth bit then ignore it
	incf	tempx, f	;
	btfss	tempx, 0	; 'cause it sends a one every time
	goto	bity		; and we wish to percieve it as up/down on a button
	movlw	H'06'		;  -->
	movwf	tspeed		;  -->
	movwf	aspeed		;  -->
	movwf	data1		;  -->
	movwf	data2		;  -->
	movwf	data3		;  -->
	movwf	data4		;  initial values of zero
	movlw	H'04'		;  -->
	movwf	sig		;  defines signal light state as both off	
	goto	loop
bity	movlw	H'88'		;  -->
	movwf	tspeed		;  -->
	movwf	aspeed		;  -->
	movwf	data1		;  -->
	movwf	data2		;  -->
	movwf	data3		;  -->
	movwf	data4		;  initial values of zero
	movwf	sig		;  defines signal light state as both off	
	goto	loop

;=============================================================================================
;  Subroutines for controlling the signal lights
;=============================================================================================
;  tests the variable "sig"
;  xxxx 0001 means both off
;  xxxx 0010 means right on, left off
;  xxxx 0100 means left on, right off 
;  xxxx 1000 means both on
;--------------------------------------------------------------------------------------------
sigl	btfsc	sig, 0
	goto	off
	btfsc	sig, 1
	goto	right
	btfsc	sig, 2
	goto	left
	btfsc	sig, 3
	goto	both
	return
off	bcf	Lsig, LsigP	;  turn on the left signal light
	bcf	Rsig, RsigP	;  turn on the right signal light
	return			;  --
right	bcf	Lsig, LsigP	;  turn on the left signal light
	bsf	Rsig, RsigP	;  turn on the right signal light
	return			;  --
left	bsf	Lsig, LsigP	;  turn on the left signal light
	bcf	Rsig, RsigP	;  turn on the right signal light
	return			;  --
both	bsf	Lsig, LsigP	;  turn on the left signal light
	bsf	Rsig, RsigP	;  turn on the right signal light
	return			;  --
;=============================================================================================
;  Subroutines for controlling the LCD
;=============================================================================================
;  "dataput" puts variables data1-4 in their proper location on the screen
;--------------------------------------------------------------------------------------------
dataput	movlw	H'03'		;  -->
	movwf	addy
	movf	data1, w	;  -->
	movwf	datax		;  -->
	call	datarol
	movlw	H'0b'		;  -->
	movwf	addy
	movf	data2, w	;  -->
	movwf	datax		;  -->
	call	datarol
	movlw	H'43'		;  -->
	movwf	addy
	movf	data3, w	;  -->
	movwf	datax		;  -->
	call	datarol
	movlw	H'4b'		;  -->
	movwf	addy
	movf	data4, w	;  -->
	movwf	datax		;  -->
	call	datarol
	return			;  --
;=============================================================================================
datarol	movlw	H'03'
	movwf	rolls
	movlw	H'01'
	movwf	slide
zloop	movf	datax, w
	movwf	espace
	movlw	H'00'
	movwf	gspace
	movf	addy, w
	call	lcdsdda
	btfsc	slide, 0
	movlw	H'64'
	btfsc	slide, 1
	movlw	H'0a'
	btfsc	slide, 2
	movlw	H'01'
cutit	subwf	espace, f
	btfss	status, c
	goto	printit
	incf	gspace, f
	goto	cutit
printit	movlw	H'30'
	addwf	gspace, w
	call	lcdputchar
	rlf	slide, f
	btfsc	gspace, 0
	goto	notz
	btfsc	gspace, 1
	goto	notz
	btfsc	gspace, 2
	goto	notz
	btfss	gspace, 3
	goto	fini
notz	btfsc	slide, 1
	goto	takeh
taket	movlw	H'0a'
	subwf	datax, f
	decfsz	gspace, f
	goto	taket
	goto	fini
takeh	movlw	H'64'
	subwf	datax, f
	decfsz	gspace, f
	goto	takeh
fini	incf	addy, f
	decfsz	rolls
	goto	zloop
	return
;=============================================================================================
;  puts the inital lables (current, voltage, etc) on the lcd
;--------------------------------------------------------------------------------------------
Imsg	MOVLW	H'00'
	CALL	LCDSDDA		; Position cursor leftmost on first line
	MOVLW	0		; 1st index of table message
	CALL	TABLE_MSG	; Display message
	MOVLW	H'08'
	CALL	LCDSDDA		; Position cursor out on first line
	MOVLW	H'07'		; 7th index of table message
	CALL	TABLE_MSG	; Display message
	MOVLW	H'40'
	CALL	LCDSDDA		; Position cursor leftmost on second line
	MOVLW	H'0e'		; 13th index of table message
	CALL	TABLE_MSG	; Display message
	MOVLW	H'48'
	CALL	LCDSDDA		; Position cursor out on second line
	MOVLW	H'15'		; 19th index of table message
	CALL	TABLE_MSG	; Display message
	return			;  --
;=============================================================================================
TABLE_MSG	MOVWF	TABLE_INDEX	; Holds message address
		CALL	MSG1
		ANDLW	0x0FF		; Check if at end of message
		BTFSC	STATUS, Z	; (zero returned at end)
		GOTO	TABLE_MSG_END             
		CALL	LCDPUTCHAR	; Display character
		MOVF	TABLE_INDEX, W	; Point to next character
		ADDLW	1
		GOTO	TABLE_MSG
TABLE_MSG_END	RETURN
;=============================================================================================
MSG1	addwf	PCL ,F		;Jump to char pointed to in W reg
	retlw	'I'
	retlw	'a'
	retlw	' '
	retlw	'_'
	retlw	'_'
	retlw	'_'
	retlw	0
	retlw	'I'
	retlw	'm'
	retlw	' '
	retlw	'_'
	retlw	'_'
	retlw	'_'
	retlw	0
	retlw	'I'
	retlw	'b'
	retlw	' '
	retlw	'_'
	retlw	'_'
	retlw	'_'
	retlw	0
	retlw	'V'
	retlw	'b'
	retlw	' '
	retlw	'_'
	retlw	'_'
	retlw	'_'
	retlw	0
;=============================================================================================
LCDINIT	MOVLW	0x01E		; Busy-flag is not yet valid ... power-up delay
	CALL	X_DELAY500	; 30 * 0.5mS = 15mS
				;  Busy Flag should be valid from here
	MOVLW	0x028		; 8-bit-interface, 2-lines
	CALL	LCDPUTCMD
	MOVLW	0x000		; disp.off, curs.off, no-blink
	CALL	LCDDMODE
	CALL	LCDCLEAR
	MOVLW	0x004		; disp.on, curs.off
	CALL	LCDDMODE
	MOVLW	0x002		; auto-inc (shift-cursor)
	CALL	LCDEMODE
	RETURN
;=============================================================================================
LCDBUSY	BSF	STATUS,RP0	; Select Register page 1
	MOVLW	H'FF'		; Set PORTA for input
	MOVWF	LCD_DATA_TRIS
	BCF	STATUS, RP0	; Select Register page 0
	BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
	BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
	BSF	LCD_CTRL, LCD_E	; LCD E-line High
	MOVF	LCD_DATA, W	; Read busy flag + DDram address
	BCF	LCD_CTRL, LCD_E	; LCD E-line Low
	BSF	LCD_CTRL, LCD_E	; LCD E-line High
	BCF	LCD_CTRL, LCD_E	; LCD E-line Low
	ANDLW	H'08'		; Check Busy flag, High = Busy
	BTFSS	STATUS, Z
	GOTO	LCDBUSY
LCDNOTBUSY	BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	H'00'
		MOVWF	LCD_DATA_TRIS	; Set PORTA for output
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================================
LCDCLEAR	MOVLW	0x001
		CALL	LCDPUTCMD
		RETURN
;=============================================================================================
; LCDEMODE
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0	: 0 = no display shift	1 = display shift
;  b1	: 0 = auto-decrement	1 = auto-increment
;  b2-7	: don't care
; OK
;--------------------------------------------------------------------------------------------
LCDEMODE	ANDLW	0x003		; Strip upper bits
		IORLW	0x004		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================================
; LCDDMODE
; Sets display control.
; Required display mode must be set in W
;  b0	: 0 = cursor blink off	1 = cursor blink on
;  b1	: 0 = cursor off	1 = cursor on
;  b2	: 0 = display off	1 = display on (display data remains in DDRAM)
;  b3-7	: don't care
; OK
;--------------------------------------------------------------------------------------------
LCDDMODE	ANDLW	0x007		; Strip upper bits
		IORLW	0x008		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================================
; LCDSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6	: required DDRAM address
;  b7	: don't care
; OK
;--------------------------------------------------------------------------------------------
LCDSDDA	IORLW	0x080		; Function set
	CALL	LCDPUTCMD
	RETURN
;=============================================================================================
LCDPUTCHAR	MOVWF	LCD_TEMP	; Character to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BSF	LCD_CTRL, LCD_RS; Set LCD in data mode
		SWAPF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================================
LCDPUTCMD	MOVWF	LCD_TEMP	; Character to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BCF	LCD_CTRL, LCD_RS; Set LCD in COMMAND mode
		SWAPF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================================
; Delay_time	= ((DELAY_value * 3) + 4) * Cycle_time
; DELAY_value	= (Delay_time - (4 * Cycle_time)) / (3 * Cycle_time)
;
; i.e. (@ 4MHz crystal)
; Delay_time	= ((32 * 3) + 4) * 1uSec
;		= 100uSec
; DELAY_value	= (500uSec - 4) / 3
;		= 165.33
;		= 165
;--------------------------------------------------------------------------------------------
DELAY500	MOVLW	D'165'		; +1		1 cycle
		MOVWF	DELAY		; +2		1 cycle
DELAY500_LOOP	DECFSZ	DELAY, F	; step 1	1 cycle
		GOTO	DELAY500_LOOP	; step 2	2 cycles
DELAY500_END	RETURN			; +3		2 cycles
;
;
X_DELAY500	MOVWF	X_DELAY		; +1		1 cycle
X_DELAY500_LOOP	CALL	DELAY500	; step1		wait 500uSec
		DECFSZ	X_DELAY, F	; step2		1 cycle
		GOTO	X_DELAY500_LOOP	; step3		2 cycles
X_DELAY500_END	RETURN			; +2		2 cycles
;=============================================================================================
;  Subroutines for SPI to secondary PIC
;=============================================================================================
;  "gobyte" subroutine sends the "tspeed" and "aspeed" to the 
;  secondary pic using "byebyte" SPI
;--------------------------------------------------------------------------------------------
gobyte	bcf	Pcs, PcsP	;  take cs low so secondary pic listens listens
	movf	tspeed, w	;  -->
	movwf	holder		;  -->
	call	byebyte		;  send target speed
	movf	aspeed, w	;  -->
	movwf	holder		;  -->
	call	byebyte		;  send actual speed
	bsf	Pcs, PcsP	;  take cs high so secondary pic stops listening
	return			;  --
;=============================================================================================
;  "byebyte" subroutine sends to the secondry pic the byte that is in "holder"
;--------------------------------------------------------------------------------------------
byebyte	movlw	H'08'		;  -->
	movwf	ewait		;  set up the eight cycle for sending 8 bits
yloop	btfsc	holder, 7	;  check the seventh bit of w
	bsf	Pdat, PdatP	;  if it is 1 put a one on the pin
	btfss	holder, 7	;  if it was 1, skip the command for if it was zero
	bcf	Pdat, PdatP	;  if it is 0 put a zero on the pin
	bsf	Pclk, PclkP	;  clock goes up
	movlw	H'ff'		;  -->
	movwf	dwait		;  -->
burn	decfsz	dwait, f	;  -->
	goto	burn		;  waste ff worth of time
	bcf	Pclk, PclkP	;  clock goes down and the secondary pic has taken the bit
	rlf	holder,1	;  line up the next bit
	decfsz	ewait, f	;  --
	goto	yloop		;  --
	return			;  --
;=============================================================================================
;  Subroutines for SPI to the BUS
;=============================================================================================
binit	movlw	H'0f'			;  -->
	movwf	databyte		;  -->
	movlw	H'0c'			;  -->
	movwf	addressbyte		;  -->
	call	wribyte			;  putting bus into pin interrupt mode
	movlw	H'f0'			;  -->
	movwf	databyte		;  -->
	movlw	H'60'			;  -->
	movwf	addressbyte		;  -->
	call	wribyte			;  setting bus filters such that all 
					;  bus messages show up in buffer 0
	movlw	H'ff'			;  -->
	movwf	databyte		;  -->
	movlw   H'2a'			;  -->
	movwf	addressbyte		;  -->
	call 	wribyte			;  make it the slowest bit rate on the bus

	movlw	H'ff'			;  -->
	movwf	databyte		;  -->
	movlw   H'29'			;  -->
	movwf	addressbyte		;  -->
	call 	wribyte			;  make it the slowest bit rate on the bus

	movlw	H'07'			;  -->
	movwf	databyte		;  -->
	movlw   H'28'			;  -->
	movwf	addressbyte		;  -->
	call 	wribyte			;  make it the slowest bit rate on the bus

	movlw	H'04'			;  -->
	movwf	databyte		;  -->
	movlw   H'0f'			;  -->
	movwf	addressbyte		;  -->
	call 	wribyte			;  set it into normal mode
	return
;=============================================================================================
;--------------------------------------------------------------------------------------------
;--- sendmsg subroutine
sendmsg movlw 	H'30'			;  address of first byte in CAN transmit buffer
	movwf   addressbyte		;  pass to addressbyte param of wribyte
	movlw   0			;  write a 0 data to this (priority 0, don't send yet)
	movwf	databyte		;  pass to databyte param of wribyte
	call	wribyte			;  write 0 to 30h in CAN controller
	movlw   H'31'			;  standard ID high byte register address
	movwf   addressbyte		;  send addy
	movlw	IDhigh			;  standard ID CAN high node address (be very sure to
	  				;  declare these differently for each node
	movwf   databyte		;  send the CAN node byte
	call 	wribyte 		;  SID low byte reg address
	incf	addressbyte		;
	movlw	IDlow			;  <see IDhigh remarks>
	movwf	databyte		;
	call 	wribyte			;  skip regs 32-33h (extended ID bytes)
					;-----------------------
					;  next we have to get our data length value from
					;  40h (passed into sendmsg there) and put it into
					;  the DLC register in the transmit buffer.
					;  then we have to read that number of data bytes
					;  from the param array (41-48h) and save them to
					;  the buffer (36-3dh)
	movlw	H'35'			;  addy of DLC register
 	movwf	addressbyte		;
	movf	40,w			;  get data length
	movwf	databyte		;
	call 	wribyte			;  store it
	incf	addressbyte		;
	movf	41,w			;
	movwf	databyte		;
	call    wribyte			;  store data byte 1
	incf    addressbyte		;
	movf	42,w			;
	movwf   databyte		;
        call    wribyte			;  store data byte 2
        incf	addressbyte		;
	movf	H'43',w			;
	movwf	databyte		;
	call 	wribyte			;  store data byte 3
	incf    addressbyte		;
	movf	H'44',w			;
	movwf   databyte		;
	call 	wribyte			;  store data byte 4
	incf	addressbyte		;
	movf	H'45',w			;
	movwf	databyte		;
	call 	wribyte			;  store data byte 5
	incf    addressbyte		;
	movf	H'46',w			;
	movwf	databyte		;
	call	wribyte			;  store data byte 6
	incf	addressbyte		;
	movf	H'47',w			;
	movwf	databyte		;
	call 	wribyte			;  store data byte 7
	incf	addressbyte		;
	movf	H'48',w			;
	movwf	databyte		;
	call    wribyte			;  store data byte 8

					;  now set transmit flag high
	movlw   H'30'			;  address of buffer byte 1
	movwf	addressbyte		;
	movlw	H'08'			;  turn bit 4 high to transmit
	movwf	databyte		;
	call 	wribyte			;  transmit entire packet.

	movlw	H'81'
	movwf	handler
	call	outbyte

	return				;  --
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;--- wribyte subroutine 
;--- manages the delivery of three bytes to the bus, one of which is data
;--- addressbyte holds the address to which you write
;--- databyte holds the data you send
;--- portb, 4 is /cs
;--- porta, 0 is clock pin
;--- porta, 1 is data pin
wribyte bcf	portb, 4		;  taking cs low so bus controller listens
	movlw	H'02'			;  -->
	movwf	handler
	call	outbyte			;  sends a byte that tells the bus we want to WRITE
	movf	addressbyte, w		;  !!you must name a register "addressbyte" and 
					;  have it holding the address you are writing to
	movwf	handler
	call	outbyte			;  sends byte determining the register we are 
					;  writing to on the bus
	movf	databyte, w		;  !!you must name a register "databyte" and 
					;  have it holding the data you wish to send
	movwf	handler
	call	outbyte			;  sends our data byte
	bsf	portb, 4		;  taking cs high so bus controller stops listening
	return				;  --
;--------------------------------------------------------------------------------------------
;--- outbyte subroutine 
;--- sends to the bus the byte that is in the handler
outbyte	movlw	H'08'			;  -->
	movwf	cbit			;  set up the eight cycle for sending 8 bits
bloop	btfsc	handler, 7		;  check the seventh bit of w
	bsf	porta, 1		;  if it is 1 put a one on the pin
	btfss	handler, 7		;  if it was 1, skip the command for if it was zero
	bcf	porta, 1		;  if it is 0 put a zero on the pin
	bsf	porta, 0		;  clock goes up
	bcf	porta, 0		;  clock goes down and the bus has taken the bit
	rlf	handler,1		;  line up the next bit
	decfsz	cbit, f			;  --
	goto	bloop			;  --
	return				;  --
;--------------------------------------------------------------------------------------------
;--- getmsg subroutine 
getmsg	movlw 	H'65'			;  address of first byte in CAN transmit buffer
	movwf   addressbyte		;  pass to addressbyte param of wribyte
	call	reabyte			;  write 0 to 30h in CAN controller
	movf    databyte, w
        andlw   H'0f'
	movwf	H'32'	

	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'33'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'34'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'35'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'36'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'37'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'38'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'39'
	incf	addressbyte
	call	reabyte
	movf    databyte, w
	movwf   H'3A'
	movlw	H'2c'		;  -->
	movwf	addressbyte	;  -->
	movlw	H'00'		;  -->
	movwf	databyte	;  -->
	call	wribyte		;  put the interrupt pin back
	return				;  --
;--------------------------------------------------------------------------------------------
;--- reabyte subroutine 
;--- manages the sending two bytes to the bus and retreval of one data byte
;--- addressbyte holds the address from which you read
;--- databyte holds the data you read
reabyte bcf	portb, 4		;  taking cs low so bus controller listens
	movlw	H'03'			;  -->
	movwf	handler			;  -->
	call	outbyte			;  sends a byte that tells the bus we want to READ
	movf	addressbyte, 0		;  !!you must name a register "addressbyte" and 
					;  have it holding the address you are reading from
	movwf	handler
	call	outbyte			;  sends byte determining the register we are 
					;  reading from on the bus
	call	inbyte			;  gets our data byte
	bsf	portb, 4		;  taking cs high so bus controller stops listening
	return				;  --
;--------------------------------------------------------------------------------------------
;--- inbyte subroutine gets a byte from the bus
inbyte	BSF	STATUS,RP0	; Select Register page 1
	MOVLW	H'04'		; Set PORTA for out put but pin 2 as input
	MOVWF	trisa
	BCF	STATUS, RP0	; Select Register page 0
	movlw	H'08'		;  -->
	movwf	cbit		;  set up the eight cycle for sending 8 bits
cloop	bsf	porta, 0	;  clock goes up
	btfss	porta, 2	;  check data
	bcf     databyte,0 	;  if data was a 0 record a 0
	btfsc	porta, 2	;  if data was a 0 don't do command for if data was a 1
	bsf     databyte,0	;  if data was a 1 record a 1
	bcf	porta, 0	;  clock goes down and we have taken the bit
	rlf	databyte,1	;  line up the next bit
	decfsz	cbit, f		;  --
	goto	cloop		;  --
	rrf	databyte,1	;  line up the next bit
	BSF	STATUS,RP0	; Select Register page 1
	MOVLW	H'00'		; Set PORTA for output
	MOVWF	trisa
	BCF	STATUS, RP0	; Select Register page 0
	return			;  --
;=============================================================================================
	END			; End of program

slave

;controls two dual 7 segment diplays.
;Recieves values via SPI from another PIC


;  porta, 0 is data (input) for spi with primary pic
;  porta, 1 is clock (input) for spi with primary pic
;  porta, 2 is cs (input) for spi with primary pic
;  portb, 0-3 are 4 bits to be sent to 7447
;  portb, 7 is to activate the mosfet that controls the upper digit of target speed
;  portb, 6 is to activate the mosfet that controls the lower digit of target speed
;  portb, 5 is to activate the mosfet that controls the upper digit of actual speed
;  portb, 4 is to activate the mosfet that controls the lower digit of actual speed
;--------------------------------------------------------------------------------------------
LIST P=16F84
errorlevel 0,-305
INCLUDE "P16F84.inc"
__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
;--------------------------------------------------------------------------------------------
aspeed		equ	0c	;  actual speed
tspeed		equ	0d	;  target speed
hspace		equ	0e	;  space for holding misc information
cbit		equ	0f	;  counter for inbyte subroutine
databyte	equ	10	;  data recieved from primary pic via inbyte 
				;  subroutine is dumped into this file register
espace		equ	11	;  hex to dec
gspace		equ	12	;  hex to dec
;--------------------------------------------------------------------------------------------
setup	org	0
	clrf	portb		;  clears all the bits in portb
	clrf	porta		;  clears all the bits in porta
	clrf	status		;  clears all the bits in status
	bsf	status, rp0	;  switches to bank 1
	movlw	0		;  -->
	movwf	trisb		;  sets portb as output
	movlw	H'ff'		;  -->
	movwf	trisa		;  sets porta as input
	bcf	status, rp0	;  switches back to bank 0
;--------------------------------------------------------------------------------------------
init	movlw	H'88'		;  -->
	movwf	tspeed		;  set target speed
	movlw	H'88'		;  -->
	movwf	aspeed		;  set actual speed
loop	movf    tspeed, w	;  -->
	andlw   H'f0'		;  isolate the upper 4 digits of target speed and 
				;  store in hspace
	movwf	hspace		;  -->
	swapf	hspace, w	;  store the upper 4 digits of target speed 
				;  (obtained from hspace) as the lower 4 digits of w
	iorlw	H'80'		;  set w to 1000xxxx (activates mosfet attached to pin 10)
	movwf	portb		;  send the message
	btfss	porta, 2	;  check cs on primary pic to see if there is a message for us
	call	getmsg		;  if cs is low, recieve message from primary pic
	movf    tspeed, w	;  -->
	andlw   H'0f'		;  isolate the lower 4 digits of target speed and store in w
	iorlw	H'40'		;  set w to 0100xxxx (activates mosfet attached to pin 11)
	movwf	portb		;  send the message
	btfss	porta, 2	;  check cs on primary pic to see if there is a message for us
	call	getmsg		;  if cs is low, recieve message from primary pic
	movf    aspeed, w	;  -->
	andlw   H'f0'		;  isolate the upper 4 digits of actual speed 
				;  and store in hspace
	movwf	hspace		;  -->
	swapf	hspace, w	;  store the upper 4 digits of actual speed 
				;  (obtained from hspace) as the lower 4 digits of w
	iorlw	H'20'		;  set w to 0010xxxx (activates mosfet attached to pin 12)
	movwf	portb		;  send the message
	btfss	porta, 2	;  check cs on primary pic to see if there is a message for us
	call	getmsg		;  if cs is low, recieve message from primary pic
	movf    aspeed, w	;  -->
	andlw   H'0f'		;  isolate the lower 4 digits of actual speed and store in w
	iorlw	H'10'		;  set w to 0001xxxx (activates mosfet attached to pin 13)
	movwf	portb		;  send the message
	btfss	porta, 2	;  check cs on primary pic to see if there is a message for us
	call	getmsg		;  if cs is low, recieve message from primary pic
goto 	loop			;  back to the begining
;=============================================================================================
datarol	movlw	H'64'
	subwf	databyte, w
	btfss	status, c
	goto	proc1
	movlw	H'64'
	subwf	databyte, f
proc1	movf	databyte, w
	movwf	espace
	movlw	H'00'
	movwf	gspace
	movlw	H'0a'
cutit	subwf	espace, f
	btfss	status, c
	goto	printit
	incf	gspace, f
	goto	cutit
printit	btfsc	gspace, 0
	goto	notz
	btfsc	gspace, 1
	goto	notz
	btfsc	gspace, 2
	goto	notz
	btfss	gspace, 3
	return
notz	movf	gspace, w
	movwf	espace
	movlw	H'0a'
down	subwf	databyte, f
	decfsz	espace, f
	goto	down
	swapf	gspace, w
	iorwf	databyte, f
	return
;=============================================================================================
;  if you can't figure this out you deserve a kick
;--------------------------------------------------------------------------------------------
getmsg	call	inbyte
	call	datarol
	movf	databyte, w
	movwf	tspeed
	call	inbyte
	call	datarol
	movf	databyte, w
	movwf	aspeed
	return
;=============================================================================================
;  "inbyte" subroutine gets a byte from the primary pic
;  porta, 0 is data (input)
;  porta, 1 is clock (input)
;  porta, 2 is cs (input)
;--------------------------------------------------------------------------------------------
inbyte	movlw	H'08'		;  -->
	movwf	cbit		;  set up the eight cycles for sending 8 bits
cloop	btfss	porta, 1	;  -->
	goto	cloop		;  when clock goes up it will stop waiting
	btfss	porta, 0	;  check data
	bcf     databyte, 0 	;  if data was a 0 record a 0
	btfsc	porta, 0	;  if data was a 0 don't do command for if data was a 1
	bsf     databyte, 0	;  if data was a 1 record a 1
	rlf	databyte, f	;  line up the next bit
aloop	btfsc	porta, 1	;  -->
	goto	aloop		;  when clock goes down it will stop waiting
	decfsz	cbit, f		;  --
	goto	cloop		;  --
	rrf	databyte, f	;  line up the next bit
	return			;  --
;=============================================================================================
	end			;  --
{ "loggedin": false, "owner": false, "avatar": "", "render": "nothing", "trackingID": "UA-36983794-1", "description": "", "page": { "blogIds": [ 231 ] }, "domain": "holtstrom.com", "base": "\/michael", "url": "https:\/\/holtstrom.com\/michael\/", "frameworkFiles": "https:\/\/holtstrom.com\/michael\/_framework\/_files.4\/", "commonFiles": "https:\/\/holtstrom.com\/michael\/_common\/_files.3\/", "mediaFiles": "https:\/\/holtstrom.com\/michael\/media\/_files.3\/", "tmdbUrl": "http:\/\/www.themoviedb.org\/", "tmdbPoster": "http:\/\/image.tmdb.org\/t\/p\/w342" }