OpenCores
URL https://opencores.org/ocsvn/openarty/openarty/trunk

Subversion Repositories openarty

[/] [openarty/] [trunk/] [sw/] [board/] [oledtest.c] - Blame information for rev 36

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 35 dgisselq
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Filename:    oledtest.c
4
//
5
// Project:     OpenArty, an entirely open SoC based upon the Arty platform
6
//
7
// Purpose:     To see whether or not we can display an image onto the OLEDrgb
8
//              PMod.  This program runs on the ZipCPU internal to the FPGA,
9
//      and commands the OLEDrgb to power on, reset, initialize, and then to
10
//      display an alternating pair of images onto the display.
11
//
12
//
13
// Creator:     Dan Gisselquist, Ph.D.
14
//              Gisselquist Technology, LLC
15
//
16
////////////////////////////////////////////////////////////////////////////////
17
//
18
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
19
//
20
// This program is free software (firmware): you can redistribute it and/or
21
// modify it under the terms of  the GNU General Public License as published
22
// by the Free Software Foundation, either version 3 of the License, or (at
23
// your option) any later version.
24
//
25
// This program is distributed in the hope that it will be useful, but WITHOUT
26
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
27
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
28
// for more details.
29
//
30
// You should have received a copy of the GNU General Public License along
31
// with this program.  (It's in the $(ROOT)/doc directory, run make with no
32
// target there if the PDF file isn't present.)  If not, see
33
// <http://www.gnu.org/licenses/> for a copy.
34
//
35
// License:     GPL, v3, as defined and found on www.gnu.org,
36
//              http://www.gnu.org/licenses/gpl.html
37
//
38
//
39
////////////////////////////////////////////////////////////////////////////////
40
//
41
//
42
 
43 36 dgisselq
#include "zipsys.h"
44 34 dgisselq
#include "artyboard.h"
45
 
46
void    idle_task(void) {
47
        while(1)
48
                zip_idle();
49
}
50
 
51
extern int      splash[], mug[];
52
 
53
#define OLED_PMODEN             0x0010001
54
#define OLED_PMODEN_OFF         0x0010000
55
#define OLED_IOPWR              OLED_PMODEN
56
#define OLED_VCCEN              0x0020002
57
#define OLED_VCC_DISABLE        0x0020000
58
#define OLED_RESET              0x0040000
59
#define OLED_RESET_CLR          0x0040004
60 35 dgisselq
#define OLED_FULLPOWER          (OLED_PMODEN|OLED_VCCEN|OLED_RESET_CLR)
61 34 dgisselq
#define OLED_POWER_DOWN         (OLED_PMODEN_OFF|OLED_VCC_DISABLE)
62
#define OLED_BUSY               1
63
#define OLED_DISPLAYON          0x0af
64
 
65
 
66
#define MICROSECOND             (CLOCKFREQ_HZ/1000000)
67
 
68
#define OLED_DISPLAY_OFF
69
 
70
 
71 35 dgisselq
 
72
/*
73
 * timer_delay()
74
 *
75
 * Using the timer peripheral, delay by a given number of counts.  We'll sleep
76
 * during this delayed time, and wait on an interrupt to wake us.  As a result,
77
 * this will only work from supervisor mode.
78
 */
79 34 dgisselq
void    timer_delay(int counts) {
80
        // Clear the PIC.  We want to exit from here on timer counts alone
81
        zip->pic = CLEARPIC;
82
 
83
        if (counts > 10) {
84
                // Set our timer to count down the given number of counts
85
                zip->tma = counts;
86
                zip->pic = EINT(SYSINT_TMA);
87
                zip_rtu();
88
                zip->pic = CLEARPIC;
89
        } // else anything less has likely already passed
90
}
91
 
92 36 dgisselq
void    wait_on_interrupt(int mask) {
93
        // Clear our interrupt only, but disable all others
94
        zip->pic = DALLPIC|mask;
95
        zip->pic = EINT(mask);
96
        zip_rtu();
97
        zip->pic = DINT(mask)|mask;
98
}
99 35 dgisselq
 
100 34 dgisselq
void oled_clear(void);
101
 
102 35 dgisselq
/*
103
 * The following outlines a series of commands to send to the OLEDrgb as part
104
 * of an initialization sequence.  The sequence itself was taken from the
105
 * MPIDE demo.  Single byte numbers in the sequence are just that: commands
106
 * to send 8-bit values across the port.  17-bit values with the 16th bit
107
 * set send two bytes (bits 15-0) to the port.  The OLEDrgb treats these as
108
 * commands (first byte) with an argument (the second byte).
109
 */
110
const int       init_sequence[] = {
111
        //  Unlock commands
112
        0x01fd12,
113
        //  Display off
114
        0x0ae,
115
        //  Set remap and data format
116
        0x01a072,
117
        //  Set the start line
118
        0x01a100,
119
        //  Set the display offset
120
        0x01a200,
121
        //  Normal display mode
122
        0x0000a4,
123
        //  Set multiplex ratio
124
        0x01a83f,
125
        //  Set master configuration:
126
        //      Use External VCC
127
        0x01ad8e,
128
        //  Disable power save mode
129
        0x01b00b,
130
        //  Set phase length
131
        0x01b131,
132
        //  Set clock divide
133
        0x01b3f0,
134
        //  Set Second Pre-change Speed For ColorA
135
        0x018a64,
136
        //  5l) Set Set Second Pre-charge Speed of Color B
137
        0x018b78,
138
        //  5m) Set Second Pre-charge Speed of Color C
139
        0x018c64,
140
        //  5n) Set Pre-Charge Voltage
141
        0x01bb3a,
142
        //  50) Set VCOMH Deselect Level
143
        0x01be3e,
144
        //  5p) Set Master Current
145
        0x018706,
146
        //  5q) Set Contrast for Color A
147
        0x018191,
148
        //  5r) Set Contrast for Color B
149
        0x018250,
150
        //  5s) Set Contrast for Color C
151
        0x01837D,
152
        //  disable scrolling
153
        0x02e
154
};
155
 
156
/*
157
 * oled_init()
158
 *
159
 * This initializes and starts up the OLED.  While it sounds important, really
160
 * the majority of the work necessary to do this is really captured in the
161
 * init_sequence[] above.  This just primarily works to send that sequence to
162
 * the PMod.
163
 *
164
 * We should be able to do all of this with the DMA: wait for an OLEDrgb not
165
 * busy interrupt, send one value, repeat until done.  For now ... we'll just
166
 * leave that as an advanced exercise.
167
 *
168
 */
169 34 dgisselq
void    oled_init(void) {
170
        int     i;
171
 
172
        for(i=0; i<sizeof(init_sequence); i++) {
173
                while(sys->io_oled.o_ctrl & OLED_BUSY)
174
                        ;
175
                sys->io_oled.o_ctrl = init_sequence[i];
176
        }
177
 
178
        oled_clear();
179
 
180 35 dgisselq
        // Wait 5ms
181
        timer_delay(CLOCKFREQ_HZ/200);
182 34 dgisselq
 
183
        // Turn on VCC and wait 100ms
184
        sys->io_oled.o_data = OLED_VCCEN;
185
        // Wait 100 ms
186
        timer_delay(CLOCKFREQ_HZ/10);
187
 
188
        // Send Display On command
189 35 dgisselq
        sys->io_oled.o_ctrl = OLED_DISPLAYON;
190
}
191 34 dgisselq
 
192 35 dgisselq
/*
193
 * oled_clear()
194
 *
195
 * This should be fairly self-explanatory: it clears (sets to black) all of the
196
 * graphics memory on the OLED.
197
 *
198
 * What may not be self-explanatory is that to send any more than three bytes
199
 * using our interface you need to send the first three bytes in o_ctrl,
200
 * and set the next bytes (up to four) in o_a.  (Another four can be placed in
201
 * o_b.)  When the word is written to o_ctrl, the command goes over the wire
202
 * and o_a and o_b are reset.  Hence we set o_a first, then o_ctrl.  Further,
203
 * the '4' in the top nibble of o_ctrl indicates that we are sending 5-bytes
204
 * (4+5), so the OLEDrgb should see: 0x25,0x00,0x00,0x5f,0x3f.
205
 */
206 34 dgisselq
void    oled_clear(void) {
207
        while(sys->io_oled.o_ctrl & OLED_BUSY)
208
                ;
209
        sys->io_oled.o_a = 0x5f3f0000;
210
        sys->io_oled.o_ctrl = 0x40250000;
211
        while(sys->io_oled.o_ctrl & OLED_BUSY)
212
                ;
213
}
214
 
215 35 dgisselq
/*
216
 * oled_fill
217
 *
218
 * Similar to oled_clear, this fills a rectangle with a given pixel value.
219
 */
220 34 dgisselq
void    oled_fill(int c, int r, int w, int h, int pix) {
221
        int     ctrl; // We'll send this value out the control/command port
222
 
223
        if (c > 95) c = 95;
224
        if (c <  0) c =  0;
225
        if (r > 63) r = 63;
226
        if (r <  0) r =  0;
227
        if (w <  0) w = 0;
228
        if (h <  0) h = 0;
229
        if (c+w > 95) w = 95-c;
230
        if (r+h > 63) h = 63-r;
231
 
232
        // Enable a rectangle to fill
233
        while(sys->io_oled.o_ctrl & OLED_BUSY)
234
                ;
235
        sys->io_oled.o_ctrl = 0x12601;
236
 
237 35 dgisselq
        //
238 34 dgisselq
        // Now, let's build the actual copy command
239 35 dgisselq
        //
240
        // This is an 11 byte command, consisting of the 0x22, followed by
241
        // the top left column and row of our rectangle, and then the bottom
242
        // right column and row.  That's the first five bytes.  The next six
243
        // bytes are the color of the border and the color of the fill.
244
        // Here, we set both colors to be identical.
245
        // 
246 34 dgisselq
        ctrl = 0xa0220000 | ((c&0x07f)<<8) | (r&0x03f);
247
        sys->io_oled.o_a = (((c+w)&0x07f)<<24) | (((r+h)&0x03f)<<16);
248
        sys->io_oled.o_a|= ((pix >> 11) & 0x01f)<< 9;
249
        sys->io_oled.o_a|= ((pix >>  5) & 0x03f)    ;
250 35 dgisselq
        sys->io_oled.o_b = ((pix      ) & 0x01f)<<25;
251
        sys->io_oled.o_b|= ((pix >> 11) & 0x01f)<<17;
252 34 dgisselq
        sys->io_oled.o_b|= ((pix >>  5) & 0x03f)<< 8;
253
        sys->io_oled.o_b|= ((pix      ) & 0x01f)<< 1;
254
 
255 35 dgisselq
        // Make certain we had finished with the port (we should've by now)
256 34 dgisselq
        while(sys->io_oled.o_ctrl & OLED_BUSY)
257
                ;
258
 
259 35 dgisselq
        // and send our new command.  Note that o_a and o_b were already set
260
        // ahead of time, and are only now being sent together with this
261
        // command.
262 34 dgisselq
        sys->io_oled.o_ctrl = ctrl;
263
 
264
        // To be nice to whatever routine follows, we'll wait 'til the port
265
        // is clear again.
266
        while(sys->io_oled.o_ctrl & OLED_BUSY)
267
                ;
268
}
269
 
270 36 dgisselq
void    oled_show_image(int *img) {
271
#define USE_DMA
272
#ifdef  USE_DMA
273
                zip->dma.ctrl = DMACLEAR;
274
                zip->dma.rd = img;
275
                zip->dma.wr = &sys->io_oled.o_data;
276
                zip->dma.len= 6144;
277
                zip->dma.ctrl = DMAONEATATIME|DMA_CONSTDST|DMA_OLED;
278
                // timer_delay(256);
279
                // zip_halt();
280
#else
281
                for(int i=0; i<6144; i++) {
282
                        while(sys->io_oled.o_ctrl & OLED_BUSY)
283
                                ;
284
                        sys->io_oled.o_data = img[i];
285
                }
286
#endif
287
}
288 35 dgisselq
 
289
/*
290
 * entry()
291
 *
292
 * In all (current) ZipCPU programs, the programs start with an entry()
293
 * function that takes no arguments.  The actual bootup entry can be found
294 36 dgisselq
 * in the bootstrap.c file, but that calls us here.
295 35 dgisselq
 *
296
 */
297 34 dgisselq
void    entry(void) {
298 35 dgisselq
 
299
        // Since we'll be returning to userspace via zip_rtu() in order to
300
        // wait for an interrupt, let's at least place a valid program into
301
        // userspace to run: the idle_task.
302 34 dgisselq
        unsigned        user_regs[16];
303
        for(int i=0; i<15; i++)
304
                user_regs[i] = 0;
305
        user_regs[15] = (unsigned int)idle_task;
306
        zip_restore_context(user_regs);
307
 
308 35 dgisselq
        // Clear the PIC.  We'll come back and use it later.  We clear it here
309
        // partly in order to avoid a race condition later.
310 34 dgisselq
        zip->pic = CLEARPIC;
311
 
312 35 dgisselq
        // Wait till we've had power for at least a quarter second
313
        if (0) {
314
                // While this appears to do the task quite nicely, it leaves
315
                // the master_ce line high within the CPU, and so it generates
316
                // a whole lot of debug information in our Verilator simulation,
317
                // busmaster_tb.
318 34 dgisselq
                int pwrcount = sys->io_pwrcount;
319
                do {
320
                        pwrcount = sys->io_pwrcount;
321
                } while((pwrcount>0)&&(pwrcount < CLOCKFREQ_HZ/4));
322
        } else {
323 35 dgisselq
                // By using the timer and sleeping instead, the simulator can
324
                // be made to run a *lot* faster, with a *lot* less debugging
325
                // ... junk.
326 34 dgisselq
                int pwrcount = sys->io_pwrcount;
327
                if ((pwrcount > 0)&&(pwrcount < CLOCKFREQ_HZ/4)) {
328
                        pwrcount = CLOCKFREQ_HZ/4 - pwrcount;
329
                        timer_delay(pwrcount);
330
                }
331
        }
332
 
333
 
334
        // If the OLED is already powered, such as might be the case if
335
        // we rebooted but the board was still hot, shut it down
336
        if (sys->io_oled.o_data & 0x07) {
337
                sys->io_oled.o_data = OLED_VCC_DISABLE;
338
                // Wait 100 ms
339
                timer_delay(CLOCKFREQ_HZ/10);
340
                // Shutdown the entire devices power
341
                sys->io_oled.o_data = OLED_POWER_DOWN;
342
                // Wait 100 ms
343
                timer_delay(CLOCKFREQ_HZ/10);
344
 
345
                // Now let's try to restart it
346
        }
347
 
348
        // 1. Power up the OLED by applying power to VCC
349
        //      This means we need to apply power to both the VCCEN line as well
350
        //      as the PMODEN line.  We'll also set the reset line low, so the
351
        //      device starts in a reset condition.
352
        sys->io_oled.o_data = OLED_PMODEN|OLED_RESET_CLR;
353
        timer_delay(4*MICROSECOND);
354
        sys->io_oled.o_data = OLED_RESET;
355
        timer_delay(4*MICROSECOND);
356
 
357
        // 2. Send the Display OFF command
358
        //      This isn't necessary, since we already pulled RESET low.
359
        //
360
        // sys->io_oled.o_ctrl = OLED_DISPLAY_OFF;
361
        //
362
 
363
        // However, we must hold the reset line low for at least 3us, as per
364
        // the spec.  We may also need to wait another 2us after that.  Let's
365
        // hold reset low for 4us here.
366
        timer_delay(4*MICROSECOND);
367
 
368
        // Clear the reset condition.
369
        sys->io_oled.o_data = OLED_RESET_CLR;
370
        // Wait another 4us.
371
        timer_delay(4*MICROSECOND);
372
 
373
        // 3. Initialize the display to the default settings
374
        //      This just took place during the reset cycle we just completed.
375
        //
376
        oled_init();
377
 
378
        // 4. Clear screen
379 35 dgisselq
        // 5. Apply voltage
380
        // 6. Turn on display
381
        // 7. Wait 100ms
382
        //      We already stuffed this command sequence into the oled_init,
383
        //      so we're good here.
384 34 dgisselq
 
385
        while(1) {
386 35 dgisselq
                sys->io_ledctrl = 0x0f0;
387 34 dgisselq
 
388
                sys->io_oled.o_ctrl = OLED_DISPLAYON;
389
 
390
                oled_clear();
391
 
392 35 dgisselq
                // Let's start our writes at the top left of the GDDRAM
393
                // (screen memory)
394 34 dgisselq
                while(sys->io_oled.o_ctrl & OLED_BUSY)
395
                        ;
396 35 dgisselq
                sys->io_oled.o_ctrl = 0x2015005f; // Sets column min/max address
397 34 dgisselq
 
398
                while(sys->io_oled.o_ctrl & OLED_BUSY)
399
                        ;
400 35 dgisselq
                sys->io_oled.o_ctrl = 0x2075003f; // Sets row min/max address
401 34 dgisselq
 
402
                // Now ... finally ... we can send our image.
403 36 dgisselq
                oled_show_image(splash);
404
                wait_on_interrupt(SYSINT_DMAC);
405 35 dgisselq
 
406
                // Wait 25 seconds.  The LEDs are for a fun effect.
407
                sys->io_ledctrl = 0x0f1;
408 34 dgisselq
                timer_delay(CLOCKFREQ_HZ*5);
409 35 dgisselq
                sys->io_ledctrl = 0x0f3;
410 34 dgisselq
                timer_delay(CLOCKFREQ_HZ*5);
411 35 dgisselq
                sys->io_ledctrl = 0x0f7;
412 34 dgisselq
                timer_delay(CLOCKFREQ_HZ*5);
413 35 dgisselq
                sys->io_ledctrl = 0x0ff;
414 34 dgisselq
                timer_delay(CLOCKFREQ_HZ*5);
415 35 dgisselq
                sys->io_ledctrl = 0x0fe;
416 34 dgisselq
                timer_delay(CLOCKFREQ_HZ*5);
417
 
418
 
419 35 dgisselq
                // Display a second image.
420
                sys->io_ledctrl = 0x0fc;
421 36 dgisselq
                oled_show_image(mug);
422
                wait_on_interrupt(SYSINT_DMAC);
423 34 dgisselq
 
424 35 dgisselq
                // Leave this one in effect for 5 seconds only.
425
                sys->io_ledctrl = 0x0f8;
426 34 dgisselq
                timer_delay(CLOCKFREQ_HZ*5);
427
        }
428
 
429 35 dgisselq
        // We'll never get here, so this line is really just for form.
430 34 dgisselq
        zip_halt();
431
}
432
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.