#include #include #include #include #include #include #include #include #include #include #include #include #define GPIO "/dev/gpio0" #define PIN_RESET 12 #define PIN_SELECT 25 #define SPI "/dev/spi0" #define MODE 3 #define ADDR 0 #define _X(a,b) ((a)?(b):0) /* table 0 */ #define CLEAR_DISPLAY 1 #define RETURN_HOME 2 #define ENTRY_MODE_SET(id,s) (0x04 | ((id)?2:0) | ((s)?1:0)) #define DISPLAY_ONOFF(d,c,b) (0x08 | _X(d,4) | _X(c,2) | _X(b,1)) #define CURSOR_OR_DISPLAY_SHIFT(sc,rl) (0x10 | _X(sc,8) | _X(rl,4)) #define FUNCTION_SET(dl,n,dh,is) (0x20 | _X(dl,16) | _X(n,8) | _X(dh,4) | ((is) & 0x3)) #define SET_CGRAM(ac) (0x40 | ((ac) & 0x3f)) #define SET_DDRAM(ac) (0x80 | ((ac) & 0x7f)) /* table 1 */ #define BIAS_SET(bs,fx) (0x10 | _X(bs,8) | _X(fx,1)) #define SET_IRAM(ac) (0x40 | ((ac) & 0xf)) #define POWER_SET(ion,bon,c) (0x50 | _X(ion,8) | _X(bon,4) | (((c)>>4)&0x3)) #define FOLLOWER_CONTROL(fon,rab) (0x60 | _X(fon,8) | ((rab) & 0x7)) #define CONTRAST_SET(c) (0x70 | ((c) & 0xf)) /* table 2 */ #define DOUBLE_HEIGHT(ud) (0x10 | _X(ud,8)) typedef struct { int gfd; int sfd; int select_pin; int reset_pin; int rows; int columns; int addr; int enabled; int cursor; int blink; int dbl; } lcd_t; void delay(double seconds) { unsigned hz = 100; unsigned micros = 1000000u / hz; useconds_t ticks = ceil(seconds * hz); usleep(ticks * micros); } int gpio_set(int fd, int pin, int val) { struct gpio_req gp; gp.gp_name[0] = '\0'; gp.gp_pin = pin; gp.gp_value = val; if (ioctl(fd, GPIOWRITE, &gp) == -1) return errno; return 0; } int spi_configure(int fd, int addr, uint32_t mode, uint32_t speed) { spi_ioctl_configure_t sic; sic.sic_addr = addr; sic.sic_mode = mode; sic.sic_speed = speed; if (ioctl(fd, SPI_IOCTL_CONFIGURE, &sic) == -1) return errno; return 0; } int spi_transfer(int fd, int addr, void *send, size_t slen, void *recv, size_t rlen) { spi_ioctl_transfer_t sit; sit.sit_addr = addr; sit.sit_send = send; sit.sit_sendlen = slen; sit.sit_recv = recv; sit.sit_recvlen = rlen; if (ioctl(fd, SPI_IOCTL_TRANSFER, &sit) == -1) return errno; return 0; } int read_busy(lcd_t *lcd) { uint8_t buf; spi_transfer(lcd->sfd, lcd->addr, NULL, 0, &buf, 1); return (int)buf; } void write_char(lcd_t *lcd, uint8_t c) { gpio_set(lcd->gfd, lcd->select_pin, GPIO_PIN_HIGH); spi_transfer(lcd->sfd, lcd->addr, &c, 1, NULL, 0); delay(50e-6); } void write_string(lcd_t *lcd, uint8_t *s) { gpio_set(lcd->gfd, lcd->select_pin, GPIO_PIN_HIGH); while (*s) { spi_transfer(lcd->sfd, lcd->addr, s++, 1, NULL, 0); delay(30e-6); } } void write_instruction_set(lcd_t *lcd, int set) { uint8_t cmd = FUNCTION_SET(1, 1, lcd->dbl, set); gpio_set(lcd->gfd, lcd->select_pin, GPIO_PIN_LOW); spi_transfer(lcd->sfd, lcd->addr, &cmd, 1, NULL, 0); delay(30e-6); } void write_command(lcd_t *lcd, uint8_t cmd, int set) { gpio_set(lcd->gfd, lcd->select_pin, GPIO_PIN_LOW); write_instruction_set(lcd, set); spi_transfer(lcd->sfd, lcd->addr, &cmd, 1, NULL, 0); delay(30e-6); } void update_display_mode(lcd_t *lcd) { write_command(lcd, DISPLAY_ONOFF(lcd->enabled,lcd->cursor,lcd->blink), 0); write_command(lcd, DOUBLE_HEIGHT(lcd->dbl >> 1), 2); } void set_bias(lcd_t *lcd, int bias) { write_command(lcd, BIAS_SET(bias, 1), 1); } void set_contrast(lcd_t *lcd, int contrast) { write_command(lcd, POWER_SET(1,1,contrast), 1); write_command(lcd, FOLLOWER_CONTROL(1,3), 1); delay(200e-3); write_command(lcd, CONTRAST_SET(contrast), 1); } void set_display_mode(lcd_t *lcd, int enabled, int cursor, int blink) { lcd->enabled = enabled; lcd->cursor = cursor; lcd->blink = blink; update_display_mode(lcd); } void enable_cursor(lcd_t *lcd, int set) { lcd->cursor = set; update_display_mode(lcd); } void enable_blink(lcd_t *lcd, int set) { lcd->blink = set; update_display_mode(lcd); } void set_cursor_offset(lcd_t *lcd, int offset) { write_command(lcd, SET_DDRAM(offset), 0); delay(50e-6); } void set_cursor_position(lcd_t *lcd, int row, int column) { int stride, n; switch (lcd->rows) { case 1: stride = 0; break; case 2: stride = 64; break; case 3: stride = 16; break; default: return; } n = row * stride + column; if (n >= 0 && n < 128) set_cursor_offset(lcd, n); } void home(lcd_t *lcd) { set_cursor_position(lcd, 0, 0); } void clear(lcd_t *lcd) { write_command(lcd, CLEAR_DISPLAY, 0); delay(2e-3); } void reset(lcd_t *lcd) { gpio_set(lcd->gfd, lcd->reset_pin, GPIO_PIN_LOW); delay(0.001); gpio_set(lcd->gfd, lcd->reset_pin, GPIO_PIN_HIGH); } void setup(lcd_t *lcd) { lcd->select_pin = PIN_SELECT; lcd->reset_pin = PIN_RESET; lcd->rows = 3; lcd->columns = 16; lcd->addr = ADDR; lcd->enabled = 1; lcd->cursor = 0; lcd->blink = 0; lcd->dbl = 1; spi_configure(lcd->sfd, lcd->addr, MODE, 0); spi_configure(lcd->sfd, lcd->addr, MODE, 1000000); gpio_set(lcd->gfd, lcd->select_pin, GPIO_PIN_HIGH); reset(lcd); delay(40e-3); } int main() { lcd_t L; int i; L.gfd = open(GPIO, O_RDWR); if (L.gfd == -1) err(1,"open %s",GPIO); L.sfd = open(SPI, O_RDWR); if (L.sfd == -1) err(1,"open %s",SPI); setup(&L); update_display_mode(&L); write_command(&L, ENTRY_MODE_SET(1,0), 0); set_bias(&L, 1); set_display_mode(&L, 1, 0, 0); set_contrast(&L, 40); clear(&L); for (;;) { time_t now; struct tm *tm; static int mday; char *tz; char buf[26]; time(&now); tm = localtime(&now); asctime_r(tm, buf); tz = tm->tm_zone; buf[10] = '\0'; buf[19] = '\0'; buf[24] = '\0'; if (tm->tm_mday != mday) { set_cursor_position(&L, 0, 0); write_string(&L, &buf[0]); set_cursor_position(&L, 0, 12); write_string(&L, &buf[20]); mday = tm->tm_mday; } set_cursor_position(&L, 1, 0); write_string(&L, &buf[11]); set_cursor_position(&L, 1, 9); write_string(&L, tz); delay(1.0); } //printf("%d\n", read_busy(&L)); close(L.sfd); close(L.gfd); }