This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

ws2812 with stm32f0

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.