main.c
来自「旋转16个LED灯控制程序」· C语言 代码 · 共 1,554 行 · 第 1/4 页
C
1,554 行
// to implement smooth scrolling. Can be used with a 0// parameter to just send out the regular 4 bytes.void clock_scroll(uint8_t sCount) { // First, send the basic 4 bytes, they go no matter what spi_transfer(fleds[0]); spi_transfer(fleds[1]); spi_transfer(fleds[2]); spi_transfer(fleds[3]); // If there is anything to do.. if (sCount != 0) { // If we have < 8 bits to transfer if (sCount < 8) { // Then that is all that we need to do spi_transfer_n(fleds[4],sCount); } else { // First latch out the full first 8 bits spi_transfer(fleds[4]); // How many bits left to do? sCount = sCount - 8; if (sCount != 0) { spi_transfer_n(fleds[5],sCount); } } } // finally, latch the bits into the LEDS LATCH_SELECT_PORT |= _BV(FRONT); NOP; NOP; NOP; NOP; LATCH_SELECT_PORT &= ~_BV(FRONT);}// TIMER0 interrupt handler. This runs about every 8ms// AFAICT. It increments the hall_debounce and sensor_timer// values until they pin.// QUESTION: what's with the setting and clearing of PORTB0?// According to the wiring diagram, it isn't connected to// anything. Is this vestigial code from when you were using// Pin Change Interrupts? Or is it debugger code so you// can monitor the pins and tell when something happens.SIGNAL (SIG_TIMER0_OVF) { // *** PORTB |= 0x1; // Let hall_debounce just wrap around. At worst it'll just // mess things up for one of the initial speedup rotations // of the blade... // if (hall_debounce != 0xFF) hall_debounce++; if (sensor_timer.bytes.high_byte != 0xFF) sensor_timer.word++; #if NUM_LINES > 0 // Increment the line timer - we only need to do this // if we are scrolling line_timer++; // increment the low byte #endif #ifdef DYNAMIC_TIME // Increment the dynamic time fractional seconds byte dynamicTimeCounter.word += DYNAMIC_TIME_CALIBRATION; if (dynamicTimeCounter.bytes.high_byte > 0x7F) { // wrap the counter dynamicTimeCounter.bytes.high_byte &= 0x7F; // Add 1 second to the time HH:MM:SS // 76-43-10 // Bytes in reverse order // Increment seconds dynamicTime[0]++; // Check for units seconds overflow - has the digit become 10? if ( dynamicTime[0] == ':' ) { // If so, reset it and increment 10's digit dynamicTime[0] = '0'; dynamicTime[1]++; // Repeat the process for the 10s seconds digit, has it become // 6? if ( dynamicTime[1] == '6' ) { // reset it and increment minutes digit dynamicTime[1] = '0'; dynamicTime[3]++; // Repeat process for minutes if ( dynamicTime[3] == ':' ) { // You should get the idea now... dynamicTime[3] = '0'; dynamicTime[4]++; if ( dynamicTime[4] == '6' ) { dynamicTime[4] = '0'; dynamicTime[6]++; // and 10s of hours if ( dynamicTime[6] == ':' ) { dynamicTime[6] = '0'; dynamicTime[7]++; } // handle special wrap #ifdef DYNAMIC_TIME_12H if ( (dynamicTime[7] == '1') && (dynamicTime[6] == '3') ) { dynamicTime[7] = '0'; dynamicTime[6] = '1'; } #else if ( (dynamicTime[7] == '2') && (dynamicTime[6] == '4') ) { dynamicTime[7] = dynamicTime[6] = '0'; } #endif } } } } } #endif // *** PORTB &= ~0x1;}// Hack - I used to copy the topChar, etc. variables into local// variables in the TIMER1 routine, but I am running out of stack space. So instead// I'm going to keep interrupts off during this routine and// use the regular versions. To make it easy to do this I'm// remapping the local variables with defines. // #define USE_LOCAL_TIMER1 1// If USE_LOCAL_TIMER1 is not defined, then the TIMER1 routine will not// enable interrupts. // As we sweep around the circle, we display 256 radial pixel// lines, once per TIMER1 interrupt. This is broken down into// 16 16-pixel wide characters, and we have two characters// stacked vertically. To save time, we keep track of the// character number, pixel number (in the character), and// pointers into the eeprom for each of the two chars being// displayed.// This code has to be fast enough to complete execution before// it gets interrupted again - and it must not make any subroutine// calls, since that'll mess up the stack and cause the entire// system to reset.volatile uint16_t topChar = 0; // top character being displayed (address in EEPROM of data)volatile uint16_t botChar = 0; // bottom character being displayedvolatile uint8_t charNum = 31; // character numbervolatile uint8_t pixelNum = 15; // pixel number#ifdef USE_LOCAL_TIMER1 volatile uint8_t clean = 0; // have these values been changed outside TIMER1?#endif#ifdef SMOOTHSCROLL volatile uint16_t scrollChar = 0; // extra scroll character#endif// This routine gets called every time the pixel timer runs down;// in other words, once per "scan line", 256 times per revolution// of the SpokePOV. Its purpose is to update the LEDs.SIGNAL (SIG_TIMER1_COMPA) { #ifdef USE_LOCAL_TIMER1 uint16_t tChar; // local copies of the values uint16_t bChar; uint8_t cNum; uint8_t pNum; #ifdef SMOOTHSCROLL uint16_t sChar; // extra scroll character #endif // When an interrupt routine is called, interrupts are disabled. // but it's important to let other interrupts interrupt us, so // they need to be re-enabled. sei(); // Copy the volatile variables into their local equivalents tChar = topChar; bChar = botChar; cNum = charNum; pNum = pixelNum; #ifdef SMOOTHSCROLL sChar = scrollChar; #endif #else #define tChar topChar #define bChar botChar #define cNum charNum #define pNum pixelNum #define sChar scrollChar #endif // If it has been less than STANDBY_TIMEOUT seconds since the last time we // got a Hall Effect sensor update, then proceed as normal and // update the LEDs. // QUESTION: what is F_CPU? ANSWER: FREQUENCY OF CPU (clocks/second) if (sensor_timer.bytes.high_byte < ( (F_CPU/NUM_PIXELS)/256 * STANDBY_TIMEOUT) / 256) { // *** PORTA |= 0x1; // The first thing we do is increment our character position; this // is done here to avoid code duplication. This means that the // Hall Effect interrupt routine must set them up so they "wrap" // into the first valid values. // Move to the next pixel in the character pNum++; // If we have moved off the edge of the character, then // we need to move to the next character if (pNum == 16) { // If we will wrap around to the first character, turn off the pixel // timer, clear the display, and exit. Might speed things up a bit if (cNum == 15) { TCCR1B &= ~0x7; set_all(~0x00); return; } pNum = 0; // reset to first pixel cNum = (cNum+1) & 0x0F; // move, with wrap, to next character position // Now we need to reset the pointers to the correct addresses // in the EEPROM for the characters they display. With the new // interleaved character sets, this becomes much easier tChar = (topLine[cNum]-32) << 1; // character offset for the top char, 0-95 // Ditto for bChar... bChar = (botLine[cNum]-32) << 1; #ifdef SMOOTHSCROLL // and if smooth scrolling, sChar sChar = (scrollLine[cNum]-32) << 1; #endif } else { // If we haven't wrapped around a character boundary, we just move // to the next 2-byte line in the character set, which will be 192 bytes // away tChar += 192; bChar += 192; #ifdef SMOOTHSCROLL sChar += 192; #endif } // Unfortunately, we can't do the cute "read from the EEPROM right // into the LEDs trick" that limor can do in SpokePOV. We have to // read the data into the ATMEL and then write it out. spieeprom_read(tChar,fleds,2); // the top 2 bytes spieeprom_read(bChar,fleds+2,2); // and the bottom 2 #ifdef SMOOTHSCROLL spieeprom_read(sChar,fleds+4,2); // and the scroll characters #endif // However, we do have a fancy trick of our own. If we are // smooth scrolling, then we clock out an extra line_shift // BITS, thus implementing the smooth scrolling #ifdef SMOOTHSCROLL clock_scroll(line_shift); // send 0-15 extra bits.. #else clock_scroll(0); // just send the 4 bytes #endif // Now we increment the variables. However, we // only do it IF haven't been touched by some other part // of the code while we were displaying the pixels. Also, we // turn interrupts off so that nobody else can touch them // while we are at it #ifdef USE_LOCAL_TIMER1 cli(); if (clean) { topChar = tChar; botChar = bChar; charNum = cNum; pixelNum = pNum; #ifdef SMOOTHSCROLL scrollChar = sChar; #endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?