/*************************************
LCD driver(Nokia 5110)
Ver 0.02 2024/06/14 by skyriver
Ver 0.03 2024/07/27 faster LcdLine
*************************************/
#include "debug.h"
#include <stdint.h>
#include "stdlib.h"
#include "LcdNokia.h"
#define LCDTIMER 1000 // write SPI timer counter
typedef enum { DATA, CMD } SpiSortTyp;
static SpiSortTyp PrevSort; // spi send previous mode(cmd or data)
static uint8_t LcdVram[ LCD_MAX_X * LCD_MAX_LN ]; // LCD VRAM image
static uint16_t LcdXPos, LcdYPos; // text write point
static uint16_t LcdTxtX, LcdTxtY; // graphic write point
// initialize GPIO and SPI
void InitLcdSpi( void )
{
GPIO_InitTypeDef GPIO_InitStructure={0};
SPI_InitTypeDef SPI_InitStructure={0};
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );
// RCC_APB2PeriphClockCmd( CONCATM(RCC_APB2Periph_, LCD_PORT) | RCC_APB2Periph_SPI1, ENABLE );
GPIO_InitStructure.GPIO_Pin = LCD_SPICK | LCD_SPIMOSI;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( LCD_PORT, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = LCD_RST | LCD_CS | LCD_CMD | LCD_LED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( LCD_PORT, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = LCD_SPIMISO; // not use
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( LCD_PORT, &GPIO_InitStructure );
GPIO_SetBits(LCD_PORT, LCD_CS );
GPIO_ResetBits(LCD_PORT, LCD_RST| LCD_LED );
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
// SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//8bit mode0 msbfirst
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
// SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // 16:500kHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
// SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init( LCD_SPI, &SPI_InitStructure );
SPI_Cmd( LCD_SPI, ENABLE );
}
// set LCD's SPI CS after send complete
void LcdCsOn( void )
{
while( LCD_SPIWRBSY() == SET ) {}
LCD_BRESET( LCD_CS );
}
// reset LCD's SPI CS after send complete
void LcdCsOff( void )
{
while( LCD_SPIWRBSY() == SET ) {}
LCD_BSET( LCD_CS );
}
// write cmd to LCD
// dat <- write data
// return -> nonzero:OK, else timeover
uint16_t LcdWrCmd( uint8_t dat )
{
uint16_t cnt = LCDTIMER;
while( --cnt ) {
if( LCD_SPIWRSTS() == SET ) { // can write
if( PrevSort != CMD ) {
PrevSort = CMD;
while( LCD_SPIWRBSY() == SET ) {}
LCD_BRESET( LCD_CMD );
}
LCD_SPIWR( dat );
break;
}
}
if( cnt == 0 ) {
printf( "\nLcd timeover" );
}
return( cnt );
}
// write data to LCD
// dat <- write data
// return -> nonzero:OK, else timeover
uint16_t LcdWrData( uint8_t dat )
{
uint16_t cnt = LCDTIMER;
while( --cnt ) {
if( LCD_SPIWRSTS() == SET ) { // can write
if( PrevSort != DATA ) {
PrevSort = DATA;
while( LCD_SPIWRBSY() == SET ) {}
LCD_BSET( LCD_CMD );
}
LCD_SPIWR( dat );
break;
}
}
if( cnt == 0 ) {
printf( "\nLcd timeover" );
}
return( cnt );
}
void InitLcd( void )
{
InitLcdSpi();
Delay_Ms(100);
// LCD_BRESET( LCD_RST );
LCD_BSET( LCD_RST );
LcdCsOn();
LcdWrCmd(0b00100000 | 1 ); // Function set : H = 1
LcdWrCmd(0b00000100 | 1 ); // Temparature control : TC=1(0-3)
LcdWrCmd(0b00010000 | 3 ); // Bias system : Bais=3(0-7)
LcdWrCmd(0b10000000 | LCD_CONTRAST ); // Set Vop : contrast(0-127)
LcdWrCmd(0b00100000 | 0 ); // Function set : H=0
LcdWrCmd(0b00001000 | 4 ); // Display control : normal(D:1,E:0)
LcdWrCmd(0b01000000 | 0 ); // Set Y addr : 0(0..5)( x 8 dot)
LcdWrCmd(0b10000000 | 0 ); // Set X addr : 0(0..83)
LcdCsOff();
}
// set write point
// x,y <- point addr x:0..83, y:0..5
void LcdLocate( uint8_t x, uint8_t y )
{
LcdTxtX = x;
LcdTxtY = y;
LcdCsOn();
LcdWrCmd(0b01000000 | y );
LcdWrCmd(0b10000000 | x );
LcdCsOff();
}
// write datas
// dpnt <- data addr
// leng <- how many data
void LcdWrDatas( uint8_t *dpnt, uint16_t len )
{
uint8_t *vpnt = LcdVram + LcdTxtY * LCD_MAX_X + LcdTxtX;
LcdTxtX += len;
if( LcdTxtX >= LCD_MAX_X ) {
LcdTxtY += LcdTxtX / LCD_MAX_X;
LcdTxtX = LcdTxtX % LCD_MAX_X;
}
LcdCsOn();
while( len-- ) {
*vpnt |= *dpnt++;
LcdWrData( *vpnt++ );
}
LcdCsOff();
}
// write same data
// data <- write data
// leng <- how many data
void LcdWrFill( uint8_t data, uint16_t len )
{
uint8_t *vpnt = LcdVram + LcdTxtY * LCD_MAX_X + LcdTxtX;
LcdTxtX += len;
if( LcdTxtX >= LCD_MAX_X ) {
LcdTxtY += LcdTxtX / LCD_MAX_X;
LcdTxtX = LcdTxtX % LCD_MAX_X;
}
LcdCsOn();
while( len-- ) {
LcdWrData( *vpnt++ = data );
}
LcdCsOff();
}
void LcdCls( void )
{
LcdLocate( 0, 0 );
LcdWrFill( 0, LCD_MAX_X * LCD_MAX_LN );
}
void LcdSetLed( uint8_t led )
{
if( led ) {
LCD_BRESET( LCD_LED );
} else {
LCD_BSET( LCD_LED );
}
}
void LcdSetCont( uint8_t val )
{
LcdCsOn();
LcdWrCmd(0b10000000 | val ); // contrast(0-127)
LcdCsOff();
}
// write font data
// data <- char code
void LcdPutc( uint8_t data )
{
LcdWrDatas( &Font4x5[ (data - 0x20) * FONTSIZX ], FONTSIZX );
}
// set point
// (x,y) <- position
void LcdSetPoint( uint8_t x, uint8_t y )
{
if( (x < LCD_MAX_X) && (y < LCD_MAX_Y ) ) {
uint8_t lin = y >> 3;
uint8_t *vpnt = LcdVram + lin * LCD_MAX_X + x;
*vpnt |= 1 << (y % 8);
LcdLocate( x, lin );
LcdWrDatas( vpnt, 1 );
}
}
// draw circle
void LcdCircle( uint8_t cx, uint8_t cy, uint8_t r )
{
uint8_t x = r;
uint8_t y = 0;
int8_t d = -2 * r + 3;
while(x >= y) {
LcdSetPoint( cx + x, cy + y );
LcdSetPoint( cx - x, cy + y );
LcdSetPoint( cx + x, cy - y );
LcdSetPoint( cx - x, cy - y );
LcdSetPoint( cx + y, cy + x );
LcdSetPoint( cx - y, cy + x );
LcdSetPoint( cx + y, cy - x );
LcdSetPoint( cx - y, cy - x );
if(d >= 0) {
d -= 4 * --x;
}
y++;
d += 4 * y + 2;
}
}
// draw line
void LcdLine( uint8_t xst, uint8_t yst, uint8_t xen, uint8_t yen )
{
int8_t x,y,difx,dify;
int8_t err, xtail, ytail;
LcdXPos = xen;
LcdYPos = yen;
difx = abs( xen - xst );
dify = abs( yen - yst );
if( ( (difx >= dify) && (xen > xst) )
|| ( (difx < dify) && (yen > yst) ) ) {
x = xst;
y = yst;
xtail = xen;
ytail = yen;
} else {
x = xen;
y = yen;
xtail = xst;
ytail = yst;
}
if( difx >= dify ) {
int8_t dy;
dy = ytail > y ? 1 : -1;
err = difx / 2;
for( ; x < xtail; x++, xtail-- ) {
LcdSetPoint( x, y );
LcdSetPoint( xtail, ytail );
err += dify;
if( err >= difx ) {
err -= difx;
y += dy;
ytail -= dy;
}
}
if( x == xtail ) {
LcdSetPoint( x, y );
}
} else {
int8_t dx;
dx = xtail > x ? 1 : -1;
err = dify / 2;
for( ; y < ytail; y++, ytail-- ) {
LcdSetPoint( x, y );
LcdSetPoint( xtail, ytail );
err += difx;
if( err >= dify ) {
err -= dify;
x += dx;
xtail -= dx;
}
}
if( y == ytail ) {
LcdSetPoint( x, y );
}
}
}