Translate

Thursday, March 30, 2017

A LCD serial terminal with Teensy

This project uses a serial 40x4 LCD display with a Teensy board to realize a compact serial terminal receiver to be connected to many systems with serial output such as our microcontroller systems and GPS receivers.
The terminal connected to a GPS receiver.

The Teensy board
I used a Teensy 3.1 because it is very powerful, its most important features are:
·        Processor: MK20DX256 32 bit ARM Cortex-M4 72 MHz;
·        Flash Memory: 256k, RAM Memory: 64k, EEPROM: 1k;
·        I/O: 34, TTL 3.3V, 5V tolerant;
·        Analog In (16 bit, 13 effective): 21;
·        UART,I2C,SPI: 3,2,1;

and finally, it is very inexpensive compared to its features. Now it has been replaced by version 3.2, 100% compatible. 
The display
I have adopted a large alphanumeric display 40 columns by 4 rows, a DMC40457 of Optrex Corporation. As can be seen from the following table, the connections of this type of display are very different from the more common 16-column for two rows. Furthermore there is a second enable signal, being necessary two HD44780 chip. The connector has 16 pins, arranged in 2x8.
pin
function
pin
function
1
D7  Data Bus Line
2
D6  Data Bus Line
3
D5  Data Bus Line
4
D4  Data Bus Line
5
D3  Data Bus Line
6
D2  Data Bus Line
7
D1  Data Bus Line
8
D0  Data Bus Line
9
E1 - Enable Signal #1
10
R/W - Read/Write
11
RS Register Select
12
VEE - Power Supply
13
VSS - Power Supply (GND)
14
VCC - Power Supply
15
E2 - Enable Signal #2
16
Not connected
There are also displays with LED backlight, in which case the connector has 18 pin. The backlight will significantly increase the consumption of the display.
To drive the LCD display dual chip, I used the LiquidCrystalFast library by John Raines. It is not, at the time, capable of driving the LCD display 40x4 with a faster 8-bit data bus, then I used the 4-bit data bus (D4..D7) plus 4 control bits (E1, E2, R / W , RS), eight bits configured as outputs. The fields of the table with a yellow background representing the used pins, then 8 bits + 3 for power supply.
The configuration of the display function has the following syntax:

LiquidCrystalFast lcd(RS, RW, E1, E2, D4, D5, D6, D7);

In our case it becomes:

LiquidCrystalFast lcd(21, 4, 2, 6, 17, 18, 19, 20);

If you don’t think of working with long lines you can use smaller displays as a 20x4 with only a single HD44780 chip. In this case you have to make some changes on the LCD wiring and program. You can now use the library LiquidCrystal of the Arduino IDE.

Baud rate selector
To select the baud rate I used three of the four dip switches for a total of eight communication speed. The fourth can be used to select other communication parameters. The following table shows the positions of the first three dip switches and the corresponding baud rates.  Usually the dip switches with the levers in the low position are closed (ON) which corresponds to logic zero.

The power supply
As already said, the Teensy provides the power supply from USB, but I preferred to have more independence from grid power using rechargeable batteries. The simplest and cheapest solution was to use a commercial device which is now very widespread, usually called 'mobile power bank'. It is used to give energy to our smartphone or tablet, when they used batteries and are not close to grid socket. These devices incorporate, in a small volume, one or more rechargeable batteries Lithium Polymer, a battery charger (5V to 4.2V) and a switching power supply step-up to produce the 5V from 3.7V. The internal battery charger uses an external power supply common with micro USB connector, now standard for mobile phones and tablets, while the output 5V are available on a standard USB type A connector
With power from Vin, as in our case, you must include a switch that must be OFF when we insert the USB plug, otherwise you may be trouble to the power supplies or you can burn the small fuse on the Teensy board. On the same tour you can be interrupted with a cutter the thin runway under the card (for more information see the www.pjrc.com/teensy/ site), but no longer will be fed the other components of the system, such as the display, when you using the USB interface.

The wiring diagram

This scheme includes some components that are not used in this project as J2, J4 and J6 connectors, SD interface and the Lithium battery for Teensy RTC. Because all pins on the Teensy connectors are previously engaged, I used for the dip switches some signals from solder pads on the back side of the board. As can be seen in the following figure, I wired four signals (pins 24, 25, 26 and R) to a small connector J7, located on the back of the Teensy board. The following figure shows the wiring diagram of dip switch and pull-up resistors, mounted on the breadboard. The SW2 button is not used in this project.

With minor changes to the wiring diagram and the program you can connect the dip switches to the pins available on Teensy connectors and avoid soldering the wires on the pad. For example, renouncing the SD interface, you can use the pins 9, 10, 11 and 12.

The LCD40 board
For this project a breadboard has been used to securing and connecting all components. The following photo shows the arrangement of the components on the board and the frame used to support all other components.

As already mentioned, some components on the board are not used in this project. This is because I have made a versatile development system with a large alphanumerical LCD, a SD card interface and a real time clock for many applications as data logger for analog signals or for GPS.
The power bank seen in the photo has been replaced because, while being of good quality, disconnects the current after a few seconds since the system consumes too little.

The program
To compile the program I used the Arduino IDE (Version 1.8.1), adding the Teensyduino software add-on  (Version 1:35), for more details visit the website https://www.pjrc.com.
The setup() function, after initializing the I/O read the status of the dip switches to set the baud rate of the terminal, this value that is also printed on the display.
To realize a real serial terminal it would be better to have an automatic scrolling of received lines, so a buffer is necessary. I used a string array of 4x41 characters. A string size of 41 is necessary to represent a line of a maximum of 40 characters because strings have a null character (ASCII code 0) as a terminator.
For the first four lines received, the printing proceeds from line 0 to line 3. Subsequently, each new line is printed on line 3 while the other three previous lines are shifted up. This operation is done in the buffer: before transferring the last line received in the buffer#3, the current content of the buffer#3 is put in buffer#2 and so on until the contents of the buffer#1 is transferred to the buffer# 0. The whole behaves like a multidimensional shift register. After this process all the buffers are printed at one time on LCD.
The libraries for the controlling of the display have a command to run the scroll, but it refers to the line, and it does not work like the one proposed.
If a line exceeds 40 characters, the program truncates it before transferring to the buffer.
To make the system faster, I decided to read a line at a time instead of working on every single character.
As Teensy has more than one UART, unlike Arduino Uno, HWSERIAL should be used in place of Serial for all serial functions and the statement #define HWSERIAL Serial1 must be used to define the UART to be use (1 in our case).
The function HWSERIAL.readStringUntil('\n') reads a line to the terminator, which in our case is a CR / LF.
I used two types of strings: the linestr variable is a String class while the string array is a character arrays type. So I used the  function linestr.toCharArray(displayline[3],41); for the conversion.

Code
/*Serial terminal, print on a 40x4 LCD display,
  the library LiquidCrystalFast is used because it controls
  also display with double enable pins as that used
  Giovanni Carrera - 07/03/2017
*/
// include the library code:
#include <LiquidCrystalFast.h>
// set this to the hardware serial port used
#define HWSERIAL Serial1

const int Button = 22; // push button
const int DipSw1 = 23; // dip switch bit 1
const int DipSw2 = 24; // dip switch bit 2
const int DipSw3 = 25; // dip switch bit 3
const int DipSw4 = 26; // dip switch bit 4
const long baud_rate[9] = {1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200};
String linestr;
char displayline[4][41];// display buffers 4 rows x 40 characters
int r, i;
boolean ft_flag = true;// first time flag

// initialize the library with the numbers of the interface pins
//LiquidCrystalFast lcd(RS, RW, E1, E2, D4, D5, D6, D7);
LiquidCrystalFast lcd(21, 4, 2, 6, 17, 18, 19, 20);

void setup() {
  pinMode(DipSw1, INPUT); // set dip switch bits to input
  pinMode(DipSw2, INPUT);
  pinMode(DipSw3, INPUT);
  pinMode(DipSw4, INPUT);
  pinMode(Button, INPUT);
  // set up the LCD's number of rows and columns:
  lcd.begin(40, 4);
  // Print a message to the LCD.
  lcd.println("Serial terminal by GCar - 07/03/2017");
  // read dip switches for baud rate coding
  int brcode = digitalRead(DipSw1) + digitalRead(DipSw2) * 2 + digitalRead(DipSw3) * 4;
  lcd.print("Baud rate = ");
  lcd.println(baud_rate[brcode]);
  HWSERIAL.begin(baud_rate[brcode]);// for Teensy
  delay(5000);
  for (r = 0; r <= 3; r++) {
    displayline[r][0] = 0; // clear buffer
  }
  LCDprint(); // clear display
}

void LCDprint() {
  lcd.clear(); // clear diplay
  for (r = 0; r <= 3; r++) {
    lcd.setCursor(0, r); // set the cursor to column 0, line r
    lcd.print(displayline[r]); // print all four lines
  }
}

void loop() {
  r = 0;
  if (HWSERIAL.available() > 0) {
    linestr = HWSERIAL.readStringUntil('\n');
    if (ft_flag == true) {
      linestr.toCharArray(displayline[r], 41);// store line in buffer
      LCDprint();// print all four lines
      r++;
      if (r > 3) {
        r = 0;
        ft_flag = false;
      }
    }
    else {
      for (i = 0; i <= 40; i++) {
        displayline[0][i] = displayline[1][i];// scroll, line#0 = line#1
        displayline[1][i] = displayline[2][i];// scroll, line#1 = line#2
        displayline[2][i] = displayline[3][i];// scroll, line#2 = line#3
      }
      linestr.toCharArray(displayline[3], 41);// store new line in line#3
      LCDprint();// print all four lines
    }
  }
}