# HG changeset patch # User Paul Boddie # Date 1385158387 0 # Node ID 3644970cf1b6e03bffd2507f611289296a3cfd9c Interfacing the Ben NanoNote to the Nu Electronics "Color LCD & Joystick Shield". diff -r 000000000000 -r 3644970cf1b6 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Fri Nov 22 22:13:07 2013 +0000 @@ -0,0 +1,41 @@ +# Makefile - Build the PCF8833 display software +# +# Copyright (C) 2013 Paul Boddie +# +# 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 2 of the License, or +# (at your option) any later version. + +LIBUBB = ../ben-blinkenlights/libubb +SYSROOT = ../openwrt-xburst/staging_dir/target-mipsel_eglibc-2.15 +TOOLBIN = ../openwrt-xburst/staging_dir/toolchain-mipsel_gcc-4.6-linaro_eglibc-2.15/bin + +ARCH = mipsel-openwrt-linux +CC = $(TOOLBIN)/$(ARCH)-gcc + +CFLAGS = -g -Wall -fPIC -march=mips32 -I$(LIBUBB)/include # -DDEBUG=1 +LDFLAGS = -lm -lubb -L$(LIBUBB) + +TARGETS = spin + +BASICSRC = pcf8833.c + +SOURCES = $(BASICSRC) spin.c +OBJECTS = $(SOURCES:.c=.o) + +.PHONY: all clean distclean + +all: $(TARGETS) + +clean: + rm -f $(OBJECTS) $(TARGETS) + +distclean: clean + echo "Nothing else to clean." + +$(TARGETS): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.c.o: + $(CC) -c $(CFLAGS) $< -o $@ diff -r 000000000000 -r 3644970cf1b6 pcf8833.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pcf8833.c Fri Nov 22 22:13:07 2013 +0000 @@ -0,0 +1,247 @@ +/* + * Ben NanoNote communication with the Nu Electronics "Color LCD & + * Joystick Shield" featuring a Nokia 6110 and the PCF8833 display + * controller. + * + * http://shieldlist.org/nuelectronics/colorlcd-joystick + * + * Copyright (C) 2013 Paul Boddie + * + * 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 2 of the License, or + * (at your option) any later version. + */ + +#include "pcf8833.h" +#include +#include + +void LCD_send(lcd_sendmode mode, uint8_t data) +{ + spi_begin(); + spi_send(data, mode); + spi_end(); +} + +void LCD_send_more_data(uint8_t data) +{ + spi_begin(); + spi_send(data, LCD_DATA); + spi_end(); +} + +void spi_init() +{ + OUT(LCD_CS); + OUT(LCD_RESET); + OUT(LCD_CLK); + OUT(LCD_SEND); + OUT(LCD_BACKLIGHT); +} + +void spi_begin() +{ + CLR(LCD_CS); +} + +void spi_end() +{ + SET(LCD_CS); +} + +/** + * Send the given value via MOSI/SEND. + */ +void spi_send(uint8_t v, lcd_sendmode mode) +{ + uint8_t mask; + + if (mode == LCD_DATA) + { + #ifdef DEBUG + printf("D"); + #endif + SET(LCD_SEND); + } + else + { + #ifdef DEBUG + printf("C"); + #endif + CLR(LCD_SEND); + } + + SET(LCD_CLK); + CLR(LCD_CLK); + + for (mask = 0x80; mask; mask >>= 1) + { + if (v & mask) + { + #ifdef DEBUG + printf("1"); + #endif + SET(LCD_SEND); + } + else + { + #ifdef DEBUG + printf("0"); + #endif + CLR(LCD_SEND); + } + + SET(LCD_CLK); + CLR(LCD_CLK); + } + + #ifdef DEBUG + printf("\n"); + #endif +} + +void LCD_init(void) +{ + /* Perform a hardware reset on the LCD. */ + /* CS=1, CLK=0, D/C=0 apparently superfluous */ + + SET(LCD_RESET); + usleep(50000); + CLR(LCD_RESET); + usleep(50000); + SET(LCD_RESET); + usleep(50000); + + /* CS=1, CLK=1, D/C=1, 10ms, SWRESET, 10ms apparently superfluous */ + + LCD_send(LCD_COMMAND, LCD_SLEEPOUT); + + /* Configure display memory access. */ + + LCD_send(LCD_COMMAND, LCD_MADCTL); + LCD_send(LCD_DATA, LCD_MADCTL_MY_MX); + + /* Contrast setting (suggested value 0x40), 10ms apparently superfluous */ + + /* Switch the display on. */ + + LCD_send(LCD_COMMAND, LCD_DISPON); + + /* Configure the display mode/format. */ + /* 5 == 16bpp */ + + LCD_send(LCD_COMMAND, LCD_COLMOD); + LCD_send(LCD_DATA, 0x05); + + LCD_send(LCD_COMMAND, LCD_NOP); + + /* Switch the backlight on. */ + + SET(LCD_BACKLIGHT); +} + +void LCD_window(uint8_t xmin, uint8_t ymin, uint8_t xmax, uint8_t ymax) +{ + LCD_send(LCD_COMMAND, LCD_CASET); + LCD_send(LCD_DATA, xmin); + LCD_send(LCD_DATA, xmax); + LCD_send(LCD_COMMAND, LCD_PASET); + LCD_send(LCD_DATA, ymin); + LCD_send(LCD_DATA, ymax); +} + +void LCD_blit_int(uint16_t colour) +{ + LCD_send_more_data((colour >> 8) & 0xff); /* RGGB -> RG */ + LCD_send_more_data(colour & 0xff); /* RGGB -> GB */ +} + +void LCD_area(uint8_t xmin, uint8_t ymin, uint8_t xmax, uint8_t ymax, uint16_t colour) +{ + uint16_t i, limit = ((xmax - xmin + 1) * (ymax - ymin + 1)) + 1; + + LCD_window(xmin, ymin, xmax, ymax); + LCD_send(LCD_COMMAND, LCD_RAMWR); + + for (i = 0; i < limit; i++) + { + LCD_blit_int(colour); + } + + LCD_send(LCD_COMMAND, LCD_NOP); +} + +void LCD_image(int x, int y, uint16_t image[], uint8_t width, uint8_t height) +{ + LCD_image_region(x, y, image, width, height, 0, width, 0, height); +} + +void LCD_image_region(int x, int y, uint16_t image[], uint8_t width, uint8_t height, uint8_t from_x, uint8_t span_x, uint8_t from_y, uint8_t span_y) +{ + uint8_t xmin, ymin, xcmax, ycmax, xcmin, xc, yc; + uint16_t xmax, ymax, colour; + + /* Find the screen ranges for blitting. */ + + xmin = (x < 0) ? 0 : x; + ymin = (y < 0) ? 0 : y; + xmax = (x + span_x > SCREEN_X_MAX) ? SCREEN_X_MAX : x + span_x - 1; + ymax = (y + span_y > SCREEN_Y_MAX) ? SCREEN_Y_MAX : y + span_y - 1; + + if ((xmin > SCREEN_X_MAX) || (ymin > SCREEN_Y_MAX) || (xmax < 0) || (ymax < 0)) + { + return; + } + + /* Find the image ranges for reading data. */ + + xcmin = (x < 0) ? -x + from_x : from_x; + yc = (y < 0) ? -y + from_y : from_y; + xcmax = from_x + ((x + span_x > SCREEN_WIDTH) ? SCREEN_WIDTH - x : span_x); + ycmax = from_y + ((y + span_y > SCREEN_HEIGHT) ? SCREEN_HEIGHT - y : span_y); + + LCD_window(xmin, ymin, xmax, ymax); + LCD_send(LCD_COMMAND, LCD_RAMWR); + + /* Reading from the appropriate image ranges, blit to the appropriate + screen ranges. */ + + xc = xcmin; + while (yc < ycmax) + { + colour = image[yc * width + xc]; + LCD_blit_int(colour); + + xc++; + if (xc == xcmax) + { + xc = xcmin; + yc++; + } + } + + LCD_send(LCD_COMMAND, LCD_NOP); +} + +void LCD_normal(void) +{ + LCD_send(LCD_COMMAND, LCD_NORON); +} + +void LCD_scroll_area(uint8_t top_fixed, uint8_t scrolling, uint8_t bottom_fixed) +{ + LCD_send(LCD_COMMAND, LCD_VSCRDEF); + LCD_send(LCD_DATA, top_fixed); + LCD_send(LCD_DATA, scrolling); + LCD_send(LCD_DATA, bottom_fixed); +} + +void LCD_scroll_start(uint8_t address) +{ + LCD_send(LCD_COMMAND, LCD_SEP); + LCD_send(LCD_DATA, address); +} + +/* vim: tabstop=4 expandtab shiftwidth=4 + */ diff -r 000000000000 -r 3644970cf1b6 pcf8833.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pcf8833.h Fri Nov 22 22:13:07 2013 +0000 @@ -0,0 +1,100 @@ +/* + * Ben NanoNote communication with the Nu Electronics "Color LCD & + * Joystick Shield" featuring a Nokia 6110 and the PCF8833 display + * controller. + * + * http://shieldlist.org/nuelectronics/colorlcd-joystick + * + * Copyright (C) 2013 Paul Boddie + * + * 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 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __PCF8833_H__ +#define __PCF8833_H__ + +#include + +/* Pin assignments: + * + * Sniffer UBB Shifters Shield + * ------- ---- ---------- ------ + * DAT2 DAT2 + * CD DAT3 A2 (#1) B2 10 (SS/CS) + * CMD CMD A1 (#1) B1 9 (RESET) + * VCC VDD LV HV VIN + * CLK CLK A4 (#1) B4 13 (SCLK/CLK) + * GND GND GND GND GND + * DAT0 DAT0 A3 (#1) B3 11 (MOSI/SEND) + * DAT1 DAT1 A1 (#2) B1 8 (BACKLIGHT) + */ + +#define LCD_CS UBB_DAT3 +#define LCD_RESET UBB_CMD +#define LCD_CLK UBB_CLK +#define LCD_SEND UBB_DAT0 +#define LCD_BACKLIGHT UBB_DAT1 + +typedef enum { + LCD_COMMAND = 0, + LCD_DATA = 1 +} lcd_sendmode; + +/* Screen constants. */ + +#define SCREEN_WIDTH 132 +#define SCREEN_X_MAX 131 +#define SCREEN_HEIGHT 132 +#define SCREEN_Y_MAX 131 + +/* Commands. */ + +#define LCD_NOP 0x00 +#define LCD_SWRESET 0x01 +#define LCD_SLEEPOUT 0x11 +#define LCD_NORON 0x13 +#define LCD_SETCON 0x25 +#define LCD_DISPOFF 0x28 +#define LCD_DISPON 0x29 +#define LCD_CASET 0x2a +#define LCD_PASET 0x2b +#define LCD_RAMWR 0x2c +#define LCD_VSCRDEF 0x33 +#define LCD_SEP 0x37 +#define LCD_MADCTL 0x36 +#define LCD_COLMOD 0x3a + +/* Command-specific constants. */ + +#define LCD_MADCTL_MY_MX 0xc0 +#define LCD_MADCTL_MY_V 0xa0 +#define LCD_MADCTL_MY_MX_V 0xe0 +#define LCD_MADCTL_MX_V 0x60 + +/* Functions. */ + +void spi_init(); +void spi_begin(); +void spi_end(); +void spi_send(uint8_t v, lcd_sendmode mode); + +void LCD_send(lcd_sendmode mode, uint8_t data); +void LCD_send_more_data(uint8_t data); +void LCD_send_raw(uint8_t data); +void LCD_init(void); +void LCD_window(uint8_t xmin, uint8_t ymin, uint8_t xmax, uint8_t ymax); +void LCD_blit_int(uint16_t colour); +void LCD_area(uint8_t xmin, uint8_t ymin, uint8_t xmax, uint8_t ymax, uint16_t colour); +void LCD_image(int x, int y, uint16_t image[], uint8_t width, uint8_t height); +void LCD_image_region(int x, int y, uint16_t image[], uint8_t width, uint8_t height, uint8_t from_x, uint8_t span_x, uint8_t from_y, uint8_t span_y); +void LCD_normal(void); +void LCD_scroll_area(uint8_t top_fixed, uint8_t scrolling, uint8_t bottom_fixed); +void LCD_scroll_start(uint8_t address); + +#endif /* __PCF8833_H__ */ + +/* vim: tabstop=4 expandtab shiftwidth=4 + */ diff -r 000000000000 -r 3644970cf1b6 spin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spin.c Fri Nov 22 22:13:07 2013 +0000 @@ -0,0 +1,84 @@ +/* + * A demonstration of area filling using different orientations. + * + * Copyright (C) 2013 Paul Boddie + * + * 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 2 of the License, or + * (at your option) any later version. + */ + +#include "pcf8833.h" +#include +#include +#include +#include + +/** + * Handle termination of the process. + */ +void shutdown(int signum) +{ + printf("Closing...\n"); + ubb_close(0); + exit(1); +} + +/** + * Show a pattern on the screen. + */ +void pattern() +{ + LCD_area(0, 0, 131, 131, 0x000); + LCD_area(21, 21, 35, 65, 0xf800); LCD_area(21, 66, 35, 110, 0xfbe0); + LCD_area(36, 21, 50, 65, 0xffe0); LCD_area(36, 66, 50, 110, 0x7fe0); + LCD_area(51, 21, 65, 65, 0x07e0); LCD_area(51, 66, 65, 110, 0x07ef); + LCD_area(66, 21, 80, 65, 0x07ff); LCD_area(66, 66, 80, 110, 0x0bff); + LCD_area(81, 21, 95, 65, 0x001f); LCD_area(81, 66, 95, 110, 0x781f); + LCD_area(96, 21, 110, 65, 0xf81f); LCD_area(96, 66, 110, 110, 0xf80f); +} + +uint8_t states[] = {LCD_MADCTL_MY_MX, LCD_MADCTL_MY_V, 0, LCD_MADCTL_MX_V}; + +int main(int argc, char *argv[]) +{ + uint8_t state = 1, times = 10; + + signal(SIGINT, shutdown); + + /* Access the 8:10 port. */ + + if (ubb_open(0) < 0) { + perror("ubb_open"); + return 1; + } + + ubb_power(1); + printf("Power on.\n"); + + spi_init(); + LCD_init(); + + printf("Updating...\n"); + pattern(); + + while (times--) + { + sleep(1); + printf("Updating...\n"); + + LCD_send(LCD_COMMAND, LCD_MADCTL); + LCD_send(LCD_DATA, states[state]); + pattern(); + + state = (state + 1) % 4; + } + + printf("Closing...\n"); + ubb_close(0); + return 0; +} + +/* vim: tabstop=4 expandtab shiftwidth=4 + */