View Full Version : Faster Serial Data Sending

03-25-2009, 04:39 AM
I am using the Propeller to serially drive 2 A6762 ICs to light up some LEDs.· I have not been able to get the LEDs to function (doing more than just staying lit or not turning on) without stalling for 381 clock cycles, which really limits my ability to fade colors.··I have been·loading·data to the·A6276 serially. ·Is there a way I can do this faster?· Here is my code.

PUB writeLong( _illuminate ) | illuminator
··· templumination := _illuminate
··· outa[outputenable] := 1················· 'disable LED output
··· illuminator := 0······
··· repeat illuminator from 0 to 15··········· ' sends eight bits as serial communication while pulsing latch enable

······ outa[serialoutpin] := templumination······· ' set data out pin to data
······ templumination >>= 1······················· ' shift to next bit····
······ outa[clockpin] := 1····················· ' set clock pin High
······ outa[clockpin] := 0····················· ' set clock pin Low
······ outa[latchenable] := 1·················· ' set latch enable high
······ outa[latchenable] := 0·················· ' set latch enable low

··· outa[outputenable] := 0················· 'enable LED output

03-25-2009, 07:53 PM
Of course. You can do it in PASM.

I don't really know the mentioned IC, but do you really have to set the latch enable to high/low after each bit? I'd expect that you first shift in your 16 bits and then do the latch-cycle.
Do you want the LEDs have different brightness? Otherwise it might be a solution to let a counter do the dimming by enable/disable the shift register outputs with PWM.

03-26-2009, 03:19 AM
Now I had a look into the datasheed of the A6276.

What you have to do is to shift 16 bits into your 2 registers by:
1. Set/Clear the data out pin
2. Wait for 50ns
3. Set clock to high
4. Wait for 50ns
5. set clock to low
6. Wait for 50ns
7. Repeat with 1 until the 16 bits have been shifted
8. Wait for 50ns
9. set latch to high
10. wait 100ns
11. set latch to low

Of course this is only the minimum time you have to wait. If your program is slower (and spin is much slower) there is no problem. With PASM it's possible to shift the data with the maximum speed. That would allow round about 300000 updates per second. Enough for a PWM.

The general brightness can be controlled by the output enable as well.

04-11-2009, 11:04 PM
Thanks Mag, I'll try exactly what you said. But, I do not know how to run PASM. Can you tell me a little bit about PASM and how I can use it for this. I have looked online a little, and I haven't found much about it.

Thanks a lot,


04-12-2009, 01:42 AM
PASM is of course described in the propeller manual. You start a PASM program with the cognew command. Instead of the name of a function you give it the adress of the PASM program and one parameter. If you need more parameters, you have to store them in a variable-block and pass the adress of the first parameter.

cognew( @A6762_driver, @parameter_block)

As you see in the name, for a lot of devices that need to be driven by PASM-code, the model of a driver is used. The driver is started once and runs in the COG for the whole time. You use the parameter block to pass commands to it and for data-exchange.

Well, the implementation depends a little bit on what you want/need. 50ns waittime means you don't have to wait in the propeller at all because runtime of one instruction is exactly 50ns. So, I would simply repeat the sequence set pin, clock high, clock low 16 times. If you use a loop you loose speed for jumping.

Here is the first bunch of code. This is currently only test code, not driving an A6276. But it's the sceleton of the dirver code. The only part which is missing is shifting out the signals.

Please note, I use a 10MHz crystal, so please change the constants accordingly, if you want to run the code. The code is build for driving 9 LED's on pins 0-8. If you remove/change the lines marked with comment 'debug code' you can adopt it to your setup.

_clkmode = xtal1 + pll8x
_xinfreq = 10_000_000
long yep
' this is the parameter variable for communication with COG
long A6276_par

pub PASMstore
A6276_par.word[1] := $0010 ' setting the waittime of the COG until it reads
' the next value from parameter variable
A6276_par.byte[0] := $02 ' set clock pin (0-31)
A6276_par.byte[1] := $01 ' set output pin (0-31)
' A6276_par := $0000_01_02 ' alternative way to do the same
dira[8]:=1 ' debug code

cognew( @A6276_driver, @A6276_par ) ' start the driver
repeat while A6276_par ' and wait until startup is complete
!outa[8] ' debug code
yep := 0 ' test code
repeat '
A6276_par:=yep ' set the parameter
repeat while A6276_par ' and wait for sync
!outa[8] ' debug code
waitcnt( clkfreq/4+cnt)
org 0
mov dira, #$ff ' debug code
mov ppointer, par ' store the pointer to the parameter
rdlong parameter, ppointer ' and read the parameter from HUB-RAM
' high word of parameter is the waittime
' maybe it makes sense to shift the value for smaller times
' or maybe use the log-table
mov waittime, parameter ' make a copy of the parameter
and waittime, highword WZ ' and extract the waittime word
if_z mov waitornot1, nowait1 ' if waittime is zero, remove waitcnt from code
if_z mov waitornot2, nowait2
' prepare the pin-masks for output-pin and clock-pin
mov temp, parameter
shr temp, #8 ' extract the high byte of low word
and temp, #$1f
shl outpin, temp
mov temp, parameter
and temp, #$1f ' extract the low byte
shl clkpin, temp
' set the two pins to output
or dira, outpin
or dira, clkpin
wrlong zero, ppointer ' show caller that we're done with reading parameter
A6276_loop1 mov time, cnt ' calculate the counter value to wait for
add time, waittime ' if waittime was zero it's uselss, but no problem
A6276_loop2 rdlong parameter, ppointer WZ ' see if we have to send a value
waitornot1 if_z waitcnt time, waittime ' if waittime was given, then wait
nowait1 if_z jmp #A6276_loop2
' if a new value is found, then start sending to A6276
' ************************************
' this part is missing currently
' ************************************
mov outa, parameter ' debug code

wrlong zero, ppointer ' show caller that we're done with sending
waitornot2 jmp #A6276_loop1 ' if waittime was given, go to calc next counter
nowait2 jmp #A6276_loop2

ppointer long 0
parameter long 0
time long 0
waittime long 0
zero long 0
highword long $ffff_0000
outpin long $0000_0001
clkpin long $0000_0001
temp long 0
A6276_end long 0

The functionality currently is to get the startup-parameters, which are all passed in one long variable. The high-word of the long is the wait-time. If a value other than 0 is given there, the driver waits after each unsucessful check of the parameter. A wait will decrease power consumption. If the parameter is 0, the startup routine moves some code to skip waiting.

In the two bytes of the low-word the pins have to be passed.

For synchronization the parameter is set to zero as soon as the driver is done with the work. If you have other things to do there is of course no need to sync.

The word to be send has to be passed in the high word. As the value can be zero and zero means 'nothing to do' for the driver, at least one bit of the low word should be set to start transmission.

Try to check out how this code works. Maybe you can try to do the rest by yourself. I'll continue tomorrow.


Post Edited (MagIO2) : 4/11/2009 10:58:54 PM GMT

04-15-2009, 01:13 AM
Thanks a lot Mag,

I have deleted the extra latching and unlatching each loop iteration , but I am still having the same problem. I am trying to decipher the code that you have here, but I do not completely understand all of it. I am having problems with the PASM. The manual is not very demonstrative in its explanations of Pasm code. I have a few questions as well that may give you a better idea:

1) I see that you have the _xinfreq at 10_000_000. Is this from the A6276?

2) I do not know how to invoke PASM coding from another method or from another object. I know how to invoke methods from other or the same object in Spin. Can you tell me the difference.

3) I think that I am having problems somewhere in my data manipulation between sending serial data to the A6276. I can get the LEDs to blink at rates faster than the eye can detect when I just send some 1's and then all zeros in a repeating loop with minimal delay. But, when I try to manipualte the bits before sending them out, there seems to be some problem that causes ther to be a minimum wait time between iterations. Is spin too slow even at 80MHz? Do I need to do all the manipulations AND the sending of the data to the A6276 in PASM instead?

Thanks a lot.


04-15-2009, 02:27 AM
Hello Glenn,

a cog can run PASM OR SPIN.

it is NOT possible to mix PASM and SPIN on ONE cog
The reason is if you execute SPIN the SPIN-interpreter occupies the COMPLETE RAM of the cog (2kB).
SPIN-bytecode is stored in the HUB-RAM (32kB) The spin-interpreter fetches bytecode from HUB-RAM and
is interpreting it running PASM-interpreter code

PASM has to be in COG-RAM for execution

PASM is low-level programming compared to SPIN
The commands that you have are much simpler than a
"repeat X from 0 to 255"
"IF Var1 > 10) and (Var2 == 70) or (Var3 < 10)"

To see if it could be done in SPIN it would be interesting how many bits you have to shift-out as maximum
and if some calculations have to be done
maybe it is possible to do the calculations in one cog and the shifting-out in a second cog or a second method

best regards


04-15-2009, 03:05 AM
1) _XINFREQ is set to 10_000_000 because I have the propeller running with a 10MHz crystal. That's why I have to set the _CLKMODE to 8x . 8x10_000_000 = 80_000_000 which is the speed that the propeller is running internally. Most other setups have a 5MHz crystal. With _CLKMODE set to 16x this is also 80MHz internal speed.

2) cognew is starting a new COG with the SPIN or PASM code you hand as first parameter. In SPIN you simply use the name of the SPIN-method. In PASM you pass the adress of the code using the @ as adress operator. But the PASM code I gave you is only called during initialisation at startup-time. From this time on, the PASM code is running endlessly. You communicate with it by using the A6276_par variable. What it really does still has to be defined in the ' this part is missing currently' -part.
So the difference is that if you are using a different COG for shifting out the bits to the A6276, your main program can go on without waiting and do something else. The A6276 code running in that COG of course can be in SPIN as well, but PASM will do it as fast as possible.

3) In your original post you said you want to fade colors. The A6276 will drive all LED's with a constant current, so you have to use PWM for fading. For a good PWM you will need a minimum speed. For a 8 bit PWM at a refresh rate of 40Hz and with independent PWM values per LED you need to send 256x40x16 bits per second (163840 bits/second). And a 8 bit PWM is not what you would call 'full color fading'. The problem is the eye, which does not recognizes the brightnes linear to the light emitted. So the effective number of different brightness values will be much smaller than 256.