Created
December 23, 2010 16:26
-
-
Save nicholasjconn/753207 to your computer and use it in GitHub Desktop.
NJS's MSP430 LaunchPad Oscilloscope
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* NJS's MSP430 LaunchPad Oscilloscope | |
* | |
* Description: This code turns the launchpad into a simple (slow) | |
* oscilloscope using a bi-directional software UART. | |
* The MCU will accept ascii commands from the computer | |
* for single measurment of a channel (internal and | |
* external) and also continous measuring of external | |
* channels. | |
* | |
* This code was originally created for "NJC's MSP430 | |
* LaunchPad Blog". | |
* | |
* Author: Nicholas J. Conn - http://msp430launchpad.com | |
* Email: webmaster at msp430launchpad.com | |
* Date: 08-29-10 | |
******************************************************************************/ | |
#include "msp430g2231.h" | |
#include "stdbool.h" | |
#define TXD BIT1 // TXD on P1.1 | |
#define RXD BIT2 // RXD on P1.2 | |
#define Bit_time 104 // 9600 Baud, SMCLK=1MHz (1MHz/9600)=104 | |
#define Bit_time_5 52 // Time for half a bit. | |
// ASCII values for the commands | |
#define TEST_SPEED 0x31 | |
#define M_A3 0x32 | |
#define STREAM 0x33 | |
#define STOP 0x34 | |
#define M_TEMP 0x35 | |
#define M_VCC 0x36 | |
unsigned char BitCnt; // Bit count, used when transmitting byte | |
unsigned int TXByte; // Value sent over UART when Transmit() is called | |
unsigned int RXByte; // Value recieved once hasRecieved is set | |
unsigned int i; // for loop variable | |
bool isReceiving; // Status for when the device is receiving | |
bool hasReceived; // Lets the program know when a byte is received | |
bool ADCDone; // ADC Done flag | |
unsigned int ADCValue; // Measured ADC Value | |
// Function Definitions | |
void Transmit(void); | |
void Receive(void); | |
void Single_Measure(unsigned int); | |
void Single_Measure_REF(unsigned int, unsigned int); | |
void Start_Stream(unsigned int); | |
void Stop_Stream(void); | |
void main(void) | |
{ | |
WDTCTL = WDTPW + WDTHOLD; // Stop WDT | |
BCSCTL1 = CALBC1_1MHZ; // Set range | |
DCOCTL = CALDCO_1MHZ; // SMCLK = DCO = 1MHz | |
P1SEL |= TXD; // Connected TXD to timer | |
P1DIR |= TXD; | |
P1IES |= RXD; // RXD Hi/lo edge interrupt | |
P1IFG &= ~RXD; // Clear RXD (flag) before enabling interrupt | |
P1IE |= RXD; // Enable RXD interrupt | |
P1DIR |= BIT0; | |
P1OUT &= ~BIT0; // Turn off LED at P1.0 | |
isReceiving = false; // Set initial values | |
hasReceived = false; | |
ADCDone = false; | |
__bis_SR_register(GIE); // interrupts enabled\ | |
while(1) | |
{ | |
if (hasReceived) // If the device has recieved a value | |
{ | |
Receive(); | |
} | |
if(ADCDone) // If the ADC is done with a measurement | |
{ | |
ADCDone = false; // Clear flag | |
TXByte = ADCValue & 0x00FF; // Set TXByte | |
Transmit(); // Send | |
TXByte = (ADCValue >> 8); // Set TXByte to the upper 8 bits | |
TXByte = TXByte & 0x00FF; | |
Transmit(); | |
} | |
if (~(hasReceived && ADCDone)) // Loop again if either flag is set | |
__bis_SR_register(CPUOFF + GIE); // LPM0, the ADC interrupt will wake the processor up. | |
} | |
} | |
/** | |
* Handles the received byte and calls the needed functions.\ | |
**/ | |
void Receive() | |
{ | |
hasReceived = false; // Clear the flag | |
switch(RXByte) // Switch depending on command value received | |
{ | |
case TEST_SPEED: | |
P1OUT |= BIT0; // Turn on LED while testing | |
for (i = 0; i != 0x100; i++) // Loop 256 times | |
{ | |
TXByte = i; // Sends the counter as if it were a 16 bit value | |
Transmit(); | |
TXByte = 0; | |
Transmit(); | |
} | |
P1OUT &= ~BIT0; // Turn off the LED | |
break; | |
case STREAM: | |
Start_Stream(INCH_3); // Starts continuously reading A3 | |
break; | |
case STOP: | |
Stop_Stream(); // Stops the ADC | |
break; | |
case M_A3: | |
Single_Measure(INCH_3); // Reads A3 only once | |
break; | |
case M_TEMP: | |
Single_Measure_REF(INCH_10, 0); // Reads the temperature sensor once | |
break; | |
case M_VCC: | |
Single_Measure_REF(INCH_11, REF2_5V); // Reads VCC once (VCC/2 internally) | |
break; | |
default:; | |
} | |
} | |
/** | |
* Starts to continuously read the 'chan'. The reference used is AVCC and not | |
* an internal reference. | |
**/ | |
void Start_Stream(unsigned int chan) | |
{ | |
ADC10CTL0 &= ~ENC; // Disable the ADC | |
ADC10CTL0 = ADC10ON + ADC10SHT_3 + MSC+ ADC10IE; // Turn on ADC10, 64 clock ticks for sample and hold, | |
// multiple mode, enable ADC interrupt | |
ADC10CTL1 = ADC10SSEL_3 + chan + ADC10DIV_7 + CONSEQ_2; // Set 'chan', SMCLK/8, repeat-single-channel | |
ADC10CTL0 |= ENC + ADC10SC; // Enable and start conversion | |
} | |
/** | |
* Stops the ADC. | |
**/ | |
void Stop_Stream() | |
{ | |
ADC10CTL0 &= ~ENC; // Disable ADC | |
ADC10CTL0 &= ~ADC10SC; // Stop conversion | |
ADC10CTL0 &= ~ADC10ON; // Turn ADC off | |
} | |
/** | |
* Reads ADC 'chan' once using AVCC as the reference. | |
**/ | |
void Single_Measure(unsigned int chan) | |
{ | |
ADC10CTL0 &= ~ENC; // Disable ADC | |
ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE; // 64 clock ticks, ADC On, enable ADC interrupt | |
ADC10CTL1 = ADC10SSEL_3 + chan; // // Set 'chan', SMCLK | |
ADC10CTL0 |= ENC + ADC10SC; // Enable and start conversion | |
} | |
/** | |
* Reads ADC 'chan' once using an internal reference, 'ref' determines if the | |
* 2.5V or 1.5V reference is used. | |
**/ | |
void Single_Measure_REF(unsigned int chan, unsigned int ref) | |
{ | |
ADC10CTL0 &= ~ENC; // Disable ADC | |
ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ref + ADC10IE; // Use reference, | |
// 64 clock ticks, internal reference on | |
// ADC On, enable ADC interrupt, Internal = 'ref' | |
ADC10CTL1 = ADC10SSEL_3 + chan; // Set 'chan', SMCLK | |
__delay_cycles (128); // Delay to allow Ref to settle | |
ADC10CTL0 |= ENC + ADC10SC; // Enable and start conversion | |
} | |
/** | |
* Transmits the value currently in TXByte. The function waits till it is | |
* finished transmiting before it returns. | |
**/ | |
void Transmit() | |
{ | |
while(isReceiving); // Wait for RX completion | |
TXByte |= 0x100; // Add stop bit to TXByte (which is logical 1) | |
TXByte = TXByte << 1; // Add start bit (which is logical 0) | |
BitCnt = 0xA; // Load Bit counter, 8 bits + ST/SP | |
CCTL0 = OUT; // TXD Idle as Mark | |
TACTL = TASSEL_2 + MC_2; // SMCLK, continuous mode | |
CCR0 = TAR; // Initialize compare register | |
CCR0 += Bit_time; // Set time till first bit | |
CCTL0 = CCIS0 + OUTMOD0 + CCIE; // Set signal, intial value, enable interrupts | |
while ( CCTL0 & CCIE ); // Wait for previous TX completion | |
} | |
/** | |
* ADC interrupt routine. Pulls CPU out of sleep mode for the main loop. | |
**/ | |
#pragma vector=ADC10_VECTOR | |
__interrupt void ADC10_ISR (void) | |
{ | |
ADCValue = ADC10MEM; // Saves measured value. | |
ADCDone = true; // Sets flag for main loop. | |
__bic_SR_register_on_exit(CPUOFF); // Enable CPU so the main while loop continues | |
} | |
/** | |
* Starts the receive timer, and disables any current transmission. | |
**/ | |
#pragma vector=PORT1_VECTOR | |
__interrupt void Port_1(void) | |
{ | |
isReceiving = true; | |
P1IE &= ~RXD; // Disable RXD interrupt | |
P1IFG &= ~RXD; // Clear RXD IFG (interrupt flag) | |
TACTL = TASSEL_2 + MC_2; // SMCLK, continuous mode | |
CCR0 = TAR; // Initialize compare register | |
CCR0 += Bit_time_5; // Set time till first bit | |
CCTL0 = OUTMOD1 + CCIE; // Dissable TX and enable interrupts | |
RXByte = 0; // Initialize RXByte | |
BitCnt = 0x9; // Load Bit counter, 8 bits + ST | |
} | |
/** | |
* Timer interrupt routine. This handles transmiting and receiving bytes. | |
**/ | |
#pragma vector=TIMERA0_VECTOR | |
__interrupt void Timer_A (void) | |
{ | |
if(!isReceiving) | |
{ | |
CCR0 += Bit_time; // Add Offset to CCR0 | |
if ( BitCnt == 0) // If all bits TXed | |
{ | |
TACTL = TASSEL_2; // SMCLK, timer off (for power consumption) | |
CCTL0 &= ~ CCIE ; // Disable interrupt | |
} | |
else | |
{ | |
CCTL0 |= OUTMOD2; // Set TX bit to 0 | |
if (TXByte & 0x01) | |
CCTL0 &= ~ OUTMOD2; // If it should be 1, set it to 1 | |
TXByte = TXByte >> 1; | |
BitCnt --; | |
} | |
} | |
else | |
{ | |
CCR0 += Bit_time; // Add Offset to CCR0 | |
if ( BitCnt == 0) | |
{ | |
TACTL = TASSEL_2; // SMCLK, timer off (for power consumption) | |
CCTL0 &= ~ CCIE ; // Disable interrupt | |
isReceiving = false; | |
P1IFG &= ~RXD; // clear RXD IFG (interrupt flag) | |
P1IE |= RXD; // enabled RXD interrupt | |
if ( (RXByte & 0x201) == 0x200) // Validate the start and stop bits are correct | |
{ | |
RXByte = RXByte >> 1; // Remove start bit | |
RXByte &= 0xFF; // Remove stop bit | |
hasReceived = true; | |
} | |
__bic_SR_register_on_exit(CPUOFF); // Enable CPU so the main while loop continues | |
} | |
else | |
{ | |
if ( (P1IN & RXD) == RXD) // If bit is set? | |
RXByte |= 0x400; // Set the value in the RXByte | |
RXByte = RXByte >> 1; // Shift the bits down | |
BitCnt --; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment