pgbpsu
09-02-2007, 10:33 AM
I've written my first bit of Propeller assebmly code to read an I2S type ADC. My code will work, but not at the speed I need. I was hoping some of the more clever, more experienced programmers here could help me tweek this to get to the speed I need.
I have two questions regarding my first propeller project:
1. How can I speed my code up so I can read a single channel of data?
2. How difficult is it to move from 1 channel to 5?
The ADC I'm reading (AD1871) puts out a bit clock (BCLK = 6.144Mhz), a left-right clock (LRCLK = 96Khz) and data (DATA). The data are I2S-justified meaning that during the low part of LRCLK the MSB of my 24-bit Lsample comes on the second rising edge of BCLK. The next 23 rising edges of BCLK contain the rest of my Lsample. The situation is similar for the right channel. The second rising edge of BCLK after a falling edge on LRCLK contains the MSB of my Rsample. The next 23 rising edges of BCLK contain valid data. Each L and R clock contain 32 bits, of which only 24 are valid data.
So far I've only been able to read data when the ADC LRCLK is running at 80Khz (BCLK=5.12Mhz). The engineer who built the eval board for me smartly put a switchable master clock (24Mhz or 20Mhz). While LRCLK is high, I read my 24-bits and immediatly turn around and write them out in 8-bit chunks to a linux box connected to the propeller. To be sure that data are transferred correctly between the propeller and the linux box, I've ginned up a bit of handshaking. The propeller starts with FRAME pin high, indicating data are NOT valid. Once all 24 bits have been collected from the ADC, I put the lowest 8 onto my propeller output pins and clear the FRAME pin. The linux box sees this and reads the valid data. After reading the data it sets the RX_OK line high. When the propeller sees this, it raises the FRAME pin and clears the data. The FRAME pin has to stay high long enough for the linux box recognizes it and clear RX_OK. This completes one 8-bit transfer. The propeller then moves to the next 8-bit chunk. It cycles through this 4 times, before returning to read the next Lsample.
My current design only reads 25 bits of the left channel and writes them out in 8-bit chunks during the remaining 39 BCLK cycles. This seems the most basic place to start. When this is working I can tackle the problem of reading mutiple channels simultanously. Maybe this is where other cogs would be useful, but that's still beyond me. My problem is that the bit loop at the top of the code which reads the data from the ADC can't keep up with 96Khz data. It does keep up with 80Khz data. How can I speed this up? I should point out that for this to work at 80Khz there must be 3 instructions then the DJNZ command. As you an see in the code below, I've got an AND(4), RCL(4), and NOP(4) (or MUX(4) for testing). This is the only way I can get this loop to work. If I take the NOP (or MUX) out things fall apart. I've verified, using an oscilliscope, that the 24-bit data word coming from the ADC matches what the propeller gets. On the scope, the propeller word takes less total time but it's all there. The propeller seems to be running a bit faster than the incoming data, but they are close enough that the propeller doesn't skip any bits. I'm clearly wasting time in this loop just to keep things "synced". There must be a way to re-write this so I can get data at 6.144Mhz.
Suggestions on how to tweak this loop, or how to expand this code from 1 channel to 5 would be greatly appreciated.
Thanks,
Peter
{{LCD_8_.spin
This program reads 24 bit data from the AD1871 and
writes it out to be read by the TS7260 on its LCD Header
INPUTS:
LRCLK - 96kHz clock; left and right channels on low or high
BCLK - 6.144Mhz clock; data are valid on rising edge
DATA - 6.144Mhz nominal; data for BCLK
RX_OK - when this line goes high the TSS has received the data
OUTPUTS:
DATA7-DATA0
FRAME - line that goes low when ouput data are valid
}}
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
VAR
long Lsamp
PUB Main
{Launch cog to read I2S data}
cognew(@i2s, @Lsamp) 'Launch new cog
DAT
{Read data from the AD1871 and pass it on to the TS7260}
ORG 0 'Begin at cog RAM addr 0
i2s mov dira, DIRA_DEFN 'Setup output pins
'Setup input pins
' BEGIN READ/WRITE LOOP
' A BIT OF SETUP AT THE BEGINNIG OF EACH READ/WRITE CYCLE
:readloop mov outa, FRAME ' Set FRAME to indicat data are
' no longer valid, clear data
' pins
mov nbits, #25 ' set number of iterations
' first bit is junk
' next 24 are good data
mov Lsamp_, #0 ' clear Lsamp_ variable
waitpeq LRCLK, LRCLK ' wait for LRCLK to high
waitpne LRCLK, LRCLK ' wait for LRCLK to go low
' get synced with LRCLK
' so we are sure to start
' reading the first BCLK in
' this LRCLK
xor outa, SHADOW ' toggle SHADOW at LRCLK rate
' START READING 25 SAMPLES FROM ADC STORE IN Lsamp_
:bitloop and DATA, ina nr, wc ' Read datapin AND ina with
' DATA, set C=1 if odd number of
' ones in result
rcl Lsamp_, #1 ' shift C left into Lsamp
' muxc outa, SHADOW ' For testing - echo
nop ' needed to keep loop synced
djnz nbits, #:bitloop ' Decrement loop count.
' FINISHED READING INPUT DATA
' START WRITING AS 8-BIT CHUNKS
' get first group of 8 bits onto the data pins
mov t1, nibble1 ' move 0x0000_00FF into t1
and t1, Lsamp_ ' extract bits 7-0 of Lsamp_
shl t1, #16 ' shift nibble1 into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX_OK to go LOW
' get second group of 8 bits onto the data pins
mov t1, nibble2 ' move 0x0000_FF00 into t1
and t1, Lsamp_ ' extract bits 15-8 of Lsamp_
shl t1, #8 ' shift result into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX to go LOW
' get third group of 8 bits onto the data pins
mov t1, nibble3 ' move 0x00FF_0000 into t1
and t1, Lsamp_ ' extract bits 23-16 of Lsamp_
shr t1, #0 ' shift result into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX to go LOW
' get upper group of 8 bits onto the data pins
mov t1, nibble4 ' move 0xFF00_0000 into t1
and t1, Lsamp_ ' extract bits 31-24 of Lsamp_
shr t1, #8 ' shift result into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX to go LOW
' FINISHED WRITING
' mov Lsamp_, cnt
' xor outa, SHADOW
jmp #:readloop ' jump back to beginning
' DONE READING AND WRITING THIS SAMPLE JUMP BACK TO START AND DO ALL OVER
' AGAIN
' outa bits which are active outputs
DIRA_DEFN long %0000_0000_1111_1111_1000_0000_0000_0000
'PIN ASSIGNMENTS
DATA7 long |< 23 ' %1000_0000 'TS LCD_7 (out) = pin 23
DATA6 long |< 22 ' %1000_0000 'TS LCD_6 (out) = pin 22
DATA5 long |< 21 ' %1000_0000 'TS LCD_5 (out) = pin 21
DATA4 long |< 20 ' %0100_0000 'TS LCD_4 (out) = pin 20
DATA3 long |< 19 ' %0010_0000 'TS LCD_3 (out) = pin 19
DATA2 long |< 18 ' %1000_0000 'TS LCD_2 (out) = pin 18
DATA1 long |< 17 ' %0100_0000 'TS LCD_1 (out) = pin 17
DATA0 long |< 16 ' %0010_0000 'TS LCD_0 (out) = pin 16
DATABITS long DATA0 | DATA1 | DATA2 | DATA3 | DATA4 | DATA5 | DATA6 | DATA7
FRAME long |< 15 ' %0001_0000 'TS FRAME (out) = pin 15
RX_OK long |< 14 ' %0000_1000 'TS RX_OK (in) = pin 14
DATA long %0000_0100 'ADC DATA (in) = pin 2
BCLK long %0000_0010 'ADC BCLK (in) = pin 1
LRCLK long %0000_0001 'ADC LRCLK (in) = pin 0
SHADOW long %0000_0000_0000_0000_0001_0000_0000_0000 'P12 will shadow the input data
'VARIABLES
MSB long %10000000_00000000_00000000_00000000
DataWord long %0000_0001_1111_1111_0110_1111_1001_0111
'0x1FF6F97 = 33517463
'DataWord long %0000_0001_0000_0000_0000_0000_0011_0111
'0x1000037 = 55 after removing parity bits
'DataWord long %1111_1111_1111_1111_1111_1111_1111_1111
nibble1 long %0000_0000_0000_0000_0000_0000_1111_1111
nibble2 long %0000_0000_0000_0000_1111_1111_0000_0000
nibble3 long %0000_0000_1111_1111_0000_0000_0000_0000
nibble4 long %1111_1111_0000_0000_0000_0000_0000_0000
nbits res 1
average res 1
total res 1
nibbles res 1
Lsamp_ res 1
DataValue res 1
t1 res 1
I have two questions regarding my first propeller project:
1. How can I speed my code up so I can read a single channel of data?
2. How difficult is it to move from 1 channel to 5?
The ADC I'm reading (AD1871) puts out a bit clock (BCLK = 6.144Mhz), a left-right clock (LRCLK = 96Khz) and data (DATA). The data are I2S-justified meaning that during the low part of LRCLK the MSB of my 24-bit Lsample comes on the second rising edge of BCLK. The next 23 rising edges of BCLK contain the rest of my Lsample. The situation is similar for the right channel. The second rising edge of BCLK after a falling edge on LRCLK contains the MSB of my Rsample. The next 23 rising edges of BCLK contain valid data. Each L and R clock contain 32 bits, of which only 24 are valid data.
So far I've only been able to read data when the ADC LRCLK is running at 80Khz (BCLK=5.12Mhz). The engineer who built the eval board for me smartly put a switchable master clock (24Mhz or 20Mhz). While LRCLK is high, I read my 24-bits and immediatly turn around and write them out in 8-bit chunks to a linux box connected to the propeller. To be sure that data are transferred correctly between the propeller and the linux box, I've ginned up a bit of handshaking. The propeller starts with FRAME pin high, indicating data are NOT valid. Once all 24 bits have been collected from the ADC, I put the lowest 8 onto my propeller output pins and clear the FRAME pin. The linux box sees this and reads the valid data. After reading the data it sets the RX_OK line high. When the propeller sees this, it raises the FRAME pin and clears the data. The FRAME pin has to stay high long enough for the linux box recognizes it and clear RX_OK. This completes one 8-bit transfer. The propeller then moves to the next 8-bit chunk. It cycles through this 4 times, before returning to read the next Lsample.
My current design only reads 25 bits of the left channel and writes them out in 8-bit chunks during the remaining 39 BCLK cycles. This seems the most basic place to start. When this is working I can tackle the problem of reading mutiple channels simultanously. Maybe this is where other cogs would be useful, but that's still beyond me. My problem is that the bit loop at the top of the code which reads the data from the ADC can't keep up with 96Khz data. It does keep up with 80Khz data. How can I speed this up? I should point out that for this to work at 80Khz there must be 3 instructions then the DJNZ command. As you an see in the code below, I've got an AND(4), RCL(4), and NOP(4) (or MUX(4) for testing). This is the only way I can get this loop to work. If I take the NOP (or MUX) out things fall apart. I've verified, using an oscilliscope, that the 24-bit data word coming from the ADC matches what the propeller gets. On the scope, the propeller word takes less total time but it's all there. The propeller seems to be running a bit faster than the incoming data, but they are close enough that the propeller doesn't skip any bits. I'm clearly wasting time in this loop just to keep things "synced". There must be a way to re-write this so I can get data at 6.144Mhz.
Suggestions on how to tweak this loop, or how to expand this code from 1 channel to 5 would be greatly appreciated.
Thanks,
Peter
{{LCD_8_.spin
This program reads 24 bit data from the AD1871 and
writes it out to be read by the TS7260 on its LCD Header
INPUTS:
LRCLK - 96kHz clock; left and right channels on low or high
BCLK - 6.144Mhz clock; data are valid on rising edge
DATA - 6.144Mhz nominal; data for BCLK
RX_OK - when this line goes high the TSS has received the data
OUTPUTS:
DATA7-DATA0
FRAME - line that goes low when ouput data are valid
}}
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
VAR
long Lsamp
PUB Main
{Launch cog to read I2S data}
cognew(@i2s, @Lsamp) 'Launch new cog
DAT
{Read data from the AD1871 and pass it on to the TS7260}
ORG 0 'Begin at cog RAM addr 0
i2s mov dira, DIRA_DEFN 'Setup output pins
'Setup input pins
' BEGIN READ/WRITE LOOP
' A BIT OF SETUP AT THE BEGINNIG OF EACH READ/WRITE CYCLE
:readloop mov outa, FRAME ' Set FRAME to indicat data are
' no longer valid, clear data
' pins
mov nbits, #25 ' set number of iterations
' first bit is junk
' next 24 are good data
mov Lsamp_, #0 ' clear Lsamp_ variable
waitpeq LRCLK, LRCLK ' wait for LRCLK to high
waitpne LRCLK, LRCLK ' wait for LRCLK to go low
' get synced with LRCLK
' so we are sure to start
' reading the first BCLK in
' this LRCLK
xor outa, SHADOW ' toggle SHADOW at LRCLK rate
' START READING 25 SAMPLES FROM ADC STORE IN Lsamp_
:bitloop and DATA, ina nr, wc ' Read datapin AND ina with
' DATA, set C=1 if odd number of
' ones in result
rcl Lsamp_, #1 ' shift C left into Lsamp
' muxc outa, SHADOW ' For testing - echo
nop ' needed to keep loop synced
djnz nbits, #:bitloop ' Decrement loop count.
' FINISHED READING INPUT DATA
' START WRITING AS 8-BIT CHUNKS
' get first group of 8 bits onto the data pins
mov t1, nibble1 ' move 0x0000_00FF into t1
and t1, Lsamp_ ' extract bits 7-0 of Lsamp_
shl t1, #16 ' shift nibble1 into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX_OK to go LOW
' get second group of 8 bits onto the data pins
mov t1, nibble2 ' move 0x0000_FF00 into t1
and t1, Lsamp_ ' extract bits 15-8 of Lsamp_
shl t1, #8 ' shift result into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX to go LOW
' get third group of 8 bits onto the data pins
mov t1, nibble3 ' move 0x00FF_0000 into t1
and t1, Lsamp_ ' extract bits 23-16 of Lsamp_
shr t1, #0 ' shift result into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX to go LOW
' get upper group of 8 bits onto the data pins
mov t1, nibble4 ' move 0xFF00_0000 into t1
and t1, Lsamp_ ' extract bits 31-24 of Lsamp_
shr t1, #8 ' shift result into bits 23-16
mov outa, t1 ' clear FRAME and write DATA
waitpeq RX_OK, RX_OK ' wait for RX to go HIGH
mov outa, FRAME ' set FRAME and clear data
waitpne RX_OK, RX_OK ' wait for RX to go LOW
' FINISHED WRITING
' mov Lsamp_, cnt
' xor outa, SHADOW
jmp #:readloop ' jump back to beginning
' DONE READING AND WRITING THIS SAMPLE JUMP BACK TO START AND DO ALL OVER
' AGAIN
' outa bits which are active outputs
DIRA_DEFN long %0000_0000_1111_1111_1000_0000_0000_0000
'PIN ASSIGNMENTS
DATA7 long |< 23 ' %1000_0000 'TS LCD_7 (out) = pin 23
DATA6 long |< 22 ' %1000_0000 'TS LCD_6 (out) = pin 22
DATA5 long |< 21 ' %1000_0000 'TS LCD_5 (out) = pin 21
DATA4 long |< 20 ' %0100_0000 'TS LCD_4 (out) = pin 20
DATA3 long |< 19 ' %0010_0000 'TS LCD_3 (out) = pin 19
DATA2 long |< 18 ' %1000_0000 'TS LCD_2 (out) = pin 18
DATA1 long |< 17 ' %0100_0000 'TS LCD_1 (out) = pin 17
DATA0 long |< 16 ' %0010_0000 'TS LCD_0 (out) = pin 16
DATABITS long DATA0 | DATA1 | DATA2 | DATA3 | DATA4 | DATA5 | DATA6 | DATA7
FRAME long |< 15 ' %0001_0000 'TS FRAME (out) = pin 15
RX_OK long |< 14 ' %0000_1000 'TS RX_OK (in) = pin 14
DATA long %0000_0100 'ADC DATA (in) = pin 2
BCLK long %0000_0010 'ADC BCLK (in) = pin 1
LRCLK long %0000_0001 'ADC LRCLK (in) = pin 0
SHADOW long %0000_0000_0000_0000_0001_0000_0000_0000 'P12 will shadow the input data
'VARIABLES
MSB long %10000000_00000000_00000000_00000000
DataWord long %0000_0001_1111_1111_0110_1111_1001_0111
'0x1FF6F97 = 33517463
'DataWord long %0000_0001_0000_0000_0000_0000_0011_0111
'0x1000037 = 55 after removing parity bits
'DataWord long %1111_1111_1111_1111_1111_1111_1111_1111
nibble1 long %0000_0000_0000_0000_0000_0000_1111_1111
nibble2 long %0000_0000_0000_0000_1111_1111_0000_0000
nibble3 long %0000_0000_1111_1111_0000_0000_0000_0000
nibble4 long %1111_1111_0000_0000_0000_0000_0000_0000
nbits res 1
average res 1
total res 1
nibbles res 1
Lsamp_ res 1
DataValue res 1
t1 res 1