Skip to content

Instantly share code, notes, and snippets.

@threez
Created September 8, 2022 17:17
Show Gist options
  • Save threez/7d50e492cf04e9b4253cbc445609f42d to your computer and use it in GitHub Desktop.
Save threez/7d50e492cf04e9b4253cbc445609f42d to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dev/iicbus/iic.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <err.h>
#include <string.h>
// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00
// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
// flags for backlight control
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00
// Enable bit
#define E 0x04
// Read/write bit
#define RW 0x02
// Register select bit
#define RS 0x01
struct LCD {
int fd;
uint8_t addr;
int lines;
int chars;
int backlight;
int posx;
int posy;
};
typedef enum { IR, DR } lcd_register;
typedef struct LCD lcd_t;
void lcd_init(lcd_t *lcd, char *dev, int addr, int chars, int lines);
void lcd_set_cursor(lcd_t *lcd, int chars, int lines);
void lcd_write_nibble(lcd_t *lcd, lcd_register reg, char data);
void lcd_write_instruction_high_nibble(lcd_t *lcd, uint8_t data);
void lcd_write_instruction(lcd_t *lcd, uint8_t data);
void lcd_write_data(lcd_t *lcd, uint8_t data);
void lcd_raw_write(lcd_t *lcd, char data);
void lcd_puts(lcd_t *lcd, char* str);
void lcd_putc(lcd_t *lcd, char c);
void lcd_clear_display(lcd_t *lcd);
void lcd_close(lcd_t *lcd);
int main(int argc, char** argv) {
lcd_t lcd;
lcd_init(&lcd, "/dev/iic1", 0x27, 16, 2);
for (int i = 1; i < argc; i++) {
lcd_set_cursor(&lcd, 0, i-1);
lcd_puts(&lcd, argv[i]);
}
lcd_close(&lcd);
return 0;
}
void lcd_init(lcd_t *lcd, char *dev, int addr, int chars, int lines) {
int fd = open(dev, O_RDWR);
if (fd == -1) {
err(1, "open device failed");
}
lcd->fd = fd;
lcd->addr = addr;
lcd->chars = chars;
lcd->lines = lines;
lcd->backlight = 1;
lcd->posx = 0;
lcd->posy = 0;
lcd_write_instruction_high_nibble(lcd, LCD_FUNCTIONSET | LCD_8BITMODE);
usleep(5000);
lcd_write_instruction_high_nibble(lcd, LCD_FUNCTIONSET | LCD_8BITMODE);
usleep(100);
lcd_write_instruction_high_nibble(lcd, LCD_FUNCTIONSET | LCD_8BITMODE);
lcd_write_instruction_high_nibble(lcd, LCD_FUNCTIONSET | LCD_4BITMODE);
lcd_write_instruction(lcd, LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE);
lcd_write_instruction(lcd, LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF );// | LCD_BLINKON);
lcd_clear_display(lcd);
lcd_write_instruction(lcd, LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
}
void lcd_write_instruction_high_nibble(lcd_t *lcd, uint8_t data) {
uint8_t h = (data >> 4) & 0x0F;
lcd_write_nibble(lcd, IR, h);
usleep(37);
}
void lcd_write_instruction(lcd_t *lcd, uint8_t data) {
uint8_t h = (data >> 4) & 0x0F;
uint8_t l = data & 0x0F;
lcd_write_nibble(lcd, IR, h);
lcd_write_nibble(lcd, IR, l);
usleep(37);
}
void lcd_write_data(lcd_t *lcd, uint8_t data) {
uint8_t h = (data >> 4) & 0x0F;
uint8_t l = data & 0x0F;
lcd_write_nibble(lcd, DR, h);
lcd_write_nibble(lcd, DR, l);
usleep(41);
}
void lcd_write_nibble(lcd_t *lcd, lcd_register reg, char data) {
/* Shift the interesting data on the upper 4 bits (b7-b4) */
data = (data << 4) & 0xF0;
/* Flip the RS bit if we write do data register */
if (reg == DR)
data |= RS;
/* Keep the RW bit low, because we write */
data = data | (RW & 0x00);
/* Flip the backlight bit */
if (lcd->backlight)
data |= LCD_BACKLIGHT;
lcd_raw_write(lcd, data);
usleep(1);
/* Theoretically wait for tAS = 40ns, practically it's already elapsed */
/* Raise the E signal... */
lcd_raw_write(lcd, data | E);
/* Again, "wait" for pwEH = 230ns */
usleep(1);
/* ...and let it fall to clock the data into the HD44780's register */
lcd_raw_write(lcd, data);
/* And again, "wait" for about tCYC_E - pwEH = 270ns */
usleep(1);
}
void lcd_raw_write(lcd_t *lcd, char data) {
uint8_t addr = lcd->addr << 1;
uint8_t cmd[1] = { data };
struct iic_msg msgs = { addr | IIC_M_WR, IIC_M_WR, 1, cmd};
struct iic_rdwr_data rwd = { &msgs, 1 };
int e = ioctl(lcd->fd, I2CRDWR, &rwd);
if (e < 0) {
err(2, "i2c device write failed");
}
}
void lcd_puts(lcd_t *lcd, char* str) {
int l = strlen(str);
for (int i = 0; i < l; i++) {
lcd_putc(lcd, str[i]);
}
}
int lines_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
void lcd_set_cursor(lcd_t *lcd, int chars, int lines) {
if ( lines > lcd->lines ) {
lines = lcd->lines-1; // we count rows starting w/0
}
lcd->posx = chars;
lcd->posy = lines;
lcd_write_instruction(lcd, LCD_SETDDRAMADDR | (chars + lines_offsets[lines]));
}
void lcd_putc(lcd_t *lcd, char c) {
lcd_write_data(lcd, c);
lcd->posx++;
if (lcd->posx == lcd->chars) {
lcd->posy = (lcd->posy + 1) % lcd->lines;
lcd->posx = 0;
lcd_write_instruction(lcd, LCD_SETDDRAMADDR | lines_offsets[lcd->posy]);
}
}
void lcd_clear_display(lcd_t *lcd) {
lcd_write_instruction(lcd, LCD_CLEARDISPLAY);
/* Wait for 1.64 ms because this one needs more time */
usleep(1640);
/*
* CLEAR_DISPLAY instruction also returns cursor to home,
* so we need to update it locally.
*/
lcd->posx = 0;
lcd->posy = 0;
}
void lcd_close(lcd_t *lcd) {
close(lcd->fd);
}
all: pulse lcd
pulse: pulse.c
clang -lgpio -o $@ $<
lcd: lcd.c lcd.h lcd_const.h
clang -o $@ $<
#include <err.h>
#include <stdio.h>
#include <unistd.h>
#include <libgpio.h>
#include <time.h>
#include <stdlib.h>
int main(int argc, char** argv) {
if (argc < 3) {
err(1, "%s <gpio> <pin>", argv[0]);
}
int gpio = atoi(argv[1]);
int pin = atoi(argv[2]);
gpio_handle_t h = gpio_open(gpio);
if (h == GPIO_VALUE_INVALID)
err(1, "gpio_open failed");
gpio_pin_input(h, pin);
gpio_value_t v;
gpio_value_t lv;
while (true) {
usleep(1000);
v = gpio_pin_get(h, pin);
if (lv != v) {
if (v == 0) {
printf(".");
fflush(stdout);
}
lv = v;
}
}
gpio_close(h);
return 0;
}
@threez
Copy link
Author

threez commented Sep 8, 2022

FreeBSD usage of I2C & GPIO on BeagleBoneBlack: FreeBSD beaglebone 13.1-RELEASE FreeBSD 13.1-RELEASE releng/13.1-n250148-fc952ac2212 GENERIC arm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment