; filename: i2s_1002.asm

; This is a I2S test signal generator which generates a 1002 Hz (44 sample) sine wave with 44.1 kHz sample rate.
; PORTB puts out data to a 74HCT165 shift register
; Code clock with 16*44.1 kHz=705.6 kHz, PIC clock is 8*705.6=5.6448 MHz (on RA6)
; I2S clock is 32*44.1 kHz=1.4112 MHz (from 4040)

; v0.0	250529	Created bitstream data
; v0.1	250530	Added Si5351A code (2.8224 MHz)
; v0.2	250603	Moved I2C to PORTB (RMW issue PORTA)
; v0.3	250603	I2S data output
; v0.4	260422	Moved to PIC16F1827 (clock switching, 22.5792 MHz, port latches)
; v0.5	260428	Also 1.4 MHz clocks from Si
; v0.6	260502	Ditch the 1.4 MHz clocks from Si for a 74HCT74 from CLKOUT
; v0.7	260505	Ditch EXOR LD pulse generator for 2N7002
; v0.8	260505	Enable WDT to prevent clock switching borkage (1 MHz system clock)

	LIST	P=16F1827, F=INHX32
	#include <p16f1827.inc>
	ERRORLEVEL -302			; sod message about using proper bank

	__CONFIG _CONFIG1, _WDTE_SWDTEN & _BOREN_OFF & _CLKOUTEN_ON ;_FOSC_ECH
	__CONFIG _CONFIG2, _PLLEN_OFF

; Equates
RESET_V		EQU	0x00		; Address of RESET Vector

; Registers
FLAGS	EQU	0x20			; Various flags
I2CBUF	EQU	0x21			; Used for I2C data and 2 ms delay
I2CCNT	EQU	0x22			; Used for I2C bitbanger and 2 ms delay
INDX	EQU	0x70			; Index for I2S sample (in all banks)

; Defines
#define	SCL	PORTB,0			; I2C Clock
#define	RED	PORTA,0			; ERR LED (red), TEST 
#define	GRN	PORTA,1			; Success LED (green)
#define	SDA	PORTA,3			; I2C Data
#define	TEST	LATA,0			; Start of the sequence
#define	LD	LATA,1			; Load 74HCT165
#define	WS	LATA,2			; Word Sync
#define	SYNCC	LATA,3			; Sync 74HCT74 clock with PIC
#define	BLANK	LATA,4			; Blank 74HCT74 clock to '165 to prevent glitch
#define	SYSINIT	FLAGS,0			; Si5351A needs this
#define	NAK	FLAGS,1			; Not Acknowledge after I2C byte

	ORG	0
	GOTO	INIT
	ORG	4

; Interrupts
	RETFIE				; There are no interrupts

INIT
; Init stuff
	CLRF	STATUS			; Do initialization
	CLRF	BSR			; Select Bank 0
	CLRF	INTCON			; Clear int-flags, disable interrupts
	CLRF	PCLATH			; Keep in lower 2KByte
	CLRF 	CCP1CON
	BSF	BSR,0			; Select Bank 1
	CLRF	TRISA			; RA7-0 Outputs
	CLRF	TRISB			; RB7-0 Outputs
	MOVLW	B'00000110'		; Timer0, prescaler 1:128
	MOVWF	OPTION_REG
	MOVLW	B'01101010'
	MOVWF	OSCCON			; 4 MHz intosc
	MOVLW	B'00000010'
	MOVWF	WDTCON			; WDT 2 ms, disabled
	CLRF	BSR			; Select Bank 0
	MOVLW	0xFF
	MOVWF	PORTB
	CLRF	PORTA
	BSF	SDA
	CLRF	FLAGS
	BSF	RED
	CALL	STARTUP

; Initialize Si5351A
	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	CLRW				; Prepare register 0 for reading
	CALL	I2CWRITE
	CALL	I2CSTOP

Readsysinit
	CALL	I2CSTART
	MOVLW	0xC1			; Read address
	CALL	I2CWRITE
	CLRF	I2CCNT			; Used for ACK timeout
	BSF	BSR,0			; Select Bank 1
	BSF	TRISA,3			; Set SDA as input to float SDA line
	CLRF	BSR			; Select Bank 0
	CALL	I2CDELAY
	BSF	SCL			; Read SYS_INIT bit
	CALL	I2CDELAY
	BSF	SYSINIT
	BTFSS	SDA
	BCF	SYSINIT
	BCF	SCL
	MOVLW	0x07
	MOVWF	I2CCNT

DUMMYREAD
	CALL	I2CDELAY
	BSF	SCL
	CALL	I2CDELAY
	BCF	SCL
	DECFSZ	I2CCNT,F
	GOTO	DUMMYREAD
	BSF	BSR,0			; Select Bank 1
	BCF	TRISA,3			; Set SDA as output to assert SDA line
	CLRF	BSR			; Select Bank 0
	BCF	SDA			; Present NAK
	CALL	I2CDELAY
	BSF	SCL
	CALL	I2CDELAY
	BCF	SCL	
	CALL	I2CSTOP
	BTFSC	SYSINIT			; System init done?
	GOTO	Readsysinit		; No
	BCF	RED			; Done system init

; Start configure
	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0x03			; Register 3
	CALL	I2CWRITE
	MOVLW	0xFF			; Register 3 data (Output Enable Control)
	CALL	I2CWRITE
	CALL	I2CSTOP

	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0x10			; Register 16
	CALL	I2CWRITE
	MOVLW	0x0F			; Register 16 data: CLK0 control (On)
	CALL	I2CWRITE
	MOVLW	0x80			; Register 17 data: CLK1 control (Off)
	CALL	I2CWRITE
	MOVLW	0x4F			; Register 18 data: CLK2 control (On)
	CALL	I2CWRITE
	MOVLW	0x80			; Register 19 data: CLK3 control (Off)
	CALL	I2CWRITE
	MOVLW	0x80			; Register 20 data: CLK4 control (Off)
	CALL	I2CWRITE
	MOVLW	0x80			; Register 21 data: CLK5 control (Off)
	CALL	I2CWRITE
	MOVLW	0x80			; Register 22 data: CLK6 control (Off)
	CALL	I2CWRITE
	MOVLW	0x80			; Register 23 data: CLK7 control (Off)
	CALL	I2CWRITE
	CALL	I2CSTOP

	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0xB7			; Register 183
	CALL	I2CWRITE
	MOVLW	0xD2			; Register 183 data: XTAL load capacitance (10 pF) and mandatory bit pattern
	CALL	I2CWRITE
	CALL	I2CSTOP

; PLLA values (745.1136 MHz)
	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0x1A			; Register 26
	CALL	I2CWRITE
	MOVLW	0x3D			; Register 26 data: MSNA_P3[15:8]
	CALL	I2CWRITE
	MOVLW	0x09			; Register 27 data: MSNA_P3[7:0]
	CALL	I2CWRITE
	CLRW				; Register 28 data: MSNA_P1[17:16]
	CALL	I2CWRITE
	MOVLW	0x0C			; Register 29 data: MSNA_P1[15:8]
	CALL	I2CWRITE
	MOVLW	0xE6			; Register 30 data: MSNA_P1[7:0]
	CALL	I2CWRITE
	CLRW				; Register 31 data: MSNA_P3/2[19:16]
	CALL	I2CWRITE
	MOVLW	0x3B			; Register 32 data: MSNA_P2[15:8]
	CALL	I2CWRITE
	MOVLW	0xEA			; Register 33 data: MSNA_P2[7:0]
	CALL	I2CWRITE
	CALL	I2CSTOP

; MS0 values for 22.5792 MHz (CLK0)
	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0x2B			; Register 43
	CALL	I2CWRITE
	MOVLW	0x01			; Register 43 data: MS0_P3[7:0]
	CALL	I2CWRITE
	CLRW				; Register 44 data: MS0 misc
	CALL	I2CWRITE
	MOVLW	0x0E			; Register 45 data: MS0_P1[15:8]
	CALL	I2CWRITE
	MOVLW	0x80			; Register 46 data: MS0_P1[7:0]
	CALL	I2CWRITE
	CALL	I2CSTOP

; MS2 values for 11.2896 MHz (CLK2)
	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0x3B			; Register 59
	CALL	I2CWRITE
	MOVLW	0x01			; Register 59 data: MS2_P3[7:0]
	CALL	I2CWRITE
	CLRW				; Register 60 data: MS2 misc
	CALL	I2CWRITE
	MOVLW	0x1F			; Register 61 data: MS2_P1[15:8]
	CALL	I2CWRITE
	CALL	I2CSTOP

	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0xB1			; Register 177
	CALL	I2CWRITE
	MOVLW	0x20			; Register 177 data: PLLA reset
	CALL	I2CWRITE
	CALL	I2CSTOP

	CALL	I2CSTART
	MOVLW	0xC0			; Address
	CALL	I2CWRITE
	MOVLW	0x03			; Register 3
	CALL	I2CWRITE
	MOVLW	0xFA			; Register 3 data: enable OSC0, -2
	CALL	I2CWRITE
	CALL	I2CSTOP
; End setup Si5351A

	BSF	GRN			; Green LED on
	CALL	STARTUP
	BSF	BSR,0			; Select Bank 1
	BCF	OSCCON,1		; switch to external clock
	CLRF	BSR			; Select Bank 0
	CALL	STARTUP
	BANKSEL	WDTCON
	BSF	WDTCON,SWDTEN		; Enable WDT
	BANKSEL	LATA			; Select Bank 2
	CLRF	LATA
	BSF	SYNCC			; Sync 74HCT74 with PIC


; Output I2S data, budget 128 clock cycles per sample (32 bits)
LOOP1
	BSF	TEST			; Start of sequence
	BCF	TEST
	CLRWDT
	MOVLW	0x58
	MOVWF	INDX
	GOTO	$+9

LOOP2
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	MOVFW	INDX
	CALL	SAMPLE
	MOVWF	LATB
	NOP
	NOP
	BCF	WS			; Clear WS (L_Ch)
	NOP
	NOP
	BSF	BLANK			; Blank clock pulse to 74HCT74
	BCF	LD
	BSF	LD			; Load shift register
	NOP
	BCF	BLANK			; Unblank clock pulse to 74HCT74
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	INCF	INDX,F
	MOVFW	INDX
	CALL	SAMPLE
	MOVWF	LATB
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	BSF	BLANK			; Blank clock pulse to 74HCT74
	BCF	LD
	BSF	LD			; Load shift register
	NOP
	BCF	BLANK			; Unblank clock pulse to 74HCT74
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	DECF	INDX,F
	MOVFW	INDX
	CALL	SAMPLE
	MOVWF	LATB
	NOP
	NOP
	BSF	WS			; Set WS (R_Ch)
	NOP
	NOP
	BSF	BLANK			; Blank clock pulse to 74HCT74
	BCF	LD
	BSF	LD			; Load shift register
	NOP
	BCF	BLANK			; Unblank clock pulse to 74HCT74
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	INCF	INDX,F
	MOVFW	INDX
	CALL	SAMPLE
	MOVWF	LATB
	NOP
	NOP
	NOP
	NOP
	NOP
	DECF	INDX,F
	BSF	BLANK			; Blank clock pulse to 74HCT74
	BCF	LD
	BSF	LD			; Load shift register
	NOP
	BCF	BLANK			; Unblank clock pulse to 74HCT74
	DECF	INDX,F
	DECFSZ	INDX,F
	GOTO	LOOP2
	GOTO	LOOP1

SAMPLE
	BRW
	RETLW	0x00			; The first two samples always get skipped
	RETLW	0x00
	RETLW	0x00
	RETLW	0x00
	RETLW	0x12
	RETLW	0x37
	RETLW	0x24
	RETLW	0x0F
	RETLW	0x35
	RETLW	0x2C
	RETLW	0x45
	RETLW	0x33
	RETLW	0x53
	RETLW	0xD2
	RETLW	0x60
	RETLW	0xBC
	RETLW	0x6B
	RETLW	0xAE
	RETLW	0x74
	RETLW	0x6E
	RETLW	0x7A
	RETLW	0xD0
	RETLW	0x7E
	RETLW	0xB2
	RETLW	0x7F
	RETLW	0xFF
	RETLW	0x7E
	RETLW	0xB2
	RETLW	0x7A
	RETLW	0xD0
	RETLW	0x74
	RETLW	0x6E
	RETLW	0x6B
	RETLW	0xAE
	RETLW	0x60
	RETLW	0xBC
	RETLW	0x53
	RETLW	0xD2
	RETLW	0x45
	RETLW	0x33
	RETLW	0x35
	RETLW	0x2C
	RETLW	0x24
	RETLW	0x0F
	RETLW	0x12
	RETLW	0x37
	RETLW	0xFF
	RETLW	0xFF
	RETLW	0xED
	RETLW	0xC8
	RETLW	0xDB
	RETLW	0xF0
	RETLW	0xCA
	RETLW	0xD3
	RETLW	0xBA
	RETLW	0xCC
	RETLW	0xAC
	RETLW	0x2D
	RETLW	0x9F
	RETLW	0x43
	RETLW	0x94
	RETLW	0x51
	RETLW	0x8B
	RETLW	0x91
	RETLW	0x85
	RETLW	0x2F
	RETLW	0x81
	RETLW	0x4D
	RETLW	0x80
	RETLW	0x00
	RETLW	0x81
	RETLW	0x4D
	RETLW	0x85
	RETLW	0x2F
	RETLW	0x8B
	RETLW	0x91
	RETLW	0x94
	RETLW	0x51
	RETLW	0x9F
	RETLW	0x43
	RETLW	0xAC
	RETLW	0x2D
	RETLW	0xBA
	RETLW	0xCC
	RETLW	0xCA
	RETLW	0xD3
	RETLW	0xDB
	RETLW	0xF0
	RETLW	0xED
	RETLW	0xC8

; Subroutines (I2C)
I2CSTART
	BCF	SDA
	CALL	I2CDELAY
	BCF	SCL
	NOP
	RETURN

I2CWRITE				; Writes byte in W
	MOVWF	I2CBUF
	MOVLW	0x08
	MOVWF	I2CCNT

NEXTBIT	
	BCF	STATUS,C		; Clear carry
	RLF	I2CBUF,F		; Rotate MSB into carry
	BTFSC	STATUS,C		; Carry clear?
	BSF	SDA			; No, set SDA
	BTFSS	STATUS,C		; Carry set?
	BCF	SDA			; No, clear SDA
	BSF	SCL			; Send clock
	CALL	I2CDELAY
	BCF	SCL			; Done clock
	DECFSZ	I2CCNT,F
	GOTO	NEXTBIT

; ACK handler
	BSF	SDA
	BSF	BSR,0			; Select Bank 1
	BSF	TRISA,3			; Set SDA as input to float SDA line
	CLRF	BSR			; Select Bank 0
	BSF	NAK
	CALL	I2CDELAY
	BSF	SCL			; Solicit ACK
	CLRF	I2CCNT

TESTACK
	BTFSC	SDA			; SDA pulled low?
	GOTO	CONTACK			; No, continue testing for ACK until timeout (~20 ms)
	BCF	NAK			; Clear NAK
	GOTO	EXITACK			; Exit loop

CONTACK
	DECFSZ	I2CCNT,F
	GOTO	TESTACK

EXITACK
	BSF	BSR,0			; Select Bank 1
	BCF	TRISA,3			; Set SDA as output
	CLRF	BSR			; Select Bank 0
	BTFSC	NAK			; NAK handler
	GOTO	FAIL			; Timeout, no ACK, illuminate ERR LED
	BCF	SCL
	BSF	SDA
	CALL	I2CDELAY
	RETURN

FAIL
	BSF	RED
	SLEEP				; Red LED stays on

I2CREAD					; Reads byte to I2CBUF
	BSF	SDA			; Send ACK
	MOVLW	0x08
	MOVWF	I2CCNT
	BSF	BSR,0			; Select Bank 1
	BSF	TRISA,3			; Set SDA as input to float SDA line
	CLRF	BSR			; Select Bank 0

NEXTREAD
	BSF	SCL			; Send clock
	NOP
	NOP
	NOP
	BTFSC	SDA			; SDA low?
	BSF	STATUS,C		; No, set carry
	BTFSS	SDA			; SDA high?
	BCF	STATUS,C		; No, clear carry
	RLF	I2CBUF,F		; Rotate bit into buffer
	BCF	SCL			; Done clock
	CALL	I2CDELAY
	DECFSZ	I2CCNT,F
	GOTO	NEXTREAD

; ACK handler
	BSF	BSR,0			; Select Bank 1
	BCF	TRISA,3			; Set SDA as output
	CLRF	BSR			; Select Bank 0
	BSF	SCL
	CALL	I2CDELAY
	BCF	SCL
	BCF	SDA			; Clock out ACK
	CALL	I2CDELAY
	BSF	SCL
	CALL	I2CDELAY
	BSF	SDA
	RETURN

I2CSTOP
	BCF	SDA
	NOP
	NOP
	BSF	SCL
	CALL	I2CDELAY
	BSF	SDA
	RETURN
	
I2CDELAY
	NOP				; 5 clocks delay
	RETURN

STARTUP					; 2 ms delay at startup to let things settle
	DECFSZ	I2CBUF,F
	GOTO	$-1
	DECFSZ	I2CCNT,F
	GOTO	$-3
	RETURN

; End Subroutines

	END