2 Copyright 2016 Fred Sundvik <fsundvik@gmail.com>
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #define GDISP_DRIVER_VMT GDISPVMT_IS31FL3731C_ERGODOX
23 #include "drivers/gdisp/IS31FL3731C/gdisp_lld_config.h"
24 #include "src/gdisp/gdisp_driver.h"
26 #include "board_IS31FL3731C.h"
28 /*===========================================================================*/
29 /* Driver local definitions. */
30 /*===========================================================================*/
32 #ifndef GDISP_SCREEN_HEIGHT
33 #define GDISP_SCREEN_HEIGHT 9
35 #ifndef GDISP_SCREEN_WIDTH
36 #define GDISP_SCREEN_WIDTH 16
38 #ifndef GDISP_INITIAL_CONTRAST
39 #define GDISP_INITIAL_CONTRAST 0
41 #ifndef GDISP_INITIAL_BACKLIGHT
42 #define GDISP_INITIAL_BACKLIGHT 100
45 #define GDISP_FLG_NEEDFLUSH (GDISP_FLG_DRIVER<<0)
47 #define IS31_ADDR_DEFAULT 0x74
49 #define IS31_REG_CONFIG 0x00
51 #define IS31_REG_CONFIG_PICTUREMODE 0x00
52 #define IS31_REG_CONFIG_AUTOPLAYMODE 0x08
53 #define IS31_REG_CONFIG_AUDIOPLAYMODE 0x18
54 // D2:D0 bits are starting frame for autoplay mode
56 #define IS31_REG_PICTDISP 0x01 // D2:D0 frame select for picture mode
58 #define IS31_REG_AUTOPLAYCTRL1 0x02
59 // D6:D4 number of loops (000=infty)
60 // D2:D0 number of frames to be used
62 #define IS31_REG_AUTOPLAYCTRL2 0x03 // D5:D0 delay time (*11ms)
64 #define IS31_REG_DISPLAYOPT 0x05
65 #define IS31_REG_DISPLAYOPT_INTENSITY_SAME 0x20 // same intensity for all frames
66 #define IS31_REG_DISPLAYOPT_BLINK_ENABLE 0x8
67 // D2:D0 bits blink period time (*0.27s)
69 #define IS31_REG_AUDIOSYNC 0x06
70 #define IS31_REG_AUDIOSYNC_ENABLE 0x1
72 #define IS31_REG_FRAMESTATE 0x07
74 #define IS31_REG_BREATHCTRL1 0x08
75 // D6:D4 fade out time (26ms*2^i)
76 // D2:D0 fade in time (26ms*2^i)
78 #define IS31_REG_BREATHCTRL2 0x09
79 #define IS31_REG_BREATHCTRL2_ENABLE 0x10
80 // D2:D0 extinguish time (3.5ms*2^i)
82 #define IS31_REG_SHUTDOWN 0x0A
83 #define IS31_REG_SHUTDOWN_OFF 0x0
84 #define IS31_REG_SHUTDOWN_ON 0x1
86 #define IS31_REG_AGCCTRL 0x0B
87 #define IS31_REG_ADCRATE 0x0C
89 #define IS31_COMMANDREGISTER 0xFD
90 #define IS31_FUNCTIONREG 0x0B // helpfully called 'page nine'
91 #define IS31_FUNCTIONREG_SIZE 0xD
93 #define IS31_FRAME_SIZE 0xB4
95 #define IS31_PWM_REG 0x24
96 #define IS31_PWM_SIZE 0x90
98 #define IS31_LED_MASK_SIZE 0x12
99 #define IS31_SCREEN_WIDTH 16
103 //Generated by http://jared.geek.nz/2013/feb/linear-led-pwm
104 const unsigned char cie[256] = {
105 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
106 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
107 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
108 3, 4, 4, 4, 4, 4, 4, 5, 5, 5,
109 5, 5, 6, 6, 6, 6, 6, 7, 7, 7,
110 7, 8, 8, 8, 8, 9, 9, 9, 10, 10,
111 10, 10, 11, 11, 11, 12, 12, 12, 13, 13,
112 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
113 17, 18, 18, 19, 19, 20, 20, 21, 21, 22,
114 22, 23, 23, 24, 24, 25, 25, 26, 26, 27,
115 28, 28, 29, 29, 30, 31, 31, 32, 32, 33,
116 34, 34, 35, 36, 37, 37, 38, 39, 39, 40,
117 41, 42, 43, 43, 44, 45, 46, 47, 47, 48,
118 49, 50, 51, 52, 53, 54, 54, 55, 56, 57,
119 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
120 68, 70, 71, 72, 73, 74, 75, 76, 77, 79,
121 80, 81, 82, 83, 85, 86, 87, 88, 90, 91,
122 92, 94, 95, 96, 98, 99, 100, 102, 103, 105,
123 106, 108, 109, 110, 112, 113, 115, 116, 118, 120,
124 121, 123, 124, 126, 128, 129, 131, 132, 134, 136,
125 138, 139, 141, 143, 145, 146, 148, 150, 152, 154,
126 155, 157, 159, 161, 163, 165, 167, 169, 171, 173,
127 175, 177, 179, 181, 183, 185, 187, 189, 191, 193,
128 196, 198, 200, 202, 204, 207, 209, 211, 214, 216,
129 218, 220, 223, 225, 228, 230, 232, 235, 237, 240,
130 242, 245, 247, 250, 252, 255,
134 /*===========================================================================*/
135 /* Driver local functions. */
136 /*===========================================================================*/
139 uint8_t write_buffer_offset;
140 uint8_t write_buffer[IS31_FRAME_SIZE];
141 uint8_t frame_buffer[GDISP_SCREEN_HEIGHT * GDISP_SCREEN_WIDTH];
143 }__attribute__((__packed__)) PrivData;
145 // Some common routines and macros
146 #define PRIV(g) ((PrivData*)g->priv)
148 /*===========================================================================*/
149 /* Driver exported functions. */
150 /*===========================================================================*/
152 static GFXINLINE void write_page(GDisplay* g, uint8_t page) {
153 uint8_t tx[2] __attribute__((aligned(2)));
154 tx[0] = IS31_COMMANDREGISTER;
156 write_data(g, tx, 2);
159 static GFXINLINE void write_register(GDisplay* g, uint8_t page, uint8_t reg, uint8_t data) {
160 uint8_t tx[2] __attribute__((aligned(2)));
164 write_data(g, tx, 2);
167 static GFXINLINE void write_ram(GDisplay *g, uint8_t page, uint16_t offset, uint16_t length) {
168 PRIV(g)->write_buffer_offset = offset;
170 write_data(g, (uint8_t*)PRIV(g), length + 1);
173 LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
174 // The private area is the display surface.
175 g->priv = gfxAlloc(sizeof(PrivData));
176 __builtin_memset(PRIV(g), 0, sizeof(PrivData));
179 // Initialise the board interface
181 gfxSleepMilliseconds(10);
183 // zero function page, all registers (assuming full_page is all zeroes)
184 write_ram(g, IS31_FUNCTIONREG, 0, IS31_FUNCTIONREG_SIZE);
185 set_hardware_shutdown(g, false);
186 gfxSleepMilliseconds(10);
188 write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF);
189 gfxSleepMilliseconds(10);
190 // zero function page, all registers
191 write_ram(g, IS31_FUNCTIONREG, 0, IS31_FUNCTIONREG_SIZE);
192 gfxSleepMilliseconds(10);
195 // zero all LED registers on all 8 pages, and enable the mask
196 __builtin_memcpy(PRIV(g)->write_buffer, get_led_mask(g), IS31_LED_MASK_SIZE);
197 for(uint8_t i=0; i<8; i++) {
198 write_ram(g, i, 0, IS31_FRAME_SIZE);
199 gfxSleepMilliseconds(1);
202 // software shutdown disable (i.e. turn stuff on)
203 write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
204 gfxSleepMilliseconds(10);
209 /* Initialise the GDISP structure */
210 g->g.Width = GDISP_SCREEN_WIDTH;
211 g->g.Height = GDISP_SCREEN_HEIGHT;
212 g->g.Orientation = GDISP_ROTATE_0;
213 g->g.Powermode = powerOn;
214 g->g.Backlight = GDISP_INITIAL_BACKLIGHT;
215 g->g.Contrast = GDISP_INITIAL_CONTRAST;
219 #if GDISP_HARDWARE_FLUSH
220 LLDSPEC void gdisp_lld_flush(GDisplay *g) {
221 // Don't flush if we don't need it.
222 if (!(g->flags & GDISP_FLG_NEEDFLUSH))
227 // TODO: some smarter algorithm for this
228 // We should run only one physical page at a time
229 // This way we don't need to send so much data, and
230 // we could use slightly less memory
231 uint8_t* src = PRIV(g)->frame_buffer;
232 for (int y=0;y<GDISP_SCREEN_HEIGHT;y++) {
233 for (int x=0;x<GDISP_SCREEN_WIDTH;x++) {
234 PRIV(g)->write_buffer[get_led_address(g, x, y)]=cie[*src];
238 write_ram(g, PRIV(g)->page, IS31_PWM_REG, IS31_PWM_SIZE);
239 gfxSleepMilliseconds(1);
240 write_register(g, IS31_FUNCTIONREG, IS31_REG_PICTDISP, PRIV(g)->page);
242 g->flags &= ~GDISP_FLG_NEEDFLUSH;
246 #if GDISP_HARDWARE_DRAWPIXEL
247 LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) {
250 switch(g->g.Orientation) {
256 case GDISP_ROTATE_180:
257 x = GDISP_SCREEN_WIDTH-1 - g->p.x;
261 PRIV(g)->frame_buffer[y * GDISP_SCREEN_WIDTH + x] = gdispColor2Native(g->p.color);
262 g->flags |= GDISP_FLG_NEEDFLUSH;
266 #if GDISP_HARDWARE_PIXELREAD
267 LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay *g) {
270 switch(g->g.Orientation) {
276 case GDISP_ROTATE_180:
277 x = GDISP_SCREEN_WIDTH-1 - g->p.x;
281 return gdispNative2Color(PRIV(g)->frame_buffer[y * GDISP_SCREEN_WIDTH + x]);
285 #if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
286 LLDSPEC void gdisp_lld_control(GDisplay *g) {
288 case GDISP_CONTROL_POWER:
289 if (g->g.Powermode == (powermode_t)g->p.ptr)
291 switch((powermode_t)g->p.ptr) {
295 write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF);
298 write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
303 g->g.Powermode = (powermode_t)g->p.ptr;
306 case GDISP_CONTROL_ORIENTATION:
307 if (g->g.Orientation == (orientation_t)g->p.ptr)
309 switch((orientation_t)g->p.ptr) {
310 /* Rotation is handled by the drawing routines */
312 case GDISP_ROTATE_180:
313 g->g.Height = GDISP_SCREEN_HEIGHT;
314 g->g.Width = GDISP_SCREEN_WIDTH;
316 case GDISP_ROTATE_90:
317 case GDISP_ROTATE_270:
318 g->g.Height = GDISP_SCREEN_WIDTH;
319 g->g.Width = GDISP_SCREEN_HEIGHT;
324 g->g.Orientation = (orientation_t)g->p.ptr;
327 case GDISP_CONTROL_CONTRAST:
331 #endif // GDISP_NEED_CONTROL
333 #endif // GFX_USE_GDISP