Jump to content
Sign in to follow this  
Jack Caster

A simple shell, step by step [eZ430 - RF5000]

Recommended Posts

Hi guys,

 

I have started to work on my little project, a simple shell to control the MSP430; as I am a beginner I would like to share my little progresses and at the same time seek for an help to solve the problems on my path.

 

#include "io430.h"
#include 
#include 

//__________MACRO__________

//RED led on P1.0
#define LED_RED         BIT0
#define LED_RED_ON	(P1OUT |= BIT0)
#define LED_RED_OFF	(P1OUT &=!BIT0)
#define LED_RED_TOGGLE  (P1OUT ^= BIT0)

//GREEN led su P1.1
#define LED_GREEN       BIT1
#define LED_GREEN_ON	(P1OUT  |= BIT1)
#define LED_GREEN_OFF	(P1OUT &= !BIT1)
#define LED_GREEN_TOGGLE (P1OUT ^= BIT1)

//__________HEADER of FUNCTIONS__________
void init_UART(void);
void writeChar(char ch);
void writeString(char * str);

//__________GLOBAL VARIABLES__________
char buffer[200] = {0};
char * welcome = "Welcome";
char * prompt = "msp430>> ";

int main( void )
{
 // Stop watchdog timer to prevent time out reset
 WDTCTL = WDTPW + WDTHOLD;

 LED_RED_OFF;
 LED_GREEN_OFF;

 P1DIR = LED_RED + LED_GREEN;

 init_UART();

 writeString(welcome);
 writeString(prompt);

 __low_power_mode_0(); // Interrupts enabled

}

void init_UART(void) {
 // Basic Clock System Control 1 
 BCSCTL1 = CALBC1_1MHZ;                    // BCSCTL1 Calibration Data for 1MHz

 // DCO Clock Frequency Control
 DCOCTL = CALDCO_1MHZ;                     // DCOCTL  Calibration Data for 1MHz

 // UART Mode
 P3SEL = BIT4 + BIT5;                      // USCI_A0 TXD/RXD on P3.4 & P3.5

 // USCI A0 Control Register 1
 UCA0CTL1 |= UCSSEL_2;                     // USCI 0 Clock Source 2: SMCLK

 // USCI A0 Baud Rate 0 & 1
 UCA0BR0 = 104;                            // 1MHz 9600
 UCA0BR1 = 0;                              // 1MHz 9600

 // USCI A0 Modulation Control
 UCA0MCTL = UCBRS0;                        // USCI Second Stage Modulation Select 0

 UCA0CTL1 &= ~UCSWRST;                     // USCI Software Reset: Initialize state machine

 // Interrupt Enable 2
 IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
 char character;	

 LED_RED_TOGGLE;  

 character = UCA0RXBUF;
 // Echo back character
 if (character != 0x0D) {
   writeChar(character);        // CR = 0x0D
 } else {
     writeChar('\n');
     writeChar('\r'); //CReturn
     writeString(prompt);
 }

}

void writeChar(char ch) { 
   while (!(IFG2 & UCA0TXIFG));     // USART1 TX buffer ready?
   UCA0TXBUF  = ch;                 
}

void writeString(char * str) {
   int i;
   for (i = 0; i < strlen(str); i++) {
     writeChar(str[i]);
   }
}

 

My first problem is to find a way to make my welcome message to stay put up to when a new character is received.

Up to now that is what is happening: these two lines...

 

writeString(welcome);
writeString(prompt);

 

...seem not to work, but instead in the very moment in which I type a character the functions work.

 

puttyscreen.png

 

My second question is how can I write a function which prints a blinking cursor. It seems not easy!

 

Thank you!

Share this post


Link to post
Share on other sites

Not bad! I think the main issue some will have is handling the writing of characters/etc. inside the ISR, it's usually preferred to stay inside the ISR as little as possible...

 

Here's an alternative library I wrote: https://github.com/spirilis/mspuartcli

It's written for the value-line G2553 though, but I'd imagine it'll work on any USCI_A capable processor... specific UART timing stuff has to be edited inside uartcli.c's uartcli_begin() function but the rest is standard USCI_A operations.

 

It lets you define commands as an array of strings (in Flash/ROM too, using const char *) and the uartcli_token_cmd() function returns -1 if the command is not present or an index from 0 to (string array size minus 1) which you can then test with a big switch() section. And then you can request the different arguments by ID.

 

There's a short/non-useful main.c in the git repository but here's a ..... somewhat LONG example main.c that utilizes it to blink LEDs (lets you mess with a VLOCLK-driven TimerA):

/* UARTCLI-based demo application providing a CLI for switching the LEDs, reading the VLOCLK TimerA
* tweaking the rate of TimerA in relation to VLOCLK.
*/

#include 
#include 
#include 
#include "uartcli.h"

const char *cmdlist[] = {"led", "vlo", "vloirq", "vlorate", "help", NULL};

/* VLOCLK timer user status */
volatile char vlostat;
#define VLO_ENABLED 0x01
#define VLO_IRQ_MARK 0x02

/* Function prototypes */
void VLO_TimerA_switch(char);
char VLO_TimerA_enabled();
void VLO_TimerA_set_divider(char);
char VLO_TimerA_get_divider();
void VLO_TimerA_overflow_mark(char);
char VLO_TimerA_overflow_mark_enabled();
void VLO_TimerA_overflow_check();
void command_processor();


/* Main loop */
int main()
{
       char cmd[64];

       WDTCTL = WDTPW | WDTHOLD;
       DCOCTL = CALDCO_16MHZ;
       BCSCTL1 = CALBC1_16MHZ;
       BCSCTL2 = DIVS_2;  // SMCLK = MCLK/4
       BCSCTL3 = LFXT1S_2;  // ACLK = VLOCLK/1
       while (BCSCTL3 & LFXT1OF)
               ;

       /* Global initialization */
       vlostat = 0x00;
       P1OUT &= ~(BIT0 | BIT6);  // LEDs switched off, enabled as GPIO outputs
       P1DIR |= (BIT0 | BIT6);   //
       P1SEL &= ~(BIT0 | BIT6);  //
       P1SEL2 &= ~(BIT0 | BIT6); //

       // Let'er rip
       uartcli_begin(cmd, 32);
       uartcli_println_str("vlotoy MSP430 Command Line Interface");
       uartcli_println_str("OK");

       while (1) {
               if (uartcli_available()) {
                       command_processor();
                       uartcli_clear();  // Tell UART code we're done processing the last cmd, and it can accept new input.
                                         // Otherwise it will quite literally ignore every character coming in.
                       uartcli_println_str("OK");
               }
               if (vlostat & VLO_IRQ_MARK) {
                       uartcli_println_str("VLOCLK MARK");
                       vlostat &= ~VLO_IRQ_MARK;
               }
               LPM0;
       }
}

void VLO_TimerA_switch(char onoff)
{
       if (onoff) {
               TACTL = (TACTL & ID_3) |  // Save previous value of input divider
                       (TACTL & TAIE) |  // Save previous value of TAIE, but clear TAIFG
                       TASSEL_1 |      // ACLK (VLOCLK)
                       MC_2 |          // Count continuously
                       TACLR;          // Clear timer
               vlostat |= VLO_ENABLED;
       } else {
               TACTL &= ~MC_3;         // Stop mode
               vlostat &= ~VLO_ENABLED;
       }
}

char VLO_TimerA_enabled()
{
       if ( (TACTL & MC_3) != 0x00 )
               return 1;
       return 0;
}

void VLO_TimerA_set_divider(char divider)
{
       switch(divider) {
               case 1:
                       TACTL = (TACTL & ~ID_3);
                       break;
               case 2:
                       TACTL = (TACTL & ~ID_3) | ID_1;
                       break;
               case 4:
                       TACTL = (TACTL & ~ID_3) | ID_2;
                       break;
               case 8:
                       TACTL |= ID_3;
                       break;
       }
}

char VLO_TimerA_get_divider()
{
       switch (TACTL & ID_3) {
               case ID_0:
                       return 1;
                       break;
               case ID_1:
                       return 2;
                       break;
               case ID_2:
                       return 4;
                       break;
               case ID_3:
                       return 8;
                       break;
       }
       return 0;  // should never reach this far
}

void VLO_TimerA_overflow_mark(char onoff)
{
       if (onoff) {
               TACTL &= ~TAIFG;
               TACTL |= TAIE;
       } else {
               TACTL &= ~(TAIFG | TAIE);
       }
}

char VLO_TimerA_overflow_mark_enabled()
{
       if (TACTL & TAIE)
               return 1;
       return 0;
}

void VLO_TimerA_overflow_check()
{
       if (vlostat & VLO_IRQ_MARK) {
               uartcli_print_str("VLOCLK mark");
               vlostat &= ~VLO_IRQ_MARK;
       }
}

/* TimerA overflow interrupt */
#pragma vector=TIMER0_A1_VECTOR
__interrupt void TA_ISR(void)
{
       TACTL &= ~TAIFG;
       vlostat |= VLO_IRQ_MARK;
       __bic_SR_register_on_exit(LPM4_bits);
}

/* Command processor */
// const char *cmdlist[] = {"led", "vlo", "vloirq", "vlorate", "help", NULL};
void command_processor()
{
       int cmdidx, i=0;
       char buf[32];

       uartcli_token_begin();
       cmdidx = uartcli_token_cmd(cmdlist);
       switch (cmdidx) {
               case 0:  // LED
                       if (uartcli_token_arg(1, buf, 32) != NULL) {
                               i = 0;
                               if (!strcmp(buf, "on")) {
                                       P1OUT |= BIT0;  // Enable red LED
                                       uartcli_println_str("led: switched on");
                                       i = 1;
                               }
                               if (!strcmp(buf, "off")) {
                                       P1OUT &= ~BIT0; // Disable red LED
                                       uartcli_println_str("led: switched off");
                                       i = 1;
                               }
                               if (!i) {
                                       uartcli_println_str("Syntax: led ");
                               }
                       } else {
                               // Without any arguments, we assume the user is querying the status.
                               if (P1OUT & BIT0) {
                                       uartcli_println_str("led: on");
                               } else {
                                       uartcli_println_str("led: off");
                               }
                       }
                       break;
               case 1:  // VLO enable/disable
                       if (uartcli_token_arg(1, buf, 32) != NULL) {
                               if (!strcmp(buf, "on")) {
                                       VLO_TimerA_switch(1);
                                       uartcli_print_str("VLOCLK switched on, timer divider = ");
                                       uartcli_println_int(VLO_TimerA_get_divider());
                               }
                               if (!strcmp(buf, "off")) {
                                       VLO_TimerA_switch(0);
                                       uartcli_println_str("VLOCLK switched off");
                               }
                       } else {
                               // Display VLO TimerA on/off status
                               if (VLO_TimerA_enabled()) {
                                       uartcli_print_str("VLOCLK Timer enabled; current value: ");
                                       uartcli_println_uint(TAR);
                               } else {
                                       uartcli_println_str("VLOCLK Timer disabled");
                               }
                       }
                       break;
               case 2:  // VLOIRQ (overflow mark) enable/disable
                       if (uartcli_token_arg(1, buf, 32) != NULL) {
                               if (!strcmp(buf, "on")) {
                                       VLO_TimerA_overflow_mark(1);
                                       uartcli_println_str("VLOCLK overflow IRQ messages enabled");
                               }
                               if (!strcmp(buf, "off")) {
                                       VLO_TimerA_overflow_mark(0);
                                       uartcli_println_str("VLOCLK overflow IRQ messages disabled");
                               }
                       } else {
                               // Display VLO overflow mark status
                               if (VLO_TimerA_overflow_mark_enabled()) {
                                       uartcli_println_str("VLOCLK overflow IRQ messages: enabled");
                               } else {
                                       uartcli_println_str("VLOCLK overflow IRQ messages: disabled");
                               }
                       }
                       break;
               case 3:  // VLORATE (set divider)
                       if (vlostat & VLO_ENABLED) {
                               if (uartcli_token_arg(1, buf, 32) != NULL) {
                                       i = atoi(buf);
                                       if (i != 1 && i != 2 && i != 4 && i != 8) {
                                               uartcli_print_str("vlorate: Inappropriate divider value: ");
                                               uartcli_println_str(buf);
                                               uartcli_println_str("Divider must be 1, 2, 4 or 8.");
                                       } else {
                                               // Set it!
                                               VLO_TimerA_set_divider(i);
                                               uartcli_print_str("VLOCLK timer divider set to: ");
                                               uartcli_println_int(i);
                                       }
                               } else {
                                       // Display current VLO TimerA divider
                                       uartcli_print_str("VLOCLK timer divider set to: ");
                                       uartcli_println_int(VLO_TimerA_get_divider());
                               }
                       } else {
                               uartcli_println_str("VLOCLK timer disabled; ignoring command");
                       }
                       break;
               case 4:  // HELP
                       uartcli_println_str("HELP -- Command reference");
                       uartcli_println_str("led     : Query red LED status or switch LED on/off");
                       uartcli_println_str("vlo     : Query runtime status of VLOCLK-enabled TimerA or switch on/off");
                       uartcli_println_str("vloirq  : Display whether 16-bit timer overflow IRQ marks are enabled or switch on/off");
                       uartcli_println_str("vlorate : Display or set the TimerA clock divider");
                       break;
               default:
                       uartcli_print_str("Unknown command: ");
                       uartcli_token_cmdstr(buf, 32);
                       uartcli_println_str(buf);
       }
}

Share this post


Link to post
Share on other sites

About blinking cursor- you'd probably need to set up a timer IRQ that prints an underscore (_), then a backspace (CTRL-H or \008) alternately. Most serial terminals should interpret that correctly... however if your serial terminal is running in line mode that won't really work right. If it's not in line-by-line mode your code will have to perform the echo-back of every character as it's typed, including interpretation of backspaces properly (my mspuartcli code does not do that BTW, it assumes you're using line-by-line local-echo mode so it receives the command as one fully formed sequence)

Share this post


Link to post
Share on other sites
About blinking cursor- you'd probably need to set up a timer IRQ that prints an underscore (_), then a backspace (CTRL-H or \008) alternately. Most serial terminals should interpret that correctly... however if your serial terminal is running in line mode that won't really work right. If it's not in line-by-line mode your code will have to perform the echo-back of every character as it's typed, including interpretation of backspaces properly (my mspuartcli code does not do that BTW, it assumes you're using line-by-line local-echo mode so it receives the command as one fully formed sequence)

 

I have just tried to print an underscore and a backspace using a while loop and it works! I don't know very well the timer's interrupts, so I don't know how to implement it in order to avoid problems on the UART's interrupts...

I think the blinking cursor will be the last function which I implement.

 

Now I'm studying your code, but there are so many functions and I am quite confused!

 

About the welcome message issue, I have put a delay before the print function and now it works, but I'd prefer to receive a welcome as soon as Putty is started...

Share this post


Link to post
Share on other sites

hi

 

this is noob to noob info so i hope i am actually helping rather than muddying the waters further.

your initial request reminded me of this:

 

 

which led to this:

 

http://www.msp430launchpad.com/2012/06/using-printf.html

 

which links to this:

 

http://www.43oh.com/forum/viewtopic.php?f=10&t=1732

 

*edit* i was not in anyway attempting to insinuate that any preceding posts (which i can barely comprehend) may be 'muddying the water' and only that i am unsure of my own (mis)understanding.

 

/fartooparanoidforhisowngood.blog

Share this post


Link to post
Share on other sites
hi

 

this is noob to noob info so i hope i am actually helping rather than muddying the waters further.

your initial request reminded me of this:

 

 

which led to this:

 

http://www.msp430launchpad.com/2012/06/using-printf.html

 

which links to this:

 

http://www.43oh.com/forum/viewtopic.php?f=10&t=1732

 

*edit* i was not in anyway attempting to insinuate that any preceding posts (which i can barely comprehend) may be 'muddying the water' and only that i am unsure of my own (mis)understanding.

 

/fartooparanoidforhisowngood.blog

 

Don't worry,

 

actually my problem isn't how to print a string (and in oder to keep the code simple a printf() function isn't necessary) but how to print a message as soon as the serial communication begins. Up to now the message is sent by the UART, but if I don't open quickly a Putty session, I won't see it. Instead if I put a delay of 5 sec, for instance, I have the time to start Putty and see the message! It is not probably such a big deal, but for now is OK.

 

The next step is to implement an help function that is called back by typing, for instance, the word "help". Probably I will use the strcmp command.

 

As soon as possible I will post my progresses and I will appreciate a lot your suggestions!

I want to keep things as easy as possible, and only afterwards refine the code.

 

I know that some of you guys have already done this stuff but for me is something new and probably I don't have your same high knowledge. So it's hard to handle a full code, moreover written with a personal coding style!

 

Thanks in advance :wave:

Share this post


Link to post
Share on other sites

HI!

 

following the spirilis's I wrote the following code, and now, the UART's ISR is as little as possible! In fact the ISR's aim is simply to wake up the MCU.

 

In order to solve the problem relating the "boot" I decided to use the switch as a power button, it seems a good idea...

 

Now the output is the following:

 

bootk.png

 

The updated code is:

 

#include "io430.h"
#include 
#include 

//__________MACRO__________

//RED led on P1.0
#define LED_RED         BIT0
#define LED_RED_ON	(P1OUT |= BIT0)
#define LED_RED_OFF	(P1OUT &=!BIT0)
#define LED_RED_TOGGLE  (P1OUT ^= BIT0)

//GREEN led on P1.1
#define LED_GREEN       BIT1
#define LED_GREEN_ON	(P1OUT  |= BIT1)
#define LED_GREEN_OFF	(P1OUT &= !BIT1)
#define LED_GREEN_TOGGLE (P1OUT ^= BIT1)

//SWITCH on P1.2
#define SWITCH_ENABLE   P1DIR &=~ BIT2
#define SWITCH_DISABLE  P1DIR |= BIT2

//__________CONSTANTS__________
#define CR "\n\r"
#define BUFFER_SIZE 128

//__________HEADER of FUNCTIONS__________
void init_UART(void);
void init_LED(void);
void init_TIMERA(void);
void init_SWITCH(void);
void writeChar(char ch);
void writeString(char * str);



//__________GLOBAL VARIABLES__________
int iterator = 0;
char buffer[bUFFER_SIZE];
 int bufferActualSize  = 0;
 int bufferReadIndex   = 0;
 int bufferWriteIndex  = 0;
char character;
char welcome1[] = "\t*** WELCOME ***";
char welcome2[] = "Type HELP for the list of command";
char prompt[] = "msp430>> ";

int main( void )
{
 // Stop watchdog timer to prevent time out reset
 WDTCTL = WDTPW + WDTHOLD;
 // Basic Clock System Control 1 
 BCSCTL1 = CALBC1_1MHZ;                    // BCSCTL1 Calibration Data for 1MHz
 // DCO Clock Frequency Control
 DCOCTL = CALDCO_1MHZ;                     // DCOCTL  Calibration Data for 1MHz

 //INITIAL CONFIGURATION
 init_LED();

 //Boot with switch
 init_SWITCH();
 __low_power_mode_0();
 SWITCH_DISABLE;

 init_UART();

 __no_operation();

 //INITIAL MESSAGES
 writeString(welcome1);
 writeString(CR);
 writeString(welcome2);
 writeString(CR);
 writeString(prompt);

 while(1){
   __low_power_mode_0(); // Interrupts enabled

   // echo character and save the string
   if (character != '\r') {
     writeChar(character);  //echo       

     if (character != '\b') {
       buffer[iterator] = character;
       iterator++;
     } // if

   } else {

       buffer[iterator] = '\0';
       writeString(CR);

         if (strcmp(buffer,"help") == 0) {
           writeString("This is the HELP!");
           writeString(CR);
         } //if

     iterator = 0;
     writeString(prompt);
     } //end if-else
 } //end while
} // end main

void init_UART(void) {
 // UART Mode
 P3SEL = BIT4 + BIT5;                      // USCI_A0 TXD/RXD on P3.4 & P3.5

 // USCI A0 Control Register 1
 UCA0CTL1 |= UCSSEL_2;                     // USCI 0 Clock Source 2: SMCLK

 // USCI A0 Baud Rate 0 & 1
 UCA0BR0 = 104;                            // 1MHz 9600
 UCA0BR1 = 0;                              // 1MHz 9600

 // USCI A0 Modulation Control
 UCA0MCTL = UCBRS0;                        // USCI Second Stage Modulation Select 0

 UCA0CTL1 &= ~UCSWRST;                     // USCI Software Reset: Initialize state machine

 // Interrupt Enable 2
 IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {
 LED_RED_TOGGLE;  
 character = UCA0RXBUF;

 __low_power_mode_off_on_exit();
}

void writeChar(char ch) { 
   while (!(IFG2 & UCA0TXIFG));     // USART1 TX buffer ready?
   UCA0TXBUF  = ch;                 
}

void writeString(char * str) {
   int i;
   for (i = 0; i < strlen(str); i++) {
     writeChar(str[i]);
   }
}


void init_LED(void) {
   LED_RED_OFF;
   LED_GREEN_OFF;
   P1DIR = LED_RED + LED_GREEN;
}


void init_SWITCH(void) {
   SWITCH_ENABLE;
   P1REN  =  BIT2;                            // P1.2 pull-up/down R enabled 
   P1OUT  =  BIT2;                            // P1.2 pull-up  R enabled 
   P1IE   =  BIT2;                            // P1.2 interrupt enabled
   P1IES  =  BIT2;                            // P1.2 Low edge
   P1IFG &=~ BIT2;                            // P1.2 IFG cleared
}

#pragma vector = PORT1_VECTOR 
__interrupt void Port_1 (void) {
   P1IFG &=! BIT2;
   LED_GREEN_ON;
   __low_power_mode_off_on_exit();
}

 

but I have a problem to solve...

I would like to store the string only if I don't type the backspace; in this case I can compare the string even if there are some typing error, but these lines seem not working:

 

if (character != '\b') {
       buffer[iterator] = character;
       iterator++;
     }

 

Can you help me?

 

Thanks!

Share this post


Link to post
Share on other sites

This is opening up a whole can of worms you unlikely to want open.

 

The terminal emulator will send codes according to what it's been configured as -- let's, for simplicity's sake, assume that it is VT100.

 

To make sense of this, you will need to write a parser for the VT100 terminal codes (multi-byte and traditionally timing-dependant things). Useful to have indeed, but not something I'd want to write for the '2553 :).

 

http://vt100.net/ will get you started.

Share this post


Link to post
Share on other sites
This is opening up a whole can of worms you unlikely to want open.

 

The terminal emulator will send codes according to what it's been configured as -- let's, for simplicity's sake, assume that it is VT100.

 

To make sense of this, you will need to write a parser for the VT100 terminal codes (multi-byte and traditionally timing-dependant things). Useful to have indeed, but not something I'd want to write for the '2553 :).

 

http://vt100.net/ will get you started.

 

:lol:

 

Thank you for the response! I'll think about that...

Share this post


Link to post
Share on other sites

:lol:

 

Thank you for the response! I'll think about that...

 

Hi guys!

 

I found the VT100'codes in order to blink the cursor:

 

#define  CURSOR_OFF   "\33[?25l"
#define  CURSOR_ON   "\33[?25h"

 

Thanks to http://powwow.gforge.inria.fr/doxygen/d0/ddf/_v_t100_8_h.html!

You can see also http://hackipedia.org/Protocols/Terminal,%20DEC%20VT100/html/VT100%20Escape%20Codes.html

 

ASAP I will post my update code! Now I am very busy!

 

Bye!

Share this post


Link to post
Share on other sites

I apologise, I found the functions defined by scrolling down. But I could not still run the program on msp430g2553. You guys may have done this thing already but i am a newbie ; help please!

 

You should change the ports/pins number... Probably they are different with respect to my MSP

Share this post


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.

Sign in to follow this  

×
×
  • Create New...