1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

ili9341: Initial include

This commit adds support for the ili9341 display
This commit is contained in:
Koen Zandberg 2018-09-17 10:26:31 +02:00
parent f2dbd3ba4a
commit ef4dcb8eec
No known key found for this signature in database
GPG Key ID: 0E63411F8FCA8247
7 changed files with 670 additions and 0 deletions

View File

@ -219,6 +219,11 @@ ifneq (,$(filter hts221,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
endif
ifneq (,$(filter ili9341,$(USEMODULE)))
FEATURES_REQUIRED += periph_spi
FEATURES_REQUIRED += periph_gpio
endif
ifneq (,$(filter ina2%,$(USEMODULE)))
USEMODULE += ina2xx
endif

View File

@ -114,6 +114,10 @@ ifneq (,$(filter hts221,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/hts221/include
endif
ifneq (,$(filter ili9341,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ili9341/include
endif
ifneq (,$(filter ina2xx,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ina2xx/include
endif

1
drivers/ili9341/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

306
drivers/ili9341/ili9341.c Normal file
View File

@ -0,0 +1,306 @@
/*
* Copyright (C) 2018 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_ili9341
* @{
*
* @file
* @brief Device driver implementation for the ili9341 display controller
*
* @author Koen Zandberg <koen@bergzand.net>
*
* @}
*/
#include <string.h>
#include "byteorder.h"
#include "periph/spi.h"
#include "xtimer.h"
#include "ili9341.h"
#include "ili9341_internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _ili9341_spi_acquire(ili9341_t *dev)
{
spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_0,
dev->params->spi_clk);
}
static void _ili9341_cmd_start(ili9341_t *dev, uint8_t cmd, bool cont)
{
gpio_clear(dev->params->dcx_pin);
spi_transfer_byte(dev->params->spi, dev->params->cs_pin, cont, cmd);
gpio_set(dev->params->dcx_pin);
}
/* datasheet page 178, table converted to equation.
* gvdd in 1mv increments: 4850 = 4.85V */
static uint8_t _ili9341_calc_pwrctl1(uint16_t gvdd)
{
return (gvdd - 2850) / 50;
}
static uint8_t _ili9341_calc_vmh(uint16_t vcomh)
{
return (vcomh - 2700) / 25;
}
static uint8_t _ili9341_calc_vml(int16_t vcoml)
{
return (vcoml + 2500) / 25;
}
static void _write_cmd(ili9341_t *dev, uint8_t cmd, const uint8_t *data,
size_t len)
{
_ili9341_cmd_start(dev, cmd, len ? true : false);
if (len) {
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data,
NULL, len);
}
}
static void _ili9341_set_area(ili9341_t *dev, uint16_t x1, uint16_t x2,
uint16_t y1, uint16_t y2)
{
be_uint16_t params[2];
params[0] = byteorder_htons(x1);
params[1] = byteorder_htons(x2);
_write_cmd(dev, ILI9341_CMD_CASET, (uint8_t *)params,
sizeof(params));
params[0] = byteorder_htons(y1);
params[1] = byteorder_htons(y2);
_write_cmd(dev, ILI9341_CMD_PASET, (uint8_t *)params,
sizeof(params));
}
int ili9341_init(ili9341_t *dev, const ili9341_params_t *params)
{
dev->params = params;
uint8_t command_params[4] = { 0 };
gpio_init(dev->params->dcx_pin, GPIO_OUT);
int res = spi_init_cs(dev->params->spi, dev->params->cs_pin);
if (res != SPI_OK) {
DEBUG("[ili9341] init: error initializing the CS pin [%i]\n", res);
return -1;
}
if (dev->params->rst_pin != GPIO_UNDEF) {
gpio_init(dev->params->rst_pin, GPIO_OUT);
gpio_clear(dev->params->rst_pin);
xtimer_usleep(120 * US_PER_MS);
gpio_set(dev->params->rst_pin);
}
xtimer_usleep(120 * US_PER_MS);
/* Acquire once at release at the end */
_ili9341_spi_acquire(dev);
/* Soft Reset */
_write_cmd(dev, ILI9341_CMD_SWRESET, NULL, 0);
xtimer_usleep(120 * US_PER_MS);
/* Display off */
_write_cmd(dev, ILI9341_CMD_DISPOFF, NULL, 0);
/* PWRCTL1/2 */
command_params[0] = _ili9341_calc_pwrctl1(ILI9341_GVDD);
_write_cmd(dev, ILI9341_CMD_PWCTRL1, command_params, 1);
command_params[0] = 0x10; /* PWRCTL 0 0 0 */
_write_cmd(dev, ILI9341_CMD_PWCTRL2, command_params, 1);
/* VCOMCTL */
command_params[0] = _ili9341_calc_vmh(ILI9341_VCOMH);
command_params[1] = _ili9341_calc_vml(ILI9341_VCOML);
_write_cmd(dev, ILI9341_CMD_VMCTRL1, command_params, 2);
command_params[0] = 0x86;
_write_cmd(dev, ILI9341_CMD_VMCTRL2, command_params, 1);
/* Memory access CTL */
command_params[0] = ILI9341_MADCTL_HORZ_FLIP | ILI9341_MADCTL_BGR;
_write_cmd(dev, ILI9341_CMD_MADCTL, command_params, 1);
/* Frame control */
command_params[0] = 0x00;
command_params[1] = 0x18;
_write_cmd(dev, ILI9341_CMD_FRAMECTL1, command_params, 2);
/* Display function control */
command_params[0] = 0x08;
command_params[1] = 0x82;
command_params[2] = 0x27; /* 320 lines */
_write_cmd(dev, ILI9341_CMD_DFUNC, command_params, 3);
/* Pixel format */
command_params[0] = 0x55; /* 16 bit mode */
_write_cmd(dev, ILI9341_CMD_PIXSET, command_params, 1);
command_params[0] = 0x01;
_write_cmd(dev, ILI9341_CMD_GAMSET, command_params, 1);
/* Gamma correction */
{
static const uint8_t gamma_pos[] = {
0x0F,
0x31,
0x2B,
0x0C,
0x0E,
0x08,
0x4E,
0xF1,
0x37,
0x07,
0x10,
0x03,
0x0E,
0x09,
0x00
};
_write_cmd(dev, ILI9341_CMD_PGAMCTRL, gamma_pos,
sizeof(gamma_pos));
}
{
static const uint8_t gamma_neg[] = {
0x00,
0x0E,
0x14,
0x03,
0x11,
0x07,
0x31,
0xC1,
0x48,
0x08,
0x0F,
0x0C,
0x31,
0x36,
0x0F
};
_write_cmd(dev, ILI9341_CMD_NGAMCTRL, gamma_neg,
sizeof(gamma_neg));
}
/* Sleep out (turn off sleep mode) */
_write_cmd(dev, ILI9341_CMD_SLPOUT, NULL, 0);
/* Display on */
_write_cmd(dev, ILI9341_CMD_DISPON, NULL, 0);
spi_release(dev->params->spi);
return 0;
}
void ili9341_write_cmd(ili9341_t *dev, uint8_t cmd, const uint8_t *data,
size_t len)
{
_ili9341_spi_acquire(dev);
_write_cmd(dev, cmd, data, len);
spi_release(dev->params->spi);
}
void ili9341_read_cmd(ili9341_t *dev, uint8_t cmd, uint8_t *data, size_t len)
{
assert(len);
_ili9341_spi_acquire(dev);
_ili9341_cmd_start(dev, cmd, true);
/* Dummy transfer */
spi_transfer_byte(dev->params->spi, dev->params->cs_pin, true, 0x00);
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, NULL,
data, len);
spi_release(dev->params->spi);
}
void ili9341_fill(ili9341_t *dev, uint16_t x1, uint16_t x2, uint16_t y1,
uint16_t y2, uint16_t color)
{
/* Send fill area to the display */
/* Calculate number of pixels */
int32_t num_pix = (x2 - x1 + 1) * (y2 - y1 + 1);
DEBUG("[ili9341]: Write x1: %" PRIu16 ", x2: %" PRIu16 ", "
"y1: %" PRIu16 ", y2: %" PRIu16 ". Num pixels: %lu\n",
x1, x2, y1, y2, (unsigned long)num_pix);
/* Send fill area to the display */
_ili9341_spi_acquire(dev);
_ili9341_set_area(dev, x1, x2, y1, y2);
/* Memory access command */
_ili9341_cmd_start(dev, ILI9341_CMD_RAMWR, true);
#if ILI9341_LE_MODE
color = htons(color);
#endif
for (int i = 0; i < (num_pix - 1); i++) {
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, true,
(uint8_t *)&color, NULL, sizeof(color));
}
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false,
(uint8_t *)&color, NULL, sizeof(color));
spi_release(dev->params->spi);
}
void ili9341_pixmap(ili9341_t *dev, uint16_t x1, uint16_t x2,
uint16_t y1, uint16_t y2, const uint16_t *color)
{
size_t num_pix = (x2 - x1 + 1) * (y2 - y1 + 1);
DEBUG("[ili9341]: Write x1: %" PRIu16 ", x2: %" PRIu16 ", "
"y1: %" PRIu16 ", y2: %" PRIu16 ". Num pixels: %lu\n",
x1, x2, y1, y2, (unsigned long)num_pix);
_ili9341_spi_acquire(dev);
/* Send fill area to the display */
_ili9341_set_area(dev, x1, x2, y1, y2);
/* Memory access command */
_ili9341_cmd_start(dev, ILI9341_CMD_RAMWR, true);
#if ILI9341_LE_MODE
for (size_t i = 0; i < num_pix - 1; i++) {
uint16_t ncolor = htons(*(color + i));
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, true,
&ncolor, NULL, sizeof(uint16_t));
}
uint16_t ncolor = htons(*(color + num_pix - 1));
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false,
&ncolor, NULL, sizeof(uint16_t));
#else
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false,
(const uint8_t *)color, NULL, num_pix * 2);
#endif
spi_release(dev->params->spi);
}
void ili9341_invert_on(ili9341_t *dev)
{
ili9341_write_cmd(dev, ILI9341_CMD_DINVON, NULL, 0);
}
void ili9341_invert_off(ili9341_t *dev)
{
ili9341_write_cmd(dev, ILI9341_CMD_DINVOFF, NULL, 0);
}
void ili9341_set_brightness(ili9341_t *dev, uint8_t brightness)
{
ili9341_write_cmd(dev, ILI9341_CMD_WRDISBV, &brightness, 1);
uint8_t param = 0x26;
ili9341_write_cmd(dev, ILI9341_CMD_WRCTRLD, &param, 1);
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2018 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_ili9341
* @{
*
* @file
* @brief Device driver implementation for the ili9341 display controller
*
* @author Koen Zandberg <koen@bergzand.net>
*
* @}
*/
#ifndef ILI9341_INTERNAL_H
#define ILI9341_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name ILI9341 commands
*
* Not exhaustive, please extend when required
* @{
*/
#define ILI9341_CMD_SWRESET 0x01 /**< Software reset */
#define ILI9341_CMD_RDDIDIF 0x04 /**< Read display ID */
#define ILI9341_CMD_SPLIN 0x10 /**< Enter sleep mode */
#define ILI9341_CMD_SLPOUT 0x11 /**< Sleep out */
#define ILI9341_CMD_DINVOFF 0x20 /**< Display inversion off */
#define ILI9341_CMD_DINVON 0x21 /**< Display inversion on */
#define ILI9341_CMD_GAMSET 0x26 /**< Gamma Set */
#define ILI9341_CMD_DISPOFF 0x28 /**< Display OFF */
#define ILI9341_CMD_DISPON 0x29 /**< Display ON */
#define ILI9341_CMD_CASET 0x2A /**< Column Address Set */
#define ILI9341_CMD_PASET 0x2b /**< Page Address Set */
#define ILI9341_CMD_RAMWR 0x2c /**< Memory Write */
#define ILI9341_CMD_RAMRD 0x2e /**< Memory Read */
#define ILI9341_CMD_MADCTL 0x36 /**< Memory data access control */
#define ILI9341_CMD_IDMOFF 0x38 /**< Idle Mode OFF */
#define ILI9341_CMD_IDMON 0x39 /**< Idle Mode ON */
#define ILI9341_CMD_PIXSET 0x3A /**< COLMOD: Pixel Format Set */
#define ILI9341_CMD_WRDISBV 0x51 /**< Write Display Brightness */
#define ILI9341_CMD_WRCTRLD 0x53 /**< Write Control Display */
#define ILI9341_CMD_RDCTRLD 0x54 /**< Read Control Display */
#define ILI9341_CMD_FRAMECTL1 0xb1 /**< Frame control normal*/
#define ILI9341_CMD_FRAMECTL2 0xb2 /**< Frame control idle */
#define ILI9341_CMD_FRAMECTL3 0xb3 /**< Frame control partial */
#define ILI9341_CMD_DFUNC 0xb6 /**< Display function control */
#define ILI9341_CMD_PWCTRL1 0xc0 /**< Power control 1 */
#define ILI9341_CMD_PWCTRL2 0xc1 /**< Power control 2 */
#define ILI9341_CMD_VMCTRL1 0xc5 /**< VCOM control 1 */
#define ILI9341_CMD_VMCTRL2 0xc7 /**< VCOM control 2 */
#define ILI9341_CMD_PGAMCTRL 0xe0 /**< Positive gamma correction */
#define ILI9341_CMD_NGAMCTRL 0xe1 /**< Negative gamma correction */
#define ILI9341_CMD_IFCTL 0xf6 /**< Interface control */
/** @} */
/**
* @name Memory access control bits
* @{
*/
#define ILI9341_MADCTL_MY 0x80 /**< Row address order */
#define ILI9341_MADCTL_MX 0x40 /**< Column access order */
#define ILI9341_MADCTL_MV 0x20 /**< Row column exchange */
#define ILI9341_MADCTL_ML 0x10 /**< Vertical refresh order */
#define ILI9341_MADCTL_BGR 0x08 /**< Color selector switch control */
#define ILI9341_MADCTL_MH 0x04 /**< Horizontal refresh direction */
/** @} */
/**
* @name Display rotation modes
* @{
*/
#define ILI9341_MADCTL_VERT ILI9341_MADCTL_MX /**< Vertical mode */
#define ILI9341_MADCTL_VERT_FLIP ILI9341_MADCTL_MY /**< Flipped vertical */
#define ILI9341_MADCTL_HORZ ILI9341_MADCTL_MV /**< Horizontal mode */
#define ILI9341_MADCTL_HORZ_FLIP ILI9341_MADCTL_MV | \
ILI9341_MADCTL_MY | \
ILI9341_MADCTL_MX /**< Horizontal flipped */
/** @} */
#define ILI9341_PIXSET_16BIT 0x55 /**< MCU and RGB 16 bit interface */
#define ILI9341_PIXSET_18BIT 0x66 /**< MCU and RGB 18 bit interface (not implemented) */
#ifdef __cplusplus
}
#endif
#endif /* ILI9341_INTERNAL_H */

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2018 Koen Zandberg <koen@bergzand.net>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_ili9341
*
* @{
* @file
* @brief Default configuration for ili9341
*
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef ILI9341_PARAMS_H
#define ILI9341_PARAMS_H
#include "board.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Default parameters for ILI9341 display */
/**
* @name Set default configuration parameters for the ILI9341
* @{
*/
#ifndef ILI9341_PARAM_SPI
#define ILI9341_PARAM_SPI SPI_DEV(0)
#endif
#ifndef ILI9341_PARAM_SPI_CLK
#define ILI9341_PARAM_SPI_CLK SPI_CLK_5MHZ
#endif
#ifndef ILI9341_PARAM_CS
#define ILI9341_PARAM_CS GPIO_PIN(2, 2)
#endif
#ifndef ILI9341_PARAM_DCX
#define ILI9341_PARAM_DCX GPIO_PIN(3, 13)
#endif
#ifndef ILI9341_PARAM_RST
#define ILI9341_PARAM_RST GPIO_UNDEF
#endif
#ifndef ILI9341_PARAMS
#define ILI9341_PARAMS { .spi = ILI9341_PARAM_SPI, \
.spi_clk = ILI9341_PARAM_SPI_CLK, \
.cs_pin = ILI9341_PARAM_CS, \
.dcx_pin = ILI9341_PARAM_DCX, \
.rst_pin = ILI9341_PARAM_RST }
#endif
/**@}*/
/**
* @brief Configure ILI9341
*/
static const ili9341_params_t ili9341_params[] =
{
ILI9341_PARAMS,
};
#ifdef __cplusplus
}
#endif
#endif /* ILI9341_PARAMS_H */

185
drivers/include/ili9341.h Normal file
View File

@ -0,0 +1,185 @@
/*
* Copyright (C) 2018 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_ili9341 ili9341 display driver
* @ingroup drivers
*
* @brief ili9341 Display driver
*
* @{
*
* @file
* @brief Driver for ili941 display
*
* @author Koen Zandberg <koen@bergzand.net>
*
* The ILI9341 is a generic display driver for small RGB displays. The driver
* implemented here operates over SPI to communicate with the device.
*
* The device requires colors to be send in big endian RGB-565 format. The
* @ref ILI9341_LE_MODE compile time option can switch this, but only use this
* when strictly necessary. This option will slow down the driver as it
* certainly can't use DMA anymore, every short has to be converted before
* transfer.
*/
#ifndef ILI9341_H
#define ILI9341_H
#include "periph/spi.h"
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ILI9341_GVDD
/**
* @brief ili9341 gvdd level.
*
* Default GVDD voltage of 4.8V
*/
#define ILI9341_GVDD 4800
#endif /* ILI9341_GVDD */
#ifndef ILI9341_VCOMH
/**
* @brief ili9341 VCOMH voltage level.
*
* Default VCOMH voltage of 4.25V
*/
#define ILI9341_VCOMH 4250
#endif /* ILI9341_VCOMH */
#ifndef ILI9341_VCOML
/**
* @brief ili9341 VCOML voltage level.
*
* Default VCOMH voltage of -2V
*/
#define ILI9341_VCOML -2000
#endif /* ILI9341_VCOML */
#ifndef ILI9341_LE_MODE
/**
* @brief Compile time switch to change the driver to convert little endian
* colors to big endian.
*/
#define ILI9341_LE_MODE (0)
#endif
/**
* @brief Device initialization parameters
*/
typedef struct {
spi_t spi; /**< SPI device that the display is connected to */
spi_clk_t spi_clk; /**< SPI clock speed to use */
gpio_t cs_pin; /**< pin connected to the CHIP SELECT line */
gpio_t dcx_pin; /**< pin connected to the DC line */
gpio_t rst_pin; /**< pin connected to the reset line */
} ili9341_params_t;
/**
* @brief Device descriptor for a ili9341
*/
typedef struct {
const ili9341_params_t *params; /**< Device initialization parameters */
} ili9341_t;
/**
* @brief Setup an ili9341 display device
*
* @param[out] dev device descriptor
* @param[in] params parameters for device initialization
*/
int ili9341_init(ili9341_t *dev, const ili9341_params_t *params);
/**
* @brief Fill a rectangular area with a single pixel color
*
* the rectangular area is defined as x1 being the first column of pixels and
* x2 being the last column of pixels to fill. similar to that, y1 is the first
* row to fill and y2 is the last row to fill.
*
* @param[in] dev device descriptor
* @param[in] x1 x coordinate of the first corner
* @param[in] x2 x coordinate of the opposite corner
* @param[in] y1 y coordinate of the first corner
* @param[in] y2 y coordinate of the opposite corner
* @param[in] color single color to fill the area with
*/
void ili9341_fill(ili9341_t *dev, uint16_t x1, uint16_t x2,
uint16_t y1, uint16_t y2, uint16_t color);
/**
* @brief Fill a rectangular area with an array of pixels
*
* the rectangular area is defined as x1 being the first column of pixels and
* x2 being the last column of pixels to fill. similar to that, y1 is the first
* row to fill and y2 is the last row to fill.
*
* @note @p color must have a length equal to `(x2 - x1 + 1) * (y2 - y1 + 1)`
*
* @param[in] dev device descriptor
* @param[in] x1 x coordinate of the first corner
* @param[in] x2 x coordinate of the opposite corner
* @param[in] y1 y coordinate of the first corner
* @param[in] y2 y coordinate of the opposite corner
* @param[in] color array of colors to fill the area with
*/
void ili9341_pixmap(ili9341_t *dev, uint16_t x1, uint16_t x2, uint16_t y1,
uint16_t y2, const uint16_t *color);
/**
* @brief Raw write command
*
* @param[in] dev device descriptor
* @param[in] cmd command code
* @param[in] data command data to the device
* @param[in] len length of the command data
*/
void ili9341_write_cmd(ili9341_t *dev, uint8_t cmd, const uint8_t *data,
size_t len);
/**
* @brief Raw read command
*
* @pre len > 0
*
* @param[in] dev device descriptor
* @param[in] cmd command
* @param[out] data data from the device
* @param[in] len length of the returned data
*/
void ili9341_read_cmd(ili9341_t *dev, uint8_t cmd, uint8_t *data, size_t len);
/**
* @brief Invert the display colors
*
* @param[in] dev device descriptor
*/
void ili9341_invert_on(ili9341_t *dev);
/**
* @brief Disable color inversion
*
* @param[in] dev device descriptor
*/
void ili9341_invert_off(ili9341_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* ILI9341_H */
/** @} */