Jump to content
43oh

Magic Eight Ball


Recommended Posts

Here's my first attempt at having the TI Launchpad do something a little more than flashing LEDs. I used this project to learn more about the MSP430 interrupts, GPIO, low power mode and timers. It operates similar to the old "Magic Eight Ball" in that you ask a question, press S2 (P1.3) on the Launchpad, watch the blinking lights and look at the answer in a terminal program such as "minicom" in Linux or "putty.exe" in WinXP/Vista/Win7. You have to configure your comm program to read what ever port the Launchpad uses (for my Linux box it is /dev/ttyACM0 and for my Windows 7 box it was COM5 - the options are 9600,8,N,1 no flow control). I imagine a serial LCD device could be used, however, I don't have one. If anyone tries this project with a serial LCD display, please let me know how it works. I'm sure my code could be cleaned up to be more efficient, however, as I said I'm learning! The device pulls about 75-80 uA when it's waiting for a button press in LPM0 mode. Here is the code:

 

//******************************************************************************
//  Magic Eight Ball Project
//  UART code taken from msp430g2xx1_ta_uart9600.c example file.  
//  (D. Dang, Texas Instuments Inc.) 
//  MSP430G2xx1 Demo - Timer_A, Ultra-Low Pwr UART 9600 Echo, 32kHz ACLK 
//  ACLK = TACLK = LFXT1 = 32768Hz, MCLK = SMCLK = default DCO
//  //* An external watch crystal is required on XIN XOUT for ACLK *//
//
//  Description:  Simple Magic Eight Ball adaptation utilizing the MSP430 Launchpad.
//  To operate, simply connect Launchpad to PC USB port and fire up a comm program
//  such as "minicom" in Linux or "putty.exe" in Windows XP/Vista/7.  (Be sure to
//  configure your comm program for whatever comm port the Launchpad uses.)
//  Press S1 (Reset) on Launchpad. "Magic Eight Ball Ready" should appear in
//  terminal window.
//  Ask your question, press S2, watch the blinking lights and answer will appear
//  in the terminal window.
//  
//               MSP430G2xx1
//            -----------------
//        /|\|              XIN|-
//         | |                 | 32kHz
//         --|RST          XOUT|-
//       <---|P1.3 Switch      |
//           |   CCI0B/TXD/P1.1|-------->
//           |                 | 9600 8N1
//           |   CCI0A/RXD/P1.2|<--------
//
//  R.G. Rioux
//  July 2011
//  Built with CCS Version 4.2.3.00004
//******************************************************************************

#include "msp430g2231.h"
#include 			// rand() & srand() 

//------------------------------------------------------------------------------
// Hardware-related definitions
//------------------------------------------------------------------------------
#define UART_TXD   0x02                     // TXD on P1.1 (Timer0_A.OUT0)
#define UART_RXD   0x04                     // RXD on P1.2 (Timer0_A.CCI1A)
#define BUTTON	BIT3						// Button on P1.3

//------------------------------------------------------------------------------
// Conditions for 9600 Baud SW UART, SMCLK = 1MHz
//------------------------------------------------------------------------------
#define UART_TBIT_DIV_2     (1000000 / (9600 * 2))
#define UART_TBIT           (1000000 / 9600)

//------------------------------------------------------------------------------
// Global variables used for full-duplex UART communication
//------------------------------------------------------------------------------
unsigned int txData;                        // UART internal variable for TX
unsigned char rxBuffer;                     // Received UART character
//------------------------------------------------------------------------------
//  Global variables in program
//------------------------------------------------------------------------------
unsigned int randnum = 0;
int i;
char msgflag = 0;
static char *answer[10] = { "Yes, in due time.", "My sources say no.", "Definitely not.",
"Yes.", "Probably.", "I have my doubts.", "Who knows?", "Looking good!",
"Go for it!", "Forget about it!" };
//------------------------------------------------------------------------------
// Function prototypes
//------------------------------------------------------------------------------
void TimerA_UART_init(void);
void TimerA_UART_tx(unsigned char byte);
void TimerA_UART_print(char *string);

//------------------------------------------------------------------------------
// main()
//------------------------------------------------------------------------------
void main(void)
{
   WDTCTL = WDTPW + WDTHOLD;               // Stop watchdog timer

   DCOCTL = 0x00;                          // Set DCOCLK to 1MHz
   BCSCTL1 = CALBC1_1MHZ;
   DCOCTL = CALDCO_1MHZ;

   P1OUT = 0x00;                           // Initialize all GPIO
   P1SEL = UART_TXD;            // Timer function for TXD pin
   P1DIR = 0xFF & ~BUTTON;			// Set all pins but BUTTON to output
   P2OUT = 0x00;
   P2SEL = 0x00;
   P2DIR = 0xFF;
   P1IE |= BUTTON;		//P1.3 interrupt enabled
   P1IFG = 0x00;		//Clear all interrupt flags

   __enable_interrupt();

   TimerA_UART_init();                     // Start Timer_A UART
   TimerA_UART_print("Magic Eight Ball v0.01\r\n");
   TimerA_UART_print("READY.\r\n");

   while (1)
   {	
   	_bis_SR_register(LPM0_bits + GIE);
   	if (msgflag == 1)
   	{	
   		P1OUT |= BIT6;
   		for (i=1;i<=25;i++)
   		{
   			P1OUT ^= BIT0 + BIT6;	// Flash LEDs
   			__delay_cycles(100000);
   		}
   		P1OUT &= ~(BIT0 + BIT6);
   		TimerA_UART_print(answer[randnum]);		// Send message
   		TimerA_UART_print("\r\n");				// Send CRLF
   		msgflag = 0;							// Reset message flag   
   	}
   }
}
//------------------------------------------------------------------------------
// Function configures Timer_A for full-duplex UART operation
//------------------------------------------------------------------------------
void TimerA_UART_init(void)
{
   TACCTL0 = OUT;                          // Set TXD Idle as Mark = '1'
   TACCTL1 = SCS + CM1 + CAP + CCIE;       // Sync, Neg Edge, Capture, Int
   TACTL = TASSEL_2 + MC_2;                // SMCLK, start in continuous mode
}
//------------------------------------------------------------------------------
// Outputs one byte using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_tx(unsigned char byte)
{
   while (TACCTL0 & CCIE);                 // Ensure last char got TX'd
   TACCR0 = TAR;                           // Current state of TA counter
   TACCR0 += UART_TBIT;                    // One bit time till first bit
   TACCTL0 = OUTMOD0 + CCIE;               // Set TXD on EQU0, Int
   txData = byte;                          // Load global variable
   txData |= 0x100;                        // Add mark stop bit to TXData
   txData <<= 1;                           // Add space start bit
}

//------------------------------------------------------------------------------
// Prints a string over using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_print(char *string)
{
   while (*string) {
       TimerA_UART_tx(*string++);
   }
}
//------------------------------------------------------------------------------
// Timer_A UART - Transmit Interrupt Handler
//------------------------------------------------------------------------------
#pragma vector = TIMERA0_VECTOR
__interrupt void Timer_A0_ISR(void)
{
   static unsigned char txBitCnt = 10;

   TACCR0 += UART_TBIT;                    // Add Offset to CCRx
   if (txBitCnt == 0) {                    // All bits TXed?
       TACCTL0 &= ~CCIE;                   // All bits TXed, disable interrupt
       txBitCnt = 10;                      // Re-load bit counter
   }
   else {
       if (txData & 0x01) {
         TACCTL0 &= ~OUTMOD2;              // TX Mark '1'
       }
       else {
         TACCTL0 |= OUTMOD2;               // TX Space '0'
       }
       txData >>= 1;
       txBitCnt--;
   }
}      

// Switch interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
  	randnum = rand() % 10;	// random number 0 to 9
  							// Not very random, however, adequate for
						// this project.
msgflag = 1;			// message flag
   P1IFG &= ~BUTTON;		// reset interrupt flag
   _bic_SR_register_on_exit(LPM0_bits);
}
//------------------------------------------------------------------------------

Link to post
Share on other sites
  • 1 month later...

Magic Eight Ball Part II

I decided to update this project by adding code to generate a random seed for the random number generator. This gives a little better randomness to the answers. I also added a LCD display as pictured below. The random seed code is adapted from a translation of TI assembler code to C from NatureTM in the "Code Vault" section of this forum. I used the same LCD Display that is used in the "Using 3 wires to control parallel LCD display" by RobG. I had to use different ports to accommodate the random number generator code.

This project won't change the world, however, it kept kids busy for a while. :D:D:D

 

** Added delay toward beginning of main(void) to allow settling of MSP430. Prior to this change the reset button had to

be pressed to start the program.

 

post-3266-135135512614_thumb.jpg

 

post-3266-135135512624_thumb.jpg

 

post-3266-135135512634_thumb.jpg

 

//******************************************************************************
//  Magic Eight Ball Project
//
//  Description:  Simple Magic Eight Ball adaptation utilizing the MSP430 Launchpad.
//  To use, press S1, "Magic Eight Ball v0.03 - READY" should appear on the LCD display.
//  Ask your question, press S2, watch "<>" animation and an answer will appear
//  in the LCD display.
//
//  08/14/11 Improved randomness of answers.  Used NatureTM's code from the 43oh
//  forum to generate a random seed. Requires that P1.4 and P1.5 be in the Input
//  mode (floating).  Thanks NatureTM!
//
//  09/17/11 Adapted output to LCD Display.  Modified code from 43oh forum "Using
//	3 wires to control parallel LCD display" by RobG.  Thanks Robg!  Also modified
//  nuetron's code modification to break long lines up.  Thanks nuetron!
//  
//               MSP430G2xx1
//            -----------------
//        /|\|              XIN|-
//         | |                 | 32kHz
//         --|RST          XOUT|-
//       <---|P1.3 Switch      |
//           |   CCI0B/TXD/P1.1|--------> Pin 6 of Display (E)
//           |             P1.7|--------> Pin 1&2 of 74HC164 (AB)
//           |   CCI0A/RXD/P1.2|--------> Pin 8 of 74HC164 (CLK)
//
//  R.G. Rioux
//  September 2011
//  Built with CCS Version 4.2.4.00033
//******************************************************************************

#include "msp430g2231.h"
#include 			// rand() & srand()
#include 			// strlen() 

//------------------------------------------------------------------------------
// Display related definitions
//------------------------------------------------------------------------------
#define sendData(data) send(data, 1)
#define sendInstruction(data) send(data, 0)
#define initDisplay() sendInstruction(0x3C); sendInstruction(0x0C); clearDisplay(); sendInstruction(0x06)
#define clearDisplay() sendInstruction(0x01); _delay_cycles(2000)
#define DATAPIN BIT7		// P1.7
#define CLOCKPIN BIT2		// P1.2
#define ENABLEPIN BIT1		// P1.1

#define BUTTON	BIT3		// Button on P1.3

//------------------------------------------------------------------------------
// Display related functions and variables
//------------------------------------------------------------------------------
void send(char data, char registerSelect);
void sendStr(char data[], int length);
char charIndex = 0;
char bitCounter = 0;


//------------------------------------------------------------------------------
//  Global variables in program
//------------------------------------------------------------------------------
unsigned int seed;
unsigned int randnum = 0;
int i; int j; // indexes
char msgflag = 0;
// Used comma in string as a delimiter to break string up into two lines
static char *answer[11] = { "Yes,in due time.",
						"My sources say,no.",
						"Definitely not.",							
						"Yes.",
						"Probably.",
 						"I have my,doubts.",
  						"Who knows?",
   						"Looking good!",
						"Go for it!",
 						"Forget about it!",
  						"Are you kidding?" };
// <> animation array	  						
static char *anim[8] = { "       <>       ",
					 "      <  >      ",
					 "     <    >     ",
					 "    <      >    ",
					 "   <        >   ",
					 "  <          >  ",
					 " <            > ",
					 "<              >" };

int getRandomBit();
int length = 0;
int lFlag = 0;	// line flag

//------------------------------------------------------------------------------
// main()
//------------------------------------------------------------------------------
void main(void)
{
   WDTCTL = WDTPW + WDTHOLD;               // Stop watchdog timer
   _delay_cycles(100000);
   DCOCTL = 0x00;                          // Set DCOCLK to 1MHz
   BCSCTL1 = CALBC1_1MHZ;
   DCOCTL = CALDCO_1MHZ;

   P1OUT &= ~(CLOCKPIN + DATAPIN);
   P1OUT &= ~(BIT0 + BIT6);	
   P1OUT |= ENABLEPIN;
   // Set all pins to output except BUTTON, P1.4 and P1.5
   P1DIR = 0xFF & ~(BUTTON + BIT4 + BIT5);	

   P2OUT = 0x00;
   P2SEL = 0x00;
   P2DIR = 0xFF;
   P1IE |= BUTTON;		//P1.3 interrupt enabled
   P1IFG = 0x00;		//Clear all interrupt flags

   __enable_interrupt();

   initDisplay();
   sendStr("Magic Eight Ball,v0.03 - READY", 30);
   srand(getRandomBit());		//Random number generator seed

   while (1)
   {	
   	_bis_SR_register(LPM0_bits + GIE);
   	if (msgflag == 1)
   	{
   		for (i=1;i<=5;i++)	// Waste time display animated "<>"
   		{
   			clearDisplay();
   			for (j=0;j<8;j++){    			
   				sendStr(anim[j],16);
   				__delay_cycles(150000);
   			}   			
   			lFlag ^= 1;
   		}
   		clearDisplay();
   		lFlag = 0;
   		sendStr(answer[randnum], strlen(answer[randnum]));		// Send message
   		msgflag = 0;				// Reset message flag
   	}
   }
} 

// Switch interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
  	randnum = rand() % 11;	// random number 0 to 10
  							// Not very random, however, adequate for
						// this project.
msgflag = 1;			// message flag
   P1IFG &= ~BUTTON;		// reset interrupt flag
   _bic_SR_register_on_exit(LPM0_bits);
}

int getRandomBit(){
  ADC10CTL1 |= INCH_5;
  ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON;
  ADC10CTL0 |= ENC + ADC10SC;
  while(ADC10CTL1 & ADC10BUSY);
  return ADC10MEM;
}

void send(char data, char registerSelect) {
bitCounter = 0;
while(bitCounter < 8) {
	(data & BIT7) ? (P1OUT |= DATAPIN) : (P1OUT &= ~DATAPIN);
	data <<= 1;
	P1OUT |= CLOCKPIN;
	P1OUT &= ~CLOCKPIN;
	bitCounter++;
}
registerSelect ? (P1OUT |= DATAPIN) : (P1OUT &= ~DATAPIN);
P1OUT &= ~ENABLEPIN;
P1OUT |= ENABLEPIN;
}

void sendStr(char string[], int size) {
int charIndex = 0;
lFlag == 0 ? sendInstruction(0x80) : sendInstruction(0xC0);  // Select line
while(charIndex < size) {
	// If "," appears in string use line 2
	if (string[charIndex] == 0x2C) {
		sendInstruction(0xC0);  // Second line
		charIndex++;  // Bypass displaying comma
	}
	sendData(string[charIndex]);
	charIndex++;
}
}
//------------------------------------------------------------------------------

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...