492 lines
13 KiB
C
492 lines
13 KiB
C
/* vim: set ai et ts=4 sw=4: */
|
|
#include "stm32f1xx_hal.h"
|
|
#include "ili9341.h"
|
|
#include <math.h>
|
|
|
|
#ifndef _swap_int16_t
|
|
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
|
|
#endif
|
|
|
|
static void ILI9341_Select() {
|
|
HAL_GPIO_WritePin(ILI9341_CS_GPIO_Port, ILI9341_CS_Pin, GPIO_PIN_RESET);
|
|
}
|
|
|
|
void ILI9341_Unselect() {
|
|
HAL_GPIO_WritePin(ILI9341_CS_GPIO_Port, ILI9341_CS_Pin, GPIO_PIN_SET);
|
|
}
|
|
|
|
static void ILI9341_Reset() {
|
|
HAL_GPIO_WritePin(ILI9341_RES_GPIO_Port, ILI9341_RES_Pin, GPIO_PIN_RESET);
|
|
HAL_Delay(5);
|
|
HAL_GPIO_WritePin(ILI9341_RES_GPIO_Port, ILI9341_RES_Pin, GPIO_PIN_SET);
|
|
}
|
|
|
|
static void ILI9341_WriteCommand(uint8_t cmd) {
|
|
HAL_GPIO_WritePin(ILI9341_DC_GPIO_Port, ILI9341_DC_Pin, GPIO_PIN_RESET);
|
|
HAL_SPI_Transmit(&ILI9341_SPI_PORT, &cmd, sizeof(cmd), HAL_MAX_DELAY);
|
|
}
|
|
|
|
static void ILI9341_WriteData(uint8_t* buff, size_t buff_size) {
|
|
HAL_GPIO_WritePin(ILI9341_DC_GPIO_Port, ILI9341_DC_Pin, GPIO_PIN_SET);
|
|
|
|
// split data in small chunks because HAL can't send more then 64K at once
|
|
while(buff_size > 0) {
|
|
uint16_t chunk_size = buff_size > 32768 ? 32768 : buff_size;
|
|
HAL_SPI_Transmit(&ILI9341_SPI_PORT, buff, chunk_size, HAL_MAX_DELAY);
|
|
buff += chunk_size;
|
|
buff_size -= chunk_size;
|
|
}
|
|
}
|
|
|
|
static void ILI9341_SetAddressWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
|
// column address set
|
|
ILI9341_WriteCommand(0x2A); // CASET
|
|
{
|
|
uint8_t data[] = { (x0 >> 8) & 0xFF, x0 & 0xFF, (x1 >> 8) & 0xFF, x1 & 0xFF };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// row address set
|
|
ILI9341_WriteCommand(0x2B); // RASET
|
|
{
|
|
uint8_t data[] = { (y0 >> 8) & 0xFF, y0 & 0xFF, (y1 >> 8) & 0xFF, y1 & 0xFF };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// write to RAM
|
|
ILI9341_WriteCommand(0x2C); // RAMWR
|
|
}
|
|
|
|
void ILI9341_Init() {
|
|
ILI9341_Select();
|
|
ILI9341_Reset();
|
|
|
|
// command list is based on https://github.com/martnak/STM32-ILI9341
|
|
|
|
// SOFTWARE RESET
|
|
ILI9341_WriteCommand(0x01);
|
|
HAL_Delay(1000);
|
|
|
|
// POWER CONTROL A
|
|
ILI9341_WriteCommand(0xCB);
|
|
{
|
|
uint8_t data[] = { 0x39, 0x2C, 0x00, 0x34, 0x02 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// POWER CONTROL B
|
|
ILI9341_WriteCommand(0xCF);
|
|
{
|
|
uint8_t data[] = { 0x00, 0xC1, 0x30 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// DRIVER TIMING CONTROL A
|
|
ILI9341_WriteCommand(0xE8);
|
|
{
|
|
uint8_t data[] = { 0x85, 0x00, 0x78 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// DRIVER TIMING CONTROL B
|
|
ILI9341_WriteCommand(0xEA);
|
|
{
|
|
uint8_t data[] = { 0x00, 0x00 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// POWER ON SEQUENCE CONTROL
|
|
ILI9341_WriteCommand(0xED);
|
|
{
|
|
uint8_t data[] = { 0x64, 0x03, 0x12, 0x81 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// PUMP RATIO CONTROL
|
|
ILI9341_WriteCommand(0xF7);
|
|
{
|
|
uint8_t data[] = { 0x20 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// POWER CONTROL,VRH[5:0]
|
|
ILI9341_WriteCommand(0xC0);
|
|
{
|
|
uint8_t data[] = { 0x23 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// POWER CONTROL,SAP[2:0];BT[3:0]
|
|
ILI9341_WriteCommand(0xC1);
|
|
{
|
|
uint8_t data[] = { 0x10 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// VCM CONTROL
|
|
ILI9341_WriteCommand(0xC5);
|
|
{
|
|
uint8_t data[] = { 0x3E, 0x28 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// VCM CONTROL 2
|
|
ILI9341_WriteCommand(0xC7);
|
|
{
|
|
uint8_t data[] = { 0x86 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// MEMORY ACCESS CONTROL
|
|
ILI9341_WriteCommand(0x36);
|
|
{
|
|
uint8_t data[] = { 0x48 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// PIXEL FORMAT
|
|
ILI9341_WriteCommand(0x3A);
|
|
{
|
|
uint8_t data[] = { 0x55 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// FRAME RATIO CONTROL, STANDARD RGB COLOR
|
|
ILI9341_WriteCommand(0xB1);
|
|
{
|
|
uint8_t data[] = { 0x00, 0x18 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// DISPLAY FUNCTION CONTROL
|
|
ILI9341_WriteCommand(0xB6);
|
|
{
|
|
uint8_t data[] = { 0x08, 0x82, 0x27 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// 3GAMMA FUNCTION DISABLE
|
|
ILI9341_WriteCommand(0xF2);
|
|
{
|
|
uint8_t data[] = { 0x00 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// GAMMA CURVE SELECTED
|
|
ILI9341_WriteCommand(0x26);
|
|
{
|
|
uint8_t data[] = { 0x01 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// POSITIVE GAMMA CORRECTION
|
|
ILI9341_WriteCommand(0xE0);
|
|
{
|
|
uint8_t data[] = { 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1,
|
|
0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00 };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// NEGATIVE GAMMA CORRECTION
|
|
ILI9341_WriteCommand(0xE1);
|
|
{
|
|
uint8_t data[] = { 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1,
|
|
0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
// EXIT SLEEP
|
|
ILI9341_WriteCommand(0x11);
|
|
HAL_Delay(120);
|
|
|
|
// TURN ON DISPLAY
|
|
ILI9341_WriteCommand(0x29);
|
|
|
|
// MADCTL
|
|
ILI9341_WriteCommand(0x36);
|
|
{
|
|
uint8_t data[] = { ILI9341_ROTATION };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
void ILI9341_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
|
|
if((x >= ILI9341_WIDTH) || (y >= ILI9341_HEIGHT))
|
|
return;
|
|
|
|
ILI9341_Select();
|
|
|
|
ILI9341_SetAddressWindow(x, y, x+1, y+1);
|
|
uint8_t data[] = { color >> 8, color & 0xFF };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
static void ILI9341_WriteChar(uint16_t x, uint16_t y, char ch, FontDef font, uint16_t color, uint16_t bgcolor) {
|
|
uint32_t i, b, j;
|
|
|
|
ILI9341_SetAddressWindow(x, y, x+font.width-1, y+font.height-1);
|
|
|
|
for(i = 0; i < font.height; i++) {
|
|
b = font.data[(ch - 32) * font.height + i];
|
|
for(j = 0; j < font.width; j++) {
|
|
if((b << j) & 0x8000) {
|
|
uint8_t data[] = { color >> 8, color & 0xFF };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
} else {
|
|
uint8_t data[] = { bgcolor >> 8, bgcolor & 0xFF };
|
|
ILI9341_WriteData(data, sizeof(data));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ILI9341_WriteString(uint16_t x, uint16_t y, const char* str, FontDef font, uint16_t color, uint16_t bgcolor) {
|
|
ILI9341_Select();
|
|
|
|
while(*str) {
|
|
if(x + font.width >= ILI9341_WIDTH) {
|
|
x = 0;
|
|
y += font.height;
|
|
if(y + font.height >= ILI9341_HEIGHT) {
|
|
break;
|
|
}
|
|
|
|
if(*str == ' ') {
|
|
// skip spaces in the beginning of the new line
|
|
str++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ILI9341_WriteChar(x, y, *str, font, color, bgcolor);
|
|
x += font.width;
|
|
str++;
|
|
}
|
|
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
void ILI9341_FillRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
|
|
// clipping
|
|
if((x >= ILI9341_WIDTH) || (y >= ILI9341_HEIGHT)) return;
|
|
if((x + w - 1) >= ILI9341_WIDTH) w = ILI9341_WIDTH - x;
|
|
if((y + h - 1) >= ILI9341_HEIGHT) h = ILI9341_HEIGHT - y;
|
|
|
|
ILI9341_Select();
|
|
ILI9341_SetAddressWindow(x, y, x+w-1, y+h-1);
|
|
|
|
uint8_t data[] = { color >> 8, color & 0xFF };
|
|
HAL_GPIO_WritePin(ILI9341_DC_GPIO_Port, ILI9341_DC_Pin, GPIO_PIN_SET);
|
|
for(y = h; y > 0; y--) {
|
|
for(x = w; x > 0; x--) {
|
|
HAL_SPI_Transmit(&ILI9341_SPI_PORT, data, sizeof(data), HAL_MAX_DELAY);
|
|
}
|
|
}
|
|
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
void ILI9341_writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
|
|
|
|
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
|
|
if (steep) {
|
|
_swap_int16_t(x0, y0);
|
|
_swap_int16_t(x1, y1);
|
|
}
|
|
|
|
if (x0 > x1) {
|
|
_swap_int16_t(x0, x1);
|
|
_swap_int16_t(y0, y1);
|
|
}
|
|
|
|
int16_t dx, dy;
|
|
dx = x1 - x0;
|
|
dy = abs(y1 - y0);
|
|
|
|
int16_t err = dx / 2;
|
|
int16_t ystep;
|
|
|
|
if (y0 < y1) {
|
|
ystep = 1;
|
|
} else {
|
|
ystep = -1;
|
|
}
|
|
|
|
for (; x0<=x1; x0++) {
|
|
if (steep) {
|
|
ILI9341_writePixel(y0, x0, color);
|
|
} else {
|
|
ILI9341_writePixel(x0, y0, color);
|
|
}
|
|
err -= dy;
|
|
if (err < 0) {
|
|
y0 += ystep;
|
|
err += dx;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ILI9341_DrawArrow(int16_t x1, int16_t y1, int16_t x2, int16_t y2, float k, uint16_t color)
|
|
{
|
|
float dx, dy, x2Outer,y2Outer,x3,y3,x4,y4;
|
|
|
|
dx = x2 + (x1 - x2) * k;
|
|
dy = y2 + (y1 - y2) * k;
|
|
|
|
x2Outer = x2 - dx;
|
|
y2Outer = dy - y2;
|
|
x3 = y2Outer * k + dx;
|
|
y3 = x2Outer * k + dy;
|
|
x4 = dx - y2Outer * k;
|
|
y4 = dy - x2Outer * k;
|
|
ILI9341_writeLine(round(x1), round(y1), round(x2), round(y2), color);
|
|
ILI9341_writeLine(round(x1), round(y1), round(dx), round(dy), color);
|
|
ILI9341_writeLine(round(x3), round(y3), round(x2), round(y2), color);
|
|
ILI9341_writeLine(round(x2), round(y2), round(x4), round(y4), color);
|
|
}
|
|
|
|
void ILI9341_drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
|
|
ILI9341_Select();
|
|
ILI9341_writeLine(x, y, x+w-1, y, color);
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
void ILI9341_writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
|
|
// Overwrite in subclasses if startWrite is defined!
|
|
// Example: writeLine(x, y, x+w-1, y, color);
|
|
// or writeFillRect(x, y, w, 1, color);
|
|
ILI9341_drawFastHLine(x, y, w, color);
|
|
}
|
|
|
|
void ILI9341_drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
|
|
ILI9341_Select();
|
|
ILI9341_writeLine(x, y, x, y+h-1, color);
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
void ILI9341_writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
|
|
// Overwrite in subclasses if startWrite is defined!
|
|
// Can be just writeLine(x, y, x, y+h-1, color);
|
|
// or writeFillRect(x, y, 1, h, color);
|
|
ILI9341_drawFastVLine(x, y, h, color);
|
|
}
|
|
|
|
void ILI9341_writePixel(int16_t x, int16_t y, uint16_t color){
|
|
ILI9341_DrawPixel(x, y, color);
|
|
}
|
|
|
|
void ILI9341_drawCircleHelper( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color) {
|
|
int16_t f = 1 - r;
|
|
int16_t ddF_x = 1;
|
|
int16_t ddF_y = -2 * r;
|
|
int16_t x = 0;
|
|
int16_t y = r;
|
|
|
|
while (x<y) {
|
|
if (f >= 0) {
|
|
y--;
|
|
ddF_y += 2;
|
|
f += ddF_y;
|
|
}
|
|
x++;
|
|
ddF_x += 2;
|
|
f += ddF_x;
|
|
if (cornername & 0x4) {
|
|
ILI9341_writePixel(x0 + x, y0 + y, color);
|
|
ILI9341_writePixel(x0 + y, y0 + x, color);
|
|
}
|
|
if (cornername & 0x2) {
|
|
ILI9341_writePixel(x0 + x, y0 - y, color);
|
|
ILI9341_writePixel(x0 + y, y0 - x, color);
|
|
}
|
|
if (cornername & 0x8) {
|
|
ILI9341_writePixel(x0 - y, y0 + x, color);
|
|
ILI9341_writePixel(x0 - x, y0 + y, color);
|
|
}
|
|
if (cornername & 0x1) {
|
|
ILI9341_writePixel(x0 - y, y0 - x, color);
|
|
ILI9341_writePixel(x0 - x, y0 - y, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ILI9341_drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
|
|
int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis
|
|
if(r > max_radius) r = max_radius;
|
|
// smarter version
|
|
ILI9341_Select();
|
|
ILI9341_writeFastHLine(x+r , y , w-2*r, color); // Top
|
|
ILI9341_writeFastHLine(x+r , y+h-1, w-2*r, color); // Bottom
|
|
ILI9341_writeFastVLine(x , y+r , h-2*r, color); // Left
|
|
ILI9341_writeFastVLine(x+w-1, y+r , h-2*r, color); // Right
|
|
// draw four corners
|
|
ILI9341_drawCircleHelper(x+r , y+r , r, 1, color);
|
|
ILI9341_drawCircleHelper(x+w-r-1, y+r , r, 2, color);
|
|
ILI9341_drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
|
|
ILI9341_drawCircleHelper(x+r , y+h-r-1, r, 8, color);
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
void ILI9341_drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
|
|
int16_t f = 1 - r;
|
|
int16_t ddF_x = 1;
|
|
int16_t ddF_y = -2 * r;
|
|
int16_t x = 0;
|
|
int16_t y = r;
|
|
|
|
ILI9341_Select();
|
|
ILI9341_writePixel(x0 , y0+r, color);
|
|
ILI9341_writePixel(x0 , y0-r, color);
|
|
ILI9341_writePixel(x0+r, y0 , color);
|
|
ILI9341_writePixel(x0-r, y0 , color);
|
|
|
|
while (x<y) {
|
|
if (f >= 0) {
|
|
y--;
|
|
ddF_y += 2;
|
|
f += ddF_y;
|
|
}
|
|
x++;
|
|
ddF_x += 2;
|
|
f += ddF_x;
|
|
|
|
ILI9341_writePixel(x0 + x, y0 + y, color);
|
|
ILI9341_writePixel(x0 - x, y0 + y, color);
|
|
ILI9341_writePixel(x0 + x, y0 - y, color);
|
|
ILI9341_writePixel(x0 - x, y0 - y, color);
|
|
ILI9341_writePixel(x0 + y, y0 + x, color);
|
|
ILI9341_writePixel(x0 - y, y0 + x, color);
|
|
ILI9341_writePixel(x0 + y, y0 - x, color);
|
|
ILI9341_writePixel(x0 - y, y0 - x, color);
|
|
}
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
|
|
void ILI9341_FillScreen(uint16_t color) {
|
|
ILI9341_FillRectangle(0, 0, ILI9341_WIDTH, ILI9341_HEIGHT, color);
|
|
}
|
|
|
|
void ILI9341_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) {
|
|
if((x >= ILI9341_WIDTH) || (y >= ILI9341_HEIGHT)) return;
|
|
if((x + w - 1) >= ILI9341_WIDTH) return;
|
|
if((y + h - 1) >= ILI9341_HEIGHT) return;
|
|
|
|
ILI9341_Select();
|
|
ILI9341_SetAddressWindow(x, y, x+w-1, y+h-1);
|
|
ILI9341_WriteData((uint8_t*)data, sizeof(uint16_t)*w*h);
|
|
ILI9341_Unselect();
|
|
}
|
|
|
|
void ILI9341_InvertColors(bool invert) {
|
|
ILI9341_Select();
|
|
ILI9341_WriteCommand(invert ? 0x21 /* INVON */ : 0x20 /* INVOFF */);
|
|
ILI9341_Unselect();
|
|
}
|
|
|