Cannot consistently read UART data

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

Hi

 

I am interfacing with a SIM800L module. I can get the device to work and all however I would like to implement error handling based on the GSM module responses. I have setup a breadboard with an ATmega328P with a LCD and the GSM module. The ATmega is running at 8MHz and I am using ATmel Studio. My compiler optimization is on -01. 

 

What I want to achieve, for now, is to read the response from the GSM unit after i send the AT command. it responds back OK (confirmed with my logic analyzer)  however I cannot for some reason load this "OK" into a char buffer on the first cycle. I will explain abit more now but here is my code:

 

  



#define F_CPU 8000000UL
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdlib.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <string.h>
#include "CALCD2_DRIVER.h"



#define PIN_SCK PINB5
#define PIN_MOSI PINB3
#define PIN_CS PINB2


#define GSM_PWR PIND3
#define GSM_WAKEUP PIND2
#define GSM_RI PINB6



define BAUD 9600
#define BAUDRATE (((F_CPU)/(16UL*BAUD))-1)



void setup_pins(void){

	DDRB |= (1<<DEBUG_LED_B);
	DDRD |= (1<<GSM_PWR);
	PORTD |= (1<<GSM_PWR);
	DDRD |= (1<<GSM_WAKEUP);
	DDRB &= ~(1<<GSM_RI);
	DDRD |= (1<<GSM_MOSFET_D);
	PORTD &= ~(1<<GSM_MOSFET_D);
}

void usart_initialize(void){
	
	UBRR0H = (BAUDRATE>>8);
	UBRR0L = (BAUDRATE);
	UCSR0B|= (1<<TXEN0)|(1<<RXEN0);
	UCSR0C|=  (1<<UCSZ01) | (1<<UCSZ00);
	
}

unsigned char uart_receive (void)
{
	// function to receive data
	while (!( UCSR0A & (1<<RXC0)));
	return UDR0;
}

void uart_transmit (unsigned char data)
{
	// function to send data
	while (!( UCSR0A & (1<<UDRE0)));              
	UDR0 = data;                                 
}

void spiSendByte (char databyte)
{
	
	// Copy data into the SPI data register
	PORTB &= ~(1<<PIN_CS);
	SPDR = databyte;
	// Wait until transfer is complete
	while (!(SPSR & (1 << SPIF)));
	PORTB |= (1<<PIN_CS);

}

void lcd_setup(void){
	//function sets up SPI
	//setup SPI pins as output
	DDRB |= (1 << PIN_SCK) | (1 << PIN_MOSI) | (1 << PIN_CS);
	// SPI Enable, Master mode
	SPCR |= (1 << SPE) | (1 << MSTR) | (1<<SPR1) | (1<<SPR0);
	lcd_clear();
	_delay_ms(2000);
	lcd_color(0,128,255);
	lcd_contrast(30);
}


void lcd_clear(void){
	PORTB &= ~(1<<PIN_CS);
	_delay_ms(1);
	spiSendByte('|');
	spiSendByte('-');
	PORTB |= (1<<PIN_CS);
}



void lcd_write_text(char array[]){
	int j = 0;
	PORTB &= ~(1<<PIN_CS);
	
	while (array[j] != '\0'){
		spiSendByte(array[j]);
		j++;
	}
	
	PORTB |= (1<<PIN_CS);
}



void gsm_on(void){

	
	_delay_ms(1000);
	
	if (PINB & (1<<GSM_RI)){
		//do nothing already on
	}
	else{
		//put gsm on
		PORTD &= ~(1<<GSM_PWR);
		_delay_ms(2000);
		PORTD |= (1<<GSM_PWR);
	}
	_delay_ms(15000);

}


void gsm_set_echo_off(void){
	
	uart_transmit_string("ATE0\r");
	_delay_ms(200);
	
	
}





int main(void)
{
	
	
	lcd_setup();
	setup_pins();
	usart_initialize();
	gsm_set_echo_off();

    while (1) 
    {
		char buffer[10];
		
		uart_transmit_string("AT\r");
				
		for (uint8_t i = 0 ;i<5;i++)            ////Only works when i use 5
		{
				buffer[i] = uart_receive();
		}
	

		if(strstr(buffer,"OK"))
		{
			lcd_write_text("OK");
		}
		else
		{
			lcd_write_text("ERROR!");
		}

		_delay_ms(1000);
		lcd_clear();

		
    }
}

 

So when the main loop starts the UART successfully sends the AT command. It then goes into the for loop where the uart_receive() function runs that returns the UDR0 register content and stores it into the first element of the char buffer.  After looping 5 times the strstr() function is used to check for the string "OK". If it finds it the lcd prints OK else it prints ERROR!. Always on the first main loop cycle i get an error. After that it works and prints OK. I have tried printing the String when i get an error and funnily enough it prints "OK" so not too sure why its not working. 

 

 

Any help would be appreciated thanks 

 

 

This topic has a solution.
Last Edited: Sat. Aug 1, 2020 - 08:45 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

calvingloster wrote:
The ATmega is running at 8MHz

What ATmega would that be? M8, M328p, or ???

 

calvingloster wrote:

void usart_initialize(void){

	UBRR0H = (BAUDRATE>>8);
	UBRR0L = (BAUDRATE);
	UCSR0B|= (1<<TXEN0)|(1<<RXEN0);
	UCSR0C|=  (1<<UCSZ01) | (1<<UCSZ00);

}

Don't use H/L register, C does not guarantee order, just use UBRR0 = BAUDRATE;  The compiler knows how to talk to 16 bit registers!

Also when setting up reg's, use = not != , best not to use RMW when initializing peripheral registers.

 

calvingloster wrote:

unsigned char uart_receive (void)
{
	// function to receive data
	while (!( UCSR0A & (1<<RXC0)));
	return UDR0;
}

Best to use interrupt driven USART RX to fill buffer, then poll buffer for any contents in main and process as received.

You may want to check out this Arduino Sim800 lib for ways to improve your code, or just use this lib.

Website: [Sim800L library](https://github.com/VittorioEspos...)

 

Jim

Edit: add one more....

Edit2: forgot lib link

 

You should turn off modem echo to prevent your USART rxd from overflowing when you send commands to the modem.

ATE0 is the command to turn off echo

 

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

Last Edited: Thu. Jul 30, 2020 - 08:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are assuming the uart rx buffer is empty when you sent a command. You most likely have bytes in rx already from handshaking or other.

 

 

1. enable rx (starts with a clear hardware buffer)

2. send command

3. wait for cr, wait for lf (with maybe a timeout in case no response), if either are not correct- error

4. loop getting data into a buffer until you see a cr or buffer_space-1 is used up, 0 terminate buffer

5. disable rx (no need to rx anymore)

5. process buffer

 

repeat

 

before every command, your rx hardware buffer is cleared by enabling the rx (could also leave in on and clear by reading)

the responses are always <cr><lf>response<cr><lf>, so check/discard the first cr+lf, then get the data into a buffer until you reach cr

 

Something like-

 

        char buffer[10]; //is uninitialized
        
        uart_transmit_string("AT\r");

        uart_rx_on(); //start with a clear hardware buffer

        uint8_t idx = 0;

        if( ( '\r' == uart_receive() ) && ( '\n' == uart_receive() ){ //<cr><lf>

            for( ; idx < sizeof(buffer)-1;  ){

                char c = uart_receive();

                if( c == '\r' ) break; //done (and buffer already 0 terminated)

                buffer[idx++] = c;

                buffer[idx] = 0; //just 0 terminate as we go

            }

        }

        uart_rx_off();

        if( idx == 0 ) lcd_write_text( "Unknown Error" ); //did not see <cr><lf>, or no response text

        else lcd_write_text( buffer ); //print response

Last Edited: Thu. Jul 30, 2020 - 10:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Look at how the Arduino manages the usart. You would want to do the same. Then, at least, have a look at the sim800 library to see how they do it. Otherwise you're going to have a rough ride as there's a few tricks implementing a AT interface.

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

As Kartman suggests, all of this has been done before countless times over many decades.

 

A couple more examples to look at here: https://www.avrfreaks.net/commen... - and take a look at the rest of that thread.

 

Also note that there's an AVR App Note on AT Command handling ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Just to be clear - counting receive characters is not a good way to solve the problem. The module can give various responses - all different lengths. About the only thing you can count on is the line terminated by a CR or LF.  Curtvm illustrates this in #3.

There's also timeouts and un-requested messages that make the solution more difficult.

 

 

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

ki0bk wrote:
What ATmega would that be? M8, M328p, or ???
  This would be the ATmega328p sorry I should have explicitly typed this

 

ki0bk wrote:
Don't use H/L register, C does not guarantee order
Thanks for the tip here, I will start doing this i have made a note.

 

ki0bk wrote:
Best to use interrupt driven USART RX
Thanks i will implement this after reading the library link you have pointed me towards. 

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

curtvm wrote:
before every command, your rx hardware buffer is cleared by enabling the rx
Never thought of this thanks.

 

curtvm wrote:
Something like-
Thanks for the code it makes sense now waiting for the first \r and \n and doing nothing with those two characters then going into receiving the response code and then terminating once the second set of \r and \n characters have been received.  

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

Thanks Kartman and Awneil, i have some reading to do on already implemented libraries.

 

 

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

Kartman wrote:
un-requested messages

aka "unsolicited responses"

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That was the word I was looking for. Thankyou for soliciting your opinion :)

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

you mean my unsolicited opinion?

 

cheeky

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:
Best to use interrupt driven USART RX to fill buffer
I have a question with regards to using an interrupt driven receiver.

 

If the next step in my program is based on the result of what is received and the process is serial in nature and not parallel how can i deal with this? i am guessing i would set a global flag in the ISR that is read by the next function to see if we have received the data, if not just wait in a while loop. In this case the interrupt driven RX is not very useful (other cases it might be). Is my train of thought here incorrect?

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

ki0bk wrote:
Best to use interrupt driven USART RX to fill buffer
Just to give you guys feedback, setting up the interrupt driven RX USART solved my problem thanks. I will continue to read and learn from the libraries you guys have linked thanks.