/*
* Author: Pawel Jablonski
* E-mail: pj@xirx.net
* WWW: xirx.net
* GIT: git.xirx.net
*
* License: You can use this code however you like
* but leave information about the original author.
* Code is free for non-commercial and commercial use.
*/
#include <stm32f0xx.h>
#include <stm32f0xx_hal.h>
#include <stm32f0xx_hal_gpio.h>
#include <stm32f0xx_hal_rcc.h>
#include <system_stm32f0xx.h>
#include "type.h"
#include "font.h"
#include "lcd.h"
//! LCD buffer type list
enum LCDBufferID
{
LCD_BUFFER_WORK = 0, //!< Work buffer
LCD_BUFFER_READY, //!< Ready to display buffer
LCD_BUFFER_DISPLAY, //!< Display buffer used to generate an image on the LCD
LCD_BUFFER_QUANTITY //!< Quantity of the buffers
};
static volatile bool refreshRequest = FALSE; //!< Status of refresh request
static unsigned char charBuffer[LCD_BUFFER_QUANTITY][LCD_HEIGHT][LCD_WIDTH]; //!< Char buffers
static unsigned char colorBuffer[LCD_BUFFER_QUANTITY][LCD_HEIGHT][LCD_WIDTH]; //!< Background and foreground color buffers
static void lcdCopyWorkBuffer(void);
static void lcdCopyReadyBuffer(void);
static void lcdDraw(void);
static void lcdDrawLine(int lineNumber);
//! Init LCD and set up buffers and output pins
void lcdInit(void)
{
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef gpioInit = {0};
gpioInit.Pin = (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8);
gpioInit.Mode = GPIO_MODE_OUTPUT_PP;
gpioInit.Pull = GPIO_NOPULL;
gpioInit.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &gpioInit);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(10);
lcdClear();
lcdRefresh();
}
//! Copy data from the canvas painting buffer to the ready to show buffer
void lcdRefresh(void)
{
lcdCopyWorkBuffer();
__disable_irq();
refreshRequest = TRUE;
__enable_irq();
}
//! Clear the canvas painting buffer
void lcdClear(void)
{
for(int i = 0; i < LCD_HEIGHT; i++)
{
for(int j = 0; j < LCD_WIDTH; j++)
{
charBuffer[LCD_BUFFER_WORK][i][j] = 0;
colorBuffer[LCD_BUFFER_WORK][i][j] = 0;
}
}
}
/**
* Set char in the buffer
*
* @param c Char to set
* @param x Horizontal position in range 0-WIDTH
* @param y Vertical position in range 0-HEIGHT
* @param charColor Foreground color code in range 0-15
* @param backgroundColor Background color code in range 0-15
*/
void lcdSetChar(unsigned char c, unsigned int x, unsigned int y, unsigned char charColor, unsigned char backgroundColor)
{
if((x < LCD_WIDTH) && (y < LCD_HEIGHT))
{
charBuffer[LCD_BUFFER_WORK][y][x] = c;
colorBuffer[LCD_BUFFER_WORK][y][x] = ((charColor & 0x0f) | (backgroundColor << 4));
}
}
//! Scroll the canvas painting buffer one line up
void lcdScroll(void)
{
for(int y = 0; y < (LCD_HEIGHT - 1); y++)
{
for(int x = 0; x < LCD_WIDTH; x++)
{
charBuffer[LCD_BUFFER_WORK][y][x] = charBuffer[LCD_BUFFER_WORK][y + 1][x];
colorBuffer[LCD_BUFFER_WORK][y][x] = colorBuffer[LCD_BUFFER_WORK][y + 1][x];
}
}
for(int x = 0; x < LCD_WIDTH; x++)
{
charBuffer[LCD_BUFFER_WORK][LCD_HEIGHT - 1][x] = 0;
colorBuffer[LCD_BUFFER_WORK][LCD_HEIGHT - 1][x] = 0;
}
}
//! Infinity draw loop on the LCD. It works at background, and it refreshes the LCD when no other operation is in progress.
void lcdDrawLoop(void)
{
while(1)
{
if(refreshRequest)
{
__disable_irq();
{
lcdCopyReadyBuffer();
refreshRequest = FALSE;
}
__enable_irq();
lcdDraw();
}
}
}
//! Copy data from the work buffer to the ready buffer
static void lcdCopyWorkBuffer(void)
{
for(int i = 0; i < LCD_HEIGHT; i++)
{
for(int j = 0; j < LCD_WIDTH; j++)
{
charBuffer[LCD_BUFFER_READY][i][j] = charBuffer[LCD_BUFFER_WORK][i][j];
colorBuffer[LCD_BUFFER_READY][i][j] = colorBuffer[LCD_BUFFER_WORK][i][j];
}
}
}
//! Copy data from the ready buffer to the display buffer
static void lcdCopyReadyBuffer(void)
{
for(int i = 0; i < LCD_HEIGHT; i++)
{
for(int j = 0; j < LCD_WIDTH; j++)
{
charBuffer[LCD_BUFFER_DISPLAY][i][j] = charBuffer[LCD_BUFFER_READY][i][j];
colorBuffer[LCD_BUFFER_DISPLAY][i][j] = colorBuffer[LCD_BUFFER_READY][i][j];
}
}
}
//! Draw a whole buffer on the LCD
static void lcdDraw(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);
for(int i = 0; i < 4; i++)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
}
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
for(int i = 0; i < LCD_HEIGHT; i++)
{
lcdDrawLine(i);
}
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
}
/**
* Draw a one line on the LCD
*
* @param lineNumber Row number to draw on the LCD
*/
static void lcdDrawLine(int lineNumber)
{
for(int i = 0; i < (LCD_CHAR_HEIGHT * 2); i++)
{
int row = lineNumber;
int fontRow = (i / 2);
for(int column = 0; column < LCD_WIDTH; column++)
{
unsigned char c = charBuffer[LCD_BUFFER_DISPLAY][row][column];
unsigned char b = font[c][fontRow];
unsigned char colorChar = (colorBuffer[LCD_BUFFER_DISPLAY][row][column] & 0x0f);
unsigned char colorBackground = ((colorBuffer[LCD_BUFFER_DISPLAY][row][column] >> 4) & 0x0f);
for(int k = 0; k < LCD_CHAR_WIDTH; k++)
{
if(b & 0x01)
{
GPIOC->ODR = ((GPIOC->ODR & 0xff00) | 0xa0 | colorChar);
GPIOC->BSRR = 0x40;
GPIOC->BRR = 0x40;
GPIOC->BSRR = 0x40;
}
else
{
GPIOC->ODR = ((GPIOC->ODR & 0xff00) | 0xa0 | colorBackground);
GPIOC->BSRR = 0x40;
GPIOC->BRR = 0x40;
GPIOC->BSRR = 0x40;
}
b >>= 1;
}
}
}
}