Search code examples
cspiledshift-registersamd21

Change LED intencity through 74HC595 shift register


I'm currently working on a project for which I need to control 13 white LEDs and one RGB LED with two shift registers 74HC595 in cascade. I work on windows with a SAMD21 XPLAINED PRO board (µC samd21j18a) and ATMEL Studio as IDE.

For the moment I manage to do it quite basically using SPI (I define most of my project's parameters on ATMEL Start).

#include <atmel_start.h>

#define message_size    2

bool RgbColors[7][3] = {
    {1, 0, 0}, //Red
    {0, 1, 0}, //Green
    {0, 0, 1}, //Blue
    {1, 1, 0}, //Yellow
    {1, 0, 1}, //Pink
    {0, 1, 1}, //Cyan
    {1, 1, 1}, //White
};

uint8_t spi_transfer_array[message_size];
struct spi_xfer spi_driver_xfer;

// Send a 16 bits message through SPI
void spi_16bits_transfer(uint16_t data);
// Control a frame of 13 white LEDs and 1 RGB LED
void turn_led_model_on(uint8_t led, bool red, bool green, bool blue);


int main(void)
{
    atmel_start_init();
    spi_m_sync_enable(&SPI_0);
    
    uint8_t i = 0;
    bool red = 0;
    bool green = 0;
    bool blue = 0;
    
    while (1) {
        
        // Test
        for (uint8_t led = 0; led <= 13; led++){
            
            // RGB modulation
            if (i <= 6){
                red = RgbColors[i][0];
                green = RgbColors[i][1];
                blue = RgbColors[i][2];
                i++;
            }
            else {
                i = 0;
            }
            
            turn_led_model_on(led, red, green, blue);
            delay_ms(200);
        }
        
        for (uint8_t led = 0; led <= 13; led++){
            
            // RGB modulation
            if (i <= 6){
                red = RgbColors[i][0];
                green = RgbColors[i][1];
                blue = RgbColors[i][2];
                i++;
            }
            else {
                i = 0;
            }
            
            turn_led_model_on(13 - led, red, green, blue);
            delay_ms(200);
        }
    }
}


void spi_16bits_transfer(uint16_t data){
    spi_transfer_array[0] = ~data >> 8;
    spi_transfer_array[1] = ~data & 0xFF;
    
    spi_driver_xfer.txbuf = spi_transfer_array;
    spi_driver_xfer.size = message_size;
    
    gpio_set_pin_level(EN_pin, false);
    gpio_set_pin_level(SPI_SS, false);
    spi_m_sync_transfer(&SPI_0, &spi_driver_xfer);
    gpio_set_pin_level(SPI_SS, true);
    gpio_set_pin_level(EN_pin, true);
    delay_ms(1);
    gpio_set_pin_level(EN_pin, false);
}


void turn_led_model_on(uint8_t led, bool red, bool green, bool blue){
    if (led > 13) led = 13;
    
    uint16_t mask = 0;
    
    if (led > 0){
        for (uint8_t i = 1; i <= led; i++){
            mask |= (1 << (led - i));
        }
    }
    
    mask |= (red << 13);
    mask |= (green << 14);
    mask |= (blue << 15);
    
    spi_16bits_transfer(mask);
}

The problem I am currently facing is that I want to be able to change light intensity but I don't know how to do it with 74HC595 and I don't know if I can do it using SPI. The goal is not only to change white LEDs intensity but most of all to get more possibility for the choice of the color of the RGB LED (for the moment I only have 7 possibilities).

I have the feeling it's possible to create a kind of PWM using a timer to choose the frequency of lighting of the LEDs and thus to modify their intensity, but I don't manage to do it.

Also, I don't know if it is possible to change the intensity of each LED independently (that's what I would need for the RGB LED).

Thank you


Solution

  • Ok, I'm working on a software solution that works really good.

    I create an "hard written" values table which is a kind of gauge to fix the LED frequency.

    My gauge contains 26 values (the first 13 values are "0" and the last 13 values are "1"). I browse this gauge with a sample of 13 values depending on the intensity I want (the values in my sample start at gauge[intensity] to gauge[intensity + 12]).

    (When intensity is 0, my sample contains only 13 times 0. When intensity is 1, my sample contains only 12 times 0 and one 1.). Based on these values, I turn on or off my LEDs to modulate the intensity.

    #include <atmel_start.h>
    
    #define message_size    2
    
    uint8_t spi_transfer_array[message_size];
    struct spi_xfer spi_driver_xfer;
    
    void spi_16bits_transfer(uint16_t data);
    void turn_led_model_on(uint8_t led, bool red, bool green, bool blue);
    
    bool gauge[26] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
    
    int main(void)
    {
        atmel_start_init();
        spi_m_sync_enable(&SPI_0);
        
        uint8_t intencity = 13; // The highest this value is, the brightest the light will be (from 0 to 13)
        uint8_t cycles = 3; 
        
        while (1) {
            
            // Test to create wave effect with LED
            // Increase
            for (uint8_t j = 0; j <= intencity; j++){
                for (uint8_t k = 0; k <= cycles; k++){
                    for (uint8_t i = j; i < (j + 13); i++){
                        if (gauge[i]){
                            turn_led_model_on(13, 1, 0, 0);
                        }
                        else {
                            turn_led_model_on(0, 0, 0, 0);
                        }
                    }
                }
            }
            
            // Decrease
            for (uint8_t j = 0; j <= intencity; j++){
                for (uint8_t k = 0; k <= cycles; k++){
                    for (uint8_t i = j; i < (j + 13); i++){
                        if (!gauge[i]){
                            turn_led_model_on(13, 1, 0, 0);
                        }
                        else {
                            turn_led_model_on(0, 0, 0, 0);
                        }
                    }
                }
            }
            
        }
        
    }
    
    
    void spi_16bits_transfer(uint16_t data){
        spi_transfer_array[0] = ~data >> 8;
        spi_transfer_array[1] = ~data & 0xFF;
        
        spi_driver_xfer.txbuf = spi_transfer_array;
        spi_driver_xfer.size = message_size;
        
        gpio_set_pin_level(EN_pin, false);
        gpio_set_pin_level(SPI_SS, false);
        spi_m_sync_transfer(&SPI_0, &spi_driver_xfer);
        gpio_set_pin_level(SPI_SS, true);
        gpio_set_pin_level(EN_pin, true);
        delay_ms(1);
        gpio_set_pin_level(EN_pin, false);
    }
    
    
    void turn_led_model_on(uint8_t led, bool red, bool green, bool blue){
        if (led > 13) led = 13;
        
        uint16_t mask = 0;
        
        if (led > 0){
            for (uint8_t i = 1; i <= led; i++){
                mask |= (1 << (led - i));
            }
        }
        
        mask |= (red << 13);
        mask |= (green << 14);
        mask |= (blue << 15);
        
        spi_16bits_transfer(mask);
    }
    

    I still have to work on it to use it for RGB modulation but it shouldn't be that complex now.