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

Drive a TFT LCD with Capacitive Touchscreen - Part 2

09/271/2016 | 03:56 PM
lynchtron

Level 5


12_title.png

 

In the last section, we covered the basics of LCD screen architectures.  In this section, we will connect the Adafruit display to the Wonder Gecko Starter Kit and light up some pixels.

 

Connecting the Adafruit Display to the Wonder Gecko Starter Kit

Use wire jumpers to attach all of the connections between the Wonder Gecko Starter Kit and the Adafruit display as shown in the table.  Note that most of the connections should be made on the edge of the board that has the SPI labeling (i.e. MOSI/MISO, etc.) with the two I2C connections on the opposite board edge. 

12_lcd_pinout.jpg

12_starter_kit_locations.png 

 

Signal

Adafruit LCD Display

Wonder Gecko GPIO Pin

LCD Chip Select (CS)

CS

PD3 – USART1 CS

MISO

MISO

PD1 – USART1 RX (MISO)

DC (Data/Command)

D/C

PD4 – Chosen at random

GND

GND

GND

MOSI

MOSI

PD0 – USART1 TX (MOSI)

Clock (CLK)

CLK

PD2 – USART1 CLK

Vcc

3-5V

V3V

Reset

RST

PD5 – Chosen at random

I2C SDA

SDA [ Capacitive Touchscreen]

PD7 – I2C0 SDA

I2C SCL

SCL [ Capacitive Touchscreen]

PD6 – I2C0 SCL

I2C Interrupt

IRQ [ Capacitive Touchscreen]

PD13 – Chosen at random

MicroSD Chip Select (CS)

Card CS

PD8 – Chosen at random

 

The application note for the display from Adafruit instructs us to close the IM1, IM2, and IM3 solder jumpers on the back of the display board in order to use the SPI interface, so warm up your soldering iron and make those connections.  This will also allow us to communicate to the MicroSD card built into the display.

12_solder_shorts.png

When you make these connections to the Starter Kit, the backlight to the display will automatically light up.  This is because the Adafruit board is built to give full brightness by default.  To turn off the backlight, you set the backlight pin to ground.  You can dim the display by applying a PWM pulse (with open-drain output) to the backlight pin. 

 

Configuring the Display

The Adafruit display is a board comprised of multiple devices bundled together on the same board.  We have worked with the fundamental communication methods already in previous chapters, and this will just be an exercise of learning how to deal with several devices at once in the same firmware.  We will communicate with the LCD and the MicroSD card over the same SPI bus (selecting each device independently with their own Chip Select (CS) signal. 

 

We could make use of the Configurator tool in Simplicity Studio to find suitable pins for the SPI and I2C interfaces, and it would automatically generate code for our project.  We will instead set things up manually to help you get practice in learning how it is done.

 

Remember, as we learned in chapter 11, the order in which to initialize the MCU to communicate with external devices follows a particular order:

  • Enable the GPIO clock
  • Enable the peripheral clocks (i.e. USART1 and SPI0)
  • 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.)

If you do the above in the wrong order, you can see glitches on the output which could confuse your external devices.  By following this order (and not forgetting a step!) we can ensure that the peripherals will be configured and external devices ready to be controlled.

 

By following these steps, I created a peripheral_setup() function to do all of the above:

 

void peripheral_setup()
{
      // Set up the necessary peripheral clocks
      CMU_ClockEnable(cmuClock_GPIO, true);
      CMU_ClockEnable(cmuClock_USART1, true);
      CMU_ClockEnable(cmuClock_I2C0, true);
 
      // Initialize and enable the USART
      USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT;
      init.msbf = true;
 
      USART_InitSync(USART1, &init);
 
      USART1->CTRL |= USART_CTRL_AUTOCS;
 
      // Connect the USART signals to the GPIO peripheral
      USART1->ROUTE = USART_ROUTE_RXPEN | USART_ROUTE_TXPEN |
                  USART_ROUTE_CLKPEN | USART_ROUTE_CSPEN |
                  USART_ROUTE_LOCATION_LOC1;
 
      // Set up i2c
      I2C_Init_TypeDef i2c_init = I2C_INIT_DEFAULT;
      I2C_Init(I2C0, &i2c_init);
 
      /* Module I2C0 is configured to location 1 */
      I2C0->ROUTE = (I2C0->ROUTE & ~_I2C_ROUTE_LOCATION_MASK) |
                                   I2C_ROUTE_LOCATION_LOC1;
 
      /* Enable signals SCL, SDA */
      I2C0->ROUTE |= I2C_ROUTE_SCLPEN | I2C_ROUTE_SDAPEN;
 
      // Enable the GPIO pins for the USART, starting with CS
      // This is to avoid clocking the flash chip when we set CLK high
      GPIO_PinModeSet(gpioPortD, 3, gpioModePushPull, 1);         // CS
      GPIO_PinModeSet(gpioPortD, 8, gpioModePushPull, 1);         // MicroSD CS
      GPIO_PinModeSet(gpioPortD, 0, gpioModePushPull, 0);         // MOSI
      GPIO_PinModeSet(gpioPortD, 1, gpioModeInput, 0);         // MISO
      GPIO_PinModeSet(gpioPortD, 2, gpioModePushPull, 1);         // CLK
 
      // Enable the GPIO pins for the misc signals, leave pulled high
      GPIO_PinModeSet(gpioPortD, 4, gpioModePushPull, 1);         // DC
      GPIO_PinModeSet(gpioPortD, 5, gpioModePushPull, 1);         // RST
 
      // Enable the GPIO pins for the i2c signals, open drain, pulled up, with filter
      GPIO_PinModeSet(gpioPortD, 6, gpioModeWiredAndPullUpFilter, 1);         // SDA
      GPIO_PinModeSet(gpioPortD, 7, gpioModeWiredAndPullUpFilter, 1);         // SCL
      GPIO_PinModeSet(gpioPortD, 13, gpioModeInput, 1);                              // IRQ
 
}

 

In this initialization code for the USART, we are going to let the USART control the chip select (CS) signal for the LCD.  This requires that we set the AUTOCS flag in the USART peripheral with the code:

 

USART1->CTRL |= USART_CTRL_AUTOCS;

We also must route the CS pin through to the GPIO with the ROUTE statement that follows it.  This will only work up until the point where we start to control the MicroSD card on the same SPI bus, since the USART cannot automatically control multiple chip selects at a time.  But for our initial work with just the ILI9341 controller active on the SPI bus, we can let the USART control the CS signal.

 

Now, we are ready to start sending some cycles over SPI to configure the LCD for our first try at generating some graphics.

 

Activating Pixels

The LCD is controlled by the ILI9341 display driver chip built into the display panel.  The ILI9341.pdf datasheet details the component package, pinout, electrical ratings, interface procedures, and provides a comprehensive guide of the different commands that the controller accepts.  However, it is very frustrating that there is no “big picture” overview or even a guide detailing how to turn the display on from a power-off state, and no examples of how to use it.  I had to scour the internet to find some examples, and even then, the examples didn’t always explain why they were doing what they were doing to initialize the display, as well as referencing registers that are not found in the datasheet.  It is at times like these that working as an embedded developer feels more like a hacker and less like a software engineer!

 

Through my research, and some trial and error, I was able to initialize the display with just the following code.

#include "em_device.h"
#include "em_chip.h"
#include "em_usart.h"
#include "em_cmu.h"
#include "utilities.h" 
     
#define ILI9341_SLPOUT  0x11
#define ILI9341_DISPON  0x29
 
void writecommand(uint8_t command)
{
      delay(1);
      GPIO_PinOutClear(gpioPortD, 4);
      USART_Tx(USART1, command);
      delay(1);
}
 
void writedata(uint8_t data)
{
      GPIO_PinOutSet(gpioPortD, 4);
      USART_Tx(USART1, data);
}
int main(void)
{
      /* Chip errata */
      CHIP_Init();
 
      if (SysTick_Config(CMU_ClockFreqGet(cmuClock_CORE) / 1000))
      {
            DEBUG_BREAK;
      }
 
      peripheral_setup();
 
      // Reset the display driver chip
      GPIO_PinOutSet(gpioPortD, 5);
      GPIO_PinOutClear(gpioPortD, 5);
      delay(1);
      GPIO_PinOutSet(gpioPortD, 5);
 
      delay(10);
      writecommand(ILI9341_SLPOUT);    //Exit Sleep
      delay(120);
      writecommand(ILI9341_DISPON);    //Display on
      while (1) {
      }
}

 

 

When this code is executed, the display will go dark after it executes the ILI9341_DISPON command.  This makes sense, because when the display is in the “off” state, the pixel grid allows the backlight to pass, but when the display driver chip turns on, all of the pixels are instructed to not allow any light to pass, causing the display to go dark, or mostly dark.  If you look carefully at the screen, you can see that the backlight is still powered behind the blocking pixels.  This confirms that we can communicate with the ILI9341 in the most basic of commands.

12_lcd_states.png

When you examine the writedata() and writecommand() functions, you will see the code implements the procedure that is detailed in the ILI9341 specification for SPI writes and reads:

12_ili_spec.png

