An external input for a factory stereo

Note: I’d been meaning to post about this for a while, and since the project made the MAKE blog, I figure it is high time…

After having two aftermarket stereo systems stolen from my car in less than a year’s time, I decided to give up and go back to the factory radio. The problem? It only has tape and radio inputs, and I prefer to have access to my entire MP3 collection. The traditional way to fix this is to either get an FM broadcaster or a tape adapter, but where is the fun in that? Instead, I elected to wire in an external input, and built a little circuit to let me switch between the two. Here is a photo set to document the process:


(click on the picture to view the album)

Source code after the break.

This code is written for a PIC 12F629 as the controller, largely because I had a bunch of them sitting around. If you are starting from scratch, I would suggest skipping this bit and spending the whole $4 on a part that has an open source C compiler (*cough* AVR *cough*).

; Input multiplexer for car stereo system
;
; By Matt Mets, completed in 2008
;
; This code is released into the public domain.  Attribution is appreciated.

; Build configuration
PROCESSOR 12F629

include p12f629.inc

; Global Register Definitions
STATE        equ    0x20                ; System state:
                                        ; 0=radio/tape, 1=external input
                                        ; (other bits are don't care)

SLEEP_CNT    equ    0x30                ; For use in sleep routine
SLEEP_CNT_L  equ    0x31
BTN_CNT      equ    0x32                ; Countdown for button press
BTN_STATE    equ    0x33                ; Read the GPIO 
BTN_DOWN     equ    0x34                ; Last known button state

; EEPROM Definitions
STATE_ADDR equ      0x00                ; Last known system status

; Set up the I/O ports to be in the right state
            bsf     STATUS,RP0          ; Bank 1
            movlw   0x39                ; Set GP<2:1> as output
            movwf   TRISIO              ; and set GP<5:3,0> as input
            bcf     STATUS,RP0          ; Bank 0

; Load the last-known state from the EEPROM
            call    LOAD_ST

; Main loop
START       call    CHECK_BTN           ; If button pressed, do some debouncing
            btfss   BTN_DOWN,0
            goto    START               ; Otherwise, repeat

            movlw   0xff                ; Must have x counts of button down
            movfw   BTN_CNT             ; before toggling
BTN_LOOP    call    DO_SLEEP_L          ; sleep for a bit
            call    CHECK_BTN           ; If button released, return to loop
            btfss   BTN_DOWN,0
            goto    START
            movlw   0x01                ; Subtract 1 from the count
            subwf   BTN_CNT,1
            btfss   STATUS,Z            ; If non-zero, loop again
            goto    BTN_LOOP
            call    TOGGLE_ST           ; Otherwise, toggle
BTN_LOOPE   call    DO_SLEEP            ; Don't return until the button is
            call    CHECK_BTN
            btfsc   BTN_DOWN,0
            goto    BTN_LOOPE
            goto    START

; See if we can tell when the button has been pressed.  Because it is being
; driven by a matrix controller, it is 'down' when both sides are at the same
; potential.
CHECK_BTN   clrf    BTN_DOWN            ; Clear the button down register
            movf    GPIO,0              ; Capture the input state
            movwf   BTN_STATE
            btfss   BTN_STATE,3         ; Determine state of buttons

            goto    CHECK_LOW
CHECK_HIGH  btfss   BTN_STATE,4         ; Check if both buttons are high
            return
            goto    SET_BTN
CHECK_LOW   btfsc   BTN_STATE,4         ; Check if both buttons are low
            return
SET_BTN     comf    BTN_DOWN,1          ; Set the button down register
            return

; Toggle the system state (then apply it)
TOGGLE_ST   movlw   0x01                ; Flip the state variable
            xorwf   STATE,1
            call WRITE_ST               ; Then store it in EEPROM

; Apply the system state to the hardware
APPLY_ST    btfss   STATE,0             ; If the low bit is 0, set radio/tape
            goto    APPLY_ST1
APPLY_ST0   movlw   0x02                ; Select radio/tape input
            movwf   GPIO
            return
APPLY_ST1   movlw   0x04                ; Select aux input
            movwf   GPIO
            return

; Read the last value of the State register from the EEPROM
LOAD_ST     bsf     STATUS,RP0          ; Bank 1
            movlw   STATE_ADDR          ; Program the address to read
            movwf   EEADR
            bsf     EECON1,RD           ; Read the data from EEPROM
            movf    EEDATA,0            ; and load it to the state regiser
            movwf   STATE
            bcf     STATUS,RP0          ; Bank 0
            call    APPLY_ST            ; And apply the state
            return

; Write the current value of the State register to the EEPROM
WRITE_ST    bsf     STATUS,RP0          ; Bank 1
            movf    STATE,0             ; Load the state register value
            movwf   EEDATA
            movlw   STATE_ADDR          ; And its address
            movwf   EEADR
WRITE_SEQ   bsf     EECON1,WREN         ; Enable write
            movlw   0x55                ; Special dance to unlock writes
            movwf   EECON2              ; (this feature prevents runaway code
            movlw   0xAA                ;  from trashing the EEPROM)
            movwf   EECON2
            bsf     EECON1,WR           ; Start the write
            bcf     EECON1,WREN         ; Disable any future writes
            bcf     STATUS,RP0          ; Bank 0
            return

; Sleep for some time
DO_SLEEP    movlw   0xff                ; Count down from 255
            movwf   SLEEP_CNT
DO_SLEEPI   decfsz  SLEEP_CNT,1         ; Decrement loop
            goto    DO_SLEEPI
            return

; Sleep for even longer
DO_SLEEP_L  movlw   0x6                ; Count down from 255
            movwf   SLEEP_CNT_L
DO_SLEEP_LI call    DO_SLEEP
            decfsz  SLEEP_CNT_L,1       ; Decrement loop
            goto    DO_SLEEP_LI
            return
END
This entry was posted in Journal. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>