View previous topic :: View next topic |
Author |
Message |
temtronic
Joined: 01 Jul 2010 Posts: 9097 Location: Greensville,Ontario
|
|
Posted: Thu Feb 08, 2018 2:36 pm |
|
|
Can't answer directly as I don't use that PIC but.....
There are 2 modes for SPI.
Hardware SPI, uses the HW spi perihperal in PIC.
Software SPI, emulates spi via software.
If you use the I/O pins the HW SPI is attached to, the compiler should use HW SPI. If you dump the listing it'll be obvious. HW SPI acceses SPI registers, code is small compared to SW SPI.
perhaps MR T will respond on the 'math' and 'SPI 'option' selections.
I'm stuck getting snowblower ready for another 4" of 'wet white'...sigh, 40 days until Spring.
You probably need to use one PIC for the 'LED controller' and a 'master PIC' to do the rest....
just ideas....
Jay |
|
|
Woody
Joined: 11 Sep 2003 Posts: 75 Location: Warmenhuizen - NL
|
|
Posted: Thu Feb 08, 2018 3:11 pm |
|
|
In the 16F18326 the SPI pins (SDO/SCK) are not attached to pins by default. They all need to be set using #pin_select.
I'd say the hardware SPI is used; a spi_write(x) translates to 6 instructions that select bank 4 and write out x to register 11, SSP1BUF.
I surmise that Mr T used a much faster PIC. Apart from that I need to take another look at the quick and dirty way I created the half-nibbles; that might account for part of the slowdown.
Take care with the snowblower! And cherish the snow :-) Our winter started the day before yesterday (-6C in the night) and will be over this Saturday when the night temperature is expected to be above 0C.
Paul |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19215
|
|
Posted: Fri Feb 09, 2018 1:07 am |
|
|
On timings, I tend to forget the 'limitations' of the 8bit PIC's.
Over 60% of the stuff I do now uses the DsPIC's, and on these I also have some 'specials' from MicroChip, that implement faster clocks for SPI than they officially support (these will probably appear in a few months on 'release' chips). I've actually got SPI running now at over 20MHz from some of these...
This though is also why when designing a project I always type out the clock timings, and usually incorporate this into a header file for the particular project. It's too easy to get a timing wrong. |
|
|
Woody
Joined: 11 Sep 2003 Posts: 75 Location: Warmenhuizen - NL
|
|
Posted: Fri Feb 09, 2018 2:19 am |
|
|
That clears it up. I really liked your idea of misusing the SPI to generate the bitstream for the LED's but alas this is slower than bit banging. Fortunately fast clocks seem to trickle down to the 8 bit family so maybe I can get away eventually without resorting to using dsPIC's :-)
I came across an application note (AN1606) that explains how one could use SPI in combination with PWM and CLC to generate the precise timing for these LED's. As the 16F18326 has plenty of these peripherals I'll give that a go and see how that compares.
I find PIC timing rather complex. Although in the habit of over commenting what I do I still get bitten by it every now and then. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19215
|
|
Posted: Fri Feb 09, 2018 2:42 am |
|
|
'Slower than bit banging'?.
Shouldn't be.
The 'fast' way to generate this, is to pre-store the data patterns that have to be SPI sent, as an array. I do it by the nibble. So one nibble of the source selects a line in the array, which is four bytes to send. The array has 16*4 entries. The PIC has a 'swap' instruction that switches the high and low nibbles, so you can just re-use the same array for the high nibble.
You've already got the timing working, but I suspect you are wasting a lot of time on generating the patterns. The point is that with the SPI you can be loading the next byte while the first is sending, and if you just pull the data from the array like this, you can send the data really fast.
You can also use a divider of 0 with the Timer2, which doubles the clock rate from what you show. |
|
|
Woody
Joined: 11 Sep 2003 Posts: 75 Location: Warmenhuizen - NL
|
|
Posted: Fri Feb 09, 2018 3:50 am |
|
|
Yep, I spend 5us to translate the data from byte to half-nibble and into the SPI buffer and another 9us to get the next byte out of the LED array. So 24us / byte or 17ms out of the 26ms of the total transfer time it takes for 720 bytes (240 LED's / 3 colors). There certainly is a lot to gain there.
Use Timer 2 with divide by 0? How do I do that? AFAIK I can prescale with DIV_BY_1 as a minimum (also in the DS), which gives me Fosc /4.
The T2 period goes as low as 0x01. 0x00 gives me no output. What am I missing?
Paul |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19215
|
|
Posted: Fri Feb 09, 2018 6:53 am |
|
|
It is the division, not the prescaler. It sounds as if chips differ on this. I found on another chip a while ago, that it would accept 0. It gives a match on the very first clock cycle, and resets on the next, so gave division by 1.
The way to do the translation is with an array as already mentioned. You just have a two dimensional array with 4 elements per line, and use val & 0xF as the row index, then 0, 1, 2, 3 for the column index to send the bytes. Then swap(val), and repeat to handle the high nibble. |
|
|
Woody
Joined: 11 Sep 2003 Posts: 75 Location: Warmenhuizen - NL
|
|
Posted: Tue Feb 13, 2018 11:33 am |
|
|
Ha! I spend the best part of today understanding the @!!$@& CLC in the 16F18326 and finally wrestled it into submission
Now when I send a byte to the SPI device this results in 8 bits out of the CLC magic that are 340ns high/780ns low for a 0 and 680ns high/560ns low for a 1. All neatly within the limits of the WS2812b.
A byte takes around 9.5us depending on the number of 1's and 0's it contains. A complete led word (3 bytes + getting the data out of the array) takes a little over 39us. A 240 led string takes under 10 ms to write from the 720 byes long buffer.
The important bits:
Code: |
// Setup clock
#use delay(internal=32000000)
// Setup output pin for WS2812b data in
#pin_select CLC1OUT = PIN_C0
// Setup timer2
setup_timer_2(T2_DIV_BY_1,0x04,1);
// setup pwm
setup_pwm5(PWM_ENABLED|PWM_TIMER2);
set_pwm5_duty(0x0009);
// Setup CLC
setup_clc1(CLC_ENABLED|CLC_MODE_AND_OR);
setup_clc2(CLC_DISABLED);
setup_clc3(CLC_DISABLED);
setup_clc4(CLC_DISABLED);
clc1_setup_input(1,CLC_INPUT_SCL1);
clc1_setup_input(2,CLC_INPUT_SDA1);
clc1_setup_input(3,CLC_INPUT_PWM5);
clc1_setup_input(4,CLC_INPUT_CLCIN0);
clc1_setup_gate(1, CLC_GATE_NON_INVERTED_INPUT_1);
clc1_setup_gate(2, CLC_GATE_NON_INVERTED_INPUT_2);
clc1_setup_gate(3, CLC_GATE_NON_INVERTED_INPUT_1);
clc1_setup_gate(4, CLC_GATE_NON_INVERTED_INPUT_3);
|
I have some questions left as I took a slightly different approach than laid out in AN1606. There two CLC units are used and nSDO is ANDed with SCK and PWM to make the 0 bits, where I use SDA (SDO) AND PWM only. This means I always generate a 0 bit with the width of the PWM signal in the middle of a 1 bit. But as the 1 bit is wider that does not show in the output signal. But I have no idea if I wreak havoc with this approach. So far it works great.
Paul |
|
|
|