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

📄 spokepov.lss

📁 旋转16个LED灯控制程序
💻 LSS
📖 第 1 页 / 共 3 页
字号:
    // 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
      
    OCR1A = (sensor_timer << 8) | TCNT0;
      
    // Clear the residual of TIMER0
      
    TCNT0 = 0;
      
    // Set the character and pixel numbers so they will overflow
    // on the next pixel interrupt, and cause the correct data to
    // be loaded.
    
    charNum = 15;		// will wrap to 0, the first char
    pixelNum = 15;		// will wrap to 0, the first pixel
    clean = 0;			// flag that we changed things
          
    // 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_led(2);
      
    TCCR1B &= ~_BV(CS10);		// no incrementing = no interrupting
  }   
    
  // Whether we're displaying or not, we reset sensor_timer so we can
  // time the next revolution.
    
  sensor_timer = 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 = 0;
  
}

// Delay for a specified number of milliseconds using some
// assembly code.  Will this be dependant on the clock speed?

void delay_ms(unsigned char ms)
{
  unsigned short delay_count = F_CPU / 4000;
  ae:	20 ed       	ldi	r18, 0xD0	; 208
  b0:	37 e0       	ldi	r19, 0x07	; 7

000000b2 <L_dl140>:
  
  unsigned short cnt;
  asm volatile ("\n"
  b2:	e2 2f       	mov	r30, r18
  b4:	f3 2f       	mov	r31, r19

000000b6 <L_dl240>:
  b6:	31 97       	sbiw	r30, 0x01	; 1
  b8:	f1 f7       	brne	.-4      	; 0xb6 <L_dl240>
  ba:	a8 95       	wdr
  bc:	8a 95       	dec	r24
  be:	c9 f7       	brne	.-14     	; 0xb2 <L_dl140>
  c0:	08 95       	ret

000000c2 <__vector_1>:
  c2:	1f 92       	push	r1
  c4:	0f 92       	push	r0
  c6:	0f b6       	in	r0, 0x3f	; 63
  c8:	0f 92       	push	r0
  ca:	11 24       	eor	r1, r1
  cc:	2f 93       	push	r18
  ce:	3f 93       	push	r19
  d0:	4f 93       	push	r20
  d2:	5f 93       	push	r21
  d4:	6f 93       	push	r22
  d6:	7f 93       	push	r23
  d8:	8f 93       	push	r24
  da:	9f 93       	push	r25
  dc:	af 93       	push	r26
  de:	bf 93       	push	r27
  e0:	cf 93       	push	r28
  e2:	df 93       	push	r29
  e4:	ef 93       	push	r30
  e6:	ff 93       	push	r31
  e8:	c0 e0       	ldi	r28, 0x00	; 0
  ea:	d0 e0       	ldi	r29, 0x00	; 0
  ec:	82 99       	sbic	0x10, 2	; 16
  ee:	05 c0       	rjmp	.+10     	; 0xfa <__stack+0x1b>
  f0:	21 96       	adiw	r28, 0x01	; 1
  f2:	81 e0       	ldi	r24, 0x01	; 1
  f4:	dc df       	rcall	.-72     	; 0xae <delay_ms>
  f6:	82 9b       	sbis	0x10, 2	; 16
  f8:	fb cf       	rjmp	.-10     	; 0xf0 <__stack+0x11>
  fa:	c5 36       	cpi	r28, 0x65	; 101
  fc:	d1 05       	cpc	r29, r1
  fe:	60 f0       	brcs	.+24     	; 0x118 <__stack+0x39>
 100:	c4 5f       	subi	r28, 0xF4	; 244
 102:	d1 40       	sbci	r29, 0x01	; 1
 104:	18 f4       	brcc	.+6      	; 0x10c <__stack+0x2d>
 106:	88 e0       	ldi	r24, 0x08	; 8
 108:	81 bd       	out	0x21, r24	; 33
 10a:	ff cf       	rjmp	.-2      	; 0x10a <__stack+0x2b>
 10c:	8f ef       	ldi	r24, 0xFF	; 255
 10e:	9f ef       	ldi	r25, 0xFF	; 255
 110:	90 93 8c 00 	sts	0x008C, r25
 114:	80 93 8b 00 	sts	0x008B, r24
 118:	ff 91       	pop	r31
 11a:	ef 91       	pop	r30
 11c:	df 91       	pop	r29
 11e:	cf 91       	pop	r28
 120:	bf 91       	pop	r27
 122:	af 91       	pop	r26
 124:	9f 91       	pop	r25
 126:	8f 91       	pop	r24
 128:	7f 91       	pop	r23
 12a:	6f 91       	pop	r22
 12c:	5f 91       	pop	r21
 12e:	4f 91       	pop	r20
 130:	3f 91       	pop	r19
 132:	2f 91       	pop	r18
 134:	0f 90       	pop	r0
 136:	0f be       	out	0x3f, r0	; 63
 138:	0f 90       	pop	r0
 13a:	1f 90       	pop	r1
 13c:	18 95       	reti

0000013e <ioinit>:
 13e:	83 e7       	ldi	r24, 0x73	; 115
 140:	81 bb       	out	0x11, r24	; 17
 142:	8f ed       	ldi	r24, 0xDF	; 223
 144:	87 bb       	out	0x17, r24	; 23
 146:	80 e1       	ldi	r24, 0x10	; 16
 148:	88 bb       	out	0x18, r24	; 24
 14a:	8c e4       	ldi	r24, 0x4C	; 76
 14c:	82 bb       	out	0x12, r24	; 18
 14e:	98 e0       	ldi	r25, 0x08	; 8
 150:	95 bf       	out	0x35, r25	; 53
 152:	80 ec       	ldi	r24, 0xC0	; 192
 154:	8b bf       	out	0x3b, r24	; 59
 156:	10 be       	out	0x30, r1	; 48
 158:	84 e0       	ldi	r24, 0x04	; 4
 15a:	83 bf       	out	0x33, r24	; 51
 15c:	89 b7       	in	r24, 0x39	; 57
 15e:	82 60       	ori	r24, 0x02	; 2
 160:	89 bf       	out	0x39, r24	; 57
 162:	1f bc       	out	0x2f, r1	; 47
 164:	9e bd       	out	0x2e, r25	; 46
 166:	10 92 8a 00 	sts	0x008A, r1
 16a:	10 92 8c 00 	sts	0x008C, r1
 16e:	10 92 8b 00 	sts	0x008B, r1
 172:	08 95       	ret

00000174 <spi_transfer>:
		"L_dl1%=:\n\t"
		"mov %A0, %A2\n\t"
		"mov %B0, %B2\n"
		"L_dl2%=:\n\t"
		"sbiw %A0, 1\n\t"
		"brne L_dl2%=\n\t"
		"wdr\n\t"
		"dec %1\n\t" "brne L_dl1%=\n\t":"=&w" (cnt)
		:"r"(ms), "r"((unsigned short) (delay_count))
		);
}

// All of the following routines have been modified so they
// only deal with the front leds, for speed, and to save
// code space!

// Sends the 4-byte LED pixel data block out
// over the serial link.  Front LEDs only

void clock_leds(void) {

  // QUESTION: this code sends 4 bytes over the link to
  // update the LEDs.  But spieeprom_read_into_leds() in
  // eeprom.c sends 5.  Why the difference?
  
  // Also, to get the character set to display properly,
  // I had to send out the bytes in a slightly shuffled
  // order.  Not sure WHY this is so, but it works...
  
  spi_transfer(fleds[2]);
  spi_transfer(fleds[3]);
  spi_transfer(fleds[0]);
  spi_transfer(fleds[1]);
 
  LATCH_SELECT_PORT |= _BV(FRONT);
  NOP; NOP; NOP; NOP;
  LATCH_SELECT_PORT &= ~_BV(FRONT);
}

// 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/8] = ~_BV(led%8);

  clock_leds();
}

// 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) {

  fleds[0] = fleds[1] = fleds[2] = fleds[3] = blockValue;
  
  clock_leds();
}

// Test the LEDs on power-on.  Runs through them
// quickly, then displays alternating LEDs, and
// finally puts them all on.  This test sequence
// is slightly modified from the original, and
// makes it easier to see problems with the LEDs.

void test_leds(void) {
  uint8_t i;

  // Quick run through the LEDs
  
  for(i=0; i< 33; i++) {
    set_led(i);
    delay_ms(10);
  }
  
  // Set groups of 8 LEDs to the same value.
  // Note that the LED state is the opposite
  // of what you might expect:
  //
  // 0 bits = on, 1 bits = off!
  
  // Light every other LED
  
  set_all(0xAA);
  delay_ms(50);
  
  // Now light the other LEDs
  
  set_all(0x55);
  set_all(0x55);
  delay_ms(50);
  
  // Now light all LEDs
  
  set_all(0x00);
  delay_ms(255);
  delay_ms(255);
  
  // likely the 1-second reset timer will go off before
  // this ends.  But no biggy, since if it does, it'll
  // reset the LEDs..
  
}
 
 
int main(void) {

  uint8_t cmd;			// the reason we reset

  // MCUSR is the MCU Status Register (page 40).  It tells us
  // why we reset, and a reset is the only way to get here.
  
  cmd = MCUSR;
  
  // The first order of business is to tell the chip that
  // we've got things under control.
  
  MCUSR = 0;
  
  // Turn on watchdog timer immediately, this protects against
  // a 'stuck' system by resetting it.
  
  // WDTCSR is the Watchdog Timer Control Register (page 45).
  // We set it so that it'll generate a watchdog interrupt
  // every second.  The idea is that if things mess up,
  // the watchdog will kickstart us.
  
  WDTCSR = _BV(WDE) | _BV(WDP2) | _BV(WDP1); // 1 second
  
  // Initialize the various pins of the ATMEL, and set up
  // the interrupts.
  
  ioinit();
  
  // We saved the reason for the reset of the chip.  If
  // it's a power-on, then we run a test pattern through
  // the LEDs.  Note that because we've set a 1-second
  // watchdog timer (in ioinit), if this test sequence
  // takes more than a second, the chip will reset.  But
  // since we'll know it isn't a power-on, the test
  // sequence won't run...
  
  if ((cmd & _BV(PORF)) != 0)
    test_leds();

  // display the reason for the reset on the LEDs.
  
  set_led(cmd+2);

  // enable the interrupts.  I think this is not needed
  // since it'll immediately be done by the loop, below.
  
  sei();
  
  // Loop until we timeout, at which point the ATMEL is
  // put to sleep.  If the communications routine timed
  // out, or the user pressed the button for >500ms,
  // then sensor_timer will be 0xFFFF and we'll immediately
  // sleep.
  
  while (1) {
  
    // Reset the watchdog Timer.
    //
    // QUESTION: What's with toggling the PD0 output line here?
    // it doesn't seem to be connected to anything according to
    // the circuit diagram...
    
    // *** PORTD |= 0x1;
    asm("wdr");
    // *** PORTD &= ~0x1;

	// If the sensor_timer (incremented by TIMER0) maxes out
	// (in about 3 minutes), then sleep everything.
	
    if (sensor_timer == 0xFFFF) {
      
      // Avoid pesky interruptions
      
      cli();
      
      // Turn off all LEDs - I guess LED 0 is one of the "invisible ones"
      
      set_led(0);
      
      // Turn off power to the Hall Effect sensor.
      
      SENSOR_PORT &= ~_BV(SENSORPOWER);
      
      // Deselect EEPROM
      
      SPIEE_CS_PORT |= _BV(SPIEE_CS);      // pull CS high to deselect
      
      // Turn off Watchdog (must be restarted when we get the wakeup)
      // Wakeup will be via the button interrupt.
      
      WDTCSR |= _BV(WDCE) | _BV(WDE);
      WDTCSR = 0;
      MCUCR |= _BV(SM1) | _BV(SM0) | _BV(SE);
      
      // Re-enable interrupts so we can get the wakeup!
      

⌨️ 快捷键说明

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