This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LP5523: Cannot run two blink pattern on two different RGB in parallel

Part Number: LP5523

Hello,

I'm developing a driver for the LP5523 in order to manage 2 RGB LEDs (let's call them B and C) and 1 individual LED (let's call it A).

I developed blinking/breathing/etc. patterns using the instruction set.

LED A is assigned to engine 1, whose PROG START ADDR is 0x00.

LED B is assigned to engine 2, whose PROG START ADDR is 0x20.

LED C is assigned to engine 3, whose PROG START ADDR is 0x40.

This structure allows me to run a different code on each LED separately. The PC and other registers are correctly set as I use the same functions to manage all LEDs with just a parameter saying which LED is concerned. When trying the functions on a single LED or RGB LED, everything works fine.

The initialisation uses the following values:

  • reg 0x00 takes value 0x40
  • reg 0x36 takes value 0x5B
  • regs 0x06-0x0E take value 0x20

My problem is that I can run parallel patterns on engines 1 and 2, on engines 1 and 3, but not on engines 2 and 3.

When I start a blinking pattern (for example) on engine 3 and then engine 2 (or vice versa), the LED connected to engine 2 blinks a few times and then ramps up and stays on... I have also the impression that from this point, the LP5523 becomes a bit "crazy" and turn on/off some LED sometimes.

Any idea?

Thanks.

Yorick

  • Hi Yorick,

    Could you help to share your engine code? 

  • Hello,

    The code is quite long but I'll try to share what's necessary.

    Registers definition:

    #define LP5523_ADDR0    0x32
    
    #define LP5523_REG_ENGCTRL1         0x00
    #define LP5523_REG_ENGCTRL2         0x01
    #define LP5523_REG_OUTCTRL_LSB      0x05
    #define LP5523_REG_D1CTRL           0x06
    #define LP5523_REG_D2CTRL           0x07
    #define LP5523_REG_D3CTRL           0x08
    #define LP5523_REG_D4CTRL           0x09
    #define LP5523_REG_D5CTRL           0x0A
    #define LP5523_REG_D6CTRL           0x0B
    #define LP5523_REG_D7CTRL           0x0C
    #define LP5523_REG_D8CTRL           0x0D
    #define LP5523_REG_D9CTRL           0x0E
    #define LP5523_REG_D1PWM            0x16
    #define LP5523_REG_D2PWM            0x17
    #define LP5523_REG_D3PWM            0x18
    #define LP5523_REG_D4PWM            0x19
    #define LP5523_REG_D5PWM            0x1A
    #define LP5523_REG_D6PWM            0x1B
    #define LP5523_REG_D7PWM            0x1C
    #define LP5523_REG_D8PWM            0x1D
    #define LP5523_REG_D9PWM            0x1E
    #define LP5523_REG_MISC             0x36
    #define LP5523_REG_STATUS           0x3A
    #define LP5523_REG_RESET            0x3D
    #define LP5523_REG_ENG1PROGSTARTADR 0x4C
    #define LP5523_REG_ENG2PROGSTARTADR 0x4D
    #define LP5523_REG_ENG3PROGSTARTADR 0x4E
    #define LP5523_REG_PROGMEMPAGE      0x4F
    #define LP5523_REG_PROGMEM          0x50
    
    typedef enum
    {
        LP5523_ENGINE_1 = 1,
        LP5523_ENGINE_2,
        LP5523_ENGINE_3,
        LP5523_ENGINE_NBR_OF_ENTRIES = 3,
    } LP5523_ENGINE;
    
    #define LP5523_VAR_A    0
    #define LP5523_VAR_B    1
    #define LP5523_VAR_C    2
    #define LP5523_VAR_D    3
    
    #define LP5523_LOOP_INF     0
    
    #define LP5523_PWM_0    0x00
    #define LP5523_PWM_100  0xFF
    
    #define LP5523_OP_SET_PWM(v)            (0x4000|v)  // Set PWM
    #define LP5523_OP_WAIT_100MS            (0x4700)    // Wait, 109 ms
    #define LP5523_OP_WAIT_200MS            (0x4D00)    // Wait, 203 ms
    #define LP5523_OP_WAIT_240MS            (0x4F00)    // Wait, 234 ms
    #define LP5523_OP_WAIT_400MS            (0x5A00)    // Wait, 406 ms
    #define LP5523_OP_WAIT_480MS            (0x5F00)    // Wait, 484 ms
    #define LP5523_OP_END(it,rst)           (0xC000|(it<<12)|(rst<<11))    // End
    #define LP5523_OP_BRANCH(stp,cnt)       (0xA000|((cnt&0x3F)<<7)|(stp&0x7F)) // Branch
    #define LP5523_OP_BRANCHA(stp,var)      (0x8600|((stp&0x7F)<<2)|(var&0x03)) // Branch Var A
    #define LP5523_OP_LOAD(var,val)         (0x9000|((var&0x03)<<10)|(val&0xFF))    // Load
    #define LP5523_OP_RAMPUP_Q(tim,inc)     (0x0000|((tim&0x1F)<<9)|(inc&0xFF))    // Ramp Up quick, increment step of 0.488 ms
    #define LP5523_OP_RAMPUP_S(tim,inc)     (0x4000|((tim&0x1F)<<9)|(inc&0xFF))    // Ramp Up slow, increment step of 15.625 ms
    #define LP5523_OP_RAMPDOWN_Q(tim,inc)   (0x0100|((tim&0x1F)<<9)|(inc&0xFF))    // Ramp Up quick, increment step of 0.488 ms
    #define LP5523_OP_RAMPDOWN_S(tim,inc)   (0x4100|((tim&0x1F)<<9)|(inc&0xFF))    // Ramp Up slow, increment step of 15.625 ms
    #define LP5523_OP_MUX_CLR               (0x9D00)    // Mux clear
    #define LP5523_OP_MUX_SEL(led)          (0x9D00|led)    // Mux selection
    #define LP5523_OP_MUX_MAPADDR(addr)     (0x9F80|addr)   // Mux map address
    #define LP5523_OP_END(it,rst)           (0xC000|(it<<12)|(rst<<11))
    #define LP5523_OP_NOOP                  (0x0000)
    
    #define LP5523_VAL_RESET                0xFF
    #define LP5523_VAL_ENGCTRL1_CE          0x40
    #define LP5523_VAL_MISC                 0x5B
    #define LP5523_VAL_OUTCTRL_OUTON_D5     0x10
    #define LP5523_VAL_DxCTRL_LOGADJON      0x20
    
    #define LP5523_VAL_ENGCTRL1_ENG_MASK(i)     (0x03<<((LP5523_ENGINE_3-i)<<1))
    #define LP5523_VAL_ENGCTRL1_ENG_HOLD(i)     (0x00<<((LP5523_ENGINE_3-i)<<1))
    #define LP5523_VAL_ENGCTRL1_ENG_RUN(i)      (0x02<<((LP5523_ENGINE_3-i)<<1))
    
    #define LP5523_VAL_ENGCTRL2_ENG_MASK(i)     (0x03<<((LP5523_ENGINE_3-i)<<1))
    #define LP5523_VAL_ENGCTRL2_ENG_DISABLE(i)  (0x00<<((LP5523_ENGINE_3-i)<<1))
    #define LP5523_VAL_ENGCTRL2_ENG_LOAD(i)     (0x01<<((LP5523_ENGINE_3-i)<<1))
    #define LP5523_VAL_ENGCTRL2_ENG_RUN(i)      (0x02<<((LP5523_ENGINE_3-i)<<1))
    #define LP5523_VAL_ENGCTRL2_ENG_HALT(i)     (0x03<<((LP5523_ENGINE_3-i)<<1))
    
    #define LP5523_VAL_STATUS_ENGINEBUSY        0x10
    #define LP5523_VAL_STATUS_ENG1_INT          0x04
    #define LP5523_VAL_STATUS_ENG2_INT          0x02
    #define LP5523_VAL_STATUS_ENG3_INT          0x01

    Blink program:

    #define LED_PROG_ADDR_SIZE  1
    #define LED_PROG_CODE_SIZE  26 // Each instruction uses 2 bytes
    #define LED_PROG_SIZE       (LED_PROG_ADDR_SIZE + LED_PROG_CODE_SIZE)
    
    #define LED_ADD_OP(ar,i,op) ar[i++] = (uint8_t)(op >> 8); \
                                ar[i++] = (uint8_t)(op)
    
    uint8_t u8ProgSelEngineAndAddr(LP5523_ENGINE eEng)
    {
        uint8_t u8Addr = 0;
        switch(eEng)
        {
            case LP5523_ENGINE_1:
                au8EngProgVal4a[1] = 0; // Page selection
                au8EngProgVal4c[0] = LP5523_REG_ENG1PROGSTARTADR;
                au8EngProgVal4c[1] = 0x00;
                u8Addr = 0x00;
                break;
            case LP5523_ENGINE_2:
                au8EngProgVal4a[1] = 2; // Page selection
                au8EngProgVal4c[0] = LP5523_REG_ENG2PROGSTARTADR;
                au8EngProgVal4c[1] = 0x20;
                u8Addr = 0x20;
                break;
            case LP5523_ENGINE_3:
                au8EngProgVal4a[1] = 4; // Page selection
                au8EngProgVal4c[0] = LP5523_REG_ENG3PROGSTARTADR;
                au8EngProgVal4c[1] = 0x40;
                u8Addr = 0x40;
                break;
            default:
                break;
        }
        return u8Addr;
    }
    
    void vProgBlink(LP5523_ENGINE eEng, uint16_t u16LedMap, LED_LED eLed, uint16_t u16LedAll)
    {
        uint8_t u8Idx = LED_PROG_ADDR_SIZE;
        uint8_t u8Start = u8ProgSelEngineAndAddr(eEng);
    
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_MUX_MAPADDR((u8Start+12)));           // op  1/13
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_SET_PWM(0x00));                       // op  2/13
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_MUX_MAPADDR((u8Start+11)));           // op  3/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_SET_PWM(0xFF));                       // op  4/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_WAIT_480MS);                          // op  5/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_SET_PWM(0x00));                       // op  6/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_WAIT_480MS);                          // op  7/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_BRANCH((u8Start+3),LP5523_LOOP_INF)); // op  8/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_NOOP);                                // op  9/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_NOOP);                                // op 10/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, LP5523_OP_NOOP);                                // op 11/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, u16LedMap);                                     // op 12/10
        LED_ADD_OP(au8EngProgVal4b, u8Idx, u16LedAll);                                     // op 13/13
    
        TWI_vExec(&tTrnsEngProg1);
    }

    And finally the transactions. I use "nrfx manager" to manage the I2C communication.

    #define LED_REG_READ(p_buffer) NRF_TWI_MNGR_WRITE(LP5523_ADDR0, p_buffer, 1, NRF_TWI_MNGR_NO_STOP), NRF_TWI_MNGR_READ(LP5523_ADDR0, p_buffer+1, sizeof(p_buffer)-1, 0)
    
    #define LED_REG_WRITE(p_buffer) NRF_TWI_MNGR_WRITE(LP5523_ADDR0, p_buffer, sizeof(p_buffer), 0)
    
    #define LED_TRANSACTION(name, transf, transid) \
    static nrf_twi_mngr_transaction_t name = { \
        .callback            = vTransactionCallback, \
        .p_user_data         = (void*)(transid << 24), \
        .p_transfers         = transf, \
        .number_of_transfers = sizeof(transf) / sizeof(transf[0]) \
    }
    
    
    /** @brief Registers' values for Program engine
     *
     * Read Status
     */
    static uint8_t au8EngProgVal1[2] =
    {
        LP5523_REG_STATUS,
    };
    static nrf_twi_mngr_transfer_t atTrsfrEngProg1[] = {
        LED_REG_READ(au8EngProgVal1),
    };
    LED_TRANSACTION(tTrnsEngProg1, atTrsfrEngProg1, LED_TRANSID_ENGINE_PROG1);
    
    /** @brief Registers' values for Program engine
     *
     * Read Engine Control 1 and 2
     */
    static uint8_t au8EngProgVal2[3] =
    {
        LP5523_REG_ENGCTRL1,
        0,
        0,
    };
    static nrf_twi_mngr_transfer_t atTrsfrEngProg2[] = {
        LED_REG_READ(au8EngProgVal2),
    };
    LED_TRANSACTION(tTrnsEngProg2, atTrsfrEngProg2, LED_TRANSID_ENGINE_PROG2);
    
    /** @brief Registers' values for Program engine
     *
     * Write Engine Control 1 and 2, halt engine
     */
    static uint8_t au8EngProgVal3a[] =
    {
        LP5523_REG_ENGCTRL1,
        0, // Hold (CTRL1)
    };
    static nrf_twi_mngr_transfer_t atTrsfrEngProg3A[] = {
        LED_REG_WRITE(au8EngProgVal3a),
    };
    LED_TRANSACTION(tTrnsEngProg3A, atTrsfrEngProg3A, LED_TRANSID_ENGINE_PROG3A);
    
    
    /** @brief Registers' values for Program engine
     *
     * Write Engine Control 1 and 2, hold and disable engine
     */
    static uint8_t au8EngProgVal3b[] =
    {
        LP5523_REG_ENGCTRL2,
        0, // Disable (CTRL2)
    };
    /** @brief Registers' values for Program engine
     *
     * Write Engine Control 1 and 2, load engine
     */
    static uint8_t au8EngProgVal3c[] =
    {
        LP5523_REG_ENGCTRL2,
        0, // Load (CTRL2)
    };
    static nrf_twi_mngr_transfer_t atTrsfrEngProg3B[] = {
        LED_REG_WRITE(au8EngProgVal3b),
        LED_REG_WRITE(au8EngProgVal3c),
    };
    LED_TRANSACTION(tTrnsEngProg3B, atTrsfrEngProg3B, LED_TRANSID_ENGINE_PROG3B);
    
    /** @brief Registers' values for Program engine
     *
     * Write memory page selection
     */
    static uint8_t au8EngProgVal4a[] =
    {
        LP5523_REG_PROGMEMPAGE,
        0,  // 0 -> 00H-0FH, 1 -> 10H-1FH, 2 -> 20H-2FH, 3 -> 30H-3FH, 4 -> 40H-4FH, 5 -> 50H-5FH
            // ENG1 00H-1FH, ENG2 20H-3FH, ENG3 40H-5FH
    };
    /** @brief Registers' values for Program engine
     *
     * Write code
     */
    static uint8_t au8EngProgVal4b[LED_PROG_SIZE] =
    {
        LP5523_REG_PROGMEM,
    };
    /** @brief Registers' values for Program engine
     *
     * Write engine start address
     */
    static uint8_t au8EngProgVal4c[] =
    {
        0,  // ENG1 4CH, ENG2 4DH, ENG3 4EH
        0,  // ENG1 00H, ENG2 20H, ENG3 40H
    };
    /** @brief Registers' values for Program engine
     *
     * Write Engine Control 2, run engine
     */
    static uint8_t au8EngProgVal4d[] =
    {
        LP5523_REG_ENGCTRL2,
        0, // Run engine (CTRL2)
    };
    static nrf_twi_mngr_transfer_t atTrsfrEngProg4[] = {
        LED_REG_WRITE(au8EngProgVal4a),
        LED_REG_WRITE(au8EngProgVal4b),
        LED_REG_WRITE(au8EngProgVal4c),
        LED_REG_WRITE(au8EngProgVal4d),
    };
    LED_TRANSACTION(tTrnsEngProg4, atTrsfrEngProg4, LED_TRANSID_ENGINE_PROG4);
    
    
    void vTransactionCallback(ret_code_t result, void * const p_user_data)
    {
        uint32_t userdata = (uint32_t)p_user_data;
        LED_TRANSID eTransId = (LED_TRANSID)((userdata >> 24) & 0xFF);
        LP5523_ENGINE eEng = (LP5523_ENGINE)((userdata >> 16) & 0xFF);
    
        if(eTransId == LED_TRANSID_ENGINE_PROG1)
        {
            if(au8EngProgVal1[1] & LP5523_VAL_STATUS_ENGINEBUSY)
            {
                // ABORT
            }
            else
            {
                TWI_vExec(&tTrnsEngProg2);
            }
        }
        else if(eTransId == LED_TRANSID_ENGINE_PROG2)
        {
            uint8_t u8EngMask1    = (uint8_t)LP5523_VAL_ENGCTRL1_ENG_MASK(eEng);
            uint8_t u8EngMask2    = (uint8_t)LP5523_VAL_ENGCTRL2_ENG_MASK(eEng);
            uint8_t u8EngMask1Inv = (uint8_t)(~u8EngMask1);
            uint8_t u8EngMask2Inv = (uint8_t)(~u8EngMask2);
            // Update values
            //  Hold (CTRL1)
            au8EngProgVal3a[1]  = au8EngProgVal2[1] & u8EngMask1Inv;
            au8EngProgVal3a[1] |= LP5523_VAL_ENGCTRL1_ENG_HOLD(eEng);
            //  Disable (CTRL2)
            au8EngProgVal3b[1]  = au8EngProgVal2[2] & u8EngMask2Inv;
            au8EngProgVal3b[1] |= LP5523_VAL_ENGCTRL2_ENG_DISABLE(eEng);
            //  Load (CTRL2)
            au8EngProgVal3c[1]  = au8EngProgVal2[2] & u8EngMask2Inv;
            au8EngProgVal3c[1] |= LP5523_VAL_ENGCTRL2_ENG_LOAD(eEng);
            // Check CTRL1 status
            //  Engine is in running state
            if(au8EngProgVal2[1] & u8EngMask1)
            {
                // Stop the engine first
                TWI_vExec(&tTrnsEngProg3A);
            }
            //  Engine is in hold state
            else
            {
                // Set the engine in LOAD mode
                TWI_vExec(&tTrnsEngProg3B);
            }
        }
        else if(eTransId == LED_TRANSID_ENGINE_PROG3A)
        {
            // Set the engine in LOAD mode at time out
            
            // Start timer to wait end the end of the instruction of the LP5523 program
        }
        else if(eTransId == LED_TRANSID_ENGINE_PROG3B)
        {
            uint8_t u8EngMask2    = (uint8_t)LP5523_VAL_ENGCTRL2_ENG_MASK(eEng);
            uint8_t u8EngMask2Inv = (uint8_t)(~u8EngMask2);
            // Run (CTRL2)
            au8EngProgVal4d[1]  = au8EngProgVal2[2] & u8EngMask2Inv;
            au8EngProgVal4d[1] |= LP5523_VAL_ENGCTRL2_ENG_RUN(eEng);
            TWI_vExec(&tTrnsEngProg4);
        }
        else if(eTransId == LED_TRANSID_ENGINE_PROG4)
        {
            TWI_vExec(&tTrnsEngStart1); // Start the engine
        }
    }

    It's kind of complicated with all the I2C transfers, but it works very well when engines are started independently. Note that this structure shares the xxxEngProgValx arrays, but I make sure that two engines are not started concurrently. When I'm going my tests, the engines are started with an interval of a few seconds.

    Thank you.

    Best regards,

    Yorick

  • I made some new tests. It seems that engines 1 and 3 can run in parallel without problem. However, engine 2 cannot run in parallel with another engine, or sometimes cannot run at all.

    Is there some hardware problem on the LP5523 (I haven't find any errata) or is there anything wrong in the software?

    The code for starting engines is the same for any of my engines, except for the registers specific to an engine (PC, START ADDRESS, MEMORY PROGRAM LOCATION, CONTROL 1 and 2, etc.)

    I also tried to read the PROGRAM MEMORY after having written it by reading 6 times 32 bytes (16 instructions), each time incrementing the PROG MEM PAGE SEL from 0 to 5. I read only 0s.

    Is there a special state for the chip to be able to read the PROGRAM MEMORY?