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" | |
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 | ||
15 | ST7565::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 | ||
61 | ST7565::~ST7565() { | |
62 | //delete this->spi; | |
63 | } | |
64 | ||
65 | //send commands to lcd | |
66 | void 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 | |
76 | void 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 | |
87 | void ST7565::clear(){ | |
88 | memset(framebuffer, 0, FB_SIZE); | |
89 | this->tx=0; | |
90 | this->ty=0; | |
91 | } | |
92 | ||
93 | void 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 | |
102 | void 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 | ||
113 | void ST7565::setCursor(uint8_t col, uint8_t row){ | |
114 | this->tx=col*6; | |
115 | this->ty=row*8; | |
116 | } | |
117 | ||
118 | void ST7565::home(){ | |
119 | this->tx=0; | |
120 | this->ty=0; | |
121 | } | |
122 | ||
123 | void ST7565::display(){ | |
124 | ///nothing | |
125 | } | |
126 | ||
127 | void 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 | } | |
149 | int 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 | |
182 | void ST7565::write_char(char value){ | |
183 | drawChar(this->tx, this->ty,value,1); | |
184 | } | |
185 | ||
186 | void 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 | ||
193 | void 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 | |
203 | uint8_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 | ||
213 | int 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 | ||
227 | void 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 | ||
249 | void 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 | ||
262 | void 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 | } |