diff options
Diffstat (limited to 'firmware/target/arm/as3525/lcd-ssd1303.c')
-rw-r--r-- | firmware/target/arm/as3525/lcd-ssd1303.c | 211 |
1 files changed, 129 insertions, 82 deletions
diff --git a/firmware/target/arm/as3525/lcd-ssd1303.c b/firmware/target/arm/as3525/lcd-ssd1303.c index a00398a998..2aa0b844c5 100644 --- a/firmware/target/arm/as3525/lcd-ssd1303.c +++ b/firmware/target/arm/as3525/lcd-ssd1303.c | |||
@@ -10,6 +10,7 @@ | |||
10 | * Copyright (C) 2002 by Alan Korr | 10 | * Copyright (C) 2002 by Alan Korr |
11 | * Copyright (C) 2008 François Dinel | 11 | * Copyright (C) 2008 François Dinel |
12 | * Copyright (C) 2008-2009 Rafaël Carré | 12 | * Copyright (C) 2008-2009 Rafaël Carré |
13 | * Copyright (C) 2017 William Wilgus | ||
13 | * | 14 | * |
14 | * This program is free software; you can redistribute it and/or | 15 | * This program is free software; you can redistribute it and/or |
15 | * modify it under the terms of the GNU General Public License | 16 | * modify it under the terms of the GNU General Public License |
@@ -118,10 +119,10 @@ void lcd_set_flip(bool yesno) | |||
118 | #ifdef HAVE_LCD_ENABLE | 119 | #ifdef HAVE_LCD_ENABLE |
119 | void lcd_enable(bool enable) | 120 | void lcd_enable(bool enable) |
120 | { | 121 | { |
121 | if(display_on == enable) | 122 | if (display_on == enable) |
122 | return; | 123 | return; |
123 | 124 | ||
124 | if( (display_on = enable) ) /* simple '=' is not a typo ! */ | 125 | if ( (display_on = enable) ) /* simple '=' is not a typo ! */ |
125 | { | 126 | { |
126 | lcd_enable_power(enable); | 127 | lcd_enable_power(enable); |
127 | lcd_write_command(LCD_SET_DISPLAY_ON); | 128 | lcd_write_command(LCD_SET_DISPLAY_ON); |
@@ -149,50 +150,65 @@ void lcd_init_device(void) | |||
149 | variant = lcd_hw_init(); | 150 | variant = lcd_hw_init(); |
150 | offset = (variant == 0) ? 2 : 0; | 151 | offset = (variant == 0) ? 2 : 0; |
151 | 152 | ||
152 | /* Set display clock (divide ratio = 1) and oscillator frequency (1) */ | 153 | /* power on display to accept commands */ |
153 | lcd_write_command(LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ); | 154 | lcd_enable_power(true); |
154 | lcd_write_command(0x10); | 155 | |
155 | 156 | lcd_write_cmd_triplet | |
156 | /* Set VCOM deselect level to 0.76V */ | 157 | ( |
157 | lcd_write_command(LCD_SET_VCOM_DESELECT_LEVEL); | 158 | /* Set display clock */ |
158 | lcd_write_command(0x34); | 159 | (LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ), |
159 | 160 | /* Set display clock (divide ratio = 1) and oscillator frequency (1) */ | |
160 | /* Set pre-charge period (p1period is 2 dclk and p2period is 5 dclk) */ | 161 | (0x10), |
161 | lcd_write_command(LCD_SET_PRECHARGE_PERIOD); | 162 | /* Set VCOM deselect level */ |
162 | lcd_write_command(0x25); | 163 | (LCD_SET_VCOM_DESELECT_LEVEL) |
164 | ); | ||
165 | |||
166 | lcd_write_cmd_triplet | ||
167 | ( | ||
168 | /* Set VCOM deselect level to 0.76V */ | ||
169 | (0x34), | ||
170 | /* Set pre-charge period */ | ||
171 | (LCD_SET_PRECHARGE_PERIOD), | ||
172 | /* Set pre-charge period (p1period is 2 dclk and p2period is 5 dclk) */ | ||
173 | (0x25) | ||
174 | ); | ||
163 | 175 | ||
164 | /* Set contrast register to 12% */ | 176 | /* Set contrast register to 12% */ |
165 | lcd_set_contrast(lcd_default_contrast()); | 177 | lcd_set_contrast(lcd_default_contrast()); |
166 | 178 | ||
167 | /* Configure DC-DC */ | 179 | lcd_write_cmd_triplet |
168 | lcd_write_command(LCD_SET_DC_DC); | 180 | ( |
169 | lcd_write_command((variant == 0) ? 0x8A : 0x10); | 181 | /* Configure DC-DC */ |
170 | 182 | (LCD_SET_DC_DC), | |
171 | /* Set starting line as 0 */ | 183 | /* Configure DC-DC */ |
172 | lcd_write_command(LCD_SET_DISPLAY_START_LINE /*|(0 & 0x3f)*/); | 184 | ((variant == 0) ? 0x8A : 0x10), |
173 | 185 | /* Set starting line as 0 */ | |
174 | /* Column 131 is remapped to SEG0 */ | 186 | (LCD_SET_DISPLAY_START_LINE /*|(0 & 0x3f)*/) |
175 | lcd_write_command(LCD_SET_SEGMENT_REMAP_INV); | 187 | ); |
176 | 188 | ||
177 | /* Invert COM scan direction (N-1 to 0) */ | 189 | lcd_write_cmd_triplet |
178 | lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV); | 190 | ( |
179 | 191 | /* Column 131 is remapped to SEG0 */ | |
180 | /* Set normal display mode (not every pixel ON) */ | 192 | (LCD_SET_SEGMENT_REMAP_INV), |
181 | lcd_write_command(LCD_SET_ENTIRE_DISPLAY_OFF); | 193 | /* Invert COM scan direction (N-1 to 0) */ |
182 | 194 | (LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV), | |
183 | /* Set normal display mode (not inverted) */ | 195 | /* Set normal display mode (not every pixel ON) */ |
184 | lcd_write_command(LCD_SET_NORMAL_DISPLAY); | 196 | (LCD_SET_ENTIRE_DISPLAY_OFF) |
185 | 197 | ); | |
186 | /* Clear whole framebuffer, including "overscan" | 198 | |
187 | * We don't need to handle that out of screen columns in lcd_clear_display() | 199 | lcd_write_cmd_triplet |
188 | * since we will never write into it anymore | 200 | ( |
189 | */ | 201 | /* Set normal display mode (not inverted) */ |
190 | lcd_write_command (LCD_SET_HIGHER_COLUMN_ADDRESS /*| 0*/); | 202 | (LCD_SET_NORMAL_DISPLAY), |
191 | lcd_write_command (LCD_SET_LOWER_COLUMN_ADDRESS /*| 0*/); | 203 | /* set upper 4 bits of 8-bit column address */ |
204 | (LCD_SET_HIGHER_COLUMN_ADDRESS /*| 0*/), | ||
205 | /* set lower 4 bits of 8-bit column address */ | ||
206 | (LCD_SET_LOWER_COLUMN_ADDRESS /*| 0*/) | ||
207 | ); | ||
192 | 208 | ||
193 | fb_data p_bytes[LCD_WIDTH + 2 * offset]; | 209 | fb_data p_bytes[LCD_WIDTH + 2 * offset]; |
194 | memset(p_bytes, 0, sizeof(p_bytes)); /* fills with 0 : pixel off */ | 210 | memset(p_bytes, 0, sizeof(p_bytes)); /* fills with 0 : pixel off */ |
195 | for(i = 0; i < 8; i++) | 211 | for (i = 0; i < 8; i++) |
196 | { | 212 | { |
197 | lcd_write_command (LCD_SET_PAGE_ADDRESS | (i /*& 0xf*/)); | 213 | lcd_write_command (LCD_SET_PAGE_ADDRESS | (i /*& 0xf*/)); |
198 | lcd_write_data(p_bytes, LCD_WIDTH + 2 * offset); | 214 | lcd_write_data(p_bytes, LCD_WIDTH + 2 * offset); |
@@ -205,27 +221,48 @@ void lcd_init_device(void) | |||
205 | 221 | ||
206 | /*** Update functions ***/ | 222 | /*** Update functions ***/ |
207 | 223 | ||
224 | /* returns LCD_CNTL_HIGHCOL or'd with higher 4 bits of | ||
225 | the 8-bit column address for the display data RAM. | ||
226 | */ | ||
227 | static inline int get_column_high_byte(const int x) | ||
228 | { | ||
229 | return (LCD_CNTL_HIGHCOL | (((x+offset) >> 4) & 0xf)); | ||
230 | } | ||
231 | |||
232 | /* returns LCD_CNTL_LOWCOL or'd with lower 4 bits of | ||
233 | the 8-bit column address for the display data RAM. | ||
234 | */ | ||
235 | static inline int get_column_low_byte(const int x) | ||
236 | { | ||
237 | return (LCD_CNTL_LOWCOL | ((x+offset) & 0xf)); | ||
238 | } | ||
239 | |||
208 | /* Performance function that works with an external buffer | 240 | /* Performance function that works with an external buffer |
209 | note that by and bheight are in 8-pixel units! */ | 241 | note that by and bheight are in 8-pixel units! */ |
210 | void lcd_blit_mono(const unsigned char *data, int x, int by, int width, | 242 | void lcd_blit_mono(const unsigned char *data, int x, int by, int width, |
211 | int bheight, int stride) | 243 | int bheight, int stride) |
212 | { | 244 | { |
213 | if(!display_on) | 245 | if (!display_on) |
214 | return; | 246 | return; |
215 | 247 | ||
248 | const int column_high = get_column_high_byte(x); | ||
249 | const int column_low = get_column_low_byte(x); | ||
250 | |||
216 | /* Copy display bitmap to hardware */ | 251 | /* Copy display bitmap to hardware */ |
217 | while (bheight--) | 252 | while (bheight--) |
218 | { | 253 | { |
219 | lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); | 254 | lcd_write_cmd_triplet |
220 | lcd_write_command (LCD_CNTL_HIGHCOL | (((x+offset)>>4) & 0xf)); | 255 | ( |
221 | lcd_write_command (LCD_CNTL_LOWCOL | ((x+offset) & 0xf)); | 256 | (LCD_CNTL_PAGE | (by++ & 0xf)), |
257 | (column_high), | ||
258 | (column_low) | ||
259 | ); | ||
222 | 260 | ||
223 | lcd_write_data(data, width); | 261 | lcd_write_data(data, width); |
224 | data += stride; | 262 | data += stride; |
225 | } | 263 | } |
226 | } | 264 | } |
227 | 265 | ||
228 | |||
229 | #ifndef BOOTLOADER | 266 | #ifndef BOOTLOADER |
230 | /* Helper function for lcd_grey_phase_blit(). */ | 267 | /* Helper function for lcd_grey_phase_blit(). */ |
231 | void lcd_grey_data(unsigned char *values, unsigned char *phases, int count); | 268 | void lcd_grey_data(unsigned char *values, unsigned char *phases, int count); |
@@ -235,16 +272,22 @@ void lcd_grey_data(unsigned char *values, unsigned char *phases, int count); | |||
235 | void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases, | 272 | void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases, |
236 | int x, int by, int width, int bheight, int stride) | 273 | int x, int by, int width, int bheight, int stride) |
237 | { | 274 | { |
238 | if(!display_on) | 275 | if (!display_on) |
239 | return; | 276 | return; |
240 | 277 | ||
278 | const int column_high = get_column_high_byte(x); | ||
279 | const int column_low = get_column_low_byte(x); | ||
280 | |||
241 | stride <<= 3; /* 8 pixels per block */ | 281 | stride <<= 3; /* 8 pixels per block */ |
242 | /* Copy display bitmap to hardware */ | 282 | /* Copy display bitmap to hardware */ |
243 | while (bheight--) | 283 | while (bheight--) |
244 | { | 284 | { |
245 | lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); | 285 | lcd_write_cmd_triplet |
246 | lcd_write_command (LCD_CNTL_HIGHCOL | (((x+offset)>>4) & 0xf)); | 286 | ( |
247 | lcd_write_command (LCD_CNTL_LOWCOL | ((x+offset) & 0xf)); | 287 | (LCD_CNTL_PAGE | (by++ & 0xf)), |
288 | (column_high), | ||
289 | (column_low) | ||
290 | ); | ||
248 | 291 | ||
249 | lcd_grey_data(values, phases, width); | 292 | lcd_grey_data(values, phases, width); |
250 | 293 | ||
@@ -255,26 +298,41 @@ void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases, | |||
255 | 298 | ||
256 | #endif | 299 | #endif |
257 | 300 | ||
301 | /* Shared internal function for lcd_update and lcd_update_rect | ||
302 | WARNING does NOT check bounds | ||
303 | */ | ||
304 | static void internal_update_rect(int, int, int, int) ICODE_ATTR; | ||
305 | static void internal_update_rect(int x, int y, int width, int height) | ||
306 | { | ||
307 | if (!display_on) | ||
308 | return; | ||
309 | |||
310 | const int column_high = get_column_high_byte(x); | ||
311 | const int column_low = get_column_low_byte(x); | ||
312 | |||
313 | /* Copy specified rectange bitmap to hardware */ | ||
314 | for (; y <= height; y++) | ||
315 | { | ||
316 | lcd_write_cmd_triplet | ||
317 | ( | ||
318 | (LCD_CNTL_PAGE | (y & 0xf)), | ||
319 | (column_high), | ||
320 | (column_low) | ||
321 | ); | ||
322 | |||
323 | lcd_write_data (FBADDR(x,y), width); | ||
324 | } | ||
325 | lcd_write_command (LCD_NOP); /* return to command mode */ | ||
326 | |||
327 | } | ||
258 | 328 | ||
259 | /* Update the display. | 329 | /* Update the display. |
260 | This must be called after all other LCD functions that change the display. */ | 330 | This must be called after all other LCD functions that change the display. */ |
261 | void lcd_update(void) ICODE_ATTR; | 331 | void lcd_update(void) ICODE_ATTR; |
262 | void lcd_update(void) | 332 | void lcd_update(void) |
263 | { | 333 | { |
264 | int y; | ||
265 | |||
266 | if(!display_on) | ||
267 | return; | ||
268 | |||
269 | /* Copy display bitmap to hardware */ | 334 | /* Copy display bitmap to hardware */ |
270 | for (y = 0; y < LCD_FBHEIGHT; y++) | 335 | internal_update_rect(0, 0, LCD_WIDTH, LCD_FBHEIGHT - 1); |
271 | { | ||
272 | lcd_write_command (LCD_CNTL_PAGE | (y & 0xf)); | ||
273 | lcd_write_command (LCD_CNTL_HIGHCOL | ((offset >> 4) & 0xf)); | ||
274 | lcd_write_command (LCD_CNTL_LOWCOL | (offset & 0xf)); | ||
275 | |||
276 | lcd_write_data (FBADDR(0, y), LCD_WIDTH); | ||
277 | } | ||
278 | } | 336 | } |
279 | 337 | ||
280 | /* Update a fraction of the display. */ | 338 | /* Update a fraction of the display. */ |
@@ -282,45 +340,34 @@ void lcd_update_rect(int, int, int, int) ICODE_ATTR; | |||
282 | void lcd_update_rect(int x, int y, int width, int height) | 340 | void lcd_update_rect(int x, int y, int width, int height) |
283 | { | 341 | { |
284 | int ymax; | 342 | int ymax; |
285 | 343 | if (!display_on) | |
286 | if(!display_on) | ||
287 | return; | 344 | return; |
288 | 345 | ||
289 | /* The Y coordinates have to work on even 8 pixel rows */ | 346 | /* make sure the rectangle is bounded in the screen */ |
290 | if (x < 0) | 347 | if (width > LCD_WIDTH - x)/* Clip right */ |
348 | width = LCD_WIDTH - x; | ||
349 | if (x < 0)/* Clip left */ | ||
291 | { | 350 | { |
292 | width += x; | 351 | width += x; |
293 | x = 0; | 352 | x = 0; |
294 | } | 353 | } |
295 | |||
296 | if (x + width > LCD_WIDTH) | ||
297 | width = LCD_WIDTH - x; | ||
298 | |||
299 | if (width <= 0) | 354 | if (width <= 0) |
300 | return; /* nothing left to do, 0 is harmful to lcd_write_data() */ | 355 | return; /* nothing left to do, 0 is harmful to lcd_write_data() */ |
301 | 356 | ||
302 | if (y < 0) | 357 | if (height > LCD_HEIGHT - y) /* Clip bottom */ |
358 | height = LCD_HEIGHT - y; | ||
359 | if (y < 0) /* Clip top */ | ||
303 | { | 360 | { |
304 | height += y; | 361 | height += y; |
305 | y = 0; | 362 | y = 0; |
306 | } | 363 | } |
307 | |||
308 | if (y + height > LCD_HEIGHT) | ||
309 | height = LCD_HEIGHT - y; | ||
310 | |||
311 | if (height <= 0) | 364 | if (height <= 0) |
312 | return; /* nothing left to do */ | 365 | return; /* nothing left to do */ |
313 | 366 | ||
367 | /* The Y coordinates have to work on even 8 pixel rows */ | ||
314 | ymax = (y + height-1) >> 3; | 368 | ymax = (y + height-1) >> 3; |
315 | y >>= 3; | 369 | y >>= 3; |
316 | 370 | ||
317 | /* Copy specified rectange bitmap to hardware */ | 371 | /* Copy specified rectange bitmap to hardware */ |
318 | for (; y <= ymax; y++) | 372 | internal_update_rect(x, y, width, ymax); |
319 | { | ||
320 | lcd_write_command (LCD_CNTL_PAGE | (y & 0xf)); | ||
321 | lcd_write_command (LCD_CNTL_HIGHCOL | (((x+offset) >> 4) & 0xf)); | ||
322 | lcd_write_command (LCD_CNTL_LOWCOL | ((x+offset) & 0xf)); | ||
323 | |||
324 | lcd_write_data (FBADDR(x,y), width); | ||
325 | } | ||
326 | } | 373 | } |