Skip to content

Instantly share code, notes, and snippets.

@cr1901
Created February 21, 2018 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cr1901/fea469d4b64487c5cb925b42300deba7 to your computer and use it in GitHub Desktop.
Save cr1901/fea469d4b64487c5cb925b42300deba7 to your computer and use it in GitHub Desktop.
2013 MSP430 LCD Demo
#include <msp430g2231.h>
//const char * lcd_string = "Hello World!";
#define DB7 BIT7
#define DB6 BIT6
#define DB5 BIT5
#define DB4 BIT4
#define ADDR(_x) _x << 4
#define RS BIT0
#define EN BIT1
#define PAUSE(_x) __delay_cycles(_x)
volatile int counter = 0;
volatile int elapsed = 0;
volatile int centiseconds = 0;
volatile int seconds = 0;
volatile int minutes = 0;
volatile int hours = 0;
char time[] = "00:00:00";
char ascii_number[7] = {' '};
//Courtesy: https://sites.google.com/site/cacheattack/msp-projects/msp430-launchpad-with-lcd-module
#define LCM_DIR P1DIR
#define LCM_OUT P1OUT
//
// Define symbolic LCM - MCU pin mappings
// We've set DATA PIN TO 4,5,6,7 for easy translation
//
#define LCM_PIN_RS BIT0 // P1.0
#define LCM_PIN_EN BIT1 // P1.1
#define LCM_PIN_D7 BIT5 // P1.7
#define LCM_PIN_D6 BIT4 // P1.6
#define LCM_PIN_D5 BIT3 // P1.5
#define LCM_PIN_D4 BIT2 // P1.4
#define LCM_PIN_MASK ((LCM_PIN_RS | LCM_PIN_EN | LCM_PIN_D7 | LCM_PIN_D6 | LCM_PIN_D5 | LCM_PIN_D4))
#define FALSE 0
#define TRUE 1
//
// Routine Desc:
//
// This is the function that must be called
// whenever the LCM needs to be told to
// scan it's data bus.
//
// Parameters:
//
// void.
//
// Return
//
// void.
//
void PulseLcm()
{
//
// pull EN bit low
//
LCM_OUT &= ~LCM_PIN_EN;
PAUSE(200);
//
// pull EN bit high
//
LCM_OUT |= LCM_PIN_EN;
PAUSE(200);
//
// pull EN bit low again
//
LCM_OUT &= (~LCM_PIN_EN);
PAUSE(200);
}
//
// Routine Desc:
//
// Send a byte on the data bus in the 4 bit mode
// This requires sending the data in two chunks.
// The high nibble first and then the low nible
//
// Parameters:
//
// ByteToSend - the single byte to send
//
// IsData - set to TRUE if the byte is character data
// FALSE if its a command
//
// Return
//
// void.
//
void SendByte(char ByteToSend, int IsData)
{
//
// clear out all pins
//
LCM_OUT &= (~LCM_PIN_MASK);
//
// set High Nibble (HN) -
// usefulness of the identity mapping
// apparent here. We can set the
// DB7 - DB4 just by setting P1.7 - P1.4
// using a simple assignment
//
LCM_OUT |= ((ByteToSend & 0xF0) >> 2);
if (IsData == TRUE)
{
LCM_OUT |= LCM_PIN_RS;
}
else
{
LCM_OUT &= ~LCM_PIN_RS;
}
//
// we've set up the input voltages to the LCM.
// Now tell it to read them.
//
PulseLcm();
//
// set Low Nibble (LN) -
// usefulness of the identity mapping
// apparent here. We can set the
// DB7 - DB4 just by setting P1.7 - P1.4
// using a simple assignment
//
LCM_OUT &= (~LCM_PIN_MASK);
LCM_OUT |= ((ByteToSend & 0x0F) << 2);
if (IsData == TRUE)
{
LCM_OUT |= LCM_PIN_RS;
}
else
{
LCM_OUT &= ~LCM_PIN_RS;
}
//
// we've set up the input voltages to the LCM.
// Now tell it to read them.
//
PulseLcm();
}
//
// Routine Desc:
//
// Set the position of the cursor on the screen
//
// Parameters:
//
// Row - zero based row number
//
// Col - zero based col number
//
// Return
//
// void.
//
void LcmSetCursorPosition(char Row, char Col)
{
char address;
//
// construct address from (Row, Col) pair
//
if (Row == 0)
{
address = 0;
}
else
{
address = 0x40;
}
address |= Col;
SendByte(0x80 | address, FALSE);
}
//
// Routine Desc:
//
// Clear the screen data and return the
// cursor to home position
//
// Parameters:
//
// void.
//
// Return
//
// void.
//
void ClearLcmScreen()
{
//
// Clear display, return home
//
SendByte(0x01, FALSE);
SendByte(0x02, FALSE);
}
//
// Routine Desc:
//
// Initialize the LCM after power-up.
//
// Note: This routine must not be called twice on the
// LCM. This is not so uncommon when the power
// for the MCU and LCM are separate.
//
// Parameters:
//
// void.
//
// Return
//
// void.
//
void InitializeLcm(void)
{
//
// set the MSP pin configurations
// and bring them to low
//
LCM_DIR |= LCM_PIN_MASK;
LCM_OUT &= ~(LCM_PIN_MASK);
//
// wait for the LCM to warm up and reach
// active regions. Remember MSPs can power
// up much faster than the LCM.
//
PAUSE(100000);
//
// initialize the LCM module
//
// 1. Set 4-bit input
//
LCM_OUT &= ~LCM_PIN_RS;
LCM_OUT &= ~LCM_PIN_EN;
/* Switch to 8-bit mode in case reset occurred. */
LCM_OUT = (0x30 >> 2);
PulseLcm();
PulseLcm();
LCM_OUT = (0x20 >> 2);
PulseLcm();
//
// set 4-bit input - second time.
// (as reqd by the spec.)
//
SendByte(0x28, FALSE);
//
// 2. Display on, cursor on, blink cursor
//0x0E- Cursor not blinking
SendByte(0x0F, FALSE);
//
// 3. Cursor move auto-increment
//
SendByte(0x06, FALSE);
}
//
// Routine Desc
//
// Print a string of characters to the screen
//
// Parameters:
//
// Text - null terminated string of chars
//
// Returns
//
// void.
//
void PrintStr(char *Text)
{
char *c;
c = Text;
while ((c != 0) && (*c != 0))
{
SendByte(*c, TRUE);
c++;
}
}
//http://www.strudel.org.uk/itoa/
/**
* C++ version 0.4 char* style "itoa":
* Written by Lukás Chmela
* Released under GPLv3.
*/
char* itoa(int value, char* result, int base) {
// check that the base if valid
if (base < 2 || base > 16) { *result = '\0'; return result; }
char* ptr = result, *ptr1 = result, tmp_char;
int tmp_value;
do {
tmp_value = value;
value /= base;
*ptr++ = "fedcba9876543210123456789abcdef" [15 + (tmp_value - value * base)];
} while ( value );
// Apply negative sign
if (tmp_value < 0) *ptr++ = '-';
*ptr-- = '\0';
while(ptr1 < ptr) {
tmp_char = *ptr;
*ptr--= *ptr1;
*ptr1++ = tmp_char;
}
return result;
}
int main()
{
//unsigned int i = 0;
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer.
//TACTL = MC_0; //The output needs to be stopped!
//__delay_cycles(1000000);
//Set Auxilary clock to internal LFO
BCSCTL3 &= ~LFXT1S_0; //Don't mess with other bits
BCSCTL3 |= LFXT1S_2; /* Set ACLK to Very Low Power Oscillator-
supposedly needs to be external, according to pg. 44 of device datasheet. */
BCSCTL1 &= ~DIVA_0; //
BCSCTL1 |= DIVA_3; //Set ACLK to lowest speed (divide by 8 from 12000 Hz)
//TACCTL1 = ~CAP +
TACTL = TASSEL_1 + ID_2 + MC_1; //Use ACLK, divide by 4, use up mode.
//P1DIR |= 0x40; // P1DIR configures direction port pin (DIR) output or input.
// 1 is output, 0 is input. 0x40 is Green LED
TACCR0 = 2; // 188 approx 1Hz (12000/32) Period: TACCR0 + 1... might not be right for 50% duty cycle...
TACCTL0 = CCIE;
TACCR1 = 1; // 50% duty cycle
TACCTL1 = OUTMOD_3; //Compare Mode, Toggle output each time either TACCR1 or TACCR0 is reached.
P2SEL &= ~0xC0; //This is required: http://e2e.ti.com/support/microcontrollers/msp430/f/166/t/19225.aspx
/* Device datasheet does not say so, however... */
P2SEL |= BIT6; //http://e2e.ti.com/support/microcontrollers/msp430/f/166/t/36123.aspx
//P2OUT &= ~BIT6; //Set the PWM to 0 to discharge the charge pump and LCD power supply.
//Weird effects happen otherwise!
P2DIR |= BIT6; //Use peripheral Timer A Capture Control Out 1 and set port 2.6 as output.
//__delay_cycles(2000000);
InitializeLcm();
//__delay_cycles(1000000);
ClearLcmScreen();
PrintStr("Hello World!");
LcmSetCursorPosition(1, 0);
PrintStr("Time: ");
//itoa(elapsed, ascii_number, 10);
//PrintStr(ascii_number);
__bis_SR_register(LPM3_bits + GIE);
/* while(1)
{
} */
/* while (1)
{
__delay_cycles(1000);
} */
/* while(1) {
//What does the C standard say about assignments in if statements?
// i.e. if(P1IN &= 0x08)
if(P1IN & 0x08)
{
P1OUT |= 0x40;
//P2OUT |= 0x40;
}
else // Toggle P1.6
{
P1OUT &= 0x00;
//P2OUT &= 0x00;
}
// P1OUT register holds the status of the LED.
// Delay between Green LED toggles.
} */
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A0 (void)
{
// Timer A0 interrupt service routine
centiseconds = (centiseconds + 1)%125; //3 cycles per period... 125Hz
if(centiseconds == 0)
{
seconds = (seconds + 1)%60;
if(seconds < 10)
{
time[6] = '0';
itoa(seconds, &time[7], 10);
}
else
{
itoa(seconds, &time[6], 10);
}
if(seconds == 0)
{
minutes = (minutes + 1)%60;
if(minutes < 10)
{
time[3] = '0';
itoa(minutes, &time[4], 10);
}
else
{
itoa(minutes, &time[3], 10);
}
time[5] = ':';
if(minutes == 0)
{
hours = (hours + 1)%24;
if(hours < 10)
{
time[0] = '0';
itoa(hours, &time[1], 10);
}
else
{
itoa(hours, time, 10);
}
time[2] = ':';
}
}
LcmSetCursorPosition(1, 6);
PrintStr(time);
}
}
/* #pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A0 (void)
{
// Timer A0 interrupt service routine
counter = (counter + 1)%125; //3 cycles per period
if(counter == 0)
{
elapsed += 1;
LcmSetCursorPosition(1, 6);
itoa(elapsed, ascii_number, 10);
PrintStr(ascii_number);
}
} */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment