Silicon Labs Silicon Labs Community
silabs.com
Language
  • 简体中文
  • 日本語
  • MCUs
    • 8-bit MCU
    • 32-bit MCU
  • Wireless
    • Bluetooth
    • Proprietary
    • Wi-Fi
    • Zigbee & Thread
    • Z-Wave
  • More Products
    • Interface
    • Isolation
    • Power
    • Sensors
    • Timing
  • Development Tools
    • Simplicity Studio
    • Third Party Tools
    • Thunderboard
  • Expert's Corner
    • Announcements
    • Blog
    • General Interest
    • Projects
  1. Community - Silicon Labs
  2. Blog

Chapter 11.2: Control RGB LEDs with an LED Controller Part 2

04/117/2016 | 09:59 AM
lynchtron

Level 5


11_title.png

 

In the last section, we covered some LED driver basics and how to limit the current of an LED without the need for inline current limiting resistors.  In this section we will connect the TLC5940 LED driver breakout board to the Starter Kit and then begin to develop a serial interface software driver.  We will also define some data structures to help deal with the serial interface data stream.

 

Hardware Connections

In order to communicate with the TLC5940, we need eight wires: Vcc, Ground, a data pin, a clock pin, a pin to latch the serial data called XLAT in the specification, a pin to select the mode called VPRG, a global enable pin called BLANK, and a pin to serve as a grayscale clock, GSCLK.  We can choose to use any of the UART, LEUART, or USART peripherals on the MCU.  I will choose USART1, Location #1, since it is available on the header pins of the Wonder Gecko Starter Kit.  We have used these pins in earlier lessons for SPI and serial data.  Connect the board as follows:

 

11_connections.png

Note that we are only using two pins of the four available on USART1.  We will not be using US1_RX or US1_CS.  There is no rule that we have to use all four pins of a peripheral.

 

If you wanted to connect a second TLC5940 breakout board in series with the first, you would simply solder the pins on the right side of the first board to the pins on the left side of the second board.  You could repeat this over and over to add more and more individually-addressable channel capacity to the design.  We will talk about how that can be accomplished in software later in the chapter.

 

When I first found this part and reviewed the spec, I thought that it only required three wires because of this line: “Only 3 pins are needed to input data into the device.”  But only after I began to experiment with the chip (and could not get it working) did I figure out that in addition to the three pins necessary to load data into the device, namely SIN, SCLK, and XLAT, you need three more pins to actually perform the grayscale function: VPRG, BLANK and GSCLK.  I was really surprised.  This is exactly why you should build breadboard prototypes with any new parts before you spend money on a PCB!

 

Another surprise that I found upon working with this board is that it was getting hot after being connected to the Starter Kit.  The problem was that Sparkfun decided to run VPRG through a “solder jumper trace,” which is way to connect the VPRG pin to ground but allow you to later cut the trace if you decide to control that line from the MCU.  I had assumed that the trace would be open by default, and they would leave it up to me to populate a resistor to close the connection to ground.  So when I drove PD3 high, it was being connected to ground on the TLC5940 breakout board.  Once I cut the trace, the problem was solved, and VPRG is now able to be controlled by the MCU.

 

You must cut this trace on the back of your TLC5940 breakout board to use the code for this chapter.  If you don’t, your board will get slightly warm, and you won’t be able to control the dot compensation (channel current control).

 

11_vprg_trace.png

 

Peripheral Software Configuration

We are going to configure all pins with code rather than using the Configurator tool to generate the code for us, just as a succinct reminder of what is needed to activate the GPIO pins.  Remember, whenever we want to activate a peripheral, we need to do the steps listed below.  The order is important.  If we don’t enable the peripheral clocks before we configure the peripheral, the peripheral will not receive any of the configuration register writes. If we route the peripheral through to the GPIO before configuring the peripheral, then we will could get some glitches on the GPIO pins as the peripheral is activated.  In summary:

 

  • Enable the GPIO clock
  • Enable the peripheral clock (in this case, USART1)
  • Configure and enable the peripheral
  • Route the pins used by the peripheral through to the GPIO
  • Configure the pins used by the peripheral in the GPIO (push-pull, input, etc.)

