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