Create LCD driver
This commit is contained in:
parent
99e3a82c4c
commit
cc7992bd93
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.o
|
||||||
|
rpi4b-temp-humidity
|
13
Makefile
13
Makefile
@ -2,15 +2,20 @@ CC=clang
|
|||||||
CFLAGS=-std=c99
|
CFLAGS=-std=c99
|
||||||
LD=clang
|
LD=clang
|
||||||
LDFLAGS=-lgpio
|
LDFLAGS=-lgpio
|
||||||
OBJS=main.o lcd.o
|
OBJS=main.o util.o lcd.o
|
||||||
|
OUTFILE=rpi4b-temp-humidity
|
||||||
|
|
||||||
|
${OUTFILE}: ${OBJS}
|
||||||
|
${LD} ${LDFLAGS} -o $@ ${OBJS}
|
||||||
|
|
||||||
main.o lcd.o: lcd.h
|
main.o lcd.o: lcd.h
|
||||||
|
main.o util.o lcd.o: util.h
|
||||||
rpi4b-temp-himidity: ${OBJS}
|
|
||||||
${LD} ${LDFLAGS} -o $@ ${OBJS}
|
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
${CC} ${CFLAGS} -c $<
|
${CC} ${CFLAGS} -c $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ${OUTFILE} ${OBJS}
|
||||||
|
|
||||||
.SUFFIXES: .c .o
|
.SUFFIXES: .c .o
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
140
lcd.c
Normal file
140
lcd.c
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* lcd.c - Subroutines for communicating with HD44780U based LCD panels
|
||||||
|
* Copyright (C) 2024 Alexander Rosenberg
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or (at your option) any later
|
||||||
|
* version. See the LICENSE file for more information.
|
||||||
|
*
|
||||||
|
* This file was created with help from the document located here:
|
||||||
|
* https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
|
||||||
|
*/
|
||||||
|
#include "lcd.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static void lcd_set_read(LCD *lcd, bool is_read, bool force) {
|
||||||
|
if (force || lcd->is_read != is_read) {
|
||||||
|
gpio_config_t cfg = {.g_flags = is_read ? GPIO_PIN_INPUT
|
||||||
|
: GPIO_PIN_OUTPUT};
|
||||||
|
cfg.g_pin = lcd->d0;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
cfg.g_pin = lcd->d1;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
cfg.g_pin = lcd->d2;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
cfg.g_pin = lcd->d3;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
cfg.g_pin = lcd->d4;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
cfg.g_pin = lcd->d5;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
cfg.g_pin = lcd->d6;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
cfg.g_pin = lcd->d7;
|
||||||
|
gpio_pin_set_flags(lcd->handle, &cfg);
|
||||||
|
lcd->is_read = is_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_pulse_enable(LCD *lcd) {
|
||||||
|
gpio_pin_high(lcd->handle, lcd->en);
|
||||||
|
usleep(1); // min of 1000ns
|
||||||
|
gpio_pin_low(lcd->handle, lcd->en);
|
||||||
|
}
|
||||||
|
|
||||||
|
LCD *lcd_open(gpio_handle_t handle, int rs, int rw, int en, int d0, int d1,
|
||||||
|
int d2, int d3, int d4, int d5, int d6, int d7) {
|
||||||
|
LCD *lcd = malloc_checked(sizeof(LCD));
|
||||||
|
lcd->handle = handle;
|
||||||
|
lcd->rs = rs;
|
||||||
|
lcd->rw = rw;
|
||||||
|
lcd->en = en;
|
||||||
|
lcd->d0 = d0;
|
||||||
|
lcd->d1 = d1;
|
||||||
|
lcd->d2 = d2;
|
||||||
|
lcd->d3 = d3;
|
||||||
|
lcd->d4 = d4;
|
||||||
|
lcd->d5 = d5;
|
||||||
|
lcd->d6 = d6;
|
||||||
|
lcd->d7 = d7;
|
||||||
|
lcd_set_read(lcd, false, true);
|
||||||
|
lcd_clear(lcd);
|
||||||
|
lcd_call(lcd, 0, 0, 0, 0, 1, 1, 1, 0, 0); // 5x8 font, 2 lines, 8bit
|
||||||
|
lcd_entry_mode(lcd, LCD_INCREMENT, LCD_CURSOR_MOVE);
|
||||||
|
lcd_display_control(lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, LCD_DISPLAY_ON);
|
||||||
|
return lcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_close(LCD *lcd) {
|
||||||
|
free(lcd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_call(LCD *lcd, int rs, int d0, int d1, int d2, int d3, int d4, int d5,
|
||||||
|
int d6, int d7) {
|
||||||
|
while(lcd_is_busy(lcd)) {
|
||||||
|
usleep(5);
|
||||||
|
}
|
||||||
|
lcd_set_read(lcd, false, false);
|
||||||
|
gpio_pin_low(lcd->handle, lcd->en);
|
||||||
|
gpio_pin_low(lcd->handle, lcd->rw);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->rs, rs);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d0, d0);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d1, d1);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d2, d2);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d3, d3);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d4, d4);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d5, d5);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d6, d6);
|
||||||
|
gpio_pin_set(lcd->handle, lcd->d7, d7);
|
||||||
|
lcd_pulse_enable(lcd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_write_char(LCD *lcd, char c) {
|
||||||
|
lcd_call(lcd, 1, c & 1, (c >> 1) & 1, (c >> 2) & 1, (c >> 3) & 1, (c >> 4) & 1,
|
||||||
|
(c >> 5) & 1, (c >> 6) & 1, (c >> 7) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t lcd_write_string(LCD *lcd, const char *str) {
|
||||||
|
size_t count;
|
||||||
|
for (count = 0; *str; ++str, ++count) {
|
||||||
|
lcd_write_char(lcd, *str);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_move_to(LCD *lcd, int line, int col) {
|
||||||
|
lcd_call(lcd, 0, col & 1, (col >> 1) & 1, (col >> 2) & 1,
|
||||||
|
(col >> 3) & 1, (col >> 4) & 1, (col >> 5) & 1, line, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_clear(LCD *lcd) {
|
||||||
|
lcd_call(lcd, 0, 1, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_display_control(LCD *lcd, int b, int c, int d) {
|
||||||
|
lcd_call(lcd, 0, b, c, d, 1, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_entry_mode(LCD *lcd, int id, int s) {
|
||||||
|
lcd_call(lcd, 0, s, id, 1, 0, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_cursor_shift(LCD *lcd, int sc, int rl) {
|
||||||
|
lcd_call(lcd, 0, 0, 0, rl, sc, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lcd_is_busy(LCD *lcd) {
|
||||||
|
lcd_set_read(lcd, true, false);
|
||||||
|
gpio_pin_low(lcd->handle, lcd->rs);
|
||||||
|
gpio_pin_high(lcd->handle, lcd->rw);
|
||||||
|
gpio_pin_high(lcd->handle, lcd->en);
|
||||||
|
usleep(1);
|
||||||
|
gpio_value_t busy = gpio_pin_get(lcd->handle, lcd->d7);
|
||||||
|
gpio_pin_low(lcd->handle, lcd->en);
|
||||||
|
usleep(1);
|
||||||
|
return busy;
|
||||||
|
|
||||||
|
}
|
107
lcd.h
Normal file
107
lcd.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* lcd.h - Subroutines for communicating with HD44780U based LCD panels
|
||||||
|
* Copyright (C) 2024 Alexander Rosenberg
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or (at your option) any later
|
||||||
|
* version. See the LICENSE file for more information.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_LCD_H
|
||||||
|
#define INCLUDED_LCD_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <libgpio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gpio_handle_t handle;
|
||||||
|
// pin numbers
|
||||||
|
int rs; // register select
|
||||||
|
int rw; // read-write
|
||||||
|
int en; // enable
|
||||||
|
int d0;
|
||||||
|
int d1;
|
||||||
|
int d2;
|
||||||
|
int d3;
|
||||||
|
int d4;
|
||||||
|
int d5;
|
||||||
|
int d6;
|
||||||
|
int d7;
|
||||||
|
|
||||||
|
// internal use
|
||||||
|
bool is_read;
|
||||||
|
} LCD;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate and initialize a new LCD object and return a pointer to it. HANDLE
|
||||||
|
* is a gpio_handle_t, each of the other arguments corresponds to the GPIO pin
|
||||||
|
* number that the given controller pin is connected to: "register select",
|
||||||
|
* "read-write", "enable", and data pins 0-7.
|
||||||
|
* Return: the newly created object.
|
||||||
|
*/
|
||||||
|
LCD *lcd_open(gpio_handle_t handle, int rs, int rw, int en, int d0, int d1,
|
||||||
|
int d2, int d3, int d4, int d5, int d6, int d7);
|
||||||
|
/*
|
||||||
|
* Close LCD by freeing any resources associated with it.
|
||||||
|
*/
|
||||||
|
void lcd_close(LCD *lcd);
|
||||||
|
/*
|
||||||
|
* Call a single instruction on LCD. Each argument is the value of that pin.
|
||||||
|
*/
|
||||||
|
void lcd_call(LCD *lcd, int rs, int d0, int d1, int d2, int d3, int d4, int d5,
|
||||||
|
int d6, int d7);
|
||||||
|
/*
|
||||||
|
* Write character C to LCD.
|
||||||
|
*/
|
||||||
|
void lcd_write_char(LCD *lcd, char c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write STR to LCD.
|
||||||
|
* Return: the number of characters written.
|
||||||
|
*/
|
||||||
|
size_t lcd_write_string(LCD *lcd, const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move the cursor to COL on LINE, which both start from 0.
|
||||||
|
*/
|
||||||
|
void lcd_move_to(LCD *lcd, int line, int col);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear LCD and return the cursor to the top right.
|
||||||
|
*/
|
||||||
|
void lcd_clear(LCD *lcd);
|
||||||
|
|
||||||
|
#define LCD_DISPLAY_ON 1
|
||||||
|
#define LCD_DISPLAY_OFF 0
|
||||||
|
#define LCD_CURSOR_ON 1
|
||||||
|
#define LCD_CURSOR_OFF 0
|
||||||
|
#define LCD_CURSOR_BLINK 1
|
||||||
|
#define LCD_CURSOR_NO_BLINK 0
|
||||||
|
/*
|
||||||
|
* Control B, cursor blink, C, cursor visibility, and D, display power for LCD.
|
||||||
|
*/
|
||||||
|
void lcd_display_control(LCD *lcd, int b, int c, int d);
|
||||||
|
|
||||||
|
#define LCD_INCREMENT 1
|
||||||
|
#define LCD_DECREMENT 0
|
||||||
|
#define LCD_DISPLAY_SHIFT 1
|
||||||
|
#define LCD_CURSOR_MOVE 0
|
||||||
|
#define LCD_SHIFT_RIGHT 1
|
||||||
|
#define LCD_SHIFT_LEFT 0
|
||||||
|
/*
|
||||||
|
* Control shift and increment for LCD.
|
||||||
|
*/
|
||||||
|
void lcd_entry_mode(LCD *lcd, int id, int s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Control direction and shift/move for cursor for LCD.
|
||||||
|
*/
|
||||||
|
void lcd_cursor_shift(LCD *lcd, int sc, int rl);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return: weather the busy flag is set for LCD.
|
||||||
|
*/
|
||||||
|
bool lcd_is_busy(LCD *lcd);
|
||||||
|
|
||||||
|
#endif
|
30
main.c
Normal file
30
main.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "lcd.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
#define RS 2
|
||||||
|
#define RW 3
|
||||||
|
#define EN 4
|
||||||
|
#define D0 17
|
||||||
|
#define D1 27
|
||||||
|
#define D2 22
|
||||||
|
#define D3 10
|
||||||
|
#define D4 9
|
||||||
|
#define D5 14
|
||||||
|
#define D6 15
|
||||||
|
#define D7 18
|
||||||
|
|
||||||
|
int main(int argc, const char **argv) {
|
||||||
|
gpio_handle_t handle = gpio_open(0);
|
||||||
|
if (handle == GPIO_INVALID_HANDLE) {
|
||||||
|
errx(1, "count not open GPIO handle!");
|
||||||
|
}
|
||||||
|
LCD *lcd = lcd_open(handle, RS, RW, EN, D0, D1, D2, D3, D4, D5, D6, D7);
|
||||||
|
lcd_write_string(lcd, "This is a test");
|
||||||
|
lcd_move_to(lcd, 1, 0);
|
||||||
|
lcd_write_string(lcd, "FreeBSD!");
|
||||||
|
lcd_close(lcd);
|
||||||
|
gpio_close(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
29
util.c
Normal file
29
util.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* util.c - Utility functions
|
||||||
|
* Copyright (C) 2024 Alexander Rosenberg
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or (at your option) any later
|
||||||
|
* version. See the LICENSE file for more information.
|
||||||
|
*/
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
struct GlobalFlags GLOBAL_FLAGS = {
|
||||||
|
.config_path = NULL,
|
||||||
|
.gpio_path = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
void *malloc_checked(size_t n) {
|
||||||
|
return realloc_checked(NULL, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *realloc_checked(void *old_ptr, size_t n) {
|
||||||
|
void *ptr = realloc(old_ptr, n);
|
||||||
|
if (n && !ptr) {
|
||||||
|
errx(1, "out of memory!");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
33
util.h
Normal file
33
util.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* util.h - Utility functions
|
||||||
|
* Copyright (C) 2024 Alexander Rosenberg
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or (at your option) any later
|
||||||
|
* version. See the LICENSE file for more information.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_UTIL_H
|
||||||
|
#define INCLUDED_UTIL_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct GlobalFlags {
|
||||||
|
const char *config_path; // path to config file
|
||||||
|
const char *gpio_path; // path of GPIO device file to use
|
||||||
|
};
|
||||||
|
extern struct GlobalFlags GLOBAL_FLAGS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like malloc(3), except that if allocation fails, an error will be written to
|
||||||
|
* standard error and abort(3) called
|
||||||
|
*/
|
||||||
|
void *malloc_checked(size_t n);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like realloc(3), except that if allocation fails, an error will be written to
|
||||||
|
* standard error and abort(3) called
|
||||||
|
*/
|
||||||
|
void *realloc_checked(void *old_ptr, size_t n);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user