lots of updates
parent
e3217ffa69
commit
e38da22af5
File diff suppressed because it is too large
Load Diff
Binary file not shown.
105
inc/timdac.h
105
inc/timdac.h
|
@ -6,10 +6,98 @@
|
|||
// ============================================================================
|
||||
// TIMDAC
|
||||
// ----------------------------------------------------------------------------
|
||||
// TIMDAC is a firmware-defined precision 15-bit DAC using external hardware.
|
||||
// For the full hardware design, see the documentation in this repository.
|
||||
// In summary, TIMDAC requires:
|
||||
//
|
||||
// NOTE - estimated CPU utilization - 0.9% at 72 MHz, 12.25us total
|
||||
// (5.48us discharge, 2.29us pulse, 2.14us stabilize, 2.29us transfer) across a
|
||||
// 1.29ms cycle
|
||||
// - A 32-bit (ideally) platform running at somewhere near 72 MHz
|
||||
//
|
||||
// (If you can run the timers at that speed, that's fine - 144 MHz with a
|
||||
// divide-by-two prescale works. Also, necessary frequency is only about
|
||||
// plus or minus 20% as long as you scale the slope resistor R1 in the
|
||||
// reference design accordingly.)
|
||||
//
|
||||
// - A 16-bit timer that can emit single pulses on demand and interrupt when
|
||||
// it's done.
|
||||
//
|
||||
// - 5 GPIOs, plus log2(number of channels)
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuring TIMDAC
|
||||
// ----------------------------------------------------------------------------
|
||||
// TIMDAC requires a configuration file called timdac_config.h to be accessible
|
||||
// on the include path. The core driver only has a few options, but the
|
||||
// individual hardware drivers will have many more -- see the documentation in
|
||||
// the driver files for those options. For the TIMDAC core:
|
||||
//
|
||||
// #define TIMDAC_TIMERFREQ_HZ 72000000
|
||||
// Defines the counting frequency of the timer in Hz. Remember that if you
|
||||
// are running at a frequency other than 72 MHz, one of the resistors in the
|
||||
// reference design needs changed -- see the notes on the schematic.
|
||||
//
|
||||
// #define TIMDAC_N_CHANNELS 4
|
||||
// Number of output channels. May be up to 254.
|
||||
//
|
||||
// #define TIMDAC_DIAGNOSTIC 1
|
||||
// Define to nonzero to make the diagnostic functions (currently just
|
||||
// timdac_ll_diagnostic) work. If zero or undefined, the diagnostic
|
||||
// functionality is not built in. These diagnostics are mostly used to dial
|
||||
// in parameters for a new reference design, so you shouldn't need them.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuring TIMDAC (advanced)
|
||||
// ----------------------------------------------------------------------------
|
||||
// The following parameters pertain to an individual hardware design. Unless
|
||||
// you're hacking on TIMDAC itself, you should just use a provided set of
|
||||
// parameters.
|
||||
//
|
||||
// #define TIMDAC_TUNE_WEIGHT
|
||||
// Numerator of the tuning weight. This weight determines how quickly the DAC
|
||||
// tuning is pulled towards the result of each tuning operation; adjustments
|
||||
// are made by a factor of (TIMDAC_TUNE_WEIGHT / 65536). This parameter will
|
||||
// determine how much tuning noise bleeds through into the output; the RMS
|
||||
// noise can be measured using the TIMDAC_DIAGNOSTIC option, but should also
|
||||
// be validated using a precise multimeter.
|
||||
//
|
||||
// #define TIMDAC_TUNE_NTHRESH
|
||||
// Tuning noise threshold. Above this amount of error in timer counts, the
|
||||
// tuning is assumed to have drifted suddenly, and is immediately clobbered.
|
||||
// Because the tuning algorithm can be noisy, this must be high enough to
|
||||
// avoid spurious clobbers which can result in sudden shifts in output value.
|
||||
// The clobber count can be measured using the TIMDAC_DIAGNOSTIC option.
|
||||
//
|
||||
// #define TIMDAC_TUNE_INTERVAL
|
||||
// How many channels may be scanned before tuning. Beware that changing this
|
||||
// will have ramifications for TIMDAC_TUNE_WEIGHT and TIMDAC_TUNE_NTHRESH.
|
||||
//
|
||||
// #define TIMDAC_DISCHARGE_TIME
|
||||
// Integrator discharge time. It is essential that all charge on the
|
||||
// integrator be fully discharged before the next channel is emitted. This
|
||||
// parameter can be validated by setting all channels to zero and measuring
|
||||
// the output, then setting all channels except one to full-scale and
|
||||
// ensuring that the channel that remains set to zero has not shifted.
|
||||
//
|
||||
// #define TIMDAC_STABILIZATION_TIME
|
||||
// Stabilization time. This is the time after the pulse finishes before we
|
||||
// transfer the final ramp value to the holding capacitor. This time allows
|
||||
// the integrator to settle. If too short, transient edge behavior could
|
||||
// create offsets or ripple in the output. If too long, the integrator will
|
||||
// integrate leakage current and create an offset error.
|
||||
//
|
||||
// #define TIMDAC_FAST_TRANSFER_TIME
|
||||
// Fast transfer time in timer counts. This is the time for which the
|
||||
// integrated ramp is sent to the output when doing fast scanning (no output
|
||||
// has been changed significantly). It only needs to be fast enough to "top
|
||||
// up" the output capacitor to account for any leakage that may have occurred
|
||||
// during a cycle.
|
||||
//
|
||||
// #define TIMDAC_SLOW_TRANSFER_TIME
|
||||
// Slow transfer time in timer counts. This is the time for which the
|
||||
// integrated ramp is sent to the output when performing an output value
|
||||
// change.
|
||||
//
|
||||
// #define TIMDAC_SLOW_DELTA
|
||||
// How much can an output change without triggering a slow transfer cycle.
|
||||
// ============================================================================
|
||||
|
||||
#include <stdbool.h>
|
||||
|
@ -18,12 +106,12 @@
|
|||
#include <stdatomic.h>
|
||||
#include "timdac_ll.h"
|
||||
|
||||
// Initialize the DAC and DAC scanner. You must have filled in the "USER MUST
|
||||
// INITIALIZE" fields in the struct.
|
||||
// Initialize TIMDAC but do not start scanning yet.
|
||||
void timdac_init(void);
|
||||
|
||||
// Start the scanner, if using interrupts. If you are not using interrupts,
|
||||
// simply poll in a loop when you want to run - do not call this.
|
||||
// Start TIMDAC. It will immediately start with a tuning cycle, then begin
|
||||
// scanning channels. If you would like the channels to start with an initial
|
||||
// value other than zero, you may call timdac_set() prior to timdac_start().
|
||||
void timdac_start(void);
|
||||
|
||||
// Set a new value for an output channel. If no other channel has a pending
|
||||
|
@ -41,7 +129,4 @@ bool timdac_set(uint8_t chan, int32_t value);
|
|||
// can poll for this flag to clear before calling timdac_set().
|
||||
bool timdac_pending(void);
|
||||
|
||||
// Poll the scanner. Should be called periodically.
|
||||
void timdac_poll(void);
|
||||
|
||||
#endif // !defined(TIMDAC_H)
|
||||
|
|
|
@ -8,17 +8,6 @@
|
|||
// design. These parameters should not be changed without good reason, as they
|
||||
// can affect the DAC performance in surprising ways. If you do need to change
|
||||
// them, you should #undef and re-#define them after including this file.
|
||||
//
|
||||
// The macros in this file depend on a definition in timdac_config.h:
|
||||
//
|
||||
// #define TIMDAC_TIMERFREQ_HZ 72000000
|
||||
// This definition should be set to the base operating frequency of your
|
||||
// system's timer in Hz. It is strongly recommended not to deviate far
|
||||
// from the design frequency of 72 MHz.
|
||||
//
|
||||
// For the curious, there is a good amount of documentation in this file
|
||||
// explaining how the parameters affect the output, but for a basic
|
||||
// implementation you are not required to understand this.
|
||||
// ============================================================================
|
||||
|
||||
#ifndef TIMDAC_CONFIG_REF1P0_H
|
||||
|
@ -48,59 +37,25 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
// TIMDAC control parameters
|
||||
|
||||
// Numerator of the tuning weight. This weight determines how quickly the DAC
|
||||
// tuning is pulled towards the result of each tuning operation; adjustments
|
||||
// are made by a factor of (TIMDAC_TUNE_WEIGHT / 65536). This parameter will
|
||||
// determine how much tuning noise bleeds through into the output; the RMS
|
||||
// noise can be measured using the TIMDAC_DIAGNOSTIC option, but should also
|
||||
// be validated using a precise multimeter.
|
||||
#define TIMDAC_TUNE_WEIGHT 64
|
||||
|
||||
// Tuning noise threshold. Above this amount of error in timer counts, the
|
||||
// tuning is assumed to have drifted suddenly, and is immediately clobbered.
|
||||
// Because the tuning algorithm can be noisy, this must be high enough to avoid
|
||||
// spurious clobbers which can result in sudden shifts in output value. The
|
||||
// clobber count can be measured using the TIMDAC_DIAGNOSTIC option.
|
||||
#define TIMDAC_TUNE_NTHRESH 2048
|
||||
|
||||
// How many channels may be scanned before tuning. Beware that changing this
|
||||
// will have ramifications for TIMDAC_TUNE_WEIGHT and TIMDAC_TUNE_NTHRESH.
|
||||
#define TIMDAC_TUNE_INTERVAL 64
|
||||
|
||||
// Integrator discharge time. It is essential that all charge on the integrator
|
||||
// be fully discharged before the next channel is emitted. This parameter can
|
||||
// be validated by setting all channels to zero and measuring the output, then
|
||||
// setting all channels except one to full-scale and ensuring that the channel
|
||||
// that remains set to zero has not shifted.
|
||||
//
|
||||
// REV 1.1 TODO: Rev 1.1 is changing the recommended integrating cap type;
|
||||
// recheck this afterward. It can probably go down.
|
||||
#define TIMDAC_DISCHARGE_TIME TIMDAC_NRC(12, TIMDAC_DISCH_RES, TIMDAC_INT_CAP)
|
||||
|
||||
// Stabilization time. This is the time after the pulse finishes before we
|
||||
// transfer the final ramp value to the holding capacitor. This time allows the
|
||||
// integrator to settle. If too short, transient edge behavior could create
|
||||
// offsets or ripple in the output. If too long, the integrator will integrate
|
||||
// leakage current and create an offset error.
|
||||
#define TIMDAC_STABILIZATION_TIME TIMDAC_NRC(50e-6, 1, 1)
|
||||
|
||||
// Fast transfer time in timer counts. This is the time for which the
|
||||
// integrated ramp is sent to the output when doing fast scanning (no output
|
||||
// has been changed significantly). It only needs to be fast enough to "top up"
|
||||
// the output capacitor to account for any leakage that may have occurred
|
||||
// during a cycle.
|
||||
#define TIMDAC_FAST_TRANSFER_TIME TIMDAC_NRC(2, TIMDAC_OUT_RES, TIMDAC_OUT_CAP)
|
||||
|
||||
// Slow transfer time in timer counts. This is the time for which the
|
||||
// integrated ramp is sent to the output when performing an output value
|
||||
// change.
|
||||
//
|
||||
// REV 1.1 TODO: This is currently set to the maximum because dielectric
|
||||
// absorption in the rev 1.0 design is extending the time necessary to set the
|
||||
// output voltage. After changing to lower absorption capacitors, recheck.
|
||||
#define TIMDAC_SLOW_TRANSFER_TIME 65535
|
||||
|
||||
// How much can an output change without triggering a slow transfer cycle.
|
||||
#define TIMDAC_SLOW_DELTA 128
|
||||
|
||||
#endif // !defined(TIMDAC_CONFIG_REF1P0_H)
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
#include <ch32v10x_tim.h>
|
||||
#include <ch32v10x_gpio.h>
|
||||
|
||||
#define TIMDAC_MAX_SELCHAN 5
|
||||
|
||||
// Diagnostic info struct. If TIMDAC_DIAGNOSTIC is defined nonzero, this is the
|
||||
// struct that timdac_ll_diagnostic() will fill.
|
||||
typedef struct timdac_lldiag_s {
|
||||
// Total number of calibration cycles run (will overflow)
|
||||
uint16_t cycles;
|
||||
|
@ -38,41 +38,35 @@ typedef struct timdac_lldiag_s {
|
|||
uint64_t noise;
|
||||
} timdac_lldiag_t;
|
||||
|
||||
// Initialize the DAC. The mux GPIOs will be initialized as well, but you are
|
||||
// responsible for configuring the timer channel IOs as well as enabling the
|
||||
// clock to the GPIO and timer.
|
||||
// Initialize the DAC.
|
||||
void timdac_ll_init(void);
|
||||
|
||||
// Start to tune the DAC. After calling, run timdac_poll() until it
|
||||
// reports idle.
|
||||
// Initiate a tuning cycle. Once the tuning cycle has been started, it will
|
||||
// proceed through a sequence as stepped through by timdac_ll_poll(). Note that
|
||||
// a full tuning requires multiple cycles -- the initial tuning is not complete
|
||||
// until timdac_ll_tuned() returns true.
|
||||
//
|
||||
// Tuning requires external hardware support. A comparator must sense
|
||||
// when the ramp crosses Vref and signal back to us on the input capture line.
|
||||
// Note that the CH32V10x has no internal comparator; an external one will be
|
||||
// needed. Do not use hysteresis as this will make the level less exact.
|
||||
// Undefined (analog) behavior will be produced if this is called when
|
||||
// timdac_ll_poll() is not yet returning false. Don't do that.
|
||||
//
|
||||
// Returns whether the timer is now busy.
|
||||
// Returns whether the timer is now busy. If not, this "cycle" was
|
||||
// purely computational and has completed immediately.
|
||||
bool timdac_ll_tune(void);
|
||||
|
||||
// Start to emit a voltage on a channel. The integrating ramp will be started.
|
||||
// After calling, run timdac_poll() until it reports idle.
|
||||
// Start to emit a voltage on a channel. Once the cycle has been started, it
|
||||
// will proceed through a sequence as stepped through by timdac_ll_poll().
|
||||
//
|
||||
// If timdac_poll() was not already reporting idle, the previous cycle will be
|
||||
// aborted.
|
||||
// Undefined (analog) behavior will be produced if this is called when
|
||||
// timdac_ll_poll() is not yet returning false. Don't do that.
|
||||
//
|
||||
// td: timdac instance
|
||||
// chan: channel number, sent to the channel select GPIOs. Remember that 0
|
||||
// is the discharge channel - you should never use it directly. Behavior
|
||||
// is undefined if you do.
|
||||
// value: DAC code
|
||||
// slow: if true, dwell longer during transfer for large updates
|
||||
// chan: channel number to select; will be passed to the hardware driver
|
||||
// value: DAC code to emit
|
||||
// slow: whether to perform a slow transfer at the end of the cycle
|
||||
void timdac_ll_emit(uint8_t chan, int16_t value, bool slow);
|
||||
|
||||
// Poll the timer. This must be called repeatedly while a tuning or output
|
||||
// cycle is in progress - it does not need to be called with perfect regularity,
|
||||
// but waiting too long may result in some signal droop and loss of accuracy.
|
||||
//
|
||||
// return: true if busy, false if idle
|
||||
// Run one step of the low-level DAC process. Should be called from
|
||||
// timdac_poll() every time the timer stops. Returns whether the timer is now
|
||||
// busy - if not, the last cycle finished and a new one may be started.
|
||||
bool timdac_ll_poll(void);
|
||||
|
||||
// Run a single idle cycle. This can be used to kickstart an interrupt driven
|
||||
|
@ -86,6 +80,10 @@ bool timdac_ll_tuned(void);
|
|||
// Get low-level diagnostics. TIMDAC_DIAGNOSTIC must be defined in
|
||||
// timdac_config.h for this to work; otherwise the struct will be filled with
|
||||
// zeros.
|
||||
//
|
||||
// This is the only low-level function that may be called by normal users
|
||||
// rather than the high-level driver, but it is probably not of much interest
|
||||
// to them.
|
||||
void timdac_ll_diagnostic(timdac_lldiag_t * diag);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -103,5 +101,8 @@ void timdac_hw_gpio_discharge(bool en);
|
|||
void timdac_hw_gpio_polarity(bool pos);
|
||||
void timdac_hw_gpio_select_channel(uint8_t chan);
|
||||
bool timdac_hw_gpio_tune_is_high(void);
|
||||
// This is a high-level function, but declared here as it is never meant to be
|
||||
// called by the user. Instead, the hardware driver will call it from an ISR.
|
||||
void timdac_poll(void);
|
||||
|
||||
#endif // !defined(TIMDAC_LL_H)
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
#include <signal.h> // TODO use atomics instead
|
||||
|
||||
_Static_assert(TIMDAC_N_CHANNELS <= 254, "TIMDAC_N_CHANNELS: too many channels");
|
||||
|
||||
// Reserved channel numbers to indicate a pending queue-jumper. First, a queue
|
||||
// jumper is written into ->pend. The ISR swaps that with PEND_SLOT. PEND_SLOT
|
||||
|
@ -30,8 +31,8 @@
|
|||
|
||||
static atomic_uint _channels[TIMDAC_N_CHANNELS];
|
||||
static atomic_uint _pend;
|
||||
static sig_atomic_t volatile _chan;
|
||||
static sig_atomic_t volatile _count;
|
||||
static atomic_uint _chan;
|
||||
static atomic_uint _count;
|
||||
|
||||
void timdac_init(void)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue