📄 main copy.c
字号:
// 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 displayvoid set_all(uint8_t blockValue) { fleds[0] = fleds[1] = fleds[2] = fleds[3] = blockValue; clock_scroll(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 { // Do we have dynamic updating to do? #ifdef DYNAMIC uint8_t tBytes; // Number of bytes to transfer // Use unions to overlay variables and save some space union { char *fPtr; // from pointer var16bit divisor; // the divisor for RPM } a; union { char *tPtr; // to pointer char *divTable; // divisor table } b; // Update the dynamic data display switch (dynamicType & 0xF0) { #ifdef DYNAMIC_REVCOUNT // Rev counter compiled in? case 0x10: // Rev counter tBytes = 4; // number of bytes we'll move down below a.fPtr = (char *)dynamicREV; break; // gets put into dynamicBuffer in reverse order. #endif #ifdef DYNAMIC_RPM // RPM counter compiled in? case 0x20: // CODE NOT YET OPTIMIZED FOR SPACE. // Initialize the RPM counter to 0 dynamicRPM[0]=dynamicRPM[1]=dynamicRPM[2] = '0'; // Get a copy of the TIMER1 value that we need to divide 732 by a.divisor.word = OCR1A; // Start out pointing to the SECOND element of the div732 array. // We need to set to the second one because the min value of the // OCR1A hibyte (max speed) is 01, and we'll do at most 7 shifts // before we see a bit. But there are 8 bits after that that // might get looked at, so the div732 table must be 8 entries long. // We also add 2 so that we are pointing at the last byte of each // entry. b.divTable = (char *)(div732+5); // 3 bytes per entry // Shift the divisor to the left and the divtable pointer to the // right until the high bit of the divisor is set while (a.divisor.bytes.high_byte < 0x80) { a.divisor.word = a.divisor.word << 1; b.divTable = b.divTable + 3; } // Once we have found the high bit, that tells us the fastest // the device could possibly be rotating. Any other set bits in // a.divisor mean that the RPM is lower. So what we do is clear // the high bit and add in our partial sums only when the bits // in the remainder are ** 0 **. Since we just cleared the // high bit, it'll get counted. If all the bits are zero, all // the sums will be added. // NOTE: could be made smaller by using a.divisor.bytes.high_byte // below, sacrificing a bit of accuracy at very high speeds. a.divisor.word = a.divisor.word & 0x7FFF; while (b.divTable > div732) { if (a.divisor.bytes.high_byte < 0x80) { // Add in the least significant bit. Since the table is actual // byte values, we don't need to deal with an ascii conversion dynamicRPM[0] = dynamicRPM[0] + pgm_read_byte(b.divTable--); if (dynamicRPM[0] > '9') { dynamicRPM[0] = dynamicRPM[0] - 10; dynamicRPM[1]++; } // repeat for second byte... dynamicRPM[1] = dynamicRPM[1] + pgm_read_byte(b.divTable--); if (dynamicRPM[1] > '9') { dynamicRPM[1] = dynamicRPM[1] - 10; dynamicRPM[2]++; } // and for third, but no carry needed this time dynamicRPM[2] = dynamicRPM[2] + pgm_read_byte(b.divTable--); // At this point, we've already subtracted 3 from b.divTable, so we're // ready to go for the next loop. } else { // But if we didn't add anything in, we need to shift b.divTable b.divTable = b.divTable - 3; } // And finally move to the next bit a.divisor.word = a.divisor.word << 1; } // And when that's all done, we just set up for the move tBytes = 3; a.fPtr = (char *)dynamicRPM; break; #endif #ifdef DYNAMIC_TIME // Clock compiled in? case 0x30: // Rev counter tBytes = 8; // number of bytes we'll move down below a.fPtr = (char *)dynamicTime; break; // gets put into dynamicBuffer in reverse order. #endif default: // This will get executed if no dynamic data tBytes = 0x00; // and will clear things. } // If we have bytes to transfer, and if the dynamic display // is still active, then transfer the bytes into the display cli(); if ( (dynamicType != 0x00) && (tBytes != 0x00) ) { b.tPtr = (char *)dynamicPtr; // we now know dynamicPtr is valid // Remember, dynamicPtr points to the last byte, so we // copy bass ackwards! for(;tBytes>0;tBytes--) { *b.tPtr-- = *a.fPtr++; } } #endif sei(); } } // *** PORTD |= 0x2;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -