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

📄 main.c

📁 旋转16个LED灯控制程序
💻 C
📖 第 1 页 / 共 3 页
字号:
    pNum++;        // If we have moved off the edge of the character, then    // we need to move to the next character        if (pNum == 16) {          // If we will wrap around to the first character, turn off the pixel      // timer, clear the display, and exit.  Might speed things up a bit            if (cNum == 15) {        TCCR1B &= ~0x7;        set_all(~0x00);        return;      }      pNum = 0;					// reset to first pixel      cNum = (cNum+1) & 0x0F;	// 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(line_shift);			// send 0-15 extra bits..	    #else      clock_scroll(0);					// just send the 4 bytes        #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        #ifdef USE_LOCAL_TIMER1        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();        #endif#ifdef DONOTCOMPILE  // The following code is obsoleted by the fact that we turn off the  // timer in the main code, above.    } else {        // We have not seen the magnet in a while, so turn off the    // pixel timer...        // *** PORTA |= 0x2;    // Turn off this pixel timer    // Question: this is different from the code in SIG_INT1.  Why?        TCCR1B &= ~0x7;     // display reason for turnoff        set_all(~0x07);    // *** PORTA &= ~0x2;#endif  }  // *** PORTB &= ~0x2;  }// Interrupt 0 executes when the button is pressed.// QUESTION: unlike the pixel output interrupt, this one// doesn't sei().  Why?SIGNAL (SIG_INT0) {    uint16_t timer;  // *** PORTB |= 0x4;    // Twiddle our thumbs until the user releases the  // button - but measure how long he takes...    timer = 0;  while (! (BUTTON_PIN & _BV(BUTTON))) {    timer++;    delay_ms(1);  }    // A short (<500ms) press will just restart the watchdog  // timer.  I think this explains the structure of the  // main() function; it doesn't have a loop to keep it  // listening for commands if it times out, but pressing  // the button will restart it.    // We do expect that the button will be down at least  // a small period of time...    if (timer > BUTTON_DEBOUNCE) {    	// If a quick press...  	    if (timer < 500UL) {            // Re-enable the watchdog timer, then loop until      // it fires off.            WDTCSR = _BV(WDE);      while (1);          } else {            // We want to shut everything down.  Setting sensor_timer      // to the pin value will cause both the communications      // loop and the regular timeout loop in the main() to      // give up, which results in the device going to sleep.            sensor_timer = 0xFFFF;          }  }    // *** PORTB &= ~0x4;}// Interrupt 1 executes when the hall effect sensor fires// QUESTION: unlike the pixel output interrupt, this one// doesn't sei().  Why?SIGNAL (SIG_INT1) {    #if NUM_LINES > 0    uint8_t cLine;		// temp var used in scroll code   #endif    #ifdef DYNAMIC    	uint8_t dCode;		// temp var used in dynamic code  	  #endif  // make sure we don't get bitten by the watchdog    asm("wdr");  // *** PORTB |= 0x8;  // The first issue we need to deal with when the hall-effect  // sensor tells us it sees the magnet is to avoid doing any  // processing if we get an interrupt too soon after the previous  // interrupt.    // hall_debounce is incremented by TIMER0, which fires every 3ms  // or so.  At the current setting of 4, this means that at least  // 15ms must elapse per trigger, which translates to about 4000  // rpm.    if (hall_debounce > HALL_DEBOUNCE_THRESH) {  // We know the number of ms since the last hall sensor trigger  // and there are 128 radial 'pixels' per sweep so divide to get  // the necessary ms between the pixel interrupts      // QUESTION: 128 or 256?      // Then we just make TIMER1 trigger at that rate!      // Reset the Timer Count Register for TIMER1 to 0, so it will  // begin counting up.      TCNT1 = 0;      // sensor_timer contains the number of TIMER0 interrupts since  // the last time we updated TIMER1.  If it has a reasonable  // value, then we use it to reset the TIMER1 clock.      if ((sensor_timer < 0xFF) && (sensor_timer > 0x3)) {        // TIMER1 works differently from TIMER0.  It's a 16-bit timer    // that apparently increments at the system clock rate.    //    // Because TIMER0 increments at 1/256 of the clock rate, and    // fires the interrupt only when it overflows, sensor_timer    // is incremented once ever 256*256 cycles.    //    // We want TIMER1 to fire off 256 times around the loop, so    // we can display 256 lines of pixels.  We do this by putting    // sensor_timer into the high byte of TIMER1's comparator    // value, and the residual of TIMER0 (what it's counted up    // to since the last time sensor_timer was incremented) into    // the low byte, effectively a fractional value!    //    // Since TIMER0 is incrementing at 1/256 of the rate of TIMER1,    // this results in TIMER1 firing off 256 times per rotation,    // with excellent time resolution.    //    // I was quite touched by the elegance of how this works out;    // it may be able to handle the extreme RPMs of BrightSaber    // without modification...          // Set the TIMER1 comparator value.  As a hack to Limor's hack,    // reduce the timing a little bit so that the display doesn't    // span the full 360 degrees, thus giving us a little more time    // around hall-effect interrupt time to do things.  An attempt    // to get more speed, it doesn't seem to help - so back to the    // old way of doing things.        // OCR1A = ((sensor_timer << 8) | TCNT0) - (sensor_timer);        OCR1A = ((sensor_timer << 8) | TCNT0);        // Clear the residual of TIMER0          TCNT0 = 0;	// If we are in dynamic mode and want the rev counter, increment the rev counter		#ifdef DYNAMIC_REVCOUNT		  // Increment the 4 byte rev counter - stored in reverse	  // byte order			  dynamicCounter[0]++;	  dCode = 0;	  	  while (dynamicCounter[dCode] > '9') {		dynamicCounter[dCode] = '0';		if (dCode != 3) {		  dynamicCounter[++dCode]++;			// Dont'cha just love compressed C syntax...?		}      }			#endif		// if we have only 2 lines, then we never scroll	// otherwise, we have to move between the lines.	// The code for no scrolling is NUM_LINES = 0	    #if NUM_LINES > 0      // Check the line timer; if it has reached a particular value      // then increment line_shift.  When that reaches 16, wrap it      // and increment cur_line.  This system lets us be a little      // more flexible in our timing system, and permits long delays      // in the scrolling.          if (line_timer_l > 15) {            line_timer_l = line_timer_l - 16;		// reset in a safe way that retains any residual        line_shift = (line_shift + 1) & 0x0f;	// increment line_shift              if (line_shift == 0x00) {	      // Move down 1 line in the line list, wrapping around	  	  // Made the mistake of using % which isn't a good thing	  	  // on a chip without mult/div...	  	  	      cur_line++;	      	      if (cur_line == NUM_LINES) {	        cur_line = 0;	      }	  	      // Move the new first line into topLine.  We must shift	      // the line number 4 bits (x16) to index the correct line.	  	      memcpy_P(topLine,lines+(cur_line << 4),16);	      	      // If we are doing dynamic data, set it if there	      // is such data in the line.	      	      #ifdef DYNAMIC	      	        dCode = pgm_read_byte(dInfo+cur_line);	        	        if (dCode != 0) {	          newDynamicPtr = topLine + (dCode & 0x0F);	          dynamicType = dCode;	        }	        	      #endif	  	      // Get the second line, which may wrap..	  	      cLine = (cur_line + 1);	      	      if (cLine == NUM_LINES) {	        cLine = 0;	      }	      memcpy_P(botLine,lines+(cLine << 4),16);		  	      // If we are doing dynamic data, set it if there	      // is such data in the line.	      	      #ifdef DYNAMIC	      	        dCode = pgm_read_byte(dInfo+cLine);	        	        if (dCode != 0) {	          newDynamicPtr = botLine + (dCode & 0x0F);	          dynamicType = dCode;	        }	        	      #endif	  		  #ifdef SMOOTHSCROLL	        // get the third line, which may wrap..	  	        cLine++;	      	        if (cLine == NUM_LINES) {	          cLine = 0;	        }	  	        memcpy_P(scrollLine,lines+(cLine << 4),16);	    		        // If we are doing dynamic data, set it if there	        // is such data in the line.	      	        #ifdef DYNAMIC	      	          dCode = pgm_read_byte(dInfo+cLine);	        	          if (dCode != 0) {	            newDynamicPtr = scrollLine + (dCode & 0x0F);	            dynamicType = dCode;	       	  }	        	        #endif	  		  #endif		        }          }    #else      // we could do this just once when the app initializes, but      // for now, let's do it here.  Later we'll move it.    	  cur_line = line_shift = 0;	  memcpy_P(topLine,lines,16);	  memcpy_P(botLine,lines+16,16);		  #ifdef DYNAMIC	      	    dCode = pgm_read_byte(dInfo);	        	    if (dCode != 0) {	       newDynamicPtr = topLine + (dCode & 0x0F);	       dynamicType = dCode;	    }	        	    dCode = pgm_read_byte(dInfo+1);	        	    if (dCode != 0) {	       newDynamicPtr = botLine + (dCode & 0x0F);	       dynamicType = dCode;	    }	        	  #endif	    #endif    // Set the character and pixel numbers so they will overflow    // on the next pixel interrupt, and cause the correct data to    // be loaded.        charNum = 31;		// will wrap to 0, the first char.  Not set to 15 as you might    					// expect, because when it hits 15 again, the pixel output    					// routine will shut down.  But (31+1) mod 16 = (15+1) mod 16...    pixelNum = 15;		// will wrap to 0, the first pixel            #ifdef USE_LOCAL_TIMER1      clean = 0;		// flag that we changed things    #endif        // Start TIMER1 on its merry way...          TCCR1B |= _BV(CS10);		// increment at clock/1    TIMSK |= _BV(OCIE1A);		// enable interrupt when it matches OCR1A

⌨️ 快捷键说明

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