Go Down

Topic: Getting Serial, LCD and SoftwareSerial to work together (Read 4188 times) previous topic - next topic

EvilPharmacist

Jul 24, 2012, 12:56 am Last Edit: Aug 03, 2012, 02:39 pm by EvilPharmacist Reason: 1
I use an Arduino Uno R2 with a Flytron GPS and a Sparkfun Nokia LCD with the TinyGPS library (version 12) and ColorLCDShield library.
The GPS is attached to pins 2 and 3 with SoftwareSerial. The LCD shield is attached as such: Vbat to 5v of the Arduino, 3.3V to the 3.3V pin on the Arduino, GND to gnd obviously. Then reset to pin 8, DIO to pin 11, SCK to pin 13 and CS to pin 9.
I also use an external 12V power supply because I noticed the GPS wouldn't get a fix when I just powered everything of a USB port.

The idea is to have GPS data show on the lcd, but for now I just output it to Serial.
I can get the GPS to work, or the LCD shield, but not both together.

This is the code, it's a combination of sample code found for both the gps and lcd shield:

Code: [Select]
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <ColorLCDShield.h>


TinyGPS gps;
LCDShield lcd;

#define RXPIN 2
#define TXPIN 3
SoftwareSerial nss(RXPIN, TXPIN);

long GPS_lat, GPS_lon = 0;
unsigned long GPS_speed, GPS_course, GPS_number_of_satellites = 0;
unsigned long GPS_chars;
unsigned short GPS_sentences, GPS_failed_checksum;
unsigned long previousTime, currentTime;
int GPS_year = 0 ;
byte GPS_month, GPS_day, GPS_hour, GPS_minute, GPS_second, GPS_hundredths = 0 ;
unsigned long GPS_fix_age = 0;

byte LCD_cont = 50;  // Good center value for contrast

void setup()
{
 previousTime = currentTime = 0;

 nss.begin(9600);
 Serial.begin(9600);

 lcd.init(EPSON);  // Initialize the LCD, try using PHILLIPS if it's not working
 lcd.contrast(LCD_cont);  // Initialize contrast
 lcd.clear(WHITE);  // Set background to white
 lcd.setPixel(GREEN, 66, 66);
 lcd.setCircle(66, 66, 30, GREEN);
 lcd.setCircle(66, 66, 60, GREEN);
 lcd.setCircle(66, 66, 1, GREEN);
 lcd.setStr("test", 20, 20, WHITE, BLACK);
}


void loop()
{
 currentTime = millis();
 while (nss.available())
 {
   //Serial.println("ding dong");
   int c = nss.read();
   if (gps.encode(c))
   {
     Serial.println("GPS data ontvangen!");
     // retrieves +/- lat/long in 100.000ths of a degree
     gps.get_position(&GPS_lat, &GPS_lon, &GPS_fix_age);

     gps.crack_datetime(&GPS_year, &GPS_month, &GPS_day, &GPS_hour, &GPS_minute, &GPS_second, &GPS_hundredths, &GPS_fix_age);
     GPS_hour = GPS_hour+2;
     if (GPS_hour >= 24) GPS_hour = GPS_hour -24;
     // returns speed in 100ths of a knot
     GPS_speed = gps.speed();

     // course in 100ths of a degree
     GPS_course = gps.course();

     GPS_number_of_satellites = gps.satellites();

   }
 }
 if (currentTime - previousTime >= 1000) {
   if (GPS_number_of_satellites > 0) {
     Serial.print (GPS_hour);
     Serial.print (":");
     Serial.print (GPS_minute);
     Serial.print (":");
     Serial.print (GPS_second);
     Serial.print (" - ");
     Serial.print ("Latitude: ");
     Serial.print (GPS_lat);
     Serial.print (" Longitude: ");
     Serial.print (GPS_lon);
     Serial.print (" Fix age: ");
     Serial.print (GPS_fix_age);
     Serial.print (" Satellieten: ");
     Serial.println (GPS_number_of_satellites);
   }
   else {
     Serial.println ("No GPS fix");
   }
     previousTime = currentTime;
 }
}


Symptom: the LCD keeps flashing on and off, drawing only a small part of the circle.

So I started commenting parts. If I comment the lcd.SetStr:
Code: [Select]
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <ColorLCDShield.h>


TinyGPS gps;
LCDShield lcd;

#define RXPIN 2
#define TXPIN 3
SoftwareSerial nss(RXPIN, TXPIN);

long GPS_lat, GPS_lon = 0;
unsigned long GPS_speed, GPS_course, GPS_number_of_satellites = 0;
unsigned long GPS_chars;
unsigned short GPS_sentences, GPS_failed_checksum;
unsigned long previousTime, currentTime;
int GPS_year = 0 ;
byte GPS_month, GPS_day, GPS_hour, GPS_minute, GPS_second, GPS_hundredths = 0 ;
unsigned long GPS_fix_age = 0;

byte LCD_cont = 50;  // Good center value for contrast

void setup()
{
 previousTime = currentTime = 0;

 nss.begin(9600);
 Serial.begin(9600);

 lcd.init(EPSON);  // Initialize the LCD, try using PHILLIPS if it's not working
 lcd.contrast(LCD_cont);  // Initialize contrast
 lcd.clear(WHITE);  // Set background to white
 lcd.setPixel(GREEN, 66, 66);
 lcd.setCircle(66, 66, 30, GREEN);
 lcd.setCircle(66, 66, 60, GREEN);
 lcd.setCircle(66, 66, 1, GREEN);
//  lcd.setStr("test", 20, 20, WHITE, BLACK); <<<<-------------------------------------------------
}


void loop()
{
 currentTime = millis();
 while (nss.available())
 {
   //Serial.println("ding dong");
   int c = nss.read();
   if (gps.encode(c))
   {
     Serial.println("GPS data ontvangen!");
     // retrieves +/- lat/long in 100.000ths of a degree
     gps.get_position(&GPS_lat, &GPS_lon, &GPS_fix_age);

     gps.crack_datetime(&GPS_year, &GPS_month, &GPS_day, &GPS_hour, &GPS_minute, &GPS_second, &GPS_hundredths, &GPS_fix_age);
     GPS_hour = GPS_hour+2;
     if (GPS_hour >= 24) GPS_hour = GPS_hour -24;
     // returns speed in 100ths of a knot
     GPS_speed = gps.speed();

     // course in 100ths of a degree
     GPS_course = gps.course();

     GPS_number_of_satellites = gps.satellites();

   }
 }
 if (currentTime - previousTime >= 1000) {
   if (GPS_number_of_satellites > 0) {
     Serial.print (GPS_hour);
     Serial.print (":");
     Serial.print (GPS_minute);
     Serial.print (":");
     Serial.print (GPS_second);
     Serial.print (" - ");
     Serial.print ("Latitude: ");
     Serial.print (GPS_lat);
     Serial.print (" Longitude: ");
     Serial.print (GPS_lon);
     Serial.print (" Fix age: ");
     Serial.print (GPS_fix_age);
     Serial.print (" Satellieten: ");
     Serial.println (GPS_number_of_satellites);
   }
   else {
     Serial.println ("No GPS fix");
   }
   previousTime = currentTime;
 }
}


Symptom: Everything works, with GPS info over Serial, but there's obviously no text on the lcd.

With only the gps code commented:

Code: [Select]
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <ColorLCDShield.h>


TinyGPS gps;
LCDShield lcd;

#define RXPIN 2
#define TXPIN 3
SoftwareSerial nss(RXPIN, TXPIN);

long GPS_lat, GPS_lon = 0;
unsigned long GPS_speed, GPS_course, GPS_number_of_satellites = 0;
unsigned long GPS_chars;
unsigned short GPS_sentences, GPS_failed_checksum;
unsigned long previousTime, currentTime;
int GPS_year = 0 ;
byte GPS_month, GPS_day, GPS_hour, GPS_minute, GPS_second, GPS_hundredths = 0 ;
unsigned long GPS_fix_age = 0;

byte LCD_cont = 50;  // Good center value for contrast

void setup()
{
 previousTime = currentTime = 0;

 nss.begin(9600);
 Serial.begin(9600);

 lcd.init(EPSON);  // Initialize the LCD, try using PHILLIPS if it's not working
 lcd.contrast(LCD_cont);  // Initialize contrast
 lcd.clear(WHITE);  // Set background to white
 lcd.setPixel(GREEN, 66, 66);
 lcd.setCircle(66, 66, 30, GREEN);
 lcd.setCircle(66, 66, 60, GREEN);
 lcd.setCircle(66, 66, 1, GREEN);
 lcd.setStr("test", 20, 20, WHITE, BLACK);
}


void loop()
{
 currentTime = millis();
//  while (nss.available())
//  {
//    //Serial.println("ding dong");
//    int c = nss.read();
//    if (gps.encode(c))
//    {
//      Serial.println("GPS data ontvangen!");
//      // retrieves +/- lat/long in 100.000ths of a degree
//      gps.get_position(&GPS_lat, &GPS_lon, &GPS_fix_age);
//
//      gps.crack_datetime(&GPS_year, &GPS_month, &GPS_day, &GPS_hour, &GPS_minute, &GPS_second, &GPS_hundredths, &GPS_fix_age);
//      GPS_hour = GPS_hour+2;
//      if (GPS_hour >= 24) GPS_hour = GPS_hour -24;
//      // returns speed in 100ths of a knot
//      GPS_speed = gps.speed();
//
//      // course in 100ths of a degree
//      GPS_course = gps.course();
//
//      GPS_number_of_satellites = gps.satellites();
//
//    }
//  }
 if (currentTime - previousTime >= 1000) {
   if (GPS_number_of_satellites > 0) {
     Serial.print (GPS_hour);
     Serial.print (":");
     Serial.print (GPS_minute);
     Serial.print (":");
     Serial.print (GPS_second);
     Serial.print (" - ");
     Serial.print ("Latitude: ");
     Serial.print (GPS_lat);
     Serial.print (" Longitude: ");
     Serial.print (GPS_lon);
     Serial.print (" Fix age: ");
     Serial.print (GPS_fix_age);
     Serial.print (" Satellieten: ");
     Serial.println (GPS_number_of_satellites);
   }
   else {
     Serial.println ("No GPS fix");
   }
   previousTime = currentTime;
 }
}


Symptom: The screen just hangs, blue color, no serial info is sent (Tx on the Arduino doesn't blink)

It seems that the lcd.SetStr and setChar functions are the key, as everything starts working as soon as I comment the lcd.setStr line.
If I change
Code: [Select]
lcd.setStr("test", 20, 20, WHITE, BLACK); to
Code: [Select]
lcd.setChar('t', 20, 20, WHITE, BLACK); the circles draw partially, the 't' appears on the screen, but Serial is outputting crap.

Nick Gammon

Any reason for not using hardware serial? (Pins 0 and 1)
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

EvilPharmacist

FYI, these are the setStr and setChar functions from the ColorLCDShield library:
Code: [Select]
void LCDShield::setChar(char c, int x, int y, int fColor, int bColor)
{
y = (COL_HEIGHT - 1) - y; // make display "right" side up
x = (ROW_LENGTH - 2) - x;

int             i,j;
unsigned int    nCols;
unsigned int    nRows;
unsigned int    nBytes;
unsigned char   PixelRow;
unsigned char   Mask;
unsigned int    Word0;
unsigned int    Word1;
unsigned char   *pFont;
unsigned char   *pChar;

// get pointer to the beginning of the selected font table
pFont = (unsigned char *)FONT8x16;
// get the nColumns, nRows and nBytes
nCols = *pFont;
nRows = *(pFont + 1);
nBytes = *(pFont + 2);
// get pointer to the last byte of the desired character
pChar = pFont + (nBytes * (c - 0x1F)) + nBytes - 1;

if (driver) // if it's an epson
{
// Row address set (command 0x2B)
LCDCommand(PASET);
LCDData(x);
LCDData(x + nRows - 1);
// Column address set (command 0x2A)
LCDCommand(CASET);
LCDData(y);
LCDData(y + nCols - 1);

// WRITE MEMORY
LCDCommand(RAMWR);
// loop on each row, working backwards from the bottom to the top
for (i = nRows - 1; i >= 0; i--) {
// copy pixel row from font table and then decrement row
PixelRow = *pChar++;
// loop on each pixel in the row (left to right)
// Note: we do two pixels each loop
Mask = 0x80;
for (j = 0; j < nCols; j += 2)
{
// if pixel bit set, use foreground color; else use the background color
// now get the pixel color for two successive pixels
if ((PixelRow & Mask) == 0)
Word0 = bColor;
else
Word0 = fColor;
Mask = Mask >> 1;
if ((PixelRow & Mask) == 0)
Word1 = bColor;
else
Word1 = fColor;
Mask = Mask >> 1;
// use this information to output three data bytes
LCDData((Word0 >> 4) & 0xFF);
LCDData(((Word0 & 0xF) << 4) | ((Word1 >> 8) & 0xF));
LCDData(Word1 & 0xFF);
}
}
}
else
{
// Row address set (command 0x2B)
LCDCommand(PASETP);
LCDData(x);
LCDData(x + nRows - 1);
// Column address set (command 0x2A)
LCDCommand(CASETP);
LCDData(y);
LCDData(y + nCols - 1);

// WRITE MEMORY
LCDCommand(RAMWRP);
// loop on each row, working backwards from the bottom to the top
pChar+=nBytes-1;  // stick pChar at the end of the row - gonna reverse print on phillips
for (i = nRows - 1; i >= 0; i--) {
// copy pixel row from font table and then decrement row
PixelRow = *pChar--;
// loop on each pixel in the row (left to right)
// Note: we do two pixels each loop
Mask = 0x01;  // <- opposite of epson
for (j = 0; j < nCols; j += 2)
{
// if pixel bit set, use foreground color; else use the background color
// now get the pixel color for two successive pixels
if ((PixelRow & Mask) == 0)
Word0 = bColor;
else
Word0 = fColor;
Mask = Mask << 1; // <- opposite of epson
if ((PixelRow & Mask) == 0)
Word1 = bColor;
else
Word1 = fColor;
Mask = Mask << 1; // <- opposite of epson
// use this information to output three data bytes
LCDData((Word0 >> 4) & 0xFF);
LCDData(((Word0 & 0xF) << 4) | ((Word1 >> 8) & 0xF));
LCDData(Word1 & 0xFF);
}
}
}
}

void LCDShield::setStr(char *pString, int x, int y, int fColor, int bColor)
{
x = x + 16;
y = y + 8;
// loop until null-terminator is seen
while (*pString != 0x00) {
// draw the character
setChar(*pString++, x, y, fColor, bColor);
// advance the y position
y = y + 8;
// bail out if y exceeds 131
if (y > 131) break;
}
}

EvilPharmacist


Any reason for not using hardware serial? (Pins 0 and 1)

I've only got one hardware serial because it's a Uno, and I want to use it to communicate with my pc for debugging.

Nick Gammon

Do you have a second Uno (or similar)?

http://www.gammon.com.au/forum/?id=11329

You can debug via I2C or SPI if you do.

SoftwareSerial is not going to be as reliable as hardware serial.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

EvilPharmacist


Do you have a second Uno (or similar)?

http://www.gammon.com.au/forum/?id=11329

You can debug via I2C or SPI if you do.

SoftwareSerial is not going to be as reliable as hardware serial.

Yeah, I have another Uno.
I don't think I can use SPI as it's already used by the lcd shield. I could try I2C... but I feel I'm circumventing the problem then  :)
Is there something I could change to the lcd.setChar code to get it working together nicely?

Nick Gammon

Circumventing the problem that software-based serial is less reliable than hardware-based serial? That's a problem I would want to circumvent, personally.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

EvilPharmacist

Alright, thanks for the advice. I will try using the hardware serial only and if that works, use the lcd for debugging.

Do you have any background on why SoftwareSerial is unreliable?

Nick Gammon

Well ... You are tying up the processor in a tight loop waiting for bits to arrive at precise intervals. Things can go wrong with that. And you have dedicated hardware designed to do exactly that. By experts.

Let me ask you this: "why buy a dog, and bark yourself?"
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

EvilPharmacist

And here I was thinking SoftwareSerial used interrupts... I've still a lot to learn.

PaulS

Quote
And here I was thinking SoftwareSerial used interrupts... I've still a lot to learn.

It does - to know when to start listening (in the tight loop) for the bits that make up the byte.

Nick Gammon

It does use interrupts. I never said it didn't. But once the initial interrupt arrives it sits in a loop. The interrupt tells it the first bit arrives, then this:

Code: [Select]

   // Read each of the 8 bits
   for (uint8_t i=0x1; i; i <<= 1)
   {
     tunedDelay(_rx_delay_intrabit);
     DebugPulse(_DEBUG_PIN2, 1);
     uint8_t noti = ~i;
     if (rx_pin_read())
       d |= i;
     else // else clause added to ensure function timing is ~balanced
       d &= noti;
   }


No interrupts there, a timed loop.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

I should also point out that whilst interrupts are great in their own way, they can have an initial latency which depends on whether another interrupt (eg. a timer) is in progress, and other things. The hardware doesn't suffer from that.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PaulS

Quote
The hardware doesn't suffer from that.

OK. I'll bite. Since HardwareSerial uses interrupts to send and receive data, why is it not impacted by other interrupts happening?

Go Up