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

📄 main.c

📁 旋转16个LED灯控制程序
💻 C
📖 第 1 页 / 共 2 页
字号:
       	// Back pacman, as seen from front.  We have to be a little tricky       	// here because the two sides of the mouth will be displayed on       	// different rotations!  So line_shift will almost certainly be       	// opposite for each unless we are spinning fast.      	      	if ( (line_shift & 0x01) == 0 ) {      	      	  if ( (pixelNum < (PAC_OPEN)) | (pixelNum > (PAC_CLOSED_NEG)) ) {      	        	    cPattern[0] = 0xFF;      	          	  }      	        	} else {      	      	  if ( (pixelNum < (PAC_CLOSED)) | (pixelNum > (PAC_OPEN_NEG)) ) {      	        	    cPattern[0] = 0xFF;      	          	  }      	}      } else if (cur_code == 0xE4) {             	// pacman dying       	       	eeprom_addr.bytes.high_byte = (line_shift << 3) & 0xF0;      	      	if ( (pixelNum < eeprom_addr.bytes.high_byte) | (pixelNum > (0xFF-eeprom_addr.bytes.high_byte)) ) {      	        	  cPattern[0] = 0xFF;      	          	}      } else {              cPattern[0] = 0x30;              }            set_all(cPattern[0]);            // cPattern[3] = cPattern[2] = cPattern[1] = cPattern[0];      // clock_scroll(0);        }      }}// Interrupt 0 executes when the button is pressed.// QUESTION: unlike the pixel output interrupt, this one// doesn't sei().  Why?SIGNAL (SIG_INT0) {    // Wait until button no longer pressed    while (! (BUTTON_PIN & _BV(BUTTON))) {  }    if (sensor_timer.bytes.high_byte == 0xFF) {        // so in this instance, we set the watchdog to reset us        sensor_timer.bytes.high_byte = 0x00;          // Re-enable the watchdog timer, then loop until    // it fires off.          WDTCSR = _BV(WDE);    for (;;);        } 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.bytes.high_byte = 0xFF;        }}// Interrupt 1 executes when the hall effect sensor fires// QUESTION: unlike the pixel output interrupt, this one// doesn't sei().  Why?SIGNAL (SIG_INT1) {    // 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.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;	// 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 LINEINC lines 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 += LINEINC;	      	      if (cur_line >= NUM_LINES) {	        cur_line = 0;	      }	  		        }          }    #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;	      #endif	// read in the code, and if need be, the pixel data		cur_code = pgm_read_byte(&lines[cur_line]);	if (cur_code < 0x80) {	  	  memcpy_P(cPattern,&patterns[cur_code],6);	} else if (cur_code >= 0xF0) {		  // eeprom bitmap read; because of my current rotation order, we read	  // the bytes in reverse order	  	  #ifdef CLOCKWISE	  	    eeprom_addr.bytes.high_byte = cur_code & 0x0F;	    eeprom_addr.bytes.low_byte = 0x00;	  	  #else	  	    eeprom_addr.bytes.high_byte = (cur_code & 0x0F) | 0x03;	    eeprom_addr.bytes.low_byte = 0xFC;	  	  #endif	  	}    pixelNum = 255;		// will wrap to 0, the first pixel        // 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(~0x0F);          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        #if NUM_LINES > 0          cur_line = NUM_LINES-1;			// will wrap to first line        #else          cur_line = 0;          #endif      }         // 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;  }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();    // Show that we are active.    set_all(~0x01);  // 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.    // Change to for (;;) to see if it makes any difference    for (;;) {      // 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.bytes.high_byte == 0xFF) {            // Avoid pesky interruptions            cli();            // Turn off all LEDs - I guess LED 0 is one of the "invisible ones"            set_all(0xFF);            // 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!            sei();           // Go into sleep mode            asm("sleep");          } else {              	      sei();               }            }    // *** PORTD |= 0x2;}

⌨️ 快捷键说明

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