4 * Created on: 21-06-2013
9 #include "ST7565/glcdfont.h"
11 #include "platform_memory.h"
16 #define LCDPAGES (LCDHEIGHT+7)/8
17 #define FB_SIZE LCDWIDTH*LCDPAGES
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")
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; }
41 // select which SPI channel to use
42 int spi_channel
= THEKERNEL
->config
->value(panel_checksum
, spi_channel_checksum
)->by_default(0)->as_number();
46 mosi
= P0_18
; sclk
= P0_15
;
47 }else if(spi_channel
== 1){
48 mosi
= P0_9
; sclk
= P0_7
;
50 mosi
= P0_18
; sclk
= P0_15
;
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
57 this->cs
.from_string(THEKERNEL
->config
->value( panel_checksum
, spi_cs_pin_checksum
)->by_default("0.16")->as_string())->as_output();
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);
65 this->a0
.from_string(THEKERNEL
->config
->value( panel_checksum
, a0_pin_checksum
)->by_default("2.13")->as_string())->as_output();
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();
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();
75 // contrast, mviki needs 0x018
76 this->contrast
= THEKERNEL
->config
->value(panel_checksum
, contrast_checksum
)->by_default(9)->as_number();
78 this->reversed
= THEKERNEL
->config
->value(panel_checksum
, reverse_checksum
)->by_default(false)->as_bool();
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");
88 AHB0
.dealloc(framebuffer
);
91 //send commands to lcd
92 void ST7565::send_commands(const unsigned char* buf
, size_t size
){
102 void ST7565::send_data(const unsigned char* buf
, size_t size
){
113 void ST7565::clear(){
114 memset(framebuffer
, 0, FB_SIZE
);
119 void ST7565::send_pic(const unsigned char* data
){
120 for (int i
=0; i
<LCDPAGES
; i
++)
123 send_data(data
+ i
*LCDWIDTH
, LCDWIDTH
);
127 // set column and page number
128 void ST7565::set_xy(int x
, int y
)
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);
139 void ST7565::setCursor(uint8_t col
, uint8_t row
){
149 void ST7565::display(){
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
165 this->contrast
, //contrast value
171 if(this->rst
.connected()) rst
.set(1);
172 send_commands(init_seq
, sizeof(init_seq
));
175 int ST7565::drawChar(int x
, int y
, unsigned char c
, int color
){
185 for (uint8_t i
=0; i
<5; i
++ ) {
187 framebuffer
[x
+ (y
/8*128) ] = ~(glcd_font
[(c
*5)+i
]<< y
%8);
189 framebuffer
[x
+ ((y
+8)/8*128) ] = ~(glcd_font
[(c
*5)+i
] >>(8-(y
%8)));
193 framebuffer
[x
+ ((y
)/8*128) ] = glcd_font
[(c
*5)+i
] <<(y
%8);
195 framebuffer
[x
+ ((y
+8)/8*128) ] = glcd_font
[(c
*5)+i
] >>(8-(y
%8));
207 //write single char to screen
208 void ST7565::write_char(char value
){
209 drawChar(this->tx
, this->ty
,value
,1);
212 void ST7565::write(const char* line
, int len
){
213 for (int i
= 0; i
< len
; ++i
) {
219 void ST7565::on_refresh(bool now
){
220 static int refresh_counts
= 0;
223 if(now
|| refresh_counts
% 2 == 0 ){
224 send_pic(framebuffer
);
228 //reading button state
229 uint8_t ST7565::readButtons(void) {
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);
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()) {
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)];
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
);
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
265 const uint8_t *src
= &glyph
[y_offset
*span
+ x_offset
/8];
266 for (int i
= 0; i
< h
; ++i
) {
271 renderGlyph(x
, y
, g
, w
, h
);
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
);
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)));
288 void ST7565::pixel(int x
, int y
, int colour
)
291 unsigned char mask
= 1<<(y
%8);
292 unsigned char *byte
= &framebuffer
[page
*LCDWIDTH
+ x
];
294 *byte
&= ~mask
; // clear pixel
296 *byte
|= mask
; // set pixel