How to avoid this ESP8266 / LCD / AVR UART glitch easily?

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

I have a PCB with an ESP8266 + graphic LCD and an ATtiny816.

 

The init phase on the main file looks like so:

int main(void)
{
	CLOCKSet20MHZ();
	UARTInit();
	SPIInit();
	sei();

	LCDInit();
	LCDScrollInit();
	
	#ifdef DEBUG
		LCDFillColor(BLACK);
		LCDSetCursor(0, 0);
		LCDSetTextSize(1);		
		LCDSetTextColor(BLACK, WHITE);
		LCDGWriteString("Start DEBUG\n");
	#endif
	
	ESPInit();
	
	while(1)
	...
}

ESP8266 is communicating via UART, LCD via SPI.

 

Notice how the DEBUG macro is checked and if enabled "Start DEBUG" is written to the LCD.

The next thing that happens is the ESP8266 is being initiated with a list of commands, what they are is not important right now but the send function used to transfer the data + init are:

void UARTInit(void)
{
	cli();
	PORTB.OUTSET = PIN2_bm; // TxD pin high
	PORTB.DIRSET = PIN2_bm; // TxD pin as output
	USART0.BAUDL = 0xB6;
	USART0.BAUDH = 0x02;
	USART0.CTRLC |= USART_CHSIZE_8BIT_gc;
	USART0.CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
	USART0.CTRLA |= USART_RXCIE_bm; // RxD interrupt enabled	
	
	UARTRXFlag = false;
}

void UARTSendString_P (const char *data)
{	
	#ifdef DEBUG
		LCDSetTextColor(GREEN, BLACK);
	#endif

	for (char c = pgm_read_byte(data) ; c != 0x00 ; c = pgm_read_byte(++data))
	{
		UARTSendChar(c);
		
		#ifdef DEBUG
			LCDGWriteChar(c);
		#endif
	}
}

Please note that the commands that are sent using UARTSendString_P should be displayed in green over black.

Incoming UART is also sent to LCD using the following code:

 

ISR(USART0_RXC_vect)
{
	static uint8_t UARTRXBufferIndex = 0;
	
	UARTRXBuffer[UARTRXBufferIndex] = USART0.RXDATAL;

	#ifdef DEBUG
		LCDSetTextColor(BLUE, BLACK);
		LCDGWriteChar(UARTRXBuffer[UARTRXBufferIndex]);
	#endif

	if (UARTRXBuffer[UARTRXBufferIndex] == '\n')
	{
		UARTRXBufferIndex = 0;
		//UARTRXFlag = true;
	} 
	else if (UARTRXBufferIndex < UART_RX_BUFFER_SIZE)
		UARTRXBufferIndex++;	
	else
		UARTRXBufferIndex = 0;
}

As you can see the incoming data will be printed in blue letters on the LCD.

 

Here is the problem:

  1. When I load the board by powering up the power supply I can see that the text that is written to the 1st line has some spaces in it and the colors of the letters are changing, indicating that the UART RX interrupt was fired a few times.
  2. When the AVR resets by writing new code, the letters appear on the LCD as should (no spaces and no color changes - all green for the 1st line).
  3. Disabling UART RX caused the text to be displayed without the spaces so I suspect the UART RX firing a few times when powering up.

 

What could be the cause of this different behavior on power-ups? I have added a long delay before the ESP8266 init sequence (1 sec) which had no effect on the above behavior.

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

slow_rider wrote:
What could be the cause of this different behavior on power-ups?
The bootloader of the ESP8266 sends some messages on power-up.

Stefan Ernst

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

You haven't shown the LCD functions - are they suitable for calling from an ISR ... ?

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

awneil wrote:

You haven't shown the LCD functions - are they suitable for calling from an ISR ... ?

 

What do you mean by suitable?

Here are the LCD functions - communicating over SPI

 

void LCDSetTextColor(uint16_t text, uint16_t bg)
{
    LCDTextData.textColor = text;
    LCDTextData.textBGColor = bg;
}

void LCDGWriteChar(char c)
{
	if (c == '\n')
	{
		LCDTextData.cursorY += LCDTextData.textSize * 8;
		LCDTextData.cursorX = 0;
	} 
	else if (c != '\r') // If normal char
	{
		LCDGDrawChar(LCDTextData.cursorX, LCDTextData.cursorY, c, LCDTextData.textColor, LCDTextData.textBGColor, LCDTextData.textSize);
		LCDTextData.cursorX += LCDTextData.textSize * 6;
		
		// If end of line, skip to next line
		if (LCDTextData.cursorX >= LCD_W)
		{
			LCDTextData.cursorY += LCDTextData.textSize * 8;
			LCDTextData.cursorX = 0;
		}
	}
	
	// If reached last line, scroll 1 line
	if (LCDTextData.cursorY > LCD_H)
	{
		LCDScrollLine();
		LCDTextData.cursorY = LCD_H;
	}
}

 

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

slow_rider wrote:
What do you mean by suitable?

In general, stuff that takes a long time should not be called from ISR ...

 

 

EDIT

 

slow_rider wrote:
communicating over SPI

Not the kind of thing that's generally good in an ISR!

 

surprise

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...
Last Edited: Fri. Mar 2, 2018 - 10:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:

The bootloader of the ESP8266 sends some messages on power-up.

 

Adding a 1 sec delay before the ESP8266 init function should fixed it, I think. That allows enough time for the ESP to send a few chars before I start init. As far as I can tell _delay_ms() is not clearing global interrupts. However this does not help and I can see the same artifacts. 

 

You can see the top white block that says start DEBUG is showing OK, the problem is with the line under it that should say "ATE0" all in green text.

Right now it says "A T. E 0." with the T in green all the other fonts in blue. The spaces and dots are not something I'm sending or expecting from the ESP8266.

The color changes over because the UART interrupt fires since some data in coming in.

 

An ugly solution could possibly be to enable the RX interrupt only after sending the 1st command?

 

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

awneil wrote:

Not the kind of thing that's generally good in an ISR!

 

surprise

 

Fair enough, I can add some flags and insert that into the while(1) loop in main... Trying it out now.

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

slow_rider wrote:
Adding a 1 sec delay before the ESP8266 init function should fixed it, I think.
No. The UART is already initialized, so you do receive that messages no matter of that delay. If you want to resolve the problem with a delay, then you need it in front of the UART initialization.

Stefan Ernst

Last Edited: Fri. Mar 2, 2018 - 11:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:
If you want to resolve the problem with a delay...

That does not resolve the problem - it simply masks the symptoms.

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

awneil wrote:

 

Not the kind of thing that's generally good in an ISR!

 

OK so here is the problem... how can I move the code out of the ISR?

The init of the ESP8266 and the LCD takes place at the beginning of the code, before it reaches to the while(1) loop inside main. This means that I can not simply set a flag because no one will be there to check it just yet.

 

I can do something else though, I can send the data to the LCD from within the UART RX ISR, but only after a full commands was received - which means to check for '/n'.

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

slow_rider wrote:
how can I move the code out of the ISR?

The standard approach (not specific to AVR)  is to use a so-called Ring Buffer (aka "Circular Buffer" or "FIFO").

 

This has been covered many times both here and elsewhere.

 

 

EDIT

 

eg, https://www.avrfreaks.net/comment...

 

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...
Last Edited: Fri. Mar 2, 2018 - 12:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You seem to be using the uart (and the TFT routines) both from within "normal" code and from ISR's ???

That looks like pretty sloppy progamming and is almost beggin for timing dependant bugs.

 

My advise to you is to make a nice and clean split in your code between different functions.

A certain part of the code shoud be responsible for handling TFT routines, and no other code should be able to interfere with it.

Same for the uart.

C++ and Classes are ideal for this.

 

Different parts of the code should exchange data in a predictable way. Some messaging system or ring buffers.

 

In my code ISR's are usually pretty dumb and small.

For the uart I first prepare some buffer with data and set a counter, (in regular code).

The ISR then just sends a byte,( without even knowing what that byte means) and decrements the byte counter.

Once the (volatile) byte counter reaches zero the interrupt disables itself.

 

If the rest of the code detects that the byte counter has reached zero, then it know's the data is send and the buffer can be reused / filled with new data.

It's a very simple but robust semaphore like behaviour.

 

I think it's a good idea for you to make a cup of tea (coffee) sit back with a pen an paper (No PC) and sketch some idea's for the global behaviour of your program.

After an hour or so take the best of your ideas and put it in a pseudo code framework.

Then again look at it from a distance and decide if you want to go this way.

If so, then fill in the details.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Paulvdh wrote:
If the rest of the code detects that the byte counter has reached zero, then it knows the data is send and the buffer can be reused

Using a Ring buffer means you don't have to wait for the buffer to be empty 

 

EDIT

 

This Ring buffer implementation often gets recommended here:

 

http://www.fourwalledcubicle.com/files/LUFA/Doc/170418/html/group___group___ring_buff.html

 

EDIT 2

 

Download link: http://www.fourwalledcubicle.com/AVRCodeSamples.php

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...
Last Edited: Fri. Mar 2, 2018 - 07:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ring buffers also called circular buffers are also a good way to exchange data between different parts of a program.

 

If you need performance from your small AVR you may want to avoid copying data from one buffer to another too many times.

Passing a pointer around is "cheaper" but can easily lead to more difficult to maintain code.

The main thing here is that any message passing code used (and also the rest of the code) is based on simple, easy to maintain and well thought out algorithms.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

So you suggest implementing 2 buffers? One for UART RX and another for TX?

This means the buffers should be large enough to hold the entire init sequence for the ESP8266 and the replies from the module prior to being able to send them to the LCD.

Is there an alternative that might get away with a much smaller memory usage? This means sending the text to the LCD right after UART send is complete (last char or command) and displaying incoming data on the LCD right after it was reprieved.

 

I can't set a Boolean flag for this as a variable can be tested only when I get into the while(1) loop in the code.

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

slow_rider wrote:
So you suggest implementing 2 buffers? One for UART RX and another for TX?

Not necessarily.

 

Rx is the important one - as you have on control of when charters arrive.

Tx is entirely under your control - so you can manage it however fits your application.

 

This means the buffers should be large enough to hold the entire init sequence

No, not at all.

 

The whole point of Ring buffering is that it allows your code to get on with stuff while receiving/transmitting.

You don't just fill the buffers completely, then send all at once.

 

The buffer forms a queue - so it just needs to big enough to match the peak rate of character arrival to the average rate of processing.

 

 

Is there an alternative

Flow control [1]

 

 This means sending the text to the LCD right after UART send is complete (last char or command) and displaying incoming data on the LCD right after it was reprieved.

Again, you've missed the point - it allows you to keep doing stuff while the send/receive is ongoing.

 

 

EDIT

 

Another option could be to reduce the baud rate ?

 

 

EDIT 2

 

Specifically, in this kind of application, Harware Flow Control, or Software Flow Control

 

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...
Last Edited: Sat. Mar 3, 2018 - 11:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, I think I'm not clear about the part regarding the buffer size.

 

Let's say I have a function called ESP8266_Init() and it send multiple commands over UART to the ESP8266 module. That function is being called right after UART was initialized so it send 8 strings more or less. After each one is sent there is an incoming transfer from the ESP8266 with some reply.

 

Now here is the main issue - I imagine that in order to figure out if the buffer contains new data there is an expression to evaluate in the while(1) loop in main. For example if "head" pointer was changed from previous value (assuming I have a head + tail pointers). If there is new data it is sent to the LCD, if not the program continues normal execution.

 

The 1st time the condition can be evaluated is after ESP8266_Init() was already executed and sent a bunch of data and also a bunch of data was received.

 

How can I fire the init commands at a time where the buffer is being evaluated not inside an ISR but rather in the while(1) loop? 

 

Hope that makes sense. 

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

slow_rider wrote:
Let's say I have a function called ESP8266_Init() and it send multiple commands over UART to the ESP8266 module.

But you should never send multiple AT commands without waiting for - and paying attention to - the response to each!

 

surprise

 

 

I imagine that in order to figure out if the buffer contains new data there is an expression

Did you look at the links I posted earlier? It has a RingBuffer_IsEmpty() function: http://www.fourwalledcubicle.com...

 

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

OK... I see the code will have to be more complicated than I originally thought. :)

The init part must be blocking though?

Last Edited: Sat. Mar 3, 2018 - 07:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

slow_rider wrote:
The init part must be blocking though?

Without looking too deply into your code, it's bad habit to write blocking code.

 

Some time ago I wrote a network stack for an RS485 based network.

You could declare a packet (with it's own buffer), fill the packet (data addres etc) and send it on it's way.

It also had a callback funcion.

If there was a response from a remote node it could call a callback function.

The callback function could format a new packet, set the callback pointer to a new function and send the new packet on it's way.

Once the second response arrived the 2nd callback function was executed, and it could do whatever was needed with the new packet.

 

(The packet class has built in address checking, resends timeouts, CRC, and they are all handled for each packet before the callback was called, so the callback funcion only had to do something with the actual data).  "Simple" callback functons did nothing more than copy a sting into the packet, change the callback pointer and send the new packet on it's way. 3 lines of code.

 

It was quite a hassle to write and debug the code, but after I got it going It all works pretty neatly.

The implementation of the callbacks automatically enforces a natural flow into your data stream without writing extra loops of if() statements. Functions just get called when they need to be called.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Sat. Mar 3, 2018 - 08:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

slow_rider wrote:
The init part must be blocking though?

I don't see why that should necessarily be the case ?

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

So do you use a timer interrupt to check on the buffer state?

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

The init part must be blocking though?

Yes and no.

 

It is pseudo - blocking, in that you can not just send several init commands all at once.

You have to wait for an appropriate response following each command you send.

 

But it need not be truly blocking in the sense that the micro can't do anything else but sit in a wait loop awaiting the response.

Depending upon how you code it the micro could be doing other things while awaiting the response.

When it gets a response, verifies that the init is preceding correctly, then it sends the next init instruction.    

 

Note, that if the init is taking place at the start of the program, and there isn't anything else to do between sending the init instruction sequence, then the program might well just sit there awaiting the next response.

 

JC