#include <bcm2835.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>

#define LCD_BACK 2
#define LCD_CS   17
#define LCD_RST  27
#define LCD_A0   22

// raspberry picture
const uint8_t raspi[] = {
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0xF0,0xF8,0x58,0x1C,0x1C,0x0C,0x0C,0x06,0x86,0x86,0x86,0x0E,
  0x0E,0x06,0x0E,0x1E,0x1C,0x1C,0x0C,0x3C,0x38,0x78,0xF0,0xE0,0xC0,0xC0,0xE0,0x70,
  0x38,0x18,0x1C,0x1C,0x0C,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x86,0x86,0x06,0x04,0x0C,
  0x0C,0x18,0xF8,0xF8,0xF8,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x03,0x0F,0x3F,0x7C,0xF0,0xC0,0xC0,0xC0,0xC0,0x00,0x01,0x01,
  0x03,0x02,0x06,0x04,0x04,0x1C,0xB8,0xF0,0xE0,0x70,0x30,0x1F,0x0F,0x0F,0x3F,0x30,
  0xF0,0xE0,0xB0,0x18,0x08,0x0C,0x04,0x06,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0xC0,
  0xF0,0xF8,0x7F,0x1F,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xE0,0xF3,0x33,0x3F,0x1F,0x1E,0x0E,0x0E,
  0x0C,0x0C,0xEC,0xEC,0xEE,0xBE,0x0F,0x0F,0x07,0x06,0x02,0x02,0x02,0x02,0x02,0x06,
  0x06,0x0F,0x0F,0xBE,0xEE,0xCC,0x8C,0x0C,0x0C,0x0C,0x0E,0x1E,0x1E,0x3F,0x73,0xE1,
  0xC1,0x00,0x36,0x25,0x55,0x5D,0x49,0x6D,0x32,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x7F,0x7F,0x3F,0xFC,0xFC,0xFC,0x3C,0x1C,0x0E,
  0x07,0x07,0x03,0x03,0x03,0x03,0x03,0x02,0x06,0x0E,0x1C,0xFC,0xFC,0xFC,0x3C,0x0E,
  0x06,0x02,0x03,0x03,0x03,0x03,0x03,0x03,0x07,0x06,0x0C,0x18,0x78,0xF0,0xF0,0x33,
  0x7F,0x7F,0x70,0xF0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0xFF,0xFF,0x83,0x00,0x00,0x00,0x00,0xC0,0xFF,0xFF,0xF0,0xF0,0xF0,0x80,
  0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0x60,0x78,0x7F,0x7F,0x7F,0x7C,0x70,
  0xE0,0xC0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0xE0,0xFF,0xFF,0xC0,
  0x00,0x00,0x00,0x00,0xC3,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x03,0x0F,0x3E,0xFC,0xFC,0x0E,0x03,0x03,0x03,0x03,0x07,0x07,0x0F,
  0x1F,0x3F,0x7F,0xFF,0xFF,0x07,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x01,0x03,0xFF,0xFF,0x7F,0x1F,0x0F,0x07,0x07,0x03,0x01,0x01,0x01,0x01,
  0x03,0xFE,0xFE,0x7E,0x07,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x07,0x1F,0x3C,0x78,0x70,0xE0,0xE0,0xE0,0xC0,
  0xC0,0xC0,0xC0,0xE1,0xFF,0x7F,0x7F,0x3C,0x3C,0x3C,0x38,0x30,0x30,0x30,0x30,0x38,
  0x38,0x3C,0x3C,0x7F,0xFF,0xE1,0xC0,0xC0,0xC0,0xC0,0xC0,0xE0,0x60,0x70,0x38,0x38,
  0x1E,0x0F,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
  0x01,0x03,0x03,0x07,0x07,0x0E,0x18,0x18,0x38,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
  0x38,0x18,0x1C,0x1E,0x0F,0x07,0x03,0x03,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

const uint8_t initcmd[] = 
{
  0xa1,							//screen orientation
  0x41,							//set starting line
  0xc0,							//page count direction
  0xa3,							//1/7 bias
  0x2c,							//vc
  0x2e,							//vc+vr
  0x2f,							//vc+vr+vf
  0x24,							//voltage regulator (0x20-0x27)
  0xa6,							//do not reverse the display
  0xaf,							//display on
  0xa4,							//display from ram
  0x81,							//turn on brightness regulation
  0x18							//set brightness (0x0-0x40)
};

const font[] = 
{
  0x00, 0x00, 0x00, 0x00, 0x00,//  0x20 32
  0x00, 0x00, 0x5e, 0x00, 0x00,//! 0x21 
  0x00, 0x0e, 0x00, 0x0e, 0x00,//" 0x22 
  0x14, 0x7f, 0x14, 0x7f, 0x14,//# 0x23 
  0x04, 0x2a, 0x7f, 0x2a, 0x10,//$ 0x24 
  0x00, 0x16, 0x08, 0x34, 0x00,//% 0x25 
  0x36, 0x49, 0x36, 0x40, 0x00,//& 0x26 
  0x00, 0x00, 0x0e, 0x00, 0x00,//' 0x27 
  0x00, 0x3c, 0x42, 0x00, 0x00,//( 0x28 
  0x00, 0x42, 0x3c, 0x00, 0x00,//) 0x29 
  0x54, 0x38, 0x38, 0x54, 0x00,//* 0x2a 
  0x10, 0x10, 0x7c, 0x10, 0x10,//+ 0x2b 
  0x00, 0x80, 0x60, 0x20, 0x00,//, 0x2c 
  0x10, 0x10, 0x10, 0x10, 0x00,//- 0x2d 
  0x00, 0x40, 0xe0, 0x40, 0x00,//. 0x2e 
  0x60, 0x10, 0x08, 0x06, 0x00,/// 0x2f 
  0x00, 0x3c, 0x42, 0x3c, 0x00,//0 0x30 
  0x00, 0x44, 0x7e, 0x40, 0x00,//1 0x31 
  0x64, 0x52, 0x52, 0x4c, 0x00,//2 0x32 
  0x22, 0x4a, 0x4e, 0x32, 0x00,//3 0x33 
  0x18, 0x14, 0x7e, 0x10, 0x00,//4 0x34 
  0x2e, 0x4a, 0x4a, 0x32, 0x00,//5 0x35 
  0x3c, 0x4a, 0x4a, 0x30, 0x00,//6 0x36 
  0x02, 0x62, 0x1a, 0x06, 0x00,//7 0x37 
  0x34, 0x4a, 0x4a, 0x34, 0x00,//8 0x38 
  0x0c, 0x52, 0x52, 0x3c, 0x00,//9 0x39 
  0x00, 0x6c, 0x6c, 0x00, 0x00,//: 0x3a 
  0x00, 0x80, 0x6c, 0x2c, 0x00,//; 0x3b 
  0x00, 0x18, 0x24, 0x42, 0x00,//< 0x3c 
  0x28, 0x28, 0x28, 0x28, 0x00,//= 0x3d 
  0x00, 0x42, 0x24, 0x18, 0x00,//> 0x3e 
  0x00, 0x04, 0x52, 0x0c, 0x00,//? 0x3f 
  0x3c, 0x42, 0x99, 0xa5, 0x1e,//@ 0x40 
  0x7c, 0x12, 0x12, 0x7c, 0x00,//A 0x41 
  0x7e, 0x4a, 0x4a, 0x34, 0x00,//B 0x42 
  0x3c, 0x42, 0x42, 0x24, 0x00,//C 0x43 
  0x7e, 0x42, 0x42, 0x3c, 0x00,//D 0x44 
  0x7e, 0x4a, 0x4a, 0x42, 0x00,//E 0x45 
  0x7e, 0x0a, 0x0a, 0x02, 0x00,//F 0x46 
  0x3c, 0x42, 0x52, 0x34, 0x00,//G 0x47 
  0x7e, 0x08, 0x08, 0x7e, 0x00,//H 0x48 
  0x00, 0x42, 0x7e, 0x42, 0x00,//I 0x49 
  0x20, 0x42, 0x3e, 0x02, 0x00,//J 0x4a 
  0x7e, 0x08, 0x34, 0x42, 0x00,//K 0x4b 
  0x7e, 0x40, 0x40, 0x40, 0x00,//L 0x4c 
  0x7e, 0x0c, 0x0c, 0x7e, 0x00,//M 0x4d 
  0x7e, 0x0c, 0x38, 0x7e, 0x00,//N 0x4e 
  0x3c, 0x42, 0x42, 0x3c, 0x00,//O 0x4f 
  0x7e, 0x12, 0x12, 0x0c, 0x00,//P 0x50 
  0x3c, 0x52, 0x62, 0xbc, 0x00,//Q 0x51 
  0x7e, 0x12, 0x12, 0x6c, 0x00,//R 0x52 
  0x24, 0x4a, 0x52, 0x24, 0x00,//S 0x53 
  0x00, 0x02, 0x7e, 0x02, 0x00,//T 0x54 
  0x3e, 0x40, 0x40, 0x3e, 0x00,//U 0x55 
  0x1e, 0x60, 0x60, 0x1e, 0x00,//V 0x56 
  0x7e, 0x30, 0x30, 0x7e, 0x00,//W 0x57 
  0x66, 0x18, 0x18, 0x66, 0x00,//X 0x58 
  0x06, 0x08, 0x70, 0x08, 0x06,//Y 0x59 
  0x62, 0x52, 0x4a, 0x46, 0x00,//Z 0x5a
  0x00, 0x7e, 0x42, 0x42, 0x00,//[ 0x5b 
  0x06, 0x08, 0x10, 0x60, 0x00,//\ 0x5c 
  0x00, 0x42, 0x42, 0x7e, 0x00,//] 0x5d 
  0x00, 0x04, 0x02, 0x04, 0x00,//^ 0x5e 
  0x80, 0x80, 0x80, 0x80, 0x00,//_ 0x5f 
  0x00, 0x02, 0x04, 0x00, 0x00,//` 0x60 96
  0x30, 0x48, 0x48, 0x78, 0x00,//a 0x61 97
  0x7e, 0x48, 0x48, 0x30, 0x00,//b 0x62 98
  0x00, 0x30, 0x48, 0x48, 0x00,//c 0x63 99
  0x30, 0x48, 0x48, 0x7e, 0x00,//d 0x64 100
  0x30, 0x68, 0x58, 0x10, 0x00,//e 0x65 101
  0x10, 0x7c, 0x12, 0x04, 0x00,//f 0x66 102
  0x10, 0xa8, 0xa8, 0x70, 0x00,//g 0x67 
  0x7e, 0x08, 0x08, 0x70, 0x00,//h 0x68 
  0x00, 0x48, 0x7a, 0x40, 0x00,//i 0x69 
  0x00, 0x40, 0x80, 0x7a, 0x00,//j 0x6a
  0x7e, 0x10, 0x10, 0x68, 0x00,//k 0x6b 
  0x00, 0x42, 0x7e, 0x40, 0x00,//l 0x6c 
  0x78, 0x08, 0x70, 0x08, 0x70,//m 0x6d 
  0x78, 0x08, 0x08, 0x70, 0x00,//n 0x6e 
  0x30, 0x48, 0x48, 0x30, 0x00,//o 0x6f 
  0xf8, 0x28, 0x28, 0x10, 0x00,//p 0x70 
  0x10, 0x28, 0x28, 0xf8, 0x00,//q 0x71 
  0x78, 0x10, 0x08, 0x10, 0x00,//r 0x72 
  0x00, 0x50, 0x58, 0x28, 0x00,//s 0x73 
  0x08, 0x3e, 0x48, 0x20, 0x00,//t 0x74 
  0x38, 0x40, 0x40, 0x78, 0x00,//u 0x75 
  0x00, 0x38, 0x40, 0x38, 0x00,//v 0x76 
  0x38, 0x40, 0x30, 0x40, 0x38,//w 0x77 
  0x48, 0x30, 0x30, 0x48, 0x00,//x 0x78 
  0x58, 0xa0, 0xa0, 0x78, 0x00,//y 0x79 
  0x48, 0x68, 0x58, 0x48, 0x00,//z 0x7a
  0x08, 0x2a, 0x55, 0x41, 0x00,//{ 0x7b 
  0x00, 0x00, 0x7e, 0x00, 0x00,//| 0x7c 
  0x41, 0x55, 0x2a, 0x08, 0x00,//} 0x7d 
  0x04, 0x02, 0x04, 0x02, 0x00,//~ 0x7e 
  0xff, 0x81, 0x81, 0x81, 0xff,//  0x7f 
  0
};

uint8_t init()
{
  if (!bcm2835_init()) {
    return 0;
  }
  bcm2835_gpio_fsel(LCD_BACK,BCM2835_GPIO_FSEL_OUTP);	//backlight
  bcm2835_gpio_fsel(LCD_A0,BCM2835_GPIO_FSEL_OUTP);	//A0
  bcm2835_gpio_fsel(LCD_RST,BCM2835_GPIO_FSEL_OUTP);	//RST
  bcm2835_gpio_fsel(LCD_CS,BCM2835_GPIO_FSEL_OUTP);	//CS
  bcm2835_gpio_write(LCD_CS,HIGH);			//set CS to high to indicate the bus as free
  bcm2835_gpio_write(LCD_RST,LOW);
  bcm2835_delayMicroseconds(1);
  bcm2835_gpio_write(LCD_RST,HIGH);			//hardware reset
  //setup SPI
  bcm2835_spi_begin();
  bcm2835_spi_chipSelect(BCM2835_SPI_CS_NONE);		//manual CS control
  bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_4);	//set speed to 62.5MHz (fastest supported)

  int i;
  bcm2835_gpio_write(LCD_CS,LOW);
  for(i = 0; i < sizeof(initcmd)/sizeof(uint8_t); i++)
    transfer(initcmd[i],0);
  bcm2835_gpio_write(LCD_CS,HIGH);
  bcm2835_gpio_write(LCD_BACK,HIGH);			//turn backlight on
  
  return 1;
}

void close()
{
  bcm2835_spi_end();
  bcm2835_gpio_write(LCD_BACK,LOW);			//turn backlight off
  bcm2835_gpio_write(LCD_RST,LOW);
  bcm2835_delayMicroseconds(1);
  bcm2835_gpio_write(LCD_RST,HIGH);                     //hardware reset
}

void pause()
{
  printf("Press ENTER to gracefully close the program");
  getchar();
}

void transfer(uint8_t byte, uint8_t a0)
{
  bcm2835_gpio_write(LCD_A0,(a0>0?HIGH:LOW));
  bcm2835_gpio_write(LCD_CS,LOW);
  bcm2835_spi_transfer(byte);
  bcm2835_gpio_write(LCD_CS,HIGH);
  bcm2835_gpio_write(LCD_A0,(a0>0?LOW:HIGH));
}

int set_location(uint8_t page, uint8_t column)
{
  if(page>8 || column>132)
    return -1;
  else
  {
    page=(page)%8;
    transfer(0xb0 | page, 0);				//set page
    transfer(0x10 | ((0xf0 & column)>>4),0);		//set 4 msb's of column
    transfer(0x0f & column,0);				//set 4 lsb's of column
  }
}

void clear()
{
  int i;
  for(i = 0; i < 8; i++)
  {
    int j;
    set_location(i,0);
    for(j = 0; j < 129; j++)
    {
      transfer(0x00,1);
    }
  }
}

void illuminate()
{
  int i;
  for(i = 0; i < 8; i++)
  {
    int j;
    set_location(i,0);
    for(j = 0; j < 129; j++)
    {
      transfer(0xff,1);
    }
  }
}

void picture(const uint8_t *pic)
{
  int i;
  for(i = 0; i < 8; i++)
  {
    int j;
    set_location(i,0);
    for(j = 0; j < 128; j++)
    {
      transfer(pic[i*128+j],1);
    }
  }
}

void print(const char ch, uint8_t page, uint8_t offset)
{
  int i;
  set_location(page,offset);
  for(i = 0; i < 5; i++)
    transfer(font[(ch-32)*5+i],1);
}

void prints(const char *str, uint8_t page, uint8_t offset)
{
  int i;
  for(i = 0; (str[i] != '\0') && (i < 25-(offset/5)); i++)
  {
    print(str[i],page,offset+i*5);
  }
  
}

int main(int argc, char **argv)
{
  if(init())
  {
    {		
							//main function
      bcm2835_gpio_write(LCD_CS,LOW);			//start transaction
      int i;
      picture(raspi);
      sleep(1);
      clear();
      prints("Hello World!",0,30);
      prints("!\"#$%&'()*+,-./0123456789",2,0);
      prints(":;<=>?@ABCDEFGHIJKLMNOPQR",3,0);
      prints("STUVWXYZ[\]^_`abcdefghijkl",4,0);
      prints("mnopqrstuvwxyz{|}~",5,0);
      bcm2835_gpio_write(LCD_CS,HIGH);			//end transaction
    }
    pause();
    close();
  } else return 1;
  return 0;
}
