📄 main.c
字号:
/******************************************CharPOV V1.02 firmwareCharPOV firmware is distributed under CC license. For more info on CC go to www.creativecommons.orgFor more info on SpokePOV go to www.ladyada.net/make/spokepovCreative Commons DeedAttribution-NonCommercial-ShareAlike 2.5You are free: * to copy, distribute, display, and perform the work * to make derivative worksUnder the following conditions:Attribution. You must attribute the work in the manner specified by the author or licensor.Noncommercial. You may not use this work for commercial purposes.Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one. * For any reuse or distribution, you must make clear to others the license terms of this work. * Any of these conditions can be waived if you get permission from the copyright holder.Your fair use and other rights are in no way affected by the above.A more detailed version of this license is available at:http://creativecommons.org/licenses/by-nc-sa/2.5/legalcode******************************************//* Modified from SpokePOV 1.01 by RJW to display messages from a character *//* set instead of simple bitmaps. The character set (ascii 32-127) is *//* stored in the first 3 banks of the external EEPROM *//* *//* Currently does not use the rotation offset or backside leds *//* ANNOTATED by RJW - trebor@animeigo.com - to further my understanding *//* of the code and environment before proceeding to make mods. All page *//* numbers refer to the ATMEL ATTiny2313 documentation located at *//* http://www.atmel.com/dyn/resources/prod_documents/doc2543.pdf *//* Any comments implying any ignorance were made by RJW! The esteemed *//* original author is by definition omniscient, and, it is feared, *//* omnipotent as well... */#include <avr/io.h>#include <avr/interrupt.h>/* We now store the message to be scrolled out on the SpokePOV in the flash *//* memory. This requires using the PROGMEM routines. For more info about *//* them, see: http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003 */#include <avr/pgmspace.h>#include "main.h"#include "eeprom.h"/* flag to determine whether we compile version that does smooth scrolling *//* between lines, or the simpler jumping. */#define SMOOTHSCROLL 1/* How many 8msec delays between each smooth scrolling increment. This *//* time * 16 is the line scroll delay time, even if not smooth scrolling */#define SCROLLSPEED 16/* The number of lines in our message *//* The maximum we can display right now is 15 to prevent 8bit overflow */#define NUM_LINES 16/* The text of the message. Since this is stored in FLASH, it requires *//* a special method of accessing it. */const char lines[] PROGMEM = " Episode IV " " A New Hope " " " " It is a " " period of " " civil war. " " " " Rebel " " spaceships, " " striking " " from a " " hidden base " " have won " " their first " " victory.. " " " ;/* The following two 16-character arrays define the message *//* the SpokePOV is currently displaying. They get updated from lines */char topLine[16];char botLine[16];#ifdef SMOOTHSCROLLchar scrollLine[16]; // the extra line for doing the smooth scrolling#endif// QUESTION: What clock speed is the ATMEL set to run at in the SpokePOV kits?// QUESTION: How does one change that?// How many ~3ms delays (-1) must elapse before we consider// a subsequent hall-effect sensor interrupt to be valid?#define HALL_DEBOUNCE_THRESH 4 // ~15ms// How many msecs the button must be held down before it is// considered to be an actual button press.#define BUTTON_DEBOUNCE 100// How many pixels in a full rotation of the SpokePOV#define NUM_PIXELS 256 // max is 255 (since it is 8 bit value)// How long after the SpokePOV stops rotating will the display continue?#define STANDBY_TIMEOUT 5 // in seconds// How long until the SpokePOV goes into sleep mode.// NOTE: not actually used, it's hard-coded instead!#define POWEROFF_TIMEOUT 2*60 // in seconds// Internal EEPROM storage locations // not used in this app (yet)#define EEPROM_ROTATION_OFFSET 0x00 // rotation offset to compensate for magnet position#define EEPROM_MIRROR 0x01 // flag telling whether oppposite side LEDs are mirrored#define EEPROM_ANIMATION 0x02 // animation activation flag#ifdef SMOOTHSCROLLuint8_t fleds[6]; // pixel array for the front LEDs (need 6 bytes)#elseuint8_t fleds[4]; // pixel array for the front LEDs (need only 4)#endifvolatile uint8_t hall_debounce; // count (via TIMER0) since last *used* Hall Effect detectionvolatile uint16_t sensor_timer; // count (via TIMER0) since last actual Hall Effect detectionvolatile uint8_t line_timer_h = 0x00; // counters used to step lines of text throughvolatile uint8_t line_timer_l = SCROLLSPEED;// the display; split into two bytes for conveniencevolatile uint8_t cur_line = 0xff; // the current 'top line'volatile uint8_t line_shift = 0x0f; // # of pixels extra shift we do to scroll smoothly// All of the following routines have been modified so they// only deal with the front leds, for speed, and to save// code space!// Move n bits of data over the serial linkvoid spi_transfer_n(uint8_t c, uint8_t n) { // Stuff the byte to transfer into the serial data register USIDR = c; // Set up the status register. On each state transition of // the serial bus (up or down), the lower 4 bits will be // incremented - so twice per bit. When that overflows, // the transfer is done. So if they start out as 0, 8 // bits get moved. But if you start it out at a different // value, you can move an arbitrary number of bits USISR = _BV(USIOIF) | (16 - (n<<1)); // While the transfer has not finished while (! (USISR & _BV(USIOIF))) { // Send out another bit USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); //NOP; // slow down so that the eeprom isnt overwhelmed }}// Move 4 bytes over the serial link (to the 1K/4K EEPROM or the// LED shift registers, or both!)uint8_t spi_transfer(uint8_t c) { USIDR = c; USISR = _BV(USIOIF); while (! (USISR & _BV(USIOIF))) { USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); //NOP; // slow down so that the eeprom isnt overwhelmed } // This could be used to return data from the rom, so it // has a return value return USIDR;}// Sends the 4-byte LED pixel data block out// over the serial link. Front LEDs onlyvoid clock_leds(void) { // QUESTION: this code sends 4 bytes over the link to // update the LEDs. But spieeprom_read_into_leds() in // eeprom.c sends 5. Why the difference? // Send the 32 bits over the serial link and down through // the shift registers. spi_transfer(fleds[0]); spi_transfer(fleds[1]); spi_transfer(fleds[2]); spi_transfer(fleds[3]); // latch the bits into the LEDS LATCH_SELECT_PORT |= _BV(FRONT); NOP; NOP; NOP; NOP; LATCH_SELECT_PORT &= ~_BV(FRONT);}// Sends 4 bytes + line_shift extra bits over the serial link// to implement smooth scrolling.void clock_scroll(void) { uint8_t sCount; // First, send the basic 4 bytes, they go no matter what spi_transfer(fleds[0]); spi_transfer(fleds[1]); spi_transfer(fleds[2]); spi_transfer(fleds[3]); // Get local copy of line_shift sCount = line_shift; // If there is anything to do.. if (sCount != 0) { // If we have < 8 bits to transfer if (sCount < 8) { // Then that is all that we need to do spi_transfer_n(fleds[4],sCount); } else { // First latch out the full first 8 bits spi_transfer(fleds[4]); // How many bits left to do? sCount = sCount - 8; if (sCount != 0) { spi_transfer_n(fleds[5],sCount); } } } // finally, latch the bits into the LEDS LATCH_SELECT_PORT |= _BV(FRONT); NOP; NOP; NOP; NOP; LATCH_SELECT_PORT &= ~_BV(FRONT);}// TIMER0 interrupt handler. This runs about every 8ms// AFAICT. It increments the hall_debounce and sensor_timer// values until they pin.// QUESTION: what's with the setting and clearing of PORTB0?// According to the wiring diagram, it isn't connected to// anything. Is this vestigial code from when you were using// Pin Change Interrupts? Or is it debugger code so you// can monitor the pins and tell when something happens.SIGNAL (SIG_TIMER0_OVF) { // *** PORTB |= 0x1; if (hall_debounce != 0xFF) hall_debounce++; if (sensor_timer != 0xFFFF) sensor_timer++; // increment the line timers line_timer_l++; // increment the low byte if (line_timer_l == 0) { // if we wrapped around, then line_timer_h++; // increment the high byte as well } // *** PORTB &= ~0x1;}// As we sweep around the circle, we display 256 radial pixel// lines, once per TIMER1 interrupt. This is broken down into// 16 16-pixel wide characters, and we have two characters// stacked vertically. To save time, we keep track of the// character number, pixel number (in the character), and// pointers into the eeprom for each of the two chars being// displayed.volatile uint16_t topChar = 0; // top character being displayed (address in EEPROM of data)volatile uint16_t botChar = 0; // bottom character being displayedvolatile uint8_t charNum = 0; // character numbervolatile uint8_t pixelNum = 0; // pixel numbervolatile uint8_t clean = 0; // have these values been changed outside TIMER1?#ifdef SMOOTHSCROLLvolatile uint16_t scrollChar = 0; // extra scroll character#endif// This routine gets called every time the pixel timer runs down;// in other words, once per "scan line", 256 times per revolution// of the SpokePOV. Its purpose is to update the LEDs.SIGNAL (SIG_TIMER1_COMPA) { uint16_t tChar; // local copies of the values uint16_t bChar; uint8_t cNum; uint8_t pNum; uint8_t cCode; // character code to display #ifdef SMOOTHSCROLL uint16_t sChar; // extra scroll character#endif // When an interrupt routine is called, interrupts are disabled. // but it's important to let other interrupts interrupt us, so // they need to be re-enabled. sei(); // *** PORTB |= 0x2; // Copy the volatile variables into their local equivalents tChar = topChar; bChar = botChar; cNum = charNum; pNum = pixelNum;#ifdef SMOOTHSCROLL sChar = scrollChar;#endif // If it has been less than STANDBY_TIMEOUT seconds since the last time we // got a Hall Effect sensor update, then proceed as normal and // update the LEDs. // QUESTION: what is F_CPU? if (sensor_timer < ((F_CPU/NUM_PIXELS)/256 * STANDBY_TIMEOUT)) { // *** PORTA |= 0x1; // The first thing we do is increment our character position; this // is done here to avoid code duplication. This means that the // Hall Effect interrupt routine must set them up so they "wrap" // into the first valid values. // Move to the next pixel in the character pNum++; // If we have moved off the edge of the character, then // we need to move to the next character if (pNum == 16) { pNum = 0; // reset to first pixel cNum = (cNum+1) % 16; // move, with wrap, to next character position // Now we need to reset the pointers to the correct addresses // in the EEPROM for the characters they display. The earlier // decision to store these in a format that makes them display // nicely on the original SpokePOV software makes this a little // bit difficult. cCode = topLine[cNum]-32; // character number for the top char, 0-95 // In the character set, 2 characters are stored vertically stacked. // So each group of 2 characters takes up 64 bytes (4x16). tChar = ((cCode >> 1) << 6) | ( (cCode & 0x01) << 1 ); // Ditto for bChar... cCode = botLine[cNum]-32; bChar = ((cCode >> 1) << 6) | ( (cCode & 0x01) << 1 ); #ifdef SMOOTHSCROLL // and if smooth scrolling, sChar cCode = scrollLine[cNum]-32; sChar = ((cCode >> 1) << 6) | ( (cCode & 0x01) << 1 );#endif } else { // If we haven't wrapped around a character boundary, we just move // to the next line in the character set, which is 4 pixels offset // in each case tChar += 4; bChar += 4; #ifdef SMOOTHSCROLL sChar += 4;#endif } // Unfortunately, we can't do the cute "read from the EEPROM right // into the LEDs trick" that limor can do in SpokePOV. We have to // read the data into the ATMEL and then write it out. spieeprom_read(tChar,fleds,2); // the top 2 bytes spieeprom_read(bChar,fleds+2,2); // and the bottom 2 #ifdef SMOOTHSCROLL spieeprom_read(sChar,fleds+4,2); // and the scroll characters#endif // However, we do have a fancy trick of our own. If we are // smooth scrolling, then we clock out an extra line_shift // BITS, thus implementing the smooth scrolling #ifdef SMOOTHSCROLL clock_scroll(); // send 0-15 extra bits.. #else clock_leds(); // and send them to the LEDs #endif // Now we increment the variables. However, we // only do it IF haven't been touched by some other part // of the code while we were displaying the pixels. Also, we // turn interrupts off so that nobody else can touch them // while we are at it cli(); if (clean) { topChar = tChar; botChar = bChar; charNum = cNum; pixelNum = pNum; #ifdef SMOOTHSCROLL scrollChar = sChar; #endif } else { // Since we didn't update the data, we know it was changed, and // we know that next time, we CAN update the data. So everything // is clean now! clean = 1; } sei(); // *** PORTA &= ~0x1; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -