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);
}
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.
@+