Merge pull request #268 from wolfmanjm/feature/add-mviki-panel
[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();
02d432dc 39 if(this->rst.connected()) 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();
02d432dc
JM
54 // reverse display
55 this->reversed= THEKERNEL->config->value(panel_checksum, reverse_checksum)->by_default(false)->as_bool();
3aeb7c2a 56
5b8a7abb 57 framebuffer= (uint8_t *)ahbmalloc(FB_SIZE, AHB_BANK_0); // grab some memoery from USB_RAM
58 if(framebuffer == NULL) {
59 THEKERNEL->streams->printf("Not enough memory available for frame buffer");
60 }
61}
62
63ST7565::~ST7565() {
64 //delete this->spi;
65}
66
67//send commands to lcd
68void ST7565::send_commands(const unsigned char* buf, size_t size){
69 cs.set(0);
70 a0.set(0);
71 while(size-- >0){
72 spi->write(*buf++);
73 }
74 cs.set(1);
75}
76
77//send data to lcd
78void ST7565::send_data(const unsigned char* buf, size_t size){
79 cs.set(0);
80 a0.set(1);
81 while(size-- >0){
82 spi->write(*buf++);
83 }
84 cs.set(1);
85 a0.set(0);
86}
87
88//clearing screen
89void ST7565::clear(){
90 memset(framebuffer, 0, FB_SIZE);
91 this->tx=0;
92 this->ty=0;
93}
94
95void ST7565::send_pic(const unsigned char* data){
96 for (int i=0; i<LCDPAGES; i++)
97 {
98 set_xy(0, i);
99 send_data(data + i*LCDWIDTH, LCDWIDTH);
100 }
101}
102
103// set column and page number
104void ST7565::set_xy(int x, int y)
105{
106 CLAMP(x, 0, LCDWIDTH-1);
107 CLAMP(y, 0, LCDPAGES-1);
108 unsigned char cmd[3];
109 cmd[0] = 0xb0 | (y & 0x07);
110 cmd[1] = 0x10 | (x >> 4);
111 cmd[2] = 0x00 | (x & 0x0f);
112 send_commands(cmd, 3);
113}
114
115void ST7565::setCursor(uint8_t col, uint8_t row){
116 this->tx=col*6;
117 this->ty=row*8;
118}
119
120void ST7565::home(){
121 this->tx=0;
122 this->ty=0;
123}
124
125void ST7565::display(){
126 ///nothing
127}
128
129void ST7565::init(){
130 const unsigned char init_seq[] = {
131 0x40, //Display start line 0
02d432dc
JM
132 reversed?0xa0:0xa1, // ADC
133 reversed?0xc8:0xc0, // COM select
5b8a7abb 134 0xa6, //Display normal
135 0xa2, //Set Bias 1/9 (Duty 1/65)
136 0x2f, //Booster, Regulator and Follower On
137 0xf8, //Set internal Booster to 4x
138 0x00,
139 0x27, //Contrast set
140 0x81,
3aeb7c2a 141 this->contrast, //contrast value
5b8a7abb 142 0xac, //No indicator
143 0x00,
144 0xaf, //Display on
145 };
146 //rst.set(0);
02d432dc 147 if(this->rst.connected()) rst.set(1);
5b8a7abb 148 send_commands(init_seq, sizeof(init_seq));
149 clear();
150}
151int ST7565::drawChar(int x, int y, unsigned char c, int color){
152 int retVal=-1;
153 if(c=='\n'){
154 this->ty+=8;
155 retVal= -tx;
156 }
157 if(c=='\r'){
158 retVal= -tx;
159 }
160 else{
161 for (uint8_t i =0; i<5; i++ ) {
162 if(color==0){
163 framebuffer[x + (y/8*128) ] = ~(glcd_font[(c*5)+i]<< y%8);
164 if(y+8<63){
165 framebuffer[x + ((y+8)/8*128) ] = ~(glcd_font[(c*5)+i] >>(8-(y%8)));
166 }
167 }
168 if(color==1){
169 framebuffer[x + ((y)/8*128) ] = glcd_font[(c*5)+i] <<(y%8);
170 if(y+8<63){
171 framebuffer[x + ((y+8)/8*128) ] = glcd_font[(c*5)+i] >>(8-(y%8));
172 }
173 }
174 x++;
175 }
176 retVal= 6;
177 this->tx+=6;
178 }
179
180 return retVal;
181}
182
183//write single char to screen
184void ST7565::write_char(char value){
185 drawChar(this->tx, this->ty,value,1);
186}
187
188void ST7565::write(const char* line, int len){
189 for (int i = 0; i < len; ++i) {
190 write_char(line[i]);
191 }
192}
193//refreshing screen
194
195void ST7565::on_refresh(bool now){
196 static int refresh_counts = 0;
197 refresh_counts++;
198 // 10Hz refresh rate
199 if(now || refresh_counts % 2 == 0 ){
200 send_pic(framebuffer);
201 }
202}
203
204//reading button state
205uint8_t ST7565::readButtons(void) {
206 uint8_t state= 0;
207 state |= (this->click_pin.get() ? BUTTON_SELECT : 0);
3aeb7c2a
JM
208 if(this->up_pin.connected()) {
209 state |= (this->up_pin.get() ? BUTTON_UP : 0);
210 state |= (this->down_pin.get() ? BUTTON_DOWN : 0);
211 }
5b8a7abb 212 return state;
213}
214
215int ST7565::readEncoderDelta() {
3aeb7c2a
JM
216 static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
217 static uint8_t old_AB = 0;
218 if(this->encoder_a_pin.connected()) {
219 // mviki
220 old_AB <<= 2; //remember previous state
221 old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state
222 return enc_states[(old_AB&0x0f)];
223
224 }else{
225 return 0;
226 }
5b8a7abb 227}
228
229void ST7565::bltGlyph(int x, int y, int w, int h, const uint8_t *glyph, int span, int x_offset, int y_offset) {
230 if(x_offset == 0 && y_offset == 0 && span == 0) {
231 // blt the whole thing
232 renderGlyph(x, y, glyph, w, h);
233
234 }else{
235 // copy portion of glyph into g where x_offset is left byte aligned
236 // Note currently the x_offset must be byte aligned
237 int n= w/8; // bytes per line to copy
238 if(w%8 != 0) n++; // round up to next byte
239 uint8_t g[n*h];
240 uint8_t *dst= g;
241 const uint8_t *src= &glyph[y_offset*span + x_offset/8];
242 for (int i = 0; i < h; ++i) {
243 memcpy(dst, src, n);
244 dst+=n;
245 src+= span;
246 }
247 renderGlyph(x, y, g, w, h);
248 }
249}
250
251void ST7565::renderGlyph(int x, int y, const uint8_t *g, int w, int h) {
252 CLAMP(x, 0, LCDWIDTH-1);
253 CLAMP(y, 0, LCDHEIGHT-1);
254 CLAMP(w, 0, LCDWIDTH - x);
255 CLAMP(h, 0, LCDHEIGHT - y);
256
257 for(int i=0; i<w; i++){
258 for(int j=0; j<h; j++){
832393a7 259 pixel(x+i,y+j,g[(i/8)+ j*((w-1)/8 +1)] & (1<<(7-i%8)));
5b8a7abb 260 }
261 }
262}
263
264void ST7565::pixel(int x, int y, int colour)
265{
266 int page = y / 8;
267 unsigned char mask = 1<<(y%8);
268 unsigned char *byte = &framebuffer[page*LCDWIDTH + x];
269 if ( colour == 0 )
270 *byte &= ~mask; // clear pixel
271 else
272 *byte |= mask; // set pixel
273}