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

Subversion Repositories openarty

[/] [openarty/] [trunk/] [sw/] [board/] [oledtest.c] - Diff between revs 34 and 35

Go to most recent revision | Show entire file | Details | Blame | View Log

Rev 34 Rev 35
Line 1... Line 1...
 
////////////////////////////////////////////////////////////////////////////////
 
//
 
// Filename:    oledtest.c
 
//
 
// Project:     OpenArty, an entirely open SoC based upon the Arty platform
 
//
 
// Purpose:     To see whether or not we can display an image onto the OLEDrgb
 
//              PMod.  This program runs on the ZipCPU internal to the FPGA,
 
//      and commands the OLEDrgb to power on, reset, initialize, and then to
 
//      display an alternating pair of images onto the display.
 
//
 
//
 
// Creator:     Dan Gisselquist, Ph.D.
 
//              Gisselquist Technology, LLC
 
//
 
////////////////////////////////////////////////////////////////////////////////
 
//
 
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
 
//
 
// This program is free software (firmware): you can redistribute it and/or
 
// modify it under the terms of  the GNU General Public License as published
 
// by the Free Software Foundation, either version 3 of the License, or (at
 
// your option) any later version.
 
//
 
// This program is distributed in the hope that it will be useful, but WITHOUT
 
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
 
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 
// for more details.
 
//
 
// You should have received a copy of the GNU General Public License along
 
// with this program.  (It's in the $(ROOT)/doc directory, run make with no
 
// target there if the PDF file isn't present.)  If not, see
 
// <http://www.gnu.org/licenses/> for a copy.
 
//
 
// License:     GPL, v3, as defined and found on www.gnu.org,
 
//              http://www.gnu.org/licenses/gpl.html
 
//
 
//
 
////////////////////////////////////////////////////////////////////////////////
 
//
 
//
 
 
#include "artyboard.h"
#include "artyboard.h"
#include "zipsys.h"
#include "zipsys.h"
 
 
void    idle_task(void) {
void    idle_task(void) {
        while(1)
        while(1)
Line 13... Line 55...
#define OLED_IOPWR              OLED_PMODEN
#define OLED_IOPWR              OLED_PMODEN
#define OLED_VCCEN              0x0020002
#define OLED_VCCEN              0x0020002
#define OLED_VCC_DISABLE        0x0020000
#define OLED_VCC_DISABLE        0x0020000
#define OLED_RESET              0x0040000
#define OLED_RESET              0x0040000
#define OLED_RESET_CLR          0x0040004
#define OLED_RESET_CLR          0x0040004
#define OLED_PWRRESET           (OLED_PMODEN|OLED_RESET) // 5
#define OLED_FULLPOWER          (OLED_PMODEN|OLED_VCCEN|OLED_RESET_CLR)
#define OLED_FULLPOWER          (OLED_PMODEN|OLED_VCCEN|OLED_RESET_CLR) // 3->7
 
#define OLED_POWER_DOWN         (OLED_PMODEN_OFF|OLED_VCC_DISABLE)
#define OLED_POWER_DOWN         (OLED_PMODEN_OFF|OLED_VCC_DISABLE)
#define OLED_BUSY               1
#define OLED_BUSY               1
#define OLED_DISPLAYON          0x0af
#define OLED_DISPLAYON          0x0af
 
 
 
 
#define MICROSECOND             (CLOCKFREQ_HZ/1000000)
#define MICROSECOND             (CLOCKFREQ_HZ/1000000)
 
 
#define OLED_DISPLAY_OFF
#define OLED_DISPLAY_OFF
 
 
 
 
 
 
 
/*
 
 * timer_delay()
 
 *
 
 * Using the timer peripheral, delay by a given number of counts.  We'll sleep
 
 * during this delayed time, and wait on an interrupt to wake us.  As a result,
 
 * this will only work from supervisor mode.
 
 */
 
void    timer_delay(int counts) {
 
        // Clear the PIC.  We want to exit from here on timer counts alone
 
        zip->pic = CLEARPIC;
 
 
 
        if (counts > 10) {
 
                // Set our timer to count down the given number of counts
 
                zip->tma = counts;
 
                zip->pic = EINT(SYSINT_TMA);
 
                zip_rtu();
 
                zip->pic = CLEARPIC;
 
        } // else anything less has likely already passed
 
}
 
 
 
 
 
void oled_clear(void);
 
 
 
/*
 
 * The following outlines a series of commands to send to the OLEDrgb as part
 
 * of an initialization sequence.  The sequence itself was taken from the
 
 * MPIDE demo.  Single byte numbers in the sequence are just that: commands
 
 * to send 8-bit values across the port.  17-bit values with the 16th bit
 
 * set send two bytes (bits 15-0) to the port.  The OLEDrgb treats these as
 
 * commands (first byte) with an argument (the second byte).
 
 */
        const int       init_sequence[] = {
        const int       init_sequence[] = {
                //  Unlock commands
                //  Unlock commands
                0x01fd12,
                0x01fd12,
                //  Display off
                //  Display off
                0x0ae,
                0x0ae,
Line 67... Line 141...
                //  5r) Set Contrast for Color B
                //  5r) Set Contrast for Color B
                0x018250,
                0x018250,
                //  5s) Set Contrast for Color C
                //  5s) Set Contrast for Color C
                0x01837D,
                0x01837D,
                //  disable scrolling
                //  disable scrolling
                0x02e };
        0x02e
 
};
void    timer_delay(int counts) {
 
        // Clear the PIC.  We want to exit from here on timer counts alone
 
        zip->pic = CLEARPIC;
 
 
 
        if (counts > 10) {
 
                // Set our timer to count down the given number of counts
 
                zip->tma = counts;
 
                zip->pic = EINT(SYSINT_TMA);
 
                zip_rtu();
 
                zip->pic = CLEARPIC;
 
        } // else anything less has likely already passed
 
}
 
 
 
void oled_clear(void);
 
 
 
 
/*
 
 * oled_init()
 
 *
 
 * This initializes and starts up the OLED.  While it sounds important, really
 
 * the majority of the work necessary to do this is really captured in the
 
 * init_sequence[] above.  This just primarily works to send that sequence to
 
 * the PMod.
 
 *
 
 * We should be able to do all of this with the DMA: wait for an OLEDrgb not
 
 * busy interrupt, send one value, repeat until done.  For now ... we'll just
 
 * leave that as an advanced exercise.
 
 *
 
 */
void    oled_init(void) {
void    oled_init(void) {
        int     i;
        int     i;
 
 
        for(i=0; i<sizeof(init_sequence); i++) {
        for(i=0; i<sizeof(init_sequence); i++) {
                while(sys->io_oled.o_ctrl & OLED_BUSY)
                while(sys->io_oled.o_ctrl & OLED_BUSY)
                        ;
                        ;
                sys->io_oled.o_ctrl = init_sequence[i];
                sys->io_oled.o_ctrl = init_sequence[i];
        }
        }
 
 
        // 5u) Clear Screen
 
        oled_clear();
        oled_clear();
 
 
        zip->tma = (CLOCKFREQ_HZ/200);
        // Wait 5ms
        zip->pic = EINT(SYSINT_TMA);
        timer_delay(CLOCKFREQ_HZ/200);
        zip_rtu();
 
        zip->pic = CLEARPIC;
 
 
 
 
 
        // Turn on VCC and wait 100ms
        // Turn on VCC and wait 100ms
        sys->io_oled.o_data = OLED_VCCEN;
        sys->io_oled.o_data = OLED_VCCEN;
        // Wait 100 ms
        // Wait 100 ms
        timer_delay(CLOCKFREQ_HZ/10);
        timer_delay(CLOCKFREQ_HZ/10);
 
 
        // Send Display On command
        // Send Display On command
        sys->io_oled.o_ctrl = 0xaf;
        sys->io_oled.o_ctrl = OLED_DISPLAYON;
}
}
 
 
 
/*
 
 * oled_clear()
 
 *
 
 * This should be fairly self-explanatory: it clears (sets to black) all of the
 
 * graphics memory on the OLED.
 
 *
 
 * What may not be self-explanatory is that to send any more than three bytes
 
 * using our interface you need to send the first three bytes in o_ctrl,
 
 * and set the next bytes (up to four) in o_a.  (Another four can be placed in
 
 * o_b.)  When the word is written to o_ctrl, the command goes over the wire
 
 * and o_a and o_b are reset.  Hence we set o_a first, then o_ctrl.  Further,
 
 * the '4' in the top nibble of o_ctrl indicates that we are sending 5-bytes
 
 * (4+5), so the OLEDrgb should see: 0x25,0x00,0x00,0x5f,0x3f.
 
 */
void    oled_clear(void) {
void    oled_clear(void) {
        while(sys->io_oled.o_ctrl & OLED_BUSY)
        while(sys->io_oled.o_ctrl & OLED_BUSY)
                ;
                ;
        sys->io_oled.o_a = 0x5f3f0000;
        sys->io_oled.o_a = 0x5f3f0000;
        sys->io_oled.o_ctrl = 0x40250000;
        sys->io_oled.o_ctrl = 0x40250000;
        while(sys->io_oled.o_ctrl & OLED_BUSY)
        while(sys->io_oled.o_ctrl & OLED_BUSY)
                ;
                ;
}
}
 
 
 
/*
 
 * oled_fill
 
 *
 
 * Similar to oled_clear, this fills a rectangle with a given pixel value.
 
 */
void    oled_fill(int c, int r, int w, int h, int pix) {
void    oled_fill(int c, int r, int w, int h, int pix) {
        int     ctrl; // We'll send this value out the control/command port
        int     ctrl; // We'll send this value out the control/command port
 
 
        if (c > 95) c = 95;
        if (c > 95) c = 95;
        if (c <  0) c =  0;
        if (c <  0) c =  0;
Line 137... Line 225...
        // Enable a rectangle to fill
        // Enable a rectangle to fill
        while(sys->io_oled.o_ctrl & OLED_BUSY)
        while(sys->io_oled.o_ctrl & OLED_BUSY)
                ;
                ;
        sys->io_oled.o_ctrl = 0x12601;
        sys->io_oled.o_ctrl = 0x12601;
 
 
 
        //
        // Now, let's build the actual copy command
        // Now, let's build the actual copy command
 
        //
 
        // This is an 11 byte command, consisting of the 0x22, followed by
 
        // the top left column and row of our rectangle, and then the bottom
 
        // right column and row.  That's the first five bytes.  The next six
 
        // bytes are the color of the border and the color of the fill.
 
        // Here, we set both colors to be identical.
 
        // 
        ctrl = 0xa0220000 | ((c&0x07f)<<8) | (r&0x03f);
        ctrl = 0xa0220000 | ((c&0x07f)<<8) | (r&0x03f);
        sys->io_oled.o_a = (((c+w)&0x07f)<<24) | (((r+h)&0x03f)<<16);
        sys->io_oled.o_a = (((c+w)&0x07f)<<24) | (((r+h)&0x03f)<<16);
        sys->io_oled.o_a|= ((pix >> 11) & 0x01f)<< 9;
        sys->io_oled.o_a|= ((pix >> 11) & 0x01f)<< 9;
        sys->io_oled.o_a|= ((pix >>  5) & 0x03f)    ;
        sys->io_oled.o_a|= ((pix >>  5) & 0x03f)    ;
        sys->io_oled.o_b = ((pix >> 11) & 0x01f)<<17;
        sys->io_oled.o_b = ((pix      ) & 0x01f)<<25;
 
        sys->io_oled.o_b|= ((pix >> 11) & 0x01f)<<17;
        sys->io_oled.o_b|= ((pix >>  5) & 0x03f)<< 8;
        sys->io_oled.o_b|= ((pix >>  5) & 0x03f)<< 8;
        sys->io_oled.o_b|= ((pix      ) & 0x01f)<< 1;
        sys->io_oled.o_b|= ((pix      ) & 0x01f)<< 1;
 
 
        // Make certain we had finished with the port (should've ...)
        // Make certain we had finished with the port (we should've by now)
        while(sys->io_oled.o_ctrl & OLED_BUSY)
        while(sys->io_oled.o_ctrl & OLED_BUSY)
                ;
                ;
 
 
        // and send our new command
        // and send our new command.  Note that o_a and o_b were already set
 
        // ahead of time, and are only now being sent together with this
 
        // command.
        sys->io_oled.o_ctrl = ctrl;
        sys->io_oled.o_ctrl = ctrl;
 
 
        // To be nice to whatever routine follows, we'll wait 'til the port
        // To be nice to whatever routine follows, we'll wait 'til the port
        // is clear again.
        // is clear again.
        while(sys->io_oled.o_ctrl & OLED_BUSY)
        while(sys->io_oled.o_ctrl & OLED_BUSY)
                ;
                ;
}
}
 
 
 
 
 
/*
 
 * entry()
 
 *
 
 * In all (current) ZipCPU programs, the programs start with an entry()
 
 * function that takes no arguments.  The actual bootup entry can be found
 
 * in the bootstram directory, but that calls us here.
 
 *
 
 */
void    entry(void) {
void    entry(void) {
 
 
 
        // Since we'll be returning to userspace via zip_rtu() in order to
 
        // wait for an interrupt, let's at least place a valid program into
 
        // userspace to run: the idle_task.
        unsigned        user_regs[16];
        unsigned        user_regs[16];
        for(int i=0; i<15; i++)
        for(int i=0; i<15; i++)
                user_regs[i] = 0;
                user_regs[i] = 0;
        user_regs[15] = (unsigned int)idle_task;
        user_regs[15] = (unsigned int)idle_task;
        zip_restore_context(user_regs);
        zip_restore_context(user_regs);
 
 
        // Clear the PIC.  We'll come back and use it later.
        // Clear the PIC.  We'll come back and use it later.  We clear it here
 
        // partly in order to avoid a race condition later.
        zip->pic = CLEARPIC;
        zip->pic = CLEARPIC;
 
 
        if (0) { // Wait till we've had power for at least a quarter second
        // Wait till we've had power for at least a quarter second
 
        if (0) {
 
                // While this appears to do the task quite nicely, it leaves
 
                // the master_ce line high within the CPU, and so it generates
 
                // a whole lot of debug information in our Verilator simulation,
 
                // busmaster_tb.
                int pwrcount = sys->io_pwrcount;
                int pwrcount = sys->io_pwrcount;
                do {
                do {
                        pwrcount = sys->io_pwrcount;
                        pwrcount = sys->io_pwrcount;
                } while((pwrcount>0)&&(pwrcount < CLOCKFREQ_HZ/4));
                } while((pwrcount>0)&&(pwrcount < CLOCKFREQ_HZ/4));
        } else {
        } else {
 
                // By using the timer and sleeping instead, the simulator can
 
                // be made to run a *lot* faster, with a *lot* less debugging
 
                // ... junk.
                int pwrcount = sys->io_pwrcount;
                int pwrcount = sys->io_pwrcount;
                if ((pwrcount > 0)&&(pwrcount < CLOCKFREQ_HZ/4)) {
                if ((pwrcount > 0)&&(pwrcount < CLOCKFREQ_HZ/4)) {
                        pwrcount = CLOCKFREQ_HZ/4 - pwrcount;
                        pwrcount = CLOCKFREQ_HZ/4 - pwrcount;
                        timer_delay(pwrcount);
                        timer_delay(pwrcount);
                }
                }
Line 228... Line 349...
        //      This just took place during the reset cycle we just completed.
        //      This just took place during the reset cycle we just completed.
        //
        //
        oled_init();
        oled_init();
 
 
        // 4. Clear screen
        // 4. Clear screen
        // sys->io_oled.o_ctrl = OLED_CLEAR_SCREEN;
        // 5. Apply voltage
 
        // 6. Turn on display
        // Wait for the command to complete
        // 7. Wait 100ms
        while(sys->io_oled.o_ctrl & OLED_BUSY)
        //      We already stuffed this command sequence into the oled_init,
                ;
        //      so we're good here.
 
 
        // 5. Apply power to VCCEN
 
        sys->io_oled.o_data = OLED_FULLPOWER;
 
 
 
        // 6. Delay 100ms
 
        timer_delay(CLOCKFREQ_HZ/10);
 
 
 
        while(1) {
        while(1) {
        // 7. Send Display On command
                sys->io_ledctrl = 0x0f0;
        // sys->io_oled.o_ctrl = OLED_CLEAR_SCREEN; // ?? What command is this?
 
                sys->io_ledctrl = 0x0ff;
 
 
 
                sys->io_oled.o_ctrl = OLED_DISPLAYON;
                sys->io_oled.o_ctrl = OLED_DISPLAYON;
 
 
                oled_clear();
                oled_clear();
 
 
 
                // Let's start our writes at the top left of the GDDRAM
 
                // (screen memory)
                while(sys->io_oled.o_ctrl & OLED_BUSY)
                while(sys->io_oled.o_ctrl & OLED_BUSY)
                        ;
                        ;
                sys->io_oled.o_ctrl = 0x2015005f;
                sys->io_oled.o_ctrl = 0x2015005f; // Sets column min/max address
 
 
                while(sys->io_oled.o_ctrl & OLED_BUSY)
                while(sys->io_oled.o_ctrl & OLED_BUSY)
                        ;
                        ;
                sys->io_oled.o_ctrl = 0x2075003f;
                sys->io_oled.o_ctrl = 0x2075003f; // Sets row min/max address
 
 
                sys->io_ledctrl = 0x0fe;
 
                // Now ... finally ... we can send our image.
                // Now ... finally ... we can send our image.
                for(int i=0; i<6144; i++) {
                for(int i=0; i<6144; i++) {
                        while(sys->io_oled.o_ctrl & OLED_BUSY)
                        while(sys->io_oled.o_ctrl & OLED_BUSY)
                                ;
                                ;
                        sys->io_oled.o_data = splash[i];
                        sys->io_oled.o_data = splash[i];
                }
                }
                sys->io_ledctrl = 0x0fc;
 
 
                // Wait 25 seconds.  The LEDs are for a fun effect.
 
                sys->io_ledctrl = 0x0f1;
                timer_delay(CLOCKFREQ_HZ*5);
                timer_delay(CLOCKFREQ_HZ*5);
 
                sys->io_ledctrl = 0x0f3;
                timer_delay(CLOCKFREQ_HZ*5);
                timer_delay(CLOCKFREQ_HZ*5);
 
                sys->io_ledctrl = 0x0f7;
                timer_delay(CLOCKFREQ_HZ*5);
                timer_delay(CLOCKFREQ_HZ*5);
 
                sys->io_ledctrl = 0x0ff;
                timer_delay(CLOCKFREQ_HZ*5);
                timer_delay(CLOCKFREQ_HZ*5);
 
                sys->io_ledctrl = 0x0fe;
                timer_delay(CLOCKFREQ_HZ*5);
                timer_delay(CLOCKFREQ_HZ*5);
 
 
 
 
 
                // Display a second image.
 
                sys->io_ledctrl = 0x0fc;
                for(int i=0; i<6144; i++) {
                for(int i=0; i<6144; i++) {
                        while(sys->io_oled.o_ctrl & OLED_BUSY)
                        while(sys->io_oled.o_ctrl & OLED_BUSY)
                                ;
                                ;
                        sys->io_oled.o_data = mug[i];
                        sys->io_oled.o_data = mug[i];
                }
                }
 
 
                sys->io_ledctrl = 0x0f0;
                // Leave this one in effect for 5 seconds only.
 
                sys->io_ledctrl = 0x0f8;
                timer_delay(CLOCKFREQ_HZ*5);
                timer_delay(CLOCKFREQ_HZ*5);
        }
        }
 
 
 
        // We'll never get here, so this line is really just for form.
        zip_halt();
        zip_halt();
}
}
 
 
 
 
 No newline at end of file
 No newline at end of file

powered by: WebSVN 2.1.0

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