View previous topic :: View next topic |
Author |
Message |
nickdc
Joined: 19 Sep 2018 Posts: 15
|
Receiving a string from terminal |
Posted: Mon Nov 18, 2019 12:30 pm |
|
|
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: 19538
|
|
Posted: Mon Nov 18, 2019 12:46 pm |
|
|
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
|
|
Posted: Mon Nov 18, 2019 1:51 pm |
|
|
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
|
|
Posted: Tue Nov 19, 2019 3:27 pm |
|
|
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
|
|
Posted: Tue Nov 19, 2019 3:35 pm |
|
|
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
|
|
Posted: Wed Nov 20, 2019 3:00 am |
|
|
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
|
|
Posted: Thu Nov 21, 2019 3:02 am |
|
|
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: 19538
|
|
Posted: Thu Nov 21, 2019 3:18 am |
|
|
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
|
|
Posted: Thu Nov 21, 2019 3:42 am |
|
|
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: 19538
|
|
Posted: Thu Nov 21, 2019 8:55 am |
|
|
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
|
|
Posted: Thu Nov 21, 2019 11:52 am |
|
|
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: 19538
|
|
Posted: Fri Nov 22, 2019 3:16 am |
|
|
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.
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
}
}
}
|
|
|
|
|