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