This document is divided into two sections. The first one simply describes the available timers, and what they are capable of (by "simply describes" I don't claim to have made a simple description, only that the purpose is simple. and that is to describe the timers). The second section describes how they are used by this core in particular. The first section is shared by DxCore and megaTinyCore. The second contains many sections specific to one core or another, however the same document is used for both for the sake of th4e maintainers' sanity. Because this is a very long document, a table of contents is included!
- Quick answer: Which PWM pins should I use?
- Section One: Background: Timers on modern AVRs
- Background: The Timers on the AVR Dx-series and Ex-series parts
- Timer Prescaler Availability
- Resolution, Frequency and Period
- Section Two: How the core uses these timers
- Millis/Micros Timekeeping
- Tone
- Servo Library
- Additional functions for advanced timer control
- Appendix I: Names of timers
- Appendix II: TCB Micros Artifacts
TCA or TCD pins; these timers are much better for generation of PWM. Only use TCB pins if desperate. See the part-specific docs for your part and pincount to see where the timers are pointed by the core on startup. You can set which pins the TCAs (and the TCD on the DD-series) use at runtime by simply writing to PORTMUX.TCAROUTEA
. See the part-specific docs (the ones with the pinout charts, linked to from top of main README and from the column headings in About the Dx-Series). These contain a table for each timer, listing what we set the portmux for the timer to on initialization, and what options are available and note which options are precluded by errata.
TIMER | On DA/DB | On DD | On EA | On EB | Pins: | Relevant Errata: |
---|---|---|---|---|---|---|
TCA0 | Yes | Yes | No | No | Pins 0-5 on your choice of ports. | Restart Command not intended to reset direction and on DD and future revisions will not |
TCA1 | >32 pin | No | Yes | No | Pins 0-5 on PB or PG, else pins 4-6 of PC, PE, PA* PD* | AVR128DA64 cannot output TCA1 compare match (pwm) on PORTG or PORTE |
TCD0 | Yes | Yes | No | No | PA4-7 on DA/DB. DD has special split-port mux option | Only default portmux works on pre-DD parts (Where is our damned die rev already?) |
TCB0 | Yes | Yes | Yes | Yes | PA2 or PF4. All TCBs are poor PWM timers | AND you have to set both duty cycle and period at the same time |
TCB1 | Yes | Yes | Yes | Yes | PA3 or PF5. | And you get just 1 channel of 8-bit PWM. |
TCB2 | Yes | 28/32 | Yes | No | PC0 or PB4. Default millis timer ** |
They make an AMAZING millis timer though! |
TCB3 | 48/64 pin | No | Yes | No | PB5 or PC1. | because you can set them to interrupt once/ms |
TCB4 | 64 pin | No | No | No | PG3 or PC6. | which makes both micros and especially millis FAST |
*
MUX options for PA/PD on TCA1 are EA-series only. On DA-series, only PB and PC mux options work.
**
A TCB cannot be used for PWM if being used for millis. On DD-series with 14 or 20 pins, TCB1 is the default millis timer as it likely will be on the EB-series (unless it's clear that we want to use TCF0). Otherwise, TCB2 is: TCB2 is always the default millis timer if it exists (again, unless it turns out that TCF is better for that). A TCA can be used for millis, though it is less accurate, and it can output PWM and act as the millis timer. TCD as millis is not supported because TCD is really not meant for that kind of thing. and is an outstanding PWM timer (see TCD reference for specific "can I change this without breaking stuff" questions re: TCD0. We are more forgiving of users tweaking the settings of TCD and wanting to use analogWrite with it because TCD0 is very confusing to configure, and we believe that the number of people who want to be able to tweak it's PWM is markedly larger than the number of people who can figure out how to manually configure it after taking over TCD0.
Simple - it's the highest numbered timer that's widely distributed (our servo library and tone function check one of TCB1 and TCB0 for being millis, and use that timer if not (hence, since each checks a different timer, if you have three TCBs and one is doing millis, both servo and tone will work only if millis is on TCB2 - otherwise only one of them will). It's also what everyone else seems to be doing, and we should do it the same way for compatibility. Some parts (the smaller pincount DD and all of the future DU and EB parts) do not have a TCB2. In this case, we will instead use TCB1 by default. Servo/tone will notice that TCB1 is used by millis and fall back to TCB0, but that means you can only use one of those at a time with TCB timekeeping
Remember, you can change which timer is used to any type A or B timer from the millis timer menu, and the TCD or rtc on the tinyAVR parts
This applies to the tinyAVR 0/1/2-series, megaAVR 0-series, and AVR DA, DB and DD-series, and all other future modern AVRs until such a time as a part which departs radically from the modern AVR precedents is released. There are few differences between the implementations on the different families.
Before reconfiguring timers, be sure to read the "Section 2: How this core uses timers" below to avoid stepping on the core's toes, or getting your toes stepped on by the core!
This applies to the tinyAVR 0/1/2-series, megaAVR 0-series, and AVR DA, DB and DD-series, and likely other future modern AVRs. There are very few differences between the implementations on the different families.
This timer is the crown jewel of the modern AVR devices, as far as timers go. It can be operated in two very different modes. The default mode on startup is "Normal" or SINGLE
mode - it acts as a single 16-bit timer with 3 output compare channels. It can count in either direction, and can also be used as an event counter (ie, effectively "clocked" off the event), is capable of counting up or down, generating PWM in single and dual slope modes, and has 7-output prescaler. In terms of PWM generation, a TCA in SINGLE mode is on the same level as the classic avr 16-bit Timer1 (though most classic AVRs only had 2 outputs per timer - there the third output was a rare feature reserved for top-end parts like the 2560). The newly added features aren't ones that are particularly relevant for most Arduino users. TCA0 can generate events or interrupts on compare match for each channel (independently), as well as on an overflow.
The Type A timer can be also be set to split mode to get six 8-bit PWM channels (this is how it is configured by default by megaTinyCore and DxCore - since analogWrite()
is only 8-bit, we might as well double the number of channels we get right? In split mode the high and low bytes of the timer count TCA0.SINGLE.CNT
register become TCA.SPLIT.LCNT
and TCA.SPLIT.LCNT
; likewise the period and compare registers in SINGLE mode, these are 16-bit registers; accessing them uses the temporary register. In SPLIT mode, they are 8-bit registers! (Unlike the TCB in PWM mode, this works correctly on all parts). The frequency of the clock from which the signals generated by the two halves of the timer are derived is always the same - they share the prescaler - but the period, and hence the frequency of the output, is not. For example, if we have a 1 MHz clock (16 MHz prescaled by 16), and set LPER to 249 (250 clocks) and HPER to 199 (200 clocks) the low-half period, covering WO 0-2 will generate PWM at 4kHz, and the high half will generate PWM at 5 kHz on WO3-5.
Event functionality is not supported in SPLIT mode.
There are a few examples of using TCA0 to generate PWM at specific frequencies and duty cycles in the document on Taking over TCA0
The pins that the type A timer is directed to can be mapped to a variety of different pins.
- On ATtiny parts, each Waveform Output (WO) pin can be controlled separately.
- On ATtiny parts with 14+ pins, WO0-2 can be mapped to either PB0-2 or PB3-5, and WO3-5 (the split mode only ones) can be mapped to PA3-5 or PC3-5 (if the part has those pins).
- On ATtiny parts with 8 pins, they are mapped to (in order) PA3-or-PA7, PA1, PA2, and PA3. Only the first 4 are available, and WO0 must be remapped in order to use WO3.
- The desired pwm pin mapping must be chosen from the tools submenu at compile time
- On non-TinyAVR parts, each TCA must be be pointed at one group of pins from a list of options. TCA0 can (on all parts thus far released) be directed to pins 0-5 of any port, while TCA1's WO0-5 channels are only available on PB0-5 or (on - and not DA-series as of mid-2022 due to errata, 128DA64's) PG0-5, and TCA1's WO0-2 64-pin parts only can be output on pins PC4-6, or (on DB-series parts - DA's are again impacted by errata here) PE4-6. The Ex-series adds options for PD4-6 and PA4-6.
- This can be freely changed at runtime simply by writing to PORTMUX.TCAROUTEA.
- On the AVR DD-series, where the TCD0 mux also works, the same functionality is applied there. You need only set PORTMUX.TCDROUTEA.
- It is recommended that PWM be off when changing the PORTMUX. Doing so when there is active PWM will cause it to move to the new pins - but it reveals a difference between the DA/DB and DD parts: On the DA/DB, the pin must be set output to generate PWM. This is not the case on the DD; this is apparently a feature, not a bug, and will be handled as a datasheet clarification for the DD
On all parts, the TCA can be used to count events (counting either positive edges, or all edges), instead of system clocks (in this mode, the prescaler is ignored, and the events must be longer than 2 system clocks or generated synchronously to the system clock to guarantee the event input is seen) or to count only while the event channel is HIGH, or to reverse direction while the event input is high. On 2-series tinyAVR parts, AVR Dx and AVR Ex, there is a second event input dedicated to restarting the timer compare cycle on rising edge, any edge or have the timer only count or to reverse direction when an event is HIGH. None of these event inputs are not available in SPLIT mode. There is no restart-on-event input on the tinyAVR 0/1-series or megaAVR 0-series.
The TCA also generates 4 event outputs - compare match for the first three channels, and an overflow event (in split mode, the overflow event is replaced by two underflow events, for a total of five event generators). These are all pulse events 1 system clock long, synchronous to the system clock (among other things, this means they can't be used to remap the PWM outputs). However the first three WO output levels are ALSO available to the CCL LUTs, so they can be used for remapping WO0-2 to any LUT output pin.
Remember that the pin invert (INVEN) effects event inputs and outputs. So if you want to count negative edges, or count prescaled clock cycles while the pin is LOW, etc - just invert the input pin.
When firing interrupts from a TCA, you must ALWAYS manually clear the intflags. It is not done for you by the hardware.
There will be parts that have a TCE instead of a TCA. As nothing is known about the TCE, we don't know the form TCE will take - but an educated guess would be something like "A TC[A|D] on steroids", likely the first of the two. We'll have to wait any see.
The type B timer is what I would describe as a "utility timer" - It is a very good utility timer. In this role, it can take one of no less than 7 modes, most of which require using the event system. They can also be used as a single channel 8-bit PWM timer. Unfortunately, they have limited selection of clock prescalers: only the system clock, the system clock divided by 2, or a clock that is already being generated for TCA0 or TCA1, making these very unappealing PWM timers. 2-series, Dx, and Ex-parts can also count rising event edges using the second event input, which is not available on the tinyAVR 0/1-series or megaAVR 0-series. In terms of input capture, the TCBs are able to do everything a classic AVR timer1 could do, plus multiple things that the classic AVR's timers couldn't even dream of doing. On the other hand, the classic Timer1 was an excellent PWM timer - while these are terrible ones.
This does exactly what you would expect - counts up to CCMP, and resets to 0 and generates an interrupt. This is how it's used for millis timekeeping. Even with the limited prescaling selections (1, 2, or the TCA prescaler), being able to count up to 64k makes this a lot less limiting than it would be on an 8-bit timer.
Timer counts continually from 0 to 65535. This is very much like the old ICP mode on classic AVRs. Upon the specified edge (selectable) of the the event connected to the timer, it will copy the current count to CCMP and fire an interrupt. It is the responsibility of the user code to know track when the last input capture occurred and subtract those values, and your interrupt must swap EDGE if you want to detect both.
As above, but at the specified edge, the timer count is automatically reset (after copying count to CCMP of course), so it starts timing the next event. As the name implies, this is of great use for measuring the frequency of an input. Does not work with prescaled clocks on some tinyAVRs (see silicon errata)
One edge restarts the timer. The other edge copies the value and fires the interrupt, thus making it perfect for timing the length of pulses.
This one is the weird one. Does not work with prescaled clocks on some tinyAVRs (see silicon errata listings). On one edge, it starts the timer. On the other edge, it copies the count to CCMP and continues to count until the next edge. Only then will the interrupt fire (if enabled). Whether via polling or interrupt, when you see that this has happened, you need copy the count value someplace safely, then the CCMP value. As soon as CCMP has been read, when it next sees the "start count" edge, it will reset the timer and start counting. Thus, while great for measuring PWM of varying frequency and duty cycle where the duty cycle does not change drastically from cycle to cycle, it is not useful if you need to record every cycle (for that, you're forced to use Input Capture on Event mode, and have your interrupt very quickly switch the edge to which it is sensitive).
In addition to the input capture modes, they can be used for precise generation of a pulse with timing accuracty down to a single clock cycle. In this mode, the output pin will go high or low (selectable) in response to the specified edge of an event (though see below). The counter will count up to it's CCMP value, then reset both it's count and it's pin (these are the same output pins as are used for PWM). It is NOT NECESSARY to use the pin itself - you can use this instead to generate a event or interrupt after a delay has passed! Likewise, it could be triggered with an event channel connected to nothing, and manually kicked off with a software event. Without relying on TCA's prescaler, this can generate pulses of around 130ms in length.
Datasheet Clarification Warning: On the latest AVR DA/DB datasheet, and on all DD datasheet versions, it is stated that when EDGE is 1, it triggers on any edge, not just negative ones, - it's not clear if that applies to other parts or not and if so which ones; it may very well apply to all parts and simply not have been updated for the other parts, or it may only reaches back as far as the DA-series. This may sound like a downgrade; if you think that, though, you have forgotten about some of the features common to all modern AVRs, namely the INVEN
bit in PINnCTRL: it is applied before the signal passes through the event generator. So you want to trigger on negative edges? Just invert the pin and trigger on positive edges! (Though, there are some cases where you can't do this. )
In this mode, one type of edge resets the counter to 0 and starts it counting, while the other edge stops the counting. In this mode the interrupt fires when the count passes CCMP - that's the "time out" that it's checking for. While this may at first sound like an odd feature, it is in fact extremely useful in combination with the custom logic and event system. The magic is that because it is both reacting to and can output an event you can use it to do things entirely in the background. Among other things, it makes working with bespoke single wire protocols much easier.
They can be pressed into service as a rather poor PWM timer. TCB in PWM mode is 8-bit only. When used for PWM, they can only generate 8-bit PWM, despite being a 16-bit timer, because the 16-bit TCBn.CCMP
register is used for both the period and the compare value in the low and high bytes respectively. They always operate in single-slope mode, counting upwards, the selection of clock options is limited in the extreme: system clock, half the system clock, or the frequency depends on that of the TCA (or one of the TCA's on parts with more than one). In other words, the type B timers are not very good at generating PWM.
Errata Alert - TCBn.CCMP
is effected by silicon errata on all available silicon save the DD and EA-series: It still acts like a 16-bit register. That means that it uses the TCB.TEMP register for access, and that you must read and write both bytes together, starting with the low byte, then high byte: Writes to the low byte are redirected to the temp register, reading the low byte copies the high byte of the CCMP register to TEMP, and writing to the high byte is what copies the low byte from the TEMP register to the actual CCMP register low byte. However, if you only write the high byte, and write the low byte only once, writes to the high byte alone do work. Until you do something like reading the CNT register, at which point everything will fall over.
The tinyAVR 2-series and Dx parts add three upgrades, two useful, and the other less-so. The less useful one is a separate OVF event/interrupt source. I find this to be of dubious utility - likely the best use of the separate OVF bit is as a 17th bit in input capture mode, but this can generally be done without using it as an interrupt.
A much more interesting option is the clock-on-event option: The TCBs now have a second event user, which can be selected as the clock source! Combined with the CCL, this can, for example, be used to get a prescaled system clock into the TCB different from that of a TCA (see the Logic library documentation and examples for discussion of how the CCL filter and synchronizer can be used to generate s prescaled clock).
Finally, on these parts, you can combine two timers to make a single, monster, 32-bit input capture timer using the CASCADE feature. To do this, you configure the high byte timer such that it is clocked from the OVF event of the low timer, and the CASCADE bit enabled. When the event fires, both timers will get their CCMP values saved and/or be stopped, permitting incredible precision (or just really long things) to be measured. 232 system clocks, which is around 3 minutes at 24 MHz. Prescaling the the low timer by 2 would double that. Ever wanted to time approximately 6 minute long event to a resolution of tenths of a microsecond? Er, sorry, I meant tenths of some period of time that is within a percent or so of a microsecond unless using an external crystal. This could also be useful if you were, instead of time, counting events on the first timer, but 16 bits just wasn't enough.
Sometimes the intflags automatically clear, but usually they don't. Be sure to clear them manually in the cases where it isn't done automatically!
The OVF flag only exists on 2-series, DA, DB, DD, and EA parts
Mode | CAPT Reset by | OVF reset by | CAPT set under normal use | OVF set under normal use |
---|---|---|---|---|
Interrupts | User code | User code | When CNT reaches CCMP | Only when CCMP set below CNT and allowed to roll over |
Timeout Check | User code | User code | When CNT reaches CCMP | Only when time between events that reset it exceeds 2^16 ticks |
Input capt. Event | Read CCMP | User code | When event occurs and CNT copied to CCMP | Every time the counter rolls over, Rarely needed |
Input capt. (all) | Read CCMP | User code | When capture occurs, depending on mode | When counter rolls over. Effectively 17th bit |
Singleshot | User code | User code | Never | Never |
PWM | User code | User code | When CNT reaches CCMPH | At end of every PWM cycle, when CNT reaches CCMPL |
The Type D timer, is a very strange timer indeed. It can run from a totally separate clock supplied on EXTCLK, or from the unprescaled internal oscillator - or, on the Dx-series, from the on-chip PLL at 2, 3, or even 4 times the speed of the external clock or internal oscillator! (the 4x multiplication option is unofficial though - it was in the very earliest headers, but was neverin the datasheet andwas removed from headers shortly thereafter. It works under favorable conditions though). It was apparently designed with a particular eye towards motor control and SMPS control applications. This makes it very nice for those sorts of use cases, but in a variety of ways ,these get in the way of using it for the sort of things that people who would be using the Arduino IDE are likely to want to do with it. First, none of the control registers can be changed while it is running; it must be briefly stopped, the register changed, and the timer restarted. In addition, the transition between stopping and starting the timer is not instant due to the synchronization process. This is fast (it looks to me to be about 2 x the synchronizer prescaler 1-8x Synchronizer-prescaler, in clock cycless. The same thing applies to reading the value of the counter - you have to request a capture by writing the SCAPTUREx bit of TCD0.CTRLE, and wait a sync-delay for it. can also be clocked from the unprescaled 20 MHz (or 16 MHz) internal oscillator, even if the main CPU is running more slowly. - though it also has it's own prescaler - actually, two of them - a "synchronizer" clock that can then be further prescaled for the timer itself. It supports normal PWM (what they call one-ramp mode) and dual slope mode without that much weirdness, beyond the fact that CMPBSET
is TOP, rather than it being set by a dedicated register. But the other modes are quite clearly made for driving motors and switching power supplies. Similar to Timer1 on the ATtiny x5 and x61 series parts in the classic AVR product line, this timer can also create programmable dead-time between cycles.
It also has a 'dither' option to allow PWM at a frequency in between frequencies possible by normal division of the clock - a 4-bit value is supplied to the TCD0.DITHER register, and this is added to a 4-bit accumulator at the end of each cycle; when this rolls over, another clock cycle is inserted in the next TCD0 cycle.
The asynchronous nature of this timer, however, comes at a great cost: It is much harder to use than the other timers. Most changes to settings require it to be disabled as noted above - and you need to wait for that operation to complete (check for the ENABLERDY
bit in TCD0.STATUS
). Similarly, to tell it to apply changes made to the CMPxSET
and CMPxCLR
registers, you must use the TCD.CTRLE
(the "command" register) to instruct it to synchronize the registers. Similarly, to capture the current count, you need to issue a SCAPTUREx command (x is A or B - there are two capture channels) - and then wait for the corresponding bit to be set in the TCD0.STATUS
register. In the case of turning PWM channels on and off, not only must the timer be stopped, but a timed write sequence is needed ie, _PROTECTED_WRITE(TCD0.FAULTCTRL,value)
to write to the register that controls whether PWM is enabled; this is apparenmtly because, in the intended use-cases of motor and switching power supply control, changing this accidentally (due to a wild pointer or other software bug) could have catastrophic consequences (these are, remember, certified for use in safety critical applications). Writes to any register when it is not "legal" to write to it will be ignored. Thus, making use of the type D timer for even simple tasks requires careful study of the datasheet - which is a frustratingly terse chapter at key points, yet is STILL the longest chapter at 50 pages (counting only chapters that are mostly text (so the electrical/typical charachteristics section doesn't count).
So if you're doing something where misbehavior of the PWM timer could lead serious hardware damage (eg, via shootthrough on an H-bridge or the nearly identical situation on a synchronously rectifying DC-DC converter), this is the timer to use if you want the best protection against that, and more flexibility in generating the waveforms that would be needed in such applications. Note that I don't recommend building your own DC-DC converter, owing to the low price of assembled modules, especially ones prone to potentially destructive shoot-through because that shouldn't even be a problem with off the shelf solutions in this day and age, and I certainly don't recommend building anything where a malfunction could result in serious damage.
Remember this part of the license.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
We have a separate document entirely dedicated to TCD0
The recently announced EB-series will feature not one but TWO new timers. Nothing is currently known about the TCE other than it can output 8 PWM channels in some configurations, and six in others, and that it's a 16-bit timer, and that it has WEX. Whether these are independent channels or whether half of them must be the inverse of the other half is not known (that has precedent on classic AVRs. I would have hoped they have learned).
WEX is a feature from xMega. One hopes it has been streamlined, as it was incomprehensible there (like essentially everything about xmega). It allowed generation of enhanced resolution PWM, and probably some other things (like I said, it was incomprehensible). Only time (specifically the moment that datasheet is released) will reveal whether WEX is friendly, or if heinous supervillian WEX Luther has escaped imprisonment on xMegatraz and is out for revenge...
Even though the EB-series, from a great distance away, is looking like something less than a big prize, the timers it gets may be a sign of good things to come. Or bad things.
No, I didn't make a mistake there, yes, they're both coming in the same release. We know even less about this one. For example we know that TCE is going to be built around a 16-bit timer. TCF lists 16-bits on one page and 24 on another. And it suggests that the timer's waveform outputs are for "frequency generation" (read: duty cycle 50%; now that would represent a significant escalation of the the abuse we're taking here, and which is starting to look like the theme of the family. We already have the main feature of the family on the 2-series with the new ADC, but then they had to rip out the good internal oscillator and switch back to the tinyAVR one and cut the non-tiny EB's down to sub-tiny peripheral limitations with only USART? You think we just wanted pin options? There is a HUGE difference netween 1 UART and 2 - it's a larger difference (between 1 and 2 than there is between and 5, and only a little smaller than 0 versus 1 USART). By EB release we should have gotten to look at the titles of the chapters for the next installment or two of AVRs and we'll know whether the TCE and TCF are going almost everywhere or hardly anywhere - so the size of our wager will be set, and we will just have to wait a bit longer to see whether the Ex-series is as Exey as the double D's - or whether AVR is going to be like Windows (where alternating releases were either great or awful (by the standards of the company, to be clear. I don't think any hardware company could beat that record unless it blew up unexpectedly - (the unexpectedly bit is key, otherwise it's a different industry, and a rather lucrative one. A device that would burst into flames under programmable conditions would be an unsavory, but not unprofitable line of business. If it only burst into flames at random, well, that's not going to do well. Samsung tried it a few years back, wasn't much of a success. I think I'd still rather keep my phone in an asbestos lined suitcase than use Windows ME...
Hm? Where did I get the nice suitcase? Thanks, it's it from Hazmart Short & Small Menswear, same place as the buy one three free deal on Note 8's. Whether you are looking for Russian nerve agents, fissile material to fuel a home-built nuclear reactor or just trying to keep up with the family down the street and their pet tiger, they're's something for everyone on your list. There's no shopping experience quite like Hazmart. Hazardous goods for hazardous people. See in store for details on how you can win this stylish mercury fountain featuring a genuine poisonivywood base. Nothing quite like a mercury fountain on a genuine poison ivy wood base... Quiz - Which one of the following actually existed: The home nuclear reactor? The asbestos-lined fireproof clothing? The poison ivy knicknacks? The mercury fountain? Or the radium-infused shampoo?
Answers are somewhere in this long and otherwise rather dry document
Information on the RTC and PIT will be added in a future update.
One very important thing to be aware of on the tinyAVR 0/1-series only (and not the 3216/3217, where this has been fixed) is that there is some NASTY errata here. First, you must never have the RTC running without the PIT, nor the pit without the RTC). The Microchip errata for impacted parts does not do justice to the extent of how strange the resulting behavior is. Second, the PIT timer will be glitch every time RTC.CTRLA is written to, because the prescaler count gets reset (since you can have up to 15 bits of prescaling, this can set the timer back up to 1 second (assuming a 32.768 kHz clock)). It is also easy to inadvertently rely on this to get a deterministic delay from the PIT, which is not supposed to be possible. The intended behavior is that if you set the PIT to generate an interrupt every X seconds, it will do that, but you don't get to control where in the cycle you start from.
Prescaler | TCAn | TCBn | TCD0 | TCD0 sync | TD0 counter |
---|---|---|---|---|---|
CLK | YES | YES | YES | YES | YES |
CLK2 | YES | YES | YES* | YES | NO |
CLK/4 | YES | TCA | YES | YES | YES |
CLK/8 | YES | TCA | YES* | YES | NO |
CLK/16 | YES | TCA | YES* | NO | NO |
CLK/32 | NO | NO | YES | NO | YES |
CLK/64 | YES | TCA | YES* | NO | NO |
CLK/128 | NO | NO | YES* | NO | NO |
CLK/256 | YES | TCA | YES* | NO | NO |
CLK/1024 | YES | TCA | NO | NO | NO |
- Requires using the synchronizer prescaler as well. My understanding is that this results in sync cycles taking longer- I believe it takes 2-3 synchronizer clocks, with is 2-3 clock cycles without prescaling (just fast enough so that if you don't check it, and immediately write an enable locked register, it won't work) and 16-24 with maximum prescale. This is also how long it takes after disabling the timer for the EN_READY status flag to return indicating that you can turn it back on again.
TCA
indicates that for this prescaler, a TCA must also use it, and the TCB set to use that TCA's clock.
When working with timers, I constantly found myself calculating periods, resolution, frequency and so on for timers at the common prescaler settings. While that is great for adhoc calculations, I felt it was worth some time to make a nice looking chart that showed those figures at a glance. The numbers shown are the resolution (when using it for timing), the frequency (at maximum range), and the period (at maximum range - ie, the most time you can measure without accounting for overflows).
Timer function | Timer used | Under conditions | Examples |
---|---|---|---|
PWM | TCA0 | Pin in question is linked to TCA0 | All |
PWM | TCD0 | TCD0 is present. | 1-series |
PWM | TCBn | Not supported on megaTinyCore - could only get 1 pin | None |
tone | TCB1, TCB0 | Prefers TCB1 if not used for time | All |
servo | TCB0, TCB1 | Prefers TCB0 if not used for time | All |
millis | TCD0 | Default where TCD0 present | 1-series |
millis | TCB1 | Default where TCB1 present but TCD0 is not | 2-series |
millis | TCA0 | Default where neither TCD0 nor TCB1 present | 0-series |
millis | TCB0 | Used only when millis timer explicitly set to TCB0 |
This section consists of around 6 points, Under many of these points are the consequences for users and any thing the user might need to do.
- All TCAs are initialized for SPLIT mode to generate 6x 8-bit PWM channels, not 3 16-bit ones. If you need that, you must implement it yourself and
takeOverTCA0();
(or TCA1 - TCA1 is probably a better choice since TCA0 has lots of 6-pin options while TCA1 does not, assuming you have both to chooe from) insetup()
. The PORTMUX register is set according to the part specific documentation page for the part you are using. - If you want to take over a type A timer, simply call takeOverTCA0() or takeOverTCA1() - this will stop the timer, send the reset command to reset it's registers to their defaults and set a flag that tells the core not to use the timer for analogWrite(). a. That means it will no longer be in SPLIT mode. If SPLIT mode is desired, simply write TCA0.CTRLD to 1 before enabling the timer. b. You may not takeOverTCAn if that timer is used for millis (it will be treated as a badCall compile error to call the takeOver function). Reconfiguring it anyway will likely cause unexpected, undefined, and undesired behavior, potentially impacting anything relating to PWM and timekeeping. So don't do that. b. If any type B timers are being used for PWM, remember that they share the prescaler settings with a TCA, and changing the frequency of the TCA will also change that of the TCB(s) following it. See TCB notes at bottom of this list.
- The TCD is initialized for PWM in single-ramp mode. See the TCD reference for specifics about what speeds and what value it counts up to - TOP is 254 or a power-of-two multiple of that depending on clock speed, and other power-of-two multipliers may be set by the user at runtime with neither takeOverTCD0() nor custom code to handle updating the duty cycle! The PORTMUX register (being broken on DA/DB parts) is never set to anything other than the default value there. It works on the DD-series, but there is only one case where a certain option is unambiguously better: on the DD14 parts only the ALT4 mux option has any pins at all, So on the DD14's we set the portmux to ALT4.
- If you do
takeOverTCD0()
, that will stop the timer at the end of the current cycle, and that function will wait until that has happened before returning (it will also, at that time, clear the TCD intflag register). , but it is still your responsibility to clear out any settings the core put there because there is no reset command. It may be best to override init_TCD() with an empty function, takeOverTCD0 immediately in setup, and reconfigure it then. a. Once you have taken over TCD0 with that function, if you want to restore TCD0 to it's power on reset state, you need to set these registers to 0: TCD0.CTRLA (this controls the prescalers as well as enabling), TCD0.CTRLC (the core sets this to 0x40 so WOD outputs the second channel), TCD0.CMPASET*
, TCD0.CMPBSET*
, TCD0.CMPACLR, TCD0.CMPBCLR. b.__PROTECTED_WRITE(TCD0_FAULTCTRL,0)
*
(this register is not just enablelocked. It is protected as well) c. You also need to set set TCD0.STATUS*
= 0xC0 to clear the PWMACT bits if you need them cleared (If you don't know what I'm talking about you don't care, and if you do, you know if you care). d. You will never literally write 0 to that list of registers, since if you're taking over the type D timer, presumably you plan to reconfigure it to do something else. Those are just the registers you need to be sure to hit duruing this process in order to ensure that there's nothing left of the core-set options. Just remember to save TCD0.CTRLA (or at least it's enable bit) for last, because almost everything is enable-locked on the TCD. e. Registers marked with*
can be assumed to be in power on reset state if you haven't set them manually nor called analogWrite() with them. f. The above ONLY applies to DxCore or megaTinyCore when a non-type-D timer is used for millis. On megaTinyCore, if you've used the type D timer for millis, takeOverTCD0 is not available, and TCD0 cannot be configured without causing gross misbehavior. If you need to take over TCD0 on mTC, make sure a different timer is chosen for millis in the tools submenu. - Assuming a TCB is used for millis(), it is configured for periodic interrupt mode. User code should absolutely not mess with the millis timer, otherwise unexpected, undefined, and undesired behavior is likely to occur relating to timekeeping or whatever you are trying to make the timer do. Turn off millis, or use a different timer.
- PORTMUX.TCBROUTEA is set during initialization. The value here is assumed to never change. No method is provided to change this at the current time. The two conditions where this may be a problem are: a. If you are reconfiguring a TCB, but are still using it 8-bit PWM mode, AND there is no other timer available on the same pin as the one the core used by default, digitalWrite() and analogWrite() must not be called on that pin (digitalWriteFast() can be used instead, as it does not do anything relating to PWM). b. If you are changing the TCB portmux hoping to get PWM on different pins, it won't work. This can only be done by creating a new variant file which changes the
- All other TCBs are configured for 8-bit PWM (even though they suck as PWM timers), so that analogWrite() can be used with them. a. There is no takeOverTCBn() function. Just remember that THE TIMER IS NOT in the default state b. analogWrite won't try to use a TCB whose CTRLB register is not set for PWM - in that case, either you have reconfigured the timer (so we shouldn't mess up your configuration), or it's the millis timer, and we definitely shouldn't mess with that.
If top were 255, there would be exactly 256 timer ticks per clock, TOP = 254, there are 255 ticks. The millis math is magically much easier in the latter case!
There is also a problem with analogWrite. We pass a value between 0 and 255 inclusive to it, for 1 of 256 outcomes, 2 of which involve PWM. However, When TOP is 255, the timer output compare can generate 255 duty cycles + 1 constant level. But we need to generate 2 constant levels! So inevitably (usually at one end, where it's most noticeable) one count gets skipped. With TOP=254, however, there are 256 possible inputs and 256 possible outputs. And there's no glitch at the end,
Many pins have more than one possible timer that they could use. When asked to write PWM to a pin, the order of priority used is as listed below. The DAC is obviously not a timer; it is however USED like a timer as far as PWM is concerned. analogWrite() treats the DAC pin as if the DAC were a timer, and the duty cycle as the output voltage. See ADC and DAC reference for more info on that.
- TCA0
- TCA1
- DAC or TCD0 (these never overlap; there is no code written to support hypothetical parts where there's a DAC that can use the same pin as a TCD or TCB)
- Any TCB.
Note that while it does not currently impact any parts, there is a significant limitation in this implementation: A pin may have at most one out of DAC output, TCD output, or TCB output, because there is a table that relates a single timer identifier to each pin. Oddly enough, there has yet to be a conflict. Note that TCA outputs don't matter here, only TCD, TCB and DAC.
In the future EB-series, the timer priorities will have to be considered carefully.
If you are using a part (like a DD-series) where the pin mapping being used doesn't provide a pin for a given timer output channel, that channel is not available... at least not directly. However it is still there, it can still be used as an internal input... and therefore, you CAN use the CCL (either manually or with the Logic library) and set it to use input 0, 1, or 2 from a TCA, TCB, or TCD. In the case of a TCA, the input number of the logic block is the waveform output - so only WO0-WO2 (the outputs that go on Px0, Px1, and Px2). In the case of a TCB, the input number is the number of the type B timer (only the first three timers can be used in this way). And in the case of the TCD, input 0 is WOA, input 1 is WOB, and input 2 is WOC. But WOC just mirrors WOA (unless you've changed TCD0.CTRLC) - so it's just a way of using the WOA channel as input 2 instead of input 0, which may be important (since CCL input 2 is special, particularly for even numbered LUTs).which means it can be output through a CCL logic block. See the CCL chapter of the datasheet or the Logic library docs for information on how you would implement these. Whereas the event system gives you a relatively useless 1-clock long pulse when the PWM compare match occurs, the CCL has access to the levels. So you if were using a DD14, with the TCA mux set to the most-pinful and the default of the core, PORTC, there is still no PC0, and only WO1-3 of the TCA available. Between those three and TCD0 on PD4 and PD5, you've still only got 5 PWMs.
But you could set input0 of LUT2 to be TCA0, enable the alternate pin mapping for the CCL LUT2 output (CCL output is on pin 3 or 6 of the LUT's "home port"), mask the other two inputs and set the truth table to 0x02, and get TCA0 WO0 on PD6. 6 PWM channels! Hm? You need seven you say? Well, okay - you could use TCB0 as the input for a the CCL LUT0, 1, or 3, but instead of enabling pin output (since those pins don't exist, or in the case of LUT1 on PORTC are already being used), you would have to instead set one of the event channels to use that CCL LUT as it's generator. Event output can be piped to either pin 2 or 7 in a port. PORTD is your only good choice. Then you could set EVOUTD to be an event user for that channel, and switch it's portmux to the alternate pin, PIN_PD7. Now you've got 7 channels! You need another? You can actually get an 8th if you really work at it - but millis must be either running on TCA0 or disabled, you must have an HV UPDI programmer to reprogram it, otherwise the required fuse settings will brick it. You would have to disable the UPDI functionality (hence it cannot be reprogrammed without an HV programmer). That turns PF7, the nominal UPDI pin, into a GPIO pin (reset can be turned into an input, but not an output on the DD (and barely can in general) *
) This has very few functions available - in fact it has only 2 special functions, the SS pin of one of the wacky SPI mappings on the DD-series... and the alternate output pin of EVOUTF! You can then repeat the process above with TCB1 and EVOUTF. And at that point you have 8 independently controllable duty cycles coming out of 8 pins on your 14-pin package, of which 3 pins are power or ground, and another of which (PF6/reset) is input only. So 80% of the potential output pins and more over half of the physical pins could be used to output PWM without even having to use some sort of software PWM scheme - once the duty cycles were set and the peripherals enabled, the PWM would be generated without CPU intervention. The two remaining pins could then be used as, say, a serial port so you could tell it what duty cycles to use or something.
*
On modern AVRs, only tinies have the option to set the pin nominally used as reset as an I/O pin (to the extent that one can call PA0 on a modern tinyAVR the nominal reset pin - by default it's the UPDI pin, but it can be made to be the reset pin instead. If that's done it needs HV programming to reprogram) - non-tiny modern AVRs can use it as an input, but not an output. On the parts where it can be used as one, it's a miserable output: even sourcing or sinking just 0.5mA and running at Vdd = 5V (which maximizes the pin driver strength), the output could be a volt away from the rail it's trying to drive towards (on those parts, a pin sourcing or sinking so much current that it was a volt away from the power rail would be carrying at least 15mA. The same was true on classic AVRs when reset was disabled - it could be turned into I/O, but it was a crap output). You may be wondering why this is. The answer - apparently - is that whichever pin the HV for HV programming has to touch is forced to use weaker pin drivers. This isn't entirely surprising - imagine using MOSFETS and a very weak input. If the output would never be higher than supply or lower than ground, you'd just have a complementary pair of FETs... but if it can also be an input, which can go above supply, the high side P-channel FET would turn into a diode and channel the higher voltage to the positive supply rail! (the same would happen on the low side and voltages below 0V) Either would obviously be a Bad Thing, so on the pin that is designed to be exposed to 12v, they had to take precautions, and that ends up weakening the pin drive greatly. This hurts particularly on tinyAVR parts, because that's also UPDI: it is much easier for the tinyAVR's UPDI pin to get overwhelmed by some other source of voltage (ex, a pullup that's stronger than it should be) than other parts. Even optimistically assuming that the current will increase linearly if we accept higher voltage drop (not necessarily a valid assumption, generally speaking; doubling the current may more than double the voltage drop; see the ID vs Vds plot in any MOSFET datasheet, where lines are shown for each Vgs: the result is linear when current is well below it's rated maximum, but flattens out as the current and voltage drop fall further. Microcontroller pins, outside of fault conditions (and possibly even in fault conditions), run in the low current regime, where the I/V behavior is approximately ohmic.
The core reconfigures the type A timers in split mode, so each can generate up to 6 PWM channels simultaneously. The LPER
and HPER
registers are set to 254, giving a period of 255 cycles (it starts from 0), thus allowing 255 levels of dimming (though 0, which would be a 0% duty cycle, is not used via analogWrite, since analogWrite(pin,0)
calls digitalWrite(pin,LOW)
to turn off PWM on that pin). This is used instead of a PER=255 because analogWrite(255)
in the world of Arduino is 100% on, and sets that via digitalWrite()
, so if it counted to 255, the arduino API would provide no way to set the 255/256th duty cycle). Additionally, modifications would be needed to make millis()
/micros()
timekeeping work without drift when TCA is selected for timekeeping. Preventing drift is easy with PER = 254, but hard at PER = 255 (.
The core supports generating PWM using up to 6 channels per timer, and will work with alternate PORTMUX settings as long as the the selected option isn;t one of the three-channel ones for TCA1 - those are not supported. TCA1 can be on PB0-5 or PG0-5 (and not even the latter on DA due to errata). TCA0 can go on pin 0-5 in any port (though they must all be on the same port. We default to configuring it for PD on 28/32 pin parts, PA on 14 and 20-pin and PC on 48/64 pin ones).
analogWrite()
checks the PORTMUX.TCAROUTEA
register, and on DD-series parts (and DA/DB if we ever get a fix) the PORTMUX.TCDROUTEA
register to know which pins might have PWM from those timers.
Because analogWrite() supports PWM on TCB-controlled pins, any timer not being used for millis is configured in PWM mode on initialization. You are responsible for resetting the timers first if you need them for something else!
Alternate pins are supported from 2.6.7 onwards via a tools submenu. They cannot be changed at runtime due to the overhead involved.
analogWrite()
on tinyAVR parts does NOT interpret the PORTMUX settings like DxCore does. The logic required is more complex than on DxCore while resources are more limited. On the tinyAVR parts, alternate pins are not supported. They are set individually, and much more chaotically, so an algorithm to support takes more clock cycles and more flash on parts that have less of both; The PORTMUX.TCAROUTEA
or PORTMUX.CTRLC
register should not be written without calling takeOverTCA0()
, however, since 2.6.7 there is a tools submenu to support this.
TCD0, by default, is configured for generating PWM (unlike TCA's, that's about all it can do usefully). TCD0 is clocked from the CLK_PER when the system is using the internal clock, without prescaling. On the prescaled clocks (5 and 10 MHz) it is run it off the unprescaled oscillator (just like on the 0/1-series parts that it inherits the frequencies from), keeping the PWM frequency near the center of the target range. When an external clock is used, we run it from the internal oscillator at 8 MHz, which is right on target.
It is always used in single-ramp mode, with CMPBCLR
(hence TOP) set to either 254, 509, or 1019 (for 255 tick, 510 tick, or 1020 tick cycles), the sync prescaler set to 1 for fastest synchronization, and the count prescaler to 32 except at 1 MHz. CMPACLR
is set to 0xFFF (the timer maximum, 4095). The CMPxSET
registers are controlled by analogWrite()
which subtracts the supplied dutycycle from 255, checks the current CMPBCLR high byte to see how many places to left-shift that result by before subtracting 1 and writing to the register. The SYNCEOC
command is sent to synchronize the compare value registers at the end of the current PWM cycle if the channel is already outputting PWM. If it isn't, we have to briefly disable the timer, turn on the pin, and then re-enable it, producing a glitch on the other channel. To mitigate this issue we treat 0 and 255 duty cycles differently for the TCD pins - they instead set duty cycle to 0% without disconnecting the pin from the timer, for the 100% duty cycle case, we invert the pin (setting CMPxSET to 0 won't produce a constant output). This eliminates the glitches when the channels are enabled or disabled.
TCD0 has two output channels - however, each of them can go to either of two pins (the hardware has slightly more complexity, but it was not worth the overhead to implement. All parts with those pins have a mapping for TCD on PA4-PA7. On most, that's the only one that works. However, on the DD there are more options, refer to the part-specific documentation for a list. TCD PWM is PORTMUX aware, so you can change the pin mapping on DxCore without changing th
analogWrite(PIN_PA4, 64); // outputting 25% on PA4
analogWrite(PIN_PA5, 128); // 25% on PA4, 50% on PA5
analogWrite(PIN_PA5, 0); // 25% on PA4, PA5 constant LOW, but *still connected to timer*
digitalWrite(PIN_PA5, LOW);// NOW PA5 totally disconnected from timer. A glitch will show up briefly on PA4.
analogWrite(PIN_PA6, 192); // This is on same channel as PA4. We connect channel to PA6 too (not in place of - we do the same thing on ATTinyCore for the 167 pwm output from Timer1 on the latest versions).
// so now, both PA4 and PA6 will be outputting a 75% duty cycle. Turn the first pin off with digitalWrite() to explicitly turn off that pin.
You can get a lot of control over the frequency without having to take over full management of the timer (which is rather complicated, and difficult to reconfigure) as long as you follow the rules carefully: See TCD0 reference Step off that narrow path, however, and analogWrite()
will not work correctly.
TCD0, by default, is configured for generating PWM if the PWM pins selection includes that timer. (unlike TCA's, that's about all it can do usefully). TCD0 can output two channels, like on Dx-series parts, BUT unlike those parts, there are only two muxing options. Due to space constraints on these parts, we only support two pins at a time (at most - 14 or 8 pins have only one option
It is always used in single-ramp mode, with CMPBCLR
(hence TOP) set to either 254, 509, or 1019 (for 255 tick, 510 tick, or 1020 tick cycles), the sync prescaler set to 1 for fastest synchronization, and the count prescaler to 32 except at 1 MHz. CMPACLR
is set to 0xFFF (the timer maximum, 4095). The CMPxSET
registers are controlled by analogWrite()
which subtracts the supplied dutycycle from 255, checks the current CMPBCLR high byte to see how many places to left-shift that result by before subtracting 1 and writing to the register. The SYNCEOC
command is sent to synchronize the compare value registers at the end of the current PWM cycle if the channel is already outputting PWM. If it isn't, we have to briefly disable the timer, turn on the pin, and then re-enable it, producing a glitch on the other channel. To mitigate this issue we treat 0 and 255 duty cycles differently for the TCD pins - they instead set duty cycle to 0% without disconnecting the pin from the timer, for the 100% duty cycle case, we invert the pin (setting CMPxSET to 0 won't produce a constant output). This eliminates the glitches when the channels are enabled or disabled.
You can get a lot of control over the frequency without having to take over full management of the timer (which is rather complicated, and difficult to reconfigure) as long as you follow the rules carefully: See TCD0 reference Step off that narrow path, however, and analogWrite()
will not work correctly.
The type B timers, while not particularly good for PWM, can be used for PWM on DxCore (on mTC they always overap with other timers, making them even less useful; they are not supported there.) They are clocked from the highest number TCA available, though this can be changed freely.)
This function returns 1 if the pin currently has PWM available accounting for PORTMUX and takeOverTCxn
. The dynamic analog of digitalPinHasPWM()
- obviously, not compile-time constant.
This function returns the timer the pin will be controlled by accounting for PORTMUX and takeOverTCxn
. The dynamic analog of digitalPinToTimer()
- obviously, not compile-time constant.
After this is called, analogWrite()
will no longer control PWM on any pins attached to timer TCA0 (though it will attempt to use other timers that the pin may be controllable with to, if any), nor will digitalWrite()
turn it off. TCA0 will be disabled and returned to it's power on reset state. All TCBs that are used for PWM on parts with only TCA0 use that as their prescaled clock source buy default. These will not function until TCA1 is re-enabled or they are set to use a different clock source. Available only on parts with TCA1 where a different timer is used for millis timekeeping.
After this is called, analogWrite()
will no longer control PWM on any pins attached to timer TCA1 (though it will attempt to use other timers that the pin may be controllable with to, if any), nor will digitalWrite()
turn it off. TCA1 will be disabled and returned to it's power on reset state. All TCBs that are used for PWM on parts with TCA1 use that as their prescaled clock source buy default. These will not function until TCA1 is re-enabled or they are set to use a different clock source. Available only on parts with TCA1, and only when a different timer is used for millis timekeeping.
After this is called, analogWrite()
will no longer control PWM on any pins attached to timer TCD0 (though it will attempt to use other timers, if any), nor will digitalWrite()
turn it off. There is no way to reset type D timers like Type A ones. Instead, if you are doing this at the start of your sketch, override init_TCD0. If TCD is ever supported as millis timing source, this will not be available.
This can be called after takeOverTimerTCA0(). It resets TCA0 and sets it up the way the core normally does and re-enables TCA0 PWM via analogWrite.
This can be called after takeOverTimerTCA1(). It resets TCA1 and sets it up the way the core normally does and re-enables TCA1 PWM via analogWrite.
TCBs are handled differently since each one has only a single PWM channel - we will treat it as available (and hence use it as a timer of last resort if analogWrite() or turnOffPWM() is called on that timer's output pin) if and only if that timer is currently set to PWM mode, which we set during initialization for all timers not being used for millis timekeeping. it is currently set to PWM mode. Which pin is fixed by the variant file and shown in the core documentation for that part family - they've been chosen differently on different parts to maximize the number of usable pins).
The frequency of PWM output using the settings supplied by the core is shown in the table below. The "target" is 1 kHz, never less than 490 Hz or morethan 1.5 kHz. As can be seen below, there are several frequencies where this has proven an unachievable goal. The upper end of that range is the point at which - if PWMing the gate of a MOSFET - you have to start giving thought to the gate charge and switching losses, and may not be able to directly drive the gate of a modern power MOSFET and expect to get acceptable results (ie, MOSFET turns on and off completely in each cycle, there is minimal distortion of the duty cycle, and it spends most of it's "on" time with the low resistance quoted in the datasheet, instead of something much higher that would cause it to overheat and fail). Not to say that it definitely will work with a given MOSFET under those conditions (see the PWM section of my MOSFET guide ), but the intent was to try to keep the frequency low enough that that use case was viable (nobody wants to be forced into using a gate driver), without compromising the ability of the timers to be useful for timekeeping.
The following tables show how the core will configure the TCA and TCD timers at initialization, and the small table shows which general areas of the timer configuration are set up by the core, differently from the power on reset values, and which of those can "safely" be changed. In this context "safely" means without causing analogWrite() or digitalWrite() to stop working on the pin without calling takeOverTCnx(). To change more than that, you must call the takeover function and assume full responsibility for the timer (digitalWrite will work, but not be able to turn off PWM on the timer's pins and analogWrite() will act as though that timer did not exist).
Property | Configured | Can be safely changed |
---|---|---|
Prescale | Yes | Unless millis. TCA, TCD, TCB. |
Mode | Yes | No. Take over the timer. |
TOP | Yes, 254 | Only on DxCore TCD0, to certain values only. |
Interrupt | Only millis | Yes, though usually the associated configuration precludes pwm. |
Events | No | Yes |
16-bits (TCA) | No | Only via full takeover. No analogWrite/etc support |
On the type D timer, ALL situations with an external clock source (crystal or clock), we just say "screw this!" to the thorny problem of deciding how to generate a good PWM frequency from whackjob eternal clocks, and use the internal oscillator at 8 MHz, with a /32 prescale on the counter and /1 on the synchronizer, because we know that gives the target frequency, and don't have to play games with top or nothing. Because Timer D is uncooperative and combative, we have taken pains to make it easier to adjust. This is detailed in the TCD Reference - but to give a brief summary, without doing takeOverTCD0() and taking full responsibility for that timer in all of it's hostile glory you can do the following without breaking analogWrite():
- Change TOP to 254, 509, 1019, 2039, or 4079 (the last options are only available at F_CPU > 32 MHz - not for a fundamental reason, just tradeoff of time and space vs features, and consideration of the conditions under which those features would be most useful (high clock speeds) - in order to act as an ersatz prescaler.
- This can be done without stopping the timer. It is accomplished by setting
TCD0.CMPBCLR
to one of those values. - No values other than the 5 listed above are supported. No design consideration was given to this case while implementing analogWrite(), other than to declare it unsupported. Our tests to detect which option is selected are optimized for execution speed, and assume that only those 5 valid values will be there (so we only need to look at one byte).
- This will disrupt the duty cycle of any active PWM output.
- This can be done without stopping the timer. It is accomplished by setting
- Adjust any other settings which do not change the PWM mode. This allows use of all the event-related features.
- Stop the timer by setting the LSB of TCD0.CTRLA to 0.
- When you do this, poll TCD0.STATUS until the EN_RDY bit is 1. That means the registers are now unlocked and you can change them.
- You can then set new values for all registers except
CTRLB
CTRLC
andCTRLD
without violating the assumptions of the core. - Make changes to
CTRLA
last - this will include setting the LSB to 1.
- When using an external clock source for the system clock, TCD0 defaults to running from the internal oscillator at 8 MHz. You can change that frequency (without even stopping the timer) normally (but note that the prescaler in CLKCTRL is for the main clock, the clock source that TCD gets is unprescaled - though it can use the main clock and further prescale it if desired).
Some frequencies shown are not supported by the core, but may be specified by a variant or boards.txt and while not guaranteed to work, are believed to work. 2 should definitely work, 6 and 3 very likely work, and 14 and 7 likely work.
On the table below, I repeat, all external clock configurations get the same TCD configuration. This is shown explicitly for clock speeds that can only be achieved with an external clock, but the table will otherwise assume the internal oscillator is used as the system clock; if an external clock or crystal is used to achieve the same speed, refer to the External line for the TCD only.
CLK_PER | Prescale A | fPWM | Prescale D | TOP D | fPWM (D) | Notes |
---|---|---|---|---|---|---|
** 48 MHz | 256 | 735 Hz | external | 254 | 980 Hz | Always external clock. |
** 44 MHz | 256 | 674 Hz | always uses | 254 | 980 Hz | Always external clock. |
** 40 MHz | 256 | 613 Hz | TCD per the | 254 | 980 Hz | Always external clock. |
** 36 MHz | 256 | 551 Hz | below line | 254 | 980 Hz | Always external clock. |
External | per F_CPU | per F_CPU | OSCHF@8 32 | 254 | 980 Hz | TCD using OSC@8 because running from xtal/ext |
* 32 MHz | 256 | 490 Hz | 32 | 1019 | 980 Hz | TOP used as ersatz-prescaler to divide by 4 |
* 30 MHz | 64 | 1836 Hz | OSCHF@8 32 | 254 | 980 Hz | TCD using OSC@8 because running from xtal/ext |
* 28 MHz | 64 | 1716 Hz | 32 | 1019 | 858 Hz | TOP used as ersatz-prescaler to divide by 4 |
* 27 MHz | 64 | 1654 Hz | OSCHF@8 32 | 254 | 980 Hz | TCD using OSC@8 because running from xtal/ext |
* 25 MHz | 64 | 1532 Hz | OSCHF@8 32 | 254 | 980 Hz | TCD using OSC@8 because running from xtal/ext |
24 MHz | 64 | 1471 Hz | 32 | 1019 | 735 Hz | TOP used as ersatz-prescaler to divide by 4 |
20 MHz | 64 | 1225 Hz | 32 | 509 | 1225 Hz | TOP used as ersatz-prescaler to divide by 2 |
16 MHz | 64 | 980 Hz | 32 | 509 | 980 Hz | TOP used as ersatz-prescaler to divide by 2 |
14 MHz | 64 | 858 Hz | OSCHF@28 32 | 1019 | 858 Hz | Unsupported, timing functionality not guaranteed |
12 MHz | 64 | 735 Hz | 32 | 509 | 735 Hz | |
10 MHz | 64 | 613 Hz | OSCHF@20 32 | 509 | 1225 Hz | |
8 MHz | 64 | 490 Hz | 32 | 254 | 980 Hz | |
7 MHz | 16 | 1716 Hz | OSCHF@28 32 | 1019 | 858 Hz | Unsupported, timing functionality not guaranteed |
6 MHz | 16 | 1471 Hz | OSCHF@12 32 | 509 | 735 Hz | Unsupported, timing functionality not guaranteed |
5 MHz | 16 | 1225 Hz | OSCHF@20 32 | 509 | 1225 Hz | |
4 MHz | 16 | 980 Hz | 32 | 254 | 490 Hz | |
3 MHz | 8 | 1471 Hz | 4 | 509 | 1471 Hz | Unsupported, timing functionality not guaranteed |
2 MHz | 8 | 980 Hz | 4 | 509 | 980 Hz | Unsupported, timing functionality not guaranteed |
1 MHz | 8 | 490 Hz | 4 | 254 | 980 Hz |
*
Overclocked (generally works, 28 and 32 can be achieved with internal oscillator)
**
Way overclocked, may not work (requires external crystal or oscillator).
Prescale A
and fPWM
apply to all pins not on TCD0. TOP is always set to 254 for TCA
Prescale D
, TOP D
, and fPWM (D)
apply to the pins on TCD0.
In addition to the case of an external system clock source. Note that this did not work correctly prior to 1.4.0 Where marked, we clock TCD0 from OSCHF instead of using CLK_PER, prescale by 32. For speeds other than 5 MHz and 10 MHz, we set the internal oscillator to 8 MHz.
These are the overall Timer D prescaler (in all cases, by default only the count prescaler is used if at all possible), TOP, and resulting frequency of TCD0 PWM output.
Things are very different over on megaTinyCore, at least as far as TCD0 is concerned. Here, whenever we are using the internal clock, with or without division, we use the unprescaled 16 or 20 MHz clock. Otherwise we use the system clock (it can be argued that this is incorrect behavior and we should use the internal oscillator for more consistent PWM frequencies)
CLK_PER | Prescale A | fPWM | Prescale D | TOP D | fPWM (D) | Notes |
---|---|---|---|---|---|---|
All extern | per F_CPU | <--- | OSCHF@20 32 | 1019 | 613 Hz | 16 and 20 MHz get slightly different treatment |
All extern | per F_CPU | <--- | OSCHF@16 32 | 509 | 980 Hz | . |
* 32 MHz | 256 | 490 Hz | OSCHF@32 32 | 1019 | 980 Hz | . |
* 30 MHz | 256 | 459 Hz | OSCHF@30 32 | 1019 | 919 Hz | Unsupported, timing functionality not guaranteed |
* 28 MHz | 256 | 429 Hz | OSCHF@28 32 | 1019 | 877 Hz | . |
* 27 MHz | 256 | 413 Hz | OSCHF@27 32 | 1019 | 827 Hz | Unsupported, timing functionality not guaranteed |
* 25 MHz | 64 | 1532 Hz | OSCHF@25 32 | 1019 | 816 Hz | Unsupported, timing functionality not guaranteed |
* 24 MHz | 64 | 1471 Hz | OSCHF@24 32 | 1019 | 735 Hz | Unsupported, timing functionality not guaranteed |
20 MHz | 64 | 1225 Hz | OSCHF@20 32 | 1019 | 613 Hz | . |
16 MHz | 64 | 980 Hz | OSCHF@16 32 | 509 | 980 Hz | . |
14 MHz | 64 | 858 Hz | OSCHF@14 32 | 509 | 858 Hz | Unsupported, timing functionality not guaranteed |
12 MHz | 64 | 735 Hz | OSCHF@12 32 | 509 | 735 Hz | 0/1-series on 16 MHz osc (Applies to 3,6 MHz too) |
12 MHz | 64 | 735 Hz | OSCHF@24 32 | 1019 | 735 Hz | 0/1-series on 20 or any 2-series |
10 MHz | 64 | 613 Hz | OSCHF@20 32 | 1019 | 613 Hz | . |
8 MHz | 64 | 490 Hz | OSCHF@16 32 | 509 | 980 Hz | . |
7 MHz | 16 | 1716 Hz | OSCHF@14 32 | 509 | 858 Hz | Unsupported, timing functionality not guaranteed |
6 MHz | 16 | 1471 Hz | OSCHF@12 32 | 509 | 735 Hz | Unsupported, timing functionality not guaranteed |
6 MHz | 16 | 1471 Hz | OSCHF@24 32 | 1019 | 735 Hz | Unsupported, timing functionality not guaranteed |
5 MHz | 16 | 1225 Hz | OSCHF@20 32 | 1019 | 613 Hz | . |
4 MHz | 16 | 980 Hz | OSCHF@16 32 | 509 | 980 Hz | . |
3 MHz | 8 | 1471 Hz | OSCHF@12 32 | 509 | 735 Hz | Unsupported, timing functionality not guaranteed |
3 MHz | 8 | 1471 Hz | OSCHF@24 32 | 1019 | 735 Hz | Unsupported, timing functionality not guaranteed |
2 MHz | 8 | 980 Hz | OSCHF@16 32 | 509 | 980 Hz | . |
1 MHz | 8 | 490 Hz | OSCHF@16 32 | 509 | 980 Hz | . |
*
Overclocked (generally works, 28 and 32 can be achieved with internal oscillator)
**
Way overclocked, may not work (requires external crystal or oscillator).
Prescale A
and fPWM
apply to all pins not on TCD0. TOP is always set to 509 for TCA
A table is presented for each type of timer comparing the percentage of CPU time spent in the ISR, the resolution of the timekeeping functions, and the execution time of micros.
Prescale D
, TOP D
, and fPWM (D)
apply to the pins on TCD0.
In addition to the case of an external system clock source. Note that this did not work correctly prior to 1.4.0 Where marked, we clock TCD0 from OSCHF instead of using CLK_PER, prescale by 32. For speeds other than 5 MHz and 10 MHz, we set the internal oscillator to 8 MHz.
megaTinyCore allows any of the timers to be selected as the clock source for timekeeping via the standard millis timekeeping functions. ThThe timer used and system clock speed will effect the resolution of millis()
and micros()
, the time spent in the millis ISR, and the time it takes for micros()
to return a value. The micros()
function will typically take several times it's resolution to return, and the times returned corresponds to the time micros()
was called, regardless of how long it takes to return.
Both micros()
and millis()
take the timer value at almost the moment they are called, then do the math on it, so if you wait for something, call micros()
wait until something else happens and call micros()
again, you can take the difference and be confident that it's not grossly inaccurate. But since micros()
takes several microseconds to return, you can't code as if it returns instantly. (It has to multiply a 16 bit integer by a 32 bit one, perform division-by-successive-bitshift-and-add/subtraction on a 16-bit opperand and then add the two together, 8-bits at a time? Surely 120 clocks isn't too long to wait for a 32 bit multiply (timer_overflow_count*1000
) and a 5-9 term approximated division (ticks = (ticks >> n) - (ticks >> n+a) ... (ticks >> n+n+h)
on a 20 MHz 8-bit CPU?)
Consider:
while (!digitalReadFast(pina)); // this is an extremely tight loop - sbrs rjmp .-4 - 3 clock cycles, so 0-2 clock cycles, + 0-2 clock cycles sync delay/
starttime = micros(); // this is 6 us @ 20 MHz. starttime returns the time measured in the first few clocks after calling it.
//timetwo = micros(); // starttime - timetwo = ~6
while (digitalReadFast(pina));
endtime = micros(); //
Imagine if at t = 100 and around*
20 MHz, the pin goes high. Within 0.10 the pin change is sync'ed and then you wait 0.05-0.15us for the loop to catch it, within 0.4us it has recorded the micros timer value. So you enter the second loop at t = 106.25, and starttime = t at t = 100.45-100.55, returning 100.45-100.55 (that is, the value of micros at a true time of 100.45-100.55 us). If the pin is already low, we would call micros at that time, and expect that by 106.35 we would get the value of micros at true time 106.9, record 106 or 107, subtract the two and get 6 or 7 us for the length of the pulse, otherwise, suppose it changes at 200 us, we call micros, which has it's value around 200.4 and at 206.25 returns likely 200 or 201 (with 200 more likely if 100 was measured the first time and vice versa), and would conclude that the pulse was 100us long, at worst 99 or 101 us.
But if you check micros in a loop, it is not so harmless
while (!digitalReadFast(pina)); // this is an extremely tight loop - sbrs rjmp .-4 - 3 clock cycles, so 0-2 clock cycles, + 0-2 clock cycles sync delay/
starttime = micros(); // this is 6 us @ 20 MHz. starttime returns the time measured in the first few clocks after calling it.
digitalWriteFast(pinb, HIGH); //
while (micros() - starttime < 100); // this loop only samples micros every ~6us,
digitalWriteFast(pinb, LOW); // so now our pulse is too short
We still enter at 106.30, but now we are waiting around 6us between calls to micros(), so with with orders to stop 100 us from the time it was about 6.5us ago, and we notice that it's time at t = 200-205, so our pulse was from 106 or 107 to 200 to 205, Hence, it's length would actually be 94-101 us.
This skews the other direction:
while (!digitalReadFast(pina)); // this is an extremely tight loop - sbrs rjmp .-4 - 3 clock cycles, so 0-2 clock cycles, + 0-2 clock cycles sync delay/
digitalWriteFast(pinb, HIGH); // this time, write the pin before recording starttime.
starttime = micros(); // this is 6 us @ 20 MHz. starttime returns the time measured in the first few clocks after calling it.
while (micros() - starttime < 100); // this loop only samples micros every ~6us,
digitalWriteFast(pinb, LOW); // so now our pulse is too long
We enter as above at 106.35us, but this time we started the pulse at 100.3us, and hold about the same value (100, maybe 101) in starttime, but we wouldn't notice until T = 200-205, and our pulse would be 99-106 us long.
while (!digitalReadFast(pina)); // this is an extremely tight loop - sbrs rjmp .-4 - 3 clock cycles, so 0-2 clock cycles, + 0-2 clock cycles sync delay/
starttime = micros(); // this is 6 us @ 20 MHz. starttime returns the time measured in the first few clocks after calling it.
digitalWriteFast(pinb, HIGH); //
while (micros() - starttime < (100 - (6/2))); // this loop only samples micros every ~6us,
digitalWriteFast(pinb, LOW); // so now our pulse on average is 100usm but might be as low as 96 or as high and 103
So - the message is you shouldn't busywait on micros() if you need microsecond accuracy. micros is for time since something happened, it is not meant to be checked continually to see if a timeout is passed. in the previous example, if linked by the event system to the pin, a TCB could be counting as soon as the new pin value was synchronized, giving a consistent length of 100us starting a fraction of a us after the pulse was seen. You can generate pulses up to (131070 / Clocks-per-us), about 6.5 seconds at 20 MHz, accurate to within 2 system clocks in that way. If you also wanted to delay the execution of later code, which you likely do if your first instinct was something like what is shown above, these can be done concurrently....
setupTCB(); // call your function to configure TCB in single shot mode, outputting a pulse 2000 clocks long triggered by a rising edge.
setupEVSYS(); // anc connect pina to an event channel, and set the TCB to use it.
while (!digitalReadFast(pina)); // we'll see the pin at the same time as the TCB,
starttime = micros(); // this is 6 us @ 20 MHz. starttime returns the time measured in the first few clocks after calling it.
//timetwo = micros(); // starttime - timetwo = ~6
while (digitalReadFast(pina)); // and exit the loop at t=200 us basedon the ideal clock. Up until now, we've assumed the clock was at exactly 20 MHz, but if it's off 1%, the pulse length would be off in the same direction. So this will end after the original pulse ended, with our pulse on for another microsecond (if our clock is 1% slow) or have been off for a microsecond (if our clock is 1% fast).
endtime = micros(); //
A table is presented for each type of timer comparing the percentage of CPU time spent in the ISR, the resolution of the timekeeping functions, and the execution time of micros. Typically micros()
can have one of three execution times, the shortest one being overwhelmingly more common, and the differences between them are small.
setupTCB(); // call your function to configure TCB in single shot mode, outputting a pulse 2000 clocks long triggered by a rising edge.
setupEVSYS(); // anc connect pina to an event channel, and set the TCB to use it.
while (!digitalReadFast(pina)); // we'll see the pin at the same time as the TCB,
starttime = micros(); // this is 6 us @ 20 MHz. starttime returns the time measured in the first few clocks after calling it.
//timetwo = micros(); // starttime - timetwo = ~6
while (digitalReadFast(pinb)); //wait for our own pulse to end;
endtime = micros(); //
You're probably hoping to time the input pulse and make sure the output pulse has ended before continuing:
setupTCB(); // call your function to configure TCB in single shot mode, outputting a pulse 2000 clocks long triggered by a rising edge. If it is desired to not retrigger, enable the TCB interrupt and use it to disable the TCB and/or event channel.
setupEVSYS(); // anc connect pina to an event channel, and set the TCB to use it.
while (!digitalReadFast(pina)); // we'll see the pin at the same time as the TCB,
starttime = micros(); // this is 6 us @ 20 MHz. starttime returns the time measured in the first few clocks after calling it.
//timetwo = micros(); // starttime - timetwo = ~6
while (digitalReadFast(pina)); //wait for our input pulse to end;
endtime = micros(); // highly accurate as long as pulse len longer than micros()
while (digitalReadFast(pinb)); // make sure our pulse is over - can be omitted if we're not emitting a pulse plausibly longer by more than the micros execution time (in this example, if we were outputting a pulse that might be longer than 106 us, counting timer drift), though it only costs 4 bytes and 2 clocks to test for it.
*
internal oscillator is usually within a percent at room temperature, and within half a percent is common. We're assuming that at the moment millis started, an ideal stopwatch was started, and when it indicated 100us had gone by, drove the pin low effectively instantaneously.
A table is presented for each type of timer comparing the percentage of CPU time spent in the ISR, the resolution of the timekeeping functions, and the execution time of micros. Typically micros()
can have one of three execution times, the shortest one being overwhelmingly more common, and the differences between them are small.
When TCA0 is used as the millis timekeeping source, it is set to run at the system clock prescaled by 8 when system clock is 1MHz, 16 when system clock is <=5 MHz, and 64 for faster clock speeds, with a period of 255 (as with PWM). This provides a millis()
resolution of 1-2ms, and is effectively not higher than 1ms between 16 and 30 MHz, while micros()
resolution remains at 4 us or less. At 32 MHz or higher, to continue generating PWM output within the target range, we are forced to switch to a larger prescaler (by a factor of 4), so the resolution figures fall by a similar amount, and the ISR is called that much less often.
CLK_PER | millis() | micros() | % in ISR | micros() time |
---|---|---|---|---|
48 MHz | 1.36 ms | 5.3 us | 0.19 % | 2.5 us |
44 MHz | 1.48 ms | 5.8 us | 0.19 % | aprx 3.5 us |
40 MHz | 1.63 ms | 6.4 us | 0.19 % | 3.5 us |
36 MHz | 1.81 ms | 7.1 us | 0.19 % | aprx 4 us |
32 MHz | 2.04 ms | 8.0 us | 0.19 % | 4 us |
30 MHz | 0.54 ms | 2.1 us | 0.51 % | aprx 4 us |
28 MHz | 0.58 ms | 2.3 us | 0.51 % | 4 us |
25 MHz | 0.65 ms | 2.6 us | 0.51 % | aprx 4 us |
24 MHz | 0.68 ms | 2.7 us | 0.51 % | 5 us |
20 MHz | 0.82 ms | 3.2 us | 0.51 % | 7 us |
16 MHz | 1.02 ms | 4.0 us | 0.51 % | 9 us |
! 14 MHz | 1.14 ms | 4.6 us | 0.51 % | aprx 10 us |
12 MHz | 1.36 ms | 5.3 us | 0.51 % | 10 us |
10 MHz | 1.63 ms | 6.4 us | 0.51 % | 14 us |
8 MHz | 2.04 ms | 8.0 us | 0.51 % | 17 us |
! 7 MHz | 0.58 ms | 2.3 us | 2.13 % | aprx 18 us |
! 6 MHz | 0.68 ms | 2.7 us | 2.13 % | aprx 19 us |
5 MHz | 0.82 ms | 3.2 us | 2.13 % | 27 us |
4 MHz | 1.02 ms | 4.0 us | 2.13 % | 33 us |
! 3 MHz | 0.68 ms | 2.7 us | 3.55 % | aprx 45 us |
! 2 MHz | 1.02 ms | 4.0 us | 3.55 % | aprx 60 us |
1 MHz | 2.04 ms | 8.0 us | 3.55 % | 112 us |
!
- Theoretical, these speeds are not supported and have not been tested
In contrast to the type B timer where prescaler is held constant while the period changes, here period (in ticks) is constant but the prescaler is not. Hence each prescaler option is associated with a fixed % of time spent in the ISR (and yes, for reasons I don't understand, the generated ISR code is slightly faster for /64 prescaling compared to /256, /16, and /8 (which are equal to each other). Edit: Probably gets to render something as a swap
The micros execution time does not depend strongly on F_CPU, running from 112-145 clock cycles.
Except when the resolution is way down near the minimum, the device spends more time in the ISR on these parts. Notice that at these points that - barely - favor TCAn, the interrupt they're being compared to is firing twice as frequently! TCD0's interrupt is slower than TCA's, but it is the timer least likely to be repupurposed.
When a TCB is used for millis()
timekeeping, it is set to run at the system clock prescaled by 2 (1 at 1 MHz system clock) and tick over every millisecond. This makes the millis ISR very fast, and provides 1ms resolution at all speeds for millis. The micros()
function also has 1 us or almost-1 us resolution at all clock speeds (though there are small deterministic distortions due to the performance shortcuts used for the microsecond calculation - it never strays more than 1us or two, but there is a systematic effect there. The type B timer is an ideal timer for millis, however, they're also good for a lot of other things too. It is anticipated that as libraries for IR, 433MHz OOK'ed remote control, and similar add support for the modern AVR parts, that these timers will see even more use.
- On all parts with a TCB2, that is the default millis timer.
- On a tinyAVR 1-series, TCD0, despite being a poor millis timer, is used, since it's considered an unlikely target for user takeover.
- On any part left after those two steps, if it has a TCB1, that's the default.
- Otherwise TCA0 is, so there's a free TCB by default.
ISR execution time has decreased significantly in recent versions through aggressive optimization.
Note | CLK_PER | millis() | micros() | % in ISR | micros() time | Terms used |
---|---|---|---|---|---|---|
48 MHz | 1 ms | 1.33 us | 0.14 % | * 2.5 us | 9 (0-7, 9) | |
! | 44 MHz | 1 ms | 1 us | ! 0.15 % | ! 3-4 us | 5/7 ( |
40 MHz | 1 ms | 1 us | 0.17 % | * 3-4 us | 6 (2,4,6,8,10) | |
! | 36 MHz | 1 ms | 1 us | 0.18 % | 3-4 us | 3/5 (3,6, |
† | 32 MHz | 1 ms | 1 us | 0.20 % | 3 us | 1 |
30 MHz | 1 ms | 1.07 us | 0.22 % | < 6 us | 5 (3,4,7,8) | |
! | 28 MHz | 1 ms | 1.14 us | 0.23 % | <= 6 us | 5/7 (2,3,5,6 |
! | 27 MHz | 1 ms | 1.18 us | 0.24 % | <= 6 us | 4 (2,4,9) |
25 MHz | 1 ms | 1.28 us | 0.26 % | <= 6 us | 3/5 ( |
|
* | 24 MHz | 1 ms | 1.33 us | 0.27 % | 5 us | 9 (0-7, 9) |
* | 20 MHz | 1 ms | 1 us | 0.33 % | 6 us | 6 (2,4,6,8,10) |
† | 16 MHz | 1 ms | 1 us | 0.40 % | 6 us | 1 |
! | 14 MHz | 1 ms | 1.14 us | 0.47 % | approx. 12 us | 5/7 (2,3,5,6 |
* | 12 MHz | 1 ms | 1.33 us | 0.54 % | 10 us | 9 (0-7, 9) |
10 MHz | 1 ms | 1 us | 0.65 % | 10 us | 6 (2,4,6,8,10) | |
† | 8 MHz | 1 ms | 1 us | 0.80 % | 11 us | 1 |
! | 7 MHz | 1 ms | 1.14 us | 0.94 % | approx. 25 us | 5/7 (2,3,5,6 |
! * | 6 MHz | 1 ms | 1.33 us | 1.08 % | > 20 us | 9 (0-7, 9) |
* | 5 MHz | 1 ms | 1 us | 1.30 % | 23 us | 6 (2,4,6,8,10) |
† | 4 MHz | 1 ms | 1 us | 1.18 % | 21 us | 1 |
! * | 3 MHz | 1 ms | 1.33 us | 1.61 % | > 40 us | 9 (0-7, 9) |
!† | 2 MHz | 2 ms | 1 us | 1.18 % | 39 us | 1 |
† | 1 MHz | 2 ms | 1 us | 2.36 % | 78 us | 1 |
!
- Theoretical, these speeds are not supported and have not been tested (though 3 MHz is the same math as 6, 12, and 24 MHz). % time in ISR and micros return times, where given are theoretically calculated, not experimentally verified.
†
- Naturally ideal - no ersatz division is needed for powers of 2
*
- indicates that the shift & sum ersatz-division is done in hand-optimized assembly because I just couldn't stand how stupid and stubborn the compiler was, and by the time I was done analyzing it, implementing it was mostly done. These use every term that would improve the accuracy of the approximation, shown in the final column. The algorithm divides the timer ticks (which is between zero and F_CPU/2000) by a power if 2 (by right shifting) to get term 0; a number of terms (generated by further rightshifting) are then added and subtracted to get the time since the last millis was counted, and that total is added to 1000x the millisecond count. The ideal number of terms varies from 1 (for powers of 2 - only term 0) to 9 (for the terrible twelves (12, 24, 48, as well as 3 and 6). In optimized cases, that's how many are used. Otherwise, some may be dropped, or different combinations used (adding 1 term, then subtracting the next, would seem equivalent to just adding the next - but because of integer math, it's not, and in fact can lead to several us of backwards timetravel, so if you'd have to do the same operation twice in a row, you get more accurate numbers if you avoid that by adding another term). In optimized cases, as well as the powers of 2, the results are as good as possible in light of the specified resolution - where resolution is coarser than 1us, (1/resolution) of possible values for the three lowest digits (in normal base 10 number systems) is skipped and never shows up. These skipped values are distributed evenly between 1 and 999. For example, for 12/24/48, that means 250 values never show up. Of the remaining 750, 500 will show for only 1 "actual" microsecond value, and 250 for 2 consecutive "actual" microsecond values. See the table at the end of this document for the list of skipped least-significant-digit combinations. None of the optimized options will never go back in time, even by a single microsecond (as long as the chance of backward time travel is limited to less than the time it takes micros to execute, it can't break delay()
, cause timeouts to instantly expire or cause other catastrophic consequences) nor are more than 2 values in a row ever skipped. Those criteria are not guaranteed to be met for other speeds, though we do guarantee that negative time travel will never occur that causes two consecutive calls to micros to return a smaller value for the second call which is what triggers misbehavior of timing code.
Resolution is always exactly 1ms for millis with TCB millis except for 1 and 2 MHz speeds, where the millis resolution is 2ms, though micros resolution is still 1us, and whereas TCAn micros()
is limited by the resolution of the timer, here it's instead limited only by the fact that the calculations drop the least significant bits first; this results in a value that may be as low as 750, yet is being mapped to 0-999, for 1.33 us resolution in the worst cases. The timer count and the running tally of overflows could get us microseconds limited only by F_CPU/2
The percentage of time spent in the ISR varies in inverse proportion to the clock speed - the ISR simply increments a counter and clears its flags. 65 clocks from interrupt bit set to interrupted code resuming.
The time that micros()
takes to return a value varies significantly with F_CPU; where not exact, value is estimated. Specifically, powers of 2 are highly favorable, and almost all the calculations drop out of the 1 and 2 MHz cases (the are similar mathematically - at 1 MHz we don't prescale and count to 1999, at 2 we do prescale and count to 1999, but then we need only add the current timer count to 1000x the number of milliseconds. micros takes between 78 and 160 clocks to run. Everything else has to use some version of a shift-and-sum ersatz division algorithm, since we want to approximate division using >>
, +
and -
. Each factor of 2 increase in clock speed results in 5 extra clocks being added to micros in most cases (bitshifts, while faster than division, are still slow when you need multiples of them on larger types, especially in compiler-generated code that doesn't reuse intermediate values.)
The terms used, and - where different - the number that would be ideal, is listed above
Although it's the default timer on all tinyAVRs that have it, it's actually a pretty lousy millis timer. So why do we use it? Simply put - because it's the timer that people are least likely to want to repurpose - there are two things within the core that need a TCB if used (Tone and Servo). Plus the TCD is incredibly complicated and very hard to use. If you need a timer, unless you're doing very unusual things, it's not worth the learning curve to figure out that godawful timer. So by putting millis on that timer, even though it's not a great timer for it, we can leave the timers people are more likely to want to repurpose available.
There are a handful of functions exposed that manipulate the timekeeping:
bool stop_millis();
/* stops the millis timer. Call restart_millis to restart it.
* Was previously void. Now can return true if there's a problem (millis was not running normally when this was called).
*/
bool restart_millis();
/* restarts the millis timer after calling stop_millis()
* Was previously void. Returns true if there was a problem restarting millis, likely a timer left in a trashed state by user code.
*/
void set_millis(uint32_t);
/* Set the current millisecond count to the argument provided. With a TCB timer,
* millis and micros will stay in sync. Because of the more complicated math involved
* with the TCA, which has to track fractional milliseconds, set_millis() should not
* be expected to maintain synchronization between millis and micros, and micros may
* see small distortions. */
void nudge_millis(uint16_t);
/* Set the current millisecond time this many milliseconds forward. The same caveat
* about micros not being changed with TCA for timing apply here. Unlike the above, this
* is specifically directed at the application where you have to do something that will
* block interrupts for long enough that you know you'll miss millis counts, and you know
* how long that something is. You will end up that many milliseconds behind, less
* 1-2 x millis resolution (from table above). You could then pass that to nudge_millis()
* to limit the drift to 0-1 x millis resolution. (since the time you can have interrupts
* disabled for before drifting begins to occur) */
uint8_t _getCurrentMillisTimer();
/* New: This allows you to interrogate the timekeeping system. This will return either one of
* a subset of the options from Appendix I. Normally this will only return the value set in the
* tools submenu. However, if stop_millis() was called and restart millis hasn't, this will return
* NOT_A_TIMER (to differentiate from disabled millis). Additionally in the future with the
* sleepTime library two additional options will be possible: TIMERRTC and TIMERPIT. These are
* returned in the unlikely event that this is called when sleeptime has been sleeping with RTC
* timekeeping and hasn't switched back yet.
* See Appendix I. */
(macro) MILLIS_TIMER
/* This is the value (from appendix I - anything that _gCMT() can return except TIMERRTC and TIMERPIT, but including TIMERRTC_INT, TIMERRTC_XTAL, and TIMERRTC_EXT. These indicate that the RTC is *permanently* the millis source (mTC only).
*/
The tone()
function included with DxCore or megaTinyCore uses one Type B timer. It defaults to using TCB0; do not use that for millis timekeeping if using tone()
. Tone is not compatible with any sketch that needs to take over TCB0. If possible, use a different timer for your other needs. When used with Tone, it will use CLK_PER or CLK_PER/2 as it's clock source - the TCA clock will never be used, so it does not care if you change the TCA0 prescaler (unlike the official megaAVR core). It does not support use of the hardware output compare to generate tones like tone on some parts does. (Note that if TCB0 is used for millis/micros timing - which is not a recommended configuration, we usually use the higher numbered TCB - tone()
will instead use TCB1).
Recent Fixes - Prior to the release of 1.3.3, there were a wide variety of bugs impacting the tone()
function, particularly where the third argument, duration, was used; it could leave the pin HIGH
after it finished, invalid pins and frequencies were handled with obvously unintended behavior, and integer math overflows could produce weird results with larger values of duration at high frequencies, and three-argumwent tone()
didn't work at all above 32767Hz due to an integer overflow bug (even though maximum supported frequency is 65535 Hz).
The three argumenttone(pin,uint16_t freq, uint32_t duration (ms))
achieves that timing by possibly the crudest way imaginable - calculates the number of timer ticks that's equal to the duration, and then downcounts. This means some very large numbers can cause problems. Cases where this is is potentially a problem are referred to herafter as "Long Tones". A long tone is any tone(pin, frequency, duration) where (4.294 billion) (2^32 - 1) < (frequency * duration) < (2^32-1)*500
(2.149 trillion). These are cases where the naive calculation will overflow the uint32_t in an intermediate, that is, when frequency (Hz) * duration (ms)
will overflow. At the absolute maximum frequency that can be specified, 65535 Hz, it will occur starting at a duration of 65537 ms. Yet what we need is the number of times the ISR fires, and that would happen twice per cycle, so that becomes
2 * frequency (Hz) * duration (ms)
and we need to get it into consistent units, so we also have to divide by 1000, which can be factored out to eliminate the multiplication by two, leaving (frequency (Hz) * duration (ms)) / 500
- this would not overflow until your 65.535 kHz tone lasted longer than 22.7 days. So although the result would fit into the destination, the intermediate will overflow at 1/500th of that duration (! but we don't want to divide either of the frequency or duration by 500, as that would push a great deal of quantization noise into that one variable. However, if we're willing to use a bit more flash, we can distribute that 500 - We divide the frequency by 10 and the duration by 50, because with such long tones, the duration doesn't matter down to a granularity of 20ms, nor does frequency likely matter down to 10 Hz. The inaccuracy introduced here is far smaller than the error from the imperfect internal oscillators, for example. Use millis/micros and NoTone() for Very Long Tones. Anything longer is a "very long" tone and is never supported via the three argument tone()
. Your earsplitting high-pitched whine with a duration of more than a few hours (which is far outside the regime that tone is intended for) must be implemented with a 2-argument tone() and millis. If it needs to be implemented at all. Even normal "long tones" require a high frequency tone requested for minutes, which is an extreme use case.
So, there are two implementations of tone() used, differing very slightly
- On parts with 8k flash or less, considering that this is a corner case, we say "screw it, not worth the flash" and let it overflow.
- On parts with 16k or more of flash, we distribute the division before performing the multiplication.
It can only generate a tone on one pin at a time.
All tone generation is done via interrupts. The hardware output compare functionality is not used for generating tones because the hardware of the type B timer is ill-suited to this application, while. using a type A timer, and on many parts that's all the PWM you get - though the library would be simple.
The Servo library included with this core uses one Type B timer. It defaults to using TCB1 if available, unless that timer is selected for millis timekeeping. Otherwise, it will use TCB0. The Servo library is not compatible with any sketch that needs to take over these timers - if possible, use a different timer for your other needs. Servo and tone()
can only be used together on when neither of those is used for millis timekeeping.
Regardless of which type B timer it uses, Servo configures that timer in Periodic Interrupt mode (CNTMODE
= 0) mode with CLK_PER/2 or CLK_PER as the clock source, so there is no dependence on the TCA prescaler. The timer's interrupt vector is used, and it's period is constantly adjusted as needed to generate the requested pulse lengths. In 1.1.9 and later, CLK_PER is used if the system clock is below 10MHz to generate smoother output and improve performance at low clock speeds.
The above also applies to the Servo_DxCore/Servo_megaTinyCore library; it is an exact copy except for the name. If you have installed a version of Servo via Library Manager or by manually placing it in your sketchbook/libraries folder, the IDE will use that in preference to the one supplied with this core. Unfortunately, that version is not compatible with the Dx-series parts. Include Servo_DxCore.h instead in this case. No changes to your code are needed other than the name of the library you include.
We provide a few functions that allow the user to tell the core functions that they wish to take full control over a timer, and that the core should not try to use it for PWM. These are not part of a library because of the required integration with the core to control analogWrite()
and digitalWrite behavior.
Whenever a function supplied by the core returns a representation of a timer, these constants will be used
Timer Name | Value | Peripheral | Used for |
---|---|---|---|
NOT_ON_TIMER | 0x00 | None | millis (disabled) or when asked what timer outputs on a pin available for PWM. |
TIMERA0 * | 0x10 | TCA0 |
millis and/or PWM |
TIMERA1 * | 0x08 | TCA1 |
millis and/or PWM |
TIMERA2 * | 0x18 | TCA2 |
Nothing. No such part exists |
TIMERB0 | 0x20 | TCB0 |
millis or PWM |
TIMERB1 | 0x21 | TCB1 |
millis or PWM |
TIMERB2 | 0x22 | TCB2 |
millis or PWM |
TIMERB3 | 0x23 | TCB3 |
millis or PWM |
TIMERB4 | 0x24 | TCB4 |
millis or PWM |
TIMERB0_ALT | 0x30 | TCB0 |
Reserved, not used |
TIMERB1_ALT | 0x31 | TCB1 |
Reserved, not used |
TIMERB2_ALT | 0x32 | TCB2 |
Reserved, not used |
TIMERB3_ALT | 0x33 | TCB3 |
Reserved, not used |
TIMERB4_ALT | 0x34 | TCB4 |
Reserved, not used |
TIMERD0 | 0x40 | TCD0 |
PWM or on mTC only, millis |
TIMERD0_0WOA ** | 0x40 | TCD0 |
PWM WOA |
TIMERD0_0WOB ** | 0x50 | TCD0 |
PWM WOB |
TIMERD0_0WOC ** | 0x60 | TCD0 |
PWM WOC |
TIMERD0_0WOD ** | 0x70 | TCD0 |
PWM WOD |
TIMERD0_1WOA ** | 0x41 | TCD0 |
with mux in 3 LSBs |
TIMERD0_4WOD ** | 0x74 | TCD0 |
and so on to here |
TIMERE0 | TBD | TCE0 |
TBD |
TIMERF0 | TBD | TCF0 |
TBD |
DACOUT *** | 0x80 | DAC0 |
DAC output |
TIMERRTC | 0x90 | RTC |
@ sleepTime.h |
TIMERPIT | 0x98 | RTC PIT |
@ PD sleepTime.h |
NOT_A_TIMER | 0xFF | None | Reserved @@ |
0 (NOT_ON_TIMER
) will be returned by digitalPinToTimer() or digitalPinToTimerNow() (herafter: dPTT and dPTTN) if the specified pin has no timer.
*
Currently, the 3 low bits are never set to 1. However, this may change in the future. There are unfortunately two three-bit pieces of information vying for the same three data bits, and which piece of information you want depends on what you're doing, and is only trivial to determine for TCA0: the waveform output channel (0-2 or 0-5) or the mux option (0-6) that points to that pin, . No other timer will ever be numbered 0x10-0x17, nor 0x08-0x0F. 0x18-0x1F is reserved for hypothetical future parts with a third TCA. Hence to test for TCA type: (timerType = MILLIS_TIMER & 0xF8; if (timerType || timerType < 0x20) {timerType = 0}
will give either NOT_ON_TIMER
, TIMERA0
, TIMERA1
, or in the future, potentially TIMERA2
. Note that dPTT does not report on the TCAs at all on DxCore (unlike megaTinyCore) - only dPTTN does, because dPTT needs to be a macro (or at least constant/compile time known and foldable) otherwise it causes problems with other code that relies upon it being a macro (often without realizing it), or which produce really bad output from that. But at compile time, you have no idea which timer the TCAs are on - you don't know that until you call analogWrite, which calls the function dPTTN()
**
What was described above regarding including the TCA mux option would look much like this. Notice how we split the byte up into, effectively,
***
A hypothetical part with multiple DACs with output buffers will extend this by increasing the count. into 0x8x.
@
Planned for future use. All 0x9x values are reserved for future applications of the RTC and/or PIT and not other new kinds of timers to be determined at a future date.
@@
Any function that takes these values as one of it's arguments will - in the event that the value passed does not correspond to a timer -
Up to four mux options per TCB and up to 8 TCBs on a future part could be accommodated - alt1 and alt2 would start at 0x28 and 0x38, though as noted we currently do not use bit 4 or 3.
There were some key and non-obvious hazards:
- Unfortunately I never actually wrote down the list, just the heading, and forgot what they were, then came back days or weeks later and saw the cut-off sentence. Sorry. And it's a shame, I think these were important.
Timer Name | Value | Peripheral | Used for |
---|---|---|---|
NOT_ON_TIMER | 0x00 | None | millis (disabled) or when asked what timer outputs on a pin available for PWM. |
TIMERA0 * | 0x10 | TCA0 |
millis and/or PWM |
TIMERA1 * | 0x08 | TCA1 |
millis and/or PWM |
TIMERA2 * | 0x18 | TCA2 |
Nothing. No such part exists |
TIMERB0 | 0x20 | TCB0 |
millis or PWM |
TIMERB1 | 0x21 | TCB1 |
millis or PWM |
TIMERB2 | 0x22 | TCB2 |
millis or PWM |
TIMERB3 | 0x23 | TCB3 |
millis or PWM |
TIMERB4 | 0x24 | TCB4 |
millis or PWM |
TIMERB0_ALT | 0x30 | TCB0 |
Reserved, not used |
TIMERB1_ALT | 0x31 | TCB1 |
Reserved, not used |
TIMERB2_ALT | 0x32 | TCB2 |
Reserved, not used |
TIMERB3_ALT | 0x33 | TCB3 |
Reserved, not used |
TIMERB4_ALT | 0x34 | TCB4 |
Reserved, not used |
TIMERD0 | 0x40 | TCD0 |
PWM or on mTC only, millis |
TIMERD0_0WOA ** | 0x40 | TCD0 |
PWM WOA |
TIMERD0_0WOB ** | 0x50 | TCD0 |
PWM WOB |
TIMERD0_0WOC ** | 0x60 | TCD0 |
PWM WOC |
TIMERD0_0WOD ** | 0x70 | TCD0 |
PWM WOD |
TIMERD0_1WOA ** | 0x41 | TCD0 |
with mux in 3 LSBs |
TIMERD0_4WOD ** | 0x74 | TCD0 |
and so on to here |
TIMERE0 | TBD | TCE0 |
TBD |
TIMERF0 | TBD | TCF0 |
TBD |
DACOUT *** | 0x80 | DAC0 |
DAC output |
TIMERRTC | 0x90 | RTC |
millis |
TIMERRTC_INT | 0x91 | RTC, int. 32k |
mTC only, deprecated in light of upcoming sleepTime |
TIMERRTC_XTAL | 0x92 | RTC, 32k Xtal |
mTC only, deprecated in light of upcoming sleepTime |
TIMERRTC_EXT | 0x93 | RTC, ext clk |
mTC only, deprecated in light of upcoming sleepTime |
TIMERPIT | 0x98 | RTC PIT |
@ PD sleep time |
NOT_A_TIMER | 0xFF | None | Reserved @@ |
0 (NOT_ON_TIMER
) will be returned by digitalPinToTimer() or digitalPinToTimerNow() (herafter: dPTT and dPTTN) if the specified pin has no timer.
*
Currently, the 3 low bits are never set to 1. However, this may change in the future. There are unfortunately two three-bit pieces of information vying for the same three data bits, and which piece of information you want depends on what you're doing, and is only trivial to determine for TCA0: the waveform output channel (0-2 or 0-5) or the mux option (0-6) that points to that pin, . No other timer will ever be numbered 0x10-0x17, nor 0x08-0x0F. 0x18-0x1F is reserved for hypothetical future parts with a third TCA. Hence to test for TCA type: (timerType = MILLIS_TIMER & 0xF8; if (timerType || timerType < 0x20) {timerType = 0}
will give either NOT_ON_TIMER
, TIMERA0
, TIMERA1
, or in the future, potentially TIMERA2
. Note that dPTT does not report on the TCAs at all on DxCore (unlike megaTinyCore) - only dPTTN does, because since we
**
What was described above regarding including the TCA mux option would look much like this. Notice how we split the byte up into, effectively,
***
A hypothetical part with multiple DACs with output buffers will extend this by increasing the count. into 0x8x.
@
Planned for future use. All 0x9x values are reserved for future applications of the RTC and/or PIT and not other new kinds of timers to be determined at a future date.
@@
Any function that takes these values as one of it's arguments will - in the event that the value passed does not correspond to a timer -
Up to four mux options per TCB and up to 8 TCBs on a future part could be accommodated - alt1 and alt2 would start at 0x28 and 0x38, though as noted we currently do not use bit 4 or 3.
The algorithm that you want to use to unpick this hence looks something like:
Calls to _gCMT
should be conditionally compiled based on CORE_HAS_CURRENTTIMER. If it is 0 or is not defined, you must not attempt to call _gCMT
as it will not be defined. In this case, sleepTime is not supported, as it relies upon this, and and that there are only two possibilities, (only 1 if millis disabled) - either millis is currently running from the timer specified in the tools submenu, or it is paused. But there is no way to tell which without gCMT
available from 1.6.9 onwards
- If the value is 0, that pin has no timer (note that you must take care to use dPTTN instead of dPTT if you wish to consider the compile-time-unknown value of the portmux registers and the type A timers) if using dPTT[N], or millis is disabled if using
_gCMT
. a. When using_gCMT
, this will never return any value other than 0 (if millis is currently not running - but is enabled in the tools menus), the value chosen from the tools menu, ie,MILLIS_TIMER
, or 0x90-0x9F, indicating that the the millis timekeeping is currently based on the RTC, though it (mTC: may not be, DxC: is not) the default millis timer. This can happen when the not-yet-implemented sleepTime library is in use, and the current code is executing from a context where the part has just awoken from or is going to enter a power saving sleep mode while maintaining timekeeping. So processinggCMT
is much easier:
if (val == 0) {
// Handle millis not running
} else if (val == MILLIS_TIMER) {
// handle the normal case
} else if (val == 0x90) {
// handle the RTC used for millis
} else if (val == 0x98) {
// handle the RTC PIT used for millis
} else { //can't happen.
runInCirclesScreamAndShout();
}
- Make a copy of the value, bitwise and with 0xF8.
- If the copy is less than 0x20, -> it's TCAn a. Specifically, n(copyval >> 3) = (n/a, 1, 0, 2)
- If high bit of copy is set, there is no PWM on this pin, but there is a DAC (for dPTT[N]), or millis is using the RTC (for
_gCMT
). a. Currently there is no announced or released AVR with more than one DAC channel. b. For_gCMT
, DACOUT will never be returned, as can the generic TIMER_RTC and TIMER_PIT. In the future, there may be additioal timers supported above 0x80. - Otherwise, check bit 6 (0x40). If it is a 1, you have a type D timer on this pin. Bits 4 and 5 indicate which waveform output channel, and bits 0-2 indicate the mux option. Bit 3 is reserved for any future TCD1. Having both those pieces of information in the table improves performance.
- Finally, check bit 5 (0x20). If set, means a type B timer pin a. 3 LSBs contain the timer number. If bit 4 is set, it's the alternate pin. If a second or third is ever available, we'll use bit 3 just like TCA.
Timer Name | Value | Peripheral | Notes: |
---|---|---|---|
NOT_ON_TIMER | 0x00 | None | Means millis is paused or stopped but can be restarted. |
TIMERA0 | 0x10 | TCA0 |
|
TIMERA1 | 0x08 | TCA1 |
|
TIMERA2 | 0x18 | TCA2 |
Theoretical |
TIMERB0 | 0x20 | TCB0 |
|
TIMERB1 | 0x21 | TCB1 |
|
TIMERB2 | 0x22 | TCB2 |
|
TIMERB3 | 0x23 | TCB3 |
|
TIMERB4 | 0x24 | TCB4 |
|
TIMERD0 | 0x40 | TCD0 |
mTC only |
TIMERE0 | TBD | TCE0 |
TBD |
TIMERF0 | TBD | TCF0 |
TBD |
TIMERRTC | 0x90 | RTC |
Never the value of TIMER_MILLIS. only returned by _gCMT(). Indicates that the RTC is being used for timekeeping temporarily. |
TIMERRTC_INT | 0x91 | RTC, int. 32k |
mTC only |
TIMERRTC_XTAL | 0x92 | RTC, 32k Xtal |
mTC only |
TIMERRTC_EXT | 0x93 | RTC, ext clk |
mTC only |
TIMERPIT | 0x98 | RTC PIT |
Never given by TIMER_MILLIS only returned by _gCMT(), and only if temporarily using the PIT for timekeeping, presumably in power down sleep. |
NOT_A_TIMER | 0xFF | None | Means millis is stopped but could be restarted. |
All other unforeseen timer-like peripherals will be assigned to values above 0x9F
3, 6, 12, 24, and 48 MHz, with the new optimized micros code, running from a TCB, is known to skip these (and only these) 250 values when determining the least significant thousand micros. That is, if you repeatedly calculate micros % 1000
, none of these will show up until it has been running long enough for micros to overflow.
2, 7, 10, 13, 18, 23, 28, 31, 34, 39, 42, 45, 50, 53, 56, 61, 66, 71, 74, 77, 82, 87, 92, 95, 98, 103, 108, 113, 116, 119, 124, 127, 130,
135, 138, 141, 146, 151, 156, 159, 162, 167, 170, 173, 178, 181, 184, 189, 194, 199, 202, 205, 210, 213, 216, 221, 224, 227, 232, 237, 242, 245, 248, 253,
258, 263, 266, 269, 274, 279, 284, 287, 290, 295, 298, 301, 306, 309, 312, 317, 322, 327, 330, 333, 338, 341, 344, 349, 352, 355, 360, 365, 370, 373, 376,
381, 384, 387, 392, 395, 398, 403, 408, 413, 416, 419, 424, 429, 434, 437, 440, 445, 450, 455, 458, 461, 466, 469, 472, 477, 480, 483, 488, 493, 498, 501,
504, 509, 512, 515, 520, 523, 526, 531, 536, 541, 544, 547, 552, 555, 558, 563, 566, 569, 574, 579, 584, 587, 590, 595, 600, 605, 608, 611, 616, 621, 626,
629, 632, 637, 640, 643, 648, 651, 654, 659, 664, 669, 672, 675, 680, 685, 690, 693, 696, 701, 706, 711, 714, 717, 722, 725, 728, 733, 736, 739, 744, 749,
754, 757, 760, 765, 770, 775, 778, 781, 786, 791, 796, 799, 802, 807, 810, 813, 818, 821, 824, 829, 834, 839, 842, 845, 850, 853, 856, 861, 864, 867, 872,
877, 882, 885, 888, 893, 896, 899, 904, 907, 910, 915, 920, 925, 928, 931, 936, 941, 946, 949, 952, 957, 962, 967, 970, 973, 978, 981, 984, 989, 992, 995
Those 250 "missing" microseconds manifest as repeats. It is difficult to intuitively understand why these values are the values they are (at least, I don't); these were calculated using a spreadsheet to simulate the results for every timer count value:
0, 4, 8, 12, 16, 20, 24, 27, 32, 36, 40, 44, 48, 52, 57, 60, 64, 68, 72, 76, 80, 84, 88, 91, 96, 100, 104, 107, 111, 115, 120, 123, 128,
132, 136, 140, 144, 148, 152, 155, 160, 164, 168, 172, 176, 180, 185, 188, 192, 196, 200, 204, 208, 212, 217, 220, 225, 229, 233, 236, 240, 244, 249, 252,
256, 260, 264, 268, 272, 276, 280, 283, 288, 292, 296, 300, 304, 308, 313, 316, 320, 324, 328, 332, 336, 340, 345, 348, 353, 357, 361, 364, 368, 372, 377,
380, 385, 389, 393, 397, 401, 405, 409, 412, 417, 421, 425, 428, 432, 436, 441, 444, 448, 452, 456, 460, 464, 468, 473, 476, 481, 485, 489, 492, 496, 500,
505, 508, 513, 517, 521, 525, 529, 533, 537, 540, 545, 549, 553, 557, 561, 565, 570, 573, 577, 581, 585, 589, 593, 597, 601, 604, 609, 613, 617, 620, 624,
628, 633, 636, 641, 645, 649, 653, 657, 661, 665, 668, 673, 677, 681, 684, 688, 692, 697, 700, 704, 708, 712, 716, 720, 724, 729, 732, 737, 741, 745, 748,
752, 756, 761, 764, 768, 772, 776, 780, 784, 788, 792, 795, 800, 804, 808, 812, 816, 820, 825, 828, 832, 836, 840, 844, 848, 852, 857, 860, 865, 869, 873,
876, 880, 884, 889, 892, 897, 901, 905, 909, 913, 917, 921, 924, 929, 933, 937, 940, 944, 948, 953, 956, 960, 964, 968, 972, 976, 980, 985, 988, 993, 997,
Similar lists can be generated for any other clock speed where resolution is coarser than 1us and a TCB is used.
The number of values will, for ideal values, be (1000) * (1 - (1 / resolution))
or something very close to that. Non-ideal series of terms or particularly adverse clock speeds may have more than that, as well as more cases of consecutive times that get the same micros value. If the terms were chosen poorly, it is possible for a time t give micros M where M(t-1) > M(t). This "backwards time travel" is very dangerous and if violating the assumptions of the rest of the core and breaking EVERYTHING. The current version of the core is believed to be free of them for all supported clock speeds. Likewise, it is possible for there to be larger skips or discontinuities, where M(t) - M(t-1) > 2, in contrast to skips listed above (where M(t) - M(t-1) = 2 ), or the number of times that a value returned for a point in time 1 us in the future would be the same numeric value - (ie, M(t) = M(t-1))
Timer options which have resolution of 1us (internally, it is lower) may have repeats or skips if fewer than the optimal number of terms were used (as is the case with non optimized options) or where the clock speed is particularky hard to work with, like the 28 MHz one.
Appendix III: Quiz answer! Yes, you made it through all the intervening 400ish lines of text, just to be told
It's a trick question!
Yes, every single one of those, or something close enough for our team of expert(s) to deem them equivalent. These have all actually existed in some form or another, at one time or another - right here on Earth, the works of mankind. A teenager really did make a nuclear reactor at home (people had tracked radioactive dust all over the neighborhood by the time someone realized what was going on). Asbestos cloth was widely used to make flexible fireproof objects, including clothing. Wooden knickknacks are in fact made through a traditional artform in asian countries where the lacquer plant grows natively; the sap - rich in the same compounds as poison ivy (the same "active" ones, to be clear); they used it to make things like bowls and dishes, because it hardened into a durable plastic-like material long before plastics existed. Though it doesn't pose the persistent toxicity of some modern plastics, I wouldn't be the one to volunteer to make it... And indeed, some emperor's tomb, I believe in China, contained, or had contained at one point, a pond and possibly a fountain of mercury. And as most fans of danger already knew, around the turn of the century radioactive material (usually radium) was just one of the many poisons sold as medicine. They put it in all kinds of things, including water (which you were supposed to drink) soaps, and similar, so it's inconceivable that shampoo wasn't one of those. Obviously none of them any any benefit to speak of, and unlike older patent medicines, which were typically heroin, cocaine, and organochlorine drug
Brought to you by Hazmart - something for everyone on your "list"!