mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[Ion] Much simpler display driver
Change-Id: Ia64a14ca5ed58f70590a27b968569832a0ecb722
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
62
ion/src/device/display.h
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef ION_DEVICE_DISPLAY_H
|
||||
#define ION_DEVICE_DISPLAY_H
|
||||
|
||||
void init_display();
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
*/
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
void display_dma_init();
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
void display_spi_init();
|
||||
void spi_2_write(char * data, size_t size);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user