Interfacing an LCD to the STM32F4 Discovery – Part 1: Simple Message Display

SerLCD Simple Display

SerLCD Simple Display

I think it’s about time I wrote a bit more about my experiments with the STM32F4 Discovery board. It’s been a long time since I blogged about the Digital Dice project and as that has been by far the most popular article on Geekily Interesting, it seems like a good idea to write some more. Plus it gives me a good opportunity to get stuck into a bit of tech.

The last project concentrated on using GPIO to display random numbers on a 7-segment LED display. This time though I wanted to look at something that could be more flexible and used in many different ways. For example, wouldn’t it be great to be able to display some error text when something goes wrong so you can diagnose problems more easily? Wouldn’t also be good to be able to interact a bit more with the user by providing them with more information?

Having played about with the GPIO, I also wanted to try out the serial ports on the board. So with this in mind I went shopping online. I ended up ordering a backlit 2×16 LCD panel with a small SerLCD board mounted on it which provides a UART interface. Perfect!

My first mission was then to get some kind of message on it. I started by connecting the Discovery to the SerLCD using the Discovery’s USART1. The SerLCD module doesn’t transmit anything so it’s a very simple interface consisting of +5V, GND and the RX input.

The interface is exposed by a small 3 wire contact block with screws to clamp the wires down. This was my first delay in the proceedings by the way, I needed to find a small screwdriver to do that! I used male to female jumper leads to connect the Discovery to a piece of breadboard and wired from the breadboard to the SerLCD. The reason I did that was because I wasn’t sure if I’d end up needing an extra pull-up resistor to keep the serial interface happy during the board initialisation.

The Discovery and the SerLCD were connected as follows:

Discovery +5V -> SerLCD +5V
Discovery GND -> SerLCD GND
Discovery PB6 -> SerLCD RX

Next I downloaded and installed TrueSTUDIO Lite. I had been using my Mac with a very rickety toolchain but it was proving to be too much of a nuisance. I abandoned it in favour of TrueSTUDIO and was up and running with a test project very quickly indeed. It means I’ve had to use a PC (by the way atollic, please can we have a Mac version?!) but it just goes to show that everything is a lot easier when you have the proper tools! Now if only I had a scope too…

I plugged my Discovery into my laptop using my USB cable and was greeted with the SerLCD splash screen. Now we’re ready to code.

I created a new Discovery project in TrueSTUDIO using all the default options and added the following code to the main.c file.

** Abstract: main program
int main(void)
    * The symbol VECT_TAB_SRAM needs to be defined when building the project
    * if code has been located to RAM and interrupts are used.
    * Otherwise the interrupt table located in flash will be used.
    * See also the <system_*.c> file and how the SystemInit() function updates
    * SCB->VTOR register.
    * E.g. SCB->VTOR = 0x20000000;

    // Structures to hold the initialisation data
    GPIO_InitTypeDef GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;

    // enable the peripherals we're going to use
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

    // Usart1 Tx is on GPIOB pin 6 as an alternative function
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    // Connect pin 6 to the USART
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);

    // init the USART to 8:N:1 at 9600 baud as specified in the
    // SerLCD data sheet
    USART_InitStruct.USART_BaudRate = 9600;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStruct);

    // Enable USART1 peripheral

    // Clear the screen using the commands specified in the SerLCD data sheet.
    // The while loops test the 'Transmission Complete' flag in the USART
    // status register to make sure the previous character has gone before
    // sending the next one.
    while( !(USART1->SR & USART_SR_TC) );
    USART_SendData(USART1, 0xfe);

    while( !(USART1->SR & USART_SR_TC) );
    USART_SendData(USART1, 0x01);

    // something to display
    char* messageText = "Testing 1 2 3";

    // index into the display string
    int i = 0;

    // display the message
    while (i < strlen(messageText))
        while( !(USART1->SR & USART_SR_TC) );
        USART_SendData(USART1, messageText[i]);

    // Infinite loop
    while (1){}

This code is all pretty standard stuff. It starts by initialising the peripherals it needs. USART1 is located on APB2 and the IO on AHB1. Then the IO and the USART are both configured with pin PB6 set to its alternate function as USART1’s TX pin. I.e. we’re connecting the USART1 transmission pin (TX) to the SerLCD receive pin (RX).

Once all of that is done, it sends a control message to the SerLCD to clear the display. This is done by first sending a control byte (254 or 0xFE in hex) followed by the clear screen command (1). A full list of commands can be found in the SerLCD data sheet. In order to make sure it’s not sending data faster than the SerLCD can receive it, there are while loops between the sends that wait for the USART status register to tell it that the previous one has been sent successfully.

Ok so this is all very interesting and everything but there are a number of issues with. Firstly, it would be great if the code was a bit more re-usable and secondly, all those delays are taking up valuable processor cycles, drawing current and preventing us from doing other, more interesting things. In the next few posts I’ll attempt to address some of these issues and finally build a simple application that brings it all together. Meanwhile please feel free to give me a shout with any comments or feedback.

  1. what if i am using 16×2 simple lcd which has 16 pin?

    • Hi Jay, the interface to 16×2 lcds is a pretty standard parallel one. Unfortunately I don’t have the time at the moment to put together another tutorial but it is a well documented interface if you spend a bit of time searching for it.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: