/* * Copyright 2008 Jay Clegg. All rights reserved. * * This program is free software: 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 * MERCHANTABILITY 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. If not, see . */ /* Peggy2-i2c interface, Copyright 2008 by Jay Clegg. All rights reserved. This code is designed for an unmodified Peggy 2.0 board sold by evilmadscience.com The code configures the Peggy as an TWI (I2C) slave. Companion code for an Arduino allows it to act as an TWI master, so that it can transmit frames to the peggy. Please see http://www.planetclegg.com/projects/Twi2Peggy.html for explanation of how all this is supposed to work. Credits goes to: Windell H Oskay, (http://www.evilmadscientist.com/) for creating the Peggy 2.0 kit, and getting 16 shades of gray working Geoff Harrison (http://www.solivant.com/peggy2/), for proving that interrupt driven display on the Peggy 2.0 was viable. */ //////////////////////////////////////////////////////////////////////////////////////////// // FPS must be high enough to not have obvious flicker, low enough that main loop has // time to process one byte per pass. // ~140 seems to be about the absolute max for me (with this code on avr-gcc 4.2, -Os), // but compiler differences might make this maximum value larger or smaller. // if the value is too high errors start to occur or it will stop receiving altogether // conversely, any lower than 60 and flicker becomes apparent. // note: further code optimization might allow this number to // be a bit higher, but only up to a point... // it *must* result in a value for OCR0A in the range of 1-255 //#define FPS 144 #define FPS 90 // 25 rows * 13 bytes per row == 325 #define DISP_BUFFER_SIZE 325 #define MAX_BRIGHTNESS 15 #define TWI_SLAVE_ID 34 //////////////////////////////////////////////////////////////////////////////////////////// uint8_t frameBuffer[DISP_BUFFER_SIZE]; uint8_t *currentRowPtr = frameBuffer; uint8_t currentRow=0; uint8_t currentBrightness=0; // Note: the refresh code has been optimized heavily from the previous version. SIGNAL(TIMER0_COMPA_vect) { // there are 15 passes through this interrupt for each row per frame. // ( 15 * 25) = 375 times per frame. // during those 15 passes, a led can be on or off. // if it is off the entire time, the perceived brightness is 0/15 // if it is on the entire time, the perceived brightness is 15/15 // giving a total of 16 average brightness levels from fully on to fully off. // currentBrightness is a comparison variable, used to determine if a certain // pixel is on or off during one of those 15 cycles. currentBrightnessShifted // is the same value left shifted 4 bits: This is just an optimization for // comparing the high-order bytes. if (++currentBrightness >= MAX_BRIGHTNESS) { currentBrightness=0; if (++currentRow > 24) { currentRow =0; currentRowPtr = frameBuffer; } else { currentRowPtr += 13; } } //////////////////// Parse a row of data and write out the bits via spi uint8_t currentBrightnessShifted = currentBrightness <<4; uint8_t *ptr = currentRowPtr + 12; // its more convenient to work from right to left uint8_t p, bits=0; // optimization: by using variables for these two masking constants, we can trick gcc into not // promoting to 16-bit int (constants are 16 bit by default, causing the // comparisons to get promoted to 16bit otherwise)]. This turns out to be a pretty // substantial optimization for this handler uint8_t himask = 0xf0; uint8_t lomask = 0x0f; // Opimization: interleave waiting for SPI with other code, so the CPU can do something useful // when waiting for each SPI transmission to complete p = *ptr--; if ((p & lomask) > currentBrightness) bits|=1; SPDR = bits; bits=0; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=64; if ((p & himask) > currentBrightnessShifted) bits|=128; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=16; if ((p & himask) > currentBrightnessShifted) bits|=32; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=4; if ((p & himask) > currentBrightnessShifted) bits|=8; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=1; if ((p & himask) > currentBrightnessShifted) bits|=2; while (!(SPSR & (1< currentBrightness) bits|=64; if ((p & himask) > currentBrightnessShifted) bits|=128; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=16; if ((p & himask) > currentBrightnessShifted) bits|=32; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=4; if ((p & himask) > currentBrightnessShifted) bits|=8; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=1; if ((p & himask) > currentBrightnessShifted) bits|=2; while (!(SPSR & (1< currentBrightness) bits|=64; if ((p & himask) > currentBrightnessShifted) bits|=128; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=16; if ((p & himask) > currentBrightnessShifted) bits|=32; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=4; if ((p & himask) > currentBrightnessShifted) bits|=8; p = *ptr--; if ((p & lomask) > currentBrightness) bits|=1; if ((p & himask) > currentBrightnessShifted) bits|=2; while (!(SPSR & (1<> 3) / 25 / 15 / FPS; // Frames per second * 15 passes for brightness * 25 rows TIMSK0 = (1<= DISP_BUFFER_SIZE) { // buffer filled, so reset everything to wait for next frame //counter = 0; //ptr = frameBuffer; state = 0; } } } } void setup() // run once, when the sketch starts { // Enable pullups for buttons/i2c PORTB |= _BV(0);//(1<