Commit | Line | Data |
---|---|---|
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 | ||
41 | ST7565::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 | ||
89 | ST7565::~ST7565() { | |
a200fc31 JM |
90 | delete this->spi; |
91 | AHB0.dealloc(framebuffer); | |
5b8a7abb | 92 | } |
93 | ||
94 | //send commands to lcd | |
95 | void 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 | |
105 | void 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 | |
116 | void ST7565::clear(){ | |
117 | memset(framebuffer, 0, FB_SIZE); | |
118 | this->tx=0; | |
119 | this->ty=0; | |
120 | } | |
121 | ||
122 | void 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 | |
131 | void 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 | ||
142 | void ST7565::setCursor(uint8_t col, uint8_t row){ | |
143 | this->tx=col*6; | |
144 | this->ty=row*8; | |
145 | } | |
146 | ||
147 | void ST7565::home(){ | |
148 | this->tx=0; | |
149 | this->ty=0; | |
150 | } | |
151 | ||
152 | void ST7565::display(){ | |
153 | ///nothing | |
154 | } | |
155 | ||
156 | void 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 | } | |
178 | int 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 | |
211 | void ST7565::write_char(char value){ | |
212 | drawChar(this->tx, this->ty,value,1); | |
213 | } | |
214 | ||
215 | void 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 | ||
222 | void 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 | |
232 | uint8_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 | ||
242 | int 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 | ||
256 | void 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 | ||
278 | void 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 | ||
291 | void 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 | } |