diff --git a/avr/extras/ATtiny_x7.md b/avr/extras/ATtiny_x7.md index d7a4767f..832b7cba 100644 --- a/avr/extras/ATtiny_x7.md +++ b/avr/extras/ATtiny_x7.md @@ -184,7 +184,6 @@ The 87 and 167 are available in three package variations. Additionally, the 167 * SOIC-20 (wide) - bigger than the side of a house, but easy to hand solder * TSSOP-20 - Slightly more demanding to solder. While it is hard to imagine being able to read this text and miss a bridge between adjacent pins on a SOIC-20, the same cannot be said for a SSOP-20 - depending on your eyesight, you may need magnification or more attention to lighting in order to spot bridges visually * VQFN32 - with 12 unused pins - Atmel seemed to REALLY like this package - a lot of 20-pin tinyAVR parts got this as their QFN instead of a proper QFN20-type. It's an annoying package, though - it's very fine pitch, large for a QFN (5mm x 5mm), and the unused pins don't appear to have been arranged with consideration of the layout. They're in the same order as the pins up and down the two sides of the SOIC/SSOP parts (probably a technical constraint I've never seen a chip with what was believed to have the same die, *not* have the same pin order, so I think bond wires have to make straight lines that don't cross each other from the die to the pin), but the decisons for where those dummy pins would go appears to have been based only on their convenience. -* WQFN20 (167 only) - the only time, to my knowledge, that a new package option has been added for a classic AVR after the Microchip buyout. That this has only happened once, despite many examples of disappointing packages from the past, leads me to believe that one or more very large customers (they're automotive parts, so the main buyers tend to be small in number but make up for it in volume) was giving them holy hell over that 32-pin package. The 87 did not get the same blessing (nor did the 861, which was also a 20-pin tiny stuck in a 32-pin VQFN), nor did any other part they have made. They have also not added any packages to a post-revolutionary part either, even when we can show that the die size would would work fine based on what they've already fit it into, and it is plain to see the that the product is held back by it's current package options. ## Interrupt Vectors This table lists all of the interrupt vectors available on the ATtiny x7-family, as well as the name you refer to them as when using the `ISR()` macro. Be aware that a non-existent vector is just a "warning" not an "error" (for example, if you misspell a vector name) - however, when that interrupt is triggered, the device will (at best) immediately reset (and not clearly - I refer to this as a "dirty reset") The catastrophic nature of the failure often makes debugging challenging. diff --git a/avr/extras/Ref_ChangePWMFreq.md b/avr/extras/Ref_ChangePWMFreq.md index 4b6daaff..6769f98f 100644 --- a/avr/extras/Ref_ChangePWMFreq.md +++ b/avr/extras/Ref_ChangePWMFreq.md @@ -23,8 +23,14 @@ In a timer counter unit, when we're talking about PWM, we're talking about the s * On Timer 1 (which is not used for millis), you can optionally get an additional /2 prescaling by using phase correct mode. Many cores copy the official core and always use this mode, but this can slow the PWM down to speeds we might prefer it avoid - or slow them down **from** speeds we'd like to avoid. by being willing to change this mode, it becomes much easier to stay within the target PWM frequency of 490-980 Hz, or at absolute most 1960 Hz (chosen for a number of reasons - it's fast enough that you can blink an LED and it won't appear to flicker, but slow enough that you can PWM most power MOSFETs with just a small gate resistor while staying in spec. At higher frequencies, microcontrollers begin to have trouble driving the pins fast and hard enough to switch the pin without the use of a gate driver), since on most parts the prescaling options are 0 (timer off) 1, 8, 64, 256, and 1024. On the x5 and x61, Timer1 has prescale options of 0, 1, and 2^n where n can be any integer up to 14 (ie, divide by 16384). On parts where timer1 doesn't have that crazy prescaler, which easily gets us into our target range for any frequency, we will choose fastPWM or phase correct PWM if it gets us closer to the target range. * When full control of the timer is taken the additional functionality might include (briefly - these features are beyond the scope of this document): * Periodic interrupts with CTC (Clear Timer on Compare match) - * Arbitrary TOP values, sometimes without the loss of an output, other times only at the cost of an output channel - * Higher resolution on Timer1 and Timer2 (where present), at least on most parts, which have a 16-bit timer1 (and the x61's with a 10-bit one, though using that 10-bit one is tricky) + * Arbitrary TOP values, sometimes without the loss of an output, other times only at the cost of an output channel. + * 16-bit timers which have an input capture feature (I belive all of the classic AVR "standard" timer1s plus higher timers that are a copy of the standard timer1) have a PWM mode where you set the ICR register to the TOP value without losing either of the output compares. + * On 8-bit timers, typically the only option is to set it so output compare channel A sets top, losing a channel. + * Higher resolution on Timer1, except: + * On the 43 - it's 8-bit there, copy of timer0. + * On the x5 or 26, where timer1 is instead a high speed 8 bit async timer. + * Limited benefit is seen on the x61 - it's the souped up version of the 26, and timer1 is again a high speed timer - but a 10-bit one. The 10-bit register behavior is nonstandard. + * Higher resolution on Timer2, on the single family of parts that has one, the x41. Timer2 there is a copy of Timer1, not the Timer2 that the ATmegas got. * Asynchronous low speed operation from an external clock or watch crystal (x7, possibly a small number of others) * Asynchronous high speed operation from a x8 PLL on the 8 MHz internal osc. (x5, x61 26 only) diff --git a/avr/extras/SpecificationConventions.md b/avr/extras/SpecificationConventions.md index 2a8c5bc0..73f7711a 100644 --- a/avr/extras/SpecificationConventions.md +++ b/avr/extras/SpecificationConventions.md @@ -45,4 +45,8 @@ This control two things: Which microcontroller or family will be used (a "family Very simple. When a family (as opposed to single chip) is selected, this is where you choose which one you are using. ### Clock Source and Speed -Often a VERY long menu, this lists all supported combinations of clock source and clock speed for the system clock. The options are listed in approximately descending order of popularity/usefulness. Internmal and PLL speeds (if present) are at the top. Then come Crystal speeds, starting with common ones, then USART-crystals (the speeds that provide perfect UART baud rates, at the expense of making timekeeping slower and worse). After that are the rarely used "tuned" bootloader options that allow a chip which has been "tuned" by running the tuning sketch. These speeds are generally 8 MHz (user calibration at operating temperature and voltage beats factory cal significantly), 12.0 MHz, and on most parts, 12.8 (12.8 is mathematically favorable since 64 divides evenly into it, vastly simplifying the math. All of these speeds can almost always be reached with the internal oscillator and tuning. On the ATtiny841 and 441, the oscillator is much fancier, and an additional 16 MHz tuning is attempted. Most chips can do it (as long as they're running at around 5v) a few have unusually slow oscillators and cannot. Finally, we end on the external CLOCK options. An external clock is a component that looks near identical to a square, golden crystal package most of the time, but the pins are typically Out and Enable in place of the two crystal pins (if enable control pin is present, consult the datasheet to see if the enable is active high or low, or if it turns itself on automatically when alowed to float, and connect that pin appropriately. It is only extremely unusual use cases that will do something other than tie the pin high, low, or leave floating, whenever turns it on. The other two opposite corners are Vdd and Gnd. It is imperitve that these never be powered backwards (for example, by the easy mistake of the orientation being rotated 180 degrees) - the oscillators will destoy themselves essentially immediately if power is applied like that (this is almost always true of IC's, and though it looks like a crystal the active I've seen that's harder to see the orientation marks on. External clocks also cost a lot more money than crystals, and the "china discount" for buying parts direct from china is much smaller than with crystals". +Often a VERY long menu, this lists all supported combinations of clock source and clock speed for the system clock. The options are listed in descending order of popularity/usefulness. Internmal and PLL speeds (if present) are at the top. Then come Crystal speeds, starting with common ones, then USART-crystals (the speeds that provide perfect UART baud rates, at the expense of making timekeeping slower and worse). After that are the rarely used "tuned" bootloader options that allow a chip which has been "tuned" by running the tuning sketch. These speeds are generally 8 MHz (user calibration at operating temperature and voltage beats factory cal significantly), 12.0 MHz, and on most parts, 12.8 (12.8 is mathematically favorable since 64 divides evenly into it, vastly simplifying the math. All of these speeds can almost always be reached with the internal oscillator and tuning. On the ATtiny841 and 441, the oscillator is much fancier, and an additional 16 MHz tuning is attempted. Most chips can do it (as long as they're running at around 5v) a few have unusually slow oscillators and cannot. + +Finally, we end on the external CLOCK options. An external clock is a component that looks near identical to a square, golden crystal package most of the time (though IC-shaped ones are not unusual), but the pins are typically Out and Enable in place of the two crystal pins (if enable control is needed, enable should be tied high, low, or allowed to float depending on the device, refer to the datasheet). The other two opposite corners are Vdd and Gnd. As the most commomn, crystal-looking package is a rectangle, it can easily be installed rotated 180 degrees. It is imperitve that this be avoided! This will power them backwards and they will burn out instantly (symptom - high current, no clock, component hot). Fortunately manufacturers put highly visible orientation-marks... wait... no they don't. They mark the outside with text so tiny that you need a magnifying glass and made of nothing but a tiny groove made by a laser; smudges and dirt can easily fill this in. Some chinese external clocks (which did give the specified output clock!) had no markings on top whatsoever, and only the one notched-pad to orient it. They're also frequently horrifying power hogs. I have seen clocks spec'ed for 10, 20mA input *from Western Companies*, plus the load! Oh - and the only external clock that was rated for operation between 1.8 and 5.5 V VCC was discontinued in 2020. External clocks also cost a lot more money than crystals, and the "china discount" for buying parts direct from china is much smaller than with crystals. Besides, the chinese vendors seem to generally know neither part numbers nor voltage spec when asked. They also don't know crystal load capacitance, but on classic AVRs that's rarely a problem, they have a very tenacious crystal drive circuit. + +Try to avoid having to use external clocks, and if you do have to use them, buy them from the West (eg digikey/mouser, as opposed to aliexpress). diff --git a/avr/extras/development/removed_from_wiring.c b/avr/extras/development/removed_from_wiring.c new file mode 100644 index 00000000..d5b33757 --- /dev/null +++ b/avr/extras/development/removed_from_wiring.c @@ -0,0 +1,359 @@ +/* Delay for the given number of microseconds. Assumes a 1, 8, 12, 16, 20 or 24 MHz clock. */ +//Not used anymore, we have the version we stole from nerdralph's picocore! + +/* +void delayMicroseconds(uint16_t us) +{ + #define _MORENOP_ "" // redefine to include NOPs depending on frequency + + // call = 4 cycles + 2 to 4 cycles to init us(2 for constant delay, 4 for variable) + + // calling avrlib's delay_us() function with low values (e.g. 1 or + // 2 microseconds) gives delays longer than desired. + //delay_us(us); + #if F_CPU >= 24000000L + // for the 24 MHz clock for the adventurous ones, trying to overclock + + // zero delay fix + if (!us) return; // = 3 cycles, (4 when true) + + // the following loop takes a 1/6 of a microsecond (4 cycles) + // per iteration, so execute it six times for each microsecond of + // delay requested. + us *= 6; // x6 us, = 7 cycles + + // account for the time taken in the preceding commands. + // we just burned 22 (24) cycles above, remove 5, (5*4=20) + // us is at least 6 so we can subtract 5 + us -= 5; //=2 cycles + + #elif F_CPU >= 20000000L + // for the 20 MHz clock on rare Arduino boards + + // for a one-microsecond delay, simply return. the overhead + // of the function call takes 18 (20) cycles, which is 1us + __asm__ __volatile__ ( + "nop" "\n\t" + "nop" "\n\t" + "nop" "\n\t" + "nop"); //just waiting 4 cycles + if (us <= 1) return; // = 3 cycles, (4 when true) + + // the following loop takes a 1/5 of a microsecond (4 cycles) + // per iteration, so execute it five times for each microsecond of + // delay requested. + us = (us << 2) + us; // x5 us, = 7 cycles + + // account for the time taken in the preceding commands. + // we just burned 26 (28) cycles above, remove 7, (7*4=28) + // us is at least 10 so we can subtract 7 + us -= 7; // 2 cycles + + #elif F_CPU >= 18432000L + // for a one-microsecond delay, simply return. the overhead + // of the function call takes 18 (20) cycles, which is approx. 1us + __asm__ __volatile__ ( + "nop" "\n\t" + "nop" "\n\t" + "nop" "\n\t" + "nop"); //just waiting 4 cycles + + if (us <= 1) return; // = 3 cycles, (4 when true) + + // the following loop takes nearly 1/5 (0.217%) of a microsecond (4 cycles) + // per iteration, so execute it five times for each microsecond of + // delay requested. + us = (us << 2) + us; // x5 us, = 7 cycles + + // user wants to wait 7us or more -- here we can use approximation + if (us > 34) { // 3 cycles + // Since the loop is not accurately 1/5 of a microsecond we need + // to multiply us by (18.432 / 20), very close to 60398 / 2.**16. + + // Approximate (60398UL * us) >> 16 by using 60384 instead. + // This leaves a relative error of 232ppm, or 1 in 4321. + unsigned int r = us - (us >> 5); // 30 cycles + us = r + (r >> 6) - (us >> 4); // 55 cycles + // careful: us is generally less than before, so don't underrun below + + // account for the time taken in the preceding and following commands. + // we are burning 114 (116) cycles, remove 29 iterations: 29*4=116. + + TODO: is this calculation correct. Right now, we do + function call 6 (+ 2) cycles + wait at top 4 + comparison false 3 + multiply by 5 7 + comparison false 3 + compute r 30 + update us 55 + subtraction 2 + return 4 + total --> 114 (116) cycles + + + // us dropped to no less than 32, so we can subtract 29 + us -= 29; // 2 cycles + } else { + // account for the time taken in the preceding commands. + // we just burned 30 (32) cycles above, remove 8, (8*4=32) + // us is at least 10, so we can subtract 8 + us -= 8; // 2 cycles + } + + #elif F_CPU >= 18000000L + // for the 18 MHz clock, if somebody is working with USB + // or otherwise relating to 12 or 24 MHz clocks + + // for a 1 microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is .8 us + if (us <= 1) return; // = 3 cycles, (4 when true) + + // make the loop below last 6 cycles + #undef _MORENOP_ + #define _MORENOP_ " nop \n\t nop \n\t" + + // the following loop takes 1/3 of a microsecond (6 cycles) per iteration, + // so execute it three times for each microsecond of delay requested. + us = (us << 1) + us; // x3 us, = 5 cycles + + // account for the time taken in the preceding commands. + // we burned 20 (22) cycles above, plus 2 more below, remove 4 (4*6=24), + // us is at least 6 so we may subtract 4 + us -= 4; // = 2 cycles + + #elif F_CPU >= 16500000L + // for the special 16.5 MHz clock + + // for a one-microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is about 1us + if (us <= 1) return; // = 3 cycles, (4 when true) + + // the following loop takes 1/4 of a microsecond (4 cycles) times 32./33. + // per iteration, thus rescale us by 4. * 33. / 32. = 4.125 to compensate + us = (us << 2) + (us >> 3); // x4.125 with 23 cycles + + // account for the time taken in the preceding commands. + // we burned 38 (40) cycles above, plus 2 below, remove 10 (4*10=40) + // us is at least 8, so we subtract only 7 to keep it positive + // the error is below one microsecond and not worth extra code + us -= 7; // = 2 cycles + + #elif F_CPU >= 16000000L + // for the 16 MHz clock on most Arduino boards + + // for a one-microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is 1us + if (us <= 1) return; // = 3 cycles, (4 when true) + + // the following loop takes 1/4 of a microsecond (4 cycles) + // per iteration, so execute it four times for each microsecond of + // delay requested. + us <<= 2; // x4 us, = 4 cycles + + // account for the time taken in the preceding commands. + // we just burned 19 (21) cycles above, remove 5, (5*4=20) + // us is at least 8 so we can subtract 5 + us -= 5; // = 2 cycles, + + #elif F_CPU >= 14745600L + // for a one-microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is approx. 1us + + if (us <= 1) return; // = 3 cycles, (4 when true) + + // the following loop takes nearly 1/4 (0.271%) of a microsecond (4 cycles) + // per iteration, so execute it four times for each microsecond of + // delay requested. + us <<= 2; // x4 us, = 4 cycles + + // user wants to wait 8us or more -- here we can use approximation + if (us > 31) { // 3 cycles + // Since the loop is not accurately 1/4 of a microsecond we need + // to multiply us by (14.7456 / 16), very close to 60398 / 2.**16. + + // Approximate (60398UL * us) >> 16 by using 60384 instead. + // This leaves a relative error of 232ppm, or 1 in 4321. + unsigned int r = us - (us >> 5); // 30 cycles + us = r + (r >> 6) - (us >> 4); // 55 cycles + // careful: us is generally less than before, so don't underrun below + + // account for the time taken in the preceding and following commands. + // we are burning 107 (109) cycles, remove 27 iterations: 27*4=108. + + // us dropped to no less than 29, so we can subtract 27 + us -= 27; // 2 cycles + } else { + // account for the time taken in the preceding commands. + // we just burned 23 (25) cycles above, remove 6, (6*4=24) + // us is at least 8, so we can subtract 6 + us -= 6; // 2 cycles + } + + #elif F_CPU >= 12000000L + // for the 12 MHz clock if somebody is working with USB + + // for a 1 microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is 1.3us + if (us <= 1) return; // = 3 cycles, (4 when true) + + // the following loop takes 1/3 of a microsecond (4 cycles) + // per iteration, so execute it three times for each microsecond of + // delay requested. + us = (us << 1) + us; // x3 us, = 5 cycles + + // account for the time taken in the preceding commands. + // we just burned 20 (22) cycles above, remove 5, (5*4=20) + // us is at least 6 so we can subtract 5 + us -= 5; //2 cycles + + #elif F_CPU >= 8000000L + // for the 8 MHz internal clock + + // for a 1 and 2 microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is 2us + if (us <= 2) return; // = 3 cycles, (4 when true) + + // the following loop takes 1/2 of a microsecond (4 cycles) + // per iteration, so execute it twice for each microsecond of + // delay requested. + us <<= 1; //x2 us, = 2 cycles + + // account for the time taken in the preceding commands. + // we just burned 17 (19) cycles above, remove 4, (4*4=16) + // us is at least 6 so we can subtract 4 + us -= 4; // = 2 cycles + + #elif F_CPU >= 6000000L + // for that unusual 6mhz clock... + + // for a 1 to 3 microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is 2.5us + if (us <= 3) return; // = 3 cycles, (4 when true) + + // make the loop below last 6 cycles + #undef _MORENOP_ + #define _MORENOP_ " nop \n\t nop \n\t" + + // the following loop takes 1 microsecond (6 cycles) per iteration + // we burned 15 (17) cycles above, plus 2 below, remove 3 (3 * 6 = 18) + // us is at least 4 so we can subtract 3 + us -= 3; // = 2 cycles + + #elif F_CPU >= 4000000L + // for that unusual 4mhz clock... + + // for a 1 to 4 microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is 4us + if (us <= 4) return; // = 3 cycles, (4 when true) + + // the following loop takes 1 microsecond (4 cycles) + // per iteration, so nothing to do here! \o/ + // ... in terms of rescaling. We burned 15 (17) above plus 2 below, + // so remove 5 (5 * 4 = 20), but we may at most remove 4 to keep us > 0. + us -= 4; // = 2 cycles + + #elif F_CPU >= 2000000L + // for that unusual 2mhz clock... + + // for a 1 to 9 microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is 8us + if (us <= 9) return; // = 3 cycles, (4 when true) + // must be at least 10 if we want to do /= 2 -= 4 + + // divide by 2 to account for 2us runtime per loop iteration + us >>= 1; // = 2 cycles; + + // the following loop takes 2 microseconds (4 cycles) per iteration + // we burned 17 (19) above plus 2 below, + // so remove 5 (5 * 4 = 20), but we may at most remove 4 to keep us > 0. + us -= 4; // = 2 cycles + + #else + // for the 1 MHz internal clock (default settings for common AVR microcontrollers) + // the overhead of the function calls is 14 (16) cycles + if (us <= 16) return; //= 3 cycles, (4 when true) + if (us <= 25) return; //= 3 cycles, (4 when true), (must be at least 25 if we want to subtract 22) + + // compensate for the time taken by the preceding and next commands (about 22 cycles) + us -= 22; // = 2 cycles + // the following loop takes 4 microseconds (4 cycles) + // per iteration, so execute it us/4 times + // us is at least 4, divided by 4 gives us 1 (no zero delay bug) + us >>= 2; // us div 4, = 4 cycles + #endif + + // busy wait + __asm__ __volatile__ ( + "1: sbiw %0,1" "\n\t" // 2 cycles + _MORENOP_ // more cycles according to definition + "brne 1b" : "=w" (us) : "0" (us) // 2 cycles + ); + // return = 4 cycles +} +*/ + + +/*#if (defined(__AVR_ATtinyX41__) && F_CPU == 16000000 && CLOCK_SOURCE == 0 ) + // functions related to the 16 MHz internal option on ATtiny841/441. + // 174 CALBOOST seems to work very well - it gets almost all of them close enough for USART, which is what matters. It was empirically determined from a few parts I had lying around. + #define TINYX41_CALBOOST 174 + static uint8_t tinyx41_cal16m = 0; + static uint8_t saveTCNT = 0; + void oscBoost() { + OSCCAL0 = (origOSC>MAXINITCAL?255:(origOSC + CALBOOST)); + _NOP(); + } + void oscSafeNVM() { // called immediately prior to writing to EEPROM. + //TIMSK0& = ~(_BV(TOIE0)); // turn off millis interrupt - let PWM keep running (Though at half frequency, of course!) + //saveTCNT = TCNT0; + //if (TIFR0&_BV(TOV0)) { // might have just missed interrupt - recording as 255 is good enough (this may or may not have been what we recorded, but if it was still set, interrupt didn't fire) + // saveTCNT = 255; + //} + #ifndef DISABLEMILLIS + saveMillis = millis(); //save low bytes of millis + #endif + set_OSCCAL(read_factory_calibration()); + } +*/ + //void oscDoneNVM(uint8_t bytes_written) { + /* That's the number of bytes of eeprom written, at 3.3ms or so each. + * EEPROM does it one at a time, but user code could call these two methods when doing block writes (up to 64 bytes). Just be sure to do the eeprom_busy_wait(); at the end, as in EEPROM.h. + * Not so much because it's a prerequisite for this stupid correction to timing but because cranking the oscillator back up during the write kinda defeats the point of slowing it doewn... + * 3.3ms is good approximation of the duration of writing a byte - it'll be about 3~4% faaster since we're running around 5V at default call - hence, we're picking 3.3ms - the oscillator + * adjustment loops and these calculations should be fast enough that the time they dont take long enough to worry about... + * relies on assumptions from implementation above of millis on this part at 16MHz! + * millis interrupt was disabled when oscSaveNVM() was called - so we don't need to do anything fancy to access the volatile variables related to it. + * 1 millis interrupt fires every 1.024ms, so we want 3.3/1.024= 3.223 overflows; there are 256 timer ticksin an overflow, so 57 timer ticks... + */ + // FRACT_MAX = 125, FRACT_INC =3 + //set_OSCCAL(tinyx41_cal16m); //stored when we initially tuned + //#ifndef DISABLEMILLIS + /* + uint8_t m = 3 * bytes_written; // the 3 whole overflows + uint16_t tickcount = 57*bytes_written + saveTCNT; + m += (tickcount >> 8); // overflows from theose extra /0.223's + millis_timer_overflow_count += m; // done with actual overflows, so record that. + uint16_t f = FRACT_INC*m + millis_timer_fract; // (m could be up to 207) + while(f > FRACT_MAX){ // at most 621 + 124 = 745 + f -= FRACT_MAX; + m++; + } + // now we're adding up the contributions to millis from the 0.024 part... + // save the results + millis_timer_fract = f; + millis_timer_millis += m; + TCNT0 = 0; + TIFR0 |= _BV(TOV0); // clear overflow flag + TIMSK0 |= _BV(TOIE0); // enable overflow interrupt + TCNT0 = tickcount; // restore new tick count + // wonder if it was worth all that just to write to the EEPROM while running at 16MHz off internal oscillator without screwing up millis and micros... + */ + // I don't think it was, gonna go with a quicker dirtier method - we instead leave it running at half speed, saving the low byte of millis. Longest time at half-speed + // is 3.3 * 64 around 200 ms for a max length block write. So if we leave millis running, and know that it's running at half speed... just take difference of the LSB + // and add that much to millis. + //(uint8_t)millis_timer_overflow_count + //uint8_t milliserror=((uint8_t) millis())-saveMillis + //#endif + //} +//#endif diff --git a/avr/libraries/Wire/Readme.md b/avr/libraries/Wire/Readme.md index 4355dfd0..d90112e2 100644 --- a/avr/libraries/Wire/Readme.md +++ b/avr/libraries/Wire/Readme.md @@ -13,9 +13,9 @@ Only on the Tiny88. Here, as long as you're able to fit in overall resource limi Some resources refer to this as "Universal". Others refer to it as what you get if you strip out every feature and nicety that we expect from hardware SPI or hardware TWI, and instead of branding it as "crippled SPI/TWI" which doesn't have much of a ring to it, they called it a universal serial interface. It also happens to do very little for you. Essentially, what you get is an 8-bit SPI-like shift register, that is triggered, by USCK/scl, a way to see when 8 bits have been clocked through it, and a data register that that gets copied to at that time. You do get a start condition detector that works to wake from all sleep cycles at least. You may have noticed that I didn't mention clock generation. That's cause there is none. You have to stobe the pin from software or monopolize a timer to generate a clock, and you can't use timer0 cause that's millis, and if you use timer1, you have only 1 PWM pin left. Hence we do software strobing on the master side, -We are also not allowed to use the internal pullups: -To quote the datasheet: -"The SCL line is held low when a start detector detects a start condition and the output is enabled. Clearing the Start Condition Flag (USISIF) releases the line. The SDA and SCL pin inputs is not affected by enabling this (two wire) mode. **Pull-ups on the SDA and SCL port pin are disabled in Two-wire mode**" +We are also not allowed to use the internal pullups; To quote the datasheet: + +*The SCL line is held low when a start detector detects a start condition and the output is enabled. Clearing the Start Condition Flag (USISIF) releases the line. The SDA and SCL pin inputs is not affected by enabling this (two wire) mode. **Pull-ups on the SDA and SCL port pin are disabled in Two-wire mode*** (emphasis mine) @@ -83,4 +83,4 @@ For a read, the master would first perform the first 4 steps above setting the l ### Yeah, they don't line up so good It's like the API designer read the protocol spec and designed to that and had never actually used an I2C device, or had little imagination and hadn't attempted to make anything that acted like other I2C devices. You cannot have a register-model, because you don't know how many bytes the master will read (the protocol never tells you this), nor can you find out how many are read after the fact, nor can you put the slave to sleep because it might be silently servicing an interrupt for a read that hasn't finished yet. This will generally make you "that device" that when misused, becomes non-responsive with one or both lines being held low. You don't want to be that device. I don't really have any good solution to offer here, but this is why all arduino slave devices you've seen use I2C like lobotomized serial: That's the only mode of operation that the API supports. -The API has been extended for DxCore and mTC - the problem was far more tractable there: It only had to be done once for every AVR released since 2016 - the same library works unmodified and likely will continue to do so for the foreseeable future, with only trivial changes, whereas it would have to be done thrice for the older parts. Moreover, on all of these parts, whatever I2C implementation is available, it is much less helpful, and lacks the features that we used for this on mTC and DxC. +The API has been extended for DxCore and mTC, as the problem was far more tractable there (not only is it a single implementation covering a much more cooperative peripheral, the same library works unmodified across all post-2016 design AVRs and likely will continue to do so for the foreseeable future).