Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 2 | ||||
Software apps and online services | ||||||
![]() |
|
Imagine that you're an engineer at a prestigious company and you're hosting the party of the year with all your engineer friends. All your friends arrive and you realize: you took your shaker to Jimmy's house last week to make your famous margaritas and forgot it there. You can't serve your guests inhomogenous drinks! But luckily, you have some anti-static foam, a couple of servo motors and your handy TI MSP430 Launchpad laying around at home. Using the anti-static foam, you whip up a simple pressure sensor (you put the anti-static foam between two conducting pads and run a voltage across them. As you compress the anti-static foam, the voltage readings increase), you wire it up to your PCB and write up a quick program on Code Composer to control the servo motors. You call it the Mix-a-roo 2000, and now you can serve your guests perfectly uniform drinks that get mixed by your mini-robot. All your guests have to do is put their glass on the pressure sensor, and the robot does the rest. The robot senses that a full cup has been placed on the sensor and drops a spoon into the drink. The spoon oscillates back and forth, giving you a perfectly mixed drink!
#include "msp430g2553.h"
#include "UART.h"
void print_every(int rate);
char newprint = 0;
int lightclk = 0;
int dutycycle = 0;
char input = 0;
//initialize variables
int statevar = 0;
int timecount = 0;
int value = 0;
int milivolts = 0;
int flag = 1;
int timer = 0;
unsigned int avg = 0;
unsigned int average = 0;
int i=0;
int volts[10] = {0,0,0,0,0,0,0,0,0,0}; // initialize array
int voltsum = 0;
void main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
if (CALBC1_16MHZ ==0xFF || CALDCO_16MHZ == 0xFF) while(1);
DCOCTL = CALDCO_16MHZ; // Set uC to run at approximately 16 Mhz
BCSCTL1 = CALBC1_16MHZ;
// Initializing ADC10
ADC10CTL0 = ADC10SHT_1 + ADC10ON + ADC10IE; //setting up the register so that the sample and hold time is 8 x ADC10CLKs, the ADC10 gets turned on and the interrupt is enabled. All other settings are default
ADC10CTL1 = INCH_3; //sets up the channel so that pin A3 is set as an input
ADC10AE0 |= 0x08; // enables A3 as the ADC channel
// Initialize Port 1
P1SEL &= ~0xFF; // Clears pins so they are GPIO
P1SEL2 &= ~0xFF; // Clears pins so they are GPIO
P1REN = 0xFF; // enables the resistors
P1DIR |= 0xF1; // Sets all pins as outputs
P1OUT &= ~0xF1; // Sets all pins intially to low
//Initialize Port 2
P2DIR |= 0x12; // Pins 2.1 and 2.4 are set up as timers
P2SEL |= 0x12; // Pins 2.1 and 2.4 are set up as timers
P2SEL2 &= ~0x12; // Clear the bits for P2SEL2 to set up the timer
//Setting up the timer
TA1CCR0 = 40000; // Sets the carrier frequency to 50 Hz
TA1CCR1 = 4900; // initialize duty cycle so that robot arm is holding spoon out of the cup
TA1CCR2 = 0; // initialize duty cycle
TA1CCTL1 = OUTMOD_7;// set/reset mode
TA1CCTL2 = OUTMOD_7;// set/reset mode
TA1CCTL0 = 0;
TA1CTL = TASSEL_2 + MC_1 + ID_3; // source SMCLK, up mode ,divider 8
// Timer A Config
TACCTL0 = CCIE; // Enable Periodic interrupt
TACCR0 = 16000; // period = 1ms
TACTL = TASSEL_2 + MC_1; // source SMCLK, up mode
Init_UART(115200,1); // Initialize UART for 115200 baud serial communication
_BIS_SR(GIE); // Enable global interrupt
while(1) { // Low priority Slow computation items go inside this while loop. Very few (if anyt) items in the HWs will go inside this while loop
// for use if you want to use a method of receiving a string of chars over the UART see USCI0RX_ISR below
// if(newmsg) {
// newmsg = 0;
// }
// The newprint variable is set to 1 inside the function "print_every(rate)" at the given rate
if ( (newprint == 1) && (senddone == 1) ) { // senddone is set to 1 after UART transmission is complete
// only one UART_printf can be called every 15ms
UART_printf("Milivolts :%d \n\r", milivolts); //prints milivolts to tera term
newprint = 0;
}
}
}
// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
ADC10CTL0 |= ENC + ADC10SC; // the conversion is started and enabled
}
// ADC 10 ISR - Called when a sequence of conversions (A7-A0) have completed
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void) {
value = ADC10MEM; //sets a variable that records the ADC10 readings
milivolts = (value*3300L)/1023; //converts the ADC reading to milivolts
timecount++; // Keep track of time for main loop
timer++; //seperate timer that also keeps track of main loop
print_every(250); // units determined by the rate Timer_A ISR is called, print every "rate" calls to this function
volts[0] = milivolts; //sets the first value of the array to the milivolts value
voltsum=0; //intialize voltsum to zero
for (i=0; i<10; i++){
voltsum +=volts[i]; // adds up all of the past 10 milivolts readings
}
average = voltsum/10; //divides that sum by 10 to find the average
for (i=9; i>0; i--){
volts[i] = volts[i-1]; //goes backwards from the last array value and shifts each value so that it is equal to the previous value. Happens continually so that 10 values are being read
}
if ((flag == 1) && (average > 305) && (timecount >= 4000)){ //if loops that determines if the average voltage is met (water is poured into the cup) and waits 4 seconds to drop the spoon into the cup after the cup has been filled
statevar = 1; // sends the program into case 1
timecount = 0; //resets the timecount
flag = 0; //sets the flag to 0 so that the program doesn't stay only in case 1
}
switch(statevar){
case 1: //drops the spoon into the cup
TA1CCR1 = 4400 ; // takes the robot arm from it's upright position to the down position using servo1
if (timecount >= 2000){ //waits for 2 seconds before moving on
timecount = 0; //resets the timecount timer
statevar = 2; // switch to state 2 to start the spoon movements
timer = 0; // resets the timer to keep track of spinning
}
break;
case 2:// spins the spoon one way
TA1CCR2 = 1900; //causes the spoon to spin in one direction using servo2
if(timecount >= 400){ // waits for 400ms before moving to the case that spin it the other way
timecount = 0; // resets the timecount
statevar = 3; //sends the program to state 3 to move the spoon the other way
}
break;
case 3: //spins spoon the other way
TA1CCR2 = 4500; //causes spoon to spin in opposite direction using servo2
if (timer >= 4000){ // if timer has reached 4 seconds (the spoon has spun back and forth 5 times)
timer = 0; //reset the timer value
statevar = 4; //continue to state 4 to end the program
}
else if(timecount >= 400){ //if the timer hasn't reached 4 secs, check timecount to see if it has reached 400ms
timecount = 0; //resets timecount
statevar = 2; // switch back to state 2 and turn other way
}
break;
case 4: //lifts spoon out of the cup using servo1
TA1CCR1 = 4900; // puts robot are back into it's upright position
if (timecount > 10000){// waits for 10 seconds before re-enabling the flag to give enough time to remove the cup
flag = 1; //re-enables flag
}
break;
}
}
// USCI Transmit ISR - Called when TXBUF is empty (ready to accept another character)
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void) {
if(IFG2&UCA0TXIFG) { // USCI_A0 requested TX interrupt
if(printf_flag) {
if (currentindex == txcount) {
senddone = 1;
printf_flag = 0;
IFG2 &= ~UCA0TXIFG;
} else {
UCA0TXBUF = printbuff[currentindex];
currentindex++;
}
} else if(UART_flag) {
if(!donesending) {
UCA0TXBUF = txbuff[txindex];
if(txbuff[txindex] == 255) {
donesending = 1;
txindex = 0;
}
else txindex++;
}
} else { // interrupt after sendchar call so just set senddone flag since only one char is sent
senddone = 1;
}
IFG2 &= ~UCA0TXIFG;
}
if(IFG2&UCB0TXIFG) { // USCI_B0 requested TX interrupt (UCB0TXBUF is empty)
IFG2 &= ~UCB0TXIFG; // clear IFG
}
}
// USCI Receive ISR - Called when shift register has been transferred to RXBUF
// Indicates completion of TX/RX operation
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {
if(IFG2&UCB0RXIFG) { // USCI_B0 requested RX interrupt (UCB0RXBUF is full)
IFG2 &= ~UCB0RXIFG; // clear IFG
}
if(IFG2&UCA0RXIFG) { // USCI_A0 requested RX interrupt (UCA0RXBUF is full)
// Uncomment this block of code if you would like to use this COM protocol that uses 253 as STARTCHAR and 255 as STOPCHAR
/* if(!started) { // Haven't started a message yet
if(UCA0RXBUF == 253) {
started = 1;
newmsg = 0;
}
}
else { // In process of receiving a message
if((UCA0RXBUF != 255) && (msgindex < (MAX_NUM_FLOATS*5))) {
rxbuff[msgindex] = UCA0RXBUF;
msgindex++;
} else { // Stop char received or too much data received
if(UCA0RXBUF == 255) { // Message completed
newmsg = 1;
rxbuff[msgindex] = 255; // "Null"-terminate the array
}
started = 0;
msgindex = 0;
}
}
*/
IFG2 &= ~UCA0RXIFG;
}
}
// This function takes care of all the timing for printing to UART
// Rate determined by how often the function is called in Timer ISR
int print_timecheck = 0;
void print_every(int rate) {
if (rate < 15) {
rate = 15;
}
if (rate > 10000) {
rate = 10000;
}
print_timecheck++;
if (print_timecheck == rate) {
print_timecheck = 0;
newprint = 1;
}
}
Comments