📄 rds decoder.asm
字号:
; ----------------------------------------------------
;
; RDS decoder -- pic code
; by AHDL
;
; Config : PIC 16F84 / 4.332 / PUT enabled / XT
;
; TDA7330 demodulator
;
; (c) 2000 Andy Dewilde (xxx@xxx.xx)
; and Lieven Hollevoet (picmicro@hollie.tk)
;
;
; Re-use and modification of this code is hereby
; permitted, as long as the names and e-mail
; addresses of both authors are mentioned in the new
; version.
; ----------------------------------------------------
#include "P16F84.INC"
LIST p=PIC16F84
__CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC
; -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
; definitions
; -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
; *** PORTS ***
#DEFINE rdsClock 0x00 ; RB0 = rds clock, input
#DEFINE rdsData 0x01 ; RB1 = rds data, input
#DEFINE TxD 0x02 ; RB2 = transmit line
; *** MACROS ***
#DEFINE bank0 bcf STATUS, RP0
#DEFINE bank1 bsf STATUS, RP0
; *** CONSTANTS ***
#DEFINE dump 0x04
; *** REGISTERS ***
; used by rds routines
Reg1A EQU 0x10
Reg1B EQU 0x11
Reg2A EQU 0x12
Reg2B EQU 0x13
Reg3A EQU 0x14
Reg3B EQU 0x15
Reg4A EQU 0x16
Reg4B EQU 0x17
StoReg1 EQU 0x18
StoReg2 EQU 0x19
StoReg3 EQU 0x1A
StoReg4 EQU 0x1B
StoReg1X EQU 0x1C
StoReg2X EQU 0x1D
StoReg3X EQU 0x1E
StoReg4X EQU 0x1F
HiReg EQU 0x20
LoReg EQU 0x21
Loper EQU 0x23
CountReg EQU 0x24
DelayReg EQU 0x25
Counter EQU 0x2E
; used by serial routines
Locatie EQU 0x26
LoopCount EQU 0x27
TmrVal EQU 0x28
TxChar EQU 0x29
; used by the isr
W_Temp EQU 0x2A
Status_Temp EQU 0x2B
; used to store the PS information
; WARNING: DO NOT change these values,
; these locations are used in calcs!
Char0 EQU 0x30
Char1 EQU 0x31
Char2 EQU 0x32
Char3 EQU 0x33
Char4 EQU 0x34
Char5 EQU 0x35
Char6 EQU 0x36
Char7 EQU 0x37
Flags EQU 0x38 ; For PTY, TA (traffic announcement), TP (traffic programma)
; -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
; This is the begin of the main loop code
; -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
ORG 0x00 ; reset vector
goto _main
ORG 0X04 ; interrupt vector
goto _isr
; ---------------- _main ----------------------------------------
;
; Initialisation of the I/O ports and several variables that are
; used throughout the program, also clears ram locations
;
; ---------------------------------------------------------------
_main
bcf STATUS, RP0 ; Set up I/O ports A & B
clrf PORTA
bsf STATUS, RP0
movlw 0x00
movwf TRISA ; A = unused (set all as output)
bcf STATUS, RP0 ; PORTB is used as:
clrf PORTB ; RB0 = rds clock, input
bsf STATUS, RP0 ; RB1 = rds data, input
movlw 0x03 ; RB2 = serial data, output
movwf TRISB ;
movlw dump ; Determines after how many blocks a dump will be triggered
movwf LoopCount
clrwdt ; Change prescaler assignment (in case we use TMR0)
bsf STATUS, RP0 ; Enable pull-up
movlw b'01010000' ; Enable TMR0
movwf OPTION_REG
bcf STATUS, RP0
bsf PORTB, TxD ; pull TxD line high
movlw 0x30 ; Clear RAM locations for PS info
movwf FSR ; initialize pointer
_nloc clrf INDF ; clear INDF register
incf FSR, F ; increase pointer
btfss FSR, 3 ; stop on 0x38
goto _nloc
call _send
; --------------------------------------------------------------------
; *************** Start the detection of RDS blocks ******************
; --------------------------------------------------------------------
; ----- _block1 -----------------------------------------------------
;
; FUNCTION: searches for an A-datablock in the incoming datastream
; using the 'bit-slip'(tm) method. It shift all the previous
; bits and inserts the newly read bit as LSB
; Then it calculates the syndrome and checks for block A
; This subroutine actually synchronizes the algorithm
; with the incoming bitstream
;
; USES: _getBit
;
; FALLS INTO: _block2
;
; --------------------------------------------------------------------
_block1 ; Search for syndrome A (using the bit-slip(tm) method)
call _getBit ; wait for new databit, get syndrome
movlw 0xF6 ; syndrome A?
xorwf HiReg, W ; check 8 MSB's
btfss STATUS, Z
goto _block1 ; again
clrw
xorwf LoReg, W ; check the LSB's
btfss STATUS, Z
goto _block1 ; again
movf StoReg1, W ; store *** block 1 data ***
movwf Reg1A
movf StoReg2, W
movwf Reg1B
; ----- _block2 -----------------------------------------------------
;
; FUNCTION: reads the next 26 incoming bits and checks the syndrome
;
; USES: _get26Bits, interrupt service routine, _syndrome, _block1
;
; FALLS INTO: _block3
;
; -------------------------------------------------------------------
_block2
call _get26Bits ; read the next 26 bits
movlw 0xF5 ; syndrome B?
xorwf HiReg, W
btfss STATUS, Z
goto _block1 ; again
clrw
xorwf LoReg, W
btfss STATUS, Z
goto _block1 ; again
movf StoReg1, W ; store *block 2 data*
movwf Reg2A
movf StoReg2, W
movwf Reg2B
; ----- _block3 -----------------------------------------------------
;
; FUNCTION: reads the next 26 incoming bits and checks the syndrome
; this routine handles blocks of type C and C'
;
; USES: _get26Bits, interrupt service routine, _syndrome, _block1
;
; FALLS INTO: _block4
;
; -------------------------------------------------------------------
_block3
call _get26Bits ; read the next 26 bits
movlw 0x97 ; syndrome C?
xorwf HiReg, W
btfsc STATUS, Z
goto _block3_lo
movlw 0xF3 ; syndrome C'?
xorwf HiReg, W
btfss STATUS, Z
goto _block1 ; again
_block3_lo
clrw
xorwf LoReg, W
btfss STATUS, Z
goto _block1 ; again
movf StoReg1, W ; store *block 3 data*
movwf Reg3A
movf StoReg2, W
movwf Reg3B
; ----- _block4 -----------------------------------------------------
;
; FUNCTION: reads the next 26 incoming bits and checks the syndrome
;
; USES: interrupt service routine, _syndrome, _block1
;
; FALLS INTO: _getps
;
; -------------------------------------------------------------------
_block4
call _get26Bits ; read the next 26 bits
movlw 0x96 ; syndrome D?
xorwf HiReg, W
btfss STATUS, Z
goto _block1 ; again
clrw
xorwf LoReg, W
btfss STATUS, Z
goto _block1 ; again
movf StoReg1, W ; store *block 4 data*
movwf Reg4A
movf StoReg2, W
movwf Reg4B
; --------------------------------------------------------------------
; *************** Process the data & extract info ******************
; --------------------------------------------------------------------
; ----- _getdata ----------------------------------------------------
;
; FUNCTION: Extract the PS/PTY/TP/TA from the group data (group 0A/0B)
;
; USES: _block1
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_getdata
movf Reg2A, W
andlw 0xF0 ; Check for group 0A/B
xorlw 0x00
btfss STATUS, Z
goto _block1 ; start all over again
; REG4A & REG4B now contain 2 chars of PS
movf Reg2B, W ; read char position
andlw 0x03 ; mask bits
movwf Locatie ; save position
rlf Locatie, F ; x 2
movlw 0x30 ; calculate mempos
addwf Locatie, W
movwf FSR ; write char using indirect adressing
movf Reg4A, W
movwf INDF
incf FSR, F
movf Reg4B, W
movwf INDF
movlw 0x38 ; Now get TP(1)/PTY(5)/TA(1) code (Block B, bit 10-4)
movwf FSR
rlf Reg2B, F ; ! Reg2B & REG2A are changed here (to save clock ticks)
rlf Reg2A, F
rlf Reg2B, F
rlf Reg2A, F
rlf Reg2B, F
rlf Reg2A, F
rlf Reg2B, F
rlf Reg2A, W
movwf INDF
decfsz LoopCount, F ; decrease loop counter, if 0 => dump
goto _block1
movlw dump
movwf LoopCount
call _dump
goto _block1
; --------------------------------------------------------------------
; ************************* Subroutines ******************************
; --------------------------------------------------------------------
; ----- _getBit -----------------------------------------------------
;
; FUNCTION: used to sync the algortihm with the incoming bitstream
;
; USES: _shuffle, _syndrome
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_getBit
btfss PORTB, rdsClock
goto _getBit ; wait for RDCL to go high
call _shuffle ; shuffle new bit into stack
call _syndrome ; get syndrome
return
; ----- _get26Bits --------------------------------------------------
;
; FUNCTION: read next 26 bits from the incoming bitstream
;
; USES: _syndrome
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_get26Bits
movlw 0x1A ; read 26 bits
movwf Loper ; save value in Loper
movlw 0x90 ; enable interrupt (INT/RB0)
movwf INTCON
_get26Lus
movf Loper, F ; just to check if Loper = zero
btfss STATUS, Z
goto _get26Lus
movlw 0x00 ; Now, the 26 bits are read, disable interrupts
movwf INTCON
call _syndrome
return
; ----- _shuffle ----------------------------------------------------
;
; FUNCTION: shuffle the working registers containing the incoming data
;
; USES: nothing
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_shuffle
bcf STATUS, C
btfsc PORTB, rdsData ; incoming bit
bsf STATUS, C
rlf StoReg4, F ; shuffle
rlf StoReg3, F
rlf StoReg2, F
rlf StoReg1, F
return
; ----- _syndrome ---------------------------------------------------
;
; FUNCTION: determine the syndrome, using the data from the working
; registers and the check-matrix in program memory
;
; USES: _matrixHi, _matrixLo, _delay
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_syndrome ; Multiply the 26 bits by the 26x10
movlw 0x1A ; check-matrix H to produce a 10-bit
movwf CountReg ; syndrome...
clrf HiReg ; Hireg/Loreg will eventually contain
clrf LoReg ; the syndrome
movf StoReg4, W
movwf StoReg4X
movf StoReg3, W
movwf StoReg3X
movf StoReg2, W
movwf StoReg2X
movf StoReg1, W
movwf StoReg1X
_loops
rlf StoReg4X, F ; shuffle stack by 1 bit
rlf StoReg3X, F
rlf StoReg2X, F
rlf StoReg1X, F
btfss STATUS, C ; was it a `1'?
goto _loopx ; for a `0'
movf CountReg, W
call _matrixHi ; get appropriate row from matrix
xorwf HiReg, F ; and exclusive OR it
movf CountReg, W
call _matrixLo ; do same for last 2 bits of matrix row
xorwf LoReg, F
decfsz CountReg, F
goto _loops ; repeat this 26 times, in less than 842uS
return
_loopx ; this is trap for `0', no exclusive OR
movlw 0x03 ; just padding to avoid reading the
call _delay ; same bit twice
decfsz CountReg, F
goto _loops
return
; ----- _delay ------------------------------------------------------
;
; FUNCTION: causes a delay :-)
;
; USES: the present value of the W register
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_delay ; delay
movwf DelayReg
_del
decfsz DelayReg, F
goto _del
return
; ----- _MatrixHi ---------------------------------------------------
; (info on the checkmatrix extracted from Wireless World, '89)
;
; FUNCTION: contains the 8 upper-most bytes of the checkmatrix
;
; USES: the present value of W register
;
; RETURNS: the requested data from the checkmatrix
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_matrixHi
addwf PCL, F
nop ; nop needed because adding to PC causes 2 cycles
retlw 0xC6
retlw 0xE3
retlw 0xA9
retlw 0x3D
retlw 0x7B
retlw 0xF7
retlw 0x80
retlw 0x6E
retlw 0xDD
retlw 0xD5
retlw 0xC4
retlw 0xE7
retlw 0xA1
retlw 0x2D
retlw 0x5B
retlw 0xB7
retlw 0x00
retlw 0x00
retlw 0x01
retlw 0x02
retlw 0x04
retlw 0x08
retlw 0x10
retlw 0x20
retlw 0x40
retlw 0x80
; ----- _MatrixLo ---------------------------------------------------
; (info on the checkmatrix extracted from Wireless World, '89)
;
; FUNCTION: contains the 2 lower-most bytes of the checkmatrix
;
; USES: the present value of W register
;
; RETURNS: the requested data from the checkmatrix
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_matrixLo
addwf PCL, F
nop ; needed because adding 1 to PC jumps 2 steps
retlw 0xC0
retlw 0xC0
retlw 0xC0
retlw 0xC0
retlw 0x80
retlw 0x00
retlw 0x40
retlw 0xC0
retlw 0x80
retlw 0x40
retlw 0xC0
retlw 0xC0
retlw 0xC0
retlw 0xC0
retlw 0x80
retlw 0x00
retlw 0x40
retlw 0x80
retlw 0x00
retlw 0x00
retlw 0x00
retlw 0x00
retlw 0x00
retlw 0x00
retlw 0x00
retlw 0x00
; ----- _dump -------------------------------------------------------
;
; FUNCTION: Dumps the memory contents of locations 30h - 37h
; to the serial port
;
; USES: nothing
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_dump
movf Char0, W
call _send
movf Char1, W
call _send
movf Char2, W
call _send
movf Char3, W
call _send
movf Char4, W
call _send
movf Char5, W
call _send
movf Char6, W
call _send
movf Char7, W
call _send
movf Flags, W
call _send
return
; ----- _send ------------------------------------------------------
;
; FUNCTION: Transmits the byte that is in W register 9600, 8N1
;
; USES: _sendDelay
;
; FALLS INTO: _sendDelay
; ----------------------------------
_send
movwf TxChar
movlw 8
movwf Counter
bcf PORTB, TxD ; send the startbit
call _sendDelay ; delay
_loop
bcf STATUS, C
rrf TxChar, F
btfss STATUS, C
goto _zendNul
bsf PORTB, TxD
goto _wait
_zendNul
bcf PORTB, TxD
_wait
call _sendDelay
decfsz Counter, F
goto _loop
_eos bsf PORTB, TxD ; send the stopbit
; hier geen return, er is nog een senddelay nodig voor stopbit !
; ---------------------
; Serial send delay
; ---------------------
_sendDelay
movlw d'33'
movwf DelayReg
_serlus
decfsz DelayReg, F
goto _serlus
return
; ----- _isr -------------------------------------------------------
;
; FUNCTION: Takes care of interrupt calls.
;
; In case of a RB0/INT change interrupt -> reads the next bit
;
; USES: _shuffle
;
; FALLS INTO: nothing
;
; -------------------------------------------------------------------
_isr ; Interrupt service routine
_push
movwf W_Temp ; save W
swapf STATUS, W ; save status
movwf Status_Temp ; without changing flags
call _shuffle ; it was the bit-read routine
decf Loper, F
bcf INTCON, INTF
_pop
swapf Status_Temp, W ; get original status back
movwf STATUS ; into status register
swapf Status_Temp, F ; old no flags trick again
swapf Status_Temp, W ; to restore W
retfie ; finished
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -