Afficheur Joy-It I2C & LCD 20x04

Proposer ou rechercher un tutoriel concernant le Raspberry Pi

Modérateur : Francois

Répondre
Artemus24
Raspinaute
Messages : 1077
Enregistré le : ven. 15 sept. 2017 19:15

Afficheur Joy-It I2C & LCD 20x04

Message par Artemus24 » mer. 6 nov. 2019 08:49

Salut à tous.

Je me suis procuré cet afficheur chez GoTronic :
--> https://www.gotronic.fr/art-afficheur-l ... -25649.htm

Le but de mon projet était de créer un programme qui réalise la même chose que la version Python ou encore Liquid_Crystal_I2C.

Voici le programme :

Code : Tout sélectionner

#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>				/* Needed for struct timespec */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

/******************************************/
/*                                        */
/*     I2c : Inter-Integrated Circuit     */
/*                                        */
/******************************************/

#define I2C_SMBUS_READ		1
#define I2C_SMBUS_WRITE		0

/*---------------------------*/
/*     Private Variables     */
/*---------------------------*/

int i2c_handle;

/*--------------------------*/
/*     Function "delay"     */
/*--------------------------*/

void i2c_delay(long _milliseconds)
{
	struct timespec delay;

	if (_milliseconds > 999)
	{
		delay.tv_sec  = (int)(_milliseconds / 1000);
		delay.tv_nsec = (_milliseconds - ((long) delay.tv_sec * 1000)) * 1000000;
	}
	else
	{
		delay.tv_sec  = (int) 0;
		delay.tv_nsec = ((long) _milliseconds * 1000000);
	}

	if (nanosleep(&delay,NULL) < 0)
	{
		fprintf(stderr,"Fail Delay : %s\n", strerror(errno));
		fflush(stderr);
		exit(EXIT_FAILURE);
	}
}

/*-------------------------*/
/*     Function "open"     */
/*-------------------------*/

void i2c_open(int _bus)
{
	char i2c_device[256];

	if (_bus < 0 || _bus > 1)
	{
		fprintf(stderr,"Bus I2C = %d (Bus I2C for Raspberry is 0 or 1)\n", _bus);
		fflush(stderr);
		exit(EXIT_FAILURE);
	}

	sprintf(i2c_device,"/dev/i2c-%d",_bus);

	i2c_handle = open(i2c_device, O_RDWR);
	if (i2c_handle < 0)
	{
		fprintf(stderr,"Failcd to open I2C Bus !\n");
		fflush(stderr);
		exit(EXIT_FAILURE);
	}
}

/*--------------------------*/
/*     Function "close"     */
/*--------------------------*/

void i2c_close(void)
{
	if (close(i2c_handle) < 0)
	{
		fprintf(stderr,"Failcd to close I2C Bus : %s\n", strerror(errno));
		fflush(stderr);
		exit(EXIT_FAILURE);
	}
}

/*----------------------------------*/
/*     Function "slave_Address"     */
/*----------------------------------*/

void i2c_slaveAddress(int _slave)
{
	if (_slave < 0x03 || _slave > 0x77)
	{
		fprintf(stderr,"Wrong slave address : %d\n", _slave);
		fflush(stderr);
		i2c_close();
		exit(EXIT_FAILURE);
	}

	if (ioctl(i2c_handle,I2C_SLAVE,_slave) < 0)
	{
		fprintf(stderr,"Ioctl Error : %s\n", strerror(errno));
		fprintf(stderr,"Failcd to Acquire Bus I2C Access !\n");
		fflush(stderr);
		i2c_close();
		exit(EXIT_FAILURE);
	}
}

/*--------------------------*/
/*     Function "write"     */
/*--------------------------*/

void i2c_write(unsigned char _cmd)
{
	struct	i2c_smbus_ioctl_data	args;

	args.read_write		= I2C_SMBUS_WRITE;
	args.command		= _cmd;
	args.size		= I2C_SMBUS_BYTE;
	args.data		= NULL;

	if(ioctl(i2c_handle,I2C_SMBUS,&args) < 0)
	{
		fprintf(stderr,"Ioctl Error : %s\n", strerror(errno));
		fprintf(stderr,"Unable to write I2C data\n");
		fflush(stderr);
		exit(EXIT_FAILURE);
	}
}

/****************************************/
/*                                      */
/*     lcd : Liquid Crystal Display     */
/*                                      */
/****************************************/

/*-----------------------------*/
/*     Diacritics in French    */
/*-----------------------------*/

char fontdata [56][8]= {
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},	/* nothing */

	{0x02,0x04,0x0E,0x11,0x1F,0x11,0x11,0x00},	/*  1 : A x*/
	{0x08,0x04,0x0E,0x11,0x1F,0x11,0x11,0x00},	/*  2 : À x*/
	{0x04,0x0A,0x0E,0x11,0x1F,0x11,0x11,0x00},	/*  3 : Â x*/ 
	{0x0A,0x00,0x0E,0x11,0x1F,0x11,0x11,0x00},	/*  4 : Ä x*/

	{0x02,0x04,0x0E,0x01,0x0F,0x11,0x0F,0x00},	/*  5 : a x*/
	{0x08,0x04,0x0E,0x01,0x0F,0x11,0x0F,0x00},	/*  6 : à x*/
	{0x04,0x0A,0x0E,0x01,0x0F,0x11,0x0F,0x00},	/*  7 : â x*/
	{0x0A,0x00,0x0E,0x01,0x0F,0x11,0x0F,0x00},	/*  8 : ä x*/

	{0x00,0x0E,0x10,0x11,0x0E,0x04,0x0C,0x00},	/*  9 : ç x*/

	{0x02,0x04,0x1F,0x10,0x1F,0x10,0x1F,0x00},	/* 10 : E x*/
	{0x08,0x04,0x1F,0x10,0x1F,0x10,0x1F,0x00},	/* 11 : È x*/
	{0x04,0x0A,0x1F,0x10,0x1F,0x10,0x1F,0x00},	/* 12 : Ê x*/
	{0x0A,0x00,0x1F,0x10,0x1F,0x10,0x1F,0x00},	/* 13 : Ë x*/

	{0x02,0x04,0x0E,0x11,0x1F,0x10,0x0E,0x00},	/* 14 : é x*/
	{0x08,0x04,0x0E,0x11,0x1F,0x10,0x0E,0x00},	/* 15 : è x*/
	{0x04,0x0A,0x0E,0x11,0x1F,0x10,0x0E,0x00},	/* 16 : ê x*/
	{0x0A,0x00,0x0E,0x11,0x1F,0x10,0x0E,0x00},	/* 17 : ë x*/

	{0x02,0x04,0x0E,0x04,0x04,0x04,0x0E,0x00},	/* 18 : I x*/
	{0x08,0x04,0x0E,0x04,0x04,0x04,0x0E,0x00},	/* 19 : Ì x*/
	{0x04,0x0A,0x0E,0x04,0x04,0x04,0x0E,0x00},	/* 20 : Î x*/
	{0x0A,0x00,0x0E,0x04,0x04,0x04,0x0E,0x00},	/* 21 : Ï x*/

	{0x02,0x04,0x00,0x0C,0x04,0x04,0x0E,0x00},	/* 22 : i x*/
	{0x08,0x04,0x00,0x0C,0x04,0x04,0x0E,0x00},	/* 23 : ì x*/
	{0x04,0x0A,0x00,0x0C,0x04,0x04,0x0E,0x00},	/* 24 : î x*/
	{0x0A,0x00,0x00,0x0C,0x04,0x04,0x0E,0x00},	/* 25 : ï x*/

	{0x02,0x04,0x0E,0x11,0x11,0x11,0x0E,0x00},	/* 26 : O x*/
	{0x08,0x04,0x0E,0x11,0x11,0x11,0x0E,0x00},	/* 27 : Ò x*/
	{0x04,0x0A,0x0E,0x11,0x11,0x11,0x0E,0x00},	/* 28 : Ô x*/
	{0x0A,0x00,0x0E,0x11,0x11,0x11,0x0E,0x00},	/* 29 : Ö x*/

	{0x08,0x04,0x00,0x0E,0x11,0x11,0x0E,0x00},	/* 30 : o x*/
	{0x02,0x04,0x00,0x0E,0x11,0x11,0x0E,0x00},	/* 31 : ò x*/
	{0x04,0x0A,0x00,0x0E,0x11,0x11,0x0E,0x00},	/* 32 : ô x*/
	{0x0A,0x00,0x00,0x0E,0x11,0x11,0x0E,0x00},	/* 33 : ö x*/

	{0x02,0x04,0x11,0x11,0x11,0x11,0x0E,0x00},	/* 34 : U */
	{0x08,0x04,0x11,0x11,0x11,0x11,0x0E,0x00},	/* 35 : Ù */
	{0x04,0x0A,0x11,0x11,0x11,0x11,0x0E,0x00},	/* 36 : Û */
	{0x0A,0x00,0x11,0x11,0x11,0x11,0x0E,0x00},	/* 37 : Ü */

	{0x02,0x04,0x11,0x11,0x11,0x13,0x0D,0x00},	/* 38 : u */
	{0x08,0x04,0x11,0x11,0x11,0x13,0x0D,0x00},	/* 39 : ù */
	{0x04,0x0A,0x11,0x11,0x11,0x13,0x0D,0x00},	/* 40 : û */
	{0x0A,0x00,0x11,0x11,0x11,0x13,0x0D,0x00},	/* 41 : ü */

	{0x0A,0x00,0x11,0x11,0x0F,0x01,0x0E,0x00},	/* 42 : ÿ */

	{0x04,0x0E,0x0E,0x0E,0x1F,0x00,0x04,0x00},	/* 43 : Bell  */
	{0x02,0x03,0x02,0x0E,0x1E,0x0C,0x00,0x00},	/* 44 : Note  */
	{0x00,0x0E,0x15,0x17,0x11,0x0E,0x00,0x00},	/* 45 : Clock */
	{0x00,0x0A,0x1F,0x1F,0x0E,0x04,0x00,0x00},	/* 46 : Heart */
	{0x00,0x0C,0x1D,0x0F,0x0F,0x06,0x00,0x00},	/* 47 : Duck  */
	{0x00,0x01,0x03,0x16,0x1C,0x08,0x00,0x00},	/* 48 : Check */
	{0x00,0x1B,0x0E,0x04,0x0E,0x1B,0x00,0x00},	/* 49 : Cross */

	{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F},	/* 50,51,52 Hight Right Arrow */
	{0x00,0x00,0x04,0x06,0x07,0x07,0x1F,0x1F},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x18},
	{0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00},	/* 53,54,55 Low   Right Arrow */
	{0x1F,0x1F,0x07,0x07,0x06,0x04,0x00,0x00},
	{0x18,0x10,0x00,0x00,0x00,0x00,0x00,0x00}
};

/*------------------*/
/*     Commands     */
/*------------------*/

#define LCD_BACKLIGHT_OFF		0x00
#define LCD_CLEAR_DISPLAY		0x01
#define LCD_RETURN_HOME			0x02
#define LCD_ENTRY_MODE_SET		0x04
#define LCD_DISPLAY_CONTROL		0x08
#define LCD_CURSOR_OR_DISPLAY_SHIFT	0x10
#define LCD_FUNCTION_SET		0x20
#define LCD_SET_CG_RAM_ADDR		0x40
#define LCD_SET_DD_RAM_ADDR		0x80

/*----------------------------*/
/* Flags for "entry mode set" */
/*----------------------------*/

#define LCD_ENTRY_INCREMENT		0x02
#define LCD_ENTRY_DECREMENT		0x00

#define LCD_ENTRY_SHIFT			0x01
#define LCD_ENTRY_NO_SHIFT		0x00

/*------------------------------------*/
/* Flags for "display on/off control" */
/*------------------------------------*/

#define LCD_DISPLAY_ON			0x04
#define LCD_DISPLAY_OFF			0x00

#define LCD_CURSOR_ON			0x02
#define LCD_CURSOR_OFF			0x00

#define LCD_BLINK_ON			0x01
#define LCD_BLINK_OFF			0x00

/*------------------------------------*/
/* Flags for "cursor or display shift */
/*------------------------------------*/

#define LCD_DISPLAY_SHIFT		0x08
#define LCD_CURSOR_MOVE			0x00

#define LCD_SHIFT_TO_RIGHT		0x04
#define LCD_SHIFT_TO_LEFT		0x00

/*--------------------------*/
/* Flags for "function set" */
/*--------------------------*/

#define LCD_EIGHT_BITS			0x10
#define LCD_FOUR_BITS			0x00

#define LCD_2LINES			0x08
#define LCD_1LINE			0x00

#define LCD_5x10DOTS			0x04
#define LCD_5x8DOTS			0x00

/*----------------------*/
/*     Others Flags     */
/*----------------------*/

#define LCD_E				0x04		/* Enable bit          */
#define LCD_RW				0x02		/* Read Write bit      */
#define LCD_RS				0x01		/* Register select bit */

/*-------------------------------------*/
/*      Starting the PCF7485T chip     */
/*-------------------------------------*/

void lcd_start(int _bus, unsigned int _slave)
{
	i2c_open(_bus);
	i2c_slaveAddress(_slave);
}

/*-----------------------------------*/
/*      Finish the PCF7485T chip     */
/*-----------------------------------*/

void lcd_finish(void)
{
	i2c_close();
}

/*---------------------------------*/
/*     Write in Four bits mode     */
/*---------------------------------*/

void lcd_four_bits(unsigned char _data, unsigned char _mode)
{
	i2c_write(  _data           | _mode | LCD_DISPLAY_CONTROL);
	i2c_write( (_data |  LCD_E) | _mode | LCD_DISPLAY_CONTROL);
	i2c_write(((_data & ~LCD_E) | _mode | LCD_DISPLAY_CONTROL));
	i2c_delay(1);
}

/*-------------------------*/
/*     Write a Command     */
/*-------------------------*/

void lcd_command(unsigned char _data)
{
//	printf("--> %02X\n", _data);

	lcd_four_bits( (_data       & 0xF0), 0x00);
	lcd_four_bits(((_data << 4) & 0xF0), 0x00);
}

/*---------------------------*/
/*     Write a character     */
/*---------------------------*/

void lcd_char(unsigned char _data)
{
//	printf("==> %02X\n", _data);

	lcd_four_bits( (_data       & 0xF0), LCD_RS);
	lcd_four_bits(((_data << 4) & 0xF0), LCD_RS);
}

/*---------------------*/
/*      Initialize     */
/*---------------------*/

void lcd_init(void)
{
	i2c_delay(15);
	lcd_command(LCD_FUNCTION_SET    | LCD_EIGHT_BITS      | LCD_2LINES           | LCD_5x8DOTS);
	i2c_delay(5);
	lcd_command(LCD_FUNCTION_SET    | LCD_EIGHT_BITS      | LCD_2LINES           | LCD_5x8DOTS);
	i2c_delay(5);
	lcd_command(LCD_FUNCTION_SET    | LCD_EIGHT_BITS      | LCD_2LINES           | LCD_5x8DOTS);
	i2c_delay(1);

	lcd_command(0x02);	/* Four Bits Mode */

	lcd_command(LCD_FUNCTION_SET    | LCD_FOUR_BITS       | LCD_2LINES           | LCD_5x8DOTS);
	lcd_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON      | LCD_CURSOR_OFF       | LCD_BLINK_OFF);
	lcd_command(LCD_CLEAR_DISPLAY);
	lcd_command(LCD_ENTRY_MODE_SET  | LCD_ENTRY_INCREMENT | LCD_ENTRY_NO_SHIFT);
	lcd_command(LCD_RETURN_HOME);
}

/*--------------------------*/
/*     Backlight Switch     */
/*--------------------------*/

void lcd_backlight(char *_state)
{
	if (strcmp(_state,"on") == 0)
		lcd_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON  | LCD_CURSOR_OFF | LCD_BLINK_OFF);
	else
	{
		i2c_write(LCD_BACKLIGHT_OFF);
		i2c_delay(1);
	}
}

/*------------------------*/
/*      Clear Display     */
/*------------------------*/

void lcd_clear(void)
{
	lcd_command(LCD_CLEAR_DISPLAY);
	lcd_command(LCD_RETURN_HOME);
}

/*--------------------------------------------*/
/*                                            */
/*     Position the Cursor in the Display     */
/*                                            */
/*--------------------------------------------*/

void lcd_setcursor(int _row, int _col)
{
	unsigned int pos[04] = {0x00,0x40,0x14,0x54};

	if (_row<=1)	_row=1;
	if (_row>=4)	_row=4;
	if (_col>=19)	_col=19;

	lcd_command(LCD_SET_DD_RAM_ADDR | (pos[_row-1] + _col));
}

/*--------------------------*/
/*     Display a String     */
/*--------------------------*/

void lcd_string(char *_array, int _qte)
{
	while (_qte--)
		lcd_char(*_array++);
}

/*------------------------*/
/*     Display On/Off     */
/*------------------------*/

void lcd_display(char *_state)
{
	printf("--> Display : %s\n", _state);

	if (strcmp(_state,"on") == 0)
		lcd_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON  | LCD_CURSOR_OFF | LCD_BLINK_OFF);
	else	lcd_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_OFF | LCD_CURSOR_OFF | LCD_BLINK_OFF);
}

/*------------------------*/
/*     Blink a String     */
/*------------------------*/

void lcd_blink(int _row, int _col, char *_array, int _qte, int _loop)
{
	while (_loop--)
	{
		lcd_setcursor(_row, _col);

		for (int i=0; i<_qte; i++)
			lcd_char(' ');

		i2c_delay(250);

		lcd_setcursor(_row, _col);
		lcd_string(_array, _qte);

		i2c_delay(250);
	}
}

/*----------------*/
/*     Scroll     */
/*----------------*/

void lcd_scroll(char *_state, int _qte)
{
	for (int i=0; i<_qte; i++)
	{
		if (strcmp(_state, "left") == 0)
			lcd_command(LCD_CURSOR_OR_DISPLAY_SHIFT | LCD_DISPLAY_SHIFT | LCD_SHIFT_TO_LEFT);
		else	lcd_command(LCD_CURSOR_OR_DISPLAY_SHIFT | LCD_DISPLAY_SHIFT | LCD_SHIFT_TO_RIGHT);

		i2c_delay(250);
	}
}

/*-----------------------------------*/
/*     Loading Custom Characters     */
/*-----------------------------------*/

void lcd_load_custom_chars(int _arg,...)
{
	int	i, j;
	char	*pos;
	va_list	ap;

	va_start(ap, _arg);

	lcd_command(LCD_SET_CG_RAM_ADDR);

	for (i=0; (_arg != 0) && (i<8); i++)
	{
		pos=fontdata[_arg];

		for (j=0; j<8; j++)
			lcd_char(*pos++);

		_arg=va_arg(ap, int);
	}

	va_end(ap);

	for (; i<8; i++)
	{
		pos=fontdata[0];

		for (j=0; j<8; j++)
			lcd_char(*pos++);
	}
}

/**************************/
/*                        */
/*     Main Procedure     */
/*                        */
/**************************/

int main(void)
{
	char line[2][9] = {{0x02,0x03,0x04,' ',0x00,'t',0x00,' ', ' '},
			   {0x05,0x06,0x07,' ','f', 'o','r', 0x01,'t'}};

	lcd_start(1,0x27);
	lcd_backlight("on");
	lcd_init();

	lcd_load_custom_chars(14,16,50,51,52,53,54,55);

	lcd_setcursor(1,3);
	lcd_string(line[0],9);

	lcd_setcursor(2,3);
	lcd_string(line[1],9);

	lcd_blink(2, 7, line[1]+4, 5, 10);

	lcd_scroll("right",40);
	lcd_scroll("left",40);

	printf("Press a key to finish\n");
	getchar();

	lcd_backlight("off");
	lcd_finish();

	exit(EXIT_SUCCESS);
}
J'ai ajouté un jeu de caractères accentués. Lors d'un affichage, je suis limité à huit nouveaux caractères.
Mais bon, on peut mettre plusieurs pages et de ce fait utiliser plusieurs jeux de caractères différents.

Volontairement, je n'ai pas créé de library, ni introduit les thread, entre autre l'usage d'un timer.
De ce fait, l'écriture des fonctions "blink()" et "scroll()" seraient différentes.

En l'état, il fonctionne, mais il y a plusieurs choses que je n'ai pas compris :

a) J'aurai aimé utilisé les fonctions SMBus, autre que celle que j'ai utilisé.
Cette fonction correspond à une commande et envoie qu'un seule octet.
Dois-je comprendre que je suis limité à ce choix par le PCF8574 ?

b) je ne suis pas arrivé à faire fonctionner l'afficheur en mode 8 bits. Pourquoi ?
Je n'ai pas trouvé de réponse à ce sujet. Il est possible que ce soit le PCF8574 qui me limite dans mes choix.

c) je ne suis pas arrivé à afficher des caractères en mode 5x10 dots. Pourquoi ? Même sous-entendu.
Alors que je suis arrivé à afficher le curseur dans ce format là.

d) trouver la bonne documentation sur cet afficheur HD44780 et ce PACKBACK PCF8574.
J'ai trouvé de la documentation sur le net, mais il y a des différences qui me font penser que ce n'est pas la bonne version.

@+
RPI4B/8GB + Argon FanHAt
Rpi3A+, Rpi3B+
RPi 2B + Joy-It I2C Serial 20x4 2004 LCD Module
RPi 2B + PIM273 Unicorn HAT HD 16x16 Leds RGB
RPi0v1.3, RPi0W + LibreElec/Kodi, Rpi0WH + Tuner TV HAT
NodeMCU ESP32

Répondre

Retourner vers « Tutoriels »