[ion] Clean the display init code

This commit is contained in:
Romain Goyet
2015-09-12 15:23:55 +02:00
parent 3a06ada1e1
commit acef53ef0c
12 changed files with 194 additions and 300 deletions

View File

@@ -1,4 +1,3 @@
#include <stdint.h>
#include <assert.h>
#include "st7586.h"
@@ -9,8 +8,8 @@ enum {
};
enum {
NOP = 0x00, // No operation
RESET = 0x01, // Software reset
NOP = 0x00,
RESET = 0x01,
SLEEP_IN = 0x10,
SLEEP_OUT = 0x11,
DISPLAY_OFF = 0x28,
@@ -19,34 +18,7 @@ enum {
SET_ROW_ADDRESS = 0x2B,
WRITE_DISPLAY_DATA = 0x2C,
ENABLE_DDRAM = 0x3A,
/*
RAMWR = 0x2C, // Memory write
MADCTL = 0x36, // Memory access control
PIXSET = 0x3A, // Pixel format set
IFMODE = 0xB0, // RGB interface signal control
FRMCTR1 = 0xB1, // Frame rate control in normal/full-color mode
IFCTL = 0xF6 // Interface control
*/
};
/*
#define IFMODE_EPL (1<<0)
#define IFMODE_DPL (1<<1)
#define IFMODE_HSPL (1<<2)
#define IFMODE_VSPL (1<<3)
#define IFMODE_RCM_DE (2<<5)
// DE mode: Valid data is determined by the DE signal
#define IFMODE_RCM_SYNC (3<<5)
// Sync mode: In sync mode, DE signal is ignored
#define IFMODE_BYPASS (1<<7)
#define MADCTL_MH (1<<2)
#define MADCTL_BGR (1<<3)
#define MADCTL_ML (1<<4)
#define MADCTL_MV (1<<5)
#define MADCTL_MX (1<<6)
#define MADCTL_MY (1<<7)
*/
typedef struct {
char mode;
@@ -57,97 +29,101 @@ typedef struct {
#define DATA(d) (instruction_t){.mode = (char)DATA_MODE, .payload = (char)d}
#define DELAY(m) (instruction_t){.mode = (char)DELAY_MODE, .payload = (char)m}
#define USE_MANUFACTURER_INIT 0
#if USE_MANUFACTURER_INIT
static instruction_t init_sequence[] = {
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
static instruction_t init_sequence[] = {
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
static void perform_instruction(st7586_t * c, instruction_t * instruction) {
if (instruction->mode == DELAY_MODE) {
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++) {
for (int i = 0; i < 800*instruction.payload; i++) {
}
} else {
c->data_command_pin_write(instruction->mode);
c->spi_write(&instruction->payload, 1);
c->data_command_pin_write(instruction.mode);
c->spi_write(&instruction.payload, 1);
}
}
static void perform_instructions(st7586_t * c, instruction_t * instructions, size_t length) {
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);
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
@@ -160,7 +136,7 @@ void st7586_set_display_area(st7586_t * controller, uint16_t x_start, uint16_t x
assert(y_end >= y_start);
assert(y_end <= 0x9F);
instruction_t sequence[] = {
const instruction_t sequence[] = {
COMMAND(SET_COLUMN_ADDRESS),
DATA(x_start >> 8),
DATA(x_start),
@@ -176,79 +152,8 @@ void st7586_set_display_area(st7586_t * controller, uint16_t x_start, uint16_t x
perform_instructions(controller, sequence, sizeof(sequence)/sizeof(sequence[0]));
}
// p1 = 0, 1, 2, or 3
// p2 = 0, 1, 2, or 3
char two_pixels(int p1, int p2) {
return (p1<<6 | p2 << 3);
}
void st7586_initialize(st7586_t * c) {
// Falling edge on CSX
c->chip_select_pin_write(0);
// Send all the initialisation_sequence
perform_instructions(c, init_sequence, sizeof(init_sequence)/sizeof(init_sequence[0]));
st7586_set_display_area(c, 0, 160, 0, 160);
void st7586_prepare_frame_upload(st7586_t * controller) {
// Put the screen in "receive frame data"
perform_instruction(c, &COMMAND(WRITE_DISPLAY_DATA));
c->data_command_pin_write(DATA_MODE);
char pixels[3] = {
two_pixels(0x0,0x3),
two_pixels(0x0,0x3),
two_pixels(0x0,0x3)
};
c->spi_write(&pixels, 3);
#define FILL_SCREEN_UPON_INIT 0
#if FILL_SCREEN_UPON_INIT
/* FIGURED OUT THE PIXEL FORMAT!!!
* 1 byte = 2 pixels
* Pixel 0 : bit 6,7
* Pixel 1 : bit 3,4
*/
// 4byte data 8 dot (B B X - A A X - X X X)
/* Obesrvations
* - One byte = 2 pixels
* - 64 pixels for a "gradient" on pixel0, and constant on pixel1
* - that means 32 bytes, so values from 0 to 31
* BITMASKS!
* pixel 2 : 0x18
*/
perform_instruction(c, &COMMAND(WRITE_DISPLAY_DATA));
c->data_command_pin_write(DATA_MODE);
char pixels[] = {
two_pixels(0, 3),
two_pixels(1, 1),
two_pixels(2, 2),
two_pixels(0, 3)
};
for (int i = 0; i<256; i++) {
//char p = ((i%256) & 0x18);
//c->spi_write(&p, 1);
c->spi_write(pixels, sizeof(pixels));
}
//char pixel = 0;
for (int i=0;i<1024; i++) {
c->spi_write(&pixel, 1);
}
for (int i = 0; i<256; i++) {
char p = (i%256);
c->spi_write(&p, 1);
}
#endif
}
void st7586_display_buffer(st7586_t * controller, char * buffer, size_t length) {
perform_instruction(controller, &COMMAND(WRITE_DISPLAY_DATA));
perform_instruction(controller, COMMAND(WRITE_DISPLAY_DATA));
controller->data_command_pin_write(DATA_MODE);
controller->spi_write(buffer, length);
}

View File

@@ -2,6 +2,7 @@
#define PLATFORM_ST7586_H 1
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
/* This is the ST7586 driver
@@ -16,7 +17,5 @@ typedef struct {
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_prepare_frame_upload(st7586_t * controller);
#endif

View File

@@ -1,2 +1,2 @@
objs += $(addprefix ion/platform/device/, platform.o init.o display.o framebuffer.o init_kbd.o display/dma.o)
objs += $(addprefix ion/platform/device/, platform.o init.o display.o framebuffer.o init_kbd.o display/dma.o display/gpio.o display/spi.o)
objs += $(addprefix ion/drivers/, st7586/st7586.o fx92kbd/fx92kbd.o)

View File

@@ -17,6 +17,11 @@
*/
#include <ion.h>
#include "display/gpio.h"
#include "display/spi.h"
#include "display/dma.h"
#include "platform.h"
#include "framebuffer.h"
#include "registers/registers.h"
@@ -33,98 +38,19 @@ void ion_display_off() {
// Turn off panel
}
static void init_spi_pins();
static void init_spi_port();
static void init_panel();
void init_display() {
/* This routine is responsible for initializing the LCD panel.
* Its interface with the outer world is the framebuffer: after execution of
* this routine, everyone can expect to write to the LCD by writing to the
* framebuffer. */
init_spi_pins();
init_spi_port();
display_gpio_init();
display_spi_init();
init_panel();
display_configure_dma();
}
static void init_spi_pins() {
// We are using GPIO B, which live on the AHB1 bus. Let's enable its clock.
RCC_AHB1ENR |= GPIOBEN;
// LCD_CS(PB10, LCD_RST(PB12) and LCD_DAT_INS(PB14)are controlled directly
REGISTER_SET_VALUE(GPIO_MODER(GPIOB), MODER(10), 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);
}
static void init_spi_port() {
// 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);
}
// Panel
static void spi_2_write(char * data, size_t size);
static void gpio_b10_write(bool pin_state);
static void gpio_b12_write(bool pin_state);
static void gpio_b14_write(bool pin_state);
static void init_panel() {
st7586_t * controller = &(Platform.display);
gpio_b12_write(1); // LCD-RST high
Platform.display.chip_select_pin_write = gpio_b10_write;
Platform.display.data_command_pin_write = gpio_b14_write;
Platform.display.spi_write = spi_2_write;
st7586_initialize(&(Platform.display));
controller->chip_select_pin_write = gpio_b10_write;
controller->data_command_pin_write = gpio_b14_write;
controller->spi_write = spi_2_write;
st7586_set_display_area(0, FRAMEBUFFER_WIDTH, 0, FRAMEBUFFER_HEIGHT);
st7586_display_buffer
st7586_initialize(controller);
//for(int i=0;i<10;i++) {
// ion_set_pixel(i,i,i%4);
//}
//framebuffer[0] = 0xD8;
//st7586_display_buffer(&(Platform.display), &_framebuffer_start, &_framebuffer_end-&_framebuffer_start);
//st7586_display_buffer(&(Platform.display), &_framebuffer_start, 1000);
}
static 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) {
}
}
void gpio_b10_write(bool pin_state) {
REGISTER_SET_VALUE(GPIO_ODR(GPIOB), ODR(10), pin_state);
}
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);
st7586_set_display_area(controller, 0, FRAMEBUFFER_WIDTH, 0, FRAMEBUFFER_HEIGHT);
display_dma_init();
}

View File

@@ -6,7 +6,7 @@
extern char _framebuffer_start, _framebuffer_end;
void display_configure_dma() {
void display_dma_init() {
// 0 - ENable DMA clock!
RCC_AHB1ENR |= DMA1EN;

View File

@@ -1 +1 @@
void display_configure_dma();
void display_dma_init();

View File

@@ -0,0 +1,49 @@
/* 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 | PB10 | 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
*
*/
#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;
// LCD_CS(PB10, LCD_RST(PB12) and LCD_DAT_INS(PB14)are controlled directly
REGISTER_SET_VALUE(GPIO_MODER(GPIOB), MODER(10), 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_b10_write(bool pin_state) {
REGISTER_SET_VALUE(GPIO_ODR(GPIOB), ODR(10), pin_state);
}
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);
}

View File

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

View File

@@ -0,0 +1,25 @@
/* 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

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

View File

@@ -1,19 +0,0 @@
#include "init_lcd.h"
#include <private/memconfig.h>
extern char _ccm_heap_start;
extern char _ccm_heap_end;
heap_config_t HeapConfig;
int memsys5Init(void *NotUsed);
void init_heap() {
HeapConfig.nHeap = (&_ccm_heap_end - &_ccm_heap_start);
HeapConfig.pHeap = &_ccm_heap_start;
HeapConfig.mnReq = 1;
HeapConfig.bMemstat = 0;
HeapConfig.xLog = 0;
memsys5Init(0);
}

View File

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