Normally you have to keep in perfect synchrony with the master clock or the chip shuts down - you get a certain
number of MCLKs leeway, so you'll need to resynchronize the the master clock regularly - I found it much
easier just to run everything from the one clock then WAITCNT/cycle counting/WAITPEQ does all the heavy lifting.
If the LRCLK and BCLK are generated for you it shouldn't be too hard to sync to them, but I can't see how you'd
sync without slipping to MCLK.
I haven't nailed this down, but a lot of people are using this ES9023 in Asynchronous mode. The data sheet does not specify any method to switch from modes, but I read on one companies literature that uses the DAC that it "locks on to whatever it gets'. Since the master clock the DAC will be seeing is 768*44100, it exceeds the requirement for async. There is no method I can find to switch modes by any means, so apparently the DAC just does lock to whatever it gets. It states that it will accept clocks of 256fs, 384fs, 512fs, 768fs for audio playback of 44100.
I got a board built today. A few errors on the pcb that are hackable. I didn't get in a few parts( Op amps ) as digikey is having some bad weather. The RTC, SD card, PLL clock are working. Now to start working on how to get some audio out of it from the ES9023 DAC and then Kye's wave player.
mov Time, cnt 'setup time delay
add Time, cntadd
waitcnt Time, wait ' value doesn't matter
xor outa, ledmask
mov Time, cnt 'setup time delay
add Time, cntadd
waitcnt Time, wait
jmp #:loop
ctra_ long %01010 << 26 + 27 'mode + APIN %01010
cntadd long 2_400_000 '2_400_000 visible blink rate, below is solid LEDs
wait long 0 '<< doesnt matter what this is 0 to 2b
Ledmask long %10_00000000_00000000 ' 17
cnt_ res 1
new res 1
old res 1
temp res 1
Time res 1
Hey guys I am stuck on about 5 hours trying to find out what is going on. I have studied the manual and looked at examples that all show the ADD cntadd value to Time as the first thing to do after moving CNT to TIME. But I also see other values used in the dest value Waitcnt Time, SomeValueHere.
I have an LED connected, and at the rate shown of 2_400_000 I can still see blinking, below this rate the LED becomes more solid. What is the purpose of the destination value in Waitcnt? I can set it to 0 to any number and it has no effect on the rate of the LED blinking. The waitcnt instruction seems to be ignoring the dest value completely as if it were just a placeholder.
I have an LED connected, and at the rate shown of 2_400_000 I can still see blinking, below this rate the LED becomes more solid. What is the purpose of the destination value in Waitcnt? I can set it to 0 to any number and it has no effect on the rate of the LED blinking. The waitcnt instruction seems to be ignoring the dest value completely as if it were just a placeholder.
The destination value (waitcnt dst, src) is the cnt target at which point the insn is released from its wait state. You're most likely referring to the advancement (source value) which prepares the next target value (dst += src, waitcnt defaults to wr). In your code you manually reset the target (destination) value every time which means any advancement you apply is ignored. Try this (active advance):
entry mov dira, ledmask
mov Time, cnt
add Time, #9 ' stop waitcnt from locking up
:loop waitcnt Time, cntadd ' settle for Time+N*cntadd
xor outa, ledmask
jmp #:loop
cntadd long 2_400_000
ledmask long |< 17
Time res 1
Note, it waits first on dst then - on the way out - adds src to dst.
Yes you are correct I was referring to the Source value not dest. I finally found some discussion on this same exact subject in which Mike Green explained to someone what was going on. The manual should be more clear on this imo. Thanks for the suggestions.
This is how far I got today experimenting with how to clock the 24 bits out to the DAC using a master clock input to the Prop at 384 *44100.
My calculations are, 48 BitClocks per sample for left and right channels at 24 bits each. Each Word Select (LR Clock) phase is 192 master clock ticks each.
I intend to use a COG for reading the master clock in and outputting a bitclock to the DAC. This COG will likely not do anything else. Then a COG will watch the bitclock output pin, and divide down to produce the LR Clock output. The main COG will run the audio read from SD, and another COG will write the data bits to the DAC watching the bitclockpin and LR clock pin for timing.
DAT 'Start a Bit Clock based on external clock @ (384 * 44100 ) / 48 bits per sample
'each 384 ticks is a full sample (fs) at 44.1k. 384/2 = 192 ticks per channel for
'24bits, Word Select toggle rate is every 192 ticks. Bit Clock is 384 / 48 bits = 8 ticks per bit
org
entry mov ctra, ctra_ 'establish mode and start counter
mov frqa, #1 'increment for each edge seen
mov time, CNT 'initialize CNT to Time if needed
add time, BitLen ' setup LRCLK time accounting
or dira, ledmask
mov $, #0 wz, nr 'clear any set z
' Start Loop to dither 384 * 44100 to 8 ticks per bitclock
:loop mov new, phsa 'record new count
cmp new, BitLen wz, wc '384 / 48 bits per sample = 8 ticks
if_ae or outa, ledmask
'add delay if required, then shorten loop time to compensate for loss due to clocks in ASM
mov time, CNT
add time, cntadd
waitcnt TIME, cntadd
if_ae xor outa, ledmask
if_ae mov phsa, #0
jmp #:loop
ctra_ long %01010 << 26 + 27 'mode + APIN %01010
cntadd long 2_400_000 'value used for visible blink using LED as debug at home
BitLen long 2_400_000 ' Use 8 for bit rate 384 / 48bits
Ledmask long |< 17
I have tried to convert Mark T's WM8524 DAC code to use on my DAC. His code was Left Justify. I have changed it to I2S format which has the LR clock changing states just before the LSB or the 24 bit data per channel. The next step is to find out how to buffer the audio and have this loop read it. This loop watches the bitclock that is posted above, which is derived from the external master clock. There is some question about my use of waitpeq and waitpne to lock to the bitclock pin. The plan is to try to look at this on the Parallax Logic analyzer in I2S mode later today.
DAT
ORG 0 'Begin at Cog RAM addr 0
ES9023 mov parm, PAR ' COPT PAR to asm PARM
rdlong temp, parm ' COPY the pin config PAR to PARM in ASM
add parm, #4 ' GET VAR POSITION of LeftPtr
rdlong LeftDataaddr, parm ' COPY LEFTPTR ADDRESS IN ASM
add parm, #4 ' GET VAR POSITION of RightDataPtr
rdlong RightDataaddr, parm ' COPY RightDataPTR ADDRESS IN ASM
' SET PIN DIRECTION
'or DIRA, mclkMSK
'or DIRA, bclkMSK
or DIRA, LRClkMSK
or DIRA, DataInMask 'data in to Dac
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SET LRCLOCK HIGH to start
mov OutPut, LRClkMSK
mov OUTA, OutPut '___--- L R CLK HIGH
mov zero, #0
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'initialize LR and elapse first bitclock
WAITPEQ zero, bclkMSK ' Wait for Bitclock LOW
andn OutPut, LRClkMSK ' ---____ L R CLK LOW
'First bitclock high after LR Clock change
WAITPNE zero, bclkMSK ' Wait for Bitclock HIGH __--
'
''''WAITPEQ zero, bclkMSK ' Wait for Bitclock LOW --__
' Second bitclock High after LR Change MSB send
:loop 'GET THE L E F T WORD
rdlong LeftData, LeftDataaddr ' read LEFT data
shl LeftData, #8 ' shift LeftData 8 assumes a 16bit val
mov n, #23 'Send 23 then change LR and send 24th
' Start MSB on second bitclock high
' SHIFT OUT LeftData 24BITS start while bitclock is LOW
' RCL Rotate C Left << Data by n Bits
:LeftDataloop rcl LeftData, #1 WC ' shift out one data bit (MSB first)
muxc OutPut, DataInMask ' put THIS bit in the RightData pin position
WAITPEQ zero, bclkMSK ' wait for bclk falling edge
mov OUTA, OutPut ' _DRIVE_ outputs (LRCLK, DIN, LRCLK)
djnz n, #:LeftDataloop ' loop23 times
'LR change before LSB (bit 24)
or OutPut, LRClkMSK '___--- L R CLK HIGH
' Send 24th bit after LR Change
rcl LeftData, #1 WC ' shift out one data bit (MSB first)
muxc OutPut, DataInMask ' put THIS bit in the RightData pin position
WAITPEQ zero, bclkMSK ' wait for bclk falling edge
mov OUTA, OutPut ' _DRIVE_ outputs (LRCLK, DIN, LRCLK)
'GET THE R I G H T WORD
rdlong RightData, RightDataaddr ' read RIGHT data
shl RightData, #8 ' assumes 16 bit
mov n, #23
WAITPEQ zero, bclkMSK ' wait for the bit clock to be low
' SHIFT OUT LeftData 24BITS
:RightDataloop rcl RightData, #1 WC
muxc OutPut, DataInMask
WAITPEQ zero, bclkMSK
mov OUTA, OutPut ' _DRIVE_ outputs (LRCLK, DIN)
djnz n, #:RightDataloop
'LR change before LSB (bit 24)
andn OutPut, LRClkMSK ' ---____ L R CLK LOW
' Send 24th bit after LR Change
rcl RightData, #1 WC
muxc OutPut, DataInMask
WAITPEQ zero, bclkMSK
mov OUTA, OutPut ' _DRIVE_ outputs (LRCLK, DIN)
jmp #:loop ' ready for next sample set
Good observation! Well, to be honest when I was experimenting with the waitcnt's and if_a following cmp, I was finding that if there was NO instruction anywhere(ie commenting out the cmp for testing) that included wz or wc, I was seeing that if_a was firing as true. So I searched for info on what may be the states of z and c if no instructions were called to set z or c and couldn't determine if z and c might load in an undefined state as I was witnessing. I was testing with an LED output to watch the waitcnt states to learn how they worked, and just decided to leave in the z and c clear even though I believe that once any instruction includes wz or wc then effect they would be "cleared' by default to whatever the result was. I will take it out after I am finished testing.
The ES9023DACengine shifts the data left 8 bits, then streams to the DAC in I2S mode. I am still trying to find a way to turn off the noise shaping and counter DAC output on Kye's V2 waveplayer engine, and only use the left and right data to feed my DAC engine.
Here is the I2S output:
DAT
ORG 0 'Begin at Cog RAM addr 0
DAC mov parm, PAR ' COPT PAR to asm PARM
rdlong temp, parm ' COPY the pin config PAR to PARM in ASM
add parm, #4 ' GET VAR POSITION of LeftPtr
rdlong LeftDataaddr, parm ' COPY LEFTPTR ADDRESS IN ASM
add parm, #4 ' GET VAR POSITION of RightDataPtr
rdlong RightDataaddr, parm ' COPY RightDataPTR ADDRESS IN ASM
' SET PIN DIRECTION
'or DIRA, bclkMSK
or DIRA, LRClkMSK
or DIRA, DataInMask 'data in to Dac
or dira, ledmask ' DEBUG ONLY REMOVE LED LATER
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SET LRCLOCK HIGH to start
mov OutPut, LRClkMSK '###SETUP LR establish Output
mov OUTA, OutPut '###DRIVE LR HIGH
or outa, ledmask ' DEBUG ONLY REMOVE LED LATER
WAITPEQ HIGH, bclkMSK ' park here till bitclock starts
WAITPEQ LOW, bclkMSK ' Wait for Bitclock LOW
andn OutPut, LRClkMSK '###SETUP LR LOW
mov OUTA, OutPut '###DRIVE LR
''''''andn outa, ledmask ' DEBUG ONLY REMOVE LED LATER
' First bitclock high after LR Clock change
' Sync to 48 transitions on BitClock 24 Left 24 Right
' Second bitclock High after LR Change MSB send
WAITPEQ HIGH, bclkMSK ' Wait for Bitclock HIGH
WAITPEQ LOW, bclkMSK ' Wait for Bitclock LOW
:loop ' GET THE L E F T WORD
rdlong LeftData, LeftDataaddr ' read LEFT data
shl LeftData, #8 ' shift LeftData 8 assumes a 16bit val
mov n, #23 ' Send 1-23 then change LR and send 24th
' Start Clocking out MSB bit 23 on bitclock high
' First loop is 23 times. Then change LR clock and send 24th data bit
' SHIFT OUT LeftData 24BITS start while bitclock is LOW
' RCL= Rotate C Left INTO VALUE 1 bit this shifts data out
' Data bits and LR Clock Changes on Falling Edge of BitClock
:LeftDatalp rcl LeftData, #1 WC ' shift out one data bit (MSB first)
muxc OutPut, DataInMask ' ###SETUP Data
mov OUTA, OutPut ' ###DRIVE Data
WAITPEQ HIGH, bclkMSK ' 1-23 BitCLocks of Left phase
WAITPEQ LOW, bclkMSK ' Wait for Bitclock LOW
ADD LeftData, halfperiod 'test
djnz n, #:LeftDatalp ' loop23 times decrement N and jmp if N > 0
'LR change at LSB
or OutPut, LRClkMSK ' ###SETUP LR HIGH
' Send LSB 24th bit and LR Change
rcl LeftData, #1 WC ' shift out one data bit (MSB first)
muxc OutPut, DataInMask ' ###SETUP Data
mov OUTA, OutPut ' ###DRIVE Data
''''''or outa, ledmask ' DEBUG ONLY REMOVE LED LATER
WAITPEQ HIGH, bclkMSK ' 24 BitCLocks
WAITPEQ LOW, bclkMSK ' Wait for Bitclock LOW
'GET THE R I G H T WORD
rdlong RightData, RightDataaddr ' read RIGHT data
shl RightData, #8 ' assumes 16 bit original
mov n, #23
' SHIFT OUT LeftData 24BITS
:RightDatalp rcl RightData, #1 WC
muxc OutPut, DataInMask ' ###SETUP Data
mov OUTA, OutPut ' ###DRIVE Data
WAITPEQ HIGH, bclkMSK ' 1-23 BitCLocks of Right phase
WAITPEQ LOW, bclkMSK '
djnz n, #:RightDatalp
'LR change at LSB
andn OutPut, LRClkMSK ' ###SETUP LR LOW
' Send 24th bit after LR Change
rcl RightData, #1 WC
muxc OutPut, DataInMask
mov OUTA, OutPut ' *###DRIVE LR
''''''andn outa, ledmask
WAITPEQ HIGH, bclkMSK ' 24 Tick
WAITPEQ LOW, bclkMSK '
jmp #:loop ' ready for next sample set
'Falling Edge of Bitclock is where LR/data changes states counting bits 23..0 LR changes before LSB sent
'Leading Edge of Bitclock clocks in databit to DAC
Comments
number of MCLKs leeway, so you'll need to resynchronize the the master clock regularly - I found it much
easier just to run everything from the one clock then WAITCNT/cycle counting/WAITPEQ does all the heavy lifting.
If the LRCLK and BCLK are generated for you it shouldn't be too hard to sync to them, but I can't see how you'd
sync without slipping to MCLK.
The first attempt will be to use the DAC as async mode, so no syncing to any clock, just let the PASM code render it's own timing to the DAC.
Hey guys I am stuck on about 5 hours trying to find out what is going on. I have studied the manual and looked at examples that all show the ADD cntadd value to Time as the first thing to do after moving CNT to TIME. But I also see other values used in the dest value Waitcnt Time, SomeValueHere.
I have an LED connected, and at the rate shown of 2_400_000 I can still see blinking, below this rate the LED becomes more solid. What is the purpose of the destination value in Waitcnt? I can set it to 0 to any number and it has no effect on the rate of the LED blinking. The waitcnt instruction seems to be ignoring the dest value completely as if it were just a placeholder.
My calculations are, 48 BitClocks per sample for left and right channels at 24 bits each. Each Word Select (LR Clock) phase is 192 master clock ticks each.
I intend to use a COG for reading the master clock in and outputting a bitclock to the DAC. This COG will likely not do anything else. Then a COG will watch the bitclock output pin, and divide down to produce the LR Clock output. The main COG will run the audio read from SD, and another COG will write the data bits to the DAC watching the bitclockpin and LR clock pin for timing.
For testing, I have a crude tone generator in the BitClock generator:
The ES9023DACengine shifts the data left 8 bits, then streams to the DAC in I2S mode. I am still trying to find a way to turn off the noise shaping and counter DAC output on Kye's V2 waveplayer engine, and only use the left and right data to feed my DAC engine.
Here is the I2S output: