avr uart string receive and compare

Go To Last Post
21 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>
#define F_CPU 8000000
#define BAUD 9600
#define RATE ((F_CPU/16/BAUD)-1)
#include <util/delay.h>
#include <avr/interrupt.h>
#define LED_ON  PORTD |= (1<<PIND6);
#define LED_OFF6  PORTD &= ~(1<<PIND6);
#define LED_OFF5  PORTD &= ~(1<<PIND5);
#define toggel_led6  PORTD ^=(1<<PD6);
#define toggel_led5  PORTD ^=(1<<PD5);

volatile int a=0,b=0,c=0,m=0,n=0;

// #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
// #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define RX_BUFFER_SIZE  128

char rxBuffer[RX_BUFFER_SIZE];
uint8_t rxReadPos=0;
uint8_t rxWritePos=0;

char getChar(void);
char peekChar(void);

 void T2delay()
 {
	 while(TIFR&0x01==0);
	 TCCR2=0;
 }

 void T2_init()
 {
	 TCCR2 = (1<<CS22)|(1<<CS21)|(1<<CS20);
	 TCNT2 = 0xB2;
 }

int main(void)
{
	DDRD |=(1<<PIND6)|(1<<PIND5);
	TIMSK=(1<<TOIE2);
	T2_init();

	UBRRH |= (RATE>>8);
	UBRRL |= RATE;

	UCSRB |= (1<<RXEN)|(1<<RXCIE);
	UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
	sei();
	while (1)
	{
		char x = getChar();

	}	

}

char peekChar(void)
{
	char ret='\0';
	if (rxReadPos != rxWritePos)
	{
		ret=rxBuffer[rxReadPos];
	}
	return ret;
}

char getChar(void)
{
	char ret='\0';
	if (rxReadPos != rxWritePos)
	{
		ret=rxBuffer[rxReadPos];
		rxReadPos++;
		if (rxReadPos>=RX_BUFFER_SIZE)
		{
			rxReadPos=0;
		}
	}
	return ret;
}

ISR(USART_RXC_vect)
{
	rxBuffer[rxWritePos]=UDR;

	rxWritePos++;
	if (rxWritePos>=RX_BUFFER_SIZE)
	{
		rxWritePos=0;
	}
}

i have made this code for uart receive but i want to receive a string through uart and store in a variable then compare the string to execute any function.if it possible to you then please help me how do i make this work i want.

Rjchoudhary

Last Edited: Sat. May 23, 2020 - 09:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First you must decide how you will denote the end of the string.

 

A reasonable choice may be a carriage return '\r'.

 

Secondly; it makes for easier coding if you decide the maximum length of string you wish to transmit.

Last Edited: Sat. May 23, 2020 - 11:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As Nigel alludes to you need some kind of loop calling getchar() repeatedly. It needs (at least) two stopping conditions. One is to stop when you have received so many characters that to receive another would overflow the receiving array (and don't forget you always need room for an additional 0x00 at the end to mark the end). The other is you need to stop when you receive some king of "end of string" marker which is often '\n' (newline) but could also be '\r' (return) or perhaps even '.' (full stop at the end of a sentence)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First you must decide how you will denote the end of the string.

Using a terminator is probably 99% most common and easiest and recommended.  However, depending on what you exactly do, it is not always needed.  For example, you are waiting to get A P P L E   or  D O G   ....you  could  check char-by-incoming-char until you get one or the other, then take the corresponding action.    This sequence checking can be some form of a state machine.   It is usually easier to wait for the terminator and do the checking afterwards.  That also better allows checking DOG/r vs DOGGY/r & provides an easy resync point (since at some time you'll get partial or garbled incoming).

  

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear sir, i have already made a getchar function.suppose i am transmitting "apple\r" but how would my controller know that the complete string has been received on receiver side and also how would i store and where. I will be highly glad if you kindly suggeste me any sample code if possible.

Rjchoudhary

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you bother to read any of the answers so far?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well I see this in your main loop:

	while (1)
	{
		char x = getChar();

	}

So you already have most of the "engine".

 

You could start with an empty string in which you will eventually store "apple\r". This will do.

char rxStr[16];
  • Clear down rxStr to begin.
  • You know the first characters in order if all goes well will be 'a' , 'p', 'p', 'l', 'e', '\r'
  • If x is NOT '\r', you need to append this character onto the end of rxStr and loop around.
  • If the character x just received from getChar() was '\r'. You know you've reached the end and can move on to compare the strings.

 

Except for error handling - this is it.

 

Research the functions in string.h.they will be your friends. There are many websites covering this : http://www.cplusplus.com/reference/cstring/ is one.

 

Last Edited: Sat. May 23, 2020 - 04:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see I'm just duplicating what has been said already, but since I already wrote the reply :

 

I'm not going to write the code, but to give you a start,

I would have a separate message buffer, big enough for whatever the maximum message len is going to be eg.

#define MAX_MSG_LEN 10

/* +1 to allow for null termination */

char msg_buf[MAX_MSG_LEN + 1];

uint8_t msg_buf_index = 0;

 

When you receive the first character ('a' in this example) write it to msg_buf, then when you receive the next character ('p' in this example) write that to msg_buf, and keep going until you receive the terminating character, typically \r or \n (or until you have received MAX_MSG_LEN characters, any more characters received after this up to the terminating character will be ignored).

When you receive the terminating character, write a '\0' into msg_buf instead of the terminating character.

So msg_buf now contains  'a', 'p', 'p', 'l', 'e', '\0'

You now have a complete messgae in msg_buf and it can be handled as a null terminated string, do whatever you need to do with it, then reset msg_buf_index to 0.

 

Yor getchar function returns 0 if no new data is available in the rx buffer. That's probably OK if you only ever intend to receive text characters, but clearly doesn't work if you might actually want to receive a character with value 0 ie. not so good for binary data.

 

Last Edited: Sat. May 23, 2020 - 04:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

but how would my controller know that the complete string has been received on receiver side and also how would i store and where

You already have it stored in your RX buffer...do you need to keep it around further?  If not, that is enough, if you can start comparing right away & don't need to worry about the buffer being overwitten.

If you are looking for apple you can do it letter by letter as they arrive 

   wait for an  A... is it an "A" ?  No--start over

   yes--wait for a  P... is it an "P" ?  No--start over

    yes--wait for a  P... is it an "P" ?  No--start over

    etc

This way is more complex since if you are checking against 5 defined words, you may have to simultaneously  "keep track" of decoding 5 potential matches..And possibly eliminating them until a winner is found (or possibly no match at all).

-----------------------

Better to simply set a flag when you get a terminator  (like Carriage return), which tells the code to go ahead & compare the buffer segment against your list (dictionary), in some smart manner (for example check against the most common match first & save the rare for checking last). 

ex:

LEDOFF

LEDON  ...common

VALVEON

VALVEOFF 

FWD

STOP

BLINK

ERASE   ...rare

PARITYON ..rare

 

You might  also say during decoding,1)any incomings will be ignored (hence further commands must wait for an "ok" response before sending) 2) allow enough room in the buffer so there is enough space for additional incoming segments (separated by CR), for later decodes (setting up a queue).

 

it's a lot simpler for your first program to assume one command will come in, you will decode &  if valid, take action, THEN being finished with that, another command may thereafter be received.  So you don't allow any overlapping, making it very straightforward.  Upon detecting the terminator, the buffer will only contain either one command, or else be assumed to contain junk.  Also in this simpler case, you don't even need a circular buffer, you can just reset the pointer to the beginning & refill buffer with another incoming (though a circular is fine too).

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sat. May 23, 2020 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>
#define F_CPU 8000000
#define BAUD 9600
#define RATE ((F_CPU/16/BAUD)-1)
#include <util/delay.h>
#include <string.h>
#include <stdio.h>
#include <avr/interrupt.h>
#define LED_ON  PORTD |= (1<<PIND6);
#define LED_OFF6  PORTD &= ~(1<<PIND6);
#define LED_OFF5  PORTD &= ~(1<<PIND5);
#define toggel_led6  PORTD ^=(1<<PD6);
#define toggel_led5  PORTD ^=(1<<PD5);

volatile int a=0,b=0,c=0,m=0,n=0;

// #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
// #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define RX_BUFFER_SIZE  128


char rxBuffer[RX_BUFFER_SIZE];
char compBuffer[RX_BUFFER_SIZE+1];
unsigned int rx_wr_index;
char Reply_CallReady[RX_BUFFER_SIZE]="apple\0";
uint8_t rxReadPos=0;
uint8_t rxWritePos=0;

char getChar(void);
char peekChar(void);

void T2delay()
{
	while(TIFR&0x01==0);
	TCCR2=0;
}

void T2_init()
{
	TCCR2 = (1<<CS22)|(1<<CS21)|(1<<CS20);
	TCNT2 = 0xB2;
}

int main(void)
{
	DDRD |=(1<<PIND6)|(1<<PIND5);
	TIMSK=(1<<TOIE2);
	T2_init();
	
	UBRRH |= (RATE>>8);
	UBRRL |= RATE;
	
	UCSRB |= (1<<RXEN)|(1<<RXCIE);
	UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
	sei();
	while (1)
	{
		compBuffer[RX_BUFFER_SIZE+1] = getChar();
		if (strcmp(Reply_CallReady,compBuffer) ==0)
		{
			LED_ON;
		}
		else 
		{
			LED_OFF6;

		}

	}
	
}



char peekChar(void)
{
	char ret='\0';
	if (rxReadPos != rxWritePos)
	{
		ret=rxBuffer[rxReadPos];
	}
	return ret;
}

char getChar(void)
{
	char ret='\0';
	if (rxReadPos != rxWritePos)
	{
		ret=rxBuffer[rxReadPos];
		rxReadPos++;
		if (rxReadPos>=RX_BUFFER_SIZE)
		{
			rxReadPos=0;
		}
	}
	return ret;
}

ISR(USART_RXC_vect)
{
	rxBuffer[rxWritePos]=UDR;
	
	rxWritePos++;
	if (rxWritePos>=RX_BUFFER_SIZE)
	{
		rxWritePos=0;
	}
}

sir i am still unable to store and compare the string it does not working.kindly help me to get the solution if possible.

 

Rjchoudhary

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

what is this line doing?

compBuffer[RX_BUFFER_SIZE+1] = getChar();

you need to write a function that will collect the characters into a string and return when you detect a carriage return character. Once you have a string, then you can do a comparison.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are going to build up the receive message in a separate linear buffer (your compBuffer), which I think is a good way to do it, you have to use your existing getChar function to get each new character one at a time and append that character onto the end of compBuffer, until you get a \r (or whatever character you wish to use to terminate each message), at which point append a '\0'. You now have a complete message as a null terminated string. At this point you do the strcmp(compBuffer, "apple") or whatever.

You will need to keep track of where to write next into compBuffer, so have a separate index for this

uint8_t compBufferPos = 0;

This starts out as 0. Increment as you append each new character. Reset to 0 after you have handled a complete message ready for the start of the next message.

Your compBuffer doesn't needto be the same size as the uart rx circular buffer.

Your rx circular buffer probably doesn't need to be as big as 128.

Your compBuffer needs to be big enough to hold the longest individual message.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As the bare-bones getting started try this:

 

STEP A : set the buffer pointer to the beginning

Turn on the rcv interrupt (RXIE) bit

----

Receive your chars into the buffer, looking for CR & simply increase the pointer

     in the interrupt, if you see a cr, set a flag and disable the RX irq enable (actually the RX irq enable bit could serve as the flag)\

If the buffer reaches the end before CR, something went wrong...reset the pointer to the beginning (the next CR will resync).

 

in main: 

 if your CR RX flag is set, you are ready to scan the buffer, from beginning to CR, looking for comparisons

 Once that is finished, go to step A, & wait for the next message 

 

Drawbacks (which can be minor), are that:

   You will miss any incoming during your checking.  Messages will likely be processed within a fraction of a millisecond...so a message overlap might be rare (depending on your message separations).

   Since there is no message queue, you need to check the flag often to minimize delay.

 

You can get this much easily working, then you can upgrade to a circular buffer, copying the message segment to a different buffer, adding more buffer error detections, etc.

Even with a circ buff, there should be message handshaking, otherwise even a buffered setup could eventually overflow (if msg processing is slowed or delayed).

 

  

 

 

 

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>
#define F_CPU 8000000
#define BAUD 9600
#define RATE ((F_CPU/16/BAUD)-1)
#include <util/delay.h>
#include <string.h>
#include <stdio.h>
#include <avr/interrupt.h>
#define LED_ON  PORTD |= (1<<PIND6);
#define LED_OFF6  PORTD &= ~(1<<PIND6);
#define LED_OFF5  PORTD &= ~(1<<PIND5);
#define toggel_led6  PORTD ^=(1<<PD6);
#define toggel_led5  PORTD ^=(1<<PD5);

volatile int a=0,b=0,c=0,m=0,n=0;
volatile unsigned char j=0;
volatile unsigned char string[15],x,i =0;

#define RX_BUFFER_SIZE  128

char rxBuffer[RX_BUFFER_SIZE];
unsigned int rx_wr_index;
char Reply_CallReady[15]="apple";
uint8_t rxReadPos=0;
uint8_t rxWritePos=0;

char getChar(void);
char peekChar(void);

void T2delay()
{
	while(TIFR&0x01==0);
	TCCR2=0;
}

void T2_init()
{
	TCCR2 = (1<<CS22)|(1<<CS21)|(1<<CS20);
	TCNT2 = 0xB2;
}

unsigned char *UART_RX_STR(void)
{
	x= getChar();
	while (x != '\0')
	{
		string[i++] = x;
	}
		return string;

}

int main(void)
{
	DDRD |=(1<<PIND6)|(1<<PIND5);
	TIMSK=(1<<TOIE2);
	T2_init();

	UBRRH |= (RATE>>8);
	UBRRL |= RATE;

	UCSRB |= (1<<RXEN)|(1<<RXCIE);
	UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
	sei();
	char c;
	while (1)
	{
			//c= getChar();
			UART_RX_STR();
					if (strcmp(string,Reply_CallReady) ==0)
					{
						LED_ON;

					}
					else
					{
						LED_OFF6;
					}

	}

}

char peekChar(void)
{
	char ret='\0';
	if (rxReadPos != rxWritePos)
	{
		ret=rxBuffer[rxReadPos];
	}
	return ret;
}

char getChar(void)
{
	char ret='\0';
	if (rxReadPos != rxWritePos)
	{
		ret=rxBuffer[rxReadPos];
		rxReadPos++;
		if (rxReadPos>=RX_BUFFER_SIZE)
		{
			rxReadPos=0;
		}
	}
	return ret;
}

ISR(USART_RXC_vect)
{
	rxBuffer[rxWritePos]=UDR;

	rxWritePos++;
	if (rxWritePos>=RX_BUFFER_SIZE)
	{
		rxWritePos=0;
	}
}

hello sir, now i have made an array to store the string but still it is not working, can you please check my code and help me to find the fault i have made in this.

 

Rjchoudhary

Last Edited: Tue. May 26, 2020 - 07:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
unsigned char *UART_RX_STR(void)
{
	x= getChar();
	while (x != '\0')
	{
		string[i++] = x;
	}
		return string;

}

Even after your change, this function does not make sense - unless you want to fill memory with the value of x.

 

Have you ever considered what you want to do is so common that there is a standard function for it? gets() perhaps. Google it.

 

To help you along, here's this: https://www.nongnu.org/avr-libc/...

A little more Googling will find you examples of how to use printf() and scanf(). You'll probably find these standard functions helpful as well.

Last Edited: Tue. May 26, 2020 - 07:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also why does UART_RX_STRING

 

a) have a name in all upper case - usually that suggests a macro not a function?

b) have a return value of char * but then the invocation completely ignores this?

c) return a global?

 

Seems to me you have some basic C concepts to learn before you can move to application implementation.

 

Oh and just write out on paper what you think an "RX_STRING" function needs to do first so you have a clear picture. THEN write the accompanying C code.

 

Something like:

 

"keep calling getchar until the character returned is '\n' (or whatever end marker is to be used), while not at the end put character into buffer passed by the caller. If number of received characters reaches length of buffer then stop. At hte end mark the last byte of the string with 0x00"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think we freaks have overloaded you with much conflicting advice.

 

  1. Firstly; have you done any programming in C on your Windows PC ? You will find writing and testing software a whole lot easier if you can do this. You can enter your "LEDON" command at the keyboard instead of using Serial Comms if you wish.
  2. You can write your command processor using the string handling functions exclusively. Although not optimal for size, this will be your fastest route to a working program.
  3. I'd hoped My post #7 would lead you to write an inner and outer loop. the inner loop does the getChar() thing; appending any received characters into the "received command" string and when '\r' is received it executes the right command. The outer loop clears down the "received command" string and waits for the next command.

 

Here are my #7 notes paraphrased into Structured English / C code: [The C code being what you've already written]

void main (void)
{
    char rxCmd[16];

    while (1) {
        clear down rxCmd to empty string; // you can use strcpy for this or set the 1st character to 0
        while (1) {

            char c = getChar();

            if (c == '\r') {

                if (strcmp(rxCmd, "LEDON") == 0) {
                    LED_ON();
                    break;
                }

                if (strcmp(rxCmd, "LEDOFF") == 0) {
                    LED_OFF();
                    break;
                }

            }
            else if (c != 0) {
                concatenate c onto rxCmd; // You can use strncat for this. Read
http://www.cplusplus.com/reference/cstring/
            }
        }
    }

}

Error handling is missing but essentially that's all there is to it.

 

Last Edited: Tue. May 26, 2020 - 09:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My take on it:

void getString(char * buf, int len) {
    int idx = 0;
    while(1) {
        c = getChar();
        // if it is RET or NL..
        if ((c == '\r') || (c == '\n')) {
            break;
        }
        // else store the received character
        buf[idx] = c;
        // increment the index
        idx++;
        // if reached the end (with room for final 0x00) break
        if (idx == (len - 1)) {
            break;
        }
    }
    // mark the end of the
    buf[idx] = 0x00;
}

int main{void) {
    char rxString[14];
    while (1) {
        getString(rxString, 14);
        if (strcmp(rxString, "LEDON") == 0) {
            LED_ON();
        }
        else if (strcmp(rxString, "LEDOFF") == 0) {
            LED_OFF();
        }
    }
}

EDIT: added "== 0" to strcmp()s - thanks Mr Kendo!

Last Edited: Tue. May 26, 2020 - 12:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The OP's getChar function is non blocking and returns 0 if the uart rx buffer is empty, so I would add one extra modification to #18

 

 

void getString(char * buf, int len) {
    int idx = 0;
    while(1) {
        c = getChar();
        if (c == '\0') {  <======== here
            continue;
        }
        if ((c == '\r' etc.))

 

EDIT

oh, and the usual typo with strcmp, should be

if (strcmp(rxString, "LEDON") == 0)

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#ifndef F_CPU
#define F_CPU 8000000UL // 16Mhz clock speed
#endif
#include <avr/io.h>
#include <util/delay.h>
//#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) -1)

void UART_init(long USART_BAUDRATE)
{
	UCSRB |=(1<<RXEN)|(1<<TXEN);//TURN ON TRANSMISSION AND RECEPTION.
	UCSRC |=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);//USE 8 BIT CHARACTER.
	UBRRL |=(BAUD_PRESCALE);//LOAD LOWER 8 BITS OF THE BAUD RATE VALUE.
	UBRRH |=(BAUD_PRESCALE>>8);//LOAD UPPER 8 BIT.
}
unsigned char UART_Rxchar()
{
	while((UCSRA & (1<<RXC))==0);//wait till data is received.
	return(UDR);//RETURN THE BYTE.
}
void UART_Txchar(char ch)
{
	while (!(UCSRA & (1<<UDRE)));//wait for empty transmit buffer.
	UDR=ch;
	
}
void UART_sendstring(char *str)
{
	unsigned char j=0;
	while(str[j]!='\0')     // send string till null.
	{
		UART_Txchar(str[j]);
		j++;
	}
}
void lcd_cmd(char cm)
{
	char a=cm;
	PORTA=a;
	PORTB=(0<<PD0)|(0<<PD1)|(1<<PD2);
	_delay_ms(50);
	PORTB=(0<<PD0)|(0<<PD1)|(0<<PD2);
}
void lcd_data(char dat)
{
	char b=dat;
	PORTA=b;
	PORTB=(1<<PD0)|(0<<PD1)|(1<<PD2);
	_delay_ms(50);
	PORTB=(1<<PD0)|(0<<PD1)|(0<<PD2);
	_delay_ms(50);
}


int main()
{
	DDRB=0xFF;
	DDRA=0xFF;
	UART_init(9600);
	UART_sendstring("LEDON");
	UART_sendstring('\r');
	while(1)
	{

	}
}

this is the code what i have made for transmitting the string is it correct.

Rjchoudhary

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rajat choudhary wrote:

this is the code what i have made for transmitting the string is it correct.

Why not test it and see.

 

You seem to have dropped the uart rx circular buffer and rx interrupt in favor of a simple blocking Uart_Rxchar.

Thta's probably for the best.

Use your new Uart_Rxchar with clawson's code from #18 and you have something that should work (or at least not be far off).