何とか動くようになったので、SRCのプログラムはこれで終わり。
ベーシックらしくダラダラ書いたから4Kバイトを超えた。
そのうち、DACの制御も追加予定なので、製品版のBASCOM-AVRを(株)コンパス・ラブから買ってしまった。Compass LabのLabを「ラブ」と訳すのは怪しいので止めて欲しい気が。
困ったチャン その1
せっかくソフト制御にするので、入力信号のエラーをチョコッと表示したかった。 パソコン音源でチェックしていたが、エラー表示がまともに動かない。
1ヶ月悩んだ末、何と!、リアルテックのせい?ゾーテックのせい?か解らんが、Fs96kの時、オンボードサウンドの再生信号に高確率で[Validity]エラーが出っ放しになることが判った。
この対策で、[V]エラーが出たらエラーマスクを掛ける様にしてある。
ハードウェア制御時のDIRエラー出力に、ノン・バリディティ・エラーがあることに納得した。
困ったチャン その2
DIRのINT出力で表示をコントロールするのだが、DIRは信号の変化でINT出力を出すので、タイミングが読めない。
LCD、EEPRAM、DIR、DITへのアクセス時全部に、割込み禁止にしなければならん。これでプログラムが大きくなった。
困ったチャン その3
入力信号のサンプルレートを、DIRのORRレジスターから読んでいるが、データシートに書いてある通り、結構好い加減。雰囲気で感じる程度かな。
困ったチャン その4
CS8416は守りが堅い。出力元のカテゴリーコードを監視していて、改造SACDプレーヤーを接続すると、CCH/FCHが出ない。
困ったチャン その5
DIRのINT出力、ステータスレジスタのそれぞれのタイミングが、データシートで解り難かった。英語力の問題?。
たった170円のチップでここまで出来るとは。Z80の2kバイトメモリーに1万円出したのを今でも思い出す。
画像は、メディアプレーヤーで再生停止にしたところ。 デジタルサイレンスの[S]と[V]が出ている。
受信ビット数は、民生用SPDIFでは、最大数20bit表示に固定されている。
受信では、チャンネルステイタスをP・C・O・E表示にしたが、送信側は面倒なので、最初の2バイトをHEX表示にした。cs20はNon-copyright、59はサンプルレート・コンバーター。
受信サンプルレートが、95kなのはご愛嬌。
' **********************************
' DAI test program for CS8416 and CS8406 .
' language-specific BASCOM-AVR
' using AvrIspmk2 with AVR-STUDIO4.
' 11/Feb.- 24/mar.2014 Copyright j_audio.
' For personal use only.
'*********************************
$regfile = "m88def.dat" '
Config Clockdiv = 2 ' Default division factor is 8
$crystal = 4000000 '8MHz/2
$hwstack = 128
$swstack = 128 'set for m88
$framesize = 128 '
$lib "i2c_twi.lbx" ' Do not use software emulated I2C but the TWI
'------- ports setting ----------------
Config Portb = Output
'PortB : 0=Enc_LED, 1=LCD_RS, 2=LCD_E, 3=SAOF, 4=--, 5=--, 6=Rst_SRC, 7=PLL_Fs
Config Portc = Output '
'PortC : 0=DB4, 1=DB5, 2=DB6, 3=DB7, 4=SDA, 5=SCL.(LCD data bus and I2C)
Config Portd = Input
' PortD : 0=Enc/A, 1=Enc/B, 2=DIR_INT/0, 3=DIT_INT/1, 4=Enc/Sw , 5=W.L/Sw, 6=Copy/Sw, 7=SRC_unlock
Portd = &HFF 'pull up
'------ Configures the I2C hard-wear TWI pin. -----------------
Config Scl = Portc.5 ' we need to provide the SCL pin name
Config Sda = Portc.4 ' we need to provide the SDA pin name
Config Twi = 100000 ' wanted clock frequency 100KHz.
'------ LCD Pin ---------
Config Lcd = 16 * 2
Config Lcdpin = Pin , Db4 = Portc.0 , Db5 = Portc.1 , Db6 = Portc.2 , Db7 = Portc.3
Config Lcdpin = Pin , E = Portb.2 , Rs = Portb.1
'----------------------
Config Int0 = Falling 'Active low, low output indicates an interrupt condition has occurred.
Config Int1 = Falling 'Active low, E to F interrupt Completion
Config Base = 0 'Array C_reg, the first element will be element 0.
'-------- Declares variables. ---------------
Dim Rx_sel As Eram Byte At $11 '0-3 , ch1-4.
Dim S_rate As Eram Byte At $12 '0-5, bit0=Fs group(Ex.0=44.1k,5=192k),PortB.7=1: 44.1k.
Dim Wd_lng As Eram Byte At $13 '0/1 to 24/16bit
Dim Cpy_c As Eram Byte At $14 '0/1 Cpy_c=0 : copyright
Dim Cs_0 As Eram Byte At $15 'First byte of CS, (b00000000)
Dim Cs_1 As Eram Byte At $16 'Second byte, Category code and L bit. (b01011001)
'-----------------------------
Dim Enc As Byte 'Encoder status.
Dim Temp As Byte 'Temporary variable.
Dim Cso_flg As Bit 'dit cs set timing flag
Dim Cont As Byte ' Valiable for controls
Dim Map As Byte 'Memory address pointer of control registers.
Dim C_reg(&H22) As Byte 'array to write/read control registers value.
Dim Hexv As String * 11 ' Strings Array to display character on LCD.
Dim I_status As Byte ' DIR Interrupt status.
Dim E_status As Byte 'DIR error status .
Dim U_flg As Bit
Dim V_flg As Bit
Dim S_flg As Bit
Dim Aux_str As String * 1
Dim Fsi As Integer 'input sample rate
Const Slave_r = &H2A 'CS8416's I2C address.
Const Slave_t = &H28 'CS8406's I2C address.
'Const Slave_d = &B10011000 'CS4398's I2C address. h98
' ********** Initialize routines ***********
Reset Portb.6 'Set SRC reset-line low-level. Must be separate SRC's reset line.
Cursor On Blink
Cls
' Initialize EEPRAM +++++ The separated EEPROM data writing is required !. +++++
Temp = Rx_sel
If Temp > 3 Then 'check ERAM
Rx_sel = 0 'initialaze Eram. input is ch.1
S_rate = &B00000000 '44kHz
Wd_lng = 0 '16bit
Cpy_c = 0
End If
Cs_0 = &B00000000 'First byte of cs:0,0,C,0,0,0,0,0
Cs_1 = &B01011001 'Category code:0,1,0,1,1,0,0,L(1)(Sample Rate Converter)
If Rx_sel > 3 Then
Hexv = "EEPRAM ERR"
Goto Init_er
End If
' ------ Initialize DIR --- after power-on reset, set registers except RUN-bit.
I2cinit
'Initializes CS8416 Control register(MAP) data .
'00(cntl0) = default(00) by reset.
C_reg(0) = &H01 'Starting address (MAP)
'01(cntl1)=SWCLK:MUTESAO:INT1:INT0:HOLD1:HOLD0:RMCKF:CHS.
C_reg(1) = &B00100010 ' (INT is open drain active low, RMCK output frequency is 128*FS.)
'02(cntl2)=DETCI:EMPH_CNTL2:EMP_C1:EMP_C0:GPO0SEL3:GPO0S2:GPO0S1:GPO0S0
C_reg(2) = &H00 'de-emphasis filter off,GPO0=GND
'03(cntl3)=GPO1SEL3:GPO1S2:GPO1S1:GPO1S0:GPO2SEL3:GPO2S2:GPO2S1:GPO2S0
C_reg(3) = &B00100000 'GPO1=INT,GPO2=AD2(47k to VL).
'04(cntl4)=RUN:RXD:RXSEL2:RXSEL1:RXSEL0:TXSEL2:TXSEL1:TXSEL0
Temp = Rx_sel 'set previous input ch. selection.
Shift Temp , Left , 3 ' C_reg(4) = &H07(if Tx=7)
C_reg(4) = &H00 Or Temp 'Set Rx channel. (Except RUN-bit. Tx is ch.0)
'05(SADF)=SOMS:SOSF:SORES1:SORES0:SOJUST:SODEL:SOSPOL:SOLRPOL
C_reg(5) = &B11000000 ' master mode, OSCLK output is 128*Fs.
'06(R.E.Mask)= 0: QCRCM: CCRCM: UNLOCKM: VM: CONFM: BIPM: PARM
C_reg(6) = &B00111011 'ccrc,unlock,Validity,Bi-phase,Parity are un-masked.
'07(Int mask)= PCCHM OSLIPM DETCM CCHM RERRM QCHM FCHM
C_reg(7) = &B00001101 'CCH, RERR, FCH are unmasked.
'Interrupt Mode MSB (08h) and Interrupt Mode LSB(09h)
'C_reg(8):(9) = default to &B00000000 INT mode is Rising edge active mode.
I2csend Slave_r , C_reg(0) , 8 ' ------- Write to DIR control register 1-7.
C_reg(5) = 5
I2creceive Slave_r , C_reg(5) , 1 , 1
If C_reg(5) <> &B11000000 Then ' --- Verify SADF register.
Hexv = "DIR Init.ER"
Goto Init_er
End If
' ------ Initialize DIT --- 'CS8406 Control register(MAP) data .
C_reg(&H00) = &H01 'MAP
C_reg(&H01) = &B00000101
'*01(control1) = 0: VSET: 0: MUTEAES: 0: INT1: INT0: TCBLD
' INT1:INT0 = 00 - Active high, 01 - Active low, 10 - Open drain, active low.
I2csend Slave_t , C_reg(&H00) , 2 ' INT is open drain(Active Low), TCBL is an output.
'*04(Clock Source Control) =0: RUN: CLK1: CLK0: 0: 0: 0: 0 , 04 is set at the time of start DIT
C_reg(&H04) = &H05
C_reg(&H05) = &B11000100
'*05(Serial Input Format)=SIMS:SISF:SIRES1:SIRES0:SIJUST:SIDEL:SISPOL:SILRPOL
I2csend , Slave_t , C_reg(&H04) , 2 'SAIF= Master, 128*Fs, I2S Mode.
C_reg(&H08) = &H09
C_reg(&H09) = &B00000010 ' bit1=1 : EFTCM masked,unmask at change cs
'*09(Interrupt 1 Mask) TSLIPM: 0: 0: 0: 0: 0: EFTCM: 0
C_reg(&H0a) = 0
C_reg(&H0b) = &B00000010 'EFTC interrupt is Falling edge active.
'*0A(Int_1 Mode(MSB)) TSLIP1: 0: 0: 0: 0: 0: EFTC1: 0 , 00 - Rising edge active
'*0B(Int_1 Mode(LSB)) TSLIP0: 0: 0: 0: 0: 0: EFTC0: 0 , 01 - Falling edge active
I2csend Slave_t , C_reg(&H08) , 4 'E to F C-buffer transfer interrupt occurs at end of transfer.
C_reg(5) = &H05
I2creceive Slave_t , C_reg(5) , 1 , 1
If C_reg(5) <> &B11000100 Then
Hexv = "DIT Init.ER"
Goto Init_er
End If
' ------ Initialize DAC --- 'CS4398 Control register(MAP) data .
' ------- Set and Run --------
Enable Interrupts 'enable master switch
On Int0 Int_dir 'use the int0 to detect DIR status change
On Int1 Int_dit ' DIT's INT managements CS data writing.
Enable Int0 'we enable int0 interrupt of the DIR
Enable Int1
Gosub Set_wd 'The previous output word-length setting.(PortB.3)
Gosub Set_rx 'Set The previous input-channel then Run the DIR.
Set Portb.6 ' Set to run SRC .
Gosub Set_fso ' Set The previous sample-rate then Run the DIT
Enc = 3 'Initialize encoder status.
'*****************************
'* MAIN LOOP *
'*****************************
Do
Enc = Encoder(pind.0 , Pind.1 , Links , Rechts , 0) 'Input ch. or sample rete select.
Debounce Pind.4 , 0 , Change , Sub 'Change encoder mode. Input channel/Sample rate.
Debounce Pind.5 , 0 , Sel_wd , Sub 'Set output word length 16bit or 24bit.
Debounce Pind.6 , 0 , Sel_cpy , Sub 'Set or reset output c-bit .
If Enc = 3 Then 'encoder is not rotating
Gosub Chk_int 'Check interrupt status.
Gosub Chk_flg 'check digital-silence and unlock flag.
End If
Main2: ' Checking Cso_flag, to confirm the writing of CS in DIT.
If Cso_flg = 1 Then
Disable Interrupts
Reset Cso_flg
C_reg(9) = 0
C_reg(8) = 9
I2csend Slave_t , C_reg(8) , 2 'disable DIT's INT,Instead of Disable avr's INT1
Gosub Lcd_out
C_reg(&H07) = &H07 'Reset Interrupt 1 Status (07h) (Read Only) of DIT.
I2creceive Slave_t , C_reg(&H07) , 1 , 1
Enable Interrupts
End If
Loop
Init_er: 'Stop if initialize error occurred.
Home
Lcd Hexv
End
Data_fso:
Data " 44k" , " 48k" , " 88k" , " 96k" , "176k" , "192k" , "Lcd_out ERR"
Data_csi:
Data "-" , "O" , "c" , "P" , "E" , "o" , "C" , "p" '1st 4chr is bit=1,other cs bit is 0.
' ******** sub routine *************************
' ----- On Interrupt ------
Int_dir: 'DIR's interrupt occurred.
Waitms 5 ' to avoid a second CCH continuous occurrence of two
C_reg(&H0c) = &H0C ' 07(Int_mask) = &B00001101
I2creceive Slave_r , C_reg(&H0c) , 1 , 2 'Get value and clear register.
I_status = C_reg(&H0d)
E_status = C_reg(&H0c)
Return 'generates a RETI because it is the first RETURN
Return 'generates a RET because it is the second RETURN
Int_dit:
Set Cso_flg
C_reg(&H1f) = &H20 'DIT's MAP of F buffer
C_reg(&H20) = Cs_0 'first byte of C Data Buffer
C_reg(&H21) = Cs_1 'Cs_1 = &B01011001,category code and L bit
I2csend Slave_t , C_reg(&H1f) , 3
Return
Return
' -------- check Dir_interrupt status ------------
Chk_int:
If I_status.0 = 1 Then 'Format CHange
Reset I_status.0
Gosub Fch
End If
If I_status.2 = 1 Then 'Receiver ERRor
Reset I_status.2
Gosub Rerror
End If
If I_status.3 = 1 Then 'Channel status code CHange
Reset I_status.3
Gosub Fch
End If
Return
Rerror:
If E_status.4 = 1 Then 'unlock
Set U_flg
Aux_str = "u"
Gosub Fch2
Disable Interrupts
Locate 1 , 6
Lcd " No Signal "
Enable Interrupts
Goto Change1
End If
If E_status = 8 Then 'validity error
If V_flg = 0 Then
Set V_flg
Disable Interrupts
C_reg(5) = 6
C_reg(6) = &B00010011 'set V error mask, until next FCH/CCH or reset S_flg.
I2csend Slave_r , C_reg(5) , 2
Gosub Int_dir 'clear interrupt and error status.
Enable Interrupts
Gosub Lcd_fsi
Aux_str = "v"
Goto Rerror2
End If
Elseif E_status = 2 Then
Aux_str = "b" 'biphase error
Goto Rerror2
'End If
Elseif E_status = 1 Then
Aux_str = "p" 'parity and other
Goto Rerror2
'End If
Elseif E_status = &H20 Then
Aux_str = "c"
Goto Rerror2
Else
If U_flg = 0 Then
Aux_str = "e"
Goto Rerror2
End If
End If
Return
Rerror2:
Disable Interrupts
Locate 1 , 10
Lcd Aux_str
Enable Interrupts
Goto Change1
Fch: 'Format changed.
Gosub V_unmask
Gosub Lcd_fsi
Gosub Rd_sts
Temp = C_reg(&H0b)
Shift Temp , Right , 1
If Temp = &H21 Then
Set S_flg
Aux_str = "s"
Elseif Temp = &H20 Then
Aux_str = " "
Else
Aux_str = "n" 'Format is not L-PCM
End If
Fch2:
Disable Interrupts
Locate 1 , 5
Lcd Aux_str
Enable Interrupts
Goto Change1
Chk_flg:
If S_flg = 1 Then
Gosub Rd_sts
If C_reg(&H0b).1 = 0 Then 'when DGTL_SIL bit changed to 0,
Gosub Chk_flg2
End If
End If
If U_flg = 1 Then '
Gosub Rd_sts
If C_reg(&H0c).4 = 0 Then
Reset U_flg ' if PLL is lock, display the input signal status.
Gosub Fch
End If
End If
Return
Chk_flg2:
Reset S_flg ' reset digital-silence indication.
Aux_str = " "
Gosub Fch2
Aux_str = " " ' when digital-silence indication is reset, will reset the V indication.
Gosub Rerror2
Gosub V_unmask
Return
V_unmask:
Reset V_flg
Disable Interrupts
C_reg(5) = 6
C_reg(6) = &B00111011 'reset to V error un-mask.
I2csend Slave_r , C_reg(5) , 2
Enable Interrupts
Return
Rd_sts:
Disable Interrupts
C_reg(&H0b) = &H0B
I2creceive Slave_r , C_reg(&H0b) , 1 , 2 'Read FDS and RERR status.
Enable Interrupts
Return
'------- Encoder ------
Links: ' Left rotated.
If Enc <> 0 Then 'Wait center value between click-points.
Return
End If
If Portb.0 = 1 Then 'PORTb.0 = Encorder LED output. On=Fso set mode.
Goto Sel_fsol
End If
Temp = Rx_sel
If Temp = 0 Then 'Decrease input channel No.
Return
Else
Decr Temp
End If
Rx_sel = Temp
Goto Set_rx
Rechts: ' Right rotated.
If Enc <> 0 Then
Return
End If
If Portb.0 = 1 Then 'PORTb.0 = Encorder LED output. On:Fso set mode.
Goto Sel_fsor
End If
Temp = Rx_sel 'Increase input channel No.
If Temp = 3 Then
Return
Else
Incr Temp
End If
Rx_sel = Temp
'Goto Set_rx
Set_rx: '----- Set Rx and RUN
Disable Interrupts
Temp = Rx_sel
Incr Temp
Home 'Display Rx section.(upper line)
Lcd "Ch." ; Temp
Locate 1 , 3
Decr Temp
Shift Temp , Left , 3
C_reg(4) = &B10000000 Or Temp 'Run bit=1, Rx-ch.=temp, Tx-ch.=0
C_reg(3) = 4
I2csend Slave_r , C_reg(3) , 2
Enable Interrupts
Return
' ----- LCD control ----
Lcd_fsi:
Disable Interrupts
C_reg(&H18) = &H18 ' h18 = OMCK/RMCK Ratio
I2creceive Slave_r , C_reg(&H18) , 1 , 1 'ORR=Fso/(Fsi*256). The Fso is determined by OMCK
' Orr = C_reg(&H18) 'ORR[7:6] - Integer part of the ratio (Integer value=Integer(SRR[7:6])).
Fsi = 27000 / C_reg(&H18) 'ORR[5:0] - Fractional part of the ratio (Fraction value=Integer(SRR[5:0])/64).
Incr Fsi 'Fsi=OMCK/(ORR*4), OMCK= CS8421 MCK
Fsi = Fsi / 4
Locate 1 , 6 ' Fine adjustment of the orr number.
If Fsi > 100 Then
Lcd Fsi ; "k "
Else
Lcd " " ; Fsi ; "k "
End If
C_reg(&H0a) = &H0A 'Foermat detect Status
I2creceive Slave_r , C_reg(&H0a) , 1 , 1
Temp = C_reg(&H0a)
Shift Temp , Right , 4 'Get auxiliary data field width.
Temp = Temp + 16 'The number of bits in the input AUX data.
Locate 1 , 11
Lcd Temp
For Cont = 3 To 0 Step -1 'Display P/C/O/E status.
If C_reg(&H0a).cont = 1 Then
Temp = Cont
Else
Temp = Cont + 4
End If
Hexv = Lookupstr(temp , Data_csi)
Lcd Hexv
Next
Enable Interrupts
Goto Change1
'Return
Lcd_out:
Disable Interrupts
Locate 2 , 1
If Portb.3 = 1 Then
Hexv = "24"
Else
Hexv = "16"
End If
Lcd Hexv ; "bit"
Temp = S_rate 'S_rate setting
Hexv = Lookupstr(temp , Data_fso) ' Set hexv from Data_fso by Order of S_rate
Lcd Hexv
C_reg(&H20) = &H20 'get dit's first 2 bytes cs data.
I2creceive Slave_t , C_reg(&H20) , 1 , 2
Locate 2 , 11 '2nd byte is category code, SRC .
Lcd "cs" ; Hex(c_reg(&H20)) ; Hex(c_reg(&H21))
Enable Interrupts
Goto Change1
'Return
' - Set Word_length (PortB.3) SAOF pin Output Port Configuration is
' 1.00k to GND. I2S 16-bit data, 4.02k to GND. I2S 24-bit data
' resistors attached to the SAOF are : SAOF --- 1k --- header --- 3k --- GND
'header will be connected through open collector transistor-Sw to GND. portB.3 is 1 = 16bit.
Sel_wd:
If Wd_lng = 0 Then
Wd_lng = 1
Else
Wd_lng = 0
End If
Set_wd: 'CS8421 : SAOF control.
Reset Portb.6 'Reset SRC to enable SAOF setting.
If Wd_lng = 0 Then
Reset Portb.3
Else
Set Portb.3
End If
Set Portb.6 'Run the src
Goto Lcd_out
'Return
Change: '----- Set Encoder mode. 1 = Fs, 0 = Rx select.
Toggle Portb.0 'PORTB.0 = Encorder LED output. On:Fs set mode.
Change1:
Disable Interrupts
If Portb.0 = 1 Then 'Move cursor
Locate 2 , 6 'S_rate select mode.Y:2 , O:"24bit192k cs****"
Else
Locate 1 , 3 'Rx select mode.Y:1 , I:"ch.1 192k 20PCOE"
End If
Enable Interrupts
Return
Sel_fsol: 'Change DIT CLK, stop the RUN once.
If S_rate = 0 Then '0=512*Fs:44k, 1=512*Fs:48k,2=256*Fs:88k ,--
Return
Else
Disable Interrupts
Temp = S_rate
Decr Temp 'Left rotation, decrease sample-rate
S_rate = Temp
Enable Interrupts
End If
Goto Set_fso
Sel_fsor:
If S_rate = 5 Then
Return 'Right rotation, increase sample-rate
Else
Disable Interrupts
Temp = S_rate
Incr Temp
S_rate = Temp
Enable Interrupts
End If
Set_fso: ' Set sample rate. S_rate : bit0=PLL-Fs1, bit1-2:CLK1, CLK0
Disable Interrupts
Temp = S_rate
If Temp.0 = 0 Then 'CS8406 CLK [1 : 0] [0:0] OMCK Freq. is 256*Fs 96k
Set Portb.7 ' [1:1] OMCK Freq. is 128*Fs 192k
Else ' [1 0] OMCK Freq. is 512*Fs 48k
Reset Portb.7 ' PB7=FS1 (PLL1707) 0: 48KHz, 1: 44.1KHz group
End If
Shift Temp , Right , 1
Toggle Temp.0
If Temp = 1 Then
Shift Temp , Left , 5
Else
Shift Temp , Left , 4
End If
C_reg(0) = 1
C_reg(1) = &B00010101
I2csend Slave_t , C_reg(0) , 2 'Mute dit
C_reg(3) = 4
C_reg(4) = Temp
I2csend Slave_t , C_reg(3) , 2 'Stop DIT and set CLK ratio.
Set C_reg(4).6
I2csend Slave_t , C_reg(3) , 2 ' Run DIT
C_reg(1) = &B00000101
I2csend Slave_t , C_reg(0) , 2 'Un-Mute dit
Enable Interrupts
Gosub Set_cpy 'needs re-write cs after stop dit.
Goto Lcd_out
'Return
Sel_cpy: ' Ch_status 0 1
If Cs_0 = 0 Then ' bit0: cons pro
Cs_0 = &B00100000 ' bit1 : Lpcm No -pcm
Else ' bit2: c_rt no-copyright
Cs_0 = &B00000000 ' bit3: non pre_emphasis on
End If '
Set_cpy: ' enable dit's INT to set cs by avr's INT1
Disable Interrupts 'Enable Int1
Reset Cso_flg
C_reg(9) = 2
C_reg(8) = 9
I2csend Slave_t , C_reg(8) , 2
Enable Interrupts
Return
