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

Receiving a string from terminal

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



Joined: 19 Sep 2018
Posts: 15

View user's profile Send private message

Receiving a string from terminal
PostPosted: Mon Nov 18, 2019 12:30 pm     Reply with quote

I want to send a string from the PC to the UART of the PIC, via the terminal.
I searched the forums and ex_sisr.c in the examples folder was recommended as good start. I have searched the forums for something like I want to do but without success, although the task seems like something someone should have done before.

I need to modify this part:
Code:

while(bkbhit)
        putc( bgetc() );


Instead of printing the chars one by one, I want to store the chars in an array. I should also have code to clear the buffer after it is processed. However, I can't get it working.

The following code is a part of my attempt. I have this in my main loop:

Code:

    char buffer[BUFFER_SIZE];
    int i = 0;
    bool received = FALSE;
    while(TRUE){
        while(bkbhit){
            //putc( bgetc() );
            buffer[i] = bgetc();
            received = FALSE;
            i++;
        }
        if (i > 0 & !received){
            for(int k = i; k < BUFFER_SIZE; k++){
                buffer[k] = ' ';
            }
            printf("buffer: %s \r\n", buffer);
            for (i = 0; i <  BUFFER_SIZE; i++) buffer[i] = '\0';
            received = TRUE;
            i = 0;
        }
    }


EDIT: clarified my post


Last edited by nickdc on Mon Nov 18, 2019 1:52 pm; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Mon Nov 18, 2019 12:46 pm     Reply with quote

That is what ex_stisr does. sisr, is the receive version. stisr is the
transmit one.

The point about taking the characters out 'one by one', is this is all the
UART itself can accept, so characters have to be taken out one by one.

The point about both, is understanding the circular buffer. This is a simple
array, but with two 'indexes' called something like 'in' and 'out'.
When you print to bputc, it writes the character into the array at the
index 'in', and increments this. Then when an interrupt arrives from the
UART to say it can take a character, one character is read from the index
'out', and this index is incremented. The indexes are limited to the size of
the buffer (so if incrementing 'in' goes beyond the top of the buffer it is
wrapped back to 0, and teh same is done with 'out'). The two indexes
'chase' each other round the buffer, with the numeric difference between
in and out representing how many characters are waiting to be sent.
If 'out' catches up with 'in', the buffer is empty. For ST, the interrupt is
turned off at this point and only re-enabled when more data is loaded.
If 'in' catches up with 'out' when loading characters, it means the array
is 'full', and the write routine then has to wait for space to be made in
the buffer as another character is sent.
nickdc



Joined: 19 Sep 2018
Posts: 15

View user's profile Send private message

PostPosted: Mon Nov 18, 2019 1:51 pm     Reply with quote

Sorry, I made a mistake in my first post. I meant sending from the PC to the PIC, not from the PIC to the PC. I had overlooked the "I".
For example, writing a string via broadcast command in Tera Term. The PIC should then invoke a method to process the string further.

Thanks for the explanation, Ttelmah, it's a big help already in understanding what the example does. I didn't get the circular buffer part of the example.
PrinceNai



Joined: 31 Oct 2016
Posts: 480
Location: Montenegro

View user's profile Send private message

PostPosted: Tue Nov 19, 2019 3:27 pm     Reply with quote

The point is that you can't allow your receive buffer to overflow and start overwriting other things. You do that by wrapping your i back to 0 when you reach the end of the buffer and again writing the next character received to position 0 in the buffer. In your case, you don't need two pointers, because you only want to read from PC.


Code:

// define in main
#define BUFFER_SIZE 32                       //create 32 byte large buffer
char buffer[BUFFER_SIZE];
int8 next_in = 0;                            //max. next_in =  BUFFER_SIZE -1 !!!!!
char tmp;



//-----------RS232 data recieve ISR------------------------------------------------------------
#int_RDA
void RDA_isr(void){                   
   tmp=getc();                      // get received char and thus also clear interrupt flag                                       

   buffer[next_in]= tmp;            // move received char to the appropriate place in buffer
   ++next_in;                       // increment IN pointer                                                                                                       
                                                                                                 
   if(next_in == BUFFER_SIZE-1) {     // if we have 32 characters, go back to 0         
      next_in=0;                           
      }

}


How many different strings are you trying to react upon?
temtronic



Joined: 01 Jul 2010
Posts: 9243
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Nov 19, 2019 3:35 pm     Reply with quote

What is critical is to add 'errors' to the use #rs232(...options...) ! Without it, the UART hardware will 'lock up' after the 3rd character is sent to it.

Jay
PrinceNai



Joined: 31 Oct 2016
Posts: 480
Location: Montenegro

View user's profile Send private message

PostPosted: Wed Nov 20, 2019 3:00 am     Reply with quote

Code:

//------------------------ clear UART buffer ----------------------------------
void Clear_UART_Buffer() {         
   next_in=0;                                                                                 
   while (next_in < BUFFER_SIZE){                                                                   
      buffer[next_in] = '\0';                     
      next_in++;
   }
   next_in=0;
}
PrinceNai



Joined: 31 Oct 2016
Posts: 480
Location: Montenegro

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 3:02 am     Reply with quote

This captures the string from PC:

main.c
Code:

#define BUFFER_SIZE 32                 //create 32 byte large buffer
char buffer[BUFFER_SIZE];
int8 next_in = 0;                      //max. next_in =  BUFFER_SIZE -1 !!!!!
char tmp;
int8 Action_Flag = 0;

