Allow TABS in config
[clinton/Smoothieware.git] / src / modules / utils / panel / panels / ST7565.cpp
CommitLineData
5b8a7abb 1/*
2 * ST7565.cpp
3 *
4 * Created on: 21-06-2013
5 * Author: Wulfnor
6 */
7
8#include "ST7565.h"
9#include "ST7565/glcdfont.h"
a0e79e9b 10#include "Kernel.h"
a200fc31 11#include "platform_memory.h"
61134a65
JM
12#include "Config.h"
13#include "checksumm.h"
14#include "StreamOutputPool.h"
5b8a7abb 15
ca6effd7
JM
16//definitions for lcd
17#define LCDWIDTH 128
18#define LCDHEIGHT 64
19#define LCDPAGES (LCDHEIGHT+7)/8
20#define FB_SIZE LCDWIDTH*LCDPAGES
21#define FONT_SIZE_X 6
22#define FONT_SIZE_Y 8
23
24#define panel_checksum CHECKSUM("panel")
25#define spi_channel_checksum CHECKSUM("spi_channel")
26#define spi_cs_pin_checksum CHECKSUM("spi_cs_pin")
27#define spi_frequency_checksum CHECKSUM("spi_frequency")
28#define encoder_a_pin_checksum CHECKSUM("encoder_a_pin")
29#define encoder_b_pin_checksum CHECKSUM("encoder_b_pin")
30#define click_button_pin_checksum CHECKSUM("click_button_pin")
31#define up_button_pin_checksum CHECKSUM("up_button_pin")
32#define down_button_pin_checksum CHECKSUM("down_button_pin")
33#define contrast_checksum CHECKSUM("contrast")
34#define reverse_checksum CHECKSUM("reverse")
35#define rst_pin_checksum CHECKSUM("rst_pin")
36#define a0_pin_checksum CHECKSUM("a0_pin")
37
5b8a7abb 38#define CLAMP(x, low, high) { if ( (x) < (low) ) x = (low); if ( (x) > (high) ) x = (high); } while (0);
39#define swap(a, b) { uint8_t t = a; a = b; b = t; }
40
41ST7565::ST7565() {
42 //SPI com
3aeb7c2a
JM
43
44 // select which SPI channel to use
45 int spi_channel = THEKERNEL->config->value(panel_checksum, spi_channel_checksum)->by_default(0)->as_number();
46 PinName mosi;
47 PinName sclk;
48 if(spi_channel == 0){
49 mosi= P0_18; sclk= P0_15;
50 }else if(spi_channel == 1){
51 mosi= P0_9; sclk= P0_7;
52 }else{
53 mosi= P0_18; sclk= P0_15;
54 }
55
56 this->spi= new mbed::SPI(mosi,NC,sclk);
5b8a7abb 57 this->spi->frequency(THEKERNEL->config->value(panel_checksum, spi_frequency_checksum)->by_default(1000000)->as_number()); //4Mhz freq, can try go a little lower
3aeb7c2a 58
5b8a7abb 59 //chip select
3aeb7c2a 60 this->cs.from_string(THEKERNEL->config->value( panel_checksum, spi_cs_pin_checksum)->by_default("0.16")->as_string())->as_output();
5b8a7abb 61 cs.set(1);
3aeb7c2a 62
5b8a7abb 63 //lcd reset
3aeb7c2a 64 this->rst.from_string(THEKERNEL->config->value( panel_checksum, rst_pin_checksum)->by_default("nc")->as_string())->as_output();
02d432dc 65 if(this->rst.connected()) rst.set(1);
3aeb7c2a 66
5b8a7abb 67 //a0
3aeb7c2a 68 this->a0.from_string(THEKERNEL->config->value( panel_checksum, a0_pin_checksum)->by_default("2.13")->as_string())->as_output();
5b8a7abb 69 a0.set(1);
70
5b8a7abb 71 this->up_pin.from_string(THEKERNEL->config->value( panel_checksum, up_button_pin_checksum )->by_default("nc")->as_string())->as_input();
72 this->down_pin.from_string(THEKERNEL->config->value( panel_checksum, down_button_pin_checksum )->by_default("nc")->as_string())->as_input();
73
3aeb7c2a
JM
74 this->click_pin.from_string(THEKERNEL->config->value( panel_checksum, click_button_pin_checksum )->by_default("nc")->as_string())->as_input();
75 this->encoder_a_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_a_pin_checksum)->by_default("nc")->as_string())->as_input();
76 this->encoder_b_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_b_pin_checksum)->by_default("nc")->as_string())->as_input();
77
78 // contrast, mviki needs 0x018
79 this->contrast= THEKERNEL->config->value(panel_checksum, contrast_checksum)->by_default(9)->as_number();
02d432dc
JM
80 // reverse display
81 this->reversed= THEKERNEL->config->value(panel_checksum, reverse_checksum)->by_default(false)->as_bool();
3aeb7c2a 82
a200fc31 83 framebuffer= (uint8_t *)AHB0.alloc(FB_SIZE); // grab some memoery from USB_RAM
5b8a7abb 84 if(framebuffer == NULL) {
85 THEKERNEL->streams->printf("Not enough memory available for frame buffer");
86 }
87}
88
89ST7565::~ST7565() {
a200fc31
JM
90 delete this->spi;
91 AHB0.dealloc(framebuffer);
5b8a7abb 92}
93
94//send commands to lcd
95void ST7565::send_commands(const unsigned char* buf, size_t size){
96 cs.set(0);
97 a0.set(0);
98 while(size-- >0){
99 spi->write(*buf++);
100 }
101 cs.set(1);
102}
103
104//send data to lcd
105void ST7565::send_data(const unsigned char* buf, size_t size){
106 cs.set(0);
107 a0.set(1);
108 while(size-- >0){
109 spi->write(*buf++);
110 }
111 cs.set(1);
112 a0.set(0);
113}
114
115//clearing screen
116void ST7565::clear(){
117 memset(framebuffer, 0, FB_SIZE);
118 this->tx=0;
119 this->ty=0;
120}
121
122void ST7565::send_pic(const unsigned char* data){
123 for (int i=0; i<LCDPAGES; i++)
124 {
125 set_xy(0, i);
126 send_data(data + i*LCDWIDTH, LCDWIDTH);
127 }
128}
129
130// set column and page number
131void ST7565::set_xy(int x, int y)
132{
133 CLAMP(x, 0, LCDWIDTH-1);
134 CLAMP(y, 0, LCDPAGES-1);
135 unsigned char cmd[3];
136 cmd[0] = 0xb0 | (y & 0x07);
137 cmd[1] = 0x10 | (x >> 4);
138 cmd[2] = 0x00 | (x & 0x0f);
139 send_commands(cmd, 3);
140}
141
142void ST7565::setCursor(uint8_t col, uint8_t row){
143 this->tx=col*6;
144 this->ty=row*8;
145}
146
147void ST7565::home(){
148 this->tx=0;
149 this->ty=0;
150}
151
152void ST7565::display(){
153 ///nothing
154}
155
156void ST7565::init(){
157 const unsigned char init_seq[] = {
158 0x40, //Display start line 0
82d1ceb3
JM
159 (unsigned char)(reversed?0xa0:0xa1), // ADC
160 (unsigned char)(reversed?0xc8:0xc0), // COM select
5b8a7abb 161 0xa6, //Display normal
162 0xa2, //Set Bias 1/9 (Duty 1/65)
163 0x2f, //Booster, Regulator and Follower On
164 0xf8, //Set internal Booster to 4x
165 0x00,
166 0x27, //Contrast set
167 0x81,
3aeb7c2a 168 this->contrast, //contrast value
5b8a7abb 169 0xac, //No indicator
170 0x00,
171 0xaf, //Display on
172 };
173 //rst.set(0);
02d432dc 174 if(this->rst.connected()) rst.set(1);
5b8a7abb 175 send_commands(init_seq, sizeof(init_seq));
176 clear();
177}
178int ST7565::drawChar(int x, int y, unsigned char c, int color){
179 int retVal=-1;
180 if(c=='\n'){
181 this->ty+=8;
182 retVal= -tx;
183 }
184 if(c=='\r'){
185 retVal= -tx;
186 }
187 else{
188 for (uint8_t i =0; i<5; i++ ) {
189 if(color==0){
190 framebuffer[x + (y/8*128) ] = ~(glcd_font[(c*5)+i]<< y%8);
191 if(y+8<63){
192 framebuffer[x + ((y+8)/8*128) ] = ~(glcd_font[(c*5)+i] >>(8-(y%8)));
193 }
194 }
195 if(color==1){
196 framebuffer[x + ((y)/8*128) ] = glcd_font[(c*5)+i] <<(y%8);
197 if(y+8<63){
198 framebuffer[x + ((y+8)/8*128) ] = glcd_font[(c*5)+i] >>(8-(y%8));
199 }
200 }
201 x++;
202 }
203 retVal= 6;
204 this->tx+=6;
205 }
206
207 return retVal;
208}
209
210//write single char to screen
211void ST7565::write_char(char value){
212 drawChar(this->tx, this->ty,value,1);
213}
214
215void ST7565::write(const char* line, int len){
216 for (int i = 0; i < len; ++i) {
217 write_char(line[i]);
218 }
219}
220//refreshing screen
221
222void ST7565::on_refresh(bool now){
223 static int refresh_counts = 0;
224 refresh_counts++;
225 // 10Hz refresh rate
226 if(now || refresh_counts % 2 == 0 ){
227 send_pic(framebuffer);
228 }
229}
230
231//reading button state
232uint8_t ST7565::readButtons(void) {
233 uint8_t state= 0;
234 state |= (this->click_pin.get() ? BUTTON_SELECT : 0);
3aeb7c2a
JM
235 if(this->up_pin.connected()) {
236 state |= (this->up_pin.get() ? BUTTON_UP : 0);
237 state |= (this->down_pin.get() ? BUTTON_DOWN : 0);
238 }
5b8a7abb 239 return state;
240}
241
242int ST7565::readEncoderDelta() {
3aeb7c2a
JM
243 static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
244 static uint8_t old_AB = 0;
245 if(this->encoder_a_pin.connected()) {
246 // mviki
247 old_AB <<= 2; //remember previous state
248 old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state
249 return enc_states[(old_AB&0x0f)];
250
251 }else{
252 return 0;
253 }
5b8a7abb 254}
255
256void ST7565::bltGlyph(int x, int y, int w, int h, const uint8_t *glyph, int span, int x_offset, int y_offset) {
257 if(x_offset == 0 && y_offset == 0 && span == 0) {
258 // blt the whole thing
259 renderGlyph(x, y, glyph, w, h);
260
261 }else{
262 // copy portion of glyph into g where x_offset is left byte aligned
263 // Note currently the x_offset must be byte aligned
264 int n= w/8; // bytes per line to copy
265 if(w%8 != 0) n++; // round up to next byte
266 uint8_t g[n*h];
267 uint8_t *dst= g;
268 const uint8_t *src= &glyph[y_offset*span + x_offset/8];
269 for (int i = 0; i < h; ++i) {
270 memcpy(dst, src, n);
271 dst+=n;
272 src+= span;
273 }
274 renderGlyph(x, y, g, w, h);
275 }
276}
277
278void ST7565::renderGlyph(int x, int y, const uint8_t *g, int w, int h) {
279 CLAMP(x, 0, LCDWIDTH-1);
280 CLAMP(y, 0, LCDHEIGHT-1);
281 CLAMP(w, 0, LCDWIDTH - x);
282 CLAMP(h, 0, LCDHEIGHT - y);
283
284 for(int i=0; i<w; i++){
285 for(int j=0; j<h; j++){
832393a7 286 pixel(x+i,y+j,g[(i/8)+ j*((w-1)/8 +1)] & (1<<(7-i%8)));
5b8a7abb 287 }
288 }
289}
290
291void ST7565::pixel(int x, int y, int colour)
292{
293 int page = y / 8;
294 unsigned char mask = 1<<(y%8);
295 unsigned char *byte = &framebuffer[page*LCDWIDTH + x];
296 if ( colour == 0 )
297 *byte &= ~mask; // clear pixel
298 else
299 *byte |= mask; // set pixel
300}