CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

knotty problem with string functions

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
adrian



Joined: 08 Sep 2003
Posts: 92
Location: Glasgow, UK

View user's profile Send private message

knotty problem with string functions
PostPosted: Sun Nov 16, 2003 10:59 am     Reply with quote

I am trying to create a function that reads an ASCII number that a user types on a keyboard. This number will always be in the decimal format, unsigned, and in the range of 0 to 65535. Once this number has been read into the PIC, I wish to convert it into an unsigned long.

So far I have a working programme that uses the gets() function to read in the number. Unfortunately, gets() does not echo back what has been entered, so the user is effectively typing blind. I know the string function is correct, as I call atol and printf the result. Anybody got a better idea?

Secondly, is there an unsigned version of atol? The signed long atol(char *s) from stdlib.h is an over kill for what I need. As my number will ALWAYS be decimal and unsigned.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Sun Nov 16, 2003 2:01 pm     Reply with quote

Use getc() and then putc() to read in the string.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Nov 16, 2003 3:27 pm     Reply with quote

You can use the CCS function get_string(), instead of gets().
It echoes the characters. It also has other features, like backspace,
and a maximum char limit, so you don't overflow the buffer.
It's in INPUT.C, which is in this folder: c:\Program Files\Picc\Drivers


For a cheap version of atol, which only handles ascii decimal to long,
I have this function:

Code:
// Convert a string of up to 5 ascii decimal digits
// ("65535" max) to a binary long.   Any value that
// is not ascii decimal will end the conversion.
// For example  "25," will convert the first two digits
// and then stop at the comma, and return 25 as the result.

long adtol(char *ptr)
{
long retval;
char c;
char i;

retval = 0;

for(i = 0; i < 5; i++)
   {
    c = *ptr++;    // Read char from string

    if(c < '0')    // Test if it's an ascii decimal value
       break;
    if(c > '9')
       break;

// Multiply retval * 10, and subtract the ascii bias of 0x30.
    retval = (retval << 3) + (retval << 1) + (c - '0');
   }

return(retval);
}
adrian



Joined: 08 Sep 2003
Posts: 92
Location: Glasgow, UK

View user's profile Send private message

PostPosted: Sun Nov 16, 2003 4:11 pm     Reply with quote

Mark wrote:
Use getc() and then putc() to read in the string.


OK I could do that, but how do I group up to 5 individual characters together to perform a atol function?
adrian



Joined: 08 Sep 2003
Posts: 92
Location: Glasgow, UK

View user's profile Send private message

PostPosted: Sun Nov 16, 2003 4:19 pm     Reply with quote

PCM programmer wrote:
You can use the CCS function get_string(), instead of gets().

OK I see from this that it takes the following form:
void get_string(char * s,int max)
Does this mean that 'max' defines the maximum number of characters that can be accepted by this function? What does 'len' do?

Also your long adtol(char *ptr) function looks ideal, I shall give it a bash.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Sun Nov 16, 2003 4:34 pm     Reply with quote

What is your definition of a string? An array of char. As each char is read place it in the array or you could use a pointer.


I like to use interrupts to receive. I use something like this in the int_rda
Code:

    if ((RCSTA1bits.FERR) || (RCSTA1bits.OERR))
    {
      Comstat.Rx_Bufferoverrun = TRUE;
      PIE1bits.RC1IE = 0;         /* Disable Interrupt on receipt */
    }
    else if (Comstat.Rx_Bytes++ < RX_BUFFER_SIZE - 1)
    {
      Rx_Buffer[Comstat.RxHead++] = RCREG1;

      /* Stick a Null on the end to let us use str functions on our
       * buffer */
      Rx_Buffer[Comstat.RxHead] = 0;
    }
    else
    {
      Comstat.Rx_Bufferoverrun = TRUE;
      PIE1bits.RC1IE = 0;         /* Disable Interrupt on receipt */
    }


Then I have a function that I call from main

Code:

/* *************************************************************************
  NAME: TerminalHandler

  DESCRIPTION:  Handles the terminal operation PARAMETERS: port (IN)
                communication port GLOBALS: none

  RETURN: none

  ALGORITHM:  none

  NOTES:  none
 *************************************************************************** */
void TerminalHandler(
  enum porttypes  port) /* FIX ME: add comment */
{
  static UINT8  index1 = 0;         /* index of buffer */
  static UINT8  index2 = 0;         /* index of buffer */
  int           i;                  /* character from buffer */
  char          ch;                 /* character from buffer */
  static char   buf1[MAX_LINE_LEN]; /* for the line entry */
  static char   buf2[MAX_LINE_LEN]; /* for the line entry */
  UINT8         *p_index;
  char          *buf;

  /* setup pointers to the buffers for the port that we are reading */
  p_index = &index1;
  buf = buf1;
  switch (port)
  {
    case pt_RS232:
      /* p_index and buf are already initialized */
      break;
    case pt_RS485:
      p_index = &index2;
      buf = buf2;
      break;
    default:
      return;
  }

  while (TRUE)
  {
    i = HostReadChar(port);

    /* port error */
    if (i < 0)
      return;

    ch = (char)i;
    switch (ch)
    {
      /* illegal characters */
      case '\a':
      case '\f':
      case '\n':
      case '\t':
      case '\v':
      /* escape */
      case 0x1B:
        break;

      /* backspace */
      case '\b':
        if (*p_index)
        {
          --(*p_index);

          /* do a destructive backspace */
          HostWriteChar(port, '\b');
          HostWriteChar(port, ' ');
          HostWriteChar(port, '\b');
        }
        break;

      /* enter */
      case '\r':
        HostWriteChar(port, '\r');
        HostWriteChar(port, '\n');
        buf[*p_index] = '\0';

        /* did we get something? */
        if (*p_index)
        {
          if (!CommandHandler(port, buf, &CommandData[0]))
            HostWriteRomString(port, "Invalid Command. Type ? for help.\r\n");
        }

        buf[0] = '\0';
        *p_index = 0;

        /* write the prompt */
        HostWriteChar(port, '>');
        break;

      /* all the rest of the characters */
      default:
        /* leave room for null at the end */
        if (*p_index < (MAX_LINE_LEN - 1))
        {
          buf[*p_index] = ch;
          if (!ch)
            ch = 0;
          HostWriteChar(port, ch);
          (*p_index)++;
        }
        break;
    }
  }
}


The above function actually is used to handle 2 serial ports but it gives you an idea as to how I do it. CommandHandler() is a function that looks at the string and decides what to do by removing the first word and comparing it to a list of command. Then the proper function is called. I will post the code but it uses pointers to functions and some string functions that I wrote myself.

Code:

static BOOLEAN CommandHandler(
  enum porttypes  port,           /* FIX ME: add comment */
  char            *pArgs,         /* FIX ME: add comment */
  COMMAND_DATA    *pCommandData)  /* FIX ME: add comment */
{
  static char cmd[MAX_LINE_LEN];  /* local buffer */
  BOOLEAN     found = 0;          /* valid command? */

  if (pArgs)
  {
    /* parse and remove the only the first command */
    pArgs = stptok(pArgs, cmd, sizeof(cmd), " ");

    /* find and execute command function */
    while (pCommandData->pStr != NULL)
    {
      if (strcmpipgm2ram(cmd, pCommandData->pStr) == 0)
      {
        if (pCommandData->pFunction != NULL)
        {
          (void)strupr(cmd);
          (void)pCommandData->pFunction(port, cmd, pArgs);
        }

        found = 1;
        break;
      }

      pCommandData++;
    }
  }

  return (found);
}



Here is how I declare the commands:
Code:

/* local typedefs */
typedef rom struct  _cmd_t
{
  const rom char  *pStr;  /* command */
  void (*pFunction) (enum porttypes port, char *pCmd, char *pArgs); /* command function */
  const rom char  *pDescription;  /* command description (for help) */
} COMMAND_DATA;