// ***************** INTERRUPTS ***********************************************
#INT_TIMER0
void  TIMER0_isr(void)
{
   output_toggle(PIN_D6);           // just to see the PIC is working
}

#INT_RDA
void  RDA_isr(void)
{
   tmp=getc();                      // get received char and thus also clear interrupt flag                                       

   buffer[next_in]= tmp;            // move received char to the appropriate place in buffer

   if((next_in > 0) && (tmp == '\n')) {   // if we already have something in the buffer AND we got a new line from PC as the last character,
      Action_Flag = 1;              // string is received, signal that to main to process it
      buffer[next_in-1]= '\0';      // we also got CR and LF,  write NULL after the actual string itself
      next_in = 0;                  // reset IN pointer
   }
   else{
      next_in++;                    // regular character was received, increment IN pointer
   }     
                                                                                                 
   if(next_in == BUFFER_SIZE-1) {   // if we have 32 characters, go back to 0         
      next_in = 0;                  // we shouldn't even come here, if the buffer is big enough                     
   }
}

// *************************** MAIN *******************************************
void main()
{
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_32);   
   enable_interrupts(INT_TIMER0);
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   while(TRUE)
   {
      if(Action_Flag){
         Action_Flag = 0;
//         Proccess_your_string here       
      }
   delay_ms(50);

   }        // while

}           // main


main.h
Code:

#include <18F46K22.h>
#device ADC=10

#FUSES NOWDT                    //No Watch Dog Timer

#device ICD=TRUE
#use delay(internal=8000000)
#use rs232(baud=57600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1,errors)


Last edited by PrinceNai on Thu Nov 21, 2019 11:48 am; edited 3 times in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 3:18 am     Reply with quote

Now, 'caveats' with this:
1) Nothing is resetting 'next_in'. The processing code must do this.
2) It does not receive a 'string'. In C, a 'string' is a null terminated array
of characters. It needs to write a '\0' into the buffer when the line feed
is received, for the array to then use any 'string' functions.
3) There is only one character 'time' available to process the buffer, before
it either could get larger, or get overwritten (if next_in has been reset).
PrinceNai



Joined: 31 Oct 2016
Posts: 480
Location: Montenegro

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 3:42 am     Reply with quote

next_in is reset by Clear_UART_Buffer() function. For the null part it is true, I made a correction there. The most problematic part is the time to process the string. If those strings do come in too fast, things would brake down.

That is why I asked in my previous post how many different strings are expected from the PC. Personally I like more to process them directly in the RDA interrupt via a state machine, so no time is lost in main.
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 8:55 am     Reply with quote

Clear_UART_buffer is pointless. Provided you correctly null terminate, there
is no reason to waste the time clearing all the buffer entries.
Just set the pointer back to the start and get back to receiving ASAP.
PrinceNai



Joined: 31 Oct 2016
Posts: 480
Location: Montenegro

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 11:52 am     Reply with quote

Mr. Ttelmah,

your comments were well received and hopefully led to a correctly working and way cleaner code. I made the changes in my original post.

Thanks,
Samo
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Fri Nov 22, 2019 3:16 am     Reply with quote

As a little demo, I decided to post an example program of a 'line' based
buffering system.
As with PrinceNai's example, this receives a 'line' of text from the serial
telling the main code when a line feed has been seen, by setting a flag.
This though will carry on receiving into a second line buffer while the first
is being processed.
Set up for a PIC18F45K50 with 20MHz crystal. Tweak to suit whatever
processor is required. Smile

Code:

#include <18F45K50.h>
#device ADC=10

#FUSES NOWDT

#use delay(crystal=20000000)
#use rs232(UART1, ERRORS, STREAM=SERIAL)

//Demo of a basic 'fetch a line' buffer system
#define MAX_LINE 32 //specifies the buffer size to use
byte buffer[2][MAX_LINE] = {}; //create a two line buffer
int8 toggle=0; //Toggle for which buffer to access
int8 in_locn=0;  //buffer input location
int1 have_line=FALSE; //flag that a line has not been processed

#inline //fetch the address of the received line
char * current_line(void)
{
   return &buffer[toggle^1][0]; //return the start of the 'other' line
}   

#INT_RDA
void have_char(void)
{
   //serial receive. Just add character to buffer, increment input
   //location, and if char is a LF, set flag to say a line is waiting.
   //Then swap buffer to use.
   byte temp;
   temp=fgetc(SERIAL);
   if (temp=='\n')
   {
      if (in_locn==0)
         return; //ignore if buffer empty
      buffer[toggle][in_locn]='\0'; //terminate string
      in_locn=0; //start a new line     
      toggle^=1; //swap buffer lines
      return;
   }
   buffer[toggle][in_locn++]=temp;
   if (in_locn>=(MAX_LINE))
      in_locn--; //limit line size - will result in data being lost if
      //line tries to go past MAX_LINE characters including the LF 
}


void main(void)
{
   char * line; //pointer used for line access
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_RDA); //start the receive interrupt handling
   while(TRUE)
   {
      if (have_line) //Now a line has been received
      {
         line=current_line(); //get the line to use
         //This should only be called once 'have_line' goes TRUE
         have_line=FALSE;
         printf("%s", line); //print the received line as a demo
      }   
   }
}
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