Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : uM2(+): WS2812 LEDs revisited
Author | Message | ||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8830 |
Attached is an update to the WS2812 code which uses some of the newer features in the Micromite firmware to simplify and generalise the code. This now runs on either the Micromite or the Micromite+ at any CPU speeed greater or equal to 40MHz. The example code is for my 7-LED circular module but it should be obvious how to use it for any WS2812 modules with any number of LEDs OPTION DEFAULT NONE
OPTION EXPLICIT ' ' Function and subroutine calls ' ' WS2812.pulses: number of LEDs, Pin number, array of colour codes ' sub WS2812.pulses(numleds as integer, pinnum as integer, colours() as integer) ' ' WS2812.getcolour: Get the green, red, and blue components for a specific LED from the colour array ' sub WS2812.getcolour(led as integer, greenness as integer, redness as integer, blueness as integer) ' ' WS2812.setcolour: Load the colour array for a specific LED from the green, red, and blue components ' sub WS2812.setcolour(led as integer, greenness as integer, redness as integer, blueness as integer) ' const LED_PIN = 19 'pin to connect to the data-in on the WS2812 const NUMLEDS = 7 'Number of LEDs in the WS2812 chain setpin LED_PIN,DOUT 'Set the data pin as an output DIM INTEGER colours(NUMLEDS-1) 'set up an array to hold the colours for each LED ' EXAMPLE 'execute the example program ' end '___________________________________________________________ ____________________________________________________________ _______________ ' 'number of LEDs, Pin number, array of colour codes ' Csub WS2812.Pulses 00000000 27BDFFC8 AFBF0034 AFBE0030 AFB7002C AFB60028 AFB50024 AFB40020 AFB3001C AFB20018 AFB10014 AFB00010 3C139D00 8E620000 3C160032 36D6DCD5 8C420000 3C140026 269425A0 0056001B 02C001F4 3C17002B 36F798B7 3C150057 26B5316E AFA5003C 00808021 8E630028 8CA40000 AFA60040 24110001 0000B012 26D6FFFB 0054001B 028001F4 0000A012 2694FFFB 0057001B 02E001F4 0000B812 0055001B 02A001F4 0060F809 0000A812 8E050000 8E630040 00518804 000520C0 00052940 0060F809 00A42023 0040F021 8FA2003C 8C440000 8E620024 0040F809 24050006 8FA3003C 00409021 8E620024 8C640000 0040F809 24050005 8E030004 1C600006 03C06021 14600028 3C040008 8E030000 50600026 3C03BF88 8FA70040 00005821 1000001E 240AFFFF 8CE60004 8CE40000 00034027 00064840 00642006 01094804 00663007 30680020 01242025 00C8200B 30840001 2463FFFF A0A40000 146AFFF2 24A50001 8E040004 256B0001 000B1FC3 0064202A 258C0018 14800008 24E70008 8E040004 14830008 3C040008 8E030000 0163282B 50A00005 3C03BF88 01802821 1000FFE1 24030017 3C03BF88 AC641064 3C03BF88 AC601068 8E030000 AE510000 000320C0 00031940 00642023 00001821 40834800 10800023 00000000 03C03021 03C4F021 80C40000 1080000F 00000000 AE510000 00771821 40044800 0064282B 10A0FFFD 00000000 AC510000 00962021 40034800 0083282B 10A0FFFD 00000000 1000000E 24C60001 AE510000 00751821 40044800 0064282B 10A0FFFD 00000000 AC510000 00942021 40034800 0083282B 10A0FFFD 00000000 24C60001 54DEFFE2 80C40000 AC510000 8FBF0034 3C030008 3C02BF88 AC431064 3C02BF88 8FBE0030 8FB7002C 8FB60028 8FB50024 8FB40020 8FB3001C 8FB20018 8FB10014 8FB00010 AC431068 03E00008 27BD0038 End Csub ' Load the colour array for a specific LED from the green, red, and blue components ' sub WS2812.setcolour(led as integer, greenness as integer, redness as integer, blueness as integer) colours(led)= ((greenness and &HFF)<<16) + ((redness and &HFF)<<8) + (blueness and &HFF) end sub ' ' Get the green, red, and blue components for a specific LED from the colour array ' sub WS2812.getcolour(led as integer, greenness as integer, redness as integer, blueness as integer) greenness=colours(led)>>16 redness=(colours(led)>>8) and &HFF blueness= colours(led) and &HFF end sub ' ' uses the pin number to get the port number and the pin number on the port by reference to the Device ID ' ' Initialise required global variables depending on the number of LEDs and set up the output pin and CPU speed ' ' sub EXAMPLE local integer i,j,green,red,blue,maxbright=40 WS2812.setcolour(0,maxbright,maxbright,maxbright) 'white WS2812.setcolour(1, maxbright,0,0) 'green WS2812.setcolour(2,maxbright,maxbright,0) 'yellow WS2812.setcolour(3,0,maxbright,0) 'red WS2812.setcolour(4,0,maxbright,maxbright) 'magenta WS2812.setcolour(5,0,0,maxbright) 'blue WS2812.setcolour(6,maxbright,0,maxbright) 'cyan Do for j=1 to maxbright-1 WS2812.pulses(NUMLEDS,LED_PIN,colours()) for i=0 to 6 WS2812.getcolour(i,green,red,blue) if green<>0 then green=green-1 if red<>0 then red=red-1 if blue<>0 then blue=blue-1 WS2812.setcolour(i,green,red,blue) next i pause 10 next j rotate for j=1 to maxbright-1 WS2812.pulses(NUMLEDS,LED_PIN,colours()) for i=0 to 6 WS2812.getcolour(i,green,red,blue) if green<>0 then green=green+1 if red<>0 then red=red+1 if blue<>0 then blue=blue+1 WS2812.setcolour(i,green,red,blue) next i pause 10 next j loop end sub ' sub rotate 'rotates the colours round the edge of the display leaving white in the middle local integer i,j i=colours(6) for j=5 to 1 step -1 colours(j+1)=colours(j) next j colours(1)=i end sub C-source void Pulses(long long *numleds, long long *mypin,long long parray[]){
unsigned int t700 = CurrentCpuSpeed /2857143; unsigned int t600 = CurrentCpuSpeed /3333333-5; //allow for loop overhead unsigned int t350 = CurrentCpuSpeed /5714286; unsigned int t800 = CurrentCpuSpeed /2500000-5; //allow for loop overhead unsigned int next_clock,current_ticks = 0; unsigned int index,pin=1<<GetPinBit(*mypin); char *sarray=GetTempMemory(*numleds * 24); int k,bpos; unsigned int volatile * mysetport=GetPortAddr(*mypin, LATSET); unsigned int volatile * myclearport=GetPortAddr(*mypin, LATCLR); index=0; for(k=0;k<*numleds;k++){ //move the data into a single dimensional array that we can step though in even time for(bpos=23;bpos>=0;bpos--){ sarray[index]=(parray[k]>>bpos) & 1; index++; } } mT4IntEnable(0); //disable the RTC k=*numleds * 24;; //get the number of bytes to output *mysetport=pin; asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0 for (index=0;index<k;index++){ //step through the input data // Note the timer settings are not obvious as the execution of the loop elongates the low pulses if (sarray[index]){ *mysetport=pin; next_clock=current_ticks+t700; do{ asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed }while(current_ticks<=next_clock); *myclearport=pin; next_clock=current_ticks+t600; do{ asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed }while(current_ticks<=next_clock); } else { *mysetport=pin; next_clock=current_ticks+t350; do{ asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed }while(current_ticks<=next_clock); *myclearport=pin; next_clock=current_ticks+t800; do{ asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed }while(current_ticks<=next_clock); } } *myclearport=pin; //Set the output low to create the reset pulse which loads the data mT4IntEnable(1); //re-enable the RTC } |
||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1687 |
What's the maximum number of led's it will control? I've been looking at strips, and there are 300 led's in a 5M strip, could it control 4 of these strips in series? The strips run off 5V and obviously would need a really hefty psu |
||||
WhiteWizzard Guru Joined: 05/04/2013 Location: United KingdomPosts: 2806 |
A PSU from an 'old' computer is good for this purpose. I use a fanless PSU from a non-working media-centre PC. Being fanless it eliminates the annoying 'whirring' sound. Enough amps to drive many RGBs. WW For everything Micromite visit micromite.org Direct Email: whitewizzard@micromite.o |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8830 |
Minor timing improvement now allows operation on a MM2 at 20MHz and more accurate timing at all other speeds Csub WS2812.Pulses
00000000 27BDFFC0 AFBF003C AFBE0038 AFB70034 AFB60030 AFB5002C AFB40028 AFB30024 AFB20020 AFB1001C AFB00018 3C179D00 8EE20000 3C120032 3652DCD5 8C420000 3C100026 261025A0 0052001B 024001F4 3C13002B 367398B7 3C110057 2631316E AFA50044 0080A021 8EE30028 8CA40000 AFA60048 00009012 2652FFFF 0050001B 020001F4 00008012 2610FFFF 0053001B 026001F4 00009812 0051001B 022001F4 0060F809 00008812 8E850000 8EE30040 000520C0 00052940 00A42023 24050001 00452804 0060F809 AFA50010 8E830000 0040B021 8EE20040 000320C0 00031940 0040F809 00642023 0040A821 8FA20044 8C440000 8EE20024 0040F809 24050006 8FA30044 0040F021 8EE20024 8C640000 0040F809 24050005 8E830004 1C600006 8FA80048 1460002F 3C040008 8E830000 5060002D 3C03BF88 00006821 00006021 10000024 240BFFFF 8D070004 8D040000 00034827 00075040 012A5004 00642006 30690020 00673807 01442025 00E9200B 30840001 50800004 A0D10000 A0D30000 10000002 A0B20000 A0B00000 2463FFFF 24C60001 146BFFEC 24A50001 8E840004 25AD0001 000D1FC3 0064202A 258C0018 14800008 25080008 8E840004 14830009 3C040008 8E830000 01A3282B 50A00006 3C03BF88 02CC3021 02AC2821 1000FFDA 24030017 3C03BF88 AC641064 3C03BF88 AC601068 8E840000 00001821 000428C0 00042140 00854823 40834800 8FA70010 00003021 AFC70000 AFC70000 02C62021 80840000 00C04021 00641821 40044800 0064282B 10A0FFFD 00000000 AC470000 02A81821 80630000 24C60001 00832021 40034800 0064282B 14A0FFFD 00000000 0126202A 1080FFEC 8FA40010 3C030008 AC440000 8FBF003C 3C02BF88 AC431064 3C02BF88 8FBE0038 8FB70034 8FB60030 8FB5002C 8FB40028 8FB30024 8FB20020 8FB1001C 8FB00018 AC431068 03E00008 27BD0040 End Csub C-source void Pulses(long long *numleds, long long *mypin,long long parray[]){ unsigned int t700 = CurrentCpuSpeed /2857143; unsigned int t600 = CurrentCpuSpeed /3333333-1; unsigned int t350 = CurrentCpuSpeed /5714286; unsigned int t800 = CurrentCpuSpeed /2500000-1; register unsigned int next_clock,current_ticks = 0; int index,pin=1<<GetPinBit(*mypin); char *sarray=GetTempMemory(*numleds * 24); char *darray=GetTempMemory(*numleds * 24); int k,bpos; unsigned int volatile * mysetport=GetPortAddr(*mypin, LATSET); unsigned int volatile * myclearport=GetPortAddr(*mypin, LATCLR); index=0; for(k=0;k<*numleds;k++){ //move the data into a single dimensional array that we can step though in even time for(bpos=23;bpos>=0;bpos--){ if((parray[k]>>bpos) & 1){ sarray[index]=t700; darray[index]=t600; } else { sarray[index]=t350; darray[index]=t800; } index++; } } mT4IntEnable(0); //disable the RTC k=*numleds * 24;; //get the number of bytes to output index=0; asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0 *mysetport=pin; do { // Note the timer settings are not obvious as the execution of the loop elongates the low pulses *mysetport=pin; next_clock=current_ticks+sarray[index]; do { asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed } while(current_ticks<=next_clock); *myclearport=pin; next_clock=current_ticks+darray[index++]; do { asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed } while(current_ticks<next_clock); } while(index<=k); *myclearport=pin; //Set the output low to create the reset pulse which loads the data mT4IntEnable(1); //re-enable the RTC } |
||||
drkl Senior Member Joined: 18/10/2015 Location: HungaryPosts: 102 |
Dear Matherp, I have a 8 led strips, I try to run the code (with small modification), but LED0 allways lights! It is coded in C source code? I can't switch off the LED0. drkl |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8830 |
No. The Cfunction just works on the number of LEDs given to it in the call WS2812.pulses(NUMLEDS,LED_PIN,colours()) Can't help further without seeing your code |
||||
drkl Senior Member Joined: 18/10/2015 Location: HungaryPosts: 102 |
Dear Matherp, Here is the code: OPTION DEFAULT NONE OPTION EXPLICIT const LED_PIN = 2 'pin to connect to the data-in on the WS2812 const NUMLEDS = 8 'Number of LEDs in the WS2812 chain setpin LED_PIN,DOUT 'Set the data pin as an output DIM INTEGER colours(NUMLEDS-1) 'set up an array to hold the colours for each LED ' EXAMPLE 'execute the example program ' end '___________________________________________________________ ____________________________________________________________ _______________ ' 'number of LEDs, Pin number, array of colour codes ' Csub WS2812.Pulses 'here the C code '... End Csub ' Load the colour array for a specific LED from the green, red, and blue components ' sub WS2812.setcolour(led as integer, greenness as integer, redness as integer, blueness as integer) colours(led)= ((greenness and &HFF)<<16) + ((redness and &HFF)<<8) + (blueness and &HFF) end sub ' ' Get the green, red, and blue components for a specific LED from the colour array ' sub WS2812.getcolour(led as integer, greenness as integer, redness as integer, blueness as integer) greenness=colours(led)>>16 redness=(colours(led)>>8) and &HFF blueness= colours(led) and &HFF end sub sub EXAMPLE local integer i,j,green,red,blue,maxbright=10 ' WS2812.setcolour(5,maxbright,maxbright,maxbright) 'white WS2812.setcolour(0,0,0,0) 'cyan WS2812.setcolour(1,0,0,0) 'cyan WS2812.setcolour(2,0,0,0) 'cyan WS2812.setcolour(3,0,0,0) 'cyan WS2812.setcolour(4,0,0,0) 'cyan WS2812.setcolour(5,0,0,0) 'cyan WS2812.setcolour(6,0,0,0) 'cyan WS2812.setcolour(7,0,0,0) 'cyan Do WS2812.pulses(NUMLEDS,LED_PIN,colours()) pause 100 rotate loop end sub ' sub rotate 'rotates the colours round the edge of the display leaving white in the middle local integer i,j i=colours(7) for j=6 to 0 step -1 colours(j+1)=colours(j) next j colours(0)=i end sub The first LED is allways on! The led strip: ledstrip drkl |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8830 |
That is what my code does. It rotates the outer 6 LEDs around a fixed centre LED. This was just how I programmed the example. Just get rid of my example code completely. Use WS2812.setcolour to set the colour of specific LEDs into the array "colours" and then use WS2812.pulses to update the LED string |
||||
drkl Senior Member Joined: 18/10/2015 Location: HungaryPosts: 102 |
Dear Matherp, Thank you the answer, I understand: The first LED has a special features, the WS2812.setcolour(0,0,0,0) instruction doesn't operate. (look my code) Sorry, it is not good for a LED strip with equivalent LEDs. Thank you again drkl |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8830 |
Please try at CPU 48 and also at CPU 60 (use CPU 30 and then CPU 60 command to get 60 to work) and let me know what happens. Also, here is a revised CFunction that improves the timing of the very first pulse Csub WS2812.Pulses
00000000 27BDFFB8 AFBF0044 AFBE0040 AFB7003C AFB60038 AFB50034 AFB40030 AFB3002C AFB20028 AFB10024 AFB00020 3C169D00 8EC20000 3C120032 3652DCD5 8C420000 3C100026 261025A0 0052001B 024001F4 3C13002B 367398B7 3C110057 2631316E AFA5004C 0080A821 8EC30028 8CA40000 AFA60050 00009012 2652FFFF 0050001B 020001F4 00008012 2610FFFF 0053001B 026001F4 00009812 0051001B 022001F4 0060F809 00008812 8EA50000 8EC30040 000520C0 00052940 00A42023 24050001 00452804 0060F809 AFA50018 AFA20010 8EA30000 8EC20040 8FB40010 000320C0 00031940 0040F809 00642023 0040F021 8FA2004C 24050006 8C440000 8EC20024 0040F809 03C0B821 8FA3004C AFA20014 8EC20024 8C640000 0040F809 24050005 8EA30004 1C600006 8FAA0050 1460003F 3C040008 8EA30000 5060003D 3C03BF88 00006021 10000032 00002021 2463FFFF 25290001 2508FFFF 24E70001 24C60001 8D450004 8D440000 00035827 00057040 016E7004 00642006 00652807 306B0020 01C42025 00AB200B 30840001 10800004 00602821 A0F30000 10000003 A0D20000 A0F10000 A0D00000 1580000A 00000000 14AD0008 00000000 82840000 28850005 14A00003 2484FFFC 1000FFE0 A2840000 A2800000 0501FFDD 01202021 8EA50004 258C0001 000C1FC3 0065282A 14A00008 254A0008 8EA50004 54A3000C 3C040008 8EA30000 0183302B 50C00008 3C040008 24890001 02843821 02E43021 24080016 24030017 1000FFCE 240D0017 3C03BF88 AC641064 3C03BF88 AC601068 8EA40000 00001821 000428C0 00042140 00854823 40834800 8FA70018 8FA40014 00003021 AC870000 8FA50014 00C04021 ACA70000 8FA50010 00A62021 80840000 00641821 40044800 0064282B 10A0FFFD 00000000 AC470000 03C81821 80630000 24C60001 00832021 40034800 0064282B 14A0FFFD 00000000 0126202A 5080FFEB 8FA50014 8FA40018 3C030008 AC440000 8FBF0044 3C02BF88 AC431064 3C02BF88 8FBE0040 8FB7003C 8FB60038 8FB50034 8FB40030 8FB3002C 8FB20028 8FB10024 8FB00020 AC431068 03E00008 27BD0048 End Csub For me this definitely works at CPU 30 and above and I've checked the pulse timings on the scope. |
||||
drkl Senior Member Joined: 18/10/2015 Location: HungaryPosts: 102 |
Dear Matherp, The revised C function operates perfect, at 30,40,48 CPU speed (MM44 modul ver 5.1) Thank you drkl |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8830 |
great - the problem was that the very first pulse was elongated as a result of the loop initialisation. Didn't show on my test as I was setting LED 0 to white. Just shows boundary conditions will always catch you out. New C code attached: void Pulses(long long *numleds, long long *mypin,long long parray[]){
unsigned int t700 = CurrentCpuSpeed /2857143; unsigned int t600 = CurrentCpuSpeed /3333333-1; unsigned int t350 = CurrentCpuSpeed /5714286; unsigned int t800 = CurrentCpuSpeed /2500000-1; register unsigned int next_clock,current_ticks = 0; int index,pin=1<<GetPinBit(*mypin); char *sarray=GetTempMemory(*numleds * 24); char *darray=GetTempMemory(*numleds * 24); int k,bpos; unsigned int volatile * mysetport=GetPortAddr(*mypin, LATSET); unsigned int volatile * myclearport=GetPortAddr(*mypin, LATCLR); index=0; for(k=0;k<*numleds;k++){ //move the data into a single dimensional array that we can step though in even time for(bpos=23;bpos>=0;bpos--){ if((parray[k]>>bpos) & 1){ sarray[index]=t700; darray[index]=t600; } else { sarray[index]=t350; darray[index]=t800; } if(k==0 && bpos==23){ if(sarray[0]>4)sarray[0]=sarray[0]-4; else sarray[0]=0; } index++; } } mT4IntEnable(0); //disable the RTC k=*numleds * 24;; //get the number of bytes to output index=0; asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0 *mysetport=pin; do { // Note the timer settings are not obvious as the execution of the loop elongates the low pulses *mysetport=pin; next_clock=current_ticks+sarray[index]; do { asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed } while(current_ticks<=next_clock); *myclearport=pin; next_clock=current_ticks+darray[index++]; do { asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed } while(current_ticks<next_clock); } while(index<=k); *myclearport=pin; //Set the output low to create the reset pulse which loads the data mT4IntEnable(1); //re-enable the RTC } |
||||
Pluto Guru Joined: 09/06/2017 Location: FinlandPosts: 330 |
I have been doing some testing with a WS2812 LED-string. Worked very nicely with a standard 28-pin microMite. Tried then with the same Csubs on a 64pin microMite EXTREME...and it does not work. (This was not a big surprise; I have seen that you have made several versions for different processors, but nothing specifically for uM EXTREME.) At least nothing that I was able to find on the BackShed. Do you happen to have Csubs also for EXTREME (64pin)? |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8830 |
No sorry - CSUBs for the MZ processors are difficult because of a changed compiler version needed for that processor |
||||
Print this page |