; pinball_controller.asm ; By Joel Jordan, SigArch @ ACM, UIUC ; http://www.acm.uiuc.edu/sigarch/ ; ; Master controller for pinball machine. Uses serial port to talk ; to Linux PC, uses I2C to talk to other PIC boards in the pinball ; system. Controls flippers via relays. ; Serial port paramters for 25MHz Oscillator: ; 19200 baud, no parity, no flow-control, 1 stop bit ; ; 4/20/04 ; -- added ball shooter option ; 3/30/04 ; -- fixed so that flipper state is initialized on boot list p=18F452 #include __CONFIG _CONFIG2H, _WDT_OFF_2H ;************************************************************ ; constants #define I2CBaudSel 0x3F ; baud rate = some random number #define TestAddr 0x02 ; test slave address #define LEFT_FLIPPER PORTB, 0 #define RIGHT_FLIPPER PORTB, 1 #define CREDIT_BUTTON PORTB, 4 #define START_BUTTON PORTB, 5 #define RXDoneFlag SerialFlags, 0 ; 1 if not transferring #define TXDoneFlag SerialFlags, 1 ; 1 if not transferring #define ShootBallFlag SerialFlags, 2 ; 1 if shoot command given StringTable equ 0x0800 CardReaderAddr equ 0x4 ScoreReaderAddr equ 0x2 ShooterAddr equ 0x6 ;************************************************************ ; variables cblock 0x000 I2CAddr ; address associated with last I2C event I2CBuf:0x40 ; data buffer for I2C I2CBufLen ; length of I2C data buffer contents I2CCount ; generic counter DelayCount ; delay counter SerialRecvBufLen ; length of serial buffer contents SerialTransBufLen ; length of serial xmit buffer contents SerialFlags Temp ; scratch var WSave ; interrupt context save vars StatusSave BSRSave RCByte ; used in interrupt handler to free up RCREG LeftFlipperCount RightFlipperCount ButtonCounter1 ButtonCounter2 endc cblock 0x100 SerialTransBuf:0x100 ; data buffer for serial responses endc cblock 0x200 SerialRecvBuf:0x100 ; data buffer for serial commands endc ;************************************************************ ;program code starts here org 0x200 ; Beginning of program EPROM Start goto Main org 0x208 ; Interrupt Vector Interrupt1 goto TMRInterrupt org 0x218 ; low priority interrupt vector Interrupt2 ; handle serial port interrupts MOVWF WSave MOVFF STATUS, StatusSave MOVFF BSR, BSRSave btfsc PIR1, TXIF ; xmit buffer interrupt? goto XmitInterrupt btfsc PIR1, RCIF ; recv interrupt? goto RecvInterrupt Interrupt2Done MOVFF BSRSave, BSR MOVF WSave, W MOVFF StatusSave, STATUS RETFIE RecvInterrupt movff RCREG, RCByte bcf PIR1, RCIF ; check for framing and overrun errors movlw b'00000110' andwf RCSTA, w bnz RecvError movlw "F" ; F - set to firmware reprogram mode xorwf RCByte, w bz ReprogramFirmwareMode movlw "Q" ; Q - reset the PIC xorwf RCByte, w bz ResetPIC movlw "L" ; L - flip left flipper long xorwf RCByte, w bz FlipLeftLong movlw "R" ; R - flip right flipper long xorwf RCByte, w bz FlipRightLong movlw "l" ; l - flip left flipper short xorwf RCByte, w bz FlipLeftShort movlw "r" ; r - flip right flipper short xorwf RCByte, w bz FlipRightShort movlw "S" ; S - shoot ball xorwf RCByte, w bz ShootBall movlw "C" ; C - add credit xorwf RCByte, w bz AddCredit movlw "G" ; G - start game xorwf RCByte, w bz StartGame ; all other commands ignored goto Interrupt2Done RecvError bcf RCSTA, CREN bsf RCSTA, CREN goto Interrupt2Done FlipLeftLong movlw 0x80 movwf LeftFlipperCount bsf LEFT_FLIPPER goto Interrupt2Done FlipRightLong movlw 0x80 movwf RightFlipperCount bsf RIGHT_FLIPPER goto Interrupt2Done FlipLeftShort movlw 0x10 movwf LeftFlipperCount bsf LEFT_FLIPPER goto Interrupt2Done FlipRightShort movlw 0x10 movwf RightFlipperCount bsf RIGHT_FLIPPER goto Interrupt2Done ShootBall bsf ShootBallFlag goto Interrupt2Done StartGame bsf START_BUTTON call ButtonDelay bcf START_BUTTON goto Interrupt2Done AddCredit bsf CREDIT_BUTTON call ButtonDelay bcf CREDIT_BUTTON goto Interrupt2Done ReprogramFirmwareMode ; Sets the bootloader enable condition and resets the PIC setf EEDATA ; write FF setf EEADR ; to location FF clrf EECON1 bsf EECON1, WREN ; enable write mode bcf INTCON, GIEH ; disable interrupts movlw 0x55 ; required write sequence movwf EECON2 ; to prevent accidental writes movlw 0xAA ; see datasheet for details movwf EECON2 bsf EECON1, WR ; set write bit bsf INTCON, GIEH ; enable interrupts btfsc EECON1, WR ; wait for write to complete bra $ - 2 bcf EECON1, WREN ; disable write mode ResetPIC reset XmitInterrupt ; Note: While transmits are in progress, FSR2 is reserved for use by ; the serial port code BTFSC TXDoneFlag GOTO XmitDisable MOVFF POSTINC2, TXREG DCFSNZ SerialTransBufLen, F BSF TXDoneFlag GOTO Interrupt2Done XmitDisable BCF TXSTA, TXEN ; disable xmitter GOTO Interrupt2Done ; -------------------- ; Wait for many cycles to be sure processor sees ; button presses ; -------------------- ButtonDelay clrf ButtonCounter1 clrf ButtonCounter2 ButtonDelay1 decfsz ButtonCounter1, F goto ButtonDelay1 decfsz ButtonCounter2, F goto ButtonDelay1 return ; ------------------------------------------------------ ; High-priority Timer Interrupt ; counts down flipper counters, unflips them when ; the timer expires ; ------------------------------------------------------ TMRInterrupt btfss PIR1, TMR1IF ; Timer 1 overflowed? goto Interrupt1Done ; if not, exit interrupt movf LeftFlipperCount, f bz CheckRightFlipperCount dcfsnz LeftFlipperCount, f bcf LEFT_FLIPPER CheckRightFlipperCount movf RightFlipperCount, f bz Interrupt1Done dcfsnz RightFlipperCount, f bcf RIGHT_FLIPPER Interrupt1Done bcf PIR1, TMR1IF ; clear tmr1 interrupt flag retfie FAST ; ------------------------------------------------------ ; Main Program Routine ; ------------------------------------------------------ Main BSF RCON, IPEN ; enable interrupt priorities MOVLW b'11000111' ; enable TMR0 MOVWF T0CON ; with 1:256 prescale CLRF LeftFlipperCount CLRF RightFlipperCount CLRF TRISB BCF INTCON2,RBPU BCF LEFT_FLIPPER ; reset initial flipper state BCF RIGHT_FLIPPER CALL TMR1_Setup CALL UART_Setup CALL I2C_master_setup BSF INTCON, GIEH ; enable high priority interrupts BSF INTCON, GIEL ; enable serial interrupts CALL SendStartupMessage BCF INTCON, TMR0IF Loop CLRF TMR0L ; clear timer0 CALL PollCardReader CALL PollScoreReader btfsc ShootBallFlag call SendShootCommand TimeoutLoop BTFSS INTCON, TMR0IF GOTO TimeoutLoop BCF INTCON, TMR0IF GOTO Loop ; ------------------------------------------------------ ; Setup Timer1 for the flippers ; ------------------------------------------------------ TMR1_Setup movlw b'00000001' ; TMR1 on, prescalar 1:1 movwf T1CON bsf IPR1, TMR1IP ; high-priority interrupt bsf PIE1, TMR1IE return ; ------------------------------------------------------ ; Tell the ball launcher to shoot ball ; ------------------------------------------------------ SendShootCommand: movlw ShooterAddr movwf I2CAddr movlw 0x1 movwf I2CBufLen movlw 'S' movwf I2CBuf call I2C_master_send bcf ShootBallFlag return ; ------------------------------------------------------ ; Check for new data from card reader ; ------------------------------------------------------ PollCardReader movlw CardReaderAddr movwf I2CAddr call I2C_master_recv ; card data starts with a 'C' movf I2CBufLen, f bz CardReaderNoData movlw "C" xorwf I2CBuf, w bz ForwardCardData EndCardReaderPolling return CardReaderNoData ; call SendNoDataMessage return ; ------------------------------------------------------ ; send I-Card number to PC via serial port ; ------------------------------------------------------ ForwardCardData ; wait for pending transfers to finish btfss TXDoneFlag goto ForwardCardData ; copy i-card into serial buffer movff I2CBufLen, Temp lfsr FSR0, I2CBuf lfsr FSR1, SerialTransBuf CopyCardDataLoop movff POSTINC0, POSTINC1 decfsz Temp goto CopyCardDataLoop movff I2CBufLen, SerialTransBufLen call UART_SendBuffer return ; Poll score reader circuit PollScoreReader movlw ScoreReaderAddr movwf I2CAddr call I2C_master_recv ; score data starts with a 'S' movf I2CBufLen, f bz EndScoreReaderPolling movlw "S" xorwf I2CBuf, w bz ForwardScoreData EndScoreReaderPolling return ; ------------------------------------------------------ ; send score data to PC via serial port ; ------------------------------------------------------ ForwardScoreData ; wait for pending transfers to finish btfss TXDoneFlag goto ForwardScoreData ; copy i-card into serial buffer movff I2CBufLen, Temp lfsr FSR0, I2CBuf lfsr FSR1, SerialTransBuf CopyScoreDataLoop movff POSTINC0, POSTINC1 decfsz Temp goto CopyScoreDataLoop movff I2CBufLen, SerialTransBufLen call UART_SendBuffer return ; ------------------------------------------------------ ; set up serial port, 115200 baud ; Assumes 25MHz oscillator ; ------------------------------------------------------ UART_Setup BCF IPR1, RCIP ; set low interrupt priority BCF IPR1, TXIP ; set low interrupt priority BCF TRISC, 6 ; TX output BSF TRISC, 7 ; TX input MOVLW d'80' MOVWF SPBRG ; 19200 bps MOVLW b'00000100' ; 8-bit, async high speed, initially disabled MOVWF TXSTA MOVLW b'10010000' ; enabled, 8-bit, receiver enabled MOVWF RCSTA BSF PIE1, RCIE ; turn on receive interrupt BSF PIE1, TXIE ; enable serial xmit interrupt BSF TXDoneFlag ; set up xmitter free flag RETURN ; ------------------------------------------------------ ; UART send routine ; ------------------------------------------------------ UART_SendBuffer BCF TXDoneFlag LFSR FSR2, SerialTransBuf BSF TXSTA, TXEN ; enable xmitter RETURN ; ------------------------------------------------------ ; Send data over I2C bus in master mode ; I2CAddr - address to send to; 0x00 = broadcast ; I2CBuf - data to send ; I2CBufLen - number of data bytes to send ; ------------------------------------------------------ I2C_master_send movff I2CBufLen, I2CCount ; load counter with buf length lfsr FSR0, I2CBuf ; get pointer to I2CBuf send_start bsf SSPCON2, SEN, 0 ; create START condition btfsc SSPCON2, SEN, 0 ; wait until START completes goto $-2 movf I2CAddr, w ; clear bit 0 (R/W#) andwf 0xFE movwf SSPBUF, 0 ; send out slave address btfsc SSPSTAT, R_W, 0 ; wait until addr sent goto $-2 btfsc SSPCON2, ACKSTAT, 0 ; ack received? (0 = yes) goto stop_send send_byte movff POSTINC0, SSPBUF ; send byte btfsc SSPSTAT, R_W, 0 ; wait until byte sent goto $-2 btfsc SSPCON2, ACKSTAT, 0 ; ack received? (0 = yes) goto stop_send decfsz I2CBufLen ; continue if bytes remain goto send_byte stop_send bsf SSPCON2, PEN, 0 ; create STOP condition btfsc SSPCON2, PEN, 0 ; wait for STOP to complete goto $-2 return ; ------------------------------------------------------ ; Receive from an I2C slave ; I2CAddr - slave to receive from ; I2CBuf - data from slave ; I2CBufLen - length of data from slave ; ------------------------------------------------------ I2C_master_recv andlw 0x00 movwf I2CBufLen ; zero buffer length lfsr 0, I2CBuf ; get pointer to I2CBuf recv_start bsf SSPCON2, SEN, 0 ; create START condition btfsc SSPCON2, SEN, 0 ; wait until START completes goto $-2 movf I2CAddr, w ; set bit 0 (R/W#) iorlw 0x01 movwf SSPBUF, 0 ; send out slave address btfsc SSPSTAT, R_W, 0 ; wait until addr sent goto $-2 btfsc SSPCON2, ACKSTAT, 0 ; ack received? (0 = yes) goto stop_recv recv_length ; first receive length byte bsf SSPCON2, RCEN, 0 ; enable receive btfsc SSPCON2, RCEN, 0 ; wait until receive done goto $-2 movf SSPBUF, w, 0 ; get length byte btfsc SSPSTAT, R_W, 0 ; wait until length received goto $-2 andlw 0x3F ; cap length at 63 bytes movwf I2CBufLen movwf I2CCount movlw 0x00 ; send STOP if 0 bytes indicated cpfsgt I2CBufLen ; (0 bytes = not ready) goto recv_done bcf SSPCON2, ACKDT, 0 ; send ACK bsf SSPCON2, ACKEN, 0 ; enable acknowledge btfsc SSPCON2, ACKEN, 0 goto $-2 recv_byte bsf SSPCON2, RCEN, 0 ; enable receive btfsc SSPCON2, RCEN, 0 ; wait until receive done goto $-2 movff SSPBUF, POSTINC0 btfsc SSPSTAT, R_W, 0 ; wait until data received goto $-2 dcfsnz I2CCount goto recv_done bcf SSPCON2, ACKDT, 0 ; send ACK bsf SSPCON2, ACKEN, 0 ; enable acknowledge btfsc SSPCON2, ACKEN, 0 goto $-2 goto recv_byte recv_done bsf SSPCON2, ACKDT, 0 ; send NACK bsf SSPCON2, ACKEN, 0 ; enable acknowledge btfsc SSPCON2, ACKEN, 0 goto $-2 stop_recv bsf SSPCON2, PEN, 0 ; create STOP condition btfsc SSPCON2, PEN, 0 ; wait for STOP to complete goto $-2 return ; ------------------------------------------------------ ; Set up I2C master mode ; I2CBaudSel - baud rate divisor ; ------------------------------------------------------ I2C_master_setup movlw I2CBaudSel ; select baud rate movwf SSPADD, 0 ; set I2C baud rate andlw 0x00 ; enable slew rate control movwf SSPSTAT, 0 movlw b'00011000' ; set SCL and SDA to be inputs iorwf TRISC, f, 0 movlw b'00111000' ; set I2C master mode, enable serial movwf SSPCON1, 0 return ; ------------------------------------------------------ ; sends startup message to PC ; ------------------------------------------------------ SendStartupMessage bcf TXDoneFlag movlw StartupStringEnd - StartupString movwf Temp movwf SerialTransBufLen lfsr FSR0, SerialTransBuf movlw (StartupString>>8)&0xFF ; load base offset of StringTable movwf TBLPTRH ; high order movlw StartupString&0xFF ; low byte movwf TBLPTRL StartupMsgFillBuffer tblrd*+ ; load string byte movff TABLAT, POSTINC0 ; store it in xmit buffer decfsz Temp, F goto StartupMsgFillBuffer call UART_SendBuffer return SendNoDataMessage bcf TXDoneFlag movlw NoDataEnd - NoData movwf Temp movwf SerialTransBufLen lfsr FSR0, SerialTransBuf movlw (NoData>>8)&0xFF ; load base offset of StringTable movwf TBLPTRH ; high order movlw NoData&0xFF ; low byte movwf TBLPTRL goto StartupMsgFillBuffer org StringTable StartupString db 0xD, 0xA db "SigArch Pinball Controller", 0xD, 0xA db "Engineering Open House 2004", 0xD, 0xA ; db "By Joel Jordan, Jered Wierzbicki,", 0xD, 0xA ; db "Chris Hansen, Jacky Leung, Jason Miller", 0xD, 0xA ; db 0xD, 0xA db "Commands:", 0xD, 0xA db "Q - Reset", 0xD, 0xA db "F - Reset to Firmware", 0xD, 0xA db "r - Right Flipper (Short)", 0xD, 0xA db "R - Right Flipper (Long)", 0xD, 0xA db "l - Left Flipper (Short)", 0xD, 0xA db "L - Left Flipper (Long)", 0xD, 0xA db "S - Shoot Ball", 0xD, 0xA StartupStringEnd db 0x00 if StartupStringEnd-StartupString > 0x100 error "Startup string too large" endif org StringTable + 0x100 NoData db "No Card Data", 0xD, 0xA NoDataEnd END ; directive indicates end of code