Tinkering TI MSP430F5529

3 Wire (Software SPI) LCD

Another way of driving alphanumerical LCDs is by using software SPI. Software SPI uses the same concept as in software I2C. The only exceptions here are an additional GPIO pin and the mode of communication. We can use shift registers like CD4094B or 74HC595 for this purpose. Both are equally fine. We can also use dedicated SPI-based port expander like Microchip’s MCP23S17.

Software SPI, as in this example, can be used to read/write other SPI-compatible hardware. This is often a requirement when hardware SPI or its pins are inaccessible.

Code Example

lcd.h

#include "driverlib.h"
#include "delay.h"

#define SDO_PORT                    GPIO_PORT_P8
#define SCK_PORT                    GPIO_PORT_P8
#define STB_PORT                    GPIO_PORT_P3

#define SDO_pin                     GPIO_PIN1
#define SCK_pin                     GPIO_PIN2
#define STB_pin                     GPIO_PIN7

#define SDO_DIR_OUT                 GPIO_setAsOutputPin(SDO_PORT, SDO_pin)
#define SCK_DIR_OUT                 GPIO_setAsOutputPin(SCK_PORT, SCK_pin)
#define STB_DIR_OUT                 GPIO_setAsOutputPin(STB_PORT, STB_pin)

#define SDO_HIGH                    GPIO_setOutputHighOnPin(SDO_PORT, SDO_pin)
#define SDO_LOW                     GPIO_setOutputLowOnPin(SDO_PORT, SDO_pin)
#define SCK_HIGH                    GPIO_setOutputHighOnPin(SCK_PORT, SCK_pin)
#define SCK_LOW                     GPIO_setOutputLowOnPin(SCK_PORT, SCK_pin)
#define STB_HIGH                    GPIO_setOutputHighOnPin(STB_PORT, STB_pin)
#define STB_LOW                     GPIO_setOutputLowOnPin(STB_PORT, STB_pin)

#define clear_display               0x01
#define goto_home                   0x02
         
#define cursor_direction_inc        (0x04 | 0x02)
#define cursor_direction_dec        (0x04 | 0x00)
#define display_shift               (0x04 | 0x01)
#define display_no_shift            (0x04 | 0x00)

#define display_on                  (0x08 | 0x04)
#define display_off                 (0x08 | 0x02)
#define cursor_on                   (0x08 | 0x02)
#define cursor_off                  (0x08 | 0x00)
#define blink_on                    (0x08 | 0x01)
#define blink_off                   (0x08 | 0x00)
                                    
#define _8_pin_interface            (0x20 | 0x10)
#define _4_pin_interface            (0x20 | 0x00)
#define _2_row_display              (0x20 | 0x08)
#define _1_row_display              (0x20 | 0x00)
#define _5x10_dots                  (0x20 | 0x40)
#define _5x7_dots                   (0x20 | 0x00)
                                   
#define dly                         1

                                                                          
void SIPO(void);
void LCD_init(void);
void LCD_command(unsigned char value);
void LCD_send_data(unsigned char value); 
void LCD_4bit_send(unsigned char lcd_data);                                      
void LCD_putstr(char *lcd_string);
void LCD_putchar(char char_data);
void LCD_clear_home(void);
void LCD_goto(unsigned char x_pos, unsigned char y_pos);

lcd.c

#include "lcd.h"

static unsigned char data_value;

void SIPO(void)
{
    unsigned char bit = 0x00;
    unsigned char clk = 0x08;
    unsigned char temp = 0x00;
    
    temp = data_value;
    STB_LOW;
    
    while(clk > 0)
    {
        bit = ((temp & 0x80) >> 0x07);
        bit &= 0x01;

        switch(bit)
        {
            case 0:
            {
                SDO_LOW;
                break;
            }
            default:
            {
                SDO_HIGH;
                break;
            }

        }

        SCK_HIGH;

        temp <<= 1;
        clk--;

        SCK_LOW;
    }

    STB_HIGH;
}
                              

void LCD_init(void)
{
    SDO_DIR_OUT;
    SCK_DIR_OUT;
    STB_DIR_OUT;

    data_value = 0x08;
    SIPO();
    delay_ms(10);
    
    data_value = 0x30;
    SIPO();
                              
    data_value |= 0x08;
    SIPO();
    delay_ms(dly);
    data_value &= 0xF7;
    SIPO();
    delay_ms(dly); 
             
    data_value = 0x30;
    SIPO();
                    
    data_value |= 0x08;
    SIPO();
    delay_ms(dly);
    data_value &= 0xF7;
    SIPO();
    delay_ms(dly);

    data_value = 0x30;
    SIPO();

    data_value |= 0x08;
    SIPO();
    delay_ms(dly);
    data_value &= 0xF7;
    SIPO();
    delay_ms(dly);

    data_value = 0x20;
    SIPO();
                    
    data_value |= 0x08;
    SIPO();
    delay_ms(dly);
    data_value &= 0xF7;
    SIPO();
    delay_ms(dly);

    LCD_command(_4_pin_interface | _2_row_display | _5x7_dots);         
    LCD_command(display_on | cursor_off | blink_off);     
    LCD_command(clear_display);         
    LCD_command(cursor_direction_inc | display_no_shift);        
}   

void LCD_command(unsigned char value)
{                                   
    data_value &= 0xFB;
    SIPO();
    LCD_4bit_send(value);           
}
   

void LCD_send_data(unsigned char value)
{                               
    data_value |= 0x04;
    SIPO();
    LCD_4bit_send(value);
}  
    

void LCD_4bit_send(unsigned char lcd_data)       
{
    unsigned char temp = 0x00;
    
    temp = (lcd_data & 0xF0);
    data_value &= 0x0F;
    data_value |= temp;
    SIPO();
                                          
    data_value |= 0x08;
    SIPO();
    delay_ms(dly);
    data_value &= 0xF7;
    SIPO();
    delay_ms(dly);

    temp = (lcd_data & 0x0F);
    temp <<= 0x04;
    data_value &= 0x0F;
    data_value |= temp;
    SIPO();
               
    data_value |= 0x08;
    SIPO();
    delay_ms(dly);
    data_value &= 0xF7;
    SIPO();
    delay_ms(dly);
}  

void LCD_putstr(char *lcd_string)
{
    while (*lcd_string != '\0')   
    {
        LCD_send_data(*lcd_string);
        lcd_string++;
    };
}

void LCD_putchar(char char_data)
{
    LCD_send_data(char_data);
}

void LCD_clear_home(void)
{
    LCD_command(clear_display);
    LCD_command(goto_home);
}

void LCD_goto(unsigned char x_pos,unsigned char y_pos)
{                                                   
    if(y_pos == 0)    
    {                              
        LCD_command(0x80 | x_pos);
    }
    else 
    {                                              
        LCD_command(0x80 | 0x40 | x_pos); 
    }
}

main.c

#include "driverlib.h"
#include "delay.h"
#include "lcd.h"

void clock_init(void);
void show_value(unsigned char value);

void main(void)
{
    unsigned char s = 0x00;

    char txt1[] = {"MICROARENA"};
    char txt2[] = {"SShahryiar"};
    char txt3[] = {"MSP430F5529LP"};
    char txt4[] = {"Launchpad!"};

    WDT_A_hold(WDT_A_BASE);

    clock_init();

    LCD_init();

    LCD_clear_home();

    LCD_goto(3, 0);
    LCD_putstr(txt1);
    LCD_goto(3, 1);
    LCD_putstr(txt2);
    delay_ms(4000);

    LCD_clear_home();

    for(s = 0; s < 13; s++)
    {
        LCD_goto((1 + s), 0);
        LCD_putchar(txt3[s]);
        delay_ms(60);
    }
    for(s = 0; s < 10; s++)
    {
        LCD_goto((3 + s), 1);
        LCD_putchar(txt4[s]);
        delay_ms(60);
    }
    delay_ms(4000);

    s = 0;
    LCD_clear_home();

    LCD_goto(3, 0);
    LCD_putstr(txt1);

    while(1)
    {
        show_value(s);
        s++;
        delay_ms(400);
    };
}

void clock_init(void)
{
    PMM_setVCore(PMM_CORE_LEVEL_3);

    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,
                                               (GPIO_PIN4 | GPIO_PIN2));

    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P5,
                                                (GPIO_PIN5 | GPIO_PIN3));

    UCS_setExternalClockSource(XT1_FREQ,
                               XT2_FREQ);

    UCS_turnOnXT2(UCS_XT2_DRIVE_4MHZ_8MHZ);

    UCS_turnOnLFXT1(UCS_XT1_DRIVE_0,
                    UCS_XCAP_3);

    UCS_initClockSignal(UCS_FLLREF,
                        UCS_XT2CLK_SELECT,
                        UCS_CLOCK_DIVIDER_4);

    UCS_initFLLSettle(MCLK_KHZ,
                      MCLK_FLLREF_RATIO);

    UCS_initClockSignal(UCS_SMCLK,
                        UCS_REFOCLK_SELECT,
                        UCS_CLOCK_DIVIDER_1);

    UCS_initClockSignal(UCS_ACLK,
                        UCS_XT1CLK_SELECT,
                        UCS_CLOCK_DIVIDER_1);
}

void show_value(unsigned char value)
{
   unsigned char ch = 0x00;

   ch = ((value / 100) + 0x30);
   LCD_goto(6, 1);
   LCD_putchar(ch);

   ch = (((value / 10) % 10) + 0x30);
   LCD_goto(7, 1);
   LCD_putchar(ch);

   ch = ((value % 10) + 0x30);
   LCD_goto(8, 1);
   LCD_putchar(ch);
}

Hardware Setup

Explanation

As with the past example, GPIO pins will be used to emulate software SPI. Thus, we need to declare them first. SDO pin is serial data out pin, SCK is SPI clock pin and lastly, STB is SPI strobe/chip enable (CE)/slave select (SS) pin. Though 74HC595 and CD4094B are functionally same, my favorite is CD4094B due to its wide voltage range. This is what I have used here and the SPI pins are named in its respect.

#define SDO_PORT                    GPIO_PORT_P8
#define SCK_PORT                    GPIO_PORT_P8
#define STB_PORT                    GPIO_PORT_P3

#define SDO_pin                     GPIO_PIN1
#define SCK_pin                     GPIO_PIN2
#define STB_pin                     GPIO_PIN7


#define SDO_DIR_OUT                 GPIO_setAsOutputPin(SDO_PORT, SDO_pin)
#define SCK_DIR_OUT                 GPIO_setAsOutputPin(SCK_PORT, SCK_pin)
#define STB_DIR_OUT                 GPIO_setAsOutputPin(STB_PORT, STB_pin)

#define SDO_HIGH                    GPIO_setOutputHighOnPin(SDO_PORT, SDO_pin)
#define SDO_LOW                     GPIO_setOutputLowOnPin(SDO_PORT, SDO_pin)
#define SCK_HIGH                    GPIO_setOutputHighOnPin(SCK_PORT, SCK_pin)
#define SCK_LOW                     GPIO_setOutputLowOnPin(SCK_PORT, SCK_pin)
#define STB_HIGH                    GPIO_setOutputHighOnPin(STB_PORT, STB_pin)
#define STB_LOW                     GPIO_setOutputLowOnPin(STB_PORT, STB_pin)

We will not be modifying any function of the LCD itself. It is same as like other LCD examples. This is why we will just be going through the software function responsible for port expansion. Function named SPIO is responsible for serially taking data from host micro and providing 8-bit parallel output, hence the name SIPO.

Strobe pin is held low before serially shifting data one bit at a time. Data is shifted on every high to low transition of clock signal for 8 clock transitions, i.e. 8-bit of data is transferred. After shifting out 8 bits of data, the strobe pin is held high and at that moment the 8 output pins of CD4094B shift-register are updated.

void SIPO(void)
{
    unsigned char bit = 0x00;
    unsigned char clk = 0x08;
    unsigned char temp = 0x00;
    
    temp = data_value;
    STB_LOW;
    
    while(clk > 0)
    {
        bit = ((temp & 0x80) >> 0x07);
        bit &= 0x01;

        switch(bit)
        {
            case 0:
            {
                SDO_LOW;
                break;
            }
            default:
            {
                SDO_HIGH;
                break;
            }
        }

        SCK_HIGH;

        temp <<= 1;
        clk--;

        SCK_LOW;
    }

    STB_HIGH;
}

Demo

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

Related Posts

14 comments

Leave a Reply

Your email address will not be published. Required fields are marked *