Pages: [1] 2 3 4   Go Down
Author Topic: Getting Serial, LCD and SoftwareSerial to work together  (Read 3634 times)
0 Members and 1 Guest are viewing this topic.
Belgium
Offline Offline
Newbie
*
Karma: 0
Posts: 20
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
#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:
#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:
lcd.setStr("test", 20, 20, WHITE, BLACK);
to
Code:
lcd.setChar('t', 20, 20, WHITE, BLACK);
the circles draw partially, the 't' appears on the screen, but Serial is outputting crap.

* gps_locator.ino (2.41 KB - downloaded 9 times.)
« Last Edit: August 03, 2012, 07:39:02 am by EvilPharmacist » Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19036
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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


Belgium
Offline Offline
Newbie
*
Karma: 0
Posts: 20
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

FYI, these are the setStr and setChar functions from the ColorLCDShield library:
Code:
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;
}
}
Logged

Belgium
Offline Offline
Newbie
*
Karma: 0
Posts: 20
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19036
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged


Belgium
Offline Offline
Newbie
*
Karma: 0
Posts: 20
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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  smiley
Is there something I could change to the lcd.setChar code to get it working together nicely?
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19036
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Circumventing the problem that software-based serial is less reliable than hardware-based serial? That's a problem I would want to circumvent, personally.
Logged


Belgium
Offline Offline
Newbie
*
Karma: 0
Posts: 20
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19036
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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?"
Logged


Belgium
Offline Offline
Newbie
*
Karma: 0
Posts: 20
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 631
Posts: 50115
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19036
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
   // 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.
Logged


Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19036
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ninja'd by PaulS. <grin>

<evilgrin>
Logged


Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19036
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged


Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 631
Posts: 50115
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Pages: [1] 2 3 4   Go Up
Jump to: