[Ion] Much simpler display driver

Change-Id: Ia64a14ca5ed58f70590a27b968569832a0ecb722
This commit is contained in:
Romain Goyet
2016-08-18 15:57:23 +02:00
parent a0aa16c504
commit 596d0d3876
18 changed files with 208 additions and 778 deletions

View File

@@ -1,11 +1,5 @@
include ion/src/device/boot/Makefile
objs += $(addprefix ion/src/device/, init.o heap.o led.o)
objs += $(addprefix ion/src/device/display/,\
display.o\
gpio.o\
spi.o\
st7789.o\
)
objs += $(addprefix ion/src/device/, init.o heap.o led.o display.o)
# When using the register.h C++ file in production mode, we expect the compiler
# to completely inline all bit manipulations. For some reason, if we build using
@@ -16,6 +10,7 @@ objs += $(addprefix ion/src/device/display/,\
ifneq ($(DEBUG),1)
ifneq ($(COMPILER),llvm)
ion/src/device/led.o: SFLAGS+=-O3
ion/src/device/display.o: SFLAGS+=-O3
endif
endif

View File

@@ -1,29 +1,13 @@
extern "C" {
#include <ion/led.h>
}
#include "display.h"
#include "regs/regs.h"
#include "regs/fsmc.h"
extern "C" {
void ion_lcd_init();
#include <assert.h>
}
/* Pinout:
* PA2 - D4
* PA3 - D5
* PA4 - D6
* PA5 - D7
* PB14 - D0
* PC3 - A0
* PC4 - NE4
* PC5 - NOE
* PC6 - D1
* PC11 - D2
* PC12 - D3
* PD2 - NWE
*/
#define SEND_COMMAND(c) {*CommandAddress = LCDCommand::c;}
#define SEND_DATA(...) { uint8_t data[] = {__VA_ARGS__}; for (unsigned int i=0;i<sizeof(data);i++) { *DataAddress = data[i];};}
void ion_lcd_gpio_init() {
void Ion::Screen::initGPIO() {
// We use GPIOA to GPIOD
RCC.AHB1ENR()->setGPIOAEN(true);
RCC.AHB1ENR()->setGPIOBEN(true);
@@ -72,7 +56,7 @@ void ion_lcd_gpio_init() {
GPIOD.AFR()->setAFR(2, GPIO::AFR::AlternateFunction::AF10);
}
void ion_lcd_fsmc_init() {
void Ion::Screen::initFSMC() {
// FSMC lives on the AHB3 bus. Let's enable its clock. */
RCC.AHB3ENR()->setFSMCEN(true);
@@ -91,14 +75,102 @@ void ion_lcd_fsmc_init() {
FSMC.BCR(4)->setMBKEN(true);
// Timing register
/*
FSMC.BTR(1)->setADDSET(6);
FSMC.BTR(1)->setDATAST(10);
FSMC.BTR(1)->setBUSTURN(10);
*/
FSMC.BTR(4)->setADDSET(0);
FSMC.BTR(4)->setDATAST(0);
FSMC.BTR(4)->setBUSTURN(0);
}
void ion_lcd_init() {
static inline void delayms(long ms) {
for (long i=0; i<1040*ms; i++) {
__asm volatile("nop");
}
}
void Ion::Screen::initPanel() {
//*CommandAddress = 0x01; //software reset
//delayms(5);
*CommandAddress = LCDCommand::SleepOut;
delayms(120);
*CommandAddress = LCDCommand::PowerControlB;
SEND_DATA(0x00, 0x83, 0x30);
//*DataAddress = 0x00; *DataAddress = 0x83; *DataAddress = 0X30;
*CommandAddress = LCDCommand::PowerOnSequenceControl;
*DataAddress = 0x64; *DataAddress = 0x03; *DataAddress = 0X12;
*DataAddress = 0X81;
*CommandAddress = LCDCommand::DriverTimingControlA;
*DataAddress = 0x85; *DataAddress = 0x01; *DataAddress = 0x79;
*CommandAddress = LCDCommand::PowerControlA;
*DataAddress = 0x39; *DataAddress = 0x2C; *DataAddress = 0x00;
*DataAddress = 0x34; *DataAddress = 0x02;
*CommandAddress = LCDCommand::PumpRatioControl;
*DataAddress = 0x20;
*CommandAddress = LCDCommand::DriverTimingControlB;
*DataAddress = 0x00; *DataAddress = 0x00;
*CommandAddress = LCDCommand::PowerControl2; //Power control
*DataAddress = 0x11; //SAP[2:0];BT[3:0]
*CommandAddress = LCDCommand::VCOMControl1; //VCM control 1
*DataAddress = 0x34;
*DataAddress = 0x3D;
*CommandAddress = LCDCommand::VCOMControl2; //VCM control 2
*DataAddress = 0xC0;
*CommandAddress = LCDCommand::MemoryAccessControl; // Memory Access Control
*DataAddress = 0xA0;
*CommandAddress = LCDCommand::PixelFormatSet; // Pixel format
*DataAddress = 0x55; //16bit
*CommandAddress = LCDCommand::FrameRateControl; // Frame rate
*DataAddress = 0x00;
*DataAddress = 0x1D; //65Hz
*CommandAddress = LCDCommand::DisplayFunctionControl; // Display Function Control
*DataAddress = 0x0A; *DataAddress = 0xA2; *DataAddress = 0x27;
*DataAddress = 0x00;
*CommandAddress = LCDCommand::EntryMode; //Entry mode
*DataAddress = 0x07;
*CommandAddress = LCDCommand::Enable3G; // 3Gamma Function Disable
*DataAddress = 0x08;
*CommandAddress = LCDCommand::GammaSet; //Gamma curve selected
*DataAddress = 0x01;
*CommandAddress = LCDCommand::PositiveGammaCorrection; //positive gamma correction
*DataAddress = 0x1f; *DataAddress = 0x1a; *DataAddress = 0x18;
*DataAddress = 0x0a; *DataAddress = 0x0f; *DataAddress = 0x06;
*DataAddress = 0x45; *DataAddress = 0x87; *DataAddress = 0x32;
*DataAddress = 0x0a; *DataAddress = 0x07; *DataAddress = 0x02;
*DataAddress = 0x07; *DataAddress = 0x05; *DataAddress = 0x00;
*CommandAddress = LCDCommand::NegativeGammaCorrection; //negamma correction
*DataAddress = 0x00; *DataAddress = 0x25; *DataAddress = 0x27;
*DataAddress = 0x05; *DataAddress = 0x10; *DataAddress = 0x09;
*DataAddress = 0x3a; *DataAddress = 0x78; *DataAddress = 0x4d;
*DataAddress = 0x05; *DataAddress = 0x18; *DataAddress = 0x0d;
*DataAddress = 0x38; *DataAddress = 0x3a; *DataAddress = 0x1f;
*CommandAddress = LCDCommand::SleepOut; //Exit Sleep
delayms(120);
*CommandAddress = LCDCommand::DisplayOn; //Display on
delayms(50);
}
void ion_screen_init() {
// Turn on the backlight
RCC.AHB1ENR()->setGPIOCEN(true);
GPIOC.MODER()->setMODER(9, GPIO::MODER::MODE::Output);
@@ -109,32 +181,54 @@ void ion_lcd_init() {
GPIOB.MODER()->setMODER(13, GPIO::MODER::MODE::Output);
GPIOB.ODR()->setODR(13, true);
ion_lcd_gpio_init();
ion_lcd_fsmc_init();
delayms(120);
Ion::Screen::initGPIO();
Ion::Screen::initFSMC();
Ion::Screen::initPanel();
}
volatile uint16_t * command = (uint16_t *)0x6C000000;
volatile uint16_t * data = (uint16_t *)0x6C020000;
void Ion::Screen::setDrawingArea(uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
uint16_t x_start = x;
uint16_t x_end = x + width - 1;
uint16_t y_start = y;
uint16_t y_end = y + height - 1;
/*
uint16_t j = 0;
while (true) {
*data = j++;
// *command = j++;
}
*/
*CommandAddress = LCDCommand::ColumnAddressSet;
*DataAddress = (x_start >> 8);
*DataAddress = (x_start & 0xFF);
*DataAddress = (x_end >> 8);
*DataAddress = (x_end & 0xFF);
while (true) {
*CommandAddress = LCDCommand::PageAddressSet;
*DataAddress = (y_start >> 8);
*DataAddress = (y_start & 0xFF);
*DataAddress = (y_end >> 8);
*DataAddress = (y_end & 0xFF);
for (int i=0; i< 100000; i++) {
}
*CommandAddress = LCDCommand::MemoryWrite;
}
*command = 0x11;
for (int i=0; i< 100000; i++) {
}
*command = 0x29;
for (int i=0; i< 1000000; i++) {
}
void Ion::Screen::pushPixels(const ion_color_t * pixels, size_t numberOfPixels) {
assert(sizeof(ion_color_t) == 2); // We expect KDColor to be RGB565
for (size_t i=0; i<numberOfPixels; i++) {
*DataAddress = (pixels[i] >> 8);
*DataAddress = (pixels[i] & 0xFF);
}
}
void ion_screen_push_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const ion_color_t * pixels) {
Ion::Screen::setDrawingArea(x, y, width, height);
Ion::Screen::pushPixels(pixels, width*height);
}
void ion_screen_push_rect_uniform(uint16_t x, uint16_t y, uint16_t width, uint16_t height, ion_color_t color) {
Ion::Screen::setDrawingArea(x, y, width, height);
for (size_t i=0; i<width*height; i++) {
Ion::Screen::pushPixels(&color, 1);
}
}
void ion_screen_pull_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, ion_color_t * pixels) {
assert(0); // Unimplemented
}

62
ion/src/device/display.h Normal file
View File

@@ -0,0 +1,62 @@
extern "C" {
#include <ion/screen.h>
#include <stdint.h>
#include <string.h>
void ion_screen_init();
}
/* Pinout:
* PA2 - D4
* PA3 - D5
* PA4 - D6
* PA5 - D7
* PB14 - D0
* PC3 - A0
* PC4 - NE4
* PC5 - NOE
* PC6 - D1
* PC11 - D2
* PC12 - D3
* PD2 - NWE
*/
namespace Ion {
namespace Screen {
void init();
void initGPIO();
void initFSMC();
void initPanel();
void setDrawingArea(uint16_t x, uint16_t y, uint16_t width, uint16_t height);
void pushPixels(const ion_color_t * pixels, size_t numberOfPixels);
enum class LCDCommand : uint8_t {
SleepOut = 0x11,
DisplayOn = 0x29,
ColumnAddressSet = 0x2A,
GammaSet = 0x26,
PageAddressSet = 0x2B,
MemoryWrite = 0x2C,
MemoryAccessControl = 0x36,
PixelFormatSet = 0x3A,
FrameRateControl = 0xB1,
DisplayFunctionControl = 0xB6,
EntryMode = 0xB7,
PowerControl2 = 0xC1,
VCOMControl1 = 0xC5,
VCOMControl2 = 0xC7,
PowerControlA = 0xCB,
PowerControlB = 0xCF,
PositiveGammaCorrection = 0xE0,
NegativeGammaCorrection = 0xE1,
DriverTimingControlA = 0xE8,
DriverTimingControlB = 0xEA,
PowerOnSequenceControl = 0xED,
Enable3G = 0xF2,
PumpRatioControl = 0xF7,
};
static volatile LCDCommand * const CommandAddress = (LCDCommand *)0x6C000000;
static volatile uint8_t * const DataAddress = (uint8_t *)0x6C000001;
}
}

View File

@@ -1,119 +0,0 @@
/* LCD Initialisation code
*
* The LCD panel is connected via an SPI interface. The SPI interface is used to
* configure the panel and to stream the bitmap data.
*
* Here is the connection this file assumes:
*
* LCD | STM32 | Role
* --------------+-------+-----
* LCD_CS | PC13 | Chip-select for LCD panel (panel selected when low)
* LCD_RST | PB12 | Reset the LCD panel (panel active when high)
* LCD_DAT_INS | PB14 | Select "command" or "data" mode
* LCD_SPI_CLK | PB13 | SPI clock
* LCD_SPI_MOSI | PB15 | SPI data
*
* The entry point is init_display();
*/
#include <ion.h>
#include <string.h>
#include <assert.h>
#include "../registers/registers.h"
#include "gpio.h"
#include "spi.h"
#include "dma.h"
#include "framebuffer.h"
#include "st7789.h"
static st7789_t sDisplayController;
void ion_display_on() {
// Initialize panel
// Start DMA transfer
}
void ion_display_off() {
// Stop DMA transfer
// Turn off panel
}
void ion_screen_push_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const ion_color_t * pixels) {
st7789_set_drawing_area(&sDisplayController, x, y, width, height);
st7789_push_pixels(&sDisplayController, pixels, width*height);
}
void ion_screen_push_rect_uniform(uint16_t x, uint16_t y, uint16_t width, uint16_t height, ion_color_t color) {
st7789_set_drawing_area(&sDisplayController, x, y, width, height);
for (size_t i=0; i<width*height; i++) {
st7789_push_pixels(&sDisplayController, &color, 1);
}
}
void ion_screen_pull_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, ion_color_t * pixels) {
assert(0); // Unimplemented
}
#if 0
void ion_screen_push_rect(
uint16_t x, uint16_t y,
uint16_t width, uint16_t height,
ion_color_t * pattern, uint16_t patternWidth, uint16_t patternHeight)
{
st7789_set_drawing_area(&sDisplayController, x, y, width, height);
#if ION_DEVICE_FILL_RECT_FAST_PATH
/* If the pattern width matches the target rect width, we can easily push
* mutliple lines at once since those will be contiguous in memory. */
if (patternWidth == width) {
size_t remainingSize = width*height;
size_t patternSize = patternWidth*patternHeight;
while (remainingSize > 0) {
int32_t blockSize = remainingSize > patternSize ? patternSize : remainingSize;
st7789_push_pixels(&sDisplayController, pattern, blockSize);
remainingSize -= blockSize;
}
return;
}
#endif
uint16_t remainingHeight = height;
uint16_t patternLine = 0;
while (remainingHeight-- > 0) {
uint16_t remainingWidth = width;
while (remainingWidth > 0) {
int32_t blockSize = remainingWidth > patternWidth ? patternWidth : remainingWidth;
st7789_push_pixels(&sDisplayController, pattern+patternLine*patternWidth, blockSize);
remainingWidth -= blockSize;
}
if (++patternLine >= patternHeight) {
patternLine = 0;
}
}
}
#endif
void init_display() {
//assert(FRAMEBUFFER_LENGTH == (FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*FRAMEBUFFER_BITS_PER_PIXEL)/8);
display_gpio_init();
display_spi_init();
sDisplayController.chip_select_pin_write = gpio_c13_write;
sDisplayController.reset_pin_write = gpio_b12_write;
//sDisplayController.data_command_pin_write = gpio_b14_write;
sDisplayController.spi_write = spi_2_write;
st7789_initialize(&sDisplayController);
//st7789_set_display_area(&sDisplayController, 0, 240, 0, 320);
//st7789_enable_frame_data_upload(&sDisplayController);
/*while (1) {
}*/
//memset(FRAMEBUFFER_ADDRESS, 0, FRAMEBUFFER_LENGTH);
//display_dma_init();
}

View File

@@ -1,6 +0,0 @@
#ifndef ION_DEVICE_DISPLAY_H
#define ION_DEVICE_DISPLAY_H
void init_display();
#endif

View File

@@ -1,60 +0,0 @@
#include "../registers/registers.h"
#include "framebuffer.h"
// DMA 1, channel 0, stream 4 = SPI2_TX
#define LCD_DMA_CHANNEL 0
#define LCD_DMA_STREAM 4
void display_dma_init() {
// 0 - ENable DMA clock!
RCC_AHB1ENR |= DMA1EN;
// 1 - Reset DMA
// Stuff like this, and a wait loop...
//DMA_SCR(DMA1,4) |= DMA_EN;
//DMA_SCR(DMA1,4) ~= DMA_EN;
// 2 - Set the peripheral address
DMA_SPAR(DMA1,LCD_DMA_STREAM) = (uint32_t)&SPI_DR(SPI2);
// 3 - Set the memory address
DMA_SMA0R(DMA1,LCD_DMA_STREAM) = (uint32_t)FRAMEBUFFER_ADDRESS;
// 4 - Number of data items
REGISTER_SET_VALUE(DMA_SNDTR(DMA1,LCD_DMA_STREAM), DMA_SNDTR, FRAMEBUFFER_LENGTH);
// 5 - Select the DMA channel
REGISTER_SET_VALUE(DMA_SCR(DMA1,LCD_DMA_STREAM), DMA_CHSEL, LCD_DMA_CHANNEL);
// 6 - Set peripheral flow control
// Does not apply, only for SD/MMC
// 7 - Stream priority
// We don't care yet
// 8 - Configure FIFO usage
// I think we don't care
// 9 - Data transfer direction, peripheral/memory increment, single burst or transaction, peripheral and memory data width
REGISTER_SET_VALUE(DMA_SCR(DMA1,LCD_DMA_STREAM), DMA_DIR, DMA_DIR_MEMORY_TO_PERIPHERAL);
DMA_SCR(DMA1,LCD_DMA_STREAM) |= DMA_CIRC;
// Memory address is incremented
DMA_SCR(DMA1,LCD_DMA_STREAM) |= DMA_MINC;
// Peripheral expects 8 bits values, default
//REGISTER_SET_VALUE(DMA_SCR(DMA1,LCD_DMA_STREAM), DMA_DIR, DMA_DIR_MEMORY_TO_PERIPHERAL);
// 10 - Enable DMA transfer!
DMA_SCR(DMA1,LCD_DMA_STREAM) |= DMA_EN;
// 11 - Bonux: enable DMA requests on SPI
SPI_CR2(SPI2) |= SPI_TXDMAEN;
/*while (DMA_SCR(DMA1,LCD_DMA_STREAM) & DMA_EN) {
}
DMA_SCR(DMA1,LCD_DMA_STREAM) |= DMA_EN;
*/
}

View File

@@ -1 +0,0 @@
void display_dma_init();

View File

@@ -1,31 +0,0 @@
#include <ion.h>
#include <assert.h>
#include "framebuffer.h"
#define BIT_MASK(high, low) ((((uint32_t)1<<((high)-(low)+1))-1)<<(low))
#define BIT_VALUE(value, high, low) (((value)<<(low))&BIT_MASK(high, low))
#define BIT_SET(initial, value, high, low) ((initial&~BIT_MASK(high,low))|(BIT_VALUE(value,high,low)&BIT_MASK(high,low)))
/* We're using a pixel format imposed to us by the stupid st7586. We use a four
* color framebuffer, so that's 2 bits per pixels. Unfortunately we cannot pack
* those pixels and have to resort to two pixels per byte using the following
* format : AAxBBxxx, where AA and BB are the bits for two contiguous pixels. */
void ion_set_pixel(uint16_t x, uint16_t y, uint8_t color) {
assert(x <= FRAMEBUFFER_WIDTH);
assert(y <= FRAMEBUFFER_HEIGHT);
char * byte = FRAMEBUFFER_ADDRESS + ((y*FRAMEBUFFER_WIDTH)+x)*FRAMEBUFFER_BITS_PER_PIXEL/8;
assert(byte <= &_framebuffer_end);
char mask, value;
if (x & 1) {
mask = BIT_MASK(4,3);
value = BIT_VALUE(color,4,3);
} else {
mask = BIT_MASK(7,6);
value = BIT_VALUE(color,7,6);
}
*byte = (*byte & ~mask) | (value & mask);
}

View File

@@ -1,16 +0,0 @@
#ifndef ION_DEVICE_DISPLAY_FRAMEBUFFER_H_
#define ION_DEVICE_DISPLAY_FRAMEBUFFER_H_
#include <ion.h>
extern char _framebuffer_start;
extern char _framebuffer_end;
#define FRAMEBUFFER_ADDRESS (&_framebuffer_start)
#define FRAMEBUFFER_LENGTH (&_framebuffer_end-&_framebuffer_start)
#define FRAMEBUFFER_WIDTH SCREEN_WIDTH
#define FRAMEBUFFER_HEIGHT SCREEN_HEIGHT
#define FRAMEBUFFER_BITS_PER_PIXEL 4
#endif // ION_DEVICE_DISPLAY_FRAMEBUFFER_H_

View File

@@ -1,50 +0,0 @@
/* The LCD panel is connected via an SPI interface. The SPI interface is used to
* configure the panel and to stream the bitmap data.
*
* Here is the connection this file assumes:
*
* LCD | STM32 | Role
* --------------+-------+-----
* LCD_CS | PC13 | Chip-select for LCD panel (panel selected when low)
* LCD_RST | PB12 | Reset the LCD panel (panel active when high)
* LCD_SPI_CLK | PB13 | SPI clock
* LCD_DAT_INS | PB14 | Select "command" or "data" mode
* LCD_SPI_MOSI | PB15 | SPI data
*/
#include "../registers/registers.h"
#include "gpio.h"
void display_gpio_init() {
// We are using GPIO B, which live on the AHB1 bus. Let's enable its clock.
RCC_AHB1ENR |= GPIOBEN;
// Same for GPIO C
RCC_AHB1ENR |= GPIOCEN;
// LCD_CS(PC13), LCD_RST(PB12) and LCD_DAT_INS(PB14) are controlled directly
REGISTER_SET_VALUE(GPIO_MODER(GPIOC), MODER(13), GPIO_MODE_OUTPUT);
REGISTER_SET_VALUE(GPIO_MODER(GPIOB), MODER(12), GPIO_MODE_OUTPUT);
REGISTER_SET_VALUE(GPIO_MODER(GPIOB), MODER(14), GPIO_MODE_OUTPUT);
/* LCD_SPI_CLK(PB13) and LCD_SPI_MOSI(PB15) are used for SPI, which is an
* alternate function. */
REGISTER_SET_VALUE(GPIO_MODER(GPIOB), MODER(13), GPIO_MODE_ALTERNATE_FUNCTION);
REGISTER_SET_VALUE(GPIO_MODER(GPIOB), MODER(15), GPIO_MODE_ALTERNATE_FUNCTION);
/* More precisely, we will use AF05, which maps PB15 to SPI2_MOSI and PB13 to
* SPI2_SCK. */
REGISTER_SET_VALUE(GPIO_AFR(GPIOB, 13), AFR(13), 5);
REGISTER_SET_VALUE(GPIO_AFR(GPIOB, 15), AFR(15), 5);
}
void gpio_b12_write(bool pin_state) {
REGISTER_SET_VALUE(GPIO_ODR(GPIOB), ODR(12), pin_state);
}
void gpio_b14_write(bool pin_state) {
REGISTER_SET_VALUE(GPIO_ODR(GPIOB), ODR(14), pin_state);
}
void gpio_c13_write(bool pin_state) {
REGISTER_SET_VALUE(GPIO_ODR(GPIOC), ODR(13), pin_state);
}

View File

@@ -1,6 +0,0 @@
#include <stdbool.h>
void display_gpio_init();
void gpio_b12_write(bool pin_state);
void gpio_b14_write(bool pin_state);
void gpio_c13_write(bool pin_state);

View File

@@ -1,25 +0,0 @@
/* The LCD panel is connected via the SPI2 port.
* This file configures the port. */
#include "../registers/registers.h"
#include "spi.h"
void display_spi_init() {
// Enable the SPI2 clock (SPI2 lives on the APB1 bus)
RCC_APB1ENR |= SPI2EN;
// Configure the SPI port
SPI_CR1(SPI2) = (SPI_BIDIMODE | SPI_BIDIOE | SPI_MSTR | SPI_DFF_8_BITS | SPI_BR(SPI_BR_DIV_2) | SPI_SSM | SPI_SSI | SPI_SPE);
}
void spi_2_write(char * data, size_t size) {
while (SPI_SR(SPI2) & SPI_BSY) {
}
for (size_t i=0; i<size; i++) {
SPI_DR(SPI2) = data[i];
while (!(SPI_SR(SPI2) & SPI_TXE)) {
}
}
while (SPI_SR(SPI2) & SPI_BSY) {
}
}

View File

@@ -1,4 +0,0 @@
#include <string.h>
void display_spi_init();
void spi_2_write(char * data, size_t size);

View File

@@ -1,160 +0,0 @@
#include <assert.h>
#include "st7586.h"
enum {
COMMAND_MODE = 0,
DATA_MODE = 1,
DELAY_MODE = 2
};
enum {
NOP = 0x00,
RESET = 0x01,
SLEEP_IN = 0x10,
SLEEP_OUT = 0x11,
DISPLAY_OFF = 0x28,
DISPLAY_ON = 0x29,
SET_COLUMN_ADDRESS = 0x2A,
SET_ROW_ADDRESS = 0x2B,
WRITE_DISPLAY_DATA = 0x2C,
ENABLE_DDRAM = 0x3A,
};
typedef struct {
char mode;
char payload;
} instruction_t;
#define COMMAND(c) (instruction_t){.mode = (char)COMMAND_MODE, .payload = (char)c}
#define DATA(d) (instruction_t){.mode = (char)DATA_MODE, .payload = (char)d}
#define DELAY(m) (instruction_t){.mode = (char)DELAY_MODE, .payload = (char)m}
static void perform_instruction(st7586_t * c, instruction_t instruction) {
if (instruction.mode == DELAY_MODE) {
// FIXME: Should sleep instruction->payload miliseconds
for (int i = 0; i < 800*instruction.payload; i++) {
__asm volatile("nop");
}
} else {
c->data_command_pin_write(instruction.mode);
c->spi_write(&instruction.payload, 1);
}
}
static void perform_instructions(st7586_t * c, const instruction_t * instructions, size_t length) {
for (size_t i=0; i<length; i++) {
perform_instruction(c, instructions[i]);
}
}
void st7586_initialize(st7586_t * controller) {
// Falling edge on CSX
controller->chip_select_pin_write(0);
const instruction_t init_sequence[] = {
#define USE_MANUFACTURER_INIT 0
#if USE_MANUFACTURER_INIT
COMMAND(0x01), // ADDED BY ME
DELAY(220),
COMMAND(0x11), // Sleep Out
COMMAND(0x28), // Display OFF
DELAY(50),
COMMAND(0xC0), // Vop = B9h
DATA(0x45),
DATA(0x01),
COMMAND(0xC3), // BIAS = 1/14
DATA(0x00),
COMMAND(0xC4), // Booster = x8
DATA(0x07),
COMMAND(0xD0), // Enable Analog Circuit
DATA(0x1D),
COMMAND(0xB5), // N-Line = 0
DATA(0x00),
//COMMAND(0x39), // Monochrome Mode
COMMAND(0x38), // Grey Mode
COMMAND(0x3A), // Enable DDRAM Interface
DATA(0x02),
COMMAND(0x36), // Scan Direction Setting
DATA(0xc0), //COM:C160--C1 SEG: SEG384-SEG1
COMMAND(0xB0), // Duty Setting
DATA(0x9F),
COMMAND(0x20), // Display Inversion OFF
COMMAND(0x2A), // Column Address Setting
DATA(0x00), // SEG0 -> SEG384
DATA(0x00),
DATA(0x00),
DATA(0x7F),
COMMAND(0x2B), // Row Address Setting
DATA(0x00), // COM0 -> COM160
DATA(0x00),
DATA(0x00),
DATA(0x9F),
COMMAND(0x29) // Display ON
#else
COMMAND(RESET), DELAY(50), // Software reset, then wait 5ms
COMMAND(DISPLAY_OFF),
// Sleep out, requires a 100ms delay
COMMAND(SLEEP_OUT), DELAY(100),
// Display on, requires a 20ms delay
COMMAND(DISPLAY_ON), DELAY(20),
// Enable DDRAM interface
// The "DATA" makes no real sense but is mandatory according to the spec
COMMAND(ENABLE_DDRAM), DATA(0x2),
// This has been copy-pasted from the manufacturer.
// FIXME: Understand what it does, and maybe fix it!
COMMAND(0xC0), // Vop = B9h
DATA(0x45),
DATA(0x01),
COMMAND(0xC3), // BIAS = 1/14
DATA(0x00),
COMMAND(0xC4), // Booster = x8
DATA(0x07),
COMMAND(0xD0), // Enable Analog Circuit
DATA(0x1D),
COMMAND(0xB5), // N-Line = 0
DATA(0x00),
#endif
};
// Send all the initialisation_sequence
perform_instructions(controller, init_sequence, sizeof(init_sequence)/sizeof(init_sequence[0]));
}
void st7586_set_display_area(st7586_t * controller, uint16_t x_start, uint16_t x_length, uint16_t y_start, uint16_t y_length) {
/* The datasheet says the panel counts in "columns", groups of 3 pixels.
* It says 3, but from my understanding pixels are grouped by 2, not by 3. So
* so let's make this 2 instead of 3. Seems to be working fine! */
uint16_t x_end = x_start + x_length/2 - 1;
uint16_t y_end = y_start + y_length - 1;
assert(x_end >= x_start);
assert(x_end <= 0x7F);
assert(y_end >= y_start);
assert(y_end <= 0x9F);
const instruction_t sequence[] = {
COMMAND(SET_COLUMN_ADDRESS),
DATA(x_start >> 8),
DATA(x_start),
DATA(x_end >> 8),
DATA(x_end),
COMMAND(SET_ROW_ADDRESS),
DATA(y_start >> 8),
DATA(y_start),
DATA(y_end >> 8),
DATA(y_end),
};
perform_instructions(controller, sequence, sizeof(sequence)/sizeof(sequence[0]));
}
void st7586_enable_frame_data_upload(st7586_t * controller) {
// Put the screen in "receive frame data"
perform_instruction(controller, COMMAND(WRITE_DISPLAY_DATA));
controller->data_command_pin_write(DATA_MODE);
}

View File

@@ -1,22 +0,0 @@
#ifndef ION_DEVICE_DISPLAY_ST7586_H
#define ION_DEVICE_DISPLAY_ST7586_H
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
/* This is the ST7586 driver
*
* It just needs to be pointed to a "write" function
*/
typedef struct {
void (*chip_select_pin_write)(bool pin_state);
void (*data_command_pin_write)(bool pin_state);
void (*spi_write)(char * data, size_t size);
} st7586_t;
void st7586_initialize(st7586_t * controller);
void st7586_set_display_area(st7586_t * controller, uint16_t x_start, uint16_t x_length, uint16_t y_start, uint16_t y_length);
void st7586_enable_frame_data_upload(st7586_t * controller);
#endif

View File

@@ -1,191 +0,0 @@
#include <assert.h>
#include "st7789.h"
enum {
COMMAND_MODE = 0,
DATA_MODE = 1,
DELAY_MODE = 2
};
enum {
NOP = 0x00,
SWRESET = 0x01, // Software reset
SLPOUT = 0x11, // Sleep out
DISPOFF = 0x28,
DISPON = 0x29,
CASET = 0x2A,
RASET = 0x2B,
RAMRW = 0x2C,
MADCTL = 0x36, // Memory data access control
COLMOD = 0x3A, // Interface pixel format
PORCTRL = 0xB2, // Porch setting
GCTRL = 0xB7, // Gate control
VCOMS = 0xBB, // VCOM setting
LCMCTRL = 0xC0, // LCM control
VDVVRHEN = 0xC2, // VDV and VRH command enable
VRHS = 0xC3, // VRH set
VDVS = 0xC4,
VCMOFSET = 0xC5,
FRCTRL2 = 0xC6,
PWCTRL1 = 0xD0,
PVGAMCTRL = 0xE0,
NVGAMCTRL = 0xE1,
};
typedef struct {
uint8_t mode;
uint8_t payload;
} instruction_t;
#define COMMAND(c) (instruction_t){.mode = (uint8_t)COMMAND_MODE, .payload = (uint8_t)(c)}
#define DATA(d) (instruction_t){.mode = (uint8_t)DATA_MODE, .payload = (uint8_t)(d)}
#define DELAY(m) (instruction_t){.mode = (uint8_t)DELAY_MODE, .payload = (uint8_t)(m)}
// FIXME: This is ugly and should not live here
static inline void delay_ms(long ms) {
for (long i=0; i<1040*ms; i++) {
__asm volatile("nop");
}
}
#if ST7789_USE_9BIT_SPI
static void push_data_on_spi_queue(st7789_t * c, uint8_t data, int8_t numberOfBits) {
assert(numberOfBits >= 0);
c->spi_queue = (c->spi_queue << numberOfBits) | data;
c->spi_queue_usage_in_bits += numberOfBits;
assert(c->spi_queue_usage_in_bits < 8*sizeof(c->spi_queue));
}
static void flush_spi_buffer(st7789_t * c) {
while (c->spi_queue_usage_in_bits >= 8) {
char byte = (c->spi_queue >> (c->spi_queue_usage_in_bits - 8));
c->spi_write(&byte, 1);
c->spi_queue = c->spi_queue & ((1>>(c->spi_queue_usage_in_bits-8))-1); // Clear the 8 first bytes
c->spi_queue_usage_in_bits = c->spi_queue_usage_in_bits-8;
}
// At this point, there remain c->spi_queue_usage_in_bits in the queue
}
#endif
static void perform_instruction(st7789_t * c, instruction_t instruction) {
if (instruction.mode == DELAY_MODE) {
delay_ms(instruction.payload);
} else {
//c->chip_select_pin_write(0);
#if ST7789_USE_9BIT_SPI
push_data_on_spi_queue(c, (instruction.mode == DATA_MODE), 1);
push_data_on_spi_queue(c, instruction.payload, 8);
flush_spi_buffer(c);
#else
c->data_command_pin_write(instruction.mode);
c->spi_write(&instruction.payload, 1);
#endif
//c->chip_select_pin_write(1);
}
}
static void perform_instructions(st7789_t * c, const instruction_t * instructions, size_t length) {
for (size_t i=0; i<length; i++) {
perform_instruction(c, instructions[i]);
}
}
void st7789_initialize(st7789_t * c) {
#if ST7789_USE_9BIT_SPI
c->spi_queue = 0;
c->spi_queue_usage_in_bits = 0;
#endif
/* The ST7789S has multiple interfaces to receive data.
* In this driver, we're using the SPI interface. To initialize this
* interface, the controller expects a falling edge on the CS pin. */
c->chip_select_pin_write(1);
if (c->reset_pin_write != NULL) {
c->reset_pin_write(1);
delay_ms(10);
c->reset_pin_write(0);
delay_ms(10);
c->reset_pin_write(1);
delay_ms(100);
}
delay_ms(10);
c->chip_select_pin_write(0);
/* We were provided the following init sequence by the manufacturer */
const instruction_t init_sequence[] = {
COMMAND(SLPOUT),
COMMAND(NOP), // Just needed because of the delay and the 9-bit thing...
DELAY(120), //Delay 120ms
// Manufacturer says MADCTL = 0
// COMMAND(MADCTL), DATA(0x00),
COMMAND(MADCTL), DATA(0xA0),
// D7 = MY = 1 : Bottom-to-top
// D5 = MV = 1 : Invert columns and rows (rotate 90deg)
COMMAND(COLMOD), DATA(0x05),
COMMAND(PORCTRL),
DATA(0x0c), DATA(0x0c), DATA(0x00), DATA(0x33), DATA(0x33),
COMMAND(GCTRL), DATA(0x35), //VGH=13V, VGL=-10.4V
COMMAND(VCOMS), DATA(0x19),
COMMAND(LCMCTRL), DATA(0x2c),
COMMAND(VDVVRHEN), DATA(0x01),
COMMAND(VRHS), DATA(0x12),
COMMAND(VDVS), DATA(0x20),
COMMAND(FRCTRL2), DATA(0x0f),
COMMAND(PWCTRL1), DATA(0xa4), DATA(0xa1),
COMMAND(PVGAMCTRL), //gamma setting
DATA(0xd0), DATA(0x04), DATA(0x0d), DATA(0x11), DATA(0x13), DATA(0x2b),
DATA(0x3f), DATA(0x54), DATA(0x4c), DATA(0x18), DATA(0x0d), DATA(0x0b),
DATA(0x1f), DATA(0x23),
COMMAND(NVGAMCTRL),
DATA(0xd0), DATA(0x04), DATA(0x0c), DATA(0x11), DATA(0x13), DATA(0x2c),
DATA(0x3f), DATA(0x44), DATA(0x51), DATA(0x2f), DATA(0x1f), DATA(0x1f),
DATA(0x20), DATA(0x23),
COMMAND(DISPON) //display on
};
// Send all the initialisation_sequence
perform_instructions(c, init_sequence, sizeof(init_sequence)/sizeof(init_sequence[0]));
}
void st7789_set_drawing_area(st7789_t * controller, uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
uint16_t x_start = x;
uint16_t x_end = x + width - 1;
uint16_t y_start = y;
uint16_t y_end = y + height - 1;
const instruction_t sequence[] = {
COMMAND(CASET),
DATA(x_start >> 8),
DATA(x_start & 0xFF),
DATA(x_end >> 8),
DATA(x_end & 0xFF),
COMMAND(RASET),
DATA(y_start >> 8),
DATA(y_start & 0xFF),
DATA(y_end >> 8),
DATA(y_end & 0xFF),
COMMAND(RAMRW)
};
perform_instructions(controller, sequence, sizeof(sequence)/sizeof(sequence[0]));
}
void st7789_push_pixels(st7789_t * controller,
const ion_color_t * pixels, size_t numberOfPixels) {
assert(sizeof(ion_color_t) == 2); // We expect KDColor to be RGB565
for (size_t i=0; i<numberOfPixels; i++) {
perform_instruction(controller, DATA(pixels[i] >> 8));
perform_instruction(controller, DATA(pixels[i] & 0xFF));
}
}

View File

@@ -1,29 +0,0 @@
#ifndef ION_DEVICE_DISPLAY_ST7789_H
#define ION_DEVICE_DISPLAY_ST7789_H
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <ion/screen.h>
#define ST7789_USE_9BIT_SPI 1
typedef struct {
void (*reset_pin_write)(bool pin_state);
void (*chip_select_pin_write)(bool pin_state);
void (*spi_write)(char * data, size_t size);
#if ST7789_USE_9BIT_SPI
uint32_t spi_queue;
uint8_t spi_queue_usage_in_bits;
#else
void (*data_command_pin_write)(bool pin_state);
#endif
} st7789_t;
void st7789_initialize(st7789_t * controller);
void st7789_set_drawing_area(st7789_t * controller, uint16_t x, uint16_t y, uint16_t width, uint16_t height);
void st7789_push_pixels(st7789_t * controller,
const ion_color_t * pixels, size_t numberOfPixels);
#endif

View File

@@ -1,7 +1,6 @@
#include <ion.h>
#include <assert.h>
#include "init.h"
#include "display/display.h"
#include "keyboard/keyboard.h"
#include "registers/registers.h"
@@ -20,7 +19,7 @@ void init_platform() {
enable_fpu();
init_keyboard();
ion_led_init();
init_display();
ion_screen_init();
}
void ion_sleep() {