|
|
View previous topic :: View next topic |
Author |
Message |
maxikys
Joined: 25 Jul 2013 Posts: 17
|
WS2811 and PIC16F1824 trouble!!! |
Posted: Wed May 28, 2014 1:33 am |
|
|
Hi everyone. I'm trying to develop a device which converts the protocol of DMX-512 control protocol 20 pixels based on chip WS2811.
I was faced with the difficulty of correctly display colors. Code does not work.
Forum looked through all the threads about the WS-2811. But doing something wrong.Compiler Version 5.15.
Here is the code that should extinguish and light all the LEDs:
Code: |
#include <16F1824.h>
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES WDT_SW //No Watch Dog Timer, enabled in Software
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#bit SDOSEL = 0x11D.6
#use delay(internal=32000000)
#USE SPI (MASTER, SPI1, BAUD=1600000, MODE=0, BITS=8,MSB_FIRST)
#define LED_STRIP_LEN 20
unsigned int32 led_strip_colors[LED_STRIP_LEN];
#define ws2811_zero 0b10000000
#define ws2811_one 0b11110000
void main(void) {
enable_interrupts(GLOBAL);
//setup_oscillator(OSC_8MHZ|OSC_normal|OSC_PLL_ON,0);
unsigned int16 i;
unsigned int j;
//setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_4 );
//SDOSEL=1;
while(true){
output_toggle(PIN_A5);
for(i=0;i<480;i++) {
spi_write(ws2811_zero);
}
delay_ms(1000);
for(i=0;i<480;i++) {
spi_write(ws2811_one);
}
delay_ms(1000);
}
}
|
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed May 28, 2014 3:02 am |
|
|
I never used the WS2811 chips before so I did a quick study:
The chips like the WS2811 do clock in the data when the data line goes from high to low, this means there is no separate clock line needed. The chips see the difference between a '0' and a '1' by the difference in time the data line is high.
Because of the tight timing requirements you can use a hardware module like SPI or UART to generate the bit stream. You have chosen SPI. Great.
A '0' is sent as 0.5us high and 2.0us low. (+/- 150ns on each)
A '1' is sent as 1.2us high and 1.3us low. (+/- 150ns on each)
As SPI sends 8 bits at a time and all with equal timing in the bits you will have to come up with a trick: send data at 8 times the required speed and define two different patterns that will create these timing sets.
bit 0: 11000000 will create 0,625us high (just within spec for max. 0,65us high)
bit 1: 11110000 will create 1,250us high
First problem in your code is that you have defined a SPI baud rate of 1.6M, this equals to a bit frequency of 1.6M/8 = 200kHz. This is too low. Only allowed bit rates are 400kHz or 800kHz. Change SPI to 3.2Mbaud.
Another possible problem is that for the '0' you have chosen a bit pattern '10000000' which equals a high time of 0,325us, just a tiny bit out of specifications. Better change it to '11000000'. |
|
|
maxikys
Joined: 25 Jul 2013 Posts: 17
|
|
Posted: Wed May 28, 2014 3:26 am |
|
|
ckielstra wrote: | I never used the WS2811 chips before so I did a quick study:
The chips like the WS2811 do clock in the data when the data line goes from high to low, this means there is no separate clock line needed. The chips see the difference between a '0' and a '1' by the difference in time the data line is high.
Because of the tight timing requirements you can use a hardware module like SPI or UART to generate the bit stream. You have chosen SPI. Great.
A '0' is sent as 0.5us high and 2.0us low. (+/- 150ns on each)
A '1' is sent as 1.2us high and 1.3us low. (+/- 150ns on each)
As SPI sends 8 bits at a time and all with equal timing in the bits you will have to come up with a trick: send data at 8 times the required speed and define two different patterns that will create these timing sets.
bit 0: 11000000 will create 0,625us high (just within spec for max. 0,65us high)
bit 1: 11110000 will create 1,250us high
First problem in your code is that you have defined a SPI baud rate of 1.6M, this equals to a bit frequency of 1.6M/8 = 200kHz. This is too low. Only allowed bit rates are 400kHz or 800kHz. Change SPI to 3.2Mbaud.
Another possible problem is that for the '0' you have chosen a bit pattern '10000000' which equals a high time of 0,325us, just a tiny bit out of specifications. Better change it to '11000000'. |
Hi "ckielstra". Thanks for the answer.
1. I changed Baud = 3200000, all LEDs started to blink, but the module number 8 was the blinking red and white.
2. When changed to "0" - "0B11000000" LEDs ceased to fade. |
|
|
maxikys
Joined: 25 Jul 2013 Posts: 17
|
|
Posted: Wed May 28, 2014 6:29 am |
|
|
Using the example of the link http://www.insomnialighting.com/code/ws2811.c
I have the color change with the first test of WS2811. Other modules do not respond, the data on the DO pin persist.
Code: |
#include <16F1824.h>
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES WDT_SW //No Watch Dog Timer, enabled in Software
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#bit SDOSEL = 0x11D.6
#use delay(internal=32000000)
#USE SPI (MASTER, SPI1, BAUD=3200000, MODE=0, BITS=8,MSB_FIRST)
#define LED_STRIP_LEN 20
unsigned int32 led_strip_colors[LED_STRIP_LEN];
#define ws2811_zero 0b10000000
#define ws2811_one 0b11110000
void single_color(unsigned int32 color1) {
unsigned long i;
for(i=0; i<LED_STRIP_LEN; i++) {
led_strip_colors[i] = color1;
}
}
void dual_color(unsigned int32 color1, unsigned int32 color2) {
unsigned long i;
for(i=0; i<LED_STRIP_LEN; i++) {
if((i%2) == 0) led_strip_colors[i] = color1;
if((i%2) == 1) led_strip_colors[i] = color2;
}
}
void tri_color(unsigned int32 color1, unsigned int32 color2, unsigned int32 color3) {
unsigned long i;
for(i=0; i<LED_STRIP_LEN; i++) {
if((i%3) == 0) led_strip_colors[i] = color1;
if((i%3) == 1) led_strip_colors[i] = color2;
if((i%3) == 2) led_strip_colors[i] = color3;
}
}
void send_frame() { //clever, cleaner way of doing this. Thanks to the help on the CCS message boards. Test a singular bit, and then march the bits to the left..
unsigned int16 i;
unsigned int32 this_led;
unsigned int bit_;
for(i=0;i<LED_STRIP_LEN;i++) {
this_led = led_strip_colors[i]; // assign this LED's color to a throwaway variable
for(bit_=0;bit_<24;bit_++) {
if(bit_test(this_led, 23)) { // test fixed bit 23
spi_write(ws2811_one);
}
else {
spi_write(ws2811_zero);
}
this_led*=2; // march all the bits one position to the left so the 22nd bit is now in the 23rd position, etc..
}
}
delay_us(100); // wait 50us to latch the string
}
void main(void) {
unsigned int16 i;
unsigned int j;
while(true){
single_color(0xFFC58F);
send_frame();
delay_ms(1000);
single_color(0xFF9329);
send_frame();
delay_ms(1000);
single_color(0xFF0000);
send_frame();
delay_ms(1000);
single_color(0x00FF00);
send_frame();
delay_ms(1000);
single_color(0x0000FF);
send_frame();
delay_ms(1000);
tri_color(0xFF0000,0xFFC58F,0x0000FF);
send_frame();
}
}
|
Who has what opinion? |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Wed May 28, 2014 8:46 am |
|
|
ckielstra already covered the big issues --- so next is:
Do you have a scope to monitor the serial stream?
(I have working code for the WS2812 -- so I can tell you it works)
Monitoring what you're doing is crucial when tight timing is involved. _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed May 28, 2014 11:53 am |
|
|
When the first few modules work but fail for the modules later in the chain this is an indication for timing problems.
A Bkamen indicated it would help a lot when you can do actual timing measurements. Right now we are guessing. In the end we might get lucky and guess right but it is not a professional approach.
Do you have access to a scope?
One luring problem is the use of CCS software SPI library being mixed with hardware SPI. The website where you copied the code from already has this problem, but you deleted the line: Code: | setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 ); | which makes things a bit worse.
CCS did a poor job in documenting that you either use the software SPI library with '#use spi' and spi_xfer() or use the hardware based routines with setup_spi and spi_write().
Personally I prefer the hardware based routines as then I know what is happening. For your timing sensitive project it is important to use the hardware based routines.
So, delete the '#use SPI' line and restore the deleted setup_spi line (you will have to adapt the clock divider for your frequency, I leave that up to you). |
|
|
maxikys
Joined: 25 Jul 2013 Posts: 17
|
|
Posted: Fri May 30, 2014 12:32 am |
|
|
Thanks for the tips guys, I pulled the string Code: | # USE SPI (MASTER, SPI1, MODE = 0, BITS = 8, MSB_FIRST) | , but nothing has changed - the first LED working.
Dear bkamen, could you share a piece of working code ...
Maybe the whole thing is that my frequency of quartz 64 MHz and 32 MHz. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri May 30, 2014 2:06 am |
|
|
Time, time, time.....
First thing. With SPI on the PIC, there are two _separate_ ways of handling things. The original 'hardware only' mode, using setup_spi, and spi_read/spi_write. Then the newer 'hardware or software' mode, which uses #use spi, and spi_xfer. These should not be mixed. The manual does not make this clear, but if you look at the #use spi entry, it refers you to the spi_xfer entry, and _not_ to the spi_read and spi_write entries. Similarly, if you look at the setup_spi entry, this refers you to the spi_read, and spi_write entries, _not_ to spi_xfer. They are distinctly different libraries, and should be kept apart....
Ckielstra has pointed this out.
Unfortunately, the #use spi library, will not tell you if it cannot achieve the timings you are asking for. Hence if timing is critical, you need to check these yourself.
Now, that having been said, the code you started with, used the setup_spi form. The timings are more complex than it may at first appear, since the clock rate is not the SPI rate. The way this chip is handled, it uses two different pulse widths to generate 0 or 1. The SPI interface is being 'cheated' to generate this, by using eight SPI bits for each bit in the stream to the chip. It sends either a single '1' at the SPI rate, or four 1's at this rate, to give a 4:1 ratio in the times. 250nSec, or 1uSec at the original clock rate. The data sheet does not list the timing errors accepted at the high rate (only giving them for the low clock rate), and while the '0' time is spot on, the '1' time is rather long.
Now the problem is that at half the clock rate, you cannot generate this timing. The prescalers from the master clock for the SPI, are 4, 16, and 64 (steps of 4*), and you are changing the clock rate by 2*. So, you need to step back and see how to generate the required rate. Unfortunately on your chip at 32MHz, you can't generate this timing.
The 3.2MHz rate being suggested, would be ideal, but can't be generated by your hardware. Even switching to using Timer2, the available divisions, start at /16.
So, 'yes', your problem with the code, is the change to 32Mhz. However you may be able to get it working, by aiming to use the interface at half the rate. Unfortunately, at your clock rate, the ideal timing of 3.2MHz, is not available. I'd suggest trying the code at the lower rate, exactly as originally posted, with the SPI clocking off /16, _except_ changing the define:
#define ws2811_one 0b11110000
to
#define ws2811_one 0b11100000
This then gives 500uSec for the '0', and 1.5uSec for the '1'. Still 'out of spec', but closer.
You could also try switching to running your chip at 16MHz, and using SPI_CLK_DIV_4, which would then give the original timings. I'd still do the same change to the define, but this might well work.
Best Wishes |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Fri May 30, 2014 5:00 pm |
|
|
maxikys wrote: | Thanks for the tips guys, I pulled the string Code: | # USE SPI (MASTER, SPI1, MODE = 0, BITS = 8, MSB_FIRST) | , but nothing has changed - the first LED working.
Dear bkamen, could you share a piece of working code ...
Maybe the whole thing is that my frequency of quartz 64 MHz and 32 MHz. |
Sorry, I can't share code in this case. The work I do is typically for clients who pay me for my time and thus, own the libraries I write for them.
And you still haven't answered the question both I and ckielstra have asked:
Do you have access to an oscilloscope?
I can't stress enough how important it is to have one of these when writing code that generates timing sensitive software.
In the world of hardware, it's like trying to write software without a debugger.
Not having one makes things REALLY HARD..... (as you're finding out.) _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
stinky
Joined: 05 Mar 2012 Posts: 99 Location: Central Illinois
|
|
Posted: Mon Nov 17, 2014 4:47 pm |
|
|
Not sure if OP is still interested but I came across this thread while trying to get the 16LF1829 to run the WS2812b. I found the comments here very helpful. Hope this helps.
Edit: CCS PCM V4.141
Code: | #include "16LF1829.h"
#device adc = 8
#define OSCILLATOR 32000000
#use delay (internal = OSCILLATOR)
#fuses INTRC_IO, NOWDT, PUT, NOMCLR //Internal RC Osc IO on CLKIN, Wtchdg Timer Disabled, Pwrup Timer Disabled, Internal MCLR
#fuses NOBROWNOUT, NOLVP, NOCPD, NODEBUG, //Brwn-Out Reset disabled, LVP Disabled, Data Memory Code Protect disabled, PDAT&PCLK IO
#fuses NOPROTECT, NOCLKOUT, NOIESO //Program memory Code Protect Disabled, Two Speed External-Internal OSC Changeover disabled
#fuses NOFCMEN, NOWRT, STVREN //External Clock Failsafe Disabled, Flash Memory Slf Write Protect Disabled, Stack Over&Under-flow causes reset
#use FAST_IO(ALL)
#use RS232(baud = 9600, UART1, ERRORS)
//////////////////////////////////////////////////
// BIT BANG Driver for WS2812B RGB LEDs //
//////////////////////////////////////////////////
#define DATA_PIN PIN_A0
/*Creates the timing for the 1's and 0's. Very dependent on hardware.
If oscillator speed changes, or fast_io is disabled, timing
will be altered.*/
#define WS_ONE(pinPtr) {\
output_high(pinPtr); delay_cycles(1); delay_cycles(1); \
delay_cycles(1); delay_cycles(1); delay_cycles(1); delay_cycles(1); \
delay_cycles(1); delay_cycles(1); output_low(pinPtr);\
}
#define WS_ZERO(pinPtr) {\
output_high(pinPtr); delay_cycles(1); delay_cycles(1); \
output_low(pinPtr); delay_cycles(1); delay_cycles(1); \
delay_cycles(1); delay_cycles(1); delay_cycles(1);\
}
/*Latches the data in to the devices after passing. Call this
at the end of each update cycle.*/
#define WS_RESET(pinPtr) output_low(pinPtr); delay_us(100);
/*Low Level interface for sending data*/
void data_stream(unsigned int8 temp) {
if(temp & 0x80) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x40) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x20) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x10) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x08) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x04) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x02) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x01) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
}
/*WS2812 accepts colors in order of green-red-blue.
I do not, so these assign as red-green-blue which
is more comfortable to me.*/
void RGB_color(unsigned int8 red, unsigned int8 green, unsigned int8 blue) {
data_stream(green);
data_stream(red);
data_stream(blue);
}
/*Single LED addressed. Once per second it will be updated
to cycle through Blue --> Green --> Red, and repeat.*/
void prove_driver(void) {
static unsigned int8 temp = 0;
switch(temp) {
case 0: RGB_color(0x00, 0x00, 0xFF); temp += 1; break;
case 1: RGB_color(0x00, 0xFF, 0x00); temp += 1; break;
case 2: RGB_color(0xFF, 0x00, 0x00); temp = 0; break;
}
WS_RESET(DATA_PIN);
delay_ms(1000);
}
void main(void) {
/*pause at startup*/
delay_ms(1000);
/*fast_io needed for timing,
set corresponding TRIS bit*/
output_drive(DATA_PIN);
while(TRUE)
prove_driver();
} |
|
|
|
ressas
Joined: 15 Nov 2019 Posts: 135
|
|
Posted: Sat Dec 07, 2019 5:19 am |
|
|
Yes, this code works properly, but only one led is lit, my module consists of 9 leds. How can we do this
Code: |
#include "16LF1829.h"
#device adc = 8
#define OSCILLATOR 32000000
#use delay (internal = OSCILLATOR)
#fuses INTRC_IO, NOWDT, PUT, NOMCLR //Internal RC Osc IO on CLKIN, Wtchdg Timer Disabled, Pwrup Timer Disabled, Internal MCLR
#fuses NOBROWNOUT, NOLVP, NOCPD, NODEBUG, //Brwn-Out Reset disabled, LVP Disabled, Data Memory Code Protect disabled, PDAT&PCLK IO
#fuses NOPROTECT, NOCLKOUT, NOIESO //Program memory Code Protect Disabled, Two Speed External-Internal OSC Changeover disabled
#fuses NOFCMEN, NOWRT, STVREN //External Clock Failsafe Disabled, Flash Memory Slf Write Protect Disabled, Stack Over&Under-flow causes reset
#use FAST_IO(ALL)
#use RS232(baud = 9600, UART1, ERRORS)
//////////////////////////////////////////////////
// BIT BANG Driver for WS2812B RGB LEDs //
//////////////////////////////////////////////////
#define DATA_PIN PIN_A0
/*Creates the timing for the 1's and 0's. Very dependent on hardware.
If oscillator speed changes, or fast_io is disabled, timing
will be altered.*/
#define WS_ONE(pinPtr) {\
output_high(pinPtr); delay_cycles(1); delay_cycles(1); \
delay_cycles(1); delay_cycles(1); delay_cycles(1); delay_cycles(1); \
delay_cycles(1); delay_cycles(1); output_low(pinPtr);\
}
#define WS_ZERO(pinPtr) {\
output_high(pinPtr); delay_cycles(1); delay_cycles(1); \
output_low(pinPtr); delay_cycles(1); delay_cycles(1); \
delay_cycles(1); delay_cycles(1); delay_cycles(1);\
}
/*Latches the data in to the devices after passing. Call this
at the end of each update cycle.*/
#define WS_RESET(pinPtr) output_low(pinPtr); delay_us(100);
/*Low Level interface for sending data*/
void data_stream(unsigned int8 temp) {
if(temp & 0x80) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x40) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x20) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x10) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x08) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x04) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x02) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
if(temp & 0x01) {WS_ONE(DATA_PIN);} else {WS_ZERO(DATA_PIN);}
}
/*WS2812 accepts colors in order of green-red-blue.
I do not, so these assign as red-green-blue which
is more comfortable to me.*/
void RGB_color(unsigned int8 red, unsigned int8 green, unsigned int8 blue) {
data_stream(green);
data_stream(red);
data_stream(blue);
}
/*Single LED addressed. Once per second it will be updated
to cycle through Blue --> Green --> Red, and repeat.*/
void prove_driver(void) {
static unsigned int8 temp = 0;
switch(temp) {
case 0: RGB_color(0x00, 0x00, 0xFF); temp += 1; break;
case 1: RGB_color(0x00, 0xFF, 0x00); temp += 1; break;
case 2: RGB_color(0xFF, 0x00, 0x00); temp = 0; break;
}
WS_RESET(DATA_PIN);
delay_ms(1000);
}
void main(void) {
/*pause at startup*/
delay_ms(1000);
/*fast_io needed for timing,
set corresponding TRIS bit*/
output_drive(DATA_PIN);
while(TRUE)
prove_driver();
}
|
|
|
|
stinky
Joined: 05 Mar 2012 Posts: 99 Location: Central Illinois
|
|
Posted: Sat Dec 07, 2019 7:54 am |
|
|
This code example only communicates with one WS2812b module to show proof of concept of the function. You just need to keep calling that function with more data. And it looks like you'd then call the WS_RESET macro.
You could iterate over an array of rgb values and call Code: | RGB_color(r, g, b); | on each index |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Sat Dec 07, 2019 9:25 am |
|
|
Since you're using the internal oscillator, you may need to adjust/alter/trim the timing of the WS_ONE(), WS_ZERO() macros.
Always good to see the pulses so you can measure them, then adjust code if required. If the timing is off, you'll see that the LEDs at the end of the line will not function properly.That's the nature of a long serial datastream.
edit: I dld/ read both datasheets,those PICs internal HF oscillator is +-2%. Do the math to see if it's within range of the WS2811 timing needs...
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Sat Dec 07, 2019 11:01 am |
|
|
The chip allows timing errors up to just over 10%, so the internal
oscillator should be fine. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Sat Dec 07, 2019 2:49 pm |
|
|
temtronic wrote: | If the timing is off, you'll see that the LEDs at the end of the line will not function properly.That's the nature of a long serial datastream.
|
Hey Jay,
Just an FYI, the WS28xx series typically has a DataOut line that re-conditions the serial stream to cope with transmission parasitics.
They're fun little chips/LEDs that way. :D _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|