static COMMAND_DATA CommandData[] =
{
  /* hidden commands - description is NULL */
  { { "help" }, VHelpCommand, { NULL } },
  { { "OK" }, NULL, { NULL } },
  { { "RING" }, AnswerCommand, { NULL } },
  { { "CONNECT" }, ConnectCommand, { NULL } },
  { { "SELFTEST" }, SelfTestCommand, { NULL } },
  { { "VIRGINIZE" }, VirginCommand, { NULL } },
  { { "date" }, DateCommand, { "get/set date mm/dd/yyyy" } },
  { { "time" }, TimeCommand, { "get/set time hh:mm:ss" } },

  /* application commands */
  { { "ver" }, VersionCommand, { "get program version" } },
  { { "name" }, NameCommand, { "program/view device name" } },
  { { "modem" }, ModemCommand, { "program/view modem data" } },
  { { "event" }, EventCommand, { "program/view event data" } },
  { { "eventlist" }, EventListCommand, { "view current event schedule" } },
  { { "holiday" }, HolidayCommand, { "program/view holiday data" } },
  { { "relay" }, RelayCommand, { "program/view relay data" } },
  { { "input" }, InputCommand, { "program/view input data" } },
  { { "photocell" }, PhotocellCommand, { "program/view photocell data" } },
  { { "remote" }, RemoteCommand, { "program/view remote input data" } },
  { { "mask" }, MaskCommand, { "program/view mask data" } },
  { { "location" }, LocationCommand, { "program/view location" } },
  { { "password" }, PasswordCommand, { "program/view password data" } },
  { { "data" }, DataCommand, { "view program data" } },
  { { "reinit" }, ReInitCommand, { "reinitialize program data" } },
  { { "restart" }, RestartCommand, { "restart system" } },
  { { "bootmode" }, BootCommand, { "puts the device into bootmode" } },
  { { "checksum" }, ChecksumCommand, { NULL } },
  { { "??" }, VHelpCommand, { "verbose help" } },
  { { "?" }, THelpCommand, { "terse help" } },
  { { NULL }, NULL, { NULL } },
};


And here is one of the functions:
Code:

static void TimeCommand(
  enum porttypes  port,   /* (IN) communication port */
  char            *pCmd,  /* (IN) command string */
  char            *pArgs) /* (IN) optional arguments */
{
  UINT8 hour = 0;
  UINT8 minute = 0;
  UINT8 second = 0;
  UINT8 count = 0;
  char  buf[MAX_LINE_LEN];  /* local buffer */
  (void)pCmd;
  if (pArgs && isdigit(pArgs[0]))
  {
    count = sscanf(pArgs, "%b:%b:%b", &hour, &minute, &second);
    if (count >= 2)
    {
      if (Time_SetTime(hour, minute, second))
      {
        Event_Restart();
        PrintStatus(port, TRUE);
      }
      else
        PrintStatus(port, FALSE);
    }
    else
      PrintStatus(port, FALSE);
  }
  else
  {
    hour = Time_GetHours(Systemtime.time);
    minute = Time_GetMinutes(Systemtime.time);
    second = Systemtime.second;
    sprintf(buf, "%02d:%02d:%02d\r\n", (int)hour, (int)minute, (int)second);
    HostWrite(port, buf);
  }

  return;
}


If some of the syntax seem unfamilar, it is because these are actually snippets from some code that I wrote for the Microchip C18 compiler. Hey, it is C though!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Nov 16, 2003 6:01 pm     Reply with quote

Quote:
OK I see from this that it takes the following form:
void get_string(char * s,int max)
Does this mean that 'max' defines the maximum number of characters that can be accepted by this function? What does 'len' do?


'max' is actually the buffer size. The number of chars you
can enter is 1 less than this amount. ie, in the following
program, you can enter up to 3 chars. The last buffer element
is reserved for the string terminator char of 0, which is
inserted by get_string().

'len' is an internal variable used by the function to keep
track of how many chars you have entered. You don't
have to deal with 'len', because it's not part of the parameter
list. You only have to specify the buffer pointer, and the
buffer size.

Code:
#include <16F877.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 8000000)
#use rs232(baud = 9600, xmit=PIN_C6, rcv = PIN_C7, ERRORS)

#include <input.c>

#define BUF_SIZE  4

main()
{
char array[BUF_SIZE];

// Type in a string, and then press the Enter key.
get_string(array, BUF_SIZE);

printf("\n\r%s", array);  // Display the string.

while(1);

}
adrian



Joined: 08 Sep 2003
Posts: 92
Location: Glasgow, UK

View user's profile Send private message

PostPosted: Tue Nov 18, 2003 2:15 am     Reply with quote

Many thanks for your help guys - I now have working code.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group