PDA

View Full Version : Faster SHIFTOUT without dedicated hardware, possible?



CuriousOne
- 27th July 2022, 11:22
Hello, I'm driving APA102 LEDs with shiftout, and while for on-off speed is ok, when I want to do some cool and fast color change, speed is not enough.
So as I understand, SHIFTOUT is just making data pin high-low with required bits, synced with clock pulses, right?
So maybe there's a code which can do this at faster rate, than shiftout does? without using MSSP, 18F series and so on.

tumbleweed
- 27th July 2022, 12:23
Sure.

Here's some code that would shift out a byte without using asm or anything special... should be much faster than SHIFTOUT since it's format is dedicated and loop is unrolled. Just set WREG to the value to shift out and call SHOUT.



SHCLK var PORTB.0
SHDAT var PORTB.1

; init pins to output low
low SHCLK
low SHDAT

; send $55
WREG = $55
call SHOUT

; send $AA
WREG = $AA
call SHOUT


; SHOUT
; shift out 8 bits of data, LSB to MSB
; data clocked on rising edge of SHCLK
; WREG = data to send
SHOUT:
SHDAT = 0
if WREG.0 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.1 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.2 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.3 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.4 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.5 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.6 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.7 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

return


That would be the equivalent of
SHIFTOUT SHDAT, SHCLK, LSBFIRST, [bval/8]

tumbleweed
- 27th July 2022, 12:57
You may find that routine is actually too fast for the hardware you're controlling.
If so, you can add NOPs to slow it down a bit...


SHDAT = 0
if WREG.0 then
SHDAT = 1
endif
asm NOP endasm
SHCLK = 1
asm NOP endasm
SHCLK = 0
asm NOP endasm

CuriousOne
- 1st August 2022, 17:24
Thanks, that's great!
So if we can replace SHIFTOUT with faster code easily, maybe there is a such way for I2CREAD too? without using MSSP module?

tumbleweed
- 2nd August 2022, 00:11
I2C operations are slower to begin with, but you could speed things up over the stock routines.

Having the pins be set in software is what usually slows things down. I've implemented these before and ended up with something around 240KHz with programmable pins.
Dedicated pins speeds it up a lot... think I got around 400KHz doing it that way (running at 64MHz).

You have to watch out with the speed and open-drain IO operation to size the pullups as required.
For faster speeds I like to use active pullups which can really speed up the low-to-high transitions vs using a resistor.
This works out well when using the MSSP since you can get much faster speeds there.

CuriousOne
- 2nd August 2022, 06:21
Well, I tried to use MSSP, but no luck, so this is why I'm asking.
Current I2CREAD is too slow, it runs at about 50khz (and is NOT oscillator dependent, as someone stated, I checked with scope, it is around 50khz at 4mhz, 8mhz or 16mhz oscillator speed).
200Khz would be fine for me. I'm using 24C64 as an external font data storage, and at current speed (50khz), reading 1kb of data takes about 1 second, which does looks cool at certain moment - you see how letters are being drawn on the screen, but absolutely not cool for long-time usage.

tumbleweed
- 2nd August 2022, 13:54
which PIC device are you using?

looking at some old posts, this thread here http://www.picbasic.co.uk/forum/showthread.php?t=5568 has a number of issues.

CuriousOne
- 8th August 2022, 05:49
Just got to PC today and wanted to run your code, but I have a little problem, my current code looks like this:

SHIFTOUT SDA, SCK, 0, [$C0+row,col]

It sends 2 bytes in a row. How should I do the same with your code?

tumbleweed
- 8th August 2022, 12:43
Just send each byte individually... the result will be the same since SHIFTOUT really does it that way too.


WREG = $C0 + row
call SHOUT

WREG = col
call SHOUT


If you're using a PIC16 then you'll have to add a definition for a WREG variable


WREG var byte


If you wanted to make the code the same for all devices then change all the references to WREG to a byte variable instead, including the SHOUT subroutine.
Here I've named it SHDATA.


SHDATA var byte

SHDATA = $55
call SHOUT

CuriousOne
- 11th September 2022, 19:40
Just tried this code and it works, thanks!
But need to use it for shiftout 1 - MSBFIRST.
What should be changed in it?

tumbleweed
- 12th September 2022, 00:13
For MSBFIRST, just change the order of the 'WREG.bit' tests


; SHOUT
; shift out 8 bits of data, MSB to LSB
; data clocked on rising edge of SHCLK
; WREG = data to send
SHOUT:
SHDAT = 0
if WREG.7 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.6 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.5 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.4 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.3 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.2 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.1 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

SHDAT = 0
if WREG.0 then
SHDAT = 1
endif
SHCLK = 1
SHCLK = 0

return

CuriousOne
- 8th October 2022, 14:33
Thank you again!
Only today I managed to adapt this code to my hardware and it works very fast!

CuriousOne
- 8th October 2022, 14:35
But I have a question, WREG is not an variable, but a register name, right?

tumbleweed
- 8th October 2022, 16:26
You're right, for a PIC18 WREG is the name of a register.

If you're using a PIC16 then you have to declare a variable named WREG as I mentioned in post #9



If you're using a PIC16 then you'll have to add a definition for a WREG variable


WREG var byte


If you wanted to make the code the same for all devices then change all the references to WREG to a byte variable instead, including the SHOUT subroutine.
Here I've named it SHDATA.


SHDATA var byte

SHDATA = $55
call SHOUT

CuriousOne
- 8th October 2022, 17:19
It works fine without definition on PIC16F1828.
In fact, if I add definition, compiler gives an error - Redefiniton of VAR
This is why I asked :)

tumbleweed
- 9th October 2022, 00:03
If you're using an enhanced midrange PIC16 device (like the PIC16F1828) then it has a WREG, so it works with them too without having to declare a 'WREG' variable.

It's only for the older PIC16's you'd have to define it, or just change all the references to WREG to a new variable name of your choice.

CuriousOne
- 16th October 2022, 09:17
The above code works fine with TM1629A display.
But it does not work properly with APA102C leds.
I have the following statement:

SHIFTOUT di, ci, 1,[224+Bri[x],COLORS[X+16],COLORS[X+8],COLORS[X]]

Which I have replaced with

wreg=224+Bri[x]
gosub shout
wreg=COLORS[X+16]
gosub shout
wreg=COLORS[X+8]
gosub shout
wreg=COLORS[X]
gosub shout

The issue is that only odd bytes got transferred.
Say if I set some value to 2 or 4 it gets delivered to LEDs, but if I set it to 1 or 3 or 5 - nothing happens.
Tried to insert pause between each subroutine call - no difference

CuriousOne
- 16th October 2022, 09:19
I also tried to add some NOPs as suggested above - no difference.

tumbleweed
- 16th October 2022, 12:36
Don't know what to tell you... the routine transfers any values for WREG so there's no difference between odd/even values.

Must be some issue with the APA102C timing/format.

CuriousOne
- 17th October 2022, 05:51
I will hook up scope and check both "versions", to find the possible issue.