Hey
I want to use stm32f0 by ws2812 and keil. this is my code
/* * light weight WS2812 lib - ARM Cortex M0/M0+ version * * Created: 07.07.2013 * Author: Tim (cpldcpu@gmail.com) */
#include "light_ws2812_cortex.h"
/* * The total length of each bit is 1.25µs (25 cycles @ 20Mhz) * At 0µs the dataline is pulled high. * To send a zero the dataline is pulled low after 0.375µs * To send a one the dataline is pulled low after 0.625µs */
#define ws2812_ctot (((ws2812_cpuclk/1000)*1250)/1000000) #define ws2812_t1 (((ws2812_cpuclk/1000)*375 )/1000000) // floor #define ws2812_t2 (((ws2812_cpuclk/1000)*625+500000)/1000000) // ceil
#define w1 (ws2812_t1-2) #define w2 (ws2812_t2-ws2812_t1-2) #define w3 (ws2812_ctot-ws2812_t2-5)
#define ws2812_DEL1 " nop \n\t" #define ws2812_DEL2 " b .+2 \n\t" #define ws2812_DEL4 ws2812_DEL2 ws2812_DEL2 #define ws2812_DEL8 ws2812_DEL4 ws2812_DEL4 #define ws2812_DEL16 ws2812_DEL8 ws2812_DEL8
void ws2812_sendarray(uint8_t *data,int datlen) { uint32_t maskhi = ws2812_mask_set; uint32_t masklo = ws2812_mask_clr; volatile uint32_t *set = ws2812_port_set; volatile uint32_t *clr = ws2812_port_clr; uint32_t i; uint32_t curbyte;
while (datlen--) { curbyte=*data++;
asm volatile( " lsl %[dat],#24 \n" " movs %[ctr],#8 \n" "ilop%=: \n" " lsl %[dat], #1 \n" " str %[maskhi], [%[set]] \n" #if (w1&1) ws2812_DEL1 #endif #if (w1&2) ws2812_DEL2 #endif #if (w1&4) ws2812_DEL4 #endif #if (w1&8) ws2812_DEL8 #endif #if (w1&16) ws2812_DEL16 #endif " bcs one%= \n" " str %[masklo], [%[clr]] \n" "one%=: \n" #if (w2&1) ws2812_DEL1 #endif #if (w2&2) ws2812_DEL2 #endif #if (w2&4) ws2812_DEL4 #endif #if (w2&8) ws2812_DEL8 #endif #if (w2&16) ws2812_DEL16 #endif " sub %[ctr], #1 \n" " str %[masklo], [%[clr]] \n" " beq end%= \n\t" #if (w3&1) ws2812_DEL1 #endif #if (w3&2) ws2812_DEL2 #endif #if (w3&4) ws2812_DEL4 #endif #if (w3&8) ws2812_DEL8 #endif #if (w3&16) ws2812_DEL16 #endif
" b ilop%= \n\t" " end%=: \n\t" : [ctr] "+r" (i) : [dat] "r" (curbyte), [set] "r" (set), [clr] "r" (clr), [masklo] "r" (masklo), [maskhi] "r" (maskhi) ); } }
but i have a problem 1-light_ws2812_cortex.c(45): error: #20: identifier "asm" is undefined but if i change asm to __asm the error change to light_ws2812_cortex.c(105): error: #18: expected a ")" :ctr] "+r" (i) how can i fix it?
What horrible code!
Just about the ugliest way to drive one of the leds I've seen
My advice is find a better library or write the code yourself
>>how can i fix it?
Write code for Keil and not GNU/GCC
Have you rewrite the code for Keil? I was quite interested in it.First error is simple Keil use keyword __asm not asm as GCC. Unfortunately I do not have experience with the ARM platform assembler and so I do not know what the compilers do not like to use asm to partition the code
So I did not have it and modified the code for Keil
/* * light weight WS2812 lib - ARM Cortex M0/M0+ version * * Created: 07.07.2013 * Author: Tim (cpldcpu@gmail.com) */ #include "light_ws2812_cortex.h" /* * The total length of each bit is 1.25µs (25 cycles @ 20Mhz) * At 0µs the dataline is pulled high. * To send a zero the dataline is pulled low after 0.375µs * To send a one the dataline is pulled low after 0.625µs */ #define ws2812_ctot (((ws2812_cpuclk/1000)*1250)/1000000) #define ws2812_t1 (((ws2812_cpuclk/1000)*375 )/1000000) // floor #define ws2812_t2 (((ws2812_cpuclk/1000)*625+500000)/1000000) // ceil #define w1 (ws2812_t1-2) #define w2 (ws2812_t2-ws2812_t1-2) #define w3 (ws2812_ctot-ws2812_t2-5) #define ws2812_DEL1 " nop \n\t" #define ws2812_DEL2 " b .+2 \n\t" #define ws2812_DEL4 ws2812_DEL2 ws2812_DEL2 #define ws2812_DEL8 ws2812_DEL4 ws2812_DEL4 #define ws2812_DEL16 ws2812_DEL8 ws2812_DEL8 void ws2812_sendarray(uint8_t *data,int datlen) { uint32_t maskhi = ws2812_mask_set; uint32_t masklo = ws2812_mask_clr; volatile uint32_t *set = ws2812_port_set; volatile uint32_t *clr = ws2812_port_clr; uint32_t i; uint32_t curbyte; while (datlen--) { curbyte=*data++; __asm volatile( " lsl curbyte,#24 \n\t" " movs i,#8 \n\t" "ilop: \n\t" //"ilop%=: \n\t" " lsl curbyte, #1 \n\t" " str maskhi, [set] \n\t" #if (w1&1) ws2812_DEL1 #endif #if (w1&2) ws2812_DEL2 #endif #if (w1&4) ws2812_DEL4 #endif #if (w1&8) ws2812_DEL8 #endif #if (w1&16) ws2812_DEL16 #endif " bcs one%= \n\t" " str masklo, [clr] \n\t" "one%=: \n\t" #if (w2&1) ws2812_DEL1 #endif #if (w2&2) ws2812_DEL2 #endif #if (w2&4) ws2812_DEL4 #endif #if (w2&8) ws2812_DEL8 #endif #if (w2&16) ws2812_DEL16 #endif " sub ctr, #1 \n\t" " str masklo, [clr] \n\t" " beq end%= \n\t" #if (w3&1) ws2812_DEL1 #endif #if (w3&2) ws2812_DEL2 #endif #if (w3&4) ws2812_DEL4 #endif #if (w3&8) ws2812_DEL8 #endif #if (w3&16) ws2812_DEL16 #endif " b ilop \n\t" "end%=: \n\t" ); } }
ank keil return this
*** Using Compiler 'V5.06 update 4 (build 422)', folder: 'C:\Keil\ARM\ARMCC\Bin' compiling light_ws2812_cortex.c... Internal fault: [0x03ab7c:5060422] Please contact your supplier.
Internal fault genarate code on line remarked as comment //
/* * light weight WS2812 lib - ARM Cortex M0/M0+ version * * Created: 07.07.2013 * Author: Tim (cpldcpu@gmail.com) */ #include "light_ws2812_cortex.h" /* * The total length of each bit is 1.25µs (25 cycles @ 20Mhz) * At 0µs the dataline is pulled high. * To send a zero the dataline is pulled low after 0.375µs * To send a one the dataline is pulled low after 0.625µs */ #define ws2812_ctot (((ws2812_cpuclk/1000)*1250)/1000000) #define ws2812_t1 (((ws2812_cpuclk/1000)*375 )/1000000) // floor #define ws2812_t2 (((ws2812_cpuclk/1000)*625+500000)/1000000) // ceil #define w1 (ws2812_t1-2) #define w2 (ws2812_t2-ws2812_t1-2) #define w3 (ws2812_ctot-ws2812_t2-5) #define ws2812_DEL1 " nop \n\t" #define ws2812_DEL2 " b .+2 \n\t" #define ws2812_DEL4 ws2812_DEL2 ws2812_DEL2 #define ws2812_DEL8 ws2812_DEL4 ws2812_DEL4 #define ws2812_DEL16 ws2812_DEL8 ws2812_DEL8 void ws2812_sendarray(uint8_t *data,int datlen) { uint32_t maskhi = ws2812_mask_set; uint32_t masklo = ws2812_mask_clr; volatile uint32_t *set = ws2812_port_set; volatile uint32_t *clr = ws2812_port_clr; uint32_t i; uint32_t curbyte; while (datlen--) { curbyte=*data++; __asm volatile( " lsl curbyte,#24 \n\t" " movs i,#8 \n\t" "ilop: \n\t" " lsl curbyte, #1 \n\t" " str maskhi, [set] \n\t" #if (w1&1) ws2812_DEL1 #endif #if (w1&2) ws2812_DEL2 #endif #if (w1&4) ws2812_DEL4 #endif #if (w1&8) // ws2812_DEL8 #endif #if (w1&16) ws2812_DEL16 #endif " bcs one \n\t" " str masklo, [clr] \n\t" "one: \n\t" #if (w2&1) ws2812_DEL1 #endif #if (w2&2) // ws2812_DEL2 #endif #if (w2&4) // ws2812_DEL4 #endif #if (w2&8) ws2812_DEL8 #endif #if (w2&16) ws2812_DEL16 #endif " sub i, #1 \n\t" " str masklo, [clr] \n\t" " beq end \n\t" #if (w3&1) ws2812_DEL1 #endif #if (w3&2) ws2812_DEL2 #endif #if (w3&4) // ws2812_DEL4 #endif #if (w3&8) // ws2812_DEL8 #endif #if (w3&16) ws2812_DEL16 #endif " b ilop \n\t" "end: \n\t" ); } }
Thanks for showing us how to convert the gcc inline assemblier code to ARM. It looks like the problem is caused by this statement:
#define ws2812_DEL2 " b .+2 \n\t"
I think this is a branch to current PC + 2 but the . notation is not supported within __asm. I have tried to use the __current_pc intrinsic but that generates additional errors when used within __asm.
By changing the line to:
#define ws2812_DEL2 " nop\n"
and compiling and flashing. Pressing reset several times will cause the WS2812 to light which shows the code is working but the timing is wrong.
You should try replacing the branch with a series of nops. On an ARM (and many others), the branch instruction is used because it takes more cycles to execute than many others.
Of course, you could also consider scrapping the code and doing it properly. The SPI can easily be used to drive one of these LEDs and the code would be far easier to maintain and port to different processors, speeds etc.
OK this is my mistake (partly), partly it is a Keil error (I think). in first instruction in _asm is
lsl curbyte,#24
used variable curbyte defined and declares in C code, as local variable in function ws2812_sendarray(), this code do not works and compiler return
45: __asm__ volatile( 0x080010BE E006 B 0x080010CE 46: " lsl curbyte,#24 \n\t" 0x080010C0 2508 MOVS r5,#0x08 0x080010C2 601C STR r4,[r3,#0x00] 0x080010C4 BF00 NOP 0x080010C6 D200 BCS 0x080010CA 0x080010C8 6002 STR r2,[r0,#0x00] 0x080010CA 6002 STR r2,[r0,#0x00] 0x080010CC D1F9 BNE 0x080010C2
notice lsl curbyte,#24 not include in asm code
Code will be works only if curbyte will be global variable!!! in other words all c variable in by code used in _asm() must be declared as global. Compiler return
46: " lsl curbyte,#24 \n\t" 0x080010C8 EA4F6707 LSL r7,r7,#24 0x080010CC 6057 STR r7,[r2,#0x04] 0x080010CE 2708 MOVS r7,#0x08 0x080010D0 6017 STR r7,[r2,#0x00] 0x080010D2 6857 LDR r7,[r2,#0x04] 0x080010D4 EA4F0747 LSL r7,r7,#1 0x080010D8 6057 STR r7,[r2,#0x04] 0x080010DA 602E STR r6,[r5,#0x00] 0x080010DC BF00 NOP
lsl curbyte,#24 is in assembler code as LSL r7,r7,#24
It's interesting, that if in _asm(); use for example lsl datlen,#24 (variable datlen is from void ws2812_sendarray(uint8_t *data,int datlen) all vorsk OK and compiler Translate correctly LSL r0,r0,#24
conclusion Why the compiler does not say that it does not know what the local variable c is? Why he knows datlen? I do not understand much
NO.
It is totally your mistake. You don't have sufficient understanding of the tools.
yes I have not However on the other hand I would expect at least a warning from the tool, warning in __asm is used undeclared variable etc.