notro / fbtft

Linux Framebuffer drivers for small TFT LCD display modules. Development has moved to https://git.kernel.org/cgit/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fbtft?h=staging-testing
1.85k stars 496 forks source link

Driver for DOGS102 #175

Closed woodym closed 9 years ago

woodym commented 9 years ago

DOGS102 are a Display from ELECTRONIC ASSEMBLY with Controller UC1701. This are a black and White Display (or many other background-Colors) with 102*64 Pixels. The Display have a great temperature range -20..+70°C and is powerd with single 3,3V. The Connection can made without a reset-line (only MOSI,MISO,CS and A0 required). I have testet on rpi B+

the Driver fb_uc1701.c

/*
 * FB driver for the UC1701 LCD Controller
 *
 * The display is monochrome and the video memory is RGB565.
 * Any pixel value except 0 turns the pixel on.
 *
 * Copyright (C) 2014 Juergen Holzmann
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>

#include "fbtft.h"

#define DRVNAME        "fb_uc1701"
#define WIDTH          102
#define HEIGHT         64
#define PAGES          (HEIGHT/8)

#define LCD_DISPLAY_ENABLE    0xAE  //1: Display on/off
#define LCD_START_LINE        0x40  //2: display start line set
#define LCD_PAGE_ADDRESS      0xB0  //3: Page address set (lower 4 bits select one of the pages)
#define LCD_COL_ADDRESS       0x10  //4: column address
#define LCD_BOTTOMVIEW        0xA0  //8: select orientation
#define LCD_DISPLAY_INVERT    0xA6  //9: inverted display
#define LCD_ALL_PIXEL         0xA4  //10: show memory content or switch all pixels on
#define LCD_BIAS              0xA2  //11: lcd bias set
#define LCD_RESET_CMD         0xE2  //14: Reset Controller
#define LCD_SCAN_DIR          0xC0  //15: output mode select (turns display upside-down)
#define LCD_POWER_CONTROL     0x28  //16: power control set
#define LCD_VOLTAGE           0x20  //17: voltage regulator resistor ratio set
#define LCD_VOLUME_MODE       0x81  //18: Volume mode set
#define LCD_NO_OP             0xE3  //22: NOP command
#define LCD_ADV_PROG_CTRL     0xFA  //25: advanced program control
#define LCD_ADV_PROG_CTRL2    0x10  //25: advanced program control2
#define LCD_TEMPCOMP_HIGH     0x80
#define SHIFT_ADDR_NORMAL  0   //column offset for normal orientation
#define SHIFT_ADDR_TOPVIEW 30  //column offset for bottom view orientation

static int init_display(struct fbtft_par *par)
{
        fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

        par->fbtftops.reset(par);

        // softreset of LCD
        write_reg(par, LCD_RESET_CMD);
        mdelay(10);

        // set startpoint
        write_reg(par, LCD_START_LINE); // LCD_START_LINE | (pos & 0x3F)

        // select orientation BOTTOMVIEW
        write_reg(par, LCD_BOTTOMVIEW | 1);
        // output mode select (turns display upside-down)
        write_reg(par, LCD_SCAN_DIR | 0x00);

        // Normal Pixel mode
        write_reg(par, LCD_ALL_PIXEL | 0);

        // positive display
        write_reg(par, LCD_DISPLAY_INVERT | 0);

        // bias 1/9
        write_reg(par, LCD_BIAS | 0);

        // power control mode: all features on
        write_reg(par, LCD_POWER_CONTROL | 0x07); // LCD_POWER_CONTROL | (val&0x07)

        // set voltage regulator R/R
        write_reg(par, LCD_VOLTAGE | 0x07); // LCD_VOLTAGE | (val&0x07)

        // volume mode set
        write_reg(par, LCD_VOLUME_MODE); // LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP
        write_reg(par, 0x09); // LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP
        // ????
        write_reg(par, LCD_NO_OP); // LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP

        // advanced program control
        write_reg(par, LCD_ADV_PROG_CTRL);
        write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH);

        // enable display
        write_reg(par, LCD_DISPLAY_ENABLE | 1);

        return 0;
}

static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
        fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);

        // goto address
        write_reg(par, LCD_PAGE_ADDRESS); // LCD_PAGE_ADDRESS | ((page) & 0x1F), (((col)+SHIFT_ADDR_NORMAL) & 0x0F), LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F)
        write_reg(par, 0x00); // LCD_PAGE_ADDRESS | ((page) & 0x1F), (((col)+SHIFT_ADDR_NORMAL) & 0x0F), LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F)
        write_reg(par, LCD_COL_ADDRESS); // LCD_PAGE_ADDRESS | ((page) & 0x1F), (((col)+SHIFT_ADDR_NORMAL) & 0x0F), LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F)
}

static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
        u16 *vmem16 = (u16 *)par->info->screen_base;
        u8 *buf = par->txbuf.buf;
        int x, y, i;
        int ret = 0;

        fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);

        for (y=0;y<PAGES;y++)
        {
                buf = par->txbuf.buf;
                for (x=0;x<WIDTH;x++)
                {
                        *buf = 0x00;
                        for (i=0;i<8;i++)
                        {
                                *buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i;
                        }
                        buf++;
                }
                write_reg(par, LCD_PAGE_ADDRESS|(u8)y); // LCD_PAGE_ADDRESS | ((page) & 0x1F), (((col)+SHIFT_ADDR_NORMAL) & 0x0F), LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F)
                write_reg(par, 0x00); // LCD_PAGE_ADDRESS | ((page) & 0x1F), (((col)+SHIFT_ADDR_NORMAL) & 0x0F), LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F)
                write_reg(par, LCD_COL_ADDRESS); // LCD_PAGE_ADDRESS | ((page) & 0x1F), (((col)+SHIFT_ADDR_NORMAL) & 0x0F), LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F)
                gpio_set_value(par->gpio.dc, 1);
                ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
                gpio_set_value(par->gpio.dc, 0);
        }

        if (ret < 0)
                dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret);

        return ret;
}

static struct fbtft_display display = {
        .regwidth = 8,
        .width = WIDTH,
        .height = HEIGHT,
        .fbtftops = {
                .init_display = init_display,
                .set_addr_win = set_addr_win,
                .write_vmem = write_vmem,
        },
        .backlight = 1,
};
FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display);

MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("spi:uc1701");

MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller");
MODULE_AUTHOR("Juergen Holzmann");
MODULE_LICENSE("GPL");

and the changes for fbtft_device

{
                .name = "dogs102",
                .spi = &(struct spi_board_info) {
                        .modalias = "fb_uc1701",
                        .max_speed_hz = 8000000,
                        .mode = SPI_MODE_0,
                        .platform_data = &(struct fbtft_platform_data) {
                                .display = {
                                        .buswidth = 8,
                                },
                                .bgr = true,
                                .gpios = (const struct fbtft_gpio []) {
                                        { "reset", 13 },
                                        { "dc", 6 },
                                        {},
                                },
                        }
                }
        },

ea-dogs102w-a_2d

woodym commented 9 years ago

i have correct the code (old commented out code removed)

notro commented 9 years ago

Please run your code through checkpatch.pl: https://github.com/notro/fbtft/wiki/Development#submitting-patches

woodym commented 9 years ago

i have run against checkpatch.pl and modify the source:

/*
 * FB driver for the UC1701 LCD Controller
 *
 * The display is monochrome and the video memory is RGB565.
 * Any pixel value except 0 turns the pixel on.
 *
 * Copyright (C) 2014 Juergen Holzmann
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>

#include "fbtft.h"

#define DRVNAME        "fb_uc1701"
#define WIDTH          102
#define HEIGHT         64
#define PAGES          (HEIGHT/8)

/* 1: Display on/off */
#define LCD_DISPLAY_ENABLE    0xAE
/* 2: display start line set */
#define LCD_START_LINE        0x40
/* 3: Page address set (lower 4 bits select one of the pages) */
#define LCD_PAGE_ADDRESS      0xB0
/* 4: column address */
#define LCD_COL_ADDRESS       0x10
/* 8: select orientation */
#define LCD_BOTTOMVIEW        0xA0
/* 9: inverted display */
#define LCD_DISPLAY_INVERT    0xA6
/* 10: show memory content or switch all pixels on */
#define LCD_ALL_PIXEL         0xA4
/* 11: lcd bias set */
#define LCD_BIAS              0xA2
/* 14: Reset Controller */
#define LCD_RESET_CMD         0xE2
/* 15: output mode select (turns display upside-down) */
#define LCD_SCAN_DIR          0xC0
/* 16: power control set */
#define LCD_POWER_CONTROL     0x28
/* 17: voltage regulator resistor ratio set */
#define LCD_VOLTAGE           0x20
/* 18: Volume mode set */
#define LCD_VOLUME_MODE       0x81
/* 22: NOP command */
#define LCD_NO_OP             0xE3
/* 25: advanced program control */
#define LCD_ADV_PROG_CTRL     0xFA
/* 25: advanced program control2 */
#define LCD_ADV_PROG_CTRL2    0x10
#define LCD_TEMPCOMP_HIGH     0x80
/* column offset for normal orientation */
#define SHIFT_ADDR_NORMAL     0
/* column offset for bottom view orientation */
#define SHIFT_ADDR_TOPVIEW    30

static int init_display(struct fbtft_par *par)
{
        fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

        par->fbtftops.reset(par);

        /* softreset of LCD */
        write_reg(par, LCD_RESET_CMD);
        mdelay(10);

        /* set startpoint */
        /* LCD_START_LINE | (pos & 0x3F) */
        write_reg(par, LCD_START_LINE);

        /* select orientation BOTTOMVIEW */
        write_reg(par, LCD_BOTTOMVIEW | 1);
        /* output mode select (turns display upside-down) */
        write_reg(par, LCD_SCAN_DIR | 0x00);

        /* Normal Pixel mode */
        write_reg(par, LCD_ALL_PIXEL | 0);

        /* positive display */
        write_reg(par, LCD_DISPLAY_INVERT | 0);

        /* bias 1/9 */
        write_reg(par, LCD_BIAS | 0);

        /* power control mode: all features on */
        /* LCD_POWER_CONTROL | (val&0x07) */
        write_reg(par, LCD_POWER_CONTROL | 0x07);

        /* set voltage regulator R/R */
        /* LCD_VOLTAGE | (val&0x07) */
        write_reg(par, LCD_VOLTAGE | 0x07);

        /* volume mode set */
        /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
        write_reg(par, LCD_VOLUME_MODE);
        /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
        write_reg(par, 0x09);
        /* ???? */
        /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
        write_reg(par, LCD_NO_OP);

        /* advanced program control */
        write_reg(par, LCD_ADV_PROG_CTRL);
        write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH);

        /* enable display */
        write_reg(par, LCD_DISPLAY_ENABLE | 1);

        return 0;
}

static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
        fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);

        /* goto address */
        /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
         (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
         LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
        write_reg(par, LCD_PAGE_ADDRESS);
        /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
         (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
          LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
        write_reg(par, 0x00);
        /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
         (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
          LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
        write_reg(par, LCD_COL_ADDRESS);
}

static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
        u16 *vmem16 = (u16 *)par->info->screen_base;
        u8 *buf = par->txbuf.buf;
        int x, y, i;
        int ret = 0;

        fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);

        for (y = 0; y < PAGES; y++) {
                buf = par->txbuf.buf;
                for (x = 0; x < WIDTH; x++) {
                        *buf = 0x00;
                        for (i = 0; i < 8; i++)
                                *buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i;
                        buf++;
                }
                /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
                 (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
                  LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
                write_reg(par, LCD_PAGE_ADDRESS|(u8)y);
                /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
                 (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
                  LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
                write_reg(par, 0x00);
                /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
                 (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
                  LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
                write_reg(par, LCD_COL_ADDRESS);
                gpio_set_value(par->gpio.dc, 1);
                ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
                gpio_set_value(par->gpio.dc, 0);
        }

        if (ret < 0)
                dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret);

        return ret;
}

static struct fbtft_display display = {
        .regwidth = 8,
        .width = WIDTH,
        .height = HEIGHT,
        .fbtftops = {
                .init_display = init_display,
                .set_addr_win = set_addr_win,
                .write_vmem = write_vmem,
        },
        .backlight = 1,
};
FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display);

MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("spi:uc1701");

MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller");
MODULE_AUTHOR("Juergen Holzmann");
MODULE_LICENSE("GPL");
woodym commented 9 years ago

i attach my wiring for the DOGS102. for the Caps i use ceramic cap 0805 SMD's. the resistors 0805 SMD's.

dogs102_connect

cosailer commented 9 years ago

hi woodym, I have a EA DOGXL240-7 display and I want to write a driver for it. Could you share some more information on the normal use experience and the setup picture? (maybe a video)

woodym commented 9 years ago

hello cosailer, i havn't a Video yet. i try to make any photos for my experimental setup.

my biggest Problem for this tiny device to get a fontset with readable chars. the dogxl240-7 uses the UC1611 Controller. i think a good starting-point are the simple code for the DOGXL160 with the UC1610 from dogm-graphic.c (avr-code) Author: Jan Michel (http://www.mikrocontroller.net/topic/144500) . remeber you have to modify the code for 4 bits for one Pixel. my code use 1 bits for 1 Pixel. I don't know if the 4 bits are usable gray-scal on the dogxl240-7. you find the manual for the UC1611 on http://www.lcd-module.de/fileadmin/eng/pdf/zubehoer/uc1611s_v1_0.pdf

notro commented 9 years ago

Committed, thanks: https://github.com/notro/fbtft/commit/43d452d3b2f4ce9193103603bb453d4a97ff11f6