bastelhalde.de


Ben,

Using the TLC5940 with an STM32

DSC_8614

The TLC5940 is an LED driver which I use with my VFD clock project to power 4 RGB LEDs. I couldn't find any existing code for the STM32 so I decided to upload my code here in case anyone wants to use this component.

A better version of this code is now on github, I the pasted code on this page is just here for nostalgic reasons, please use this:

https://github.com/bkarl/tlc5940stm32

I use the grayscale mode of the chip to mix colors. You will need one timer, one hardware SPI port and some GPIO ports. I named the pins after the wirings in the code so there is no need for a schematic. For example PIN_BLANK is the pin that is connected to the BLANK input of the TLC5940.

The 4096 clock ticks for GCSLK are generated in my main loop which is not optimal. It should be outsourced in the tlc.c file.

In the current state the code switches all 16 outputs to the maximum brigthness following one after another beginning with the 5th output. You will find a video of the device in action as well as a download link for an archive containing the files below the code.

tlc.h

#ifndef TLC_H
#define TLC_H

#include "main.h"

//PIN definitions
#define PIN_BLANK   GPIO_Pin_3
#define PIN_VPRG    GPIO_Pin_2
#define PIN_XLAT    GPIO_Pin_4
#define PIN_GSCLK   GPIO_Pin_1

//init the GPIOS/Timers/SPIs
void init_tlc();
//write a byte to the tlc5940
void spi_writeByte(uint8_t data);
//write new color data to the tlc5940
void flush_Data();

//delay, timing...
volatile uint16_t currentDelay;
uint16_t clkCnt;
//array holding the data to be shifted in the driver
uint16_t leds[15];
uint16_t ledsTarget[15];
#endif

tlc.c

#include "tlc.h"

//delay in milliseconds
void delay(uint16_t d)
{
    currentDelay = d;
    while (currentDelay);
}

//init all GPIOs, timers, spi...
void init_tlc()
{
    currentDelay = 0;
    clkCnt = 0;

    //activate periphals
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_SPI1, ENABLE);

    SPI_InitTypeDef SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM2, ENABLE);

    //init 1 ms timer
    TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBase_InitStructure.TIM_Period = 1;
    TIM_TimeBase_InitStructure.TIM_Prescaler = 35999;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBase_InitStructure);

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    NVIC_Init(&NVIC_InitStructure);

    TIM_Cmd(TIM3, ENABLE);

    //init GPIOs
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = PIN_BLANK | PIN_GSCLK | PIN_VPRG | PIN_XLAT;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //initial setup
    GPIO_WriteBit(GPIOA, PIN_VPRG | PIN_XLAT | PIN_BLANK | PIN_GSCLK, RESET);

    //SPI
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CRCPolynomial = 0;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);

    //disable all outputs
    uint16_t i;

    for (i = 0; i < 16; i++)
    {
        leds[i] = 0x0000;
        ledsTarget[i] = 0x0000;
    }

    flush_Data();
    delay(10);

}

//write a byte to the chip
void spi_writeByte(uint8_t data)
{
    SPI_I2S_SendData(SPI1, data);
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
}

//write new data to chip - i hope everything is right here
void flush_Data()
{
    GPIO_WriteBit(GPIOA, PIN_BLANK, SET);
    delay(1);
    uint8_t i;
    for (i = 0; i < 16; i+=2)
    {
        uint8_t send1 = 0;
        uint8_t send = leds[i] >> 4;
        spi_writeByte(send);

        send = (leds[i] & 0x000F);
        send <<= 4;
        send1 = (leds[i+1]) >> 8;

        send |= send1;
        spi_writeByte(send);

        send = leds[i+1];
        spi_writeByte(send);
    }
    //delay(1);
    GPIO_WriteBit(GPIOA, PIN_XLAT, SET);
    delay(1);
    GPIO_WriteBit(GPIOA, PIN_BLANK, RESET);
    GPIO_WriteBit(GPIOA, PIN_XLAT, RESET);
    delay(1);
}

main loop

#include "main.h"
int main(void)
{
    init_tlc();

    uint16_t i,k;
    k = 0;

    while (1)
    {
        //switch the output every 3 seconds
        currentDelay = 3000;
        while (currentDelay)
        {
            //generate GSCLK
            for (i = 0; i < 4096; i++)
            {
                GPIO_WriteBit(GPIOA, PIN_GSCLK, SET);
                GPIO_WriteBit(GPIOA, PIN_GSCLK, RESET);
            }
            GPIO_WriteBit(GPIOA, PIN_BLANK, SET);
            GPIO_WriteBit(GPIOA, PIN_BLANK, RESET);
        }

        if (k == 16)
        {
            leds[15] = 0;
            k = 0;
        }

        leds[k] = 0x0FFF;
        if (k != 0)
            leds[k-1] = 0;

        k++;

        flush_Data();

    }
}

interrupt routine (stm32f10x_it.c)

#include "tlc.h"

void TIM3_IRQHandler(void){
  TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

  if (currentDelay)
  {
    currentDelay--;
    return;
  }

}

Download the code (zip).


Leave Comment: