diff --git a/examples/color_lcd/Makefile b/examples/color_lcd/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c2417fba9d1ccb8e0fffd6e13978439be546ee73 --- /dev/null +++ b/examples/color_lcd/Makefile @@ -0,0 +1,10 @@ +all : flash + +TARGET:=color_lcd + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean + + diff --git a/examples/color_lcd/ch32v_hal.h b/examples/color_lcd/ch32v_hal.h new file mode 100644 index 0000000000000000000000000000000000000000..ffcdd56b178e0d6c79f9907ba13c573ce97b9039 --- /dev/null +++ b/examples/color_lcd/ch32v_hal.h @@ -0,0 +1,72 @@ +// +// CH32V Hardware Abstraction layer +// written by Larry Bank +// bitbank@pobox.com +// Copyright (c) 2023 BitBank Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +#ifndef CH32V_HAL_H_ +#define CH32V_HAL_H_ + +#include "ch32v003fun.h" +#include <stdio.h> +#include <stdint.h> + +#define BITBANG +// GPIO pin states +enum { + OUTPUT = 0, + INPUT, + INPUT_PULLUP, + INPUT_PULLDOWN +}; + +#define PROGMEM +#define memcpy_P memcpy +#define pgm_read_byte(s) *(uint8_t *)s +#define pgm_read_word(s) *(uint16_t *)s + +// Wrapper methods +void delay(int i); +// +// Digital pin functions use a numbering scheme to make it easier to map the +// pin number to a port name and number +// The GPIO ports A-D become the most significant nibble of the pin number +// for example, to use Port C pin 7 (C7), use the pin number 0xC7 +// +void pinMode(uint8_t u8Pin, int iMode); +uint8_t digitalRead(uint8_t u8Pin); +void digitalWrite(uint8_t u8Pin, uint8_t u8Value); + +// The Wire library is a C++ class; I've created a work-alike to my +// BitBang_I2C API which is a set of C functions to simplify I2C +void I2CInit(uint8_t u8SDA, uint8_t u8SCL, int iSpeed); +void I2CWrite(uint8_t u8Addr, uint8_t *pData, int iLen); +int I2CRead(uint8_t u8Addr, uint8_t *pData, int iLen); +void I2CReadRegister(uint8_t u8Addr, uint8_t u8Reg, uint8_t *pData, int iLen); +int I2CTest(uint8_t u8Addr); +void I2CSetSpeed(int iSpeed); + +// SPI1 (polling mode) +void SPI_write(uint8_t *pData, int iLen); +void SPI_begin(int iSpeed, int iMode); + + +// Random stuff +void Standby82ms(uint8_t iTicks); +void breatheLED(uint8_t u8Pin, int iPeriod); + + +#endif /* CH32V_HAL_H_ */ diff --git a/examples/color_lcd/ch32v_hal.inl b/examples/color_lcd/ch32v_hal.inl new file mode 100644 index 0000000000000000000000000000000000000000..ef27b17851d610899c2369a4a05d9833ebbd4207 --- /dev/null +++ b/examples/color_lcd/ch32v_hal.inl @@ -0,0 +1,495 @@ +// +// CH32V Hardware Abstraction layer +// written by Larry Bank +// bitbank@pobox.com +// Copyright (c) 2023 BitBank Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +#include "ch32v_hal.h" + +#ifdef BITBANG +uint8_t u8SDA_Pin, u8SCL_Pin; +int iDelay = 1; +#endif + +void delay(int i) +{ + Delay_Ms(i); +} +// Arduino-like API defines and function wrappers for WCH MCUs + +void pinMode(uint8_t u8Pin, int iMode) +{ +GPIO_TypeDef *pGPIO; + + if (u8Pin < 0xa0 || u8Pin > 0xdf) return; // invalid pin number + + switch (u8Pin & 0xf0) { + case 0xa0: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA; + pGPIO = GPIOA; + break; + case 0xc0: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + pGPIO = GPIOC; + break; + case 0xd0: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; + pGPIO = GPIOD; + break; + } + u8Pin &= 0xf; // isolate the pin from this port + pGPIO->CFGLR &= ~(0xf << (4 * u8Pin)); // unset all flags + + switch (iMode) { + case OUTPUT: + pGPIO->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4*u8Pin); + break; + case INPUT: + pGPIO->CFGLR |= (GPIO_CNF_IN_FLOATING << (4*u8Pin)); + break; + case INPUT_PULLUP: + pGPIO->CFGLR |= (GPIO_CNF_IN_PUPD << (4*u8Pin)); + pGPIO->BSHR = (1 << u8Pin); + break; + case INPUT_PULLDOWN: + pGPIO->CFGLR |= (GPIO_CNF_IN_PUPD << (4 * u8Pin)); + pGPIO->BCR = (1 << u8Pin); + break; + } // switch on iMode +} /* pinMode() */ + +uint8_t digitalRead(uint8_t u8Pin) +{ + uint32_t u32GPIO = 1 << (u8Pin & 0xf); + uint32_t u32Value = 0; + switch (u8Pin & 0xf0) { + case 0xa0: + u32Value = GPIOA->INDR & u32GPIO; + break; + case 0xc0: + u32Value = GPIOC->INDR & u32GPIO; + break; + case 0xd0: + u32Value = GPIOD->INDR & u32GPIO; + break; + } + return (u32Value == 0); +} /* digitalRead() */ + +void digitalWrite(uint8_t u8Pin, uint8_t u8Value) +{ + uint32_t u32Value = 1 << (u8Pin & 0xf); // turn on bit + if (!u8Value) + u32Value <<= 16; // turn off bit + + switch (u8Pin & 0xf0) { + case 0xa0: + GPIOA->BSHR = u32Value; + break; + case 0xc0: + GPIOC->BSHR = u32Value; + break; + case 0xd0: + GPIOD->BSHR = u32Value; + break; + } +} /* digitalWrite() */ + +#ifdef BITBANG +uint8_t SDA_READ(void) +{ + return digitalRead(u8SDA_Pin); +} +void SDA_HIGH(void) +{ + pinMode(u8SDA_Pin, INPUT); +} +void SDA_LOW(void) +{ + pinMode(u8SDA_Pin, OUTPUT); + digitalWrite(u8SDA_Pin, 0); +} +void SCL_HIGH(void) +{ + pinMode(u8SCL_Pin, INPUT); +} +void SCL_LOW(void) +{ + pinMode(u8SCL_Pin, OUTPUT); + digitalWrite(u8SCL_Pin, 0); +} +void I2CSetSpeed(int iSpeed) +{ + if (iSpeed >= 400000) iDelay = 1; + else if (iSpeed >= 100000) iDelay = 10; + else iDelay = 20; +} +void I2CInit(uint8_t u8SDA, uint8_t u8SCL, int iSpeed) +{ + u8SDA_Pin = u8SDA; + u8SCL_Pin = u8SCL; + if (iSpeed >= 400000) iDelay = 1; + else if (iSpeed >= 100000) iDelay = 10; + else iDelay = 20; +} /* I2CInit() */ + +void my_sleep_us(int iDelay) +{ + Delay_Us(iDelay); +} +// Transmit a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// + +int i2cByteOut(uint8_t b) +{ +uint8_t i, ack; + +for (i=0; i<8; i++) +{ +// my_sleep_us(iDelay); + if (b & 0x80) + SDA_HIGH(); // set data line to 1 + else + SDA_LOW(); // set data line to 0 + b <<= 1; +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high (slave latches data) + my_sleep_us(iDelay); + SCL_LOW(); // clock low + my_sleep_us(iDelay); +} // for i +//my_sleep_us(iDelay); +// read ack bit +SDA_HIGH(); // set data line for reading +//my_sleep_us(iDelay); +SCL_HIGH(); // clock line high +my_sleep_us(iDelay); // DEBUG - delay/2 +ack = SDA_READ(); +//my_sleep_us(iDelay); +SCL_LOW(); // clock low +my_sleep_us(iDelay); // DEBUG - delay/2 +SDA_LOW(); // data low +return (ack == 0); // a low ACK bit means success +} /* i2cByteOut() */ + +// +// Receive a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// +uint8_t i2cByteIn(uint8_t bLast) +{ +uint8_t i; +uint8_t b = 0; + + SDA_HIGH(); // set data line as input + for (i=0; i<8; i++) + { + my_sleep_us(iDelay); // wait for data to settle + SCL_HIGH(); // clock high (slave latches data) + my_sleep_us(iDelay); + b <<= 1; + if (SDA_READ() != 0) // read the data bit + b |= 1; // set data bit + SCL_LOW(); // clock low + } // for i + if (bLast) + SDA_HIGH(); // last byte sends a NACK + else + SDA_LOW(); +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high + my_sleep_us(iDelay); + SCL_LOW(); // clock low to send ack + my_sleep_us(iDelay); +// SDA_HIGH(); + SDA_LOW(); // data low + return b; +} /* i2cByteIn() */ +// +// Send I2C STOP condition +// +void i2cEnd(void) +{ + SDA_LOW(); // data line low + my_sleep_us(iDelay); + SCL_HIGH(); // clock high + my_sleep_us(iDelay); + SDA_HIGH(); // data high + my_sleep_us(iDelay); +} /* i2cEnd() */ + +int i2cBegin(uint8_t addr, uint8_t bRead) +{ + int rc; +// SCL_HIGH(); +// my_sleep_us(iDelay); + SDA_LOW(); // data line low first + my_sleep_us(iDelay); + SCL_LOW(); // then clock line low is a START signal + addr <<= 1; + if (bRead) + addr++; // set read bit + rc = i2cByteOut(addr); // send the slave address and R/W bit + return rc; +} /* i2cBegin() */ + +void I2CWrite(uint8_t addr, uint8_t *pData, int iLen) +{ +uint8_t b; +int rc; + + i2cBegin(addr, 0); + rc = 1; + while (iLen && rc == 1) + { + b = *pData++; + rc = i2cByteOut(b); + if (rc == 1) // success + { + iLen--; + } + } // for each byte + i2cEnd(); +//return (rc == 1) ? (iOldLen - iLen) : 0; // 0 indicates bad ack from sending a byte +} /* I2CWrite() */ + +int I2CRead(uint8_t addr, uint8_t *pData, int iLen) +{ + i2cBegin(addr, 1); + while (iLen--) + { + *pData++ = i2cByteIn(iLen == 0); + } // for each byte + i2cEnd(); + return 1; +} /* I2CRead() */ + +int I2CTest(uint8_t addr) +{ +int response = 0; + + if (i2cBegin(addr, 0)) // try to write to the given address + { + response = 1; + } + i2cEnd(); +return response; +} /* I2CTest() */ + +#else // hardware I2C + +void I2CSetSpeed(int iSpeed) +{ + I2C_InitTypeDef I2C_InitTSturcture={0}; + + I2C_InitTSturcture.I2C_ClockSpeed = iSpeed; + I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C; + I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9; + I2C_InitTSturcture.I2C_OwnAddress1 = 0x02; //address; sender's unimportant address + I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable; + I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; + I2C_Init( I2C1, &I2C_InitTSturcture ); +} /* I2CSetSpeed() */ + +void I2CInit(uint8_t iSDA, uint8_t iSCL, int iSpeed) +{ + (void)iSDA; (void)iSCL; + + GPIO_InitTypeDef GPIO_InitStructure={0}; + + // Fixed to pins C1/C2 for now + RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE ); + RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE ); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init( GPIOC, &GPIO_InitStructure ); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init( GPIOC, &GPIO_InitStructure ); + + I2C_DeInit(I2C1); + I2CSetSpeed(iSpeed); + + I2C_Cmd( I2C1, ENABLE ); + + I2C_AcknowledgeConfig( I2C1, ENABLE ); + while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ); +} /* I2CInit() */ + +// +// Returns 0 for timeout error +// returns 1 for success +// +int I2CRead(uint8_t u8Addr, uint8_t *pData, int iLen) +{ + int iTimeout = 0; + + I2C_GenerateSTART( I2C1, ENABLE ); + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ); + + I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Receiver ); + + while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) ) { + iTimeout++; + } + if (iTimeout >= 10000) return 0; // error + + iTimeout = 0; + while(iLen && iTimeout < 10000) + { + if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) != RESET ) + { + iTimeout = 0; + pData[0] = I2C_ReceiveData( I2C1 ); + pData++; + iLen--; + } else { + iTimeout++; + } + } + + I2C_GenerateSTOP( I2C1, ENABLE ); + return (iLen == 0); + +} /* I2CRead() */ + +void I2CWrite(uint8_t u8Addr, uint8_t *pData, int iLen) +{ + I2C_GenerateSTART( I2C1, ENABLE ); + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ); + + I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Transmitter ); + + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) ); + + while(iLen) + { + if( I2C_GetFlagStatus( I2C1, I2C_FLAG_TXE ) != RESET ) + { + I2C_SendData( I2C1, pData[0] ); + pData++; + iLen--; + } + } + + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) ); + I2C_GenerateSTOP( I2C1, ENABLE ); + +} /* I2CWrite() */ + +int I2CTest(uint8_t u8Addr) +{ + int iTimeout = 0; + + I2C_ClearFlag(I2C1, I2C_FLAG_AF); + I2C_GenerateSTART( I2C1, ENABLE ); + while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ) { + iTimeout++; + } + if (iTimeout >= 10000) return 0; // no pull-ups, open bus + + I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Transmitter ); + + while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) ) { + iTimeout++; + } + if (iTimeout >= 10000) return 0; // no device at that address; the MTMS flag will never get set + + I2C_GenerateSTOP( I2C1, ENABLE ); + // check ACK failure flag + return (I2C_GetFlagStatus(I2C1, /*I2C_FLAG_TXE*/I2C_FLAG_AF) == RESET); // 0 = fail, 1 = succeed + +} /* I2CTest() */ +#endif // !BITBANG + +// +// Read N bytes starting at a specific I2C internal register +// returns 1 for success, 0 for error +// +void I2CReadRegister(uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen) +{ + I2CWrite(iAddr, &u8Register, 1); + I2CRead(iAddr, pData, iLen); +} /* I2CReadRegister() */ + +void SPI_begin(int iSpeed, int iMode) +{ +uint32_t u32Prescaler = 0; + + if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/2)) + u32Prescaler = SPI_BaudRatePrescaler_2; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/4)) + u32Prescaler = SPI_BaudRatePrescaler_4; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/8)) + u32Prescaler = SPI_BaudRatePrescaler_8; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/16)) + u32Prescaler = SPI_BaudRatePrescaler_16; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/32)) + u32Prescaler = SPI_BaudRatePrescaler_32; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/64)) + u32Prescaler = SPI_BaudRatePrescaler_64; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/128)) + u32Prescaler = SPI_BaudRatePrescaler_128; + else + u32Prescaler = SPI_BaudRatePrescaler_256; + + // Enable GPIOC and SPI + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1; + + // PC5 is SCK, 10MHz Output, alt func, p-p + GPIOC->CFGLR &= ~(0xf<<(4*5)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5); + + // PC6 is MOSI, 10MHz Output, alt func, p-p + GPIOC->CFGLR &= ~(0xf<<(4*6)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); + + // Configure SPI + SPI1->CTLR1 = + SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b | + SPI_Mode_Master | SPI_Direction_1Line_Tx | + u32Prescaler; + + // Enable DMA on SPI + SPI1->CTLR2 |= SPI_I2S_DMAReq_Tx; + + // enable SPI port + SPI1->CTLR1 |= CTLR1_SPE_Set; + + //SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); // enable DMA on transmit + + //SPI_Cmd( SPI1, ENABLE ); +} /* SPI_begin() */ + +// polling write +void SPI_write(uint8_t *pData, int iLen) +{ +int i = 0; + + while (i < iLen) { + while(!(SPI1->STATR & SPI_STATR_TXE)); // wait for TXE + SPI1->DATAR = *pData++; // send data + i++; + } + while(SPI1->STATR & SPI_STATR_BSY); // wait for not busy +} /* SPI_write() */ diff --git a/examples/color_lcd/color_lcd.c b/examples/color_lcd/color_lcd.c new file mode 100644 index 0000000000000000000000000000000000000000..fb359193d0ea4fc2e4cddea315c78d092f75d8bc --- /dev/null +++ b/examples/color_lcd/color_lcd.c @@ -0,0 +1,65 @@ + +#include "ch32v_hal.inl" +#include "spi_lcd.inl" +#include <stdlib.h> + +// Pin definitions for the LCD project protoboard +//#define BL_PIN 0xd2 +//#define CS_PIN 0xff +#define BL_PIN 0xd5 +#define CS_PIN 0xd2 +#define DC_PIN 0xd3 +#define RST_PIN 0xd4 + +static uint16_t usPal[8] = {COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, + COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW}; + +/* White Noise Generator State */ +#define NOISE_BITS 8 +#define NOISE_MASK ((1<<NOISE_BITS)-1) +#define NOISE_POLY_TAP0 31 +#define NOISE_POLY_TAP1 21 +#define NOISE_POLY_TAP2 1 +#define NOISE_POLY_TAP3 0 +uint32_t lfsr = 1; + +/* + * random byte generator + */ +uint8_t rand8(void) +{ + uint8_t bit; + uint32_t new_data; + + for(bit=0;bit<NOISE_BITS;bit++) + { + new_data = ((lfsr>>NOISE_POLY_TAP0) ^ + (lfsr>>NOISE_POLY_TAP1) ^ + (lfsr>>NOISE_POLY_TAP2) ^ + (lfsr>>NOISE_POLY_TAP3)); + lfsr = (lfsr<<1) | (new_data&1); + } + + return lfsr&NOISE_MASK; +} + +int main(void) +{ +int i, dx, iColor; + + SystemInit(); + + lcdInit(LCD_ST7735_80x160, 24000000, CS_PIN, DC_PIN, RST_PIN, BL_PIN); + lcdFill(COLOR_GREEN); + i = 1; dx = 1; + while (1) { + lcdRectangle(rand8() & 127, rand8() & 63, rand8() & 63, rand8() & 31, usPal[(iColor+1)&7], 1); + lcdEllipse(rand8() & 127, rand8() & 63, rand8() & 63, rand8() & 31, usPal[iColor & 7], 1); + iColor++; + lcdWriteString(0,i,"CH32V003 is fast", COLOR_BLUE, COLOR_GREEN, FONT_8x8); + lcdWriteString(0,i+8,"enough for me!", COLOR_RED, COLOR_GREEN, FONT_8x8); + lcdWriteString(0,i+16,"Large Font!", COLOR_WHITE, COLOR_MAGENTA, FONT_12x16); + i += dx; + if (i >= 39 || i == 1) dx = -dx; + } +} diff --git a/examples/color_lcd/funconfig.h b/examples/color_lcd/funconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..998cf76fede1479f162c72ef34d3a4d747430093 --- /dev/null +++ b/examples/color_lcd/funconfig.h @@ -0,0 +1,7 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define CH32V003 1 + +#endif + diff --git a/examples/color_lcd/spi_lcd.h b/examples/color_lcd/spi_lcd.h new file mode 100644 index 0000000000000000000000000000000000000000..03f9d407c1f3d568636ef0e1b5301f302e28113f --- /dev/null +++ b/examples/color_lcd/spi_lcd.h @@ -0,0 +1,113 @@ +// +// spi_lcd.h +// a Sitronix LCD display library +// Created on: Sep 11, 2023 +// written by Larry Bank (bitbank@pobox.com) +// +// +// Copyright 2023 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== +// + +#ifndef USER_SPI_LCD_H_ +#define USER_SPI_LCD_H_ + +#include "funconfig.h" +#include <stdint.h> +#include "ch32v_hal.h" +#include <string.h> + +enum { + LCD_ST7735_80x160 = 0, + LCD_ST7735_80x160_B, + LCD_ST7735_128x128, + LCD_ST7735_128x160, + LCD_ST7789_135x240, + LCD_ST7789_172x320, + LCD_ST7789_240x240, + LCD_ST7789_240x280, + LCD_ST7789_240x320, + LCD_GC9107_128x128, + LCD_ILI9341_240x320, + LCD_COUNT +}; + +enum { + ORIENTATION_0 = 0, + ORIENTATION_90, + ORIENTATION_180, + ORIENTATION_270 +}; + +#ifdef CH32V003 +// 2 complete lines across a 320 pixel color LCD +#define CACHE_SIZE (320*2) +#else // larger RAM systems +#define CACHED_LINES 16 +// enough memory to hold 16 lines of the display for fast character drawing +#define CACHE_SIZE (320*CACHED_LINES) +#endif +// memory offset of visible area (80x160 out of 240x320) + +// Proportional font data taken from Adafruit_GFX library +/// Font data stored PER GLYPH +typedef struct { + uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap + uint8_t width; ///< Bitmap dimensions in pixels + uint8_t height; ///< Bitmap dimensions in pixels + uint8_t xAdvance; ///< Distance to advance cursor (x axis) + int8_t xOffset; ///< X dist from cursor pos to UL corner + int8_t yOffset; ///< Y dist from cursor pos to UL corner +} GFXglyph; + +typedef struct { + uint8_t *bitmap; ///< Glyph bitmaps, concatenated + GFXglyph *glyph; ///< Glyph array + uint8_t first; ///< ASCII extents (first char) + uint8_t last; ///< ASCII extents (last char) + uint8_t yAdvance; ///< Newline distance (y axis) +} GFXfont; + +void lcdFill(uint16_t u16Color); +void lcdInit(int iLCDType, uint32_t u32Speed, uint8_t u8CSPin, uint8_t u8DCPin, uint8_t u8RSTPin, uint8_t u8BLPin); +void lcdWriteCMD(uint8_t ucCMD); +void lcdWriteDATA(uint8_t *pData, int iLen); +int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize); +int lcdDrawTile(int x, int y, int iTileWidth, int iTileHeight, unsigned char *pTile, int iPitch); +int lcdWriteStringCustom(GFXfont *pFont, int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int bBlank); +void lcdOrientation(int iOrientation); +void lcdRectangle(int x, int y, int cx, int cy, uint16_t usColor, int bFill); +void lcdEllipse(int centerX, int centerY, int radiusX, int radiusY, uint16_t color, int bFilled); + +void memset16(uint16_t *u16Dest, uint16_t u16Pattern, int iLen); +#define COLOR_BLACK 0 +#define COLOR_WHITE 0xffff +#define COLOR_RED 0xf800 +#define COLOR_GREEN 0x7e0 +#define COLOR_BLUE 0x1f +#define COLOR_MAGENTA 0xf81f +#define COLOR_CYAN 0x7ff +#define COLOR_YELLOW 0xffe0 + +// MADCTL flip bits +#define MADCTL_YFLIP 0x80 +#define MADCTL_XFLIP 0x40 +#define MADCTL_VFLIP 0x20 + +enum { + FONT_6x8 = 0, + FONT_8x8, + FONT_12x16, + FONT_COUNT +}; + +#endif /* USER_SPI_LCD_H_ */ diff --git a/examples/color_lcd/spi_lcd.inl b/examples/color_lcd/spi_lcd.inl new file mode 100644 index 0000000000000000000000000000000000000000..796267668336054f62bb6ff2509cbf8ee820becf --- /dev/null +++ b/examples/color_lcd/spi_lcd.inl @@ -0,0 +1,1198 @@ +// +// spi_lcd.c +// a Sitronix LCD display library +// Created on: Sep 11, 2023 +// written by Larry Bank (bitbank@pobox.com) +// +// +// Copyright 2023 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== +// + +#include "spi_lcd.h" + +static uint8_t u8CS, u8DC, u8BL; +static uint8_t u8MADCTL; // original value +static int iCursorX, iCursorY; +static int iNativeWidth, iNativeHeight, iNativeXOff, iNativeYOff, iLCDWidth, iLCDHeight, iLCDPitch, iLCDXOff, iLCDYOff; +static uint8_t u8Cache0[CACHE_SIZE]; // ping-pong data buffers +static uint8_t u8Cache1[CACHE_SIZE]; +static uint8_t *pCache0 = u8Cache0, *pCache1 = u8Cache1; +volatile int bDMA = 0; + +const uint8_t ucILI9341InitList[] = { + 4, 0xEF, 0x03, 0x80, 0x02, + 4, 0xCF, 0x00, 0XC1, 0X30, + 5, 0xED, 0x64, 0x03, 0X12, 0X81, + 4, 0xE8, 0x85, 0x00, 0x78, + 6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, + 2, 0xF7, 0x20, + 3, 0xEA, 0x00, 0x00, + 2, 0xc0, 0x23, // Power control + 2, 0xc1, 0x10, // Power control + 3, 0xc5, 0x3e, 0x28, // VCM control + 2, 0xc7, 0x86, // VCM control2 + 2, 0x36, 0x48, // Memory Access Control + 1, 0x20, // non inverted + 2, 0x3a, 0x55, + 3, 0xb1, 0x00, 0x18, + 4, 0xb6, 0x08, 0x82, 0x27, // Display Function Control + 2, 0xF2, 0x00, // Gamma Function Disable + 2, 0x26, 0x01, // Gamma curve selected + 16, 0xe0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma + 16, 0xe1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma + 3, 0xb1, 0x00, 0x10, // FrameRate Control 119Hz + 0 +}; + +const uint8_t uc240x240InitList[] = { + 1, 0x13, // partial mode off + 1, 0x21, // display inversion off + 2, 0x36,0x60, // memory access 0xc0 for 180 degree flipped + 2, 0x3a,0x55, // pixel format; 5=RGB565 + 3, 0x37,0x00,0x00, // + 6, 0xb2,0x0c,0x0c,0x00,0x33,0x33, // Porch control + 2, 0xb7,0x35, // gate control + 2, 0xbb,0x1a, // VCOM + 2, 0xc0,0x2c, // LCM + 2, 0xc2,0x01, // VDV & VRH command enable + 2, 0xc3,0x0b, // VRH set + 2, 0xc4,0x20, // VDV set + 2, 0xc6,0x0f, // FR control 2 + 3, 0xd0, 0xa4, 0xa1, // Power control 1 + 15, 0xe0, 0x00,0x19,0x1e,0x0a,0x09,0x15,0x3d,0x44,0x51,0x12,0x03, + 0x00,0x3f,0x3f, // gamma 1 + 15, 0xe1, 0x00,0x18,0x1e,0x0a,0x09,0x25,0x3f,0x43,0x52,0x33,0x03, + 0x00,0x3f,0x3f, // gamma 2 + 1, 0x29, // display on + 0 +}; + +const uint8_t ucGC9107InitList[] = { + 1, 0x13, // partial mode off + 1, 0x21, // display inversion off + 2, 0x36,0x68, // memory access 0xc0 for 180 degree flipped + 2, 0x3a,0x55, // pixel format; 5=RGB565 + 3, 0x37,0x00,0x00, // + 6, 0xb2,0x0c,0x0c,0x00,0x33,0x33, // Porch control + 2, 0xb7,0x35, // gate control + 2, 0xbb,0x1a, // VCOM + 2, 0xc0,0x2c, // LCM + 2, 0xc2,0x01, // VDV & VRH command enable + 2, 0xc3,0x0b, // VRH set + 2, 0xc4,0x20, // VDV set + 2, 0xc6,0x0f, // FR control 2 + 3, 0xd0, 0xa4, 0xa1, // Power control 1 + 15, 0xe0, 0x00,0x19,0x1e,0x0a,0x09,0x15,0x3d,0x44,0x51,0x12,0x03, + 0x00,0x3f,0x3f, // gamma 1 + 15, 0xe1, 0x00,0x18,0x1e,0x0a,0x09,0x25,0x3f,0x43,0x52,0x33,0x03, + 0x00,0x3f,0x3f, // gamma 2 + 1, 0x29, // display on + 0 +}; + +const uint8_t uc80InitList[] = { + 2, 0x3a, 0x05, // pixel format RGB565 + 2, 0x36, 0x68, // MADCTL (0/90/180/270 and color/inversion) + 17, 0xe0, 0x09, 0x16, 0x09,0x20, + 0x21,0x1b,0x13,0x19, + 0x17,0x15,0x1e,0x2b, + 0x04,0x05,0x02,0x0e, // gamma sequence + 17, 0xe1, 0x0b,0x14,0x08,0x1e, + 0x22,0x1d,0x18,0x1e, + 0x1b,0x1a,0x24,0x2b, + 0x06,0x06,0x02,0x0f, + 1, 0x20, // display inversion off + 1, 0x29, // display on + 0 +}; + +const uint8_t uc160InitList[] = { + 2, 0x3a, 0x05, // pixel format RGB565 + 2, 0x36, 0x60, // MADCTL + 17, 0xe0, 0x09, 0x16, 0x09,0x20, + 0x21,0x1b,0x13,0x19, + 0x17,0x15,0x1e,0x2b, + 0x04,0x05,0x02,0x0e, // gamma sequence + 17, 0xe1, 0x0b,0x14,0x08,0x1e, + 0x22,0x1d,0x18,0x1e, + 0x1b,0x1a,0x24,0x2b, + 0x06,0x06,0x02,0x0f, + 1, 0x20, // display inversion off + 1, 0x29, // display on + 0 +}; +const uint8_t uc128InitList[] = { + 2, 0x3a, 0x05, // pixel format RGB565 + 2, 0x36, 0x68, // MADCTL + 17, 0xe0, 0x09, 0x16, 0x09,0x20, + 0x21,0x1b,0x13,0x19, + 0x17,0x15,0x1e,0x2b, + 0x04,0x05,0x02,0x0e, // gamma sequence + 17, 0xe1, 0x0b,0x14,0x08,0x1e, + 0x22,0x1d,0x18,0x1e, + 0x1b,0x1a,0x24,0x2b, + 0x06,0x06,0x02,0x0f, + 1, 0x20, // display inversion off + 1, 0x29, // display on + 0 +}; +const uint8_t ucFont[]PROGMEM = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00, + 0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14, + 0x24,0x2e,0x2a,0x6b,0x6b,0x3a,0x12,0x46,0x66,0x30,0x18,0x0c,0x66,0x62, + 0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00,0x04,0x07,0x03,0x00,0x00,0x00, + 0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00, + 0x08,0x2a,0x3e,0x1c,0x3e,0x2a,0x08,0x00,0x08,0x08,0x3e,0x3e,0x08,0x08, + 0x00,0x00,0x80,0xe0,0x60,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08, + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01, + 0x3e,0x7f,0x59,0x4d,0x47,0x7f,0x3e,0x40,0x42,0x7f,0x7f,0x40,0x40,0x00, + 0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00, + 0x18,0x1c,0x16,0x53,0x7f,0x7f,0x50,0x27,0x67,0x45,0x45,0x7d,0x39,0x00, + 0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x03,0x03,0x71,0x79,0x0f,0x07,0x00, + 0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00, + 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x80,0xe6,0x66,0x00,0x00, + 0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00,0x14,0x14,0x14,0x14,0x14,0x14, + 0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00,0x02,0x03,0x59,0x5d,0x07,0x02, + 0x3e,0x7f,0x41,0x5d,0x5d,0x5f,0x0e,0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00, + 0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x1c,0x3e,0x63,0x41,0x41,0x63,0x22, + 0x41,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63, + 0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x1c,0x3e,0x63,0x41,0x51,0x33,0x72, + 0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00, + 0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63, + 0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x7f,0x7f,0x0e,0x1c,0x0e,0x7f,0x7f, + 0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c, + 0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x1e,0x3f,0x21,0x31,0x61,0x7f,0x5e, + 0x41,0x7f,0x7f,0x09,0x19,0x7f,0x66,0x26,0x6f,0x4d,0x49,0x59,0x73,0x32, + 0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00, + 0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x3f,0x7f,0x60,0x30,0x60,0x7f,0x3f, + 0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x07,0x4f,0x78,0x78,0x4f,0x07,0x00, + 0x47,0x63,0x71,0x59,0x4d,0x67,0x73,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00, + 0x01,0x03,0x06,0x0c,0x18,0x30,0x60,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00, + 0x08,0x0c,0x06,0x03,0x06,0x0c,0x08,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x20,0x74,0x54,0x54,0x3c,0x78,0x40, + 0x41,0x7f,0x3f,0x48,0x48,0x78,0x30,0x38,0x7c,0x44,0x44,0x6c,0x28,0x00, + 0x30,0x78,0x48,0x49,0x3f,0x7f,0x40,0x38,0x7c,0x54,0x54,0x5c,0x18,0x00, + 0x48,0x7e,0x7f,0x49,0x03,0x06,0x00,0x98,0xbc,0xa4,0xa4,0xf8,0x7c,0x04, + 0x41,0x7f,0x7f,0x08,0x04,0x7c,0x78,0x00,0x44,0x7d,0x7d,0x40,0x00,0x00, + 0x60,0xe0,0x80,0x84,0xfd,0x7d,0x00,0x41,0x7f,0x7f,0x10,0x38,0x6c,0x44, + 0x00,0x41,0x7f,0x7f,0x40,0x00,0x00,0x7c,0x7c,0x18,0x78,0x1c,0x7c,0x78, + 0x7c,0x78,0x04,0x04,0x7c,0x78,0x00,0x38,0x7c,0x44,0x44,0x7c,0x38,0x00, + 0x84,0xfc,0xf8,0xa4,0x24,0x3c,0x18,0x18,0x3c,0x24,0xa4,0xf8,0xfc,0x84, + 0x44,0x7c,0x78,0x4c,0x04,0x0c,0x18,0x48,0x5c,0x54,0x74,0x64,0x24,0x00, + 0x04,0x04,0x3e,0x7f,0x44,0x24,0x00,0x3c,0x7c,0x40,0x40,0x3c,0x7c,0x40, + 0x1c,0x3c,0x60,0x60,0x3c,0x1c,0x00,0x3c,0x7c,0x60,0x30,0x60,0x7c,0x3c, + 0x44,0x6c,0x38,0x10,0x38,0x6c,0x44,0x9c,0xbc,0xa0,0xa0,0xfc,0x7c,0x00, + 0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00, + 0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00, + 0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70}; + // 5x7 font (in 6x8 cell) +const uint8_t ucSmallFont[] PROGMEM = { +0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x5f,0x06,0x00, +0x07,0x03,0x00,0x07,0x03, +0x24,0x7e,0x24,0x7e,0x24, +0x24,0x2b,0x6a,0x12,0x00, +0x63,0x13,0x08,0x64,0x63, +0x36,0x49,0x56,0x20,0x50, +0x00,0x07,0x03,0x00,0x00, +0x00,0x3e,0x41,0x00,0x00, +0x00,0x41,0x3e,0x00,0x00, +0x08,0x3e,0x1c,0x3e,0x08, +0x08,0x08,0x3e,0x08,0x08, +0x00,0xe0,0x60,0x00,0x00, +0x08,0x08,0x08,0x08,0x08, +0x00,0x60,0x60,0x00,0x00, +0x20,0x10,0x08,0x04,0x02, +0x3e,0x51,0x49,0x45,0x3e, +0x00,0x42,0x7f,0x40,0x00, +0x62,0x51,0x49,0x49,0x46, +0x22,0x49,0x49,0x49,0x36, +0x18,0x14,0x12,0x7f,0x10, +0x2f,0x49,0x49,0x49,0x31, +0x3c,0x4a,0x49,0x49,0x30, +0x01,0x71,0x09,0x05,0x03, +0x36,0x49,0x49,0x49,0x36, +0x06,0x49,0x49,0x29,0x1e, +0x00,0x6c,0x6c,0x00,0x00, +0x00,0xec,0x6c,0x00,0x00, +0x08,0x14,0x22,0x41,0x00, +0x24,0x24,0x24,0x24,0x24, +0x00,0x41,0x22,0x14,0x08, +0x02,0x01,0x59,0x09,0x06, +0x3e,0x41,0x5d,0x55,0x1e, +0x7e,0x11,0x11,0x11,0x7e, +0x7f,0x49,0x49,0x49,0x36, +0x3e,0x41,0x41,0x41,0x22, +0x7f,0x41,0x41,0x41,0x3e, +0x7f,0x49,0x49,0x49,0x41, +0x7f,0x09,0x09,0x09,0x01, +0x3e,0x41,0x49,0x49,0x7a, +0x7f,0x08,0x08,0x08,0x7f, +0x00,0x41,0x7f,0x41,0x00, +0x30,0x40,0x40,0x40,0x3f, +0x7f,0x08,0x14,0x22,0x41, +0x7f,0x40,0x40,0x40,0x40, +0x7f,0x02,0x04,0x02,0x7f, +0x7f,0x02,0x04,0x08,0x7f, +0x3e,0x41,0x41,0x41,0x3e, +0x7f,0x09,0x09,0x09,0x06, +0x3e,0x41,0x51,0x21,0x5e, +0x7f,0x09,0x09,0x19,0x66, +0x26,0x49,0x49,0x49,0x32, +0x01,0x01,0x7f,0x01,0x01, +0x3f,0x40,0x40,0x40,0x3f, +0x1f,0x20,0x40,0x20,0x1f, +0x3f,0x40,0x3c,0x40,0x3f, +0x63,0x14,0x08,0x14,0x63, +0x07,0x08,0x70,0x08,0x07, +0x71,0x49,0x45,0x43,0x00, +0x00,0x7f,0x41,0x41,0x00, +0x02,0x04,0x08,0x10,0x20, +0x00,0x41,0x41,0x7f,0x00, +0x04,0x02,0x01,0x02,0x04, +0x80,0x80,0x80,0x80,0x80, +0x00,0x03,0x07,0x00,0x00, +0x20,0x54,0x54,0x54,0x78, +0x7f,0x44,0x44,0x44,0x38, +0x38,0x44,0x44,0x44,0x28, +0x38,0x44,0x44,0x44,0x7f, +0x38,0x54,0x54,0x54,0x08, +0x08,0x7e,0x09,0x09,0x00, +0x18,0xa4,0xa4,0xa4,0x7c, +0x7f,0x04,0x04,0x78,0x00, +0x00,0x00,0x7d,0x40,0x00, +0x40,0x80,0x84,0x7d,0x00, +0x7f,0x10,0x28,0x44,0x00, +0x00,0x00,0x7f,0x40,0x00, +0x7c,0x04,0x18,0x04,0x78, +0x7c,0x04,0x04,0x78,0x00, +0x38,0x44,0x44,0x44,0x38, +0xfc,0x44,0x44,0x44,0x38, +0x38,0x44,0x44,0x44,0xfc, +0x44,0x78,0x44,0x04,0x08, +0x08,0x54,0x54,0x54,0x20, +0x04,0x3e,0x44,0x24,0x00, +0x3c,0x40,0x20,0x7c,0x00, +0x1c,0x20,0x40,0x20,0x1c, +0x3c,0x60,0x30,0x60,0x3c, +0x6c,0x10,0x10,0x6c,0x00, +0x9c,0xa0,0x60,0x3c,0x00, +0x64,0x54,0x54,0x4c,0x00, +0x08,0x3e,0x41,0x41,0x00, +0x00,0x00,0x77,0x00,0x00, +0x00,0x41,0x41,0x3e,0x08, +0x02,0x01,0x02,0x01,0x00, +0x3c,0x26,0x23,0x26,0x3c}; + +void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt)); + +// +// This function gets called when the current DMA transaction completes +// We use this to disable the chip select (CS) signal on the LCD +// and clear our volatile flag (DMA busy) +// +void DMA1_Channel3_IRQHandler(void) +{ + // why is this needed? Can't just direct compare the reg in tests below + volatile uint16_t intfr = DMA1->INTFR; + + if (intfr & DMA1_IT_TC3) { + DMA1->INTFCR = DMA1_IT_TC3; + DMA1_Channel3->CFGR &= ~DMA_CFGR1_EN; + if (u8CS != 0xff) + digitalWrite(u8CS, 1); // de-activate CS + bDMA = 0; // no longer active DMA transaction + } +} /* DMA1_Channel3_IRQHandler() */ + +// +// Initialize the DMA channel for SPI transmit (3) +// +void DMA_Tx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize) +{ + RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; // Enable DMA peripheral + //DMA1_Channel3 is for SPI + DMA1_Channel3->PADDR = ppadr; + DMA1_Channel3->MADDR = memadr; + DMA1_Channel3->CNTR = bufsize; + DMA1_Channel3->CFGR = + DMA_M2M_Disable | + DMA_Priority_VeryHigh | + DMA_MemoryDataSize_Byte | + DMA_PeripheralDataSize_Byte | + DMA_MemoryInc_Enable | + DMA_Mode_Normal | + DMA_DIR_PeripheralDST | + DMA_IT_TC; + + NVIC_EnableIRQ( DMA1_Channel3_IRQn ); + DMA1_Channel3->CFGR |= DMA_CFGR1_EN; +} /* DMA_Tx_Init() */ +// +// Faster way to write a 16-bit pattern into memory +// +void memset16(uint16_t *u16Dest, uint16_t u16Pattern, int iLen) +{ + uint32_t *u32D, u32; + u32 = u16Pattern | (u16Pattern << 16); + if (((uint32_t)u16Dest & 3) == 0) { + u32D = (uint32_t *)u16Dest; + while (iLen >= 2) { + *u32D++ = u32; + iLen -= 2; + } + u16Dest = (uint16_t *)u32D; + } + while (iLen) { + *u16Dest += u16Pattern; + iLen--; + } +} /* memset16() */ + +// +// Write a command byte to the LCD (D/C = LOW) +// +void lcdWriteCMD(uint8_t ucCMD) +{ + while (bDMA) {}; // wait for old transaction to complete + digitalWrite(u8DC, 0); + if (u8CS != 0xff) + digitalWrite(u8CS, 0); + SPI_write(&ucCMD, 1); + if (u8CS != 0xff) + digitalWrite(u8CS, 1); + digitalWrite(u8DC, 1); + +} /* lcdWriteCMD() */ +// +// Write a block of data to the LCD (D/C = high) +// Use DMA if the block is longer than a certain threshold +// +void lcdWriteDATA(uint8_t *pData, int iLen) +{ + uint8_t *p; + while (bDMA) {}; // wait for old transaction to complete + if (iLen >= 32) { // arbitrary cutoff point + if (u8CS != 0xff) + digitalWrite(u8CS, 0); // activate CS + DMA1_Channel3->CNTR = iLen; + DMA1_Channel3->MADDR = (uint32_t)pCache0; + + DMA1_Channel3->CFGR |= DMA_CFGR1_EN; // DMA_Cmd(DMA1_Channel3, ENABLE); // have DMA send the data + bDMA = 1; // tell our code that DMA is currently active for next time + // swap buffers + p = pCache0; + pCache0 = pCache1; + pCache1 = p; + } else { + if (u8CS != 0xff) + digitalWrite(u8CS, 0); + SPI_write(pData, iLen); + if (u8CS != 0xff) + digitalWrite(u8CS, 1); + } +} /* lcdWriteDATA() */ + +// +// Initialize the GPIOs needed to control the LCD +// and send the command sequence to configure the display +// +void lcdInit(int iLCDType, uint32_t u32Speed, uint8_t u8CSPin, uint8_t u8DCPin, uint8_t u8RSTPin, uint8_t u8BLPin) +{ +// uint8_t iBGR = 0; + uint8_t *s = NULL; + int iCount; + + if (iLCDType < 0 || iLCDType >= LCD_COUNT) return; + switch (iLCDType) { + case LCD_ST7789_135x240: + iNativeWidth = iLCDWidth = 240; // initialize in landscape mode + iNativeHeight = iLCDHeight = 135; + iNativeXOff = iLCDXOff = 40; + iNativeYOff = iLCDYOff = 53; + u8MADCTL = 0x60; + s = (unsigned char *)uc240x240InitList; + break; + case LCD_ST7789_172x320: + iNativeWidth = iLCDWidth = 320; // initialize in landscape mode + iNativeHeight = iLCDHeight = 172; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 34; + u8MADCTL = 0x60; + s = (unsigned char *)uc240x240InitList; + break; + case LCD_ST7789_240x280: + iNativeWidth = iLCDWidth = 280; // initialize in landscape mode + iNativeHeight = iLCDHeight = 240; + iNativeXOff = iLCDXOff = 20; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x60; + s = (unsigned char *)uc240x240InitList; + break; + case LCD_ST7735_80x160: + iNativeWidth = iLCDWidth = 160; // initialize in landscape mode + iNativeHeight = iLCDHeight = 80; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 24; + u8MADCTL = 0x68; + s = (unsigned char *)uc80InitList; + break; + case LCD_ILI9341_240x320: + iNativeWidth = iLCDWidth = 240; + iNativeHeight = iLCDHeight = 320; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x60; + s = (unsigned char *)ucILI9341InitList; + break; + case LCD_GC9107_128x128: + iNativeWidth = iLCDWidth = 128; + iNativeHeight = iLCDHeight = 128; + iNativeXOff = iLCDXOff = 1; + iNativeYOff = iLCDYOff = 2; + u8MADCTL = 0x68; + s = (unsigned char *)ucGC9107InitList; + break; + case LCD_ST7735_128x128: + iNativeWidth = iLCDWidth = 128; + iNativeHeight = iLCDHeight = 128; + iNativeXOff = iLCDXOff = 1; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x68; + s = (unsigned char *)uc128InitList; + break; + case LCD_ST7735_128x160: + iNativeWidth = iLCDWidth = 160; + iNativeHeight = iLCDHeight = 128; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x60; + s = (unsigned char *)uc160InitList; + break; + } // switch on LCD type + iLCDPitch = iLCDWidth*2; + u8CS = u8CSPin; + if (u8CSPin != 0xff) { + pinMode(u8CSPin, OUTPUT); + digitalWrite(u8CSPin, 1); + } + pinMode(u8RSTPin, OUTPUT); + digitalWrite(u8RSTPin, 0); // reset the display controller + Delay_Ms(100); + digitalWrite(u8RSTPin, 1); + Delay_Ms(200); + + SPI_begin(u32Speed, 0); + u8DC = u8DCPin; + pinMode(u8DCPin, OUTPUT); + u8BL = u8BLPin; + pinMode(u8BL, OUTPUT); + digitalWrite(u8BL, 1); // turn on backlight +// if (pLCD->iLCDFlags & FLAGS_SWAP_RB) +// iBGR = 8; + lcdWriteCMD(0x01); // SW reset + Delay_Ms(200); + lcdWriteCMD(0x11); // sleep out + Delay_Ms(100); + iCount = 1; + while (s && iCount) + { + iCount = *s++; + if (iCount != 0) + { + lcdWriteCMD(s[0]); + lcdWriteDATA(&s[1], iCount-1); + s += iCount; + } // if count + }// while + DMA_Tx_Init(DMA1_Channel3, (u32)&SPI1->DATAR, (u32)pCache0, 0); + +} /* lcdInit() */ + +// +// Sitronix LCDs support 4 possible orientations through use of the +// X, Y and V mirror/flip bits +// V swaps the X/Y axis +// +void lcdOrientation(int iOrientation) +{ + uint8_t u8 = u8MADCTL; // original value + + switch (iOrientation) { + case ORIENTATION_0: // use original MADCTL value + iLCDWidth = iNativeWidth; + iLCDHeight = iNativeHeight; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeXOff; + iLCDYOff = iNativeYOff; + break; + case ORIENTATION_90: + u8 ^= MADCTL_XFLIP; + u8 ^= MADCTL_VFLIP; + iLCDWidth = iNativeHeight; + iLCDHeight = iNativeWidth; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeYOff; + iLCDYOff = iNativeXOff; + break; + case ORIENTATION_180: + u8 ^= MADCTL_XFLIP; + u8 ^= MADCTL_YFLIP; + iLCDWidth = iNativeWidth; + iLCDHeight = iNativeHeight; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeXOff; + iLCDYOff = iNativeYOff; + break; + case ORIENTATION_270: + u8 ^= MADCTL_YFLIP; + u8 ^= MADCTL_VFLIP; + iLCDWidth = iNativeHeight; + iLCDHeight = iNativeWidth; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeYOff; + iLCDYOff = iNativeXOff; + break; + } + lcdWriteCMD(0x36); // MADCTL + lcdWriteDATA(&u8, 1); +} /* lcdOrientation() */ + +// +// Set the memory window (AKA write position) +// +void lcdSetPosition(int x, int y, int w, int h) +{ +uint8_t ucBuf[8]; + + x += iLCDXOff; + y += iLCDYOff; + ucBuf[0] = (unsigned char)(x >> 8); + ucBuf[1] = (unsigned char)x; + x = x + w - 1; + ucBuf[2] = (unsigned char)(x >> 8); + ucBuf[3] = (unsigned char)x; + lcdWriteCMD(0x2a); // column address cmd + lcdWriteDATA(ucBuf, 4); + ucBuf[0] = (unsigned char)(y >> 8); + ucBuf[1] = (unsigned char)y; + y = y + h - 1; + ucBuf[2] = (unsigned char)(y >> 8); + ucBuf[3] = (unsigned char)y; + lcdWriteCMD(0x2b); // row address cmd + lcdWriteDATA(ucBuf, 4); + lcdWriteCMD(0x2c); // RAMWR - start writing +} /* lcdSetPosition() */ + +// +// For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction +// This function draws a single pixel and scales its position based on the x/y fraction of the ellipse +// +void DrawScaledPixel(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor) +{ + if (iXFrac != 0x10000) x = (x * iXFrac) >> 16; + if (iYFrac != 0x10000) y = (y * iYFrac) >> 16; + x += iCX; y += iCY; + if (x < 0 || x >= iLCDWidth || y < 0 || y >= iLCDHeight) + return; // off the screen + lcdSetPosition(x, y, 1, 1); + lcdWriteDATA((uint8_t *)&usColor, 2); +} /* DrawScaledPixel() */ +// +// Draw the given x/y symmetrically across the center point +// as a continuous line +// +void DrawScaledLine(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor) +{ + int32_t iLen, x2; + if (iXFrac != 0x10000) x = (x * iXFrac) >> 16; + if (iYFrac != 0x10000) y = (y * iYFrac) >> 16; + x2 = iCX + x; + x = iCX - x; + y += iCY; + if (x < 0) x = 0; + if (x2 >= iLCDWidth) x2 = iLCDWidth-1; + iLen = x2 - x + 1; // new length + lcdSetPosition(x, y, iLen, 1); + memset16((uint16_t *)pCache0, usColor, iLen); + lcdWriteDATA(pCache0, iLen*2); +} /* DrawScaledLine() */ +// +// Draw the 8 pixels around the Bresenham circle +// (scaled to make an ellipse) +// +void BresenhamCircle(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor, int bFill) +{ + if (bFill) // draw a filled ellipse + { + // for a filled ellipse, draw 4 lines instead of 8 pixels + DrawScaledLine(iCX, iCY, y, x, iXFrac, iYFrac, usColor); + DrawScaledLine(iCX, iCY, y, -x, iXFrac, iYFrac, usColor); + DrawScaledLine(iCX, iCY, x, y, iXFrac, iYFrac, usColor); + DrawScaledLine(iCX, iCY, x, -y, iXFrac, iYFrac, usColor); + } + else // draw 8 pixels around the edges + { + DrawScaledPixel(iCX, iCY, x, y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -x, y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, x, -y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -x, -y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, y, x, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -y, x, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, y, -x, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -y, -x, iXFrac, iYFrac, usColor); + } +} /* BresenhamCircle() */ +// +// Draw an ellipse (a circle if Rx==Ry) +// +void lcdEllipse(int iCenterX, int iCenterY, int iRadiusX, int iRadiusY, uint16_t usColor, int bFill) +{ + int32_t iRadius, iXFrac, iYFrac; + int32_t iDelta, x, y; + + if (iRadiusX > iRadiusY) // use X as the primary radius + { + iRadius = iRadiusX; + iXFrac = 65536; + iYFrac = (iRadiusY * 65536) / iRadiusX; + } + else + { + iRadius = iRadiusY; + iXFrac = (iRadiusX * 65536) / iRadiusY; + iYFrac = 65536; + } + usColor = __builtin_bswap16(usColor); // swap byte order + iDelta = 3 - (2 * iRadius); + x = 0; y = iRadius; + while (x < y) + { + BresenhamCircle(iCenterX, iCenterY, x, y, iXFrac, iYFrac, usColor, bFill); + x++; + if (iDelta < 0) + { + iDelta += (4*x) + 6; + } + else + { + iDelta += 4 * (x-y) + 10; + y--; + } + } +} /* lcdEllipse() */ +// +// Draw an outline or filled rectangle +// requesting a rectangle which draws past an edge will result it in being clipped to the edge +// +void lcdRectangle(int x, int y, int cx, int cy, uint16_t usColor, int bFill) +{ + int ty; + if (x >= iLCDWidth || y >= iLCDHeight) return; // not visible + if (x < 0) { + cx += x; + x = 0; + } else if (x + cx > iLCDWidth) cx = iLCDWidth - cx; + if (y < 0) { + cy += y; + y = 0; + } else if (y + cy > iLCDHeight) cy = iLCDHeight - cy; + usColor = __builtin_bswap16(usColor); + if (bFill) { // draw a filled rectangle + lcdSetPosition(x, y, cx, cy); + for (ty = 0; ty < cy; ty++) { + if (ty < 2) memset16((uint16_t *)pCache0, usColor, cx); // need to do it 2 times for buffers + lcdWriteDATA(pCache0, cx*2); + } // for ty + } else { // outline rectangle + // each side is drawn as a straight line + lcdSetPosition(x, y, cx, 1); // top line + memset16((uint16_t *)pCache0, usColor, cx); + lcdWriteDATA(pCache0, cx*2); + lcdSetPosition(x, y+cy-1, cx, 1); // bottom line + memset16((uint16_t *)pCache0, usColor, cx); + lcdWriteDATA(pCache0, cx*2); + lcdSetPosition(x, y, 1, cy); // left line + memset16((uint16_t *)pCache0, usColor, cy); + lcdWriteDATA(pCache0, cy*2); + lcdSetPosition(x+cx-1, y, 1, cy); // right line + memset16((uint16_t *)pCache0, usColor, cy); + lcdWriteDATA(pCache0, cy*2); + } +} /* lcdRectangle() */ +// +// Draw a NxN RGB565 tile +// This reverses the pixel byte order and sets a memory "window" +// of pixels so that the write can occur in one shot +// +int lcdDrawTile(int x, int y, int iTileWidth, int iTileHeight, unsigned char *pTile, int iPitch) +{ + int i, j; + uint16_t *s16, *d16; + + if (iTileWidth*iTileHeight*2 > CACHE_SIZE) { + return -1; // tile must fit in SPI cache + } + // First convert to big-endian order + d16 = (uint16_t *)pCache0; + for (j=0; j<iTileHeight; j++) + { + s16 = (uint16_t*)&pTile[j*iPitch]; + for (i=0; i<iTileWidth; i++) + { + *d16++ = __builtin_bswap16(*s16++); + } // for i; + } // for j + lcdSetPosition(x, y, iTileWidth, iTileHeight); + lcdWriteDATA(pCache0, iTileWidth*iTileHeight*2); + return 0; +} /* lcdDrawTile() */ +// +// Fill the display with a solid color +// +void lcdFill(uint16_t usData) +{ + int cx, cy; + uint16_t *d; + + usData = (usData >> 8) | (usData << 8); // swap hi/lo byte for LCD + lcdSetPosition(0,0, iLCDWidth, iLCDHeight); + // fit within our temp buffer + for (cy = 0; cy < iLCDHeight; cy++) { + if (cy < 2) { // both buffers need a copy + d = (uint16_t *)pCache0; // pointer swapped after each write + for (cx = 0; cx < iLCDWidth; cx++) { + d[cx] = usData; + } + } + lcdWriteDATA(pCache0, iLCDWidth*2); // fill with data words + } // for y + +} /* lcdFill() */ + +// +// Draw a 1-bpp pattern with the given color and translucency +// 1 bits are drawn as color, 0 are transparent +// The translucency value can range from 1 (barely visible) to 32 (fully opaque) +// If there is a backbuffer, the bitmap is draw only into memory +// If there is no backbuffer, the bitmap is drawn on the screen with a black background color +// +void spilcdDrawPattern(uint8_t *pPattern, int iSrcPitch, int iDestX, int iDestY, int iCX, int iCY, uint16_t usColor) +{ + int x, y; + uint8_t *s, uc, ucMask; + uint16_t *d, u16Clr; + + if (iDestX+iCX > iLCDWidth) // trim to fit on display + iCX = (iLCDWidth - iDestX); + if (iDestY+iCY > iLCDHeight) + iCY = (iLCDHeight - iDestY); + if (pPattern == NULL || iDestX < 0 || iDestY < 0 || iCX <=0 || iCY <= 0) + return; + u16Clr = (usColor >> 8) | (usColor << 8); // swap low/high bytes + lcdSetPosition(iDestX, iDestY, iCX, iCY); + for (y=0; y<iCY; y++) + { + s = &pPattern[y * iSrcPitch]; + ucMask = uc = 0; + d = (uint16_t *)pCache0; + for (x=0; x<iCX; x++) + { + ucMask >>= 1; + if (ucMask == 0) + { + ucMask = 0x80; + uc = *s++; + } + if (uc & ucMask) // active pixel + *d++ = u16Clr; + else + *d++ = 0; + } // for x + lcdWriteDATA(pCache0, iCX*2); + } // for y +} /* spilcdDrawPattern() */ + +// +// Draw a string of text with the built-in fonts +// +#if (CACHE_SIZE < 4096) +// This slower version uses one DMA transaction per character +int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize) +{ +int i, j, k, iLen; +unsigned char *s; +unsigned short usFG = (usFGColor >> 8) | (usFGColor << 8); +unsigned short usBG = (usBGColor >> 8) | (usBGColor << 8); + + if (x == -1) + x = iCursorX; + if (y == -1) + y = iCursorY; + if (x < 0 || y < 0) return -1; + iLen = strlen(szMsg); + if (iFontSize == FONT_8x8 || iFontSize == FONT_6x8) // draw the 6x8 or 8x8 font + { + uint16_t *usD; + int cx; + uint8_t *pFont; + + cx = (iFontSize == FONT_8x8) ? 8:6; + pFont = (iFontSize == FONT_8x8) ? (uint8_t *)ucFont : (uint8_t *)ucSmallFont; + if ((cx*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/cx; // can't display it all + if (iLen < 0)return -1; + + for (i=0; i<iLen; i++) + { + s = &pFont[((unsigned char)szMsg[i]-32) * (cx-1)]; + usD = (uint16_t *)pCache0; + lcdSetPosition(x+(i*cx), y, cx, 8); + uint8_t ucMask = 1; + for (k=0; k<8; k++) // for each scanline + { + for (j=0; j<cx-1; j++) + { + if (s[j] & ucMask) + *usD++ = usFG; + else + *usD++ = usBG; + } // for j + *usD++ = usBG; // last column is blank + ucMask <<= 1; + } // for k + // write the data in one shot + lcdWriteDATA(pCache0, cx*2*8); + } // for each character + x += (i*cx); + } // 6x8 and 8x8 + if (iFontSize == FONT_12x16) // 6x8 stretched to 12x16 (with smoothing) + { + uint16_t *usD; + + if ((12*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/12; // can't display it all + if (iLen < 0) return -1; + + for (i=0; i<iLen; i++) + { + s = (uint8_t *)&ucSmallFont[((unsigned char)szMsg[i]-32) * 5]; + usD = (uint16_t *)pCache0;; + lcdSetPosition(x+(i*12), y, 12, 16); + uint8_t ucMask = 1; + for (k=0; k<12*16; k++) + usD[k] = usBG; + for (k=0; k<8; k++) // for each scanline + { + uint8_t c0, c1; + for (j=0; j<5; j++) + { + c0 = s[j]; + if (c0 & ucMask) + usD[0] = usD[1] = usD[12] = usD[13] = usFG; + // test for smoothing diagonals + if (k < 7 && j < 5) { + uint8_t ucMask2 = ucMask << 1; + c1 = s[j+1]; + if ((c0 & ucMask) && (~c1 & ucMask) && (~c0 & ucMask2) && (c1 & ucMask2)) // first diagonal condition + usD[14] = usD[25] = usFG; + else if ((~c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask2) && (~c1 & ucMask2)) + usD[13] = usD[26] = usFG; + } // if not on last row and last col + usD+=2; + } // for j + usD[0] = usD[1] = usD[12] = usD[13] = usBG; // last column is blank + usD += 2; + usD += 12; // skip the extra line + ucMask <<= 1; + } // for k + // write the data in one shot + lcdWriteDATA(pCache0, 12*16*2); + } // for each character + x += i*12; + } // FONT_12x16 + iCursorX = x; + iCursorY = y; + return 0; +} /* lcdWriteString() */ +#else +// This faster version uses one DMA transaction per line of text +int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize) +{ +int i, j, k, iLen; +int iStride; +uint8_t *s; +uint16_t usFG = (usFGColor >> 8) | ((usFGColor & -1)<< 8); +uint16_t usBG = (usBGColor >> 8) | ((usBGColor & -1)<< 8); +uint16_t *usD; +int cx; +uint8_t *pFont; + + if (iFontSize < 0 || iFontSize >= FONT_COUNT) + return -1; // invalid size + if (x == -1) + x = iCursorX; + if (y == -1) + y = iCursorY; + if (x < 0) return -1; + iLen = strlen(szMsg); + + if (iFontSize == FONT_12x16) { + if ((12*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/12; // can't display it all + if (iLen < 0) return -1; + iStride = iLen*12; + lcdSetPosition(x, y, iStride, 16); + usD = (uint16_t *)pCache0; + for (i=0; i<iStride*16; i++) + usD[i] = usBG; // set to background color first + for (k = 0; k<8; k++) { // create a pair of scanlines from each original + uint8_t ucMask = (1 << k); + usD = (unsigned short *)&pCache0[k*iStride*4]; + for (i=0; i<iLen; i++) + { + uint8_t c0, c1; + s = (uint8_t *)&ucSmallFont[((unsigned char)szMsg[i]-32) * 5]; + for (j=1; j<6; j++) + { + uint8_t ucMask1 = ucMask << 1; + uint8_t ucMask2 = ucMask >> 1; + c0 = s[j-1]; + if (c0 & ucMask) + usD[0] = usD[1] = usD[iStride] = usD[iStride+1] = usFG; + // test for smoothing diagonals + if (j < 5) { + c1 = s[j]; + if ((c0 & ucMask) && (~c1 & ucMask) && (~c0 & ucMask1) && (c1 & ucMask1)) { // first diagonal condition + usD[iStride+2] = usFG; + } else if ((~c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask1) && (~c1 & ucMask1)) { // second condition + usD[iStride+1] = usFG; + } + if ((c0 & ucMask2) && (~c1 & ucMask2) && (~c0 & ucMask) && (c1 & ucMask)) { // repeat for previous line + usD[1] = usFG; + } else if ((~c0 & ucMask2) && (c1 & ucMask2) && (c0 & ucMask) && (~c1 & ucMask)) { + usD[2] = usFG; + } + } + usD+=2; + } // for j + usD += 2; // leave "6th" column blank + } // for each character + } // for each scanline + lcdWriteDATA(pCache0, iStride*32); + return 0; + } // 12x16 + + cx = (iFontSize == FONT_8x8) ? 8:6; + pFont = (iFontSize == FONT_8x8) ? (uint8_t *)ucFont : (uint8_t *)ucSmallFont; + if ((cx*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/cx; // can't display it all + iStride = iLen * cx*2; + for (i=0; i<iLen; i++) + { + s = &pFont[((unsigned char)szMsg[i]-32) * (cx-1)]; + uint8_t ucMask = 1; + for (k=0; k<8; k++) // for each scanline + { + usD = (unsigned short *)&pCache0[(k*iStride) + (i * cx*2)]; + for (j=0; j<cx-1; j++) + { + if (s[j] & ucMask) + *usD++ = usFG; + else + *usD++ = usBG; + } // for j + *usD++ = usBG; // blank column + ucMask <<= 1; + } // for k + } // for i + // write the data in one shot + lcdSetPosition(x, y, cx*iLen, 8); + lcdWriteDATA(pCache0, iLen*cx*16); + iCursorX = x + (cx*iLen); + iCursorY = y; + return 0; +} /* lcdWriteString() */ +#endif // cache size +// +// Draw a string in a proportional font you supply +// +int lcdWriteStringCustom(GFXfont *pFont, int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int bBlank) +{ +int i, /*j, iLen, */ k, dx, dy, cx, cy, c, iBitOff; +int tx, ty; +uint8_t *s, bits, uc; +GFXfont font; +GFXglyph glyph, *pGlyph; +#define TEMP_BUF_SIZE 64 +#define TEMP_HIGHWATER (TEMP_BUF_SIZE-8) +uint16_t *d; + + if (pFont == NULL) + return -1; + if (x == -1) + x = iCursorX; + if (y == -1) + y = iCursorY; + if (x < 0) + return -1; + // in case of running on AVR, get copy of data from FLASH + memcpy(&font, pFont, sizeof(font)); + pGlyph = &glyph; + usFGColor = (usFGColor >> 8) | (usFGColor << 8); // swap h/l bytes + usBGColor = (usBGColor >> 8) | (usBGColor << 8); + + i = 0; + while (szMsg[i] && x < iLCDWidth) + { + c = szMsg[i++]; + if (c < font.first || c > font.last) // undefined character + continue; // skip it + c -= font.first; // first char of font defined + memcpy_P(&glyph, &font.glyph[c], sizeof(glyph)); + // set up the destination window (rectangle) on the display + dx = x + pGlyph->xOffset; // offset from character UL to start drawing + dy = y + pGlyph->yOffset; + cx = pGlyph->width; + cy = pGlyph->height; + iBitOff = 0; // bitmap offset (in bits) + if (dy + cy > iLCDHeight) + cy = iLCDHeight - dy; // clip bottom edge + else if (dy < 0) { + cy += dy; + iBitOff += (pGlyph->width * (-dy)); + dy = 0; + } + if (dx + cx > iLCDWidth) + cx = iLCDWidth - dx; // clip right edge + s = font.bitmap + pGlyph->bitmapOffset; // start of bitmap data + // Bitmap drawing loop. Image is MSB first and each pixel is packed next + // to the next (continuing on to the next character line) + bits = uc = 0; // bits left in this font byte + + if (bBlank) { // erase the areas around the char to not leave old bits + int miny, maxy; + c = '0' - font.first; + miny = y + pGlyph->yOffset; + c = 'y' - font.first; + maxy = miny + pGlyph->height; + if (maxy > iLCDHeight) + maxy = iLCDHeight; + cx = pGlyph->xAdvance; + if (cx + x > iLCDWidth) { + cx = iLCDWidth - x; + } + lcdSetPosition(x, miny, cx, maxy-miny); + // blank out area above character +// cy = font.yAdvance - pGlyph->height; +// for (ty=miny; ty<miny+cy && ty < maxy; ty++) { +// for (tx=0; tx<cx; tx++) +// u16Temp[tx] = usBGColor; +// myspiWrite(pLCD, (uint8_t *)u16Temp, cx*sizeof(uint16_t), MODE_DATA, iFlags); +// } // for ty + // character area (with possible padding on L+R) + for (ty=0; ty<pGlyph->height && ty+miny < maxy; ty++) { + d = (uint16_t *)pCache0; + for (tx=0; tx<pGlyph->xOffset && tx < cx; tx++) { // left padding + *d++ = usBGColor; + } + // character bitmap (center area) + for (tx=0; tx<pGlyph->width; tx++) { + if (bits == 0) { // need more data + uc = s[iBitOff>>3]; + bits = 8; + iBitOff += bits; + } + if (tx + pGlyph->xOffset < cx) { + *d++ = (uc & 0x80) ? usFGColor : usBGColor; + } + bits--; + uc <<= 1; + } // for tx + // right padding + k = pGlyph->xAdvance - (int)(d - (uint16_t*)pCache0); // remaining amount + for (tx=0; tx<k && (tx+pGlyph->xOffset+pGlyph->width) < cx; tx++) + *d++ = usBGColor; + lcdWriteDATA(pCache0, cx*sizeof(uint16_t)); + } // for ty + // padding below the current character + ty = y + pGlyph->yOffset + pGlyph->height; + for (; ty < maxy; ty++) { + d = (uint16_t *)pCache0; + for (tx=0; tx<cx; tx++) + d[tx] = usBGColor; + lcdWriteDATA(pCache0, cx*sizeof(uint16_t)); + } // for ty + } else if (usFGColor == usBGColor) { // transparent + int iCount; // opaque pixel count + d = (uint16_t*)pCache0; + for (iCount=0; iCount < cx; iCount++) + d[iCount] = usFGColor; // set up a line of solid color + iCount = 0; // number of sequential opaque pixels + for (ty=0; ty<cy; ty++) { + for (tx=0; tx<pGlyph->width; tx++) { + if (bits == 0) { // need to read more font data + uc = s[iBitOff>>3]; // get more font bitmap data + bits = 8 - (iBitOff & 7); // we might not be on a byte boundary + iBitOff += bits; // because of a clipped line + uc <<= (8-bits); + } // if we ran out of bits + if (tx < cx) { + if (uc & 0x80) { + iCount++; // one more opaque pixel + } else { // any opaque pixels to write? + if (iCount) { + lcdSetPosition(dx+tx-iCount, dy+ty, iCount, 1); + d = (uint16_t *)pCache0; // point to start of output buffer + lcdWriteDATA(pCache0, iCount*sizeof(uint16_t)); + iCount = 0; + } // if opaque pixels to write + } // if transparent pixel hit + } + bits--; // next bit + uc <<= 1; + } // for tx + } // for ty + // quicker drawing + } else { // just draw the current character box fast + lcdSetPosition(dx, dy, cx, cy); + d = (uint16_t *)pCache0; // point to start of output buffer + for (ty=0; ty<cy; ty++) { + for (tx=0; tx<pGlyph->width; tx++) { + if (bits == 0) { // need to read more font data + uc = s[iBitOff>>3]; // get more font bitmap data + bits = 8 - (iBitOff & 7); // we might not be on a byte boundary + iBitOff += bits; // because of a clipped line + uc <<= (8-bits); + k = (int)(d-(uint16_t*)pCache0); // number of words in output buffer + if (k >= TEMP_HIGHWATER) { // time to write it + lcdWriteDATA(pCache0, k*sizeof(uint16_t)); + d = (uint16_t*)pCache0; + } + } // if we ran out of bits + if (tx < cx) { + *d++ = (uc & 0x80) ? usFGColor : usBGColor; + } + bits--; // next bit + uc <<= 1; + } // for tx + } // for ty + k = (int)(d-(uint16_t*)pCache0); + if (k) // write any remaining data + lcdWriteDATA(pCache0, k*sizeof(uint16_t)); + } // quicker drawing + x += pGlyph->xAdvance; // width of this character + } // while drawing characters + iCursorX = x; + iCursorY = y; + return 0; +} /* lcdWriteStringCustom() */ +// end of spi_lcd.inl