Here is the resulting code to enable the USART and GPIO peripherals:

 

 

#define CONTROL_PORT                            gpioPortD
#define XLAT_PIN                                1
#define VPRG_PIN                                3
#define BLANK_PIN                               4
#define SIN_PIN                                 0
#define SCLK_PIN                                2
#define GSCLK_PIN                               7

      // Turn on the peripheral clocks       CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);       CMU_ClockEnable(cmuClock_GPIO, true);       CMU_ClockEnable(cmuClock_USART1, true);         // Configure the USART peripheral       USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT;       init.baudrate = 50000;       init.msbf = true;       // MSB first, per the TLC5940 spec       USART_InitSync(USART1, &init);         // Route the peripheral to the GPIO block       USART1->ROUTE = USART_ROUTE_TXPEN |             // US1_TX                           USART_ROUTE_CLKPEN |        // US1_CLK                           USART_ROUTE_LOCATION_LOC1;  // Location #1         // Configure both the USART and control pins in GPIO       GPIO_DriveModeSet(gpioPortD, gpioDriveModeLowest);       GPIO_PinModeSet(CONTROL_PORT, SIN_PIN, gpioModePushPullDrive, 0);       GPIO_PinModeSet(CONTROL_PORT, SCLK_PIN, gpioModePushPullDrive, 0);       GPIO_PinModeSet(CONTROL_PORT, XLAT_PIN, gpioModePushPullDrive, 0);       GPIO_PinModeSet(CONTROL_PORT, VPRG_PIN, gpioModePushPullDrive, 0);       GPIO_PinModeSet(CONTROL_PORT, BLANK_PIN, gpioModePushPullDrive, 0);       GPIO_PinModeSet(CONTROL_PORT, GSCLK_PIN, gpioModePushPullDrive, 0);

 

LED Packet Data Structure

The TLC5940 implements a shift register that expects an exact 96- or 192-bit serial data stream in order to program the current limits (dot correction) and PWM values (grayscale) for all channels.  The type of serial stream at the input to the TLC5940 (dot correction versus grayscale) is selected with the VPRG pin.  The dot correction fields are 6 bits multiplied by 16 channels, which gives us the 96 bits.  The grayscale fields are 12 bits multiplied by 16 channels, which gives us the 192 bits.

 

The bit sizes for grayscale and dot correction are a little problematic for us.  They are not nice byte-sized values that we have come to know with the uint8_t and uint16_t data types that we have used on the USARTs thus far.  If we were to store the 6-bit values as uint8_t and the 12-bit values as uint16_t, then transfer these values into the USART peripheral, the result would be padded with extra zeroes, which would not work for this chip.  Things need to be packed into a serial stream exactly as the TLC5940 specification dictates.

 

Instead, we will define a structure that has bitfields.  These bitfields don’t guarantee that the values will be stored in memory in the nicely packed structure that we want, but they will take care of truncating any values stored in the structure for us.  They also remind us of the size of each field that the TLC5940 expects.

 

#define MAX_CHANNELS          16
#define DC_BIT_LEN            6
#define GS_BIT_LEN            12         
 
// Structs for LED driver
typedef struct LED_bit_fields
{                             // Number after colon is field width
      uint8_t dot_correction : DC_BIT_LEN;
      uint16_t grayscale      : GS_BIT_LEN;
} LED_data_struct;
 
typedef struct LED_stream
{
      LED_data_struct channel[MAX_CHANNELS];
      bool mode;
} LED_stream_struct;
 
LED_stream_struct stream;

 

 

Warning:  There is a compiler directive called “packed” that will help save memory space but does not guarantee that the structure will be stored in the correct order in memory, because the way that the C compiler packs the structure is not always predictable.  Don’t rely on any specific ordering; instead, use shifts and masks to build the final serial data stream.  We will cover how to do that in the next section.

 

With the above code, I have created my own type called LED_data_struct, which is then used inside of another new type definition that is called LED_stream_struct.  This enables me to create a single variable that will hold all of the programming data for the TLC5940 in a single top-level global variable called stream.  I can now set the stream with this kind of syntax:

 

        stream.mode = mode;

        stream.channel[channel_number].grayscale = grayscale_value;

        stream.channel[channel_number].dot_correction = dot_correction_value;

 

In the next section, we will complete the LED software driver and use it to bring some light to our test LEDs on the breakout board.

 

PREVIOUS | NEXT

  • Blog Posts
  • Makers

Tags

  • Wireless
  • High Performance Jitter Attenuators
  • EFR32MG21 Series 2 SoCs
  • Blue Gecko Series 2
  • Zigbee SDK
  • ZigBee and Thread
  • Internet Infrastructure
  • Sensors
  • Blue Gecko Bluetooth Low Energy SoCs
  • Z-Wave
  • Micrium OS
  • Blog Posts
  • Low Jitter Clock Generators
  • Bluetooth Classic
  • Makers
  • Flex SDK
  • Tips and Tricks
  • Smart Homes
  • IoT Heroes
  • Reviews
  • RAIL
  • Simplicity Studio
  • Mighty Gecko SoCs
  • Timing
  • Blue Gecko Bluetooth Low Energy Modules
  • Clocks
  • Ultra Low Jitter Clock Generators
  • General Purpose Clock Generators
  • Industry 4.0
  • Giant Gecko
  • 32-bit MCUs
  • blue-gecko-xpress-modules
  • Bluetooth Low Energy
  • 32-bit MCU SDK
  • Gecko
  • Microcontrollers
  • News and Events
  • Industrial Automation
  • Wi-Fi
  • Bluetooth SDK
  • Community Spotlight
  • Biometric Sensors
  • General Purpose Jitter Attenuators
  • Giant Gecko S1
  • Flex Gecko
  • Internet of Things
  • 8-bit MCUs
  • Isolation
  • Powered Devices

Top Authors

  • Avatar image Mark Mulrooney
  • Avatar image Siliconlabs
  • Avatar image Nari Shin
  • Avatar image lynchtron
  • Avatar image deirdrewalsh
  • Avatar image Lance Looper
  • Avatar image lethawicker

Archives

  • 2014 December
  • 2015 January
  • 2015 February
  • 2015 March
  • 2015 April
  • 2015 May
  • 2015 June
  • 2015 July
  • 2015 August
  • 2015 September
  • 2015 October
  • 2015 November
  • 2015 December
  • 2016 January
  • 2016 February
  • 2016 March
  • 2016 April
  • 2016 May
  • 2016 June
  • 2016 July
  • 2016 August
  • 2016 September
  • 2016 October
  • 2016 November
  • 2016 December
  • 2017 January
  • 2017 February
  • 2017 March
  • 2017 April
  • 2017 May
  • 2017 June
  • 2017 July
  • 2017 August
  • 2017 September
  • 2017 October
  • 2017 November
  • 2017 December
  • 2018 January
  • 2018 February
  • 2018 March
  • 2018 April
  • 2018 May
  • 2018 June
  • 2018 July
  • 2018 August
  • 2018 September
  • 2018 October
  • 2018 November
  • 2018 December
  • 2019 January
  • 2019 February
  • 2019 March
  • 2019 April
  • 2019 May
  • 2019 June
  • 2019 July
  • 2019 August
  • 2019 September
  • 2019 October
  • 2019 November
Silicon Labs
  • About Us
  • In the News
  • Email Newsletter
  • Cookies
  • Contact Us
  • Community
  • Site Feedback
  • Investor Relations
  • Blog
  • Privacy and Terms
  • Corporate Citizenship
Copyright © Silicon Laboratories. All rights reserved.
粤ICP备15107361号-1