⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 旋转16个LED灯控制程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/******************************************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 *//* IMPORTANT: read the erratta postings after the main posting, there are   *//* a couple that can bite you in the ass (esp: pgm_read_byte example)		*/#include <avr/pgmspace.h>/* The spi_ transfer routines are now in eeprom.c.  There is a define in	*//* eeprom.c as to whether they are functions or inline code					*/#include "main.h"#include "eeprom.h"/* We always need to define nothing...										*/#define NULL ((void *)0)/* Define a composite type that can be used to access a 16-bit variable in	*//* a variety of ways.  This will help with code-squeezing because it lets   *//* us often init variables with only 8 bit constants, lets us do 8 bit		*//* shifts for free, etc.  The cost, of course, is a little code readability,*//* so please forgive me for this.											*/typedef union {  uint16_t	word;    struct {      uint8_t low_byte;    uint8_t high_byte;      } bytes;    char *ptr;  } var16bit;/* Cute flag that can be used to easily comment out blocks of code			*/#define DONOTCOMPILE	1/* flag to determine whether we compile version that does smooth scrolling	*//* between lines, or the simpler jumping.  COMMENT OUT to disable!			*/#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/* Do we need the code for dynamic line update (RPM, etc)?	COMMENT OUT		*//* to disable this feature.													*/#define DYNAMIC	1/* Define what dynamic features are being used (comment out to disable)		*/#define	DYNAMIC_REVCOUNT	1		// blade revolution counter#define DYNAMIC_RPM			1		// RPM value// #define DYNAMIC_TIME	1				// Time of Day Clock (faked, of course)/* The number of lines in our message - basically limited by the amount of	*//* program memory left over.  If NumLines is 0, then no scrolling will      *//* happen, and the display will be static (and must have 2 lines)		    */#define NUM_LINES 6/****************************************************************************//*                                                                          *//* IMPORTANT NOTE ABOUT VARIABLES!                                          *//*                                                                          *//* This firmware is on the ragged edge of running out of RAM for variables  *//* and stack when all the features (SMOOTHSCROLL,DYNAMIC) are turned on.    *//*                                                                          *//* If you modify it and start seeing displays where some of the characters	*//* are displayed OK but others are mangled, this is a symptom that you have *//* run out of RAM!                                                          *//*                                                                          *//* REMEMBER: function calls use up RAM as well...							*//*                                                                          *//****************************************************************************//* The text of the message.  Since this is stored in FLASH, it requires		*//* a special method of accessing it.										*//* 0x7F is the last character, now a full block of bits, so useful for      *//* doing speed calculations vs. video										*//* First we have an array of offsets into the lines[] array.  For each		*//* line that is displayed, this points to the BYTE in lines[] the data is	*//* actually stored.  This level of indirection has two advantages; repeated	*//* lines (such as fully blank ones) need only be stored once, and we can	*//* eliminate the << 4 (*16) operation needed to go from line number to		*//* line offset in the PROGMEM												*//* As a further code-saving trick, lineOffsets must have NUM_LINES+2		*//* entries, and the last 2 entries duplicate the first two.  This means  	*//* that our lookup routine does not have to worry about wrapping around 	*//* when it indexes, which makes life simpler!								*//* lineOffsets is NOT used when NUM_LINES is 0; that would be a waste of	*//* time.																	*/#if (NUM_LINES != 0)  const uint8_t lineOffsets[] PROGMEM = {0x00,0x10,0x20,0x00,0x00,0x30,0x00,0x10};#endifconst char lines[] PROGMEM =	"                "		// keep the first line in the buffer as all blanks    "    SpokePOV    "	"    RPM:0000    "	"    REV:0000    "	;/* If we have dynamic data, then we need to define what it is and where it	*//* goes.  An array of bytes, 1 per line in lineOffsets[], tells us this		*//* info.  Note that this means that like lineOffsets, you need NUM_LINES+2	*//* entries, with the last 2 a duplicate of the first two...					*//* In order to keep things reasonable, you can only have one bit of dynamic *//* data being displayed at any time.  In other words, one per 2 lines if	*//* doing normal scrolling, or every 3 lines if doing smooth scrolling		*//* The high 4 bit define what we display, and the low 4 bits define what	*//* character position it ENDS at.  Like lines[], this is a PROGMEM.		    *//* Dynamic Info Codes:														*//*																			*//*		00 - No dynamic data in line										*//*		1x - 4 digit Rev Counter at character x-3							*//*		2x - 3 digit RPM display at character x-2 (note difference!)		*//*		3x - Time display (12:34:56)										*//* Note that you define pos of LAST character of the dynamic data, not the  *//* first.  Makes code simpler. 												*/#ifdef DYNAMIC  const uint8_t dInfo[] PROGMEM = {0x00,0x00,0x2A,0x00,0x00,0x1B,0x00,0x00};  // We also need to store the current dynamic effect, and the location that  // needs to be updated in the RAM lines.  As this updating is done in the  // main loop, we need two copies of the pointer, since the interrupt  // routines will likely update the pointer while the main loop is trying  // to write stuff.  The reason we do updates in the main loop is in order  // to make the interrupt routines as fast as possible; the dynamic updating  // thus gets done with spare CPU cycles.    // Since only one active line can have a dynamic data, when the display  // scrolls and updates the ...Line[] arrays, the address will certainly  // change, so this is a reliable detection method.    // QUESTION: is the compiler smart enough to know that these pointers  // should only be 8 bits?    volatile char *dynamicPtr = NULL;				// use hibit to flag as dirty...  volatile uint8_t dynamicType = 0x00;    #ifdef DYNAMIC_REVCOUNT      volatile char dynamicREV[] = "0000";		// 4 byte rev counter    #endif    #ifdef DYNAMIC_RPM       volatile char dynamicRPM[] = "000";		// 3 byte RPM counter       	 // Our big problem with RPM calculations is multiplication and division  	 // which we don't have space to do properly.  Now, at 8mhz, if the pixel  	 // clock is exactly 0x0100 (ie: TIMER0 counted exactly once per rev), then  	 // the revolution took (256*256)/8,000,000 of a second, or 0.008192 seconds.  	 // That corresponds to 7324.2 RPM, a speed we are unlikely to see anytime  	 // soon.  For simplicity, we'll only keep the top 3 digits of RPM and make  	 // the bottom digit 0.  	 //  	 // However, to get from our pixel clock timer value to the RPM/10 means we  	 // need to do (binary fractions) 732.0 / timerhi.timerlo, which is ugly.  	 // And on top of that, we then need to do /10's to get the digits.  Yuck!  	 //  	 // Instead, we use a special table lookup to compute the RPM directly into  	 // a 3 digit decimal number, using the usual sleazy tricks.  	      const char div732[] PROGMEM =            /* UNCALIBRATED RPM VALUES.  Use these as a starting point              "\x00\x00\x01"		// use escapes so we don't have to convert between       "\x00\x00\x03"		// the actual digits and the ascii digit representation       "\x00\x00\x06"		// (+48).  Saves us a few bytes of code.       "\x00\x01\x01"       "\x00\x02\x03"       "\x00\x04\x06"       "\x00\x09\x02"       "\x01\x08\x03"       "\x03\x06\x06";  	     	   */  	     	   /* CALIBRATED RPM VALUES for my SpokePOV */  	     	   "\x00\x00\x01"  	   "\x00\x00\x03"  	   "\x00\x00\x05"  	   "\x00\x01\x01"  	   "\x00\x02\x02"  	   "\x00\x04\x03"  	   "\x00\x08\x06"  	   "\x01\x07\x03"  	   "\x03\x04\x05";  	     #endif    #ifdef DYNAMIC_TIME    	// Since we don't have a real clock source on the SpokePOV, we have to  	// fake it.  The trick here is to have a preset time that the chip  	// resets to when powered up or reset.  	  #endif  #endif/* 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 SMOOTHSCROLL  char 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  10// 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 SMOOTHSCROLL							// should also take into account NUM_LINES?  uint8_t fleds[6];							// pixel array for the front LEDs (need 6 bytes)#else  uint8_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 var16bit sensor_timer;				// count (via TIMER0) since last actual Hall Effect detectionvolatile uint8_t line_timer = SCROLLSPEED;	// counter for scrolling/line stepping; realized it only needs to be 8 bitsvolatile uint8_t cur_line = 0xff;			// the current 'top line'volatile uint8_t line_shift = 0x0f;			// # of pixels extra shift we do to scroll smoothly// Sends the 4-byte LED pixel data block out// over the serial link.  Front LEDs only// Sends 4 bytes + sCount extra bits over the serial link// to implement smooth scrolling.  Can be used with a 0// parameter to just send out the regular 4 bytes.void clock_scroll(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]);   // 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;  // Let hall_debounce just wrap around.  At worst it'll just  // mess things up for one of the initial speedup rotations  // of the blade...    // if (hall_debounce != 0xFF)    hall_debounce++;    if (sensor_timer.bytes.high_byte != 0xFF)    sensor_timer.word++;  #if NUM_LINES > 0    // Increment the line timer - we only need to do this    // if we are scrolling      line_timer++;				// increment the low byte    #endif// *** PORTB &= ~0x1;}// Hack - I used to copy the topChar, etc. variables into local// variables in the TIMER1 routine, but I am running out of stack space.  So instead// I'm going to keep interrupts off during this routine and// use the regular versions.  To make it easy to do this I'm// remapping the local variables with defines.  // #define USE_LOCAL_TIMER1	1// If USE_LOCAL_TIMER1 is not defined, then the TIMER1 routine will not// enable interrupts.  // 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.// This code has to be fast enough to complete execution before// it gets interrupted again - and it must not make any subroutine// calls, since that'll mess up the stack and cause the entire// system to reset.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 number

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -