driver: ILI9340 LCD display driver

Minimal driver for ILI9340 LCD display driver including support
for adafruit 2.2" LCD display (1480)

Signed-off-by: Jan Van Winkel <jan.van_winkel@dxplore.eu>
This commit is contained in:
Jan Van Winkel 2017-10-06 08:34:43 +02:00 committed by Anas Nashif
commit ca16779d9e
17 changed files with 562 additions and 27 deletions

View file

@ -2,3 +2,7 @@ zephyr_sources_ifdef(CONFIG_MICROBIT_DISPLAY
mb_display.c
mb_font.c
)
zephyr_sources_ifdef(CONFIG_ILI9340 display_ili9340.c)
zephyr_sources_ifdef(CONFIG_ILI9340_LCD_ADAFRUIT_1480
display_ili9340_adafruit_1480.c
)

View file

@ -6,34 +6,17 @@
# SPDX-License-Identifier: Apache-2.0
#
config MICROBIT_DISPLAY
bool "BBC micro:bit 5x5 LED Display support"
depends on BOARD_BBC_MICROBIT
depends on PRINTK
depends on GPIO
menuconfig DISPLAY
bool
prompt "Display Drivers"
default n
help
Enable this to be able to display images and text on the 5x5
LED matrix display on the BBC micro:bit.
Enable display drivers
if MICROBIT_DISPLAY
if DISPLAY
config MICROBIT_DISPLAY_PIN_GRANULARITY
bool "Access the GPIO on a per-pin instead of per-port basis"
help
By default, the micro:bit display driver will update the GPIO
pins of the display (pins 4 through 15) by accessing the entire
GPIO port. This is done for efficiency, however it may interfere
with other peripherals connected to the same GPIO port. Select
this option if other peripherals are connected to the same GPIO
port.
source "drivers/display/Kconfig.microbit"
config MICROBIT_DISPLAY_STR_MAX
int "Maximum length of strings that can be shown on the display"
range 3 255
default 40
help
This value specifies the maximum length of strings that can
be displayed using the mb_display_string() and mb_display_print()
APIs.
source "drivers/display/Kconfig.ili9340"
endif # MICROBIT_DISPLAY
endif # DISPLAY

View file

@ -0,0 +1,114 @@
# Kconfig - ILI9340 display driver configuration options
#
# Copyright (c) 2017 dXplore
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig ILI9340
bool "ILI9340 display driver"
depends on SPI
default n
help
Enable driver for ILI9340 display driver.
if ILI9340
config ILI9340_DEV_NAME
string "ILI9340 device name"
default "ILI9340"
config ILI9340_SPI_DEV_NAME
string "SPI master where ILI9340 is connected"
default "SPI_0"
help
Specify the device name of the SPI master device to which ILI9340 is
connected.
config ILI9340_SPI_SLAVE_NUMBER
int "SPI Slave number where ILI9340 is connected"
default 0
help
The number of the slave on the host controller to which ILI9340 is
connected.
config ILI9340_SPI_FREQ
int "SPI Frequency"
default 15151515
help
Frequency to be used to communicate with the ILI9340.
config SYS_LOG_ILI9340_LEVEL
int "ILI9340 Sys Log level"
depends on SYS_LOG
default 0
range 0 4
help
Sets log level for ILI9340 display driver.
Levels are:
- 0 OFF: do not write
- 1 ERROR: only write SYS_LOG_ERR
- 2 WARNING: write SYS_LOG_WRN in addition to previous level
- 3 INFO: write SYS_LOG_INF in addition to previous levels
- 4 DEBUG: write SYS_LOG_DBG in addition to previous levels
config ILI9340_RESET_GPIO_PORT_NAME
string "Reset GPIO controller port name"
default "GPIO_0"
help
GPIO controller where the interrupt pin is attached to.
config ILI9340_RESET_PIN
int "ILI9340 Reset pin"
default 0
help
ILI9340 reset pin.
config ILI9340_CMD_DATA_GPIO_PORT_NAME
string "Command/Data GPIO controller port name"
default "GPIO_0"
help
GPIO controller where the command/data pin is attached to.
config ILI9340_CMD_DATA_PIN
int "ILI9340 command/data pin"
default 1
help
ILI9340 command data pin.
config ILI9340_GPIO_CS
bool "Use GPIO pin for chip select"
default n
help
Use GPIO pin for chips select.
if ILI9340_GPIO_CS
config ILI9340_CS_GPIO_PORT_NAME
string "Chip select GPIO controller port name"
default "GPIO_0"
help
GPIO controller where the chip select pin is attached to.
config ILI9340_CS_GPIO_PIN
int "ILI9340 GPIO chip select"
default 1
help
ILI9340 GPIO chip select pin.
endif #ILI9340_GPIO_CS
choice
prompt "LCD"
default ILI9340_LCD_ADAFRUIT_1480
help
Specify the type of LCD connected to the ILI9340 display controller.
config ILI9340_LCD_ADAFRUIT_1480
bool
prompt "Adafruit 2.2\" TFT 1480"
endchoice
endif #ILI9340

View file

@ -0,0 +1,39 @@
# Kconfig - Microbit display driver configuration options
#
# Copyright (c) 2017 dXplore
#
# SPDX-License-Identifier: Apache-2.0
#
config MICROBIT_DISPLAY
bool "BBC micro:bit 5x5 LED Display support"
depends on BOARD_BBC_MICROBIT
depends on PRINTK
depends on GPIO
help
Enable this to be able to display images and text on the 5x5
LED matrix display on the BBC micro:bit.
if MICROBIT_DISPLAY
config MICROBIT_DISPLAY_PIN_GRANULARITY
bool "Access the GPIO on a per-pin instead of per-port basis"
help
By default, the micro:bit display driver will update the GPIO
pins of the display (pins 4 through 15) by accessing the entire
GPIO port. This is done for efficiency, however it may interfere
with other peripherals connected to the same GPIO port. Select
this option if other peripherals are connected to the same GPIO
port.
config MICROBIT_DISPLAY_STR_MAX
int "Maximum length of strings that can be shown on the display"
range 3 255
default 40
help
This value specifies the maximum length of strings that can
be displayed using the mb_display_string() and mb_display_print()
APIs.
endif # MICROBIT_DISPLAY

View file

@ -0,0 +1,184 @@
/* Copyright (c) 2017 dXplore
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "display_ili9340.h"
#include <drivers/display/ili9340.h>
#define SYS_LOG_DOMAIN "ILI9340"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ILI9340_LEVEL
#include <logging/sys_log.h>
#include <gpio.h>
#include <misc/byteorder.h>
#include <spi.h>
struct ili9340_data {
struct device *reset_gpio;
struct device *command_data_gpio;
struct device *spi_dev;
struct spi_config spi_config;
#ifdef CONFIG_ILI9340_GPIO_CS
struct spi_cs_control cs_ctrl;
#endif
};
#define ILI9340_CMD_DATA_PIN_COMMAND 0
#define ILI9340_CMD_DATA_PIN_DATA 1
static void ili9340_exit_sleep(struct ili9340_data *data);
static void ili9340_set_mem_area(struct ili9340_data *data, const u16_t x,
const u16_t y, const u16_t w, const u16_t h);
static int ili9340_init(struct device *dev);
int ili9340_init(struct device *dev)
{
struct ili9340_data *data = (struct ili9340_data *)dev->driver_data;
SYS_LOG_DBG("Initializing display driver");
data->spi_dev = device_get_binding(CONFIG_ILI9340_SPI_DEV_NAME);
if (data->spi_dev == NULL) {
SYS_LOG_ERR("Could not get SPI device for ILI9340");
return -EPERM;
}
data->spi_config.frequency = CONFIG_ILI9340_SPI_FREQ;
data->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8);
data->spi_config.slave = CONFIG_ILI9340_SPI_SLAVE_NUMBER;
#ifdef CONFIG_ILI9340_GPIO_CS
data->cs_ctrl.gpio_dev =
device_get_binding(CONFIG_ILI9340_CS_GPIO_PORT_NAME);
data->cs_ctrl.gpio_pin = CONFIG_ILI9340_CS_GPIO_PIN;
data->cs_ctrl.delay = 0;
data->spi_config.cs = &(data->cs_ctrl);
#else
data->spi_config.cs = NULL;
#endif
data->reset_gpio =
device_get_binding(CONFIG_ILI9340_RESET_GPIO_PORT_NAME);
if (data->reset_gpio == NULL) {
SYS_LOG_ERR("Could not get GPIO port for ILI9340 reset");
return -EPERM;
}
gpio_pin_configure(data->reset_gpio, CONFIG_ILI9340_RESET_PIN,
GPIO_DIR_OUT);
data->command_data_gpio =
device_get_binding(CONFIG_ILI9340_CMD_DATA_GPIO_PORT_NAME);
if (data->command_data_gpio == NULL) {
SYS_LOG_ERR("Could not get GPIO port for ILI9340 command/data");
return -EPERM;
}
gpio_pin_configure(data->command_data_gpio, CONFIG_ILI9340_CMD_DATA_PIN,
GPIO_DIR_OUT);
SYS_LOG_DBG("Resetting display driver");
gpio_pin_write(data->reset_gpio, CONFIG_ILI9340_RESET_PIN, 1);
k_sleep(1);
gpio_pin_write(data->reset_gpio, CONFIG_ILI9340_RESET_PIN, 0);
k_sleep(1);
gpio_pin_write(data->reset_gpio, CONFIG_ILI9340_RESET_PIN, 1);
k_sleep(5);
SYS_LOG_DBG("Initializing LCD");
ili9340_lcd_init(data);
SYS_LOG_DBG("Exiting sleep mode");
ili9340_exit_sleep(data);
/* device_get_binding checks if driver_api is not zero before checking
* device name.
* So just set driver_api to 1 else the function call will fail
*/
dev->driver_api = (void *)1;
return 0;
}
void ili9340_write_pixel(const struct device *dev, const u16_t x, const u16_t y,
const u8_t r, const u8_t g, const u8_t b)
{
u8_t rgb_data[] = {r, g, b};
SYS_LOG_DBG("Writing pixel @ %dx%d (x,y)", x, y);
ili9340_write_bitmap(dev, x, y, 1, 1, &rgb_data[0]);
}
void ili9340_write_bitmap(const struct device *dev, const u16_t x,
const u16_t y, const u16_t w, const u16_t h,
const u8_t *rgb_data)
{
struct ili9340_data *data = (struct ili9340_data *)dev->driver_data;
SYS_LOG_DBG("Writing %dx%d (w,h) bitmap @ %dx%d (x,y)", w, h, x, y);
ili9340_set_mem_area(data, x, y, w, h);
ili9340_transmit(data, ILI9340_CMD_MEM_WRITE, (void *)rgb_data,
3 * w * h);
}
void ili9340_display_on(struct device *dev)
{
struct ili9340_data *data = (struct ili9340_data *)dev->driver_data;
SYS_LOG_DBG("Turning display on");
ili9340_transmit(data, ILI9340_CMD_DISPLAY_ON, NULL, 0);
}
void ili9340_display_off(struct device *dev)
{
struct ili9340_data *data = (struct ili9340_data *)dev->driver_data;
SYS_LOG_DBG("Turning display off");
ili9340_transmit(data, ILI9340_CMD_DISPLAY_OFF, NULL, 0);
}
void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data,
size_t tx_len)
{
struct spi_buf tx_buf = {.buf = &cmd, .len = 1};
struct spi_buf_set tx_bufs = {.buffers = &tx_buf, .count = 1};
gpio_pin_write(data->command_data_gpio, CONFIG_ILI9340_CMD_DATA_PIN,
ILI9340_CMD_DATA_PIN_COMMAND);
spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
if (tx_data != NULL) {
tx_buf.buf = tx_data;
tx_buf.len = tx_len;
gpio_pin_write(data->command_data_gpio,
CONFIG_ILI9340_CMD_DATA_PIN,
ILI9340_CMD_DATA_PIN_DATA);
spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
}
}
void ili9340_exit_sleep(struct ili9340_data *data)
{
ili9340_transmit(data, ILI9340_CMD_EXIT_SLEEP, NULL, 0);
k_sleep(120);
}
void ili9340_set_mem_area(struct ili9340_data *data, const u16_t x,
const u16_t y, const u16_t w, const u16_t h)
{
u16_t spi_data[2];
spi_data[0] = sys_cpu_to_be16(x);
spi_data[1] = sys_cpu_to_be16(x + w - 1);
ili9340_transmit(data, ILI9340_CMD_COLUMN_ADDR, &spi_data[0], 4);
spi_data[0] = sys_cpu_to_be16(y);
spi_data[1] = sys_cpu_to_be16(y + h - 1);
ili9340_transmit(data, ILI9340_CMD_PAGE_ADDR, &spi_data[0], 4);
}
static struct ili9340_data ili9340_data;
DEVICE_INIT(ili9340, CONFIG_ILI9340_DEV_NAME, &ili9340_init, &ili9340_data,
NULL, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

View file

@ -0,0 +1,63 @@
/* Copyright (c) 2017 dXplore
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef DISPLAY_ILI9340_H
#define DISPLAY_ILI9340_H
#include <zephyr.h>
#define ILI9340_CMD_ENTER_SLEEP 0x10
#define ILI9340_CMD_EXIT_SLEEP 0x11
#define ILI9340_CMD_GAMMA_SET 0x26
#define ILI9340_CMD_DISPLAY_OFF 0x28
#define ILI9340_CMD_DISPLAY_ON 0x29
#define ILI9340_CMD_COLUMN_ADDR 0x2a
#define ILI9340_CMD_PAGE_ADDR 0x2b
#define ILI9340_CMD_MEM_WRITE 0x2c
#define ILI9340_CMD_MEM_ACCESS_CTRL 0x36
#define ILI9340_CMD_PIXEL_FORMAT_SET 0x3A
#define ILI9340_CMD_FRAME_CTRL_NORMAL_MODE 0xB1
#define ILI9340_CMD_DISPLAY_FUNCTION_CTRL 0xB6
#define ILI9340_CMD_POWER_CTRL_1 0xC0
#define ILI9340_CMD_POWER_CTRL_2 0xC1
#define ILI9340_CMD_VCOM_CTRL_1 0xC5
#define ILI9340_CMD_VCOM_CTRL_2 0xC7
#define ILI9340_CMD_POSITVE_GAMMA_CORRECTION 0xE0
#define ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION 0xE1
#define ILI9340_DATA_MEM_ACCESS_CTRL_MY 0x80
#define ILI9340_DATA_MEM_ACCESS_CTRL_MX 0x40
#define ILI9340_DATA_MEM_ACCESS_CTRL_MV 0x20
#define ILI9340_DATA_MEM_ACCESS_CTRL_ML 0x10
#define ILI9340_DATA_MEM_ACCESS_CTRL_BGR 0x08
#define ILI9340_DATA_MEM_ACCESS_CTRL_MH 0x04
#define ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT 0x60
#define ILI9340_DATA_PIXEL_FORMAT_RGB_16_BIT 0x50
#define ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT 0x06
#define ILI9340_DATA_PIXEL_FORMAT_MCU_16_BIT 0x05
struct ili9340_data;
/**
* Send data to ILI9340 display controller
*
* @param data Device data structure
* @param cmd Command to send to display controller
* @param tx_data Data to transmit to the display controller
* In case no data should be transmitted pass a NULL pointer
* @param tx_len Number of bytes in tx_data buffer
*
*/
void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data,
size_t tx_len);
/**
* Perform LCD specific initialization
*
* @param data Device data structure
*/
void ili9340_lcd_init(struct ili9340_data *data);
#endif /* DISPLAY_ILI9340_H */

View file

@ -0,0 +1,80 @@
/* Copyright (c) 2017 dXplore
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "display_ili9340.h"
void ili9340_lcd_init(struct ili9340_data *data)
{
u8_t tx_data[15];
tx_data[0] = 0x23;
ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_1, tx_data, 1);
tx_data[0] = 0x10;
ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_2, tx_data, 1);
tx_data[0] = 0x3e;
tx_data[1] = 0x28;
ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_1, tx_data, 2);
tx_data[0] = 0x86;
ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_2, tx_data, 1);
tx_data[0] =
ILI9340_DATA_MEM_ACCESS_CTRL_MV | ILI9340_DATA_MEM_ACCESS_CTRL_BGR;
ili9340_transmit(data, ILI9340_CMD_MEM_ACCESS_CTRL, tx_data, 1);
tx_data[0] = ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT |
ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT;
ili9340_transmit(data, ILI9340_CMD_PIXEL_FORMAT_SET, tx_data, 1);
tx_data[0] = 0x00;
tx_data[1] = 0x18;
ili9340_transmit(data, ILI9340_CMD_FRAME_CTRL_NORMAL_MODE, tx_data, 2);
tx_data[0] = 0x08;
tx_data[1] = 0x82;
tx_data[2] = 0x27;
ili9340_transmit(data, ILI9340_CMD_DISPLAY_FUNCTION_CTRL, tx_data, 3);
tx_data[0] = 0x01;
ili9340_transmit(data, ILI9340_CMD_GAMMA_SET, tx_data, 1);
tx_data[0] = 0x0F;
tx_data[1] = 0x31;
tx_data[2] = 0x2B;
tx_data[3] = 0x0C;
tx_data[4] = 0x0E;
tx_data[5] = 0x08;
tx_data[6] = 0x4E;
tx_data[7] = 0xF1;
tx_data[8] = 0x37;
tx_data[9] = 0x07;
tx_data[10] = 0x10;
tx_data[11] = 0x03;
tx_data[12] = 0x0E;
tx_data[13] = 0x09;
tx_data[14] = 0x00;
ili9340_transmit(data, ILI9340_CMD_POSITVE_GAMMA_CORRECTION, tx_data,
15);
tx_data[0] = 0x00;
tx_data[1] = 0x0E;
tx_data[2] = 0x14;
tx_data[3] = 0x03;
tx_data[4] = 0x11;
tx_data[5] = 0x07;
tx_data[6] = 0x31;
tx_data[7] = 0xC1;
tx_data[8] = 0x48;
tx_data[9] = 0x08;
tx_data[10] = 0x0F;
tx_data[11] = 0x0C;
tx_data[12] = 0x31;
tx_data[13] = 0x36;
tx_data[14] = 0x0F;
ili9340_transmit(data, ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION, tx_data,
15);
}