how to read string from USART??

Go To Last Post
24 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have connected a serial device whose baudrate is 115.2K i want to read the complete string sent by the device i used the code given by dean(without interrupt) but it always recive first 2 character of sent string. Here is my code to recive the string.

	while(tmp[i-1]!=0x0A)
	{
		while ((UCSRA & 0x80)==0);
			tmp[i++]=UDR;
	}
	tmp[i-1]=0;
	i=0;
	lcd_wl(tmp,1);
	}

i also tried the intrrupt based method also but when i compiled the code it gave me a warning that

../SMS_Controller.c:135: warning: 'USART_RXC_vect' appears to be a misspelled signal handler

i also want know is there any different method declaring a global variable or i just declare a varible out main and all other function so that it become global and will global variable work in interrupts??

My main problem is this that i want to read a string from USART so if there is any predefined function for this then plz tell mee

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

Don't put the lcd write in the interrupt handler. Too slow. Misses a char while writing to lcd.

Imagecraft compiler user

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

anilsoni85 wrote:
My main problem is this that i want to read a string from USART so if there is any predefined function for this then plz tell mee

scanf() perhaps?

BTW you have two ways to read inbound strings. One is "polling" which you do in your main() loop very like what you show above - sit in a while() loop just waiting for characters to arrive. That's fine as long as the AVR doesn't need to do anything else while waiting.

The alternative is to use interrupt driven recpetion. In this the main() loop can get on and do other stuff. Then each time a character arrives it will vector to the RX interrupt handler which should just pick up the UDR that just arrived and add it into a buffer. The code shouldn't really do much else but if you are looking for "lines" which terminate with 0x0A or 0x0D then the ISR might also watch for this too. If it spots it then don't do the "long work" (output to LCD or whatever) within the ISR but simply set a global, volatile flag to say that a complete string has arrived and meanwhile the code back in the main() loop can be watching out for this flag and act upon it when it's seen to change (but you need to consider what happens if main() then goes on to use the current "line" but meanwhile MORE arriving characters cause the interrupt to fire - you don't want the new line then being built up to over-write the one you are currently working with!

Cliff

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

anilsoni85 wrote:

...
i also tried the intrrupt based method also but when i compiled the code it gave me a warning that

../SMS_Controller.c:135: warning: 'USART_RXC_vect' appears to be a misspelled signal handler


I vote interrupt receiving, it works perfectly for me and does not waste mcu time in waiting loops.

Just define global variables used in interrupt as "volatile", and they work well.

Confirm the name of interrupt handler with manual of target mcu - there are probably two serial ports and name have to contain 0 or 1, or it is simply misspeled anyhow - the names are a bit different regard to mcu choosen as a compile target.
And of course, do not stay too long in interrupt, just save received char into string and leave interrupt handler as soon as possible.
Some care should be taken when reading received string, as interrupt may occur at any time, including a moment of using 16-bit index or so, but simple 8bit index and buffer is mostly sufficient for up to 256byte long sentences on input.

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

Now this is my interrupt based code but it is not working i am not getting anything displayed on the LCD. Plz help me i m running out of time my last date for project submission is near.

void INIT()
{
UCSRB=0x18;
UCSRC =0x86;
UBRRL=0x01;
UBRRH=0x00;
UCSRA|=(1<<RXCIE);
sei();
}

volatile int i=0;
volatile char tmp[100];
volatile int RC=0;


int main()
{
INIT();
lcd_wl("Initialized",0);
while(1)
	{
	if(RC==1)
		{
		RC=0;
		lcd_wl("RC=1",0);
		lcd_wl(tmp,0);
		}
	}
}

ISR(USART_RX_vect)
{
tmp[i]=UDR;
UDR=tmp[i];
if(tmp[i++]=='\r')
	{
	RC=1;
	tmp[i-1]=0;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1) Can you verify that your LCD driver code is working?

2) Can you verify that your terminal app is sending the CR (\r) character?

3) You're not resetting "i" when the CR is recieved, so if a second string is sent you'll get memory corruption eventually and it will not display.

4) Can you verify that the ISR is executing? If you hook up a LED to a pin and get the ISR to turn it on when it fires, does it turn on?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I do not like this part:

ISR(USART_RX_vect)
{
tmp[i]=UDR;
UDR=tmp[i];
if(tmp[i++]=='\r')
   {
   RC=1;
   tmp[i-1]=0;
   }
} 

I guess that You are waiting for \r ,
but it seems to me that You will easily run
out of the memory.
Add if (i What baudrate should be set?

 UBRRL=0x01; 

This may not work in my opinion. Look into the manual and fing baudrate for Your mcu frequency to use, the error must be as low as possible. Also, remember that if using internal oscilator, OSCAL is loaded by 1MHz value automaticaly and You have to adjust it manualy thru eeprom save and program restore for higher frequences... For this simple example, better choose 2400 baud, where timing is not so critical.

At first, verify that "Initialized" is shown on LCD, that will confirm LCD driver workig.

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

My LCD driver code is working fine i have test that code many time. Still i am giving here the code of my LCD driver. I am using atmega8535 with system osc frequecy = 3.686400Mhz and the USART baudrate is 115200 bps (Error=0.0%) for this the value of UBRRL is 1.I have verified this value from datasheet of atmega8535. and the fusebit i have set for my configuration is LOW:0xde and HIGH:0xd9 which is for high frequency crystal oscillator with startup time 258CK + 64ms.

void lcd_cmd(char cmd)
{
CLR_RS;
PORTB=cmd;
_delay_us(50);
SET_EN;
CLR_EN;
}

void lcd_wl(char TxtLine[16], short int SecondLine)
{
int i=0;
if(SecondLine)
	lcd_cmd(0xC0);
else 
	lcd_cmd(0x80);
SET_RS;
_delay_us(50);
	while(TxtLine[i])
	{
	PORTB=TxtLine[i];
	SET_EN;
	CLR_EN;
	_delay_us(50);
	i++;
	}
	for(;i<16;i++)
	{
	PORTB=0x20;
	SET_EN;
	CLR_EN;
	_delay_us(50);
	} 
}

And i am also getting a warning in line:

	 	lcd_wl(tmp,1);

warning: passing argument 1 of 'lcd_wl' discards qualifiers from pointer target type
this may the problem.?????

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

Well i found one loopwhole in the code. actually the problem was ISR was not executing as abcminiuser told i tried to verify the execution of ISR by glowing LED and then i found that i was setting the bit USCRA=(1<<RXCIE) but this flag exists in USCRB. So i cange this code to

USCRB=(1<<RXCIE);

Now my ISR is executing but i am not getting garbage string displayed on LCD. Again i need help. My LCD driver code is posted by me in the post above.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
warning: passing argument 1 of 'lcd_wl' discards qualifiers from pointer target type

Probably not a problem but we can't know until you show us the definition of 'tmp' - hopefully it is a pointer to an array of char's ?

Cliff

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

@clawson tmp is initialzed as volatile char tmp[100];

Here i am submitting my complete code

#define  F_CPU 3686400
#include 
#include 
#include 
#include 
#define  CLR_RS (PORTA &= 0xFD)
#define  SET_RS (PORTA |= 0x02)
#define  CLR_EN (PORTA &= 0xFE)
#define  SET_EN (PORTA |= 0x01)

//LCD data bus is PORTB
//LCD EN is PortA.0
//LCD RS is PortA.1
//Device 1 is at PortA.5
//Device 2 is at PortA.5
//Device 3 is at PortA.6
//Device 4 is at PortA.7

void lcd_cmd(char cmd)
{
CLR_RS;
PORTB=cmd;
_delay_us(50);
SET_EN;
CLR_EN;
}

void lcd_wl(char TxtLine[16], short int SecondLine)
{
int i=0;
if(SecondLine)
	lcd_cmd(0xC0);
else 
	lcd_cmd(0x80);
SET_RS;
_delay_us(50);
	while(TxtLine[i])
	{
	PORTB=TxtLine[i];
	SET_EN;
	CLR_EN;
	_delay_us(50);
	i++;
	}
	for(;i<16;i++)
	{
	PORTB=0x20;
	SET_EN;
	CLR_EN;
	_delay_us(50);
	} 
}

void USART_TX(char *TxStr)
{
int i=0;
while(TxStr[i])
	{
	while ((UCSRA & (1<<UDRE)) == 0);
	UDR=TxStr[i++];
	}
}

void INIT()
{
UCSRB = (1<<RXCIE) | (1<<RXEN) | (1<<TXEN);
UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); 	
UBRRL=0x01;
UBRRH=0x00;
_delay_ms(5);
DDRA=0xFF;
PORTA=0;
DDRB=0xFF;
PORTB=0;
sei();
//LCD INITIALIZATION
lcd_cmd(0x38);
lcd_cmd(0x0E);
lcd_cmd(0x01);
lcd_cmd(0x06);
}

volatile int i=0;
volatile char tmp[100];
volatile int RC=0;

int main()
{
INIT();
lcd_wl("Initialized",0);

while(1)
	{
		if(RC==1)
		{
	 	lcd_wl(tmp,1);
		RC=0;
		}
	}
}

