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

📄 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"#define NULL ((void *)0)/* 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/* 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 0/****************************************************************************//*                                                                          *//* 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										*/const char lines[] PROGMEM =//	"   RPM COUNT:   "    "\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F"	"      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 lines[], tells us this info.		*//* 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 starts 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!)		*//* 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,0x28,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?    char *dynamicPtr = NULL;  volatile char *newDynamicPtr = NULL;  volatile uint8_t dynamicType = 0x00;    volatile char dynamicCounter[] = "0000";			// Rev counter (also reused by other dynamic stuff)    #ifdef DYNAMIC_RPM    	 // 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  #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  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 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 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// 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;  if (hall_debounce != 0xFF)    hall_debounce++;    if (sensor_timer != 0xFFFF)    sensor_timer++;  #if NUM_LINES > 0    // Increment the line timers - we only need to do this    // if we are scrolling      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    }    #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 numbervolatile uint8_t pixelNum = 0;		// pixel number#ifdef USE_LOCAL_TIMER1  volatile uint8_t clean = 0;	    // have these values been changed outside TIMER1?#endif#ifdef SMOOTHSCROLL  volatile 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) {  uint8_t cCode;					// character code to display    #ifdef USE_LOCALUSE_LOCAL_TIMER1      uint16_t tChar;					// local copies of the values    uint16_t bChar;    uint8_t cNum;						    uint8_t pNum;      #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();    // Copy the volatile variables into their local equivalents      tChar = topChar;    bChar = botChar;    cNum = charNum;    pNum = pixelNum;    #ifdef SMOOTHSCROLL       sChar = scrollChar;    #endif      #else      #define	tChar	topChar    #define	bChar	botChar    #define	cNum	charNum    #define	pNum	pixelNum    #define	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    

⌨️ 快捷键说明

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