#include <msp430.h>
//#include "LCD.h"
//#include "CLK.h"
// Port 1 defines
#define BTN BIT3
#define DB4 BIT4
#define DB5 BIT5
#define DB6 BIT6
#define DB7 BIT7
// Port 2 defines
#define RS BIT0
#define RW BIT1
#define E BIT2
// CGRAM character defines
#define PacMan 0x00
#define Ghost 0x01
#define Dot 0x02
/*
* main.c
*
* Title: Project 1 Hello World
*
* Authors: Daniel Hodges & Omar Arriaga
*
* Description: Displays "Hello World!" on LCD. Press BTN -> displays "Pac-Man!",
* after 5 seconds displays a Pac-Man animation.
*
* I/O Ports: (x = no connect)
*
* MSP: P1.[0 1 2 3 4 5 6 7]
* LCD: DB[x x x x 4 5 6 7]
*
* MSP: P2.[ 0 1 2 3 4 5 6 7]
* LCD: RS R/W E x x x x x
*
*/
// function and variable declarations
void lcd_wrt_cmd_nibble(unsigned short cmd);
void lcd_wrt_data_nibble(unsigned short data);
void lcd_init_seq_nibble();
void lcd_clear_display();
void lcd_set_DDRAM(unsigned short address);
void lcd_create_characters();
void lcd_display_HelloWorld();
void lcd_display_PacMan();
void lcd_animation_PacMan();
void clk_set_16MHZ();
void delay_clk_s(int sec);
void delay_clk_ms(int ms);
void delay_clk_us(int us);
char toggle_message = 0x00;
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
clk_set_16MHZ(); // set clock to 16MHz
P1DIR |= (DB4 + DB5 + DB6 + DB7); // sets ports P1.4-7 to outputs
P2DIR |= (RS + RW + E); // sets ports P2.0-2 to outputs (RS, R/W, E)
P1IE |= BTN; // P1.3 interrupt enabled
P1REN |= BTN; // add internal pullup to P1.3
P1OUT |= BTN; // selects pullup for P1.3
P1IES |= BTN; // P1.3 interrupt low to high enable
P1IFG &= ~BTN; // P1.3 IFG cleared
_enable_interrupts();
lcd_init_seq_nibble(); // initialize sequence for LCD nibble mode (2-line mode, 5x8 dots, cursor off, increment, no shifting)
lcd_create_characters(); // creates characters and stores into CGRAM
// infinite loop waiting for button press to toggle between messages
for(;;){
if (toggle_message == 0x00){
lcd_display_HelloWorld();
toggle_message |= 0x02;
}
else if (toggle_message == 0x01){
lcd_display_PacMan();
delay_clk_s(5); // wait for 5s
lcd_animation_PacMan();
}
else{
}
}
}
//write instruction to LCD in nibble mode (writes one nibble at a time)
void lcd_wrt_cmd_nibble(unsigned short cmd){
_disable_interrupts();
P2OUT = 0x00; // clear RS, R/W, E
P2OUT = 0x04; // set E
P1OUT &= 0x0F;
P1OUT |= (cmd << 4) & 0xF0; // write command data
delay_clk_us(1); // wait for more than 220ns
P2OUT = 0x00; // clear E
delay_clk_us(80); // wait for more than 39us
_enable_interrupts();
}
// write data to LCD in nibble mode (writes both nibbles at same time)
void lcd_wrt_data_nibble(unsigned short data){
_disable_interrupts();
// write upper 4-bits
P2OUT = 0x01; // Set RS; clear R/W, E
P2OUT = 0x05; // set E and RS
P1OUT &= 0x0F;
P1OUT |= data & 0xF0; // write command data
delay_clk_us(1); // wait for more than 220ns
P2OUT = 0x01; // clear E; Set RS
delay_clk_us(80); // wait for more than 39us
// write lower 4-bits
P2OUT = 0x01; // Set RS; clear R/W, E
P2OUT = 0x05; // set E and RS
P1OUT &= 0x0F;
P1OUT |= (data << 4) & 0xF0; // write command data
delay_clk_us(1); // wait for more than tw = 220ns
P2OUT = 0x01; // clear E; Set RS
delay_clk_us(80); // wait for more than 39us
_enable_interrupts();
}
// initialize sequence for LCD nibble mode (2-line mode, 5x8 dots, cursor off, increment, no shifting)
void lcd_init_seq_nibble(){
_disable_interrupts();
delay_clk_ms(60); // wait for more than 30ms after power on
//function set w/ 2-line mode and 5x8 dots
lcd_wrt_cmd_nibble(0b0010);
lcd_wrt_cmd_nibble(0b0010);
lcd_wrt_cmd_nibble(0b1100);
// Display On/Off Control w/ cursor off
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_cmd_nibble(0b1100);
// Display Clear
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_cmd_nibble(0b0001);
delay_clk_ms(5); // wait for more than 1.53ms after function set
// Entry Mode Set w/ increment and no shifting
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_cmd_nibble(0b0110);
_enable_interrupts();
}
// clears lcd display
void lcd_clear_display(){
_disable_interrupts();
// Display Clear
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_cmd_nibble(0b0001);
delay_clk_ms(5);
_enable_interrupts();
}
// sets DDRAM address for line 1
void lcd_set_DDRAM_address(unsigned short address){
lcd_wrt_cmd_nibble((BIT7 + (address & 0x70)) >> 4);
lcd_wrt_cmd_nibble(address & 0x0F);
}
// creates characters and stores them in CGRAM
void lcd_create_characters(){
_disable_interrupts();
// put PacMan into CGRAM address 0
lcd_wrt_cmd_nibble(0b0100);
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_data_nibble(0b00000000);
lcd_wrt_data_nibble(0b00001110);
lcd_wrt_data_nibble(0b00011011);
lcd_wrt_data_nibble(0b00011110);
lcd_wrt_data_nibble(0b00011100);
lcd_wrt_data_nibble(0b00011110);
lcd_wrt_data_nibble(0b00011111);
lcd_wrt_data_nibble(0b00001110);
// put Ghost into CGRAM address 1
lcd_wrt_cmd_nibble(0b0100);
lcd_wrt_cmd_nibble(0b1000);
lcd_wrt_data_nibble(0b00000000);
lcd_wrt_data_nibble(0b00001110);
lcd_wrt_data_nibble(0b00011111);
lcd_wrt_data_nibble(0b00010101);
lcd_wrt_data_nibble(0b00011111);
lcd_wrt_data_nibble(0b00011111);
lcd_wrt_data_nibble(0b00010101);
lcd_wrt_data_nibble(0b00000000);
// put Dot into CGRAM address 2
lcd_wrt_cmd_nibble(0b0101);
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_data_nibble(0b00000000);
lcd_wrt_data_nibble(0b00000000);
lcd_wrt_data_nibble(0b00000000);
lcd_wrt_data_nibble(0b00001110);
lcd_wrt_data_nibble(0b00001110);
lcd_wrt_data_nibble(0b00001110);
lcd_wrt_data_nibble(0b00000000);
lcd_wrt_data_nibble(0b00000000);
_enable_interrupts();
}
// displays "Hello World!" on display
void lcd_display_HelloWorld(){
_disable_interrupts();
lcd_clear_display();
lcd_set_DDRAM_address(0x04); // Set DDRAM address to 4 (line 1)
lcd_wrt_data_nibble('H'); // Set DDRAM to H
lcd_wrt_data_nibble('e'); // Set DDRAM to e
lcd_wrt_data_nibble('l'); // Set DDRAM to l
lcd_wrt_data_nibble('l'); // Set DDRAM to l
lcd_wrt_data_nibble('o'); // Set DDRAM to o
lcd_wrt_data_nibble(' '); // Set DDRAM to ' '
lcd_wrt_data_nibble('W'); // Set DDRAM to W
lcd_wrt_data_nibble('o'); // Set DDRAM to o
lcd_wrt_data_nibble('r'); // Set DDRAM to r
lcd_wrt_data_nibble('l'); // Set DDRAM to l
lcd_wrt_data_nibble('d'); // Set DDRAM to d
lcd_wrt_data_nibble('!'); // Set DDRAM to !
_enable_interrupts();
}
// displays "Pac-Man!" on display
void lcd_display_PacMan(){
_disable_interrupts();
lcd_clear_display();
lcd_set_DDRAM_address(0x04); // Set DDRAM address to 4 (line 1)
lcd_wrt_data_nibble('P'); // Set DDRAM to P
lcd_wrt_data_nibble('a'); // Set DDRAM to a
lcd_wrt_data_nibble('c'); // Set DDRAM to c
lcd_wrt_data_nibble('-'); // Set DDRAM to -
lcd_wrt_data_nibble('M'); // Set DDRAM to M
lcd_wrt_data_nibble('a'); // Set DDRAM to a
lcd_wrt_data_nibble('n'); // Set DDRAM to n
lcd_wrt_data_nibble('!'); // Set DDRAM to !
_enable_interrupts();
}
// displays a Pac-Man animation
void lcd_animation_PacMan(){
_disable_interrupts();
lcd_clear_display();
// Display On/Off Control w/ cursor on and blinking
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_cmd_nibble(0b1111);
lcd_set_DDRAM_address(0x03); // Set DDRAM address to 4 (line 1)
lcd_wrt_data_nibble(' '); // Set DDRAM to ' '
delay_clk_s(1);
lcd_wrt_data_nibble(PacMan); // Set DDRAM to PacMan
delay_clk_ms(750);
// for loop puts 10 dots in a row
unsigned short i;
for (i=0; i<9; i++){
lcd_wrt_data_nibble(Dot); // Set DDRAM to Dot
delay_clk_ms(750);
}
lcd_wrt_data_nibble(Ghost); // Set DDRAM to Ghost
// Display On/Off Control w/ cursor off
lcd_wrt_cmd_nibble(0b0000);
lcd_wrt_cmd_nibble(0b1100);
delay_clk_ms(750);
// PacMan eats dots
for (i=4; i<14; i++){
lcd_set_DDRAM_address(i);
lcd_wrt_data_nibble(' '); // Set DDRAM to ' '
lcd_wrt_data_nibble(PacMan); // Set DDRAM to PacMan
delay_clk_ms(500);
}
// PacMan hits ghost and dies (blinks on and off)
for (;;){
lcd_set_DDRAM_address(14);
lcd_wrt_data_nibble(' '); // Set DDRAM to ' '
delay_clk_ms(500);
lcd_set_DDRAM_address(14);
lcd_wrt_data_nibble(PacMan); // Set DDRAM to PacMan
delay_clk_ms(500);
}
}
// sets clock to 16MHz
void clk_set_16MHZ(){
//16Mhz
if (CALBC1_16MHZ==0xFF) // If calibration constant erased
{
while(1); // do not load, trap CPU!!
}
DCOCTL = 0; // Select lowest DCOx and MODx settings
BCSCTL1 = CALBC1_16MHZ; // Set range
DCOCTL = CALDCO_16MHZ; // Set DCO step + modulation
}
// delay clock by # of seconds
void delay_clk_s(int sec){
int i;
for (i=0; i< sec; i++){
_delay_cycles(16000000);
}
}
// delay clock by # of milliseconds
void delay_clk_ms(int ms){
int i;
for (i=0; i< ms; i++){
_delay_cycles(16000);
}
}
// delay clock by # of microseconds
void delay_clk_us(int us){
int i;
for (i=0; i< us; i++){
_delay_cycles(16);
}
}
// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
delay_clk_ms(100);
toggle_message &= BIT0;
toggle_message ^= BIT0;
P1IFG &= ~BTN; // P1.3 IFG cleared
delay_clk_ms(100);
}