ISR(USART_RX_vect)
{
	tmp[i]=UDR;
	if(tmp[i]=='\r')
	{
		RC=1;	
		tmp[i]=0;
		i=0;
	}
	else
	        i++;
}
Last Edited: Thu. Apr 19, 2007 - 11:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh right well the warning is completely benign then. The compiler's just reminding you that tmp is 'volatile' but what the function is expecting is not. But that's not a problem.

By the way, when you post code I'd suggest you use the [ code ] button in the message editor as it turns the code a pretty green colour which makes it much easier to read.

Cliff

EDIT: Ah aha - I think I caught your post in mid edit before you'd used [code] - MUCH better!

Last Edited: Thu. Apr 19, 2007 - 11:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yaa clawson it was my mistake i was just about to finish that post but you read that post early. Now you see my code and plz tell me where is the problem in my code? why i am getting garbage value displayed.

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

Let's just be clear - you have tested the LCD part of this in isloation from the UART and that is proven to work is it - no garbage from the "Initialized" in the above?

In other words, it's a UART comms issue.

If this is the case, before getting all tied up in exciting stuff like int driven UART-RX have you proven the basics - that the AVR is being clocked right so that the baud rate is right and a simple uart_puts("Hello World") is seen back on your PC terminal. No point moving to the fancy stuff until the basics work.

Cliff

Last Edited: Thu. Apr 19, 2007 - 12:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hey my code is working fine now thnx evryone. My problem is solved. Thnx for replying me. i love avrfreaks.net

Last Edited: Thu. Apr 19, 2007 - 11:37 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In previous version of code, You had serial loopback

tmp[i]=UDR;
UDR=tmp[i]; 

It may work as is, but it probably needs also enabled TX in init.
Then You should be able to see echo characters in terminal, right as You type them. (with local echo turned off in terminal)

Once You got garbage values on LCD, the code seems to work. The same garbage should be sent back if You will do echo in Yout code.

Now, You should check electrical connection and verify rs232 is working. Are You using some of the factory development boards, or your own scheme? Using max232 or something like that, to convert RS232/TTL?

Anyway, I suggest trying with lowest ordinary baudrate first, to verify the code. 2400...

EDIT: written past the message that it works...
Can You tell us what was the case, please?

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

thnx everyone you people are really very helpfull. i am very happy you all helped me a lot. Before 3 week i never touched avr i had never seen avr before i my life but in last 3 weeks with avrfreaks.net and you people i learned a lot and now i can create projects with avr.

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

Please tell us what fixed your problem. You announced success right after Cliff suggested you check your clock source...was that the issue?

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

look as i said earlier the main problem was that i was setting wrong bit to enable USART recive interrupt so this was my problem number 1.

and the another problem was this that the line was not actually terminated by \r but with \n so i changed that and get my project working.

Now i will submit detail about my project like code, schematic and PCB layout on avrfreaks.net soon after my exams.

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

What is the purpose of

anilsoni85 wrote:

tmp[i-1]=0;
	

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

anilson85 sits in a loop waiting for 0x0A (a linefeed) to arrive.
Since it has already stored the 0x0A and incremented the i index, you need to subtract 1 to access that 0x0A.

The 0x0A is replaced with 0x00 (NUL) character. This makes a nice C string.

A more logical structure is to just call the standard library gets() function.

However you need to remember that typists are human. They generally type the 0x0D (carriage-return) character rather than the linefeed character.

David.

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

As per the code the string "monkey/n" will change to "monkey/0"
Or is it "monke/0/n"

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

You should be able to hand trace the loop.

An input of "cakehole\n" will produce "cakehole\0"
An input of "earhole\r\n" will produce "earhole\r\0"

Most people would re-write the loop to avoid having i-1 e.g.

while (c = getchar() && c != '\n' && c != '\r' && i < max) {
    buf{i++] = c;
}
buf[i] = '\0';       // terminate with NUL
 

David.

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

The code is actually this:

if(tmp[i++]=='\r')
   {
   RC=1;
   tmp[i-1]=0;
   } 

That's actually checking for 0x0D ('\r') not 0x0A ('\n'). The problem is the post-increment in the if test:

if(tmp[i++]=='\r')

so that says "do the following block is the "current" character is 0x0D but then increment the current character pointer". Within the conditional block the "i-1" is therefore used to get back to the previous character. To be honest this code would have been MUCH clearer if the author had simply used:

if(tmp[i]=='\r')
   {
   RC=1;
   tmp[i]=0;
   } 

If it is really required that i be incremented as well then:

if(tmp[i]=='\r')
   {
   RC=1;
   tmp[i]=0;
   } 
i++;

but as 'RC' is being set to flag that a "line" has been received and will presumably then be processed with i being set back to 0 at the start of the receive buffer it doesn't seem to matter whether i is incremented or not in this case.