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

📄 spokepov.lss

📁 旋转16个LED灯控制程序
💻 LSS
📖 第 1 页 / 共 5 页
字号:
        TCCR1B &= ~0x7;
        set_all(~0x00);
        return;
      }

      pNum = 0;					// reset to first pixel
      cNum = (cNum+1) & 0x0F;	// move, with wrap, to next character position
      
      // If we have wrapped around to the first character, turn off the pixel
      // timer, clear the display, and exit.
      
      if (cNum == 15) {
        TCCR1B &= ~0x7;
        set_all(~0x00);
        return;
      }
      
      // 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

⌨️ 快捷键说明

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