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

📄 spokepov.lss

📁 旋转16个LED灯控制程序
💻 LSS
📖 第 1 页 / 共 5 页
字号:
  // value, then we use it to reset the TIMER1 clock.
    
  // if ((sensor_timer.bytes.low_byte < 0xFF) && (sensor_timer.bytes.low_byte > 0x03)) {
  if ((sensor_timer.bytes.high_byte == 0x00) || (sensor_timer.bytes.low_byte > 0x03)) {
    
    // 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);
    
    OCR1AH = sensor_timer.bytes.low_byte;
    OCR1AL = 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
		
	  dynamicREV[0]++;
	  dCode = 0;
	  
	  while (dynamicREV[dCode] > '9') {
		dynamicREV[dCode] = '0';
		if (dCode != 3) {
		  dynamicREV[++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 >= SCROLLSPEED) {
    
        line_timer = line_timer - SCROLLSPEED;	// 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.  Index through the
	      // lineOffsets array
	      
	      dCode = pgm_read_byte(lineOffsets+cur_line);
	  
	      memcpy_P(topLine,lines+dCode,16);
	      
	      // If we are doing dynamic data, set it if there
	      // is such data in the line.
	      
	      #ifdef DYNAMIC
	      
	        dynamicType = 0x00;					// assume no dynamic data will be shown
	        
	        dCode = pgm_read_byte(dInfo+cur_line);
	        
	        if (dCode != 0) {
	          dynamicPtr = topLine + (dCode & 0x0F);
	          dynamicType = dCode;
	        }
	        
	      #endif
	  
	      // Get the second line, which may wrap, but since we have
	      // extra entries in lineOffsets, this is not a problem!
	  
	      cLine = cur_line + 1;
	      
	      dCode = pgm_read_byte(lineOffsets+cLine);

	      memcpy_P(botLine,lines+dCode,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) {
	          dynamicPtr = botLine + (dCode & 0x0F);
	          dynamicType = dCode;
	        }
	        
	      #endif
	  
		  #ifdef SMOOTHSCROLL

	        // get the third line, which may wrap..
	  
	        cLine++;
	      
	        dCode = pgm_read_byte(lineOffsets+cLine);

	        memcpy_P(scrollLine,lines+dCode,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) {
	            dynamicPtr = 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.
    
      // Note that when there are only two lines, there is no
      // need for the lineOffset array, and it is not used.
      
	  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) {
	       dynamicPtr = topLine + (dCode & 0x0F);
	       dynamicType = dCode;
	    }
	        
	    dCode = pgm_read_byte(dInfo+1);
	        
	    if (dCode != 0) {
	       dynamicPtr = 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.
    
    // If we are not doing halfshifts, this is simple.
    
    #ifndef HALFSHIFT
    
      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
    
    #else
    
      // If doing half-shifts, we need to check the first byte of each buffer
      // and adjust things if it says to do a half-shift
      
      // if not shifted
      
      if ((topLine[0] & 0x80) == 0x00) {
      
        charNum = 31;
        pixelNum = 15;
        
      } else {
      
        charNum = 0;		// shifted, so start in middle of first char
        pixelNum = 7;
        topLine[0] &= 0x7F;	// and clear hi bit!
        
      }
      
      // repeat for bottom line
      
      if ((botLine[0] & 0x80) == 0x00) {
      
        charNum2 = 31;
        pixelNum2 = 15;
        
      } else {
      
        charNum2 = 0;
        pixelNum2 = 7;
        botLine[0] &= 0x7F;
        
      }
      
      // and for scroll buffer
      
      #ifdef SMOOTHSCROLL
      
        if ((scrollLine[0] & 0x80) == 0x00) {
      
          charNum3 = 31;
          pixelNum3 = 15;
        
        } else {
      
          charNum3 = 0;
          pixelNum3 = 7;
          scrollLine[0] &= 0x7F;
        
        }

      #endif
      
    #endif
    
    // Start TIMER1 on its merry way...
      
    TCCR1B |= _BV(CS10);		// increment at clock/1
    TIMSK |= _BV(OCIE1A);		// enable interrupt when it matches OCR1A
    
  } else {
    
    // Since we don't have a valid setting for the rotation
    // speed, set a couple of LEDs to let the human know we
    // aren't dead yet, and turn off the timer.
      
    set_all(~0x03);
      
    TCCR1B &= ~_BV(CS10);		// no incrementing = no interrupting
    
    // reset the line timers so that when we get a valid spinup,
    // they will start clocking the lines across the display
    
    line_timer = SCROLLSPEED;		// delay figure, will trigger wrap
    line_shift = 0x0f;				// subcharacter shift, will trigger wrap
    cur_line = NUM_LINES-1;			// will wrap to first line
    
  }   
    
  // Whether we're displaying or not, we reset sensor_timer so we can
  // time the next revolution.
    
  sensor_timer.word = 0;

  }
  
  // Finally, reset hall_debounce so we won't execute the timer reset code
  // until the Hall Effect sensor hasn't bothered us for a reasonable while.
  
  hall_debounce = 0;
  
  // *** PORTB &= ~0x8;

}


// Initialize the IO pins on the ATMEL.

void ioinit(void) {

  // Set the data direction for the PORTD and PORTB pins; see the
  // circuit diagram for more information on this.
  
  DDRD = 0x73; // input on PD2 (button), PD3 (sensor), all other output
  DDRB = 0xDF; // input on MOSI/DI (for SPI), all others output

  // Deselect EEPROM.  Not being an EE, I'm not going to worry about
  // how the ATMEL talks to the EEPROM.  It's black magic.
  
  PORTB = _BV(SPIEE_CS);

  // Just above, we set PD2 and PD3 to input.  If we now set those
  // bits to 1, they set into pullup mode (again, not EE, claim
  // ignorance), which is essential for them to work.  We also set
  // the SENSORPOWER bit to 1, which sends out little dribbles of
  // electrons to the hall effect sensor (see circuit diagram)
  //
  // Finally, we write 0's to the FRONT and BACK pins, which control
  // which bank of 30 LEDs we are talking to.  Having both of these
  // on at the same time probably causes horrible things to happen.
  
  PORTD = (_BV(BUTTON) | _BV(SENSOR) | _BV(SENSORPOWER))  
    & ~_BV(FRONT) & ~_BV(BACK);

  // Rather than poll to see when the hall effect sensor and
  // button are pressed, we configure an interrupt handler.  If you
  // look at the circuit diagram, you'll see that PD3 and PD2, which
  // are wired to SENSOR IN and BUTTON IN, do double-duty as INT1
  // and INT0.  They are both SUPPOSEDLY set to interrupt on the
  // falling edge of a pulse from the devices.  (Page 63)
  
  // POSSIBLE BUG: ISC0{1,0} seems to be being set to 00, not 10
  // as ISC1{1,0} is being set to.  So ISC0 will trigger when
  // the button interrupt line goes low.  Either this is a bug,
  // or the original comment was not correct (likely, IMHO)
  
  MCUCR = _BV(ISC11) & ~_BV(ISC01) & ~_BV(ISC00) &  ~_BV(ISC10);

  // Activate the interrupts by setting the General Interrupt Mask
  // Register (Page 63)
  
  GIMSK = _BV(INT1) | _BV(INT0);

  // The ATMEL has built-in timers that can trigger an interrupt.
  // SpokePOV uses them to update the LEDs 256 times per rotation.
  
  // Timer 0 is set to update at a rate system-clock / 256 and
  // interrupt when it overflows (8 bit).  This means that it
  // triggers every 65536 cycles.
  
  TCCR0A = 0;				// normal, overflow (count up to 256 == num pixels)
  TCCR0B = _BV(CS02);		// clk/256
  TIMSK |= _BV(TOIE0);		// turn on overflow interrupt
  
  // Timer 1 (T1) is the pixel timer, which is used to update the
  // LEDs 256 times per rotation.  It's set up as a normal timer
  // as well.  See Page 108&71; it is apparently being set into CTC
  // mode 4.  This means that the counter is compared to a 16-bit value
  // and interrupts when it reaches this value.
  //
  // Adjusting this value is how the SpokePOV compensates for
  // changes in the rotation speed of the device.
  //
  // Note that at this point, the timer is initialized, but not
  // activated.
  
  TCCR1A = 0;
  TCCR1B = _BV(WGM12);

  // Clear the debounce values, which I haven't sussed out yet.
  
  hall_debounce = 0;
  sensor_timer.word = 0;
  
}

// Turn on a single LED, turning off all the other LEDs

//void set_led(uint8_t led) {

//  fleds[0] = fleds[1] = fleds[2] = fleds[3] = 0xFF;
//  fleds[led >> 3] = ~_BV(led & 0x7F);

//  clock_scroll(0);
//}

// Set all the LEDs on a side to have the same
// repeating 8-bit value (ie: 0x00 = all on, 0xFF = all off)
// Added by RJW to permit a more comprehensive reset display

void set_all(uint8_t blockValue) {

⌨️ 快捷键说明

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