📄 spokepov.lss
字号:
// 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 + -