This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Scanf from RS232



Hello everyone,

Not to long ago I was able to override the printf() function to print to RS232.  Now I need to override the scanf() to receive a character string through RS232.  Honestly I have no idea where to start...  There is a putchar.c file that contains a char _low_level_get( void ) function, maybe there would be the place to start...?

Any help anyone can give would be GREATLY appreciated!!!

-CoryF

  • I've got some more info.  First thing is I make a copy of the getchar.c included in the ../src/clib/ and change the __low_level_get function so that it looks like this:

    char _low_level_get(void){
     
      while (!(UCA0IFG & UCRXIFG)){
        // keep waiting for the buffer to contain a character;
      }
      char c = UCA0RXBUF;
     
      return (c);
    }

    I think this should wait in the while loop until there is a character present in the RX buffer then set c to that character and return it.  This will compile and run, but in the debugger it just stays in the while loop.  Should I just leave out the loop? But then what if there is no character in the buffer...?

    That is what I have so far, it is running through and using my getchar.c file but I am unsure how to implement the _low_level_get(void) function.

    -CoryF

  • Hi Cory,

    Reading input is quite a bit more difficult than generating output.  When that input comes from a user at a terminal, it can be even more difficult.

    You might be able to get more specific help if you can give us an overview of the system and what you plan to accomplish with your terminal I/O.  I am making the following assumptions based on some of your other posts.  Please correct them as necessary.

    1. You are using IAR and CLIB.
    2. You are not using the watchdog timer.
    3. You are using an ANSI terminal emulator like Tera Term or hyperterm etc.
    4. A human is providing input at the terminal which then goes to your MSP430 system.
    5. Your MSP430 system does not use an OS or similar concepts (no context switching)
    6. You are providing a prompt to the terminal and will then wait for the response.

    Your implementation of _low_level_get( ) is a good start.  However, as you noted, it will wait forever for each character, which causes the code calling scanf to block.  In your application, that may be just fine.  If you need your MSP430 system to perform other tasks while simultaneously waiting for input from the terminal, then you probably want to start using an OS.

    As your code evolves, you may consider adding an RX ISR and input queue.  That would allow you to wait for input in low power mode, and it would help you not to miss any input characters, which can sometimes come quickly back to back or even before you expect them for a user typing "ahead".

    One nice thing about IAR's CLIB getchar, which calls _low_level_get( ), is that it includes a very basic line editor suitable for a human at a terminal.

    Jeff

  • Jeff Tenney said:
    1. You are using IAR and CLIB. TRUE
    2. You are not using the watchdog timer.TRUE
    3. You are using an ANSI terminal emulator like Tera Term or hyperterm etc.TRUE
    4. A human is providing input at the terminal which then goes to your MSP430 system. TRUE
    5. Your MSP430 system does not use an OS or similar concepts (no context switching)TRUE
    6. You are providing a prompt to the terminal and will then wait for the response.KINDA (see below)

    The system consists of a computer and the MSP430 connected via RS232, the MSP430 is going to have an ADC via SPI and control devices via the GPIO pins.The computer has a GUI that sends desired_Variables (desired_temp, desired_pressure...etc) through the RS232 line every ~0.5seconds, and the uController needs to respond to the GUI's message with its current_Variables.

    The uController is going to store the data from the ADC to current_Variables (current_temp, current_pressure... etc) multiple times a second, and then change the GPIO pins to stabilize a system based off of  the desired_Variables given to it by the computer.  The updates to the GPIO pins are going to be every 100-250ms.

    As an example the user wants the system to be 100.0*F and it is currently 98.1*F. Over the RS232 the uController will receive a message that says "set the temp to 100.0*F" the uController knows that the system is 98.1*F and needs to turn on a heater, and then keep checking the system every 100-250ms, because once the system is 100.0*F then it needs to turn off the heater.  The system will then need to maintain the 100.0*F temperature buy checking every 100-250ms to see if the heater needs to be turned back on or off.

    The GUI is currently sending out messages every ~0.5sec even if there is no change to the desired_Variables, this is so that it will get a response from the uController and then it can update its screen with the current status of the system.

     

    How to implement this in the uController is where I am now.  If I could have 2 "threads" running:

    1) Reading ADC data, maintaining the system, and updating the current_Variables

    2) Receiving/Sending information to the computer via RS232, and updating the desired_Variables

    Would be ideal! How do do this in the uController I am unsure.  This is my first project with the MSP line of uControllers.

    I hope I described the system well, if anyone needs more clarification please let me know.  Any ideas at this point are greatly appreciated!!

    -CoryF

  • Cory Frey said:

    The GUI is currently sending out messages every ~0.5sec even if there is no change to the desired_Variables, this is so that it will get a response from the uController and then it can update its screen with the current status of the system.

    I'm confused.  Is the UI a terminal (Tera Term / Hyperterminal / etc) or a custom GUI?  You can greatly simplify the design if the UI is a custom GUI on the PC instead of a dumb terminal.

    Jeff

  • It is a custom GUI, another team member is currently writing it in java.  The GUI can send messages through the RS232 port of the computer that it is run on.

  • You might be able to get away with single threading without an OS and without ISRs.
    1) Write a old DOS style kbhit() function to test for input before doing a blocking read.
    2) The communication between PC and MSP430 is strictly command and response. There can only be one message "in flight" at a time. Nothing asynchronus.
    3) Baud rate is fast enough that you can do a blocking read or write an entire message without messing up your timing. While you are witing or reading, you can do nothing else.

  • Norman Wong said:

    1) Write a old DOS style kbhit() function to test for input before doing a blocking read.

    Not a lot online about this...  Can you explain more?

    Norman Wong said:

    2) The communication between PC and MSP430 is strictly command and response. There can only be one message "in flight" at a time. Nothing asynchronus.

    Yes, and the uController will never initiate the communication either.  It only sends a response when it receives a message.  The variables are initialized to zero in the system so it will sit idle until the first message is received.  Also if a message is not received in so long it will set the variables back to zero and go into an "idle" mode.

    Norman Wong said:

    3) Baud rate is fast enough that you can do a blocking read or write an entire message without messing up your timing. While you are witing or reading, you can do nothing else.

    Yes, we are using 115200 8N1 setup so the communication should be over before the ADC data needs to be pulled again.

    -CoryF

     

  • I don't know this processor but here's my guess based upon your previous code:

    int kbhit(void)
    {
      return(UCA0IFG & UCRXIFG);
    }


    You would only call scanf() only if khbit() says there is something to read. Something like:

    if(kbhit())
    {
      sscanf()
      process_message()
    }
    do_other_things....



    If you block to read an entire message, the PC MUST send an entire message. You could hang. Or block and accumlate enough bytes for a message.

  • Well I got something to work!!!  :-)   It may not be the "best" way but I am able to capture a string through the UART.  I attached the following code that I am using.  But there is one problem...  I can not use sscanf(), scanf() etc.  The only function that saves the string is gets(my_String);  Does anyone have any ideas why this is the case?  I am pretty sure that the scanf() functions call gets(myString) at a lower level, but why can't I just use scanf(%s, myString).

    //******************************************************************************
    //
    //   Description:
    //
    //   Baud rate divider with 1048576hz = 1048576/115200 = ~9.1 (009h|01h)
    //   ACLK = REFO = ~32768Hz, MCLK = SMCLK = default DCO = 32 x ACLK = 1048576Hz
    //   See User Guide for baud rate divider table
    //
    //                   MSP430F5438
    //             -----------------------
    //        /|\ |                       |
    //         |  |                       |         ------------------  
    //         ---|RST                    |        |      ADS1118     |        
    //            |                       |        |             __   |
    //            |      P3.1/UCB0SIMO(34)|------->|DIN (10)     CS(2)|-->GND
    //            |      P3.2/UCB0SOMI(35)|<-------|DOUT (9)          |
    //            |      P3.3/UCB0CLK (36)|------->|SCLK (1)          |
    //            |                       |        |                  |
    //            |                       |         ------------------ 
    //            |                       |
    //            |                       |               ------------
    //            |       P3.4/UCA0TXD(39)|------------> |            |
    //            |                       | 115200 - 8N1 |   Beagle   |
    //            |       P3.5/UCA0RXD(40)|<------------ |   Board    |
    //            |                       |              |            |
    //   LED <--- |P1.0                   |               ------------
    //            |                       |
    //             -----------------------
    //
    //   Cory Frey
    //   April 2011
    //   Built with CCE Version: 3.2.2 and IAR Embedded Workbench Version: 4.11B
    //******************************************************************************
    
    #include "msp430x54x.h"
    #include<stdio.h>
    #include<string.h>
    
    #define   DELAY_1SEC    1066667
    #define   DELAY_1mSEC   1067
    #define   DELAY_100mSEC 106700
    #define   DELAY_500mSEC 533334
    #define   TRUE  1
    #define   FALSE 0
    
    // 0000 0001  0010  0011  0100  0101  0110  0111  1000  1001  1010  1011  1100  1101  1110  1111
    //  0    1     2     3     4     5     6     7     8     9     A      B     C    D     E      F
    
    // Function declaritions
    void flashLED(int Sec);
    
    
    void main(void){
      
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
      
      P1DIR |= 0x01;                            // Set P1.0 to output direction
      P3SEL = 0x30;                             // P3.4,5 = USCI_A0 TXD/RXD
      UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**
      UCA0CTL1 |= UCSSEL_2;                     // SMCLK
      UCA0BR0 = 9;                              // 1MHz 115200 (see User's Guide)
      UCA0BR1 = 0;                              // 1MHz 115200
      UCA0MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0
      UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
      // This was the line that was not letting the characters into the program
      //UCA0IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt
     
      while(TRUE){
        char word1[20];
        char word2[20];
        
        printf("\nEnter the first word:\n\t1: ");
        gets(word1);                                 /* Read the first word    */
        
        printf("Enter the second word:\n\t2: ");
        gets(word2);                                 /* Read the second word   */
        
        /* Compare the two words */
        if(strcmp(word1,word2) == 0){
          printf("identical words");
          flashLED(3);
        }
        else
          printf("%s comes before %s", (strcmp(word1, word2) > 0) ? word2 : word1, (strcmp(word1, word2) < 0) ? word2 : word1);
      }
    }
    
    void flashLED(int Sec){
      for(float i = 0; i < Sec; i+=0.2){
        P1OUT ^= 0x01;
        __delay_cycles(DELAY_100mSEC*2);
      }
      
    }

    /*                      - GETCHAR.C -
    
      The ANSI "getchar" function delivered here is supposed to be tailored
      for the actual target hardware.  This version of getchar contains a simple
      line-editor that can back-space and delete  (In_DELETE), skip line and
      start over again (In_SKIP), as well as recognize end of file (In_EOF).
    
      $Revision: 38615 $
    
       Copyright 1986 - 1999 IAR Systems. All rights reserved.
    */
    
    #include <stdio.h>
    #include "msp430x54x.h"
    
    _STD_BEGIN
    
    // My low level get function
    char _low_level_get(void){
     
      while (!(UCA0IFG & UCRXIFG)){
        // keep waiting for the buffer to contain a char;
      }
      unsigned char c = UCA0RXBUF;
      
      return (c);
    }
    
    static void put_message(char *s)
    {
      while (*s)
        putchar(*s++);
    }
    
    
    #define LINE_LENGTH 50         /* Change if you need */
    
    #define In_BACKSP 0x08          /* ASCII  <--  */
    #define In_DELETE 0x7F          /* ASCII <DEL> */
    #define In_EOL '\r'             /* ASCII <CR> */
    #define In_SKIP '\3'            /* ASCII control-C */
    #define In_EOF '\x1A'           /* ASCII control-Z */
    
    #define Out_DELETE "\x8 \x8"    /* VT100 backspace and clear */
    #define Out_SKIP "^C\n"         /* ^C and new line */
    #define Out_EOF "^Z"            /* ^Z and return EOF */
    
    int (getchar)(void)
    {
      static char io_buffer[LINE_LENGTH + 2];     /* Where to put chars */
      static int ptr;                             /* Pointer in buffer */
      char c;
    
      for (;;)
      {
        if (io_buffer[ptr])
          return (io_buffer[ptr++]);
        ptr = 0;
        for (;;)
        {
          if ((c = _low_level_get()) == In_EOF && !ptr)
          {
            put_message(Out_EOF);
            return EOF;
          }
          if (c == In_DELETE)
          {
            if (ptr)
            {
              ptr--;
              put_message(Out_DELETE);
            }
          }
          else if (c == In_SKIP)
          {
            put_message(Out_SKIP);
            ptr = LINE_LENGTH + 1;  /* Where there always is a zero... */
            break;
          }
          else if (c == In_EOL)
          {
            putchar(io_buffer[ptr++] = '\n');
            io_buffer[ptr] = 0;
            ptr = 0;
            break;
          }
          else if (ptr < LINE_LENGTH)
          {
            if (c >= ' ')
            {
              putchar(io_buffer[ptr++] = c);
            }
          }
          else
          {
            putchar('\7');
          }
        }
      }
    }
    
    _STD_END

    //******************************************************************************
    //
    //   Description: putchar function to write the standard output to the UCA0
    //
    //   Cory Frey
    //   April 2011
    //   Built with CCE Version: 3.2.2 and IAR Embedded Workbench Version: 4.11B
    //******************************************************************************
    
    #include<stdio.h>
    #include "msp430x54x.h"
    
    _STD_BEGIN
    
    int (putchar)(int c){
       // **** BEGIN SPECIFIC CODE FOR ANSI TERMINAL ***** //
       //
       // For I/O with ANSI terminal (or equivalent), convert C
       // line end (\n) to carriage-return + linefeed combo (\r\n).
       //
       if (c == '\n') putchar('\r');
       //
       // (**** END SPECIFIC CODE FOR ANSI TERMINAL ***** //
    
       if (c != EOF){
          while (!(UCA0IFG & UCTXIFG)); // keep waiting for the buffer to empty;
          
          UCA0TXBUF = c;
       }
       return (c);
    }
    
    _STD_END

     

    -CoryF

  • That getchar() function is a bit of a misnomer. It blocks until it receives an entire line and doles out the saved line one character at a time. It needs CR to terminate the string but echoes out LF. You might have problems with line editing features of getchar(). Good for human to machine. Bad for machine to machine. Your GUI app will get more complicated as it will get an echo of every character it sends to the MSP430. I think that Jeff was alluding to a design simplification regarding machine to machine communication.

  • Hi Cory,

    Even though you may want to do some development testing with a dumb terminal, your final product isn't using a dumb terminal.  I would definitely not use getchar( ), for all the reasons Norman just pointed out.

    Use an RX ISR to put characters into a queue.

    In a loop in main, get a character out of the queue if any.  If there was one, see if it completes an input data packet as defined by your PC/MSP430 interface.  If it does complete a packet, process the packet, and send your response.  If there was no character in the queue, or if the character did not complete a packet, handle any other system duties.

    You don't need an OS, and you should try to take advantage of the fact that everything sent to you by the PC has been qualified and assembled for you by other friendly software, the Java GUI on the PC.

    Good luck!

    Jeff

  • Jeff Tenney said:

    Use an RX ISR to put characters into a queue.

    This seems much safer, using that method there is no way for me to get stuck waiting for a character to arrive.  I will try to implement this method, I should just have the ISR push the received character onto a string that is on the heap, then in the main check the length, last character, etc. to see if I want to analyze that string.  Thank you guys for the help!  This should work perfect!!

    -CoryF

**Attention** This is a public forum