Using a 16x2 Display with the I2C Mini Terminal

This forum is dedicated to the author Joe Pardue as he teaches you about AVR C Programming in his workshop series.

Moderator: joepardue

Post Reply
gwmayes
Posts: 5
Joined: Thu Apr 07, 2005 1:01 am
Location: Albuquerque, NM
Contact:

Using a 16x2 Display with the I2C Mini Terminal

Post by gwmayes » Thu Jan 16, 2014 9:01 pm

Joe,

I recently purchased your really nifty Hand Held Prototyper. Works great. Good job. I love having nearly all the Arduino pins for other things.

I've got a project where I'd really like to use a 16x2 display. I connected a Microtivity 16x2 display to your 14 pin connector and it works great....... except, as you know, the firmware only supports 8x2.

I have and use frequently the Atmel ISP programmer with Atmel Studio 6. You wouldn't happen to have a .hex file around that will do a 16x2 display would you?

Or can I purchase a 328 that does the same? I've looked at your source code and the changes needed seem pretty minor but I'm afraid of messing up the boot loader.

Thanks much,
George

User avatar
joepardue
Posts: 51
Joined: Sun Jul 31, 2011 7:44 am
Contact:

Re: Using a 16x2 Display with the I2C Mini Terminal

Post by joepardue » Fri Jan 17, 2014 5:34 am

George,

The I2C Mini Terminal has a built-in Arduino compatible bootloader that you can access from the FTDI connector. You can access the original articles here:
http://blog.smileymicros.com/arduino-ha ... er-part-1/
http://blog.smileymicros.com/arduino-ha ... er-part-2/

Since this is based on the Arduino, you really can't mess it up - it was designed to tinker with.

The I2C Mini Terminal uses the Arduino LCD library and that can be easily ported to 16x2 LCDs. I just noticed that I don't have the source code up on my website - so I'll dig it out and post it - but I do believe that it is on the Nuts&Volts website on the page for this article, probably the August and/or September 2013 issue.

Just treat it like an Arduino, use the Arduino IDE and after you change the code you can upload it with an FTDI cable compatible USB to Serial converter. If you don't already have one of those you can get it from AdaFruit or Sparkfun.

If you want to go the Atmel Studio 6 ISP route it is quite possible to trash the bootloader, but it is also easy to reload it. Tell me if you follow this route and mess up.

Joe

User avatar
joepardue
Posts: 51
Joined: Sun Jul 31, 2011 7:44 am
Contact:

Re: Using a 16x2 Display with the I2C Mini Terminal

Post by joepardue » Fri Jan 17, 2014 5:55 am

And I found it:
http://blog.smileymicros.com/downloads/

Please see if that works for you and report back with any problems and I'll see if I can help.

Joe

gwmayes
Posts: 5
Joined: Thu Apr 07, 2005 1:01 am
Location: Albuquerque, NM
Contact:

Re: Using a 16x2 Display with the I2C Mini Terminal

Post by gwmayes » Tue Jan 21, 2014 8:01 pm

Hey Joe,

Just for grins I thought the embedded code on your 2x8 terminal might be rather generic and handle a 2x16 display anyway.

IT DOES! I modified the I2CMiniTerm.h file to be compatible with a 2x16 and voila, it worked.

However, I've run into a real mystery I can't seem to track down. In the example sketch where you have this:

#include <I2CMiniTerminal2_16.h>

// I2CMiniTerminalTest Joe Pardue May 27, 2013
// Modified for 16x2 Display by George Mayes

//#include <I2CMiniTerminal.h>
#include <Wire.h>

// Store strings in program memory
p_string(Hello) = "Hello";
p_string(world) = "World!";
p_string(up) = "up";
p_string(down) = "down";
p_string(center) = "center";
p_string(left) = "left";
p_string(right) = "right";
p_string(error) = "error";


If I change any of the text strings to be longer than 12 characters the terminal hangs! Where in the world does the size of p_string get defined?

BTW: Your blog spot seems to be down?

Thanks,
Geo.

User avatar
joepardue
Posts: 51
Joined: Sun Jul 31, 2011 7:44 am
Contact:

Re: Using a 16x2 Display with the I2C Mini Terminal

Post by joepardue » Wed Jan 22, 2014 5:50 am

Geo,

It is partly because I hard-wired the number of characters to print in the mt_print function:

// load the buffer
for (int i=0; i < 8; i++) {
buffer = (char)x;
}

You can find the I2CMiniTerminal.cpp and .h code in the Arduino library directory where you installed it and you can modify the code as you like. Good coding practice would have been to create a constant for the line length like #define MAXLINELENGTH = 8 then you could change that to 16, but I did it the lazy way so you'll have to hunt down all the '8' and change them to 16.

And that may not be the only problem because the code should only have printed 8 and not got hung up. It might have something to do with the macro:

// Use to hide complexity of locating data in program memory
#define p_string(text) prog_char (text)[] PROGMEM

AFAIK the prog_char should accommodate a string of arbitrary length.

If changing the line length in the functions doesn't work then I suggest you post your code here and I'll see if I can find any other suspects.

And, yes the blog isn't functioning properly at the moment - but were you able to access the post from the link I gave above?

Glad you are having fun with the code,
Joe

gwmayes
Posts: 5
Joined: Thu Apr 07, 2005 1:01 am
Location: Albuquerque, NM
Contact:

Re: Using a 16x2 Display with the I2C Mini Terminal

Post by gwmayes » Wed Jan 22, 2014 5:03 pm

It's got to be that macro Joe.

I'm fairly confident I got all the code changes needed

Here's the .cpp file:

Code: Select all

// MiniTerminal Library 
// Joe Pardue June 12, 2013
/*
An Arduino may use the I2C Mini Terminal to display 8x2 LCD characters
and retrieve the most recent button press.

Modified to handle a 16x2 display by George Mayes 1/15/14 

In order to make the system responsive you should put the Arduino button request in a 
timer set to 100ms.
*/

#include "I2CMiniTerminal2_16.h"

#define DEBUG

struct lcdStruct{
  
  uint8_t lcdLine;
  uint8_t lcdPosition; 

  char characters[(LCDLINES*LCDPOSITIONS)+1]; 

}myLCD;

struct lcdLineStruct{
  
  uint8_t lcdPosition; 

  char characters[LCDPOSITIONS+1]; 

}myLCDLine[LCDLINES];

int olderMillis = 0;
int key;
int oldkey;

uint8_t number = 0;

// define the LCD character buffer
// char buffer[9];
	 char buffer[17];
//
// Functions for controlling the Mini Terminal
//

// Tell the slave where to put the cursor
void mt_setCursor(uint8_t p, uint8_t l)
{  
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");
    Wire.write(LCD_SET_CURSOR);  
    Wire.write(p);  
    Wire.write(l);       
    Wire.endTransmission();
    delay(DELAY);   
}

// Turn the cursor on
void mt_cursor()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");
    Wire.write(LCD_CURSOR_ON);  
    Wire.write(LCD_CURSOR_ON);       
    Wire.endTransmission();  
    delay(DELAY);  
}

// Turn the cursor off
void mt_noCursor()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");
    Wire.write(LCD_CURSOR_ON);  
    Wire.write(LCD_CURSOR_OFF);       
    Wire.endTransmission(); 
    delay(DELAY);  
}

// Turn the cursor blink on
void mt_blink()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");
    //Wire.write(LCD_BLINK_ON);  
    Wire.write(LCD_BLINK_ON);       
    Wire.endTransmission(); 
    delay(DELAY);  
}

// Turn the cursor blink off
void mt_noBlink()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");
    //Wire.write(LCD_BLINK_ON);  
    Wire.write(LCD_BLINK_OFF);       
    Wire.endTransmission();    
    delay(DELAY);
}

// Turn the Display on
void mt_display()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");         
    Wire.write(LCD_DISPLAY_ON);
    Wire.endTransmission();    
    delay(DELAY);
}

// Turn the Display off
void mt_noDisplay()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD"); 
    Wire.write(LCD_DISPLAY_OFF);       
    Wire.endTransmission();    
    delay(DELAY);
}

// Put the cursor in the upper left position
void mt_home()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");
    Wire.write(LCD_HOME);  
    Wire.endTransmission();  
    delay(DELAY); 
}

// Clear the LCD
void mt_clear()
{
    Wire.beginTransmission(SLAVE);
    Wire.write("LCD");
    Wire.write(LCD_CLEAR); 
    Wire.endTransmission(); 
    delay(DELAY);     
}

// Clear LCD line 0
void mt_clear0()
{
  mt_setCursor(0,0);  
  mt_clearLine();
}
// Clear LCD line 1
void mt_clear1()
{
  mt_setCursor(0,1);  
  mt_clearLine();
}

// Clear the LCD line
void mt_clearLine()
{
//  for (int i=0; i < 9; i++) {
    for (int i=0; i < 17; i++) {
   	 buffer[i] = ' ';
  }
  
  Wire.beginTransmission(SLAVE);
  Wire.write("LCD:");
  Wire.write(buffer); 
  Wire.endTransmission(); 
  delay(DELAY);   
}

void mt_printPM(prog_char* x, byte p, byte l)
{ 
  mt_setCursor(p,l);
  
//  for (int i=0; i < 8; i++) {
    for (int i=0; i < 16; i++) {
    	buffer[i] = (char)pgm_read_byte(&x[i]);
  }
//  buffer[9] = '\0';
    buffer[17] = '\0';
  Wire.beginTransmission(SLAVE);
  Wire.write("LCD:");
  Wire.write(buffer); 
  Wire.endTransmission(); 
  delay(DELAY);  
}

void mt_print(char* x, byte p, byte l)
{ 
  if(l) mt_clear1();
  else mt_clear0();
  
  mt_setCursor(p,l);  
  
  // load the buffer 
//  for (int i=0; i < 8; i++) {
    for (int i=0; i < 16; i++) {
    buffer[i] = (char)x[i];
  }
//  buffer[9] = '\0';
    buffer[17] = '\0';
  
  Wire.beginTransmission(SLAVE);
  Wire.write("LCD:");
  Wire.write(buffer); 
  Wire.endTransmission(); 
  delay(DELAY);  
}

void mt_print8BitNumber(uint8_t number, byte left, byte line)
{
	char buffer[] = { ' ', ' ', ' ', '\0'};
	
	mt_print(buffer,left,line); // clear the number
	
	itoa (number,buffer,10);
	if(number < 10) mt_print(buffer, left + 2, line);
	else if (number < 100) mt_print(buffer, left + 1, line);
	else mt_print(buffer, left, line);
	
} 

/*
Returns an 8-bit number when center pressed
Left/Right move cursor
Up/Down increments number
Parameters:
Line 1 label
Maximum
Minimum

*/
uint8_t mt_get8BitNumber(char * str,uint8_t start)
{  
  //char numString[] = {' ',' ',' ','\0'}; 
  byte right = 7;
  byte left = 5;
  byte line = 1;
  byte dec = 0;
  
  // Clear any number    
  //mt_print(numString,left,line); 
  number = start;

  mt_print8BitNumber(number,left,line);
  
  // Initialize place for number
  myLCDLine[line].lcdPosition = 5; 

  // Print str on line 0
  mt_setCursor(0,1);
  mt_print(str,0,0);
 
  mt_setCursor(right,line); 
  mt_cursor();
 
  while(1)
  {   
     uint8_t key = mt_getKeyWait();
 
     switch (key)
     {
       case 0:
         break;
       case RGT:
         if(dec > 0)
         {
		   dec--;
		   mt_setCursor(right-dec,line);
		   
		   #ifdef DEBUG
		   Serial.print("RGT - number = ");
		   Serial.println(number);
		   #endif
         }
          break;
       case LFT:
          if(dec < 2)
          {
		    dec++;
		    mt_setCursor(right-dec,line);		    
			#ifdef DEBUG
		    Serial.print("LFT - number = ");
		    Serial.println(number);
		    #endif			
          }
         break;
       case UP:
	     if( (dec == 0) && (number < 255) ) number++;
		 else if( (dec == 1) && (number < 245) ) number += 10;
		 else if( (dec == 2) && (number < 155) ) number += 100;
		 mt_print8BitNumber(number,left,line);
		 mt_setCursor(right-dec,line);
		 mt_cursor();
		 #ifdef DEBUG
		 Serial.print("UP - number = ");
		 Serial.println(number);
		 #endif		 
         break;
       case DWN:
	     if( (dec == 0) && (number > 0) ) number--;
		 else if( (dec == 1) && (number > 10) ) number -= 10;
		 else if( (dec == 2) && (number > 100) ) number -= 100;
		 //else dec = 0; number = 0; 
		 mt_print8BitNumber(number,left,line);
		 mt_setCursor(right-dec,line);
		 mt_cursor();
		 #ifdef DEBUG
		 Serial.print("DWN - number = ");
		 Serial.println(number);
		 #endif		 
         break;
       case CTR:
		 #ifdef DEBUG
		 Serial.print("RGT - number = ");
		 Serial.println(number);
		 #endif
		 return number;
         break;
       default:
	     #ifdef DEBUG  
         Serial.println("mt_get8BitNumber ERROR.");
         delay(1000); 
         #endif
     }  
  }   
} 
 
 // read the last button pressed
uint8_t mt_getButton()
{
  Wire.requestFrom(SLAVE,1); // request the button 
  return Wire.read();
}

uint8_t mt_getKey()
{
	key = 0;
	while(key == 0)
	{
      Wire.requestFrom(SLAVE,1); // request the button 
      key = Wire.read();
	}
	return key;
}
 
// Blocks waiting for a key to return
uint8_t mt_getKeyWait()
{ 

  while(1)
  {
    // use millis() to check for available buttons
    // about 10 times a second 
    if( (millis() - olderMillis) > 100)
    { 
      Wire.requestFrom(SLAVE,1); // request the button 
      key = Wire.read();   
      olderMillis = millis();
	}  
    if(key != oldkey)
    {  

      oldkey = key;
      return key;
    }
  }
} 
 
	  /*#ifdef DEBUG
	  Serial.print("key = ");
	  Serial.println(key);
	  Serial.print("oldkey = ");
	  Serial.println(oldkey);
	  #endif*/ 

The .h only needed one line changed:

#define LCDPOSITIONS 16

Note. The magic number is 12 characters.

I can replace both "Hello" and "world" with "123456781234" and it works. Add that "5" = broken :sad:

Hey, I'm having fun!
Thanks,
Geo.

User avatar
joepardue
Posts: 51
Joined: Sun Jul 31, 2011 7:44 am
Contact:

Re: Using a 16x2 Display with the I2C Mini Terminal

Post by joepardue » Wed Jan 22, 2014 6:44 pm

I'm out of pocket tomorrow, but I'll look at it on Friday and see what I can come up with. In the meantime see if I am using an array set to 8 characters (or so) such an array might work okay for 11 characters if nothing important gets overwritten but the 12th non-assigned overwrite might just hit something critical.

Joe

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests