Using TWI/I2C with a LCD module [ATtiny817]

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

Hi,

 

My task is to try to get a LCD module to communicate with the Attiny817 xplained-mini board so that I can display characters and stuff on it. Been banging my head for a while now, so would like help figuring out how to get this working and to learn how it goes along the way. The LCD that I have is a NHD-C0216CiZ-FSW-FBW-3V3. I know that, from reading datasheet, that this module needs I2C [which I understand is TWI on the attiny817 board]. 

 

 

Currently connected SCL to PA1 and SDA to PA2 on the attiny board, I know the pins on Port B are where SCL and SDA are by default, but I guess I'll use the portmux register to activate the alternative location to enable PA1 and PA2 since I already have the LCD wired up to PA1 and PA2, instead of taking it apart right now.. I also know that I2C (or what Atmel calls TWI) is the way to communicate between attiny817 and the LCD display module.

 

I tried modifying the sample code that was given in the user manual to see if that would work and so far no go yet. My attempt is below.  If anybody could help me figure this out / point me in the proper direction so I don't keep on mindlessly trying things and getting nowhere, I would appreciate it.  Still learning how to get things working on the ATTiny817 with the Xplained-mini board. Thanks so much.  So far, powering up the attiny/LCD, LCD lights up, and it's not doing anything else. 

 

#define F_CPU 3330000 // 3.33 MHz
#include <avr/io.h>
#include <util/delay.h>

//#define SDA_PIN PIN1_bm
//#define SCL_PIN PIN2_bm

//------------------------------------------------------
unsigned char text1[]={"Hello World"};
unsigned char text2[]={"Test Text "};
// unsigned char text3[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
unsigned char text4[]={0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f};

#define SDA 24            //SDA (PA1)
#define SCLK 1           //SCL (PA2)
// #define RESET 24          //RESET

//------------------------------------------------------

const char Slave = 0x7C;
const char Comsend = 0x00;
const char Datasend = 0x40;
const char Line2 = 0xC0;

//------------------------------------------------------
void delay(unsigned int n)				//Delay subroutine
{
	unsigned int i,j;
	for (i=0;i<n;i++)
	for (j=0;j<350;j++)
	{;}
}

//------------------------------------------------------
void I2C_out(unsigned char j)       //I2C Output
{
	int n;
	unsigned char d;
	d = j;
	for(n=0; n<8; n++)
	{
		if((d&0x80)==0x80)
		PORTA.OUTSET =	PIN1_bm; // (SDA, HIGH);
		else
		PORTA.OUTCLR =	PIN1_bm; //(SDA, LOW);
		d=(d<<1);
		PORTA.OUTCLR =	PIN2_bm; // (SCL, LOW);
		PORTA.OUTSET =	PIN2_bm; // (SCL, HIGH);
		PORTA.OUTCLR =	PIN2_bm; // (SCL, LOW);
	}
	PORTA.OUTSET =	PIN2_bm; // (SCL, HIGH);

	while(SDA==1)
	{
		PORTA.OUTCLR =	PIN2_bm; // (SCL, LOW);
		PORTA.OUTSET =	PIN2_bm; // (SCL, HIGH);
	}

	PORTA.OUTCLR =	PIN2_bm; // (SCL, LOW);
}

//------------------------------------------------------
void I2C_Start()
{
	PORTA.OUTCLR =	PIN2_bm; // (SCL, LOW);
	PORTA.OUTSET =	PIN1_bm; // (SDA, HIGH);
	PORTA.OUTCLR =	PIN1_bm; // (SDA, HIGH);
	PORTA.OUTCLR =	PIN2_bm; // (SCL, LOW);
}

//------------------------------------------------------
void I2C_Stop()
{
	PORTA.OUTCLR =	PIN1_bm; // (SDA, HIGH);
	PORTA.OUTCLR =	PIN2_bm; // (SCL, LOW);
	PORTA.OUTSET =	PIN2_bm; // (SCL, HIGH);
	PORTA.OUTSET =	PIN2_bm; // (SCL, HIGH);
}

//------------------------------------------------------
void Show(unsigned char *text) // Display text
{
	int n, d;
	d=0x00;
	I2C_Start();
	I2C_out(Slave);
	I2C_out(Datasend);

	for(n=0; n<16; n++)
	{
		I2C_out(*text);
		++text;
	}
	I2C_Stop();
}

//------------------------------------------------------
void nextline(void)  // Move to Line 2
{
	I2C_Start();
	I2C_out(Slave);
	I2C_out(Comsend);
	I2C_out(Line2);
	I2C_Stop();
}

void CGRAM (void)
{
	I2C_Start();
	I2C_out(Slave);
	I2C_out(Comsend);
	I2C_out(0x38);		//go to instruction table 0
	I2C_out(0x40);		//Set CGRAM address to 0x00
	I2C_Stop();
	delay(10);

	I2C_Start();
	I2C_out(Slave);
	I2C_out(Datasend);
	I2C_out(0x00);		//write to first CGRAM address
	I2C_out(0x1E);
	I2C_out(0x18);
	I2C_out(0x14);
	I2C_out(0x12);
	I2C_out(0x01);
	I2C_out(0x00);
	I2C_out(0x00);		//8 bytes per character
	//continue writing to remaining CGRAM if desired
	I2C_Stop();
}

//------------------------------------------------------
//Initialization For ST7032i
void init_LCD()
{
	I2C_Start();
	I2C_out(Slave);
	I2C_out(Comsend);
	I2C_out(0x38);
	delay(10);
	I2C_out(0x39);
	delay(10);
	I2C_out(0x14);
	I2C_out(0x78);
	I2C_out(0x5D);
	I2C_out(0x6D);
	I2C_out(0x0C);
	I2C_out(0x01);
	I2C_out(0x06);
	delay(10);
	I2C_Stop();

	CGRAM();	// define CGRAM
	I2C_Start();
	I2C_out(Slave);
	I2C_out(Comsend);
	I2C_out(0x39);
	I2C_out(0x01);    //go back Home
	I2C_Stop();
	delay(10);
}

//------------------------------------------------------
int main(void)
{
	PORTMUX.CTRLB = PORTMUX_TWI0_ALTERNATE_gc; //  PA1, PA2 are alternate pins for SCL and SDA, so need to use the PORTMUX register bit to enable
	while(1) 								//continue
	{
		init_LCD();
		delay(2);
		Show(text1);
		nextline();
		Show(text2);
	}
}

 

Last Edited: Thu. Feb 8, 2018 - 09:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I strongly suggest that you use the Fleury bit-bang I2C library.    This will mean that your code will work on any AVR.   You will be using a proven library.

 

Non-interrupt TWI on the Tiny817 is a bit of a nightmare.

Interrupt TWI is probably more complex than you want.

Fleury bit-bang just works.   And it has a standard API.    So you can use the hardware TWI on a Mega with no changes to your application.

 

Oh,   I2C is an open-drain bus.   You never use push-pull output drivers.

The LCD uses a 3.3V ST7032i controller.    You use similar commands to a regular 16x2 LCD.   Just with I2C instead of parallel.

 

David.

Last Edited: Thu. Feb 8, 2018 - 09:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello David, 

 

Thank you very much for the quick response. I google "Fleury bit-bang I2C library" and take a spin with it and see how it can get me closer to what I wanted to do. If you could point me to an example of what you meant by the standard LCD commands for a 16x2 lcd, that would be great. thanks! 

 

By the way, the code in the user manual for the initialization of the ST73O2i, it is the way to go or should I just forget about that and figure out other way to get the LCD working? (Hope this makes sense) 

 

 

Regards, 

 

mc0134

Last Edited: Thu. Feb 8, 2018 - 10:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I attached a tiny817 bit-bang to http://www.avrfreaks.net/comment/2388391#comment-2388391

You would edit for PA1/PA2 if you are using the alternate TWI pins.

 

The I2C sequence is just control-byte followed by CMD or DATA bytes.

You can not read LCD data from the I2C.

 

Otherwise the CMDs are exactly like any other 16x2 controller

You will use the 8-bit initialisation sequence.

 

Untested.   I am just reading the datasheet.

I would expect NHD to supply some example code.   Probably for 8051.   You just use it at the lcd_command() and lcd_data() level.

 

David.

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

Hello David,

 

Thanks for the information. I'll read up on the stuff in the link you posted and see how things turn out. Below is an example code that I found for this the NHD LCD, that was what I was trying to emulate in the attempts of getting something to be working initially, as I was doing in my post #1.  I suppose that my task now is to verify that  I see the  I2C/TWI working properly, and then I assume that once I get the communication verified, then all I'd have to do is to send the proper information across (i.e., address of the LCD module so that ATTINY, as the master can talk to it, and then the data information that sends the proper characters out to the LCD module (slave).

 

Once again, I appreciate the time you took to help me with this, and should I have other questions later, I'll be back. 

 

Regards,

 

mc0134

   

/*****************************************************/
/*
C0220BiZ.c
https://www.newhavendisplay.com/app_notes/NHD-C0220BiZ.txt
Program for writing to Newhaven Display Character COG - I2C interface

(c)2009 Curt Lagerstam - Newhaven Display International, LLC. 

 	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
*/
/*****************************************************/
#include <REG52.H>
/*****************************************************/
code char text1[]={
"NEWHAVEN DisplayXXXX"};
code char text2[]={
"2x20 LCD Module XXXX"};
/*****************************************************/
sbit SDA = P1^0;						//Serial data
sbit SCL = P3^4;						//Serial clock
//sbit XRESET = HIGH;						//RESET
/*****************************************************/
const char Slave = 0x78;
const char Comsend = 0x00;
const char Datasend = 0x40;
const char Line2 = 0xC0;
/*****************************************************/
void delay(unsigned int n)				//Delay subroutine
{
	unsigned int i,j;
	for (i=0;i<n;i++)
  		for (j=0;j<350;j++)
  			{;}
}
/*****************************************************/
void I2C_out(unsigned char j) 			//I2C Output
{
	int n;
	unsigned char d;
	d=j;
	for(n=0;n<8;n++){
		if((d&0x80)==0x80)
		SDA=1;
		else
		SDA=0;
		d=(d<<1);
		SCL = 0;
		SCL = 1;
		SCL = 0;
		}
	SCL = 1;
	while(SDA==1){
		SCL=0;
		SCL=1;
		}
	SCL=0;
}
/*****************************************************/
void I2C_Start(void)
{
	SCL=1;
	SDA=1;
	SDA=0;
	SCL=0;
}
/*****************************************************/
void I2C_Stop(void)
{
	SDA=0;
	SCL=0;
	SCL=1;
	SDA=1;
}
/*****************************************************/
void Show(unsigned char *text)
{
	int n,d;
	d=0x00;
	I2C_Start();
	I2C_out(Slave);
	I2C_out(Datasend);
	for(n=0;n<20;n++){
		I2C_out(*text);
		++text;
		}
	I2C_Stop();
}
/*****************************************************/
void nextline(void)
{
	I2C_Start();
	I2C_out(Slave);
	I2C_out(Comsend);
	I2C_out(Line2);
	I2C_Stop();
}
/****************************************************
*           Initialization For ST7036i              *
*****************************************************/
void init_LCD()
{
I2C_Start();
I2C_out(Slave);
I2C_out(Comsend);
I2C_out(0x38);
delay(10);
I2C_out(0x39);
delay(10);
I2C_out(0x14);
I2C_out(0x78);
I2C_out(0x5E);
I2C_out(0x6D);
I2C_out(0x0C);
I2C_out(0x01);
I2C_out(0x06);
delay(10);
I2C_Stop();
}
/*****************************************************/
/*****************************************************/
int main(void)
{
int i;
P1 = 0;
P3 = 0;
while(1) 								//continue
{
	init_LCD();
	delay(2);

	Show(text1);
	nextline();
	Show(text2);
	delay(2000);

	init_LCD();
	delay(2);

	I2C_out(Slave);
	I2C_out(Datasend);
	for(i=0;i<20;i++){//show first 20 chars in font table
	  I2C_out(i);}
	I2C_Stop();
	delay(4000);
}
}

 

Last Edited: Thu. Feb 8, 2018 - 11:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would replace the P1, P3 =0 with i2c_init(). I2C_Start(), I2C_out(Slave) with i2c_start(Slave). And I2C_out(c) with i2c_write(c) ...
.
Note that 8051 is inherently open-drain. It is not using push-pull on the I2C lines.
.
NHD examples are often written for 8051. You just convert the primitives to AVR, ARM, PIC, ... or whatever.
In your case, I would just add i2cmaster_t817.S to your project.
.
Nowadays examples are written for Arduino. Probably harder to port back to regular C. But if you buy a 3.3V Arduino you can be up and running within a minute. Chinese Uno clones are available with 3.3V / 5V logic for about $5.
.
David.

Last Edited: Thu. Feb 8, 2018 - 11:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yea, I see a lot of the arduino styled cN Will be adding the i2cmaster_t187.S to the project folder, then just make sure I got the ports and pins connected, and see where I go from thereon. I assume that once I get the i2c llibrar 

 

 

One last question before I actually get off of avrfreaks for the time being to see the project in motion, no need to add any external pull up resistors of any sort to the system? I just have the 817 xplained mini board, LCD module and a couple of wires that connect LCD module to the ATTiny pins.

 

I'm going to keep trying, I hope this project finally gets done and I can finally just slow down and review it for future reference and of course, I'll probably expect to be asking more stuff here at avrfreaks as time comes along, who knows.

 

Will let you know how I end up with this LCD stuff. 

 

 

 

Regards,

 

mc0134

Last Edited: Fri. Feb 9, 2018 - 01:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

LATEST UPDATE:

 

Latest code below, but why am I seeing error messages like in the screenshot when apparently .S file and the .h both are included?

 

 

 

 

// NHD-C0216CiZ-FSW-FBW-3V3 COG (Chip-on-Glass) Liquid Crystal Display Module 

#define F_CPU 3330000UL //3.3 MHz clock
#include <avr/io.h>
#include <util/delay.h>
#include "twi_master.h"

/*! CPU speed 1.6MHz, BAUDRATE 100kHz and Baudrate Register Settings */
#define CPU_SPEED   20000000
#define BAUDRATE	100000
#define TWI_BAUDSETTING TWI_BAUD(CPU_SPEED, BAUDRATE)

/* TWI Master */
TWI_Master_t twi_master;
TWI_t inst;

unsigned char text1[]={"TESTING Display"};
unsigned char text2[]={"2x16 LCD Module"};

//----------------------------
// Initializes the LCD panel.
// ST7032i
//-----------------------------

void init_LCD(void)
{
	I2C_Start();

	unsigned char data[16];

	data[0] = 0x7C; // Slave address of the LCD panel.
	data[1] = 0x00; // Control byte: all following bytes are commands.
	data[2] = 0x38; // 8-bit bus, 2-line display, extension instruction mode.
	TWI_MasterWrite(&twi_master, 0x7C, data, 3);

	_delay_ms(10);
	//data[0] = 0x7C; // Slave address of the LCD panel.
	//data[1] = 0x00; // Control byte: all following bytes are commands.
	data[2] = 0x39; // 8-bit bus, 2-line display, extension instruction mode.
	TWI_MasterWrite(&twi_master, 0x7C, data, 1);

	//data[0] = (0x7C);
	//data[1] = (0x00);
	data[0] = (0x14);	//Set frame frequency to 192 Hz and Voltage Bias to 1/5
	data[1] = (0x78);	//Set contrast bits C3:0 to 8 (C5:0 - 0x28 *C5:4 is part of next data byte)
	data[2] = (0x5E);	//Turn on Icon Display and Booster Circuit and set C5:4 to 2 for contrast setting
	data[3] = (0x6D);	//Turn on internal follower circuit and adjust V0 generator amplified ratio (Rab2:0 - 2)
	TWI_MasterWrite(&twi_master, 0x7C, data, 4);

	_delay_ms(200);

	//data[0] = (0x7C);
	//data[1] = (0x00);
	data[0] = (0x0C); 													//Turn on display and turn on cursor and cursor blink 0x0F
	data[1] = (0x01);													//Clear the display	- Entry Mode Set is required afterwards
	data[2] = (0x06);													//Entry Mode Set - Increment DDRAM Address (cursor) and do not shift display
	TWI_MasterWrite(&twi_master, 0x7C, data, 3);

	_delay_ms( 10 );

	I2C_Stop();
}

//--------------------------------------------------
//  Writes a 16-char string to the RAM of the LCD.
//--------------------------------------------------
void show( unsigned char *text )
{
	int n;

	I2C_Start();

	I2C_out( 0x78 ); // Slave address of panel.
	I2C_out( 0x40 ); // Control byte: data bytes follow, data is RAM data.

	for( n = 0; n < 16; n++ )
	{
		I2C_out( *text );
		text++;
	}

	I2C_Stop();
}

//--------------------------------------------------
void nextline(void)  // Move to Line 2
{
	I2C_Start();
	I2C_out(0x7C); // slave = 0x7c
	I2C_out(0x00); // comsend = 0x00
	I2C_out(0xC0); // line 2 = 0xC0
	I2C_Stop();
}

//--------------------------------------------------
int main(void)
{
	inst = TWI0;
	volatile uint8_t baud_rate = (CPU_SPEED/(2*BAUDRATE) - 5);

	TWI_MasterInit(&twi_master, &TWI0, (TWI_RIEN_bm | TWI_WIEN_bm), baud_rate);

	sei();
	I2C_Init(); // I2C == TWI
	while(1) 								//continue
	{
		init_LCD();
		_delay_ms(2);

		show(text1);
		nextline();
		show(text2);
		_delay_ms(2000);
	}
}

 

Last Edited: Fri. Feb 9, 2018 - 08:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The "twi_master.h" implies interrupt driven code from Atmel Start.

The I2C_Start () calls imply polled code from the NHD 8051 examples.

 

The NHD website offers 8051 example code and Arduino example code.

Personally,  I think it is false economy for people to sneer at Arduino.   For $5 you are up and away with almost any foreign hardware.

 

Having verified the hardware with your $5 Arduino,  you can write your own code in whatever language you like.    At least you know it is your Software problem.

 

The NHD datasheet shows that you need external pullup resistors.

 

If you promise to use external pullups,   I will port the NHD 8051 example for you into a regular AS7 project.

Obviously untested.   I don't have your display.

 

David.

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

so I couldn't help posting something short here too

... and I couldn't help deleting it. DO NOT CROSS POST!

 

Ross McKenzie ValuSoft Melbourne Australia

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

I was bored.   So I ported the 8051 example for you:

I am assuming you have 4k7 pullups on SDA, SCL pins i.e. PA1, PA2 and are running your XMINI-817 on 3.3V.

 

My edits say .kbv

Ask if you don't understand.

 

Obviously untested.   It may or may not work "out of the box"

 

David.

 

 

Attachment(s): 

Last Edited: Fri. Feb 9, 2018 - 09:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:

 

My edits say .kbv

Ask if you don't understand.

 

 

 

Better explain what you meant by that :P

 

- mc0134

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

I am hoping that you can unzip the AS7 project and it will run straightaway.

 

I made the minimal amount of changes to get the NHD example code to build.

Obviously I would have written an example differently.   i.e. in my own style.

 

I hope that you can identify my edits.   Search for kbv

And you can understand their purpose.

 

If it does not run,  please say what you actually get.

If you do not understand something,  just ask.   But please quote line number,  web link, document,  ...

 

David.

 

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

Hi David, 

 

As of now, I'm just trying to get the LCD to be working. After I finally get the LCD working, I will sit down and spend more time to read through the code thoroughly to figure out what exactly makes it work.

 

The latest code that I have is below, take a look. All I've done right now is to edit the main.c file that you sent back to me and I'm getting an error or two as of the latest build. 

 

According to atmel studio, line 95, has 'd' variable unused, but that's understandable. but what i wanted to know is on line 179, it says 'main is normally a non-static function' which is a warning and on line 194, it says expected declaration or statement at end of input. Trying to figure out why to get the build to go through so I can test. Also, this LCD was an extra part that was the person who gave to me said was taken from a project he was working on, and so do I need to still have the pullup resistors?  I'll send the pic of the schematic part that I see later if it would help after I get back.  

 

 

 

#include <avr/io.h>


int main(void)
{
	#include <avr/io.h>
	#include "i2cmaster.h"    //.kbv Fleury header
	#define code              //.kbv need special func for AVR __flash
	#define I2C_out(x) i2c_write(x) //Fleury functions
	#define I2C_Stop() i2c_stop()
	#define I2C_stop() i2c_stop()

	extern int8_t CLKCTRL_init(void); //.kbv change running speed

/*****************************************************/
//C0216CiZ.c

char text1[] = {
    "NEWHAVEN Display"
};
char text2[] = {
    "2x16 LCD Module "
};

/* char text3[] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
}; */

// char text4[] = {
//    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
//};
/*****************************************************/
//sbit SDA = P1^0;                      //Serial data .kbv skip 8051 syntax
//sbit SCL = P3^4;                      //Serial clock .kbv
//sbit XRESET = HIGH;                       //RESET
/*****************************************************/
const char Slave = 0x7C;
const char Comsend = 0x00;
const char Datasend = 0x40;
const char Line2 = 0xC0;
/*****************************************************/
void delay(unsigned int n)              //Delay subroutine
{
    volatile unsigned int i, j;         //.kbv volatile
    for (i = 0; i < n; i++)
        for (j = 0; j < 350; j++)
        {
            ;
        }
}
#if 0                                   //.kbv skip 8051 funcs
/*****************************************************/
void I2C_out(unsigned char j)           //I2C Output
{
    int n;
    unsigned char d;
    d = j;
    for (n = 0; n < 8; n++) {
        if ((d & 0x80) == 0x80)
            SDA = 1;
        else
            SDA = 0;
        d = (d << 1);
        SCL = 0;
        SCL = 1;
        SCL = 0;
    }
    SCL = 1;
    while (SDA == 1) {
        SCL = 0;
        SCL = 1;
    }
    SCL = 0;
}
/*****************************************************/
void I2C_Start(void)
{
    SCL = 1;
    SDA = 1;
    SDA = 0;
    SCL = 0;
}
/*****************************************************/
void I2C_Stop(void)
{
    SDA = 0;
    SCL = 0;
    SCL = 1;
    SDA = 1;
}
#endif
/*****************************************************/
void Show(char *text)  //.kbv unsigned is not necessary
{
    int n, d;
    d = 0x00;
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Datasend);
    for (n = 0; n < 16; n++) {
        I2C_out(*text);
        ++text;
    }
    I2C_Stop();
}
/*****************************************************/
void nextline(void)
{
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(Line2);
    I2C_Stop();
}
void CGRAM (void)
{
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(0x38);      //go to instruction table 0
    I2C_out(0x40);      //Set CGRAM address to 0x00
    I2C_Stop();
    delay(10);

    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Datasend);
    I2C_out(0x00);      //write to first CGRAM address
    I2C_out(0x1E);
    I2C_out(0x18);
    I2C_out(0x14);
    I2C_out(0x12);
    I2C_out(0x01);
    I2C_out(0x00);
    I2C_out(0x00);      //8 bytes per character
    //continue writing to remaining CGRAM if desired
    I2C_Stop();
}
/****************************************************
*           Initialization For ST7032i              *
*****************************************************/
void init_LCD()
{
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(0x38);
    delay(10);
    I2C_out(0x39);
    delay(10);
    I2C_out(0x14);
    I2C_out(0x78);
    I2C_out(0x5E);
    I2C_out(0x6D);
    I2C_out(0x0C);
    I2C_out(0x01);
    I2C_out(0x06);
    delay(10);
    I2C_stop();

    CGRAM();            //define CGRAM

    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(0x39);
    I2C_out(0x01);      //go back Home
    I2C_Stop();
    delay(10);
}
/*****************************************************/
/*****************************************************/
int main(void)
{
    CLKCTRL_init();     //.kbv special clock code for Tiny817 to run at 5MHz
	i2c_init();         //.kbv
    //P1 = 0;  //.kbv  skip 8051 syntax
    //P3 = 0;
    while (1)                               //continue
    {
        init_LCD();
        delay(2);
        Show(text1);
        nextline();
        Show(text2);
        delay(2500);
	}
}

 

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

Hi David, 

 

As of now, I'm just trying to get the LCD to be working. After I finally get the LCD working, I will sit down and spend more time to read through the code thoroughly to figure out what exactly makes it work.

 

The latest code that I have is below, take a look. All I've done right now is to edit the main.c file that you sent back to me and I'm getting an error or two as of the latest build. 

 

According to atmel studio, line 95, has 'd' variable unused, but that's understandable. but what i wanted to know is on line 179, it says 'main is normally a non-static function' which is a warning and on line 194, it says expected declaration or statement at end of input. Trying to figure out why to get the build to go through so I can test. Also, this LCD was an extra part that was the person who gave to me said was taken from a project he was working on, and so do I need to still have the pullup resistors?  I'll send the pic of the schematic part that I see later if it would help after I get back.  

 

 

 

#include <avr/io.h>

int main(void)
{
	#include <avr/io.h>
	#include "i2cmaster.h"    //.kbv Fleury header
	#define code              //.kbv need special func for AVR __flash
	#define I2C_out(x) i2c_write(x) //Fleury functions
	#define I2C_Stop() i2c_stop()
	#define I2C_stop() i2c_stop()

	extern int8_t CLKCTRL_init(void); //.kbv change running speed

/*****************************************************/
//C0216CiZ.c

char text1[] = {
    "NEWHAVEN Display"
};
char text2[] = {
    "2x16 LCD Module "
};

/* char text3[] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
}; */

// char text4[] = {
//    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
//};
/*****************************************************/
//sbit SDA = P1^0;                      //Serial data .kbv skip 8051 syntax
//sbit SCL = P3^4;                      //Serial clock .kbv
//sbit XRESET = HIGH;                       //RESET
/*****************************************************/
const char Slave = 0x7C;
const char Comsend = 0x00;
const char Datasend = 0x40;
const char Line2 = 0xC0;
/*****************************************************/
void delay(unsigned int n)              //Delay subroutine
{
    volatile unsigned int i, j;         //.kbv volatile
    for (i = 0; i < n; i++)
        for (j = 0; j < 350; j++)
        {
            ;
        }
}
#if 0                                   //.kbv skip 8051 funcs
/*****************************************************/
void I2C_out(unsigned char j)           //I2C Output
{
    int n;
    unsigned char d;
    d = j;
    for (n = 0; n < 8; n++) {
        if ((d & 0x80) == 0x80)
            SDA = 1;
        else
            SDA = 0;
        d = (d << 1);
        SCL = 0;
        SCL = 1;
        SCL = 0;
    }
    SCL = 1;
    while (SDA == 1) {
        SCL = 0;
        SCL = 1;
    }
    SCL = 0;
}
/*****************************************************/
void I2C_Start(void)
{
    SCL = 1;
    SDA = 1;
    SDA = 0;
    SCL = 0;
}
/*****************************************************/
void I2C_Stop(void)
{
    SDA = 0;
    SCL = 0;
    SCL = 1;
    SDA = 1;
}
#endif
/*****************************************************/
void Show(char *text)  //.kbv unsigned is not necessary
{
    int n, d;
    d = 0x00;
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Datasend);
    for (n = 0; n < 16; n++) {
        I2C_out(*text);
        ++text;
    }
    I2C_Stop();
}
/*****************************************************/
void nextline(void)
{
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(Line2);
    I2C_Stop();
}
void CGRAM (void)
{
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(0x38);      //go to instruction table 0
    I2C_out(0x40);      //Set CGRAM address to 0x00
    I2C_Stop();
    delay(10);

    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Datasend);
    I2C_out(0x00);      //write to first CGRAM address
    I2C_out(0x1E);
    I2C_out(0x18);
    I2C_out(0x14);
    I2C_out(0x12);
    I2C_out(0x01);
    I2C_out(0x00);
    I2C_out(0x00);      //8 bytes per character
    //continue writing to remaining CGRAM if desired
    I2C_Stop();
}
/****************************************************
*           Initialization For ST7032i              *
*****************************************************/
void init_LCD()
{
    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(0x38);
    delay(10);
    I2C_out(0x39);
    delay(10);
    I2C_out(0x14);
    I2C_out(0x78);
    I2C_out(0x5E);
    I2C_out(0x6D);
    I2C_out(0x0C);
    I2C_out(0x01);
    I2C_out(0x06);
    delay(10);
    I2C_stop();

    CGRAM();            //define CGRAM

    i2c_start(Slave);  //.kbv
    //  I2C_Start();
    //  I2C_out(Slave);
    I2C_out(Comsend);
    I2C_out(0x39);
    I2C_out(0x01);      //go back Home
    I2C_Stop();
    delay(10);
}
/*****************************************************/
/*****************************************************/
int main(void)
{
    CLKCTRL_init();     //.kbv special clock code for Tiny817 to run at 5MHz
	i2c_init();         //.kbv
    //P1 = 0;  //.kbv  skip 8051 syntax
    //P3 = 0;
    while (1)                               //continue
    {
        init_LCD();
        delay(2);
        Show(text1);
        nextline();
        Show(text2);
        delay(2500);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I provided you with a ready to run AS7 project.   Unzip it into your AS7 source code folder.  e.g.

C:\Users\David Prentice\Documents\Atmel Studio\7.0\COG_t817

 

Then open the COG_t817.cproj from AS7

 

Build and run it on your XMINI board.

 

As I created the project "blind",   it might not actually work.   But it should Compile and upload to your XMINI.

 

Take notes as you go.   Write down each step on paper.   Number each step.

 

Then you can use your notes to compose your question(s) when problems arise.

 

David.

 

p.s.  if you have mucked up your COG_t817 project,   close project,   delete the directory.   Start again with unzipping,   open the freshly unzipped COG_t817.cproj.

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

mc0134 wrote:
was taken from a project he was working on, and so do I need to still have the pullup resistors?

 

I2C requires pull up resistors on both lines SDA/SCL, if they do not already exist on the mpu board or on the LCD board, then you must add them somewhere!

Note: they should exist on only one board, not both!

 

Jim

 

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

Hi David,

 

Thank you very much for helping me with this! What happens right now is that the code you provided (I followed your steps in your previous post) and it does indeed compile and I can load to the xplained mini 817 board without any error message. There's only one warning message about an unused variable, which is fine. ATTiny xplained mini board + LCD module powers up, and right now, I can see LCD module light on (assuming it's just because I merely powered it up)

 

I still have to really surf through your changes to make sure I fully understand what made it work vs what I attempted. But I will spend time later, probably a good afternoon after I can see characters on the LCD and I can change characters and all that good stuff!

 

Now, here is one thing that I was trying to ask earlier. My friend who gave me the LCD module said the board came from a different project he was working on, and he didn't need the LCD anymore so I could use it., but he showed me the schematic and you can see it below. In this case, do we still need the external 4.7k pullup resistors on the SDA and the SCL lines in order for the codes that I got from the zip file to (hopefully) work?

 

 

 

 

 

The LCD looks like this: 

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

Yes.   You should ALWAYS have external pullup resistors on SDA and SCL.  i.e. SDA3, SCL3 on your J6 connector.

The XMINI should be set for 3.3V.  The pullups pull up to VCC (3.3V).

 

Your schematic does not have pullups.   So you must invest $0.02 for two resistors.

 

Any value from 820R to 10k should be ok.   You must be able to steal them from an old transistor radio.   But life is easier with new components if you can afford $0.02.

 

David.

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

Thank you so much David. I learned quite a bit just from this thread alone..... Yes, the LCD display now works and I can see the text on it using your code that you ported to attiny817 as is. Now, I can spend the good afternoon reading the code to see why it works vs what was giving troubles earlier and play with stuff some more. By the way, what is ths .kbv thing really mean and is there anything I should pay attention to in order to use i2c/twi in future projects? It's some 8051 lingo and just wanted to know what that was about since you mentioned a few times that 'those' things got edited.

 

Just to ask about the i2c libraries that we used, we can do master to slave, and slave to master and pretty much everything that can be done with i2c/twi as supported by the ATTiny817?  Was just wondering if this fluery library can handle most situations of i2c/twi and we don't need to be worrying about having to play with registers and bits so much?

(hope my question makes sense to you).

 

Thx david!

Last Edited: Fri. Feb 9, 2018 - 09:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kbv is nothing more than a signature saying that I wus there.

 

It is a handy way to identify where I have made some changes.

Or if you see a library called xxxxxx_kbv it is likely to have come from me.

 

Nowadays you can publish a repository on GitHub.   Anyone can track edits and changes and the date and time they were made.

All the same.   Not everyone has a GitHub account.   Many people just want to install some code and never alter it.    Or if they do,   it is nothing more than changing a #define.

 

Regarding I2C / TWI on the Tiny817.

1.   TWI Master works fine with the Atmel interrupt code.   twi_master.h

2.   I can't get TWI Master to work reliably with simple polled code.

3.   TWI Slave works fine with interrupts.

 

Most Tinys in real life will be used as Slaves.    Only hobbyists would think of using a Tiny as a Master.

Regular Tinys like Tiny85, Tiny861 work well as Slaves with USI

Or Tinys like Tiny1634 work extremely well as Slaves with TWIS

 

In practice,   a Slave needs interrupts.   A Master can work just fine without interrupts.    Hence the popularity of Fleury with TWI on a Mega.    Or Fleury with bit-banged i2cmaster.S on Tiny, Mega, Xmega, ...

 

David.

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

Hi,

 

I'm back, and I see Fluery library seems to just be implementing single master to slave and it seems like twimaster.c isn't a good to attempt to use (if at all).

 

Few things wanted to ask about the I2C/TWI thinigs (whether or not I'll use it at this time and place is another story):

 

1. What if you wanted multimaster communication?

2. What about if you wanted to retrieve data from slave back to master or whatever else you may want to do that's coming from slave side back to master (I see some I2C slave library codes on github) ?

3. When would you really need to be playing with bits in TWI hardware that's on Tiny817 board or would fluery (or whatever other libraries that can be recommended do majority of the tasks you want whether it's master or slave)?

 

 I assume then Atmel start's twi_master library would be be able to handle both 1 and 2 asked above or what would be the recommended way to go for these things if atmel's library or fluery's library don't do those?

 

In regards to the LCD task that David was helping me with a day or two ago in above posts, I'm currently trying to figure out 3 things:

1. how to be able to get the LCD  to properly clear out when you want to completely start over (esp when you want to display new text, sometimes I see garbage characters left over from old attempts still on the LCD screen, if I don't have full 16 characters stored in the character array)

 

2. When I try to display text less than 16 characters (without using spaces to make the character array have 16 characters, or if I specify char array of a certain size like char arrray[8]), then I may see unwanted characters appear after the text I wanted.

 

3. I'm currently trying to also figure out how to get LCD to update text, i.e (let's say you have a rotary button of sorts, probably like a rotary switch or an encoder or something and you want to adjust say, voltage or temperature and you want the text on LCD to reflect what your current settings are. (for now, the tiny817 board that I'm using only has the LCD connected plus a user switch that's already part of the board given)

 

 

oh and @David, back in the code you provided, why you have the special clkctrl clock thing for the 5mHz ? When I remove those things, the program still does what I wanted without any issue. And I've attached my current version of the project in zip format, if there's any interest to look at it to see how it is now.

 

 

Well, that's all the stuff I got at the moment. Sorry for the long post, but hopefully my questions make sense, just trying to make sure I2C/TWI related things make sense to me anyway and if anybody would like to lend me a hand in terms of thoughts and ideas about good ways to tackle the stuff I noted above, I'd appreciate it (but I'm not saying anybody really has to do all the work for me), otherwise I'll keep on experimenting to get where I want to go. .

 

Regards,

mc0134

 

 

 

 

 

 

Attachment(s): 

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

I am a little suspicious of the NHD init_LCD() code.

Look at Figure 5. 2-line Interface protocol in the ST7032 datasheet.

Each command needs a control byte.  Only data can be sent in a block (after the appropriate control byte)

 

Anyway,  whatever LCD you have,   most libraries will end up with two primitives:

1.  lcd_command( cmd )     e.g. RS=0

2.  lcd_data( d )           e.g. RS=1

 

Then it is simply a question of building useful functions from these primitives.

 

e.g.  lcd_init()    e.g. init GPIO,  send special timed sequence of commands

e.g. lcd_putchar( c )

e.g. lcd_clear()

e.g. lcd_gotoxy( x, y )

 

Popular libraries provide these high-level functions for the user.

They probably don't give access to the primitive lcd_command() and lcd_data()

 

Note that lcd_clear() is nothing more than lcd_command(1)

Similarly,  library functions to show/hide cursor are nothing more than a lcd_command()

 

I see that you have attempted to convert the NHD code into a conventional arrangement.

 

I would rearrange the NHD code like this:

/*
 * COG_t817.c
 *
 * Created: 09-Feb-18 08:56:32
 * Author : David Prentice
 */

#include <avr/io.h>
#include "i2cmaster.h"    //.kbv Fleury header
#include <string.h>
#define code              //.kbv need special func for AVR __flash

extern int8_t CLKCTRL_init(void); //.kbv change running speed


code char text1[] = {
    "NEWHAVEN Display"
};
code char text2[] = {
    "2x16 LCD Module "
};
code char text3[] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
code char text4[] = {
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};

const char Slave = 0x7C;
const char Comsend = 0x00;
const char Datasend = 0x40;

/*****************************************************/
void delay(unsigned int n)              //Delay subroutine
{
    volatile unsigned int i, j;         //.kbv volatile
    for (i = 0; i < n; i++)
        for (j = 0; j < 350; j++)
        {
            ;
        }
}
/*****************************************************/
void lcd_command(unsigned char cmd)
{
    i2c_start(Slave);  //.kbv
    i2c_write(Comsend);
    i2c_write(cmd);
    i2c_stop();
}

void lcd_data(unsigned char dat)
{
    i2c_start(Slave);  //.kbv
    i2c_write(Datasend);
    i2c_write(dat);
    i2c_stop();
}

void lcd_block(char d[], char n)
{
    i2c_start(Slave);  //.kbv
    i2c_write(Datasend);
    for (unsigned char i = 0; i < n; i++) {
        i2c_write(d[i]);
    }
    i2c_stop();
}

#define lcd_string(s)    lcd_block(s, strlen(s))
#define Show(s)          lcd_block(s, 16)
#define lcd_gotoxy(x, y) lcd_command(0x40 + (x) + (y) * 0x80)
#define lcd_clear()      lcd_command(1)
#define lcd_home()       lcd_command(2)

void lcd_CGRAM (void)
{
    lcd_command(0x38);      //go to instructino table 0
    lcd_command(0x40);      //Set CGRAM address to 0x00
    lcd_data(0x00);      //write to first CGRAM address
    lcd_data(0x1E);
    lcd_data(0x18);
    lcd_data(0x14);
    lcd_data(0x12);
    lcd_data(0x01);
    lcd_data(0x00);
    lcd_data(0x00);      //8 bytes per character
}
/****************************************************
*           Initialization For ST7032i              *
*****************************************************/
void lcd_init()
{
    lcd_command(0x38);   //8 bit,N=1,5*7dot
    delay(10);
    lcd_command(0x39);   //8 bit,N=1,5*7dot,IS=1
    delay(10);
    lcd_command(0x14);   //Internal OSC frequency adjustment
    lcd_command(0x78);   //Contrast control
    lcd_command(0x5E);   //Power/ICON/Contrast control
    lcd_command(0x6D);   //Follower control ... datasheet says 0x6A
    lcd_command(0x0C);   //DISPLAY ON
    lcd_command(0x01);   //CLEAR DISPLAY
    lcd_command(0x06);   //ENTRY MODE SET
    delay(10);

    lcd_CGRAM();            //define CGRAM

    lcd_command(0x39);      //8 bit,N=1,5*7dot,IS=1
    lcd_command(0x01);      //go back Home
    delay(10);
}
/*****************************************************/
/*****************************************************/
int main(void)
{
    CLKCTRL_init();     //.kbv special clock code for Tiny817 to run at 5MHz
    lcd_init();         //.kbv
    while (1)                               //continue
    {
        lcd_clear();
        lcd_string(text1);
        lcd_gotoxy(0, 1);  //cursor to second line
        lcd_string(text2);
        delay(2500);

        lcd_clear();
        Show(text3);//first 16chars in font table(should show CGRAM chars two times)
        lcd_gotoxy(0, 1);  //cursor to second line
        Show(text4);
        delay(3000);
    }
}

Note that I would make a proper delay function with _delay_ms()

The Fleury i2cmaster.S has a hand-coded delay written for 4MHz.    That is why I ran the Tiny817 @ 5MHz.

 

If you are simply an I2C Master e.g. LCD,   24Cxxx eeprom,   DS1307 rtc, ...

There is little poi in using interrupts.

 

On the other hand,   if you are creating an intelligent Slave device it is a different situation.

 

David.

Last Edited: Sun. Feb 11, 2018 - 09:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thanks david.

 

Just one more thing. in the code you have

#define lcd_gotoxy(x, y) lcd_command(0x40 + (x) + (y) * 0x80)

 

just wondered how you determined this:

 lcd_command(0x40 + (x) + (y) * 0x80)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think that I may have typed this wrong. I am not at a PC. I will check the datasheet later.

 

Edit.  it should be:

#define lcd_gotoxy(x, y) lcd_command(0x80 + (x) + (y) * 0x40)

In practice,  you make these into functions rather than macros.

 

All of these controllers have single-byte commands.   Look at the Instructions on page 19 of the ST7032 datasheet.

These are exactly the same as HD44780, KS0066, ...

i.e. the "instruction" is in the most significant '1' bit.  And any parameters are in the less significant bits.

e.g. DISPLAY is bit#3 with parameters D, C, B in bits #2, #1, #0

 

lcd_gotoxy() uses the SET DDRAM ADDRESS command bit#7 with the actual address in bits #6..#0

 

There are some "Extended Instructions" on page 20.

 

David.

Last Edited: Mon. Feb 12, 2018 - 08:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see that you were doing 0x80 + (0x40 x row) + col to get the goto(x,y) to work, but how did they come up with this formula?

 

Next thing on my mind is .... now that the we are able to get the characters to display on the LCD mdule, I'm onto the other thing I wanted to do, which is to make the display adjustable like something you'd see on a radio or something, for example, where if the user adjusts a knob, then the the volume number will change to reflect what the current volume value is.

 

 

Regards, 

 

mc0134

Last Edited: Mon. Feb 12, 2018 - 05:36 PM