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

24 comments

  • Hi,
    Im interfacing MSP430F5529 with MAX17055 fuel guage. while reading 16 bit value, the first byte im receiving is 0. so while reading multiple registers continuously the data exchange is happening, but im getting the correct data. Can anyone suggest me what will be the issue? why im getting 0 in first byte?

    read16_bit data code:

    uint16_t value = 0;
    USCI_B_I2C_setslaveaddress(USCI_B1_BASE, slave_address);
    USCI_B_I2C_setmode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);
    USCI_B_I2C_masterSendStart(USCI_B1_BASE);
    while (!USCI_B_I2C_masterSendStart(USCI_B1_BASE));

    USCI_B_I2C_mastterSendSingleByte(USCI_B1_BASE, reg_address);

    USCI_B_I2C_setslaveaddress(USCI_B1_BASE, slave_address);
    USCI_B_I2C_setmode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);
    USCI_B_I2C_masterReceiveMultiByteStart(USCI_B1_BASE);

    uint8_t lb = USCI_B_I2C_masterReceiveMultiByteNext(USCI_B1_BASE);
    uint8_t hb = USCI_B_I2C_masterReceiveMultiByteFinish(USCI_B1_BASE);

    while (USCI_B_I2C_isBusBusy(USCI_B_BASE));

    value = lb << 8;
    value |= hb;
    return value;

  • Hi, im trying to send the command from the terminal view. i can able to send the command and tried to blink p1.0 led in msp430f5529 controller, its working fine. And im using led driver IS31FL3236A interfaced with msp430f5529 controller, i can able to interface im getting the expected output.

    now i need to send the command from seriak monitor based on that command i2c communication need to start. both communication are working fine, when it runs separately. its not working when i tried to combine.

    any one had any idea, why it is happening or what will be the issue?

    • It could be due to:

      1. conflicts in clock settings
      2. hardware conflict like pin mapping
      3. code is getting stuck or waiting for one communication line to finish
      4. use of polling method instead of interrupt-driven coding

      • Hi, thank you for the respose.
        Do I need to use different clock initialization for I2C and UART communication? if YES, can you explain how to do that?

      • Is there any example on how to implement polling method in uart?

        • Why go for polling method when it is a blocking method of coding? It is better to use interrupts instead at least for UART receive.

          • yes!! currently in my code, only for uart im using interrupts to recieve command from serial monitor. Im not using interrupt for I2C communication.

          • so the issue is must be in clock initialization. right?

            For UART, im using USCI_A1_BASE. and for I2C, im using USCI_B1_BASE.

            And another thing i need to ask is, in uart when i tried blink led(p1.0) in msp430f5529 by passing command. here, without clock I’m getting output. how it is possible?

            And for both i2c and uart i gave SMCLK with 1Mhz

  • I am surprised and happy to find this tutorial on the F5529 as TI makes a lot of different devices.
    Thank you very much for putting in the extra knowledge in each segment, made reading worthwhile.
    Good Work!

  • lovely tutorial but to be honest I don’t think I’d be investing my time on this board to start with it’s not cheap and readily available as the stm32 boards can you please do more tutorials on stm32 board’s and the stc micros thanks

  • Hello, I try to program MSP430FR6047 but i get error “the debug interface to the device has been secured”. when flashing using uniflash and when program using CCS this happen. can you help me to solve this problem

  • Pingback: Tinkering TI MSP430F5529 – gStore

  • Hello
    I am doing project of msp430g2553 interface(using i2c communication) with temp 100(temperature sensor) and try to read the temperature in dispaly(16*2) but didn’t get the out put (using code composer studio) can u share me any example code for this project

    Thank you sir,

  • Where is lcd_print.h?

  • You want the truth? TI makes and sell “underpowered micros”, you know? Low everything, not only the power but also peripherals. So the price is not justified.

    Otherwise, if I’ll move there, I’ll introduce them to my small hobby projects – there are still some advantages.

Leave a Reply

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