Snake
This commit is contained in:
commit
9054f9a526
7 changed files with 966 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
build
|
||||||
|
*~
|
37
CMakeLists.txt
Normal file
37
CMakeLists.txt
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8.3)
|
||||||
|
|
||||||
|
|
||||||
|
SET(BOARD "atmega328p" CACHE STRING "AVR CPU to build for")
|
||||||
|
SET(CLOCK "16000000" CACHE STRING "CPU clock")
|
||||||
|
|
||||||
|
SET(FLASH_FLAGS "-patmega328p" "-carduino" "-P/dev/ttyUSB0" "-b57600" CACHE STRING "avrdude flags")
|
||||||
|
|
||||||
|
|
||||||
|
find_program(AVR_GCC avr-gcc)
|
||||||
|
find_program(AVRDUDE avrdude)
|
||||||
|
|
||||||
|
SET(CMAKE_SYSTEM_NAME Generic)
|
||||||
|
|
||||||
|
|
||||||
|
SET(CMAKE_C_COMPILER ${AVR_GCC})
|
||||||
|
|
||||||
|
project(ARDKBD C)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH ${ARDKDB_SOURCE_DIR})
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(snake.elf
|
||||||
|
Charliplexing.c
|
||||||
|
kbd.c
|
||||||
|
snake.c
|
||||||
|
)
|
||||||
|
set_target_properties(snake.elf PROPERTIES
|
||||||
|
COMPILE_FLAGS "-Wall -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Os -mmcu=${BOARD}"
|
||||||
|
LINK_FLAGS "-Wall -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Os -mmcu=${BOARD}"
|
||||||
|
COMPILE_DEFINITIONS "F_CPU=${CLOCK}"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT snake.hex COMMAND ${CMAKE_OBJCOPY} -O ihex -R .eeprom snake.elf snake.hex DEPENDS snake.elf)
|
||||||
|
add_custom_target(snake ALL DEPENDS snake.hex)
|
||||||
|
|
||||||
|
add_custom_target(flash COMMAND ${AVRDUDE} ${FLASH_FLAGS} -D -Uflash:w:snake.hex:i DEPENDS snake)
|
503
Charliplexing.c
Normal file
503
Charliplexing.c
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
/*
|
||||||
|
Charliplexing.cpp - Using timer2 with 1ms resolution
|
||||||
|
|
||||||
|
Alex Wenger <a.wenger@gmx.de> http://arduinobuch.wordpress.com/
|
||||||
|
Matt Mets <mahto@cibomahto.com> http://cibomahto.com/
|
||||||
|
|
||||||
|
Timer init code from MsTimer2 - Javier Valencia <javiervalencia80@gmail.com>
|
||||||
|
Misc functions from Benjamin Sonnatg <benjamin@sonntag.fr>
|
||||||
|
|
||||||
|
History:
|
||||||
|
2009-12-30 - V0.0 wrote the first version at 26C3/Berlin
|
||||||
|
2010-01-01 - V0.1 adding misc utility functions
|
||||||
|
(Clear, Vertical, Horizontal) comment are Doxygen complaints now
|
||||||
|
2010-05-27 - V0.2 add double-buffer mode
|
||||||
|
2010-08-18 - V0.9 Merge brightness and grayscale
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
#include "Charliplexing.h"
|
||||||
|
|
||||||
|
volatile unsigned int LedSign_tcnt2;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _videoPage {
|
||||||
|
uint8_t pixels[SHADES][48]; // TODO: is 48 right?
|
||||||
|
} videoPage;
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Table for the LED multiplexing cycles
|
||||||
|
* Each frame is made of 24 bytes (for the 24 display cycles)
|
||||||
|
* There are SHADES frames per buffer in grayscale mode (one for each brigtness)
|
||||||
|
* and twice that many to support double-buffered grayscale.
|
||||||
|
*/
|
||||||
|
videoPage leds[2];
|
||||||
|
|
||||||
|
/// Determines whether the display is in single or double buffer mode
|
||||||
|
uint8_t displayMode = SINGLE_BUFFER;
|
||||||
|
|
||||||
|
/// Flag indicating that the display page should be flipped as soon as the
|
||||||
|
/// current frame is displayed
|
||||||
|
volatile bool videoFlipPage = false;
|
||||||
|
|
||||||
|
/// Pointer to the buffer that is currently being displayed
|
||||||
|
videoPage* displayBuffer;
|
||||||
|
|
||||||
|
/// Pointer to the buffer that should currently be drawn to
|
||||||
|
videoPage* workBuffer;
|
||||||
|
|
||||||
|
/// Flag indicating that the timer buffer should be flipped as soon as the
|
||||||
|
/// current frame is displayed
|
||||||
|
volatile bool videoFlipTimer = false;
|
||||||
|
|
||||||
|
|
||||||
|
// Timer counts to display each page for, plus off time
|
||||||
|
typedef struct timerInfo {
|
||||||
|
uint8_t counts[SHADES];
|
||||||
|
uint8_t prescaler[SHADES];
|
||||||
|
} timerInfo;
|
||||||
|
|
||||||
|
// Double buffer the timing information, of course.
|
||||||
|
timerInfo* frontTimer;
|
||||||
|
timerInfo* backTimer;
|
||||||
|
|
||||||
|
timerInfo* tempTimer;
|
||||||
|
|
||||||
|
timerInfo timer[2];
|
||||||
|
|
||||||
|
// Record a slow and fast prescaler for later use
|
||||||
|
typedef struct prescalerInfo {
|
||||||
|
uint8_t relativeSpeed;
|
||||||
|
uint8_t TCCR2;
|
||||||
|
} prescalerInfo;
|
||||||
|
|
||||||
|
// TODO: Generate these based on processor type and clock speed
|
||||||
|
prescalerInfo slowPrescaler = {1, 0x03};
|
||||||
|
//prescalerInfo fastPrescaler = {32, 0x01};
|
||||||
|
prescalerInfo fastPrescaler = {4, 0x02};
|
||||||
|
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
/// Uncomment to set analog pin 5 high during interrupts, so that an
|
||||||
|
/// oscilloscope can be used to measure the processor time taken by it
|
||||||
|
//#define MEASURE_ISR_TIME
|
||||||
|
//#ifdef MEASURE_ISR_TIME
|
||||||
|
//uint8_t statusPIN = 19;
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
typedef struct LEDPosition {
|
||||||
|
uint8_t high;
|
||||||
|
uint8_t low;
|
||||||
|
} LEDPosition;
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Table for LED Position in leds[] ram table
|
||||||
|
*/
|
||||||
|
|
||||||
|
const LEDPosition ledMap[126] = {
|
||||||
|
{13, 5}, {13, 6}, {13, 7}, {13, 8}, {13, 9}, {13,10}, {13,11}, {13,12},
|
||||||
|
{13, 4}, { 4,13}, {13, 3}, { 3,13}, {13, 2}, { 2,13},
|
||||||
|
{12, 5}, {12, 6}, {12, 7}, {12, 8}, {12, 9}, {12,10}, {12,11}, {12,13},
|
||||||
|
{12, 4}, { 4,12}, {12, 3}, { 3,12}, {12, 2}, { 2,12},
|
||||||
|
{11, 5}, {11, 6}, {11, 7}, {11, 8}, {11, 9}, {11,10}, {11,12}, {11,13},
|
||||||
|
{11, 4}, { 4,11}, {11, 3}, { 3,11}, {11, 2}, { 2,11},
|
||||||
|
{10, 5}, {10, 6}, {10, 7}, {10, 8}, {10, 9}, {10,11}, {10,12}, {10,13},
|
||||||
|
{10, 4}, { 4,10}, {10, 3}, { 3,10}, {10, 2}, { 2,10},
|
||||||
|
{ 9, 5}, { 9, 6}, { 9, 7}, { 9, 8}, { 9,10}, { 9,11}, { 9,12}, { 9,13},
|
||||||
|
{ 9, 4}, { 4, 9}, { 9, 3}, { 3, 9}, { 9, 2}, { 2, 9},
|
||||||
|
{ 8, 5}, { 8, 6}, { 8, 7}, { 8, 9}, { 8,10}, { 8,11}, { 8,12}, { 8,13},
|
||||||
|
{ 8, 4}, { 4, 8}, { 8, 3}, { 3, 8}, { 8, 2}, { 2, 8},
|
||||||
|
{ 7, 5}, { 7, 6}, { 7, 8}, { 7, 9}, { 7,10}, { 7,11}, { 7,12}, { 7,13},
|
||||||
|
{ 7, 4}, { 4, 7}, { 7, 3}, { 3, 7}, { 7, 2}, { 2, 7},
|
||||||
|
{ 6, 5}, { 6, 7}, { 6, 8}, { 6, 9}, { 6,10}, { 6,11}, { 6,12}, { 6,13},
|
||||||
|
{ 6, 4}, { 4, 6}, { 6, 3}, { 3, 6}, { 6, 2}, { 2, 6},
|
||||||
|
{ 5, 6}, { 5, 7}, { 5, 8}, { 5, 9}, { 5,10}, { 5,11}, { 5,12}, { 5,13},
|
||||||
|
{ 5, 4}, { 4, 5}, { 5, 3}, { 3, 5}, { 5, 2}, { 2, 5},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Constructor : Initialize the interrupt code.
|
||||||
|
* should be called in setup();
|
||||||
|
*/
|
||||||
|
void LedSignInit(uint8_t mode)
|
||||||
|
{
|
||||||
|
//#ifdef MEASURE_ISR_TIME
|
||||||
|
// pinMode(statusPIN, OUTPUT);
|
||||||
|
// digitalWrite(statusPIN, LOW);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
float prescaler = 0.0;
|
||||||
|
|
||||||
|
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__)
|
||||||
|
TIMSK2 &= ~(1<<TOIE2);
|
||||||
|
TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
|
||||||
|
TCCR2B &= ~(1<<WGM22);
|
||||||
|
ASSR &= ~(1<<AS2);
|
||||||
|
TIMSK2 &= ~(1<<OCIE2A);
|
||||||
|
|
||||||
|
if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) { // prescaler set to 64
|
||||||
|
TCCR2B |= ((1<<CS21) | (1<<CS20));
|
||||||
|
TCCR2B &= ~(1<<CS22);
|
||||||
|
prescaler = 32.0;
|
||||||
|
} else if (F_CPU < 1000000UL) { // prescaler set to 8
|
||||||
|
TCCR2B |= (1<<CS21);
|
||||||
|
TCCR2B &= ~((1<<CS22) | (1<<CS20));
|
||||||
|
prescaler = 8.0;
|
||||||
|
} else { // F_CPU > 16Mhz, prescaler set to 128
|
||||||
|
TCCR2B |= (1<<CS22);
|
||||||
|
TCCR2B &= ~((1<<CS21) | (1<<CS20));
|
||||||
|
prescaler = 64.0;
|
||||||
|
}
|
||||||
|
#elif defined (__AVR_ATmega8__)
|
||||||
|
TIMSK &= ~(1<<TOIE2);
|
||||||
|
TCCR2 &= ~((1<<WGM21) | (1<<WGM20));
|
||||||
|
TIMSK &= ~(1<<OCIE2);
|
||||||
|
ASSR &= ~(1<<AS2);
|
||||||
|
|
||||||
|
if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) { // prescaler set to 64
|
||||||
|
TCCR2 |= (1<<CS22);
|
||||||
|
TCCR2 &= ~((1<<CS21) | (1<<CS20));
|
||||||
|
prescaler = 64.0;
|
||||||
|
} else if (F_CPU < 1000000UL) { // prescaler set to 8
|
||||||
|
TCCR2 |= (1<<CS21);
|
||||||
|
TCCR2 &= ~((1<<CS22) | (1<<CS20));
|
||||||
|
prescaler = 8.0;
|
||||||
|
} else { // F_CPU > 16Mhz, prescaler set to 128
|
||||||
|
TCCR2 |= ((1<<CS22) && (1<<CS20));
|
||||||
|
TCCR2 &= ~(1<<CS21);
|
||||||
|
prescaler = 128.0;
|
||||||
|
}
|
||||||
|
#elif defined (__AVR_ATmega128__)
|
||||||
|
TIMSK &= ~(1<<TOIE2);
|
||||||
|
TCCR2 &= ~((1<<WGM21) | (1<<WGM20));
|
||||||
|
TIMSK &= ~(1<<OCIE2);
|
||||||
|
|
||||||
|
if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) { // prescaler set to 64
|
||||||
|
TCCR2 |= ((1<<CS21) | (1<<CS20));
|
||||||
|
TCCR2 &= ~(1<<CS22);
|
||||||
|
prescaler = 64.0;
|
||||||
|
} else if (F_CPU < 1000000UL) { // prescaler set to 8
|
||||||
|
TCCR2 |= (1<<CS21);
|
||||||
|
TCCR2 &= ~((1<<CS22) | (1<<CS20));
|
||||||
|
prescaler = 8.0;
|
||||||
|
} else { // F_CPU > 16Mhz, prescaler set to 256
|
||||||
|
TCCR2 |= (1<<CS22);
|
||||||
|
TCCR2 &= ~((1<<CS21) | (1<<CS20));
|
||||||
|
prescaler = 256.0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LedSign_tcnt2 = 256 - (int)((float)F_CPU * 0.0005 / prescaler);
|
||||||
|
|
||||||
|
// Record whether we are in single or double buffer mode
|
||||||
|
displayMode = mode;
|
||||||
|
videoFlipPage = false;
|
||||||
|
|
||||||
|
// Point the display buffer to the first physical buffer
|
||||||
|
displayBuffer = &leds[0];
|
||||||
|
|
||||||
|
// If we are in single buffered mode, point the work buffer
|
||||||
|
// at the same physical buffer as the display buffer. Otherwise,
|
||||||
|
// point it at the second physical buffer.
|
||||||
|
if( displayMode & DOUBLE_BUFFER ) {
|
||||||
|
workBuffer = &leds[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
workBuffer = displayBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the timer buffering
|
||||||
|
frontTimer = &timer[0];
|
||||||
|
backTimer = &timer[1];
|
||||||
|
|
||||||
|
videoFlipTimer = false;
|
||||||
|
LedSignSetBrightness(127);
|
||||||
|
|
||||||
|
// Clear the buffer and display it
|
||||||
|
LedSignClear(0);
|
||||||
|
LedSignFlip(false);
|
||||||
|
|
||||||
|
// Then start the display
|
||||||
|
TCNT2 = LedSign_tcnt2;
|
||||||
|
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__)
|
||||||
|
TIMSK2 |= (1<<TOIE2);
|
||||||
|
#elif defined (__AVR_ATmega128__) || defined (__AVR_ATmega8__)
|
||||||
|
TIMSK |= (1<<TOIE2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// If we are in double-buffer mode, wait until the display flips before we
|
||||||
|
// return
|
||||||
|
if (displayMode & DOUBLE_BUFFER)
|
||||||
|
{
|
||||||
|
while (videoFlipPage) {
|
||||||
|
_delay_ms(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Signal that the front and back buffers should be flipped
|
||||||
|
* @param blocking if true : wait for flip before returning, if false :
|
||||||
|
* return immediately.
|
||||||
|
*/
|
||||||
|
void LedSignFlip(bool blocking)
|
||||||
|
{
|
||||||
|
if (displayMode & DOUBLE_BUFFER)
|
||||||
|
{
|
||||||
|
// Just set the flip flag, the buffer will flip between redraws
|
||||||
|
videoFlipPage = true;
|
||||||
|
|
||||||
|
// If we are blocking, sit here until the page flips.
|
||||||
|
while (blocking && videoFlipPage) {
|
||||||
|
_delay_ms(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Clear the screen completely
|
||||||
|
* @param set if 1 : make all led ON, if not set or 0 : make all led OFF
|
||||||
|
*/
|
||||||
|
void LedSignClear(int set) {
|
||||||
|
int x, y;
|
||||||
|
for(x=0;x<14;x++)
|
||||||
|
for(y=0;y<9;y++)
|
||||||
|
LedSignSet(x,y,set);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Clear an horizontal line completely
|
||||||
|
* @param y is the y coordinate of the line to clear/light [0-8]
|
||||||
|
* @param set if 1 : make all led ON, if not set or 0 : make all led OFF
|
||||||
|
*/
|
||||||
|
void LedSignHorizontal(int y, int set) {
|
||||||
|
int x;
|
||||||
|
for(x=0;x<14;x++)
|
||||||
|
LedSignSet(x,y,set);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Clear a vertical line completely
|
||||||
|
* @param x is the x coordinate of the line to clear/light [0-13]
|
||||||
|
* @param set if 1 : make all led ON, if not set or 0 : make all led OFF
|
||||||
|
*/
|
||||||
|
void LedSignVertical(int x, int set) {
|
||||||
|
int y;
|
||||||
|
for(y=0;y<9;y++)
|
||||||
|
LedSignSet(x,y,set);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** Set : switch on and off the leds. All the position #for char in frameString:
|
||||||
|
* calculations are done here, so we don't need to do in the
|
||||||
|
* interrupt code
|
||||||
|
*/
|
||||||
|
void LedSignSet(uint8_t x, uint8_t y, uint8_t c)
|
||||||
|
{
|
||||||
|
uint8_t pin_high = ledMap[x+y*14].high;
|
||||||
|
uint8_t pin_low = ledMap[x+y*14].low;
|
||||||
|
// pin_low is directly the address in the led array (minus 2 because the
|
||||||
|
// first two bytes are used for RS232 communication), but
|
||||||
|
// as it is a two byte array we need to check pin_high also.
|
||||||
|
// If pin_high is bigger than 8 address has to be increased by one
|
||||||
|
|
||||||
|
uint8_t bufferNum = (pin_low-2)*2 + (pin_high / 8) + ((pin_high > 7)?24:0);
|
||||||
|
uint8_t work = _BV(pin_high & 0x07);
|
||||||
|
|
||||||
|
// If we aren't in grayscale mode, just map any pin brightness to max
|
||||||
|
if (c > 0 && !(displayMode & GRAYSCALE)) {
|
||||||
|
c = SHADES-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < SHADES-1; i++) {
|
||||||
|
if( c > i ) {
|
||||||
|
workBuffer->pixels[i][bufferNum] |= work; // ON
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
workBuffer->pixels[i][bufferNum] &= ~work; // OFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Set the overall brightness of the screen
|
||||||
|
* @param brightness LED brightness, from 0 (off) to 127 (full on)
|
||||||
|
*/
|
||||||
|
|
||||||
|
void LedSignSetBrightness(uint8_t brightness)
|
||||||
|
{
|
||||||
|
// An exponential fit seems to approximate a (perceived) linear scale
|
||||||
|
float brightnessPercent = ((float)brightness / 127)*((float)brightness / 127);
|
||||||
|
uint8_t difference = 0;
|
||||||
|
|
||||||
|
/* ---- This needs review! Please review. -- thilo */
|
||||||
|
// set up page counts
|
||||||
|
// TODO: make SHADES a function parameter. This would require some refactoring.
|
||||||
|
int start = 15;
|
||||||
|
int max = 255;
|
||||||
|
float scale = 1.5;
|
||||||
|
float delta = pow( max - start , 1.0 / scale) / (SHADES - 1);
|
||||||
|
uint8_t pageCounts[SHADES];
|
||||||
|
|
||||||
|
pageCounts[0] = max - start;
|
||||||
|
uint8_t i;
|
||||||
|
for (i=1; i<SHADES; i++) {
|
||||||
|
pageCounts[i] = max - ( pow( i * delta, scale ) + start );
|
||||||
|
}
|
||||||
|
//Serial.end();
|
||||||
|
|
||||||
|
if (! initialized) {
|
||||||
|
// set front timer defaults
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < SHADES; i++) {
|
||||||
|
frontTimer->counts[i] = pageCounts[i];
|
||||||
|
// TODO: Generate this dynamically
|
||||||
|
frontTimer->prescaler[i] = slowPrescaler.TCCR2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the previous brightness request goes through
|
||||||
|
while( videoFlipTimer ) {
|
||||||
|
_delay_ms(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute on time for each of the pages
|
||||||
|
// Use the fast timer; slow timer is only useful for < 3 shades.
|
||||||
|
for (i = 0; i < SHADES - 1; i++) {
|
||||||
|
uint8_t interval = 255 - pageCounts[i];
|
||||||
|
|
||||||
|
backTimer->counts[i] = 255 - brightnessPercent
|
||||||
|
* interval
|
||||||
|
* fastPrescaler.relativeSpeed;
|
||||||
|
backTimer->prescaler[i] = fastPrescaler.TCCR2;
|
||||||
|
difference += backTimer->counts[i] - pageCounts[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute off time
|
||||||
|
backTimer->counts[SHADES - 1] = 255 - difference;
|
||||||
|
backTimer->prescaler[SHADES - 1] = slowPrescaler.TCCR2;
|
||||||
|
|
||||||
|
/* ---- End of "This needs review! Please review." -- thilo */
|
||||||
|
|
||||||
|
// Have the ISR update the timer registers next run
|
||||||
|
videoFlipTimer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/** The Interrupt code goes here !
|
||||||
|
*/
|
||||||
|
ISR(TIMER2_OVF_vect) {
|
||||||
|
DDRD = 0x0;
|
||||||
|
DDRB = 0x0;
|
||||||
|
//#ifdef MEASURE_ISR_TIME
|
||||||
|
// digitalWrite(statusPIN, HIGH);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
// For each cycle, we have potential SHADES pages to display.
|
||||||
|
// Once every page has been displayed, then we move on to the next
|
||||||
|
// cycle.
|
||||||
|
|
||||||
|
// 24 Cycles of Matrix
|
||||||
|
static uint8_t cycle = 0;
|
||||||
|
|
||||||
|
// SHADES pages to display
|
||||||
|
static uint8_t page = 0;
|
||||||
|
|
||||||
|
TCCR2B = frontTimer->prescaler[page];
|
||||||
|
TCNT2 = frontTimer->counts[page];
|
||||||
|
|
||||||
|
if ( page < SHADES - 1) {
|
||||||
|
|
||||||
|
if (cycle < 6) {
|
||||||
|
DDRD = _BV(cycle+2) | displayBuffer->pixels[page][cycle*2];
|
||||||
|
PORTD = displayBuffer->pixels[page][cycle*2];
|
||||||
|
|
||||||
|
DDRB = displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
PORTB = displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
} else if (cycle < 12) {
|
||||||
|
DDRD = displayBuffer->pixels[page][cycle*2];
|
||||||
|
PORTD = displayBuffer->pixels[page][cycle*2];
|
||||||
|
|
||||||
|
DDRB = _BV(cycle-6) | displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
PORTB = displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
} else if (cycle < 18) {
|
||||||
|
DDRD = _BV(cycle+2-12) | displayBuffer->pixels[page][cycle*2];
|
||||||
|
PORTD = displayBuffer->pixels[page][cycle*2];
|
||||||
|
|
||||||
|
DDRB = displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
PORTB = displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
} else {
|
||||||
|
DDRD = displayBuffer->pixels[page][cycle*2];
|
||||||
|
PORTD = displayBuffer->pixels[page][cycle*2];
|
||||||
|
|
||||||
|
DDRB = _BV(cycle-6-12) | displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
PORTB = displayBuffer->pixels[page][cycle*2+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Turn everything off
|
||||||
|
DDRD = 0x0;
|
||||||
|
DDRB = 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
page++;
|
||||||
|
|
||||||
|
if (page >= SHADES) {
|
||||||
|
page = 0;
|
||||||
|
cycle++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cycle > 24) {
|
||||||
|
cycle = 0;
|
||||||
|
|
||||||
|
// If the page should be flipped, do it here.
|
||||||
|
if (videoFlipPage && (displayMode & DOUBLE_BUFFER))
|
||||||
|
{
|
||||||
|
// TODO: is this an atomic operation?
|
||||||
|
videoFlipPage = false;
|
||||||
|
|
||||||
|
videoPage* temp = displayBuffer;
|
||||||
|
displayBuffer = workBuffer;
|
||||||
|
workBuffer = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFlipTimer) {
|
||||||
|
videoFlipTimer = false;
|
||||||
|
|
||||||
|
tempTimer = frontTimer;
|
||||||
|
frontTimer = backTimer;
|
||||||
|
backTimer = tempTimer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef MEASURE_ISR_TIME
|
||||||
|
// digitalWrite(statusPIN, LOW);
|
||||||
|
//#endif
|
||||||
|
}
|
33
Charliplexing.h
Normal file
33
Charliplexing.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
Charliplexing.h - Library for controlling the charliplexed led board
|
||||||
|
from JimmiePRodgers.com
|
||||||
|
Created by Alex Wenger, December 30, 2009.
|
||||||
|
Modified by Matt Mets, May 28, 2010.
|
||||||
|
Released into the public domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Charliplexing_h
|
||||||
|
#define Charliplexing_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define SINGLE_BUFFER 0
|
||||||
|
#define DOUBLE_BUFFER 1
|
||||||
|
#define GRAYSCALE 2
|
||||||
|
|
||||||
|
#define DISPLAY_COLS 14 // Number of columns in the display
|
||||||
|
#define DISPLAY_ROWS 9 // Number of rows in the display
|
||||||
|
#define SHADES 8 // Number of distinct shades to display, including black, i.e. OFF
|
||||||
|
|
||||||
|
extern volatile unsigned int LedSign_tcnt2;
|
||||||
|
|
||||||
|
void LedSignInit(uint8_t mode);
|
||||||
|
void LedSignSet(uint8_t x, uint8_t y, uint8_t c);
|
||||||
|
void LedSignSetBrightness(uint8_t brightness);
|
||||||
|
void LedSignFlip(bool blocking);
|
||||||
|
void LedSignClear(int set);
|
||||||
|
void LedSignHorizontal(int y, int set);
|
||||||
|
void LedSignVertical(int x, int set);
|
||||||
|
|
||||||
|
#endif
|
167
kbd.c
Normal file
167
kbd.c
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
#include "kbd.h"
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
static volatile int8_t kbd_state = 0;
|
||||||
|
|
||||||
|
static volatile uint8_t kbd_input = 0;
|
||||||
|
static volatile uint8_t kbd_output = 0;
|
||||||
|
static volatile uint8_t kbd_flags = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static volatile uint8_t ts = 10;
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool kbd_data() {
|
||||||
|
return (PINC & (1 << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool kbd_clock() {
|
||||||
|
return (PINC & (1 << 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void kbd_clock_down() {
|
||||||
|
DDRC |= 0x02;
|
||||||
|
PORTC &= ~0x02;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kbd_clock_in() {
|
||||||
|
DDRC &= ~0x02;
|
||||||
|
PORTC |= 0x02;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kbd_data_set(bool state) {
|
||||||
|
if (state)
|
||||||
|
PORTC |= 0x01;
|
||||||
|
else
|
||||||
|
PORTC &= ~0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kbd_data_out() {
|
||||||
|
DDRC |= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kbd_data_in() {
|
||||||
|
DDRC &= ~0x01;
|
||||||
|
PORTC |= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void kbd_wait() {
|
||||||
|
while (kbd_state) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ISR(PCINT1_vect) {
|
||||||
|
if (kbd_clock())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (kbd_state < 0) {
|
||||||
|
if (kbd_state >= -8) {
|
||||||
|
kbd_data_set(kbd_output & _BV(-1-kbd_state));
|
||||||
|
}
|
||||||
|
else if (kbd_state == -9) {
|
||||||
|
kbd_data_set(!(__builtin_popcount(kbd_output) & 1));
|
||||||
|
}
|
||||||
|
else if (kbd_state == -10) {
|
||||||
|
kbd_data_in();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kbd_state = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd_state--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool data = kbd_data();
|
||||||
|
|
||||||
|
if (kbd_state == 0) {
|
||||||
|
if (!data) { /* start bit */
|
||||||
|
kbd_input = 0;
|
||||||
|
kbd_state++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kbd_state <= 8) {
|
||||||
|
if (data)
|
||||||
|
kbd_input |= _BV(kbd_state-1);
|
||||||
|
kbd_state++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kbd_state == 9) {
|
||||||
|
if ((__builtin_popcount(kbd_input) & 1) == data)
|
||||||
|
kbd_flags |= KBD_FLAG_ERROR;
|
||||||
|
|
||||||
|
kbd_state++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd_state = 0;
|
||||||
|
|
||||||
|
if (kbd_flags & KBD_FLAG_ERROR) {
|
||||||
|
/* Retry */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kbd_input == 0xe0) {
|
||||||
|
kbd_flags |= KBD_FLAG_EXT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kbd_input == 0xf0) {
|
||||||
|
kbd_flags |= KBD_FLAG_BREAK;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t code = kbd_input;
|
||||||
|
if (kbd_flags & KBD_FLAG_EXT)
|
||||||
|
code |= 0xe000;
|
||||||
|
|
||||||
|
kbd_handle(code, !(kbd_flags & KBD_FLAG_BREAK));
|
||||||
|
|
||||||
|
kbd_flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void kbd_send(uint8_t command) {
|
||||||
|
kbd_wait();
|
||||||
|
|
||||||
|
kbd_clock_down();
|
||||||
|
_delay_us(100);
|
||||||
|
|
||||||
|
kbd_data_out();
|
||||||
|
kbd_data_set(false);
|
||||||
|
_delay_us(10);
|
||||||
|
|
||||||
|
kbd_clock_in();
|
||||||
|
|
||||||
|
kbd_output = command;
|
||||||
|
kbd_state = -1;
|
||||||
|
|
||||||
|
kbd_wait();
|
||||||
|
|
||||||
|
/* wait for ack */
|
||||||
|
while (!kbd_state) {}
|
||||||
|
|
||||||
|
kbd_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void kbd_init(void) {
|
||||||
|
DDRC &= ~0x03;
|
||||||
|
PORTC |= 0x03;
|
||||||
|
|
||||||
|
PCMSK1 = (1 << PCINT9);
|
||||||
|
PCICR = (1 << PCIE1);
|
||||||
|
}
|
25
kbd.h
Normal file
25
kbd.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef _AVR_KBD_H_
|
||||||
|
#define _AVR_KBD_H_
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define KBD_FLAG_ERROR (_BV(0))
|
||||||
|
#define KBD_FLAG_BREAK (_BV(1))
|
||||||
|
#define KBD_FLAG_EXT (_BV(2))
|
||||||
|
|
||||||
|
#define KBD_CODE_UP 0xe075
|
||||||
|
#define KBD_CODE_LEFT 0xe06b
|
||||||
|
#define KBD_CODE_DOWN 0xe072
|
||||||
|
#define KBD_CODE_RIGHT 0xe074
|
||||||
|
|
||||||
|
#define KBD_CMD_RESET 0xff
|
||||||
|
|
||||||
|
|
||||||
|
void kbd_handle(uint16_t code, bool make);
|
||||||
|
|
||||||
|
void kbd_send(uint8_t command);
|
||||||
|
void kbd_init(void);
|
||||||
|
|
||||||
|
#endif /* _AVR_KBD_H_ */
|
199
snake.c
Normal file
199
snake.c
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
#include "Charliplexing.h"
|
||||||
|
#include "kbd.h"
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _point_t {
|
||||||
|
uint8_t x, y;
|
||||||
|
} point_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum _dir_t {
|
||||||
|
NORTH = 0,
|
||||||
|
WEST,
|
||||||
|
SOUTH,
|
||||||
|
EAST
|
||||||
|
} dir_t;
|
||||||
|
|
||||||
|
|
||||||
|
static volatile uint8_t dir;
|
||||||
|
|
||||||
|
static point_t tail = {7, 4};
|
||||||
|
|
||||||
|
#define HISTORY_MAX 128
|
||||||
|
|
||||||
|
static uint8_t history[HISTORY_MAX >> 2];
|
||||||
|
static uint8_t history_len = 0;
|
||||||
|
static uint8_t history_pos = 0;
|
||||||
|
|
||||||
|
static point_t q;
|
||||||
|
|
||||||
|
static uint16_t rand(void) {
|
||||||
|
static uint16_t lfsr = 0xace1;
|
||||||
|
unsigned bit;
|
||||||
|
|
||||||
|
/* taps: 16 14 13 11; feedback polynomial: x^16 + x^14 + x^13 + x^11 + 1 */
|
||||||
|
bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5)) & 1;
|
||||||
|
lfsr = (lfsr >> 1) | (bit << 15);
|
||||||
|
|
||||||
|
return lfsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rand_q(void) {
|
||||||
|
q.x = rand() % 14;
|
||||||
|
q.y = rand() % 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t rev(uint8_t d) {
|
||||||
|
return d ^ 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool points_equal(const point_t *p1, const point_t *p2) {
|
||||||
|
return p1->x == p2->x && p1->y == p2->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void move(uint8_t d, bool grow) {
|
||||||
|
uint8_t p = (history_pos+history_len) % HISTORY_MAX;
|
||||||
|
|
||||||
|
uint8_t n = p >> 2, shift = (p & 3) << 1;
|
||||||
|
|
||||||
|
history[n] &= ~(3 << shift);
|
||||||
|
history[n] |= d << shift;
|
||||||
|
|
||||||
|
if (grow)
|
||||||
|
history_len++;
|
||||||
|
else
|
||||||
|
history_pos = (history_pos+1) % HISTORY_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t get_dir(uint8_t i) {
|
||||||
|
uint8_t p = (history_pos+i) % HISTORY_MAX;
|
||||||
|
|
||||||
|
uint8_t n = p >> 2, shift = (p & 3) << 1;
|
||||||
|
|
||||||
|
return (history[n] >> shift) & 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset(void) {
|
||||||
|
rand_q();
|
||||||
|
|
||||||
|
dir = EAST;
|
||||||
|
tail = (point_t){7, 4};
|
||||||
|
history_len = 0;
|
||||||
|
history_pos = 0;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
move(EAST, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kbd_handle(uint16_t code, bool make) {
|
||||||
|
if (!make)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(code) {
|
||||||
|
case KBD_CODE_UP:
|
||||||
|
dir = NORTH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KBD_CODE_LEFT:
|
||||||
|
dir = WEST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KBD_CODE_DOWN:
|
||||||
|
dir = SOUTH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KBD_CODE_RIGHT:
|
||||||
|
dir = EAST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void go(point_t *point, uint8_t d) {
|
||||||
|
switch (d) {
|
||||||
|
case NORTH:
|
||||||
|
point->y = (point->y+8)%9;
|
||||||
|
break;
|
||||||
|
case WEST:
|
||||||
|
point->x = (point->x+13)%14;
|
||||||
|
break;
|
||||||
|
case SOUTH:
|
||||||
|
point->y = (point->y+1)%9;
|
||||||
|
break;
|
||||||
|
case EAST:
|
||||||
|
point->x = (point->x+1)%14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool point_used(const point_t *p, bool head) {
|
||||||
|
point_t pt = tail;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < history_len - (head ? 0 : 1); i++) {
|
||||||
|
go(&pt, get_dir(i));
|
||||||
|
|
||||||
|
if (points_equal(&pt, p))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void step(void) {
|
||||||
|
uint8_t d = dir;
|
||||||
|
|
||||||
|
LedSignSet(tail.x, tail.y, 0);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
point_t pt = tail;
|
||||||
|
|
||||||
|
LedSignSet(pt.x, pt.y, 0);
|
||||||
|
|
||||||
|
for (i = 0; i < history_len; i++) {
|
||||||
|
go(&pt, get_dir(i));
|
||||||
|
LedSignSet(pt.x, pt.y, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
go(&pt, d);
|
||||||
|
LedSignSet(pt.x, pt.y, 3);
|
||||||
|
|
||||||
|
if (point_used(&pt, true)) {
|
||||||
|
while(true) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points_equal(&pt, &q)) {
|
||||||
|
rand_q();
|
||||||
|
move(d, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
go(&tail, get_dir(0));
|
||||||
|
move(d, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point_used(&pt, false)) {
|
||||||
|
while(true) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
LedSignSet(q.x, q.y, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
LedSignInit(GRAYSCALE);
|
||||||
|
kbd_init();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
sei();
|
||||||
|
|
||||||
|
kbd_send(KBD_CMD_RESET);
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
step();
|
||||||
|
_delay_ms(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in a new issue