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

Subversion Repositories openarty

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

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

powered by: WebSVN 2.1.0

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