TIMDAC is a public-domain, cheap, precision DAC, comprising a hardware reference design and a firmware module.
- Reference hardware implementation, bipolar and up to 8 channels
- Reference hardware implementation, unipolar single-channel
- Theory of operation
- Why you should use it
- Why you shouldn't use it
- Supported platforms
- Implementation guide
- Future versions roadmap
Why you should use it
It boasts pretty good specs for precision control applications:
- 15 usable bits
- DNL ≤ ±0.7 LSB
- INL ≤ ±2.75 LSB
- Optional bipolar output
- Channel counts up to 254, practically limited by scan frequency
- Can natively output any voltage reachable in your supply rails
It uses an integrating sample-and-hold topology, where a microcontroller timer is used to create a precise pulse, an integrator converts it into a level, and a sample-and-hold amplifier captures and buffers the level. The scan rate is about 770 Hz, and the typical update latency is from 1.3 to 2.6 ms for a single channel, or worst-case, 1.3 ms times the number of channels if all channels are updating frequently.
At a build quantity of 10, BOM cost is around $3.00 + $0.67 n, where n is the number of output channels up to 8. This gets you a full 15 bits! Also, most parts are quite generic and can be easily substituted if hit by part shortages.
Here, have some plots:
Why you shouldn't use it
The design has some drawbacks, as does any DAC. Here are some reasons TIMDAC might not be right for you:
- Requires a microcontroller actively driving it (note that CPU utilization is typically about 0.9%)
- This microcontroller needs to be a 32-bit part that can run its timer at around 72 MHz
- Update rate is limited by the scan frequency, so if you need to update very fast or update several outputs simultaneously, you may find it limiting
The following microcontrollers are presently supported. TIMDAC is portable and has relatively basic requirements for its microcontroller as long as the timer is fast enough, so adding more ports is straightforward.
- WCH CH32V103
To implement TIMDAC, you will need a supported microcontroller with an available timer peripheral. It's also easy to port; if you plan to port it, the timer peripheral must run at around 72 MHz, count to 65535, support emitting a single pulse of specified width and then stopping, and be able to fire an interrupt when it stops.
First, look at the reference hardware implementation above. You will need to build this circuit on your board, and connect it to the microcontroller as shown.
Firmware (using Meson)
TIMDAC provides a Meson build file, so if you use Meson you can include it directly as a subproject. The following options should be defined:
timdac_hw— set to the name of the hardware port. Options are
none. If you use
none, TIMDAC can link against port functions you provide externally.
bsp— set to the name of a subproject containing your board support pack. This is where the config header,
timdac_config.h, should live. This is a nice, clean way to pull hardware definitions from the root project into subprojects in Meson. If you're not using this approach already, you can configure a very small board support pack like this:
$ ls subprojects/bsp-foo meson.build timdac_config.h $ cat subprojects/bsp-foo/meson.build project('bsp-timdac', 'c') bsp_dep = declare_dependency(include_directories: include_directories('.'))
If you want to call the dependency something other than
bsp_dep, you can set
the same-named option (
bsp_dep) to its actual name.
Firmware (not using Meson)
- Add the source files under
srcto your build (only add one of the
timdac_hwfiles, matching your target platform).
- Add the include directory
incto your build.
- When building TIMDAC itself, ensure that wherever
timdac_config.hlives is in TIMDAC's include path.
TIMDAC needs to be given a build configuration with hardware and runtime
A template configuration file can be found in
All of the configuration options for TIMDAC itself are documented in
timdac.h, and the configuration options for each hardware
driver are documented in that driver's source file (e.g.
Some of these options are labeled
[REFHW] in the documentation. These options
pertain to the reference hardware implementation, and as long as you are
following the hardware suggestions, you should not change them. They can have
surprising and difficult-to-measure effects on the DAC performance. To get the
correct options for a reference design, you should
#include that reference
design's sub-config in your config:
Once integrated into your codebase, it can be initialized and started very simply, and then you can set output values:
// First, ensure all necessary GPIO and timer clocks are running, any pin // remapping has been done, and interrupts are enabled: NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE ); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); timdac_init(); timdac_start(); timdac_set(channel, value);
When you set a channel, it is jumped to the start of the scan queue so that it will receive an output as fast as possible. If another channel is already in the pending queue-jump slot when you write a channel, this will not be done (there is only one queue-jump slot). You may wait for an available slot:
Future versions roadmap
1.0.1 or 1.1: Improve the scan algorithm to support many queue-jumpers.
1.1: Minor tweaks to the reference design BOM for cheaper cost and slightly improved performance. A comparator is replaced by a cheaper one, a diode is replaced by a capacitor, and several capacitors are replaced with lower dielectric absorption units.
2.0: Add 16-bit operation! This will require an additional $1.21 part — a digital potentiometer. The 15-bit version of TIMDAC uses a "hidden" 16th bit to perform software scaling of output values to tune itself as timing parameters vary; by moving this to an external potentiometer the full 16-bit range of the timer can be realized. This will be a substantial upgrade that is going to require careful redesign and testing on my end to maintain usably low DNL and INL.
Want to contribute something? Bugs? Ports? Improvements? Sure! Hopefully gitea/forgejo gets federation someday, but until then, I can be emailed: (this username) @ (this domain).