/* version 2024-09-23 * released in the public domain * http://sedcore.eu.org/raspi/lcd */ /* gcc -Wall main.c -o lcd -lpigpio */ #include #include #include #include #include /* change those values to fit your settings */ /* broadcom numbering */ #define GPIO_RS 26 #define GPIO_E 19 #define GPIO_D4 13 #define GPIO_D5 6 #define GPIO_D6 5 #define GPIO_D7 11 /* register selection */ #define RS_CONTROL 0 #define RS_DATA 1 /* instruction table */ #define CLEAR_DISPLAY 0x01 #define RETURN_HOME 0x02 #define ENTRY_MODE_SET 0x04 #define DISPLAY_CONTROL 0x08 #define SHIFT_CONTROL 0x10 #define FUNCTION_SET 0x20 #define SET_CGRAM_ADDR 0x40 #define SET_DDRAM_ADDR 0x80 int init_gpio(void) { if (gpioSetMode(GPIO_RS, PI_OUTPUT) != 0) goto err; if (gpioSetMode(GPIO_E, PI_OUTPUT) != 0) goto err; if (gpioSetMode(GPIO_D4, PI_OUTPUT) != 0) goto err; if (gpioSetMode(GPIO_D5, PI_OUTPUT) != 0) goto err; if (gpioSetMode(GPIO_D6, PI_OUTPUT) != 0) goto err; if (gpioSetMode(GPIO_D7, PI_OUTPUT) != 0) goto err; return 0; err: printf("gpioSetMode failed\n"); return -1; } int gpio_end() { if (gpioWrite(GPIO_E, 0) != 0) goto err; if (gpioWrite(GPIO_RS, 0) != 0) goto err; if (gpioWrite(GPIO_D4, 0) != 0) goto err; if (gpioWrite(GPIO_D5, 0) != 0) goto err; if (gpioWrite(GPIO_D6, 0) != 0) goto err; if (gpioWrite(GPIO_D7, 0) != 0) goto err; return 0; err: printf("gpioWrite failed\n"); return -1; } void nsleep(int ns) { struct timespec t; t.tv_sec = ns / 1000000000; t.tv_nsec = ns % 1000000000; if (nanosleep(&t, NULL) == -1) printf("nanosleep failed, expect display errors\n"); } int put_nibble(int rs, int data) { int d4 = (data >> 0) & 1; int d5 = (data >> 1) & 1; int d6 = (data >> 2) & 1; int d7 = (data >> 3) & 1; if (gpioWrite(GPIO_RS, rs) != 0) goto err; if (gpioWrite(GPIO_D4, d4) != 0) goto err; if (gpioWrite(GPIO_D5, d5) != 0) goto err; if (gpioWrite(GPIO_D6, d6) != 0) goto err; if (gpioWrite(GPIO_D7, d7) != 0) goto err; /* tsu1 is 50 nanoseconds */ int tsu1 = 50; nsleep(tsu1); if (gpioWrite(GPIO_E, 1) != 0) goto err; /* tw is 230 nanoseconds, but let's use 250, tiny margin for why not */ int tw = 250; nsleep(tw); if (gpioWrite(GPIO_E, 0) != 0) goto err; /* tc is 500 nanoseconds, let's sleep what remains after tw */ int tc_remain = 500 - tw; nsleep(tc_remain); return 0; err: printf("put_nibble failed\n"); return -1; } int put_byte(int rs, int data) { if (put_nibble(rs, (data >> 4) & 0xf) != 0) goto err; if (put_nibble(rs, data & 0xf) != 0) goto err; return 0; err: printf("put_byte failed\n"); return -1; } int init_lcd(void) { int v; /* We don't know what's the state of the lcd at this point * (4 or 8 bits bus mode, if 4 bits: lcd waiting for bits d3-d0 or d7-d4). * So to be sure, let's go to 8 bits bus mode. * If the lcd is in 4 bits bus mode and waiting * for d3-d0, we need to write xxxx then 0x03 in d7-d4 then xxxx for d3-d0. * If 4 bits bus mode and waiting for d7-d4, we need to write 0x03 in d7-d4 * then xxxx for d3-d0. * If 8 bits mode, nothing is needed, but writing 0x03 (in d7-d4, which is * what we do; d3-d0 is not plugged, value is whatever, we don't care) is * ok. Writing it two or three times is also ok. * Mix all cases and you find out that writing 0x03 (in d7-d4) three times * will put the lcd in 8 bits bus mode whatever the starting state. */ if (put_nibble(RS_CONTROL, 0x03)) goto err; usleep(39); if (put_nibble(RS_CONTROL, 0x03)) goto err; usleep(39); if (put_nibble(RS_CONTROL, 0x03)) goto err; usleep(39); /* Now, we are in 8 bits bus mode, let's switch to 4 bits bus mode * by outputting 0x02 in d7-d4 (with only one write, so by using * put_nibble(), not put_byte()). */ if (put_nibble(RS_CONTROL, 0x02)) goto err; usleep(39); /* and now we're good, 4 bits bus mode, we can init */ /* function set */ int dl = 0; /* 0: 4 bits bus mode, 1: 8 bits bus mode */ int n = 1; /* 0: 1 line mode, 1: 2 lines modes */ int f = 0; /* 0: 5x8 dot format, 1 : 5x11 dot format */ v = FUNCTION_SET | (dl << 4) | (n << 3) | (f << 2); if (put_byte(RS_CONTROL, v)) goto err; usleep(39); /* display on/off control */ int d = 1; /* display on/off */ int c = 0; /* cursor on/off */ int b = 0; /* blink on/off */ v = DISPLAY_CONTROL | (d << 2) | (c << 1) | b; if (put_byte(RS_CONTROL, v)) goto err; usleep(39); /* display clear */ if (put_byte(RS_CONTROL, CLEAR_DISPLAY)) goto err; usleep(1530); /* entry set mode */ int id = 1; /* 0: decrement mode, 1: increment mode */ int sh = 0; /* 0: entire shift off, 1: entire shift on */ v = ENTRY_MODE_SET | (id << 1) | sh; if (put_byte(RS_CONTROL, v)) goto err; usleep(39); /* init sequence ends here */ /* let's return home */ if (put_byte(RS_CONTROL, RETURN_HOME)) goto err; usleep(1530); return 0; err: printf("init_lcd failed\n"); return -1; } void clear_screen(void) { /* display clear */ if (put_byte(RS_CONTROL, CLEAR_DISPLAY)) goto err; usleep(1530); /* return home */ if (put_byte(RS_CONTROL, RETURN_HOME)) goto err; usleep(1530); return; err: printf("clear_string failed\n"); } void put_string(char *s, int l) { /* go to beginning of line l (l=0 for first line, l=1 for second line) */ int address = l * 0x40; int v = SET_DDRAM_ADDR | address; if (put_byte(RS_CONTROL, v)) goto err; usleep(39); /* no check on string size, caller's business */ while (*s) { if (put_byte(RS_DATA, *s)) goto err; usleep(43); s++; } return; err: printf("put_string failed\n"); } void usage(void) { printf("[options] [string]\n"); printf("options:\n"); printf(" -clear\n"); printf(" clear screen before anything else\n"); printf(" -l \n"); printf(" choose line, 1 or 2, default 1\n"); printf(" -no-init\n"); printf(" don't init LCD (for eg. it was already done)\n"); exit(1); } int main(int n, char **v) { int r; int line = 0; int do_clear = 0; int do_init = 1; char *s = NULL; for (int i = 1; i < n; i++) { if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage(); if (!strcmp(v[i], "-clear")) { do_clear = 1; continue; } if (!strcmp(v[i], "-l")) { if (i >= n-2) usage(); line = atoi(v[++i]) - 1; continue; } if (!strcmp(v[i], "-no-init")) { do_init = 0; continue; } if (s) usage(); s = v[i]; } if (line != 0 && line != 1) usage(); r = gpioInitialise(); if (r < 0) { printf("gpio lib init error\n"); exit(1); } if (init_gpio() != 0) goto end; if (do_init) { if (init_lcd() != 0) goto end; } if (do_clear) clear_screen(); if (s != NULL) put_string(s, line); /* clear GPIO output pins (set them to 0) */ gpio_end(); end: gpioTerminate(); return 0; }