The only detail that separates “command” packets from “data” packets is the D/CX signal, which we have connected to PD4.  If it is low, the packet is seen as a command.  If D/CX is high, the packet is seen as a data packet, or as any necessary parameter(s) that immediately follows a command packet.

 

The spec shows that pauses between commands and data/parameters (or successive data packets) are acceptable, so we don’t have to worry about keeping the CS signal low for the entire command/data operation.  In order to make sure that there is some delay between multiple commands and command/data packets, a delay of 1ms is added before and after each USART_Tx() call in the writecommand() function.  This will not degrade our screen display performance later on, because the command packet is only sent one time, which is then followed by thousands of data packets necessary to form an image on the display screen.

 

With the screen alive and ready to display some pixels, we can turn them all on to a specific color after a few more configuration commands, which are each accomplished with a command byte followed by a configuration data byte.  After those are set, the memory write command is issued, followed by the color data for all of the pixels in the screen.

 

#define ILI9341_TFTWIDTH  240
#define ILI9341_TFTHEIGHT 320
#define ILI9341_RAMWR     0x2C
#define ILI9341_MADCTL  0x36
#define ILI9341_PIXFMT  0x3A
      writecommand(ILI9341_PIXFMT);     // Pixel format       writedata(0x55);                   // This sets up 16-byte color data, instead of default 18-bit color data         writecommand(ILI9341_MADCTL);    // Memory Access Control       writedata(0x48);              // This configures the screen orientation         uint16_t color = ILI9341_RED;         writecommand(ILI9341_RAMWR);              //Memory write         for (int i = 0; i < (ILI9341_TFTWIDTH); i++)       {             for (int j = 0; j < ILI9341_TFTHEIGHT; j++)             {                   writedata( color >> 8);                   writedata( color );             }       }

 

With this code placed just before the while loop in main(), the screen will slowly be painted red.  It should take about four seconds to send all of those 76,800 x 2 = 153,600 SPI cycles to be written at the default 1 MHz USART interface speed. 

 

12_red_background.jpg

This works because the ILI9341 knows the width and height of the screen, so every Memory Write command advances the ILI9341’s pointer into the frame buffer by one pixel.  When it gets to the end of a row, it starts on the next row, until the whole screen is painted red.  At the present speed of the SPI bus, you can actually see that happening in real time.

 

Speeding up the SPI Bus Rate

If the fastest frame rate were critical to our application and we required several images refreshed in every second, we would have used the parallel interface, which allows up to 18 data bits to be sent to the ILI9371 at once instead of the single bit per clock with the SPI bus.  However, we can speed up the rate of the SPI bus to improve performance by changing the MCU clock configuration.

 

If you add the following line of code to the peripheral_setup() function, just after the USART_InitSync() call, you will be able to read the clock rate of the SPI bus:

uint32_t baud = USART_BaudrateGet(USART1);

You should see that the clock is initially set at 1000000, or 1MHz.  To get the fastest possible USART clock, we need to clock the MCU with the crystal oscillator with the following line of code:

CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);

This will increase the clock rate of the core to 48 MHz, and the High Frequency (HF) clock will increase to 24 MHz, which is the fastest that the USART can operate on the Wonder Gecko MCU.  But the default SPI rate is still set to 1 MHz due to the use of USART_INITSYNC_DEFAULT during the USART initialization.  You can change that to 24 MHz with the following line of code before you call USART_InitSync():

 

init.baudrate = 24000000; 

Now, when you run this and check the baud rate variable again, you will see 24000000, or 24 MHz.  Beware that you cannot always get the baud rate that you request.  Always check the baud rate using the USART_BaudrateGet() call to see if you are requesting a valid and reachable clock divisor.

 

With the SPI bus set to 24 MHz, your screen should display all red pixels in about one second.  This is still very slow and perhaps could be improved more through the use of DMA and a memory buffer, but this is good enough for the work of this chapter. 

 

TIP: If you are wondering about all of the available commands for peripherals such as CMU and USART, for example the CMU_ClockSelectSet() function or the USART_BaudRateGet() function, you can use the Software Documentation reference available from the home screen of Simplicity Studio.  Click on that tile, and a browser will open which you can use to explore all of the possible library calls in emlib, with hyperlinks to the types of parameters that each function expects as well.

12_sw_api.png

 

12_sw_api_detail.png

It should be easier to find what you are looking for in the Software Documentation than in any number of chapters of this text or looking back through all of the examples that you have ever studied.

As a reminder, the final, complete source code for all examples in this book can be found on Github here.

 

In the next section, we will put a crude graphic library in place to handle displaying some standard graphic primitives like lines, rectangles, and text.

 

 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