Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * @file Arduboy2Core.cpp
- * \brief
- * The Arduboy2Core class for Arduboy hardware initilization and control.
- * SMART XE Version - now includes power off button
- * Also includes display inversion on the SYM key
- */
- #include "Arduboy2Core.h"
- #define powerPin 20
- bool invertDisplay = false;
- void Arduboy2Core::lcdBootProgram()
- {
- sendLCDCommand(0x01); // Soft reset
- delay(5);
- //OK
- sendLCDCommand(0x011); // Sleep Out
- sendLCDCommand(0x028); // Display OFF
- delay(25); // Delay 50 ms
- //OK
- sendLCDCommand(0xC0);
- sendLCDData(0xF0);
- sendLCDData(0x00);// Vop = F0h
- //OK
- sendLCDCommand(0xC3);
- sendLCDData(0x004); // BIAS = 1/14
- //OK
- sendLCDCommand(0xC4);
- sendLCDData(0x005); // Booster = x8
- //OK
- sendLCDCommand(0xD0);
- sendLCDData(0x01D); // Enable Analog Circuit
- //OK
- sendLCDCommand(0xB3);
- sendLCDData(0x00); // Set FOSC divider
- //OK
- sendLCDCommand(0xB5);
- sendLCDData(0x8B); // N-Line = 0
- //OK Select mode
- sendLCDCommand(0x39); // 0x39 Monochrome mode. 0x38 - gray Mode
- //OK
- sendLCDCommand(0x3A); // Enable DDRAM Interface
- sendLCDData(0x02); // monochrome and 4-level
- //OK
- sendLCDCommand(0x36); // Scan Direction Setting
- sendLCDData(0x00); //
- //OK
- sendLCDCommand(0xB0); // Duty Setting
- sendLCDData(0x9F); //0x87
- sendLCDCommand(0xF1); // Frame rate monochrome
- sendLCDData(0x07);
- sendLCDData(0x07);
- sendLCDData(0x07);
- sendLCDData(0x07);
- if (invertDisplay)sendLCDCommand(0x21); // Display inversion off
- else
- sendLCDCommand(0x20); // Display inversion off
- sendLCDCommand(0x2A); // Column Address Setting
- sendLCDData(0x00); // SEG0 -> SEG240
- sendLCDData(0x00); // SEG8*3=24
- sendLCDData(0x00);
- sendLCDData(0x7f); // SEG128*3=384 seg x(dont use) seg n seg n
- sendLCDCommand(0x2B); // Row Address Setting
- sendLCDData(0x00); // COM0 -> COM160
- sendLCDData(0x00); //
- sendLCDData(0x00); //
- sendLCDData(0x87); //HEIGHT-1
- sendLCDCommand(0x29); // Display ON
- /* end of sequence */
- }
- Arduboy2Core::Arduboy2Core() { }
- void Arduboy2Core::boot()
- {
- #ifdef ARDUBOY_SET_CPU_8MHZ
- // ARDUBOY_SET_CPU_8MHZ will be set by the IDE using boards.txt
- setCPUSpeed8MHz();
- #endif
- // Select the ADC input here so a delay isn't required in initRandomSeed()
- ADMUX = RAND_SEED_IN_ADMUX;
- bootPins();
- bootSPI();
- bootOLED();
- bootPowerSaving();
- }
- #ifdef ARDUBOY_SET_CPU_8MHZ
- // If we're compiling for 8MHz we need to slow the CPU down because the
- // hardware clock on the Arduboy is 16MHz.
- // We also need to readjust the PLL prescaler because the Arduino USB code
- // likely will have incorrectly set it for an 8MHz hardware clock.
- void Arduboy2Core::setCPUSpeed8MHz()
- {
- uint8_t oldSREG = SREG;
- cli(); // suspend interrupts
- PLLCSR = _BV(PINDIV); // dissable the PLL and set prescale for 16MHz)
- CLKPR = _BV(CLKPCE); // allow reprogramming clock
- CLKPR = 1; // set clock divisor to 2 (0b0001)
- PLLCSR = _BV(PLLE) | _BV(PINDIV); // enable the PLL (with 16MHz prescale)
- SREG = oldSREG; // restore interrupts
- }
- #endif
- // Pins are set to the proper modes and levels for the specific hardware.
- // This routine must be modified if any pins are moved to a different port
- void Arduboy2Core::bootPins()
- {
- #ifdef ARDUBOY_10
- #define PWR_BIT PORTD2
- // Port B INPUT_PULLUP or HIGH
- //PORTB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) |
- // _BV(B_BUTTON_BIT);
- // Port B INPUT or LOW (none)
- // Port B inputs
- DDRB &= ~(_BV(B_BUTTON_BIT) | _BV(SPI_MISO_BIT));
- // Port B outputs
- DDRB |= _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT) | _BV(SPI_SS_BIT);
- // Port C
- // Speaker: Not set here. Controlled by audio class
- // Port D INPUT_PULLUP or HIGH
- PORTD |= (_BV(CS_BIT)|_BV(PWR_BIT));
- // Port D INPUT or LOW
- PORTD &= ~(_BV(RST_BIT));
- // Port D inputs (none)
- DDRD |= ~(_BV(PWR_BIT));
- // Port D outputs
- DDRD &= _BV(RST_BIT) | _BV(CS_BIT) | _BV(DC_BIT);
- //pinMode(powerPin,INPUT_PULLUP);//power switch
- // Port E INPUT_PULLUP or HIGH
- //PORTE |= _BV(A_BUTTON_BIT);
- // Port E INPUT or LOW (none)
- // Port E inputs
- //DDRE &= ~(_BV(A_BUTTON_BIT));
- // Port E outputs (none)
- // Port F INPUT_PULLUP or HIGH
- //PORTF |= _BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
- // _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT);
- // Port F INPUT or LOW
- //PORTF &= ~(_BV(RAND_SEED_IN_BIT));
- // Port F inputs
- //DDRF &= ~(_BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
- // _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) |
- // _BV(RAND_SEED_IN_BIT));
- // Port F outputs (none)
- pinMode(8,INPUT_PULLUP);
- pinMode(9,INPUT_PULLUP);
- pinMode(35,INPUT_PULLUP);
- pinMode(34,INPUT_PULLUP);
- #elif defined(AB_DEVKIT)
- // Port B INPUT_PULLUP or HIGH
- PORTB |= _BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) |
- _BV(BLUE_LED_BIT);
- // Port B INPUT or LOW (none)
- // Port B inputs
- DDRB &= ~(_BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) |
- _BV(SPI_MISO_BIT));
- // Port B outputs
- DDRB |= _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT) | _BV(SPI_SS_BIT) |
- _BV(BLUE_LED_BIT);
- // Port C INPUT_PULLUP or HIGH
- PORTC |= _BV(RIGHT_BUTTON_BIT);
- // Port C INPUT or LOW (none)
- // Port C inputs
- DDRC &= ~(_BV(RIGHT_BUTTON_BIT));
- // Port C outputs (none)
- // Port D INPUT_PULLUP or HIGH
- PORTD |= _BV(CS_BIT);
- // Port D INPUT or LOW
- PORTD &= ~(_BV(RST_BIT));
- // Port D inputs (none)
- // Port D outputs
- DDRD |= _BV(RST_BIT) | _BV(CS_BIT) | _BV(DC_BIT);
- // Port E (none)
- // Port F INPUT_PULLUP or HIGH
- PORTF |= _BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT);
- // Port F INPUT or LOW
- PORTF &= ~(_BV(RAND_SEED_IN_BIT));
- // Port F inputs
- DDRF &= ~(_BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT) | _BV(RAND_SEED_IN_BIT));
- // Port F outputs (none)
- // Speaker: Not set here. Controlled by audio class
- #endif
- }
- void Arduboy2Core::bootOLED()
- {
- // reset the display
- delayShort(5); // reset pin should be low here. let it stay low a while
- bitSet(RST_PORT, RST_BIT); // set high to come out of reset
- delayShort(5); // wait a while
- // select the display (permanently, since nothing else is using SPI)
- bitClear(CS_PORT, CS_BIT);
- // run our customized boot-up command sequence against the
- // OLED to initialize it properly for Arduboy
- lcdBootProgram();
- blank();
- }
- void Arduboy2Core::LCDDataMode()
- {
- bitSet(DC_PORT, DC_BIT);
- }
- void Arduboy2Core::LCDCommandMode()
- {
- bitClear(DC_PORT, DC_BIT);
- }
- // Initialize the SPI interface for the display
- void Arduboy2Core::bootSPI()
- {
- // master, mode 0, MSB first, CPU clock / 2 (8MHz)
- SPCR = _BV(SPE) | _BV(MSTR);
- SPSR = _BV(SPI2X);
- }
- // Write to the SPI bus (MOSI pin)
- void Arduboy2Core::SPItransfer(uint8_t data)
- {
- SPDR = data;
- /*
- * The following NOP introduces a small delay that can prevent the wait
- * loop form iterating when running at the maximum speed. This gives
- * about 10% more speed, even if it seems counter-intuitive. At lower
- * speeds it is unnoticed.
- */
- asm volatile("nop");
- while (!(SPSR & _BV(SPIF))) { } // wait
- }
- void Arduboy2Core::safeMode()
- {
- if (buttonsState() == UP_BUTTON)
- {
- digitalWriteRGB(RED_LED, RGB_ON);
- #ifndef ARDUBOY_CORE // for Arduboy core timer 0 should remain enabled
- // prevent the bootloader magic number from being overwritten by timer 0
- // when a timer variable overlaps the magic number location
- power_timer0_disable();
- #endif
- while (true) { }
- }
- }
- /* Power Management */
- void Arduboy2Core::idle()
- {
- SMCR = _BV(SE); // select idle mode and enable sleeping
- sleep_cpu();
- SMCR = 0; // disable sleeping
- }
- void Arduboy2Core::bootPowerSaving()
- {
- // disable Two Wire Interface (I2C) and the ADC
- // All other bits will be written with 0 so will be enabled
- PRR0 = _BV(PRTWI) | _BV(PRADC);
- // disable USART1
- PRR1 |= _BV(PRUSART1);
- }
- // Shut down the display
- void Arduboy2Core::displayOff()
- {
- SPItransfer(0x28);
- SPItransfer(0x10);
- delayShort(250);
- bitClear(RST_PORT, RST_BIT);
- }
- void Arduboy2Core::SleepNow()
- {
- displayOff();
- attachInterrupt(4,wakeUpNow, LOW);
- delay(100);
- set_sleep_mode(SLEEP_MODE_PWR_DOWN);
- sleep_enable();
- sleep_mode();
- sleep_disable();
- displayOn();
- //power on beep
- int pp = 0;
- for(pp=0;pp<100;pp++)
- {
- digitalWrite(3,HIGH);
- delay(1);
- digitalWrite(3,LOW);
- delay(1);
- }
- }
- void Arduboy2Core::wakeUpNow()
- {
- detachInterrupt(4);
- }
- // Restart the display after a displayOff()
- void Arduboy2Core::displayOn()
- {
- bootOLED();
- }
- uint8_t Arduboy2Core::width() { return WIDTH; }
- uint8_t Arduboy2Core::height() { return HEIGHT; }
- /* Drawing */
- void Arduboy2Core::paint8Pixels(uint8_t pixels)
- {
- /*
- SPItransfer(pixels);
- */
- }
- void Arduboy2Core::paintScreen(const uint8_t *image)
- {
- /*
- for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
- {
- SPItransfer(pgm_read_byte(image + i));
- }
- */
- }
- // paint from a memory buffer, this should be FAST as it's likely what
- // will be used by any buffer based subclass
- //
- // The following assembly code runs "open loop". It relies on instruction
- // execution times to allow time for each byte of data to be clocked out.
- // It is specifically tuned for a 16MHz CPU clock and SPI clocking at 8MHz.
- #if 0
- void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
- {
- uint16_t count;
- asm volatile (
- " ldi %A[count], %[len_lsb] \n\t" //for (len = WIDTH * HEIGHT / 8)
- " ldi %B[count], %[len_msb] \n\t"
- "1: ld __tmp_reg__, %a[ptr] ;2 \n\t" //tmp = *(image)
- " out %[spdr], __tmp_reg__ ;1 \n\t" //SPDR = tmp
- " cpse %[clear], __zero_reg__ ;1/2 \n\t" //if (clear) tmp = 0;
- " mov __tmp_reg__, __zero_reg__ ;1 \n\t"
- "2: sbiw %A[count], 1 ;2 \n\t" //len --
- " sbrc %A[count], 0 ;1/2 \n\t" //loop twice for cheap delay
- " rjmp 2b ;2 \n\t"
- " st %a[ptr]+, __tmp_reg__ ;2 \n\t" //*(image++) = tmp
- " brne 1b ;1/2 :18 \n\t" //len > 0
- " in __tmp_reg__, %[spsr] \n\t" //read SPSR to clear SPIF
- : [ptr] "+&e" (image),
- [count] "=&w" (count)
- : [spdr] "I" (_SFR_IO_ADDR(SPDR)),
- [spsr] "I" (_SFR_IO_ADDR(SPSR)),
- [len_msb] "M" (WIDTH * (HEIGHT / 8 * 2) >> 8), // 8: pixels per byte
- [len_lsb] "M" (WIDTH * (HEIGHT / 8 * 2) & 0xFF), // 2: for delay loop multiplier
- [clear] "r" (clear)
- );
- }
- #endif
- // For reference, this is the "closed loop" C++ version of paintScreen()
- // used prior to the above version.
- void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
- {
- if (clear)
- {
- blank();
- return;
- }
- int y = 0;
- int i=0;
- int z = 0;
- uint8_t byte;
- int x = 0;
- int j = 0;
- while (y < (HEIGHT*2) )
- {
- uint8_t bit =0x1;
- for (z=0; z<8; z++)
- {
- sendLCDCommand(0x2B); // Row Address Setting (height)
- sendLCDData(0x00); // COM0 -> COM160
- sendLCDData(0x00+y); //
- sendLCDData(0x00); //
- sendLCDData(0x87); //HEIGHT-1
- sendLCDCommand(0x2C);
- //double up the lines
- int count = 0;
- while (count < 2)
- {
- for (x=0; x<(WIDTH); x++)
- {
- uint8_t a = image[i+x];
- byte = 0;
- if (a & bit) byte = 0xFF;
- SPDR = byte;
- while (!(SPSR & _BV(SPIF))) { }
- }
- count++;
- }
- /* //1 pixel per pixel
- for (x=0; x<(WIDTH); x+=3)
- {
- uint8_t a = image[i+x];
- uint8_t b = image[i+1+x];
- uint8_t c = image[i+2+x];
- byte = 0;
- if (a & bit) // if bit 23
- byte = byte | 0b11000000; //set pixel 1
- if (b & bit) // if bit 22
- byte = byte | 0b00011000; //set pixel 2
- if (c & bit) // if bit 22
- byte = byte | 0b00000011; //set pixel 3
- SPDR = byte;
- while (!(SPSR & _BV(SPIF))) { }
- }
- */
- bit <<=1;
- y+=2;
- }
- i += WIDTH;
- }
- while (!(SPSR & _BV(SPIF))) { } // wait for the last byte to be sent
- }
- void Arduboy2Core::blank()
- {
- sendLCDCommand(0x2B); // Row Address Setting (height)
- sendLCDData(0x00); // COM0 -> COM160
- sendLCDData(0x00); //
- sendLCDData(0x00); //
- sendLCDData(0x87); //HEIGHT-1
- sendLCDCommand(0x2C);
- for (int i = 0; i < (0x80*0x88); i++)
- {
- SPItransfer(0x00);
- }
- }
- void Arduboy2Core::sendLCDCommand(uint8_t command)
- {
- LCDCommandMode();
- SPItransfer(command);
- LCDDataMode();
- }
- void Arduboy2Core::sendLCDData(uint8_t data)
- {
- SPItransfer(data);
- }
- // invert the display or set to normal
- // when inverted, a pixel set to 0 will be on
- void Arduboy2Core::invert(bool inverse)
- {
- sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL);
- }
- // turn all display pixels on, ignoring buffer contents
- // or set to normal buffer display
- void Arduboy2Core::allPixelsOn(bool on)
- {
- sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM);
- }
- // flip the display vertically or set to normal
- void Arduboy2Core::flipVertical(bool flipped)
- {
- sendLCDCommand(flipped ? OLED_VERTICAL_FLIPPED : OLED_VERTICAL_NORMAL);
- }
- // flip the display horizontally or set to normal
- void Arduboy2Core::flipHorizontal(bool flipped)
- {
- sendLCDCommand(flipped ? OLED_HORIZ_FLIPPED : OLED_HORIZ_NORMAL);
- }
- /* RGB LED */
- void Arduboy2Core::setRGBled(uint8_t red, uint8_t green, uint8_t blue)
- {
- }
- void Arduboy2Core::setRGBled(uint8_t color, uint8_t val)
- {
- }
- void Arduboy2Core::freeRGBled()
- {
- }
- void Arduboy2Core::digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue)
- {
- }
- void Arduboy2Core::digitalWriteRGB(uint8_t color, uint8_t val)
- {
- }
- /* Buttons */
- uint8_t Arduboy2Core::buttonsState()
- {
- uint8_t buttons;
- //char rowPins[4] = {35,34,8,9};
- //35 = portB(7)
- //34 = portB(6)
- //8 = portB (5)
- //9 = portB (4)
- char columnPins[4]={23,19,22,4};
- buttons =0;
- int c=0;
- for (c=0; c<4; c++) {
- pinMode(columnPins[c],OUTPUT);
- digitalWrite(columnPins[c], LOW); // Begin column pulse output.
- switch (columnPins[c])
- {
- case 23:
- if ((~PINB) &_BV(4)) buttons = buttons | LEFT_BUTTON;
- break;
- case 19:
- if ((~PINB) &_BV(5)) buttons = buttons | DOWN_BUTTON;
- break;
- case 22:
- if ((~PINB) &_BV(5)) buttons = buttons | UP_BUTTON;
- if ((~PINB) &_BV(4)) buttons = buttons | RIGHT_BUTTON;
- break;
- case 4:
- if ((~PINB) &_BV(7)) buttons = buttons | B_BUTTON;
- if ((~PINB) &_BV(6)) buttons = buttons | A_BUTTON;
- if ((~PINB) &_BV(4))
- {
- delay(250);
- invertDisplay = !invertDisplay;
- if (invertDisplay) sendLCDCommand(0x21); // Display inversion off
- else
- sendLCDCommand(0x20); // Display inversion off
- }
- }
- // Set pin to high impedance input. Effectively ends column pulse.
- digitalWrite(columnPins[c],HIGH);
- pinMode(columnPins[c],INPUT);
- }
- if ((~PIND) &_BV(2)) SleepNow();//power button
- return buttons;
- }
- // delay in ms with 16 bit duration
- void Arduboy2Core::delayShort(uint16_t ms)
- {
- delay((unsigned long) ms);
- }
- void Arduboy2Core::exitToBootloader()
- {
- }
- // Replacement main() that eliminates the USB stack code.
- // Used by the ARDUBOY_NO_USB macro. This should not be called
- // directly from a sketch.
- void Arduboy2Core::mainNoUSB()
- {
- }
Advertisement
Add Comment
Please, Sign In to add comment