diff options
-rw-r--r-- | firmware/drivers/lcd-recorder.c | 48 | ||||
-rw-r--r-- | firmware/drivers/lcd.c | 113 | ||||
-rw-r--r-- | firmware/export/lcd.h | 4 |
3 files changed, 158 insertions, 7 deletions
diff --git a/firmware/drivers/lcd-recorder.c b/firmware/drivers/lcd-recorder.c index a409c9fc63..0a95e82c3d 100644 --- a/firmware/drivers/lcd-recorder.c +++ b/firmware/drivers/lcd-recorder.c | |||
@@ -103,10 +103,8 @@ static struct scrollinfo scroll[SCROLLABLE_LINES]; | |||
103 | static int xmargin = 0; | 103 | static int xmargin = 0; |
104 | static int ymargin = 0; | 104 | static int ymargin = 0; |
105 | static int curfont = FONT_SYSFIXED; | 105 | static int curfont = FONT_SYSFIXED; |
106 | static int xoffset = 0; /* needed for flip */ | ||
106 | 107 | ||
107 | #ifndef SIMULATOR | ||
108 | static | ||
109 | #endif | ||
110 | unsigned char lcd_framebuffer[LCD_WIDTH][LCD_HEIGHT/8]; | 108 | unsigned char lcd_framebuffer[LCD_WIDTH][LCD_HEIGHT/8]; |
111 | 109 | ||
112 | /* All zeros and ones bitmaps for area filling */ | 110 | /* All zeros and ones bitmaps for area filling */ |
@@ -163,6 +161,25 @@ void lcd_init (void) | |||
163 | sizeof(scroll_stack), scroll_name); | 161 | sizeof(scroll_stack), scroll_name); |
164 | } | 162 | } |
165 | 163 | ||
164 | |||
165 | /* Performance function that works with an external buffer | ||
166 | note that y and height are in 8-pixel units! */ | ||
167 | void lcd_blit (unsigned char* p_data, int x, int y, int width, int height, int stride) __attribute__ ((section (".icode"))); | ||
168 | void lcd_blit (unsigned char* p_data, int x, int y, int width, int height, int stride) | ||
169 | { | ||
170 | /* Copy display bitmap to hardware */ | ||
171 | while (height--) | ||
172 | { | ||
173 | lcd_write (true, LCD_CNTL_PAGE | (y++ & 0xf)); | ||
174 | lcd_write (true, LCD_CNTL_HIGHCOL | (((x+xoffset)>>4) & 0xf)); | ||
175 | lcd_write (true, LCD_CNTL_LOWCOL | ((x+xoffset) & 0xf)); | ||
176 | |||
177 | lcd_write_data(p_data, width); | ||
178 | p_data += stride; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | |||
166 | /* | 183 | /* |
167 | * Update the display. | 184 | * Update the display. |
168 | * This must be called after all other LCD functions that change the display. | 185 | * This must be called after all other LCD functions that change the display. |
@@ -176,8 +193,8 @@ void lcd_update (void) | |||
176 | for (y = 0; y < LCD_HEIGHT/8; y++) | 193 | for (y = 0; y < LCD_HEIGHT/8; y++) |
177 | { | 194 | { |
178 | lcd_write (true, LCD_CNTL_PAGE | (y & 0xf)); | 195 | lcd_write (true, LCD_CNTL_PAGE | (y & 0xf)); |
179 | lcd_write (true, LCD_CNTL_HIGHCOL); | 196 | lcd_write (true, LCD_CNTL_HIGHCOL | ((xoffset>>4) & 0xf)); |
180 | lcd_write (true, LCD_CNTL_LOWCOL); | 197 | lcd_write (true, LCD_CNTL_LOWCOL | (xoffset & 0xf)); |
181 | 198 | ||
182 | for (x = 0; x < LCD_WIDTH; x++) | 199 | for (x = 0; x < LCD_WIDTH; x++) |
183 | lcd_write (false, lcd_framebuffer[x][y]); | 200 | lcd_write (false, lcd_framebuffer[x][y]); |
@@ -210,8 +227,8 @@ void lcd_update_rect (int x_start, int y, | |||
210 | for (; y <= ymax; y++) | 227 | for (; y <= ymax; y++) |
211 | { | 228 | { |
212 | lcd_write (true, LCD_CNTL_PAGE | (y & 0xf)); | 229 | lcd_write (true, LCD_CNTL_PAGE | (y & 0xf)); |
213 | lcd_write (true, LCD_CNTL_HIGHCOL | ((x_start>>4) & 0xf)); | 230 | lcd_write (true, LCD_CNTL_HIGHCOL | (((x_start+xoffset)>>4) & 0xf)); |
214 | lcd_write (true, LCD_CNTL_LOWCOL | (x_start & 0xf)); | 231 | lcd_write (true, LCD_CNTL_LOWCOL | ((x_start+xoffset) & 0xf)); |
215 | 232 | ||
216 | for (x = x_start; x < xmax; x++) | 233 | for (x = x_start; x < xmax; x++) |
217 | lcd_write (false, lcd_framebuffer[x][y]); | 234 | lcd_write (false, lcd_framebuffer[x][y]); |
@@ -232,6 +249,23 @@ void lcd_set_invert_display(bool yesno) | |||
232 | lcd_write(true, LCD_SET_NORMAL_DISPLAY); | 249 | lcd_write(true, LCD_SET_NORMAL_DISPLAY); |
233 | } | 250 | } |
234 | 251 | ||
252 | /* turn the display upside down (call lcd_update() afterwards) */ | ||
253 | void lcd_set_flip(bool yesno) | ||
254 | { | ||
255 | if (yesno) | ||
256 | { | ||
257 | lcd_write(true, LCD_SET_SEGMENT_REMAP); | ||
258 | lcd_write(true, LCD_SET_COM_OUTPUT_SCAN_DIRECTION); | ||
259 | xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 112 we have */ | ||
260 | } | ||
261 | else | ||
262 | { | ||
263 | lcd_write(true, LCD_SET_SEGMENT_REMAP | 0x01); | ||
264 | lcd_write(true, LCD_SET_COM_OUTPUT_SCAN_DIRECTION | 0x08); | ||
265 | xoffset = 0; | ||
266 | } | ||
267 | } | ||
268 | |||
235 | /** | 269 | /** |
236 | * Rolls up the lcd display by the specified amount of lines. | 270 | * Rolls up the lcd display by the specified amount of lines. |
237 | * Lines that are rolled out over the top of the screen are | 271 | * Lines that are rolled out over the top of the screen are |
diff --git a/firmware/drivers/lcd.c b/firmware/drivers/lcd.c index 3f6acee301..aaa93b3385 100644 --- a/firmware/drivers/lcd.c +++ b/firmware/drivers/lcd.c | |||
@@ -173,3 +173,116 @@ void lcd_write(bool command, int byte) | |||
173 | : /* %0 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC), | 173 | : /* %0 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC), |
174 | /* %1 */ "z"(LCDR)); | 174 | /* %1 */ "z"(LCDR)); |
175 | } | 175 | } |
176 | |||
177 | |||
178 | /* A high performance function to write data to the display, | ||
179 | one or multiple bytes. | ||
180 | Ultimately, all calls to lcd_write(false, xxx) should be substituted by | ||
181 | this, it will be most efficient if the LCD buffer is tilted to have the | ||
182 | X row as consecutive bytes, so we can write a whole row */ | ||
183 | /* FixMe: somehow the red LED is affected by this, although I don't touch | ||
184 | any other bit. Therefore not used yet, except for lcd_blit() */ | ||
185 | void lcd_write_data(unsigned char* p_bytes, int count) __attribute__ ((section (".icode"))); | ||
186 | void lcd_write_data(unsigned char* p_bytes, int count) | ||
187 | { | ||
188 | do | ||
189 | { | ||
190 | unsigned byte; | ||
191 | unsigned sda1; /* precalculated SC=low,SD=1 */ | ||
192 | unsigned clk0sda0; /* precalculated SC and SD low */ | ||
193 | |||
194 | cli(); /* make port modifications atomic */ | ||
195 | |||
196 | /* precalculate the values for later bit toggling, init data write */ | ||
197 | asm ( | ||
198 | "mov.b @%2,%0\n" /* sda1 = PBDRL */ | ||
199 | "or %4,%0\n" /* sda1 |= LCD_DS | LCD_SD DS and SD high, */ | ||
200 | "and %3,%0\n" /* sda1 &= ~(LCD_CS | LCD_SC) CS and SC low */ | ||
201 | "mov %0,%1\n" /* sda1 -> clk0sda0 */ | ||
202 | "and %5,%1\n" /* clk0sda0 &= ~LCD_SD both low */ | ||
203 | "mov.b %1,@%2\n" /* PBDRL = clk0sda0 */ | ||
204 | : // outputs | ||
205 | /* %0 */ "=r"(sda1), | ||
206 | /* %1 */ "=r"(clk0sda0) | ||
207 | : // inputs | ||
208 | /* %2 */ "r"(LCDR), | ||
209 | /* %3 */ "r"(~(LCD_CS | LCD_SC)), | ||
210 | /* %4 */ "r"(LCD_DS | LCD_SD), | ||
211 | /* %5 */ "r"(~LCD_SD) | ||
212 | ); | ||
213 | |||
214 | byte = *p_bytes++ << 24; /* fetch to MSB position */ | ||
215 | |||
216 | /* unrolled loop to serialize the byte */ | ||
217 | asm ( | ||
218 | "mov %4,r0\n" /* we need &PBDRL in r0 for "or.b x,@(r0,gbr)" */ | ||
219 | |||
220 | "shll %0\n" /* shift the MSB into carry */ | ||
221 | "bf 1f\n" | ||
222 | "mov.b %1,@%4\n" /* if it was a "1": set SD high, SC low still */ | ||
223 | "1: \n" | ||
224 | "or.b %2, @(r0,gbr)\n" /* rise SC (independent of SD level) */ | ||
225 | "shll %0\n" /* shift for next round, now for longer hold time */ | ||
226 | "mov.b %3,@%4\n" /* SC and SD low again */ | ||
227 | |||
228 | "bf 1f\n" | ||
229 | "mov.b %1,@%4\n" | ||
230 | "1: \n" | ||
231 | "or.b %2, @(r0,gbr)\n" | ||
232 | "shll %0\n" | ||
233 | "mov.b %3,@%4\n" | ||
234 | |||
235 | "bf 1f\n" | ||
236 | "mov.b %1,@%4\n" | ||
237 | "1: \n" | ||
238 | "or.b %2, @(r0,gbr)\n" | ||
239 | "shll %0\n" | ||
240 | "mov.b %3,@%4\n" | ||
241 | |||
242 | "bf 1f\n" | ||
243 | "mov.b %1,@%4\n" | ||
244 | "1: \n" | ||
245 | "or.b %2, @(r0,gbr)\n" | ||
246 | "shll %0\n" | ||
247 | "mov.b %3,@%4\n" | ||
248 | |||
249 | "bf 1f\n" | ||
250 | "mov.b %1,@%4\n" | ||
251 | "1: \n" | ||
252 | "or.b %2, @(r0,gbr)\n" | ||
253 | "shll %0\n" | ||
254 | "mov.b %3,@%4\n" | ||
255 | |||
256 | "bf 1f\n" | ||
257 | "mov.b %1,@%4\n" | ||
258 | "1: \n" | ||
259 | "or.b %2, @(r0,gbr)\n" | ||
260 | "shll %0\n" | ||
261 | "mov.b %3,@%4\n" | ||
262 | |||
263 | "bf 1f\n" | ||
264 | "mov.b %1,@%4\n" | ||
265 | "1: \n" | ||
266 | "or.b %2, @(r0,gbr)\n" | ||
267 | "shll %0\n" | ||
268 | "mov.b %3,@%4\n" | ||
269 | |||
270 | "bf 1f\n" | ||
271 | "mov.b %1,@%4\n" | ||
272 | "1: \n" | ||
273 | "or.b %2, @(r0,gbr)\n" | ||
274 | |||
275 | "or.b %5, @(r0,gbr)\n" /* restore port */ | ||
276 | : | ||
277 | : /* %0 */ "r"(byte), | ||
278 | /* %1 */ "r"(sda1), | ||
279 | /* %2 */ "I"(LCD_SC), | ||
280 | /* %3 */ "r"(clk0sda0), | ||
281 | /* %4 */ "r"(LCDR), | ||
282 | /* %5 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC) | ||
283 | ); | ||
284 | |||
285 | sti(); | ||
286 | |||
287 | } while (--count); /* tail loop is faster */ | ||
288 | } | ||
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h index c4632a0282..c81d2de2b3 100644 --- a/firmware/export/lcd.h +++ b/firmware/export/lcd.h | |||
@@ -44,10 +44,13 @@ extern void lcd_scroll_speed( int speed ); | |||
44 | extern void lcd_scroll_delay( int ms ); | 44 | extern void lcd_scroll_delay( int ms ); |
45 | extern void lcd_set_contrast(int val); | 45 | extern void lcd_set_contrast(int val); |
46 | extern void lcd_write( bool command, int byte ); | 46 | extern void lcd_write( bool command, int byte ); |
47 | extern void lcd_write_data( unsigned char* p_bytes, int count ); | ||
47 | extern int lcd_default_contrast(void); | 48 | extern int lcd_default_contrast(void); |
48 | 49 | ||
49 | #if defined(SIMULATOR) || defined(HAVE_LCD_BITMAP) | 50 | #if defined(SIMULATOR) || defined(HAVE_LCD_BITMAP) |
50 | extern void lcd_update(void); | 51 | extern void lcd_update(void); |
52 | /* performance function */ | ||
53 | extern void lcd_blit (unsigned char* p_data, int x, int y, int width, int height, int stride); | ||
51 | 54 | ||
52 | /* update a fraction of the screen */ | 55 | /* update a fraction of the screen */ |
53 | extern void lcd_update_rect(int x, int y, int width, int height); | 56 | extern void lcd_update_rect(int x, int y, int width, int height); |
@@ -134,6 +137,7 @@ extern void lcd_clearpixel(int x, int y); | |||
134 | extern void lcd_invertpixel(int x, int y); | 137 | extern void lcd_invertpixel(int x, int y); |
135 | extern void lcd_roll(int pixels); | 138 | extern void lcd_roll(int pixels); |
136 | extern void lcd_set_invert_display(bool yesno); | 139 | extern void lcd_set_invert_display(bool yesno); |
140 | extern void lcd_set_flip(bool yesno); | ||
137 | extern void lcd_bidir_scroll(int threshold); | 141 | extern void lcd_bidir_scroll(int threshold); |
138 | extern void lcd_scroll_step(int pixels); | 142 | extern void lcd_scroll_step(int pixels); |
139 | extern void lcd_setfont(int font); | 143 | extern void lcd_setfont(int font); |