Dump prerelease code, needs cleaned up
commit
d1e7ce0776
@ -0,0 +1,26 @@
|
||||
# Ignore list for: c
|
||||
*.d
|
||||
*.o
|
||||
*.obj
|
||||
*.elf
|
||||
*.map
|
||||
*.gch
|
||||
*.pch
|
||||
*.lib
|
||||
*.a
|
||||
*.so
|
||||
*.out
|
||||
*.su
|
||||
*.bin
|
||||
*.eep
|
||||
*.hex
|
||||
*.lss
|
||||
*.dSYM
|
||||
|
||||
# Ignore list for: kicad
|
||||
*.bck
|
||||
*.kicad_pcb-bak
|
||||
*.sch-bak
|
||||
*-cache
|
||||
*-backups
|
||||
*.kicad_prl
|
@ -0,0 +1,29 @@
|
||||
project('timdac', 'c', default_options: ['default_library=static'])
|
||||
|
||||
o_hw = get_option('timdac_hw')
|
||||
|
||||
sources = ['src/timdac.c', 'src/timdac_ll.c']
|
||||
dependencies = []
|
||||
defs = []
|
||||
|
||||
if o_hw == 'ch32v103'
|
||||
ch32v103_proj = subproject('ch32v103-meson')
|
||||
ch32v103_dep = ch32v103_proj.get_variable('ch32v103_dep')
|
||||
dependencies += ch32v103_dep
|
||||
sources += ['src/timdac_hw_ch32v103.c']
|
||||
endif
|
||||
|
||||
add_project_arguments(defs, language: ['c', 'cpp'])
|
||||
|
||||
timdac_lib = library(
|
||||
'timdac',
|
||||
sources,
|
||||
dependencies: dependencies,
|
||||
include_directories: ['src'],
|
||||
)
|
||||
|
||||
libtimdac_dep = declare_dependency(
|
||||
compile_args: defs,
|
||||
include_directories: include_directories('inc'),
|
||||
link_with: timdac_lib,
|
||||
)
|
@ -0,0 +1,132 @@
|
||||
// intellectual property is bullshit bgdc
|
||||
|
||||
#include "timdac.h"
|
||||
#include "timdac_config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
// 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
|
||||
// is like NO_PEND, but timdac_set will not pend another channel while it's set
|
||||
// - ensuring we do not flood the sequence with queue jumpers. Once PEND_SLOT
|
||||
// comes back around to the ISR, it is replaced with NO_PEND.
|
||||
#define NO_PEND UINT8_MAX
|
||||
#define PEND_SLOT (NO_PEND - 1)
|
||||
|
||||
// Channels are stored as 32-bit integers, packing a 16-bit signed int into the
|
||||
// low half and flags into the high half.
|
||||
#define CHAN_FLAG_MASK 0xFFFF0000u
|
||||
#define CHAN_VALUE_MASK 0x0000FFFFu
|
||||
#define ENCODE_CHAN(value, flags) \
|
||||
((uint32_t)(uint16_t)(value) | ((flags) & CHAN_FLAG_MASK))
|
||||
#define CHAN_VALUE(chan) ((int16_t)(uint16_t)((chan) & CHAN_VALUE_MASK))
|
||||
|
||||
#define CHAN_FLAG_SLOW 0x00010000u
|
||||
|
||||
void timdac_init(timdac_t * tds)
|
||||
{
|
||||
tds->chan = 0;
|
||||
tds->pend = NO_PEND;
|
||||
tds->count = 0;
|
||||
|
||||
for (size_t i = 0; i < tds->n_channels; i++)
|
||||
tds->channels[i] = CHAN_FLAG_SLOW;
|
||||
|
||||
timdac_ll_init(tds->td);
|
||||
}
|
||||
|
||||
void timdac_start(timdac_t * tds)
|
||||
{
|
||||
timdac_ll_idle(tds->td);
|
||||
}
|
||||
|
||||
bool timdac_set(timdac_t * tds, uint8_t chan, int32_t value)
|
||||
{
|
||||
if (chan >= tds->n_channels)
|
||||
return false;
|
||||
|
||||
int32_t prev = CHAN_VALUE(atomic_load(&tds->channels[chan]));
|
||||
bool slow = prev & CHAN_FLAG_SLOW;
|
||||
|
||||
if ((value < 0 && prev > 0) || (value > 0 && prev < 0))
|
||||
slow = true;
|
||||
else
|
||||
slow |= abs(value - prev) >= TIMDAC_SLOW_DELTA;
|
||||
|
||||
bool stable = atomic_compare_exchange_strong(
|
||||
&tds->channels[chan],
|
||||
&prev,
|
||||
ENCODE_CHAN(value, slow ? CHAN_FLAG_SLOW : 0)
|
||||
);
|
||||
|
||||
if (!stable)
|
||||
atomic_store(&tds->channels[chan],
|
||||
ENCODE_CHAN(value, CHAN_FLAG_SLOW));
|
||||
|
||||
unsigned no_pend = NO_PEND;
|
||||
atomic_compare_exchange_strong(
|
||||
&tds->pend,
|
||||
&no_pend,
|
||||
chan
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timdac_pending(timdac_t * tds)
|
||||
{
|
||||
return atomic_load(&tds->pend) < PEND_SLOT;
|
||||
}
|
||||
|
||||
void timdac_poll(timdac_t * tds)
|
||||
{
|
||||
if (timdac_ll_poll(tds->td))
|
||||
return;
|
||||
|
||||
_Static_assert(
|
||||
TIMDAC_TUNE_INTERVAL <= UINT8_MAX,
|
||||
"Calibration interval too high!");
|
||||
|
||||
if (tds->count >= TIMDAC_TUNE_INTERVAL || !tds->td->tune)
|
||||
{
|
||||
// Reached the end, time to calibrate
|
||||
tds->count = 0;
|
||||
if (timdac_ll_tune(tds->td))
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t chan;
|
||||
unsigned pend = atomic_exchange(&tds->pend, PEND_SLOT);
|
||||
|
||||
if (pend < PEND_SLOT)
|
||||
{
|
||||
chan = tds->pend;
|
||||
}
|
||||
else
|
||||
{
|
||||
chan = tds->chan;
|
||||
uint8_t next_chan = chan + 1;
|
||||
if (next_chan >= tds->n_channels)
|
||||
next_chan = 0;
|
||||
tds->chan = next_chan;
|
||||
|
||||
atomic_compare_exchange_strong(&tds->pend, &pend, NO_PEND);
|
||||
}
|
||||
|
||||
uint32_t chancode = atomic_fetch_and(
|
||||
&tds->channels[chan],
|
||||
~CHAN_FLAG_SLOW
|
||||
);
|
||||
|
||||
tds->count++;
|
||||
timdac_ll_emit(
|
||||
tds->td,
|
||||
chan,
|
||||
CHAN_VALUE(chancode),
|
||||
chancode & CHAN_FLAG_SLOW
|
||||
);
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
// intellectual property is bullshit bgdc
|
||||
|
||||
#ifndef TIMDAC_H
|
||||
#define TIMDAC_H
|
||||
|
||||
// ============================================================================
|
||||
// TIMDAC
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// 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
|
||||
// ============================================================================
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdatomic.h>
|
||||
#include <signal.h>
|
||||
#include "timdac_ll.h"
|
||||
|
||||
#define TIMDAC_SCANNER_MAX_CHANNELS 32
|
||||
|
||||
// timdac_scanner provides a higher level interface to timdac, allowing the
|
||||
// user to simply write output values as they need changing. When in use, it
|
||||
// takes full ownership of the timdac; you should never call any timdac_
|
||||
// functions including timdac_init().
|
||||
|
||||
typedef struct timdac_s {
|
||||
// #### BEGIN - USER MUST INITIALIZE ####
|
||||
|
||||
// Pointer to the timdac low-level instance in use
|
||||
timdac_ll_t * td;
|
||||
|
||||
// Array of output channel values
|
||||
atomic_uint * channels;
|
||||
|
||||
// Number of channels, must be no more than TIMDAC_SCANNER_MAX_CHANNELS
|
||||
uint8_t n_channels;
|
||||
|
||||
// #### END - USER MUST INITIALIZE ####
|
||||
|
||||
sig_atomic_t volatile chan; // Current channel number
|
||||
sig_atomic_t volatile count; // How many channels have been emitted since tuning
|
||||
atomic_uint pend; // A pending immediate-update channel
|
||||
} timdac_t;
|
||||
|
||||
// Initialize the DAC and DAC scanner. You must have filled in the "USER MUST
|
||||
// INITIALIZE" fields in the struct.
|
||||
void timdac_init(timdac_t * tds);
|
||||
|
||||
// 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.
|
||||
void timdac_start(timdac_t * tds);
|
||||
|
||||
// Set a new value for an output channel. If no other channel has a pending
|
||||
// immediate update, this channel will also be added to the pend slot so that
|
||||
// it can be updated as soon as the DAC is ready without waiting for a full
|
||||
// cycle.
|
||||
//
|
||||
// Returns true on success. Only fails on an invalid channel number.
|
||||
bool timdac_set(timdac_t * tds, uint8_t chan, int32_t value);
|
||||
|
||||
// Return whether an update is currently pending. Only one channel can hold the
|
||||
// pending slot at a time, so if you post an update while another channel is
|
||||
// pending, the newer update will not be written to the output until it is
|
||||
// next hit in the normal scanning cycle. To ensure the fastest update, you
|
||||
// can poll for this flag to clear before calling timdac_set().
|
||||
bool timdac_pending(timdac_t * tds);
|
||||
|
||||
// Poll the scanner. Should be called periodically.
|
||||
void timdac_poll(timdac_t * tds);
|
||||
|
||||
#endif // !defined(TIMDAC_H)
|
@ -0,0 +1,66 @@
|
||||
// intellectual property is bullshit bgdc
|
||||
|
||||
#ifndef TIMDAC_CONFIG_H
|
||||
#define TIMDAC_CONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define TIMDAC_N_CHANNELS 4
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// HARDWARE DRIVER CONFIGURATION - These may differ between hardware drivers,
|
||||
// check the documentation in yours.
|
||||
|
||||
// CH32V103 timer, must be a general-purpose timer
|
||||
#define TIMDAC_HW_TIMER TIM3
|
||||
|
||||
// CH32V103 output compare channel
|
||||
#define TIMDAC_HW_TIMER_CHANNEL 1
|
||||
|
||||
// CH32V103 timer interrupt. Optional, if you don't define this you must poll.
|
||||
#define TIMDAC_HW_TIMER_IRQn TIM3_IRQn
|
||||
// CH32V103 timer interrupt preemption priority
|
||||
#define TIMDAC_HW_TIMER_IRQPRIO 3
|
||||
// CH32V103 timer interrupt subpriority
|
||||
#define TIMDAC_HW_TIMER_IRQSUBPRIO 3
|
||||
|
||||
// CH32V103 GPIO and pin: timer output compare
|
||||
// You are responsible for making sure this and TIMDAC_HW_TIMER_CHANNEL match
|
||||
// up, and also for applying any GPIO_PinRemapConfig() and clock startup as
|
||||
// necessary prior to initializing timdac.
|
||||
#define TIMDAC_HW_GPIO_TIMER GPIOC, GPIO_Pin_6
|
||||
|
||||
// CH32V103 GPIO and pin: mux inhibit
|
||||
#define TIMDAC_HW_GPIO_MUXINHIBIT GPIOC, GPIO_Pin_3
|
||||
|
||||
// CH32V103 GPIO and pin: integrator discharge
|
||||
#define TIMDAC_HW_GPIO_DISCHARGE GPIOC, GPIO_Pin_5
|
||||
|
||||
// CH32V103 GPIO and pin: polarity
|
||||
// OPTIONAL, do not define if polarity switch is not implemented
|
||||
#define TIMDAC_HW_GPIO_POLARITY GPIOC, GPIO_Pin_4
|
||||
|
||||
// CH32V103 GPIO and pin: tuning comparator
|
||||
#define TIMDAC_HW_GPIO_TUNE GPIOC, GPIO_Pin_7
|
||||
|
||||
// CH32V103 tuning comparator polarity, true = noninverting
|
||||
#define TIMDAC_HW_GPIO_TUNE_POL false
|
||||
|
||||
// CH32V103 GPIO: channel select. All channel select pins must be on the same
|
||||
// port
|
||||
#define TIMDAC_HW_GPIO_CHAN_PORT GPIOC
|
||||
|
||||
// CH32V103 pins: channel select. Start from the LSB, omit or zero unused
|
||||
// bits. Up to 8 bits.
|
||||
#define TIMDAC_HW_GPIO_CHAN_PINS GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2
|
||||
|
||||
#define TIMDAC_TIMERFREQ_HZ 72000000
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TIMDAC CONFIGURATION
|
||||
|
||||
#include "timdac_config_ref1p0.h"
|
||||
|
||||
#endif // !defined(TIMDAC_CONFIG_H)
|
@ -0,0 +1,106 @@
|
||||
// intellectual property is bullshit bgdc
|
||||
|
||||
// ============================================================================
|
||||
// TIMDAC REFERENCE DESIGN CONFIGURATION - REFERENCE DESIGN 1.0
|
||||
//
|
||||
// This file is designed to be #included from your main timdac_config.h, and
|
||||
// provides all the configuration specified for the timdac hardware reference
|
||||
// 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
|
||||
#define TIMDAC_CONFIG_REF1P0_H
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helper macros
|
||||
|
||||
// Convert n*R*C into a number of timer counts
|
||||
#ifdef __GNUC__
|
||||
#define TIMDAC_NRC(n, r, c) ({ \
|
||||
_Static_assert((n) * (r) * (c) * TIMDAC_TIMERFREQ_HZ <= 65535, \
|
||||
"Time constant too high for 16-bit timer"); \
|
||||
(unsigned)(((n) * (r) * (c) * TIMDAC_TIMERFREQ_HZ)) })
|
||||
#else
|
||||
#define TIMDAC_NRC(n, r, c) ((unsigned)((n) * (r) * (c) * TIMDAC_TIMERFREQ_HZ))
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Circuit properties
|
||||
|
||||
#define TIMDAC_INT_CAP 10e-9
|
||||
#define TIMDAC_OUT_CAP 100e-9
|
||||
#define TIMDAC_DISCH_RES 1e+3
|
||||
#define TIMDAC_OUT_RES 1e+3
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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)
|
@ -0,0 +1,282 @@
|
||||
// intellectual property is bullshit bgdc
|
||||
|
||||
// ============================================================================
|
||||
// TIMDAC HARDWARE DRIVER -- WCH CH32V103
|
||||
// ----------------------------------------------------------------------------
|
||||
// This driver implements TIMDAC for the WCH CH32V103 microcontroller. It
|
||||
// requires the following:
|
||||
//
|
||||
// - Core clock = 72 MHz
|
||||
// - One general-purpose timer module
|
||||
// - The standard TIMDAC GPIOs:
|
||||
// - 1 output compare pin
|
||||
// - 1 mux inhibit pin
|
||||
// - 1 discharge pin
|
||||
// - 0 or 1 polarity pins
|
||||
// - From 0 to 8 channel select pins
|
||||
//
|
||||
// To use it, include this source file in your build, and define the following
|
||||
// in timdac_config.h:
|
||||
//
|
||||
// #define TIMDAC_HW_TIMER
|
||||
// Name of the timer module (e.g. TIM3)
|
||||
//
|
||||
// #define TIMDAC_HW_TIMER_CHANNEL
|
||||
// Number of the output compare channel, from 1 to 4
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_TIMER
|
||||
// GPIO and pin corresponding to the output compare channel
|
||||
// (e.g. GPIOC, GPIO_Pin_6)
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_MUXINHIBIT
|
||||
// GPIO and pin corresponding to the mux inhibit signal
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_DISCHARGE
|
||||
// GPIO and pin corresponding to the discharge signal
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_POLARITY
|
||||
// OPTIONAL, GPIO and pin corresponding to the polarity signal, where High
|
||||
// means Negative. If not using polarity, do not define this.
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_TUNE
|
||||
// GPIO and pin corresponding to the tuning comparator output signal.
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_TUNE_POL
|
||||
// Polarity of the tuning comparator; false = inverting with respect to the
|
||||
// positive-going ramp. Note that the reference design uses inverting
|
||||
// polarity, so this should generally be false.
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_CHAN_PORT
|
||||
// GPIO port where the channel select pins live. If there is only one
|
||||
// channel, this may be left undefined. If this is undefined,
|
||||
// TIMDAC_HW_GPIO_CHAN_PINS must also be undefined.
|
||||
// (e.g. GPIOC)
|
||||
//
|
||||
// #define TIMDAC_HW_GPIO_CHAN_PINS
|
||||
// List of channel select pins, least significant bit first, up to eight.
|
||||
// If there is only one channel, this may be left undefined. If this is
|
||||
// undefined, TIMDAC_HW_GPIO_CHAN must also be undefined. Note that if
|
||||
// a channel number is requested that exceeds pow2(CHAN_PINS)-1, the
|
||||
// channel number will be truncated.
|
||||
// (e.g. GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2)
|
||||
// ============================================================================
|
||||
|
||||
|
||||
#include "timdac_ll.h"
|
||||
#include "timdac_config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <ch32v10x_tim.h>
|
||||
#include <ch32v10x_gpio.h>
|
||||
|
||||
#if defined(TIMDAC_HW_GPIO_CHAN_PORT) && !defined(TIMDAC_HW_GPIO_CHAN_PINS)
|
||||
# error "timdac_hw: channel select port defined, but no pins"
|
||||
#elif defined(TIMDAC_HW_GPIO_CHAN_PINS) && !defined(TIMDAC_HW_GPIO_CHAN_PORT)
|
||||
# error "timdac_hw: channel select pins defined, but no port"
|
||||
#elif defined(TIMDAC_HW_GPIO_CHAN_PORT)
|
||||
static const uint16_t _chansel[8] = {TIMDAC_HW_GPIO_CHAN_PINS};
|
||||
#define N_CHANSEL (sizeof(_chansel)/sizeof(_chansel[0]))
|
||||
#endif
|
||||
|
||||
static uint16_t _ntim_to_channel(uint16_t ntim);
|
||||
static void _init_gpio(
|
||||
GPIO_TypeDef * gpio,
|
||||
uint16_t pin,
|
||||
GPIOMode_TypeDef mode,
|
||||
bool fast,
|
||||
bool value
|
||||
);
|
||||
|
||||
void timdac_hw_init(void)
|
||||
{
|
||||
_init_gpio(TIMDAC_HW_GPIO_MUXINHIBIT, GPIO_Mode_Out_PP, false, true);
|
||||
_init_gpio(TIMDAC_HW_GPIO_DISCHARGE, GPIO_Mode_Out_PP, false, true);
|
||||
#ifdef TIMDAC_HW_GPIO_POLARITY
|
||||
_init_gpio(TIMDAC_HW_GPIO_POLARITY, GPIO_Mode_Out_PP, false, false);
|
||||
#endif
|
||||
|
||||
#ifdef TIMDAC_HW_GPIO_CHAN_PORT
|
||||
for (size_t i = 0; i < N_CHANSEL; i++)
|
||||
{
|
||||
_init_gpio(
|
||||
TIMDAC_HW_GPIO_CHAN_PORT,
|
||||
_chansel[i],
|
||||
GPIO_Mode_Out_PP,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
_init_gpio(TIMDAC_HW_GPIO_TUNE, GPIO_Mode_IPU, false, false);
|
||||
_init_gpio(TIMDAC_HW_GPIO_TIMER, GPIO_Mode_AF_PP, true, false);
|
||||
|
||||
TIM_TimeBaseInitTypeDef tbi = {
|
||||
.TIM_Prescaler = 0,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = TIMDAC_DISCHARGE_TIME,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
};
|
||||
|
||||
TIM_TimeBaseInit(TIMDAC_HW_TIMER, &tbi);
|
||||
|
||||
TIM_OCInitTypeDef oci = {
|
||||
.TIM_OCMode = TIM_OCMode_PWM2,
|
||||
.TIM_OutputState = TIM_OutputState_Enable,
|
||||
.TIM_OutputNState = TIM_OutputNState_Disable,
|
||||
.TIM_Pulse = 0,
|
||||
.TIM_OCPolarity = TIM_OCPolarity_High,
|
||||
.TIM_OCNPolarity = TIM_OCPolarity_High,
|
||||
.TIM_OCIdleState = TIM_OCIdleState_Reset, // TIM1/8 only
|
||||
.TIM_OCNIdleState = TIM_OCIdleState_Reset, // TIM1/8 only
|
||||
};
|
||||
|
||||
switch (TIMDAC_HW_TIMER_CHANNEL) {
|
||||
case 1:
|
||||
TIM_OC1Init(TIMDAC_HW_TIMER, &oci);
|
||||
TIM_OC1PreloadConfig(TIMDAC_HW_TIMER, TIM_OCPreload_Disable);
|
||||
break;
|
||||
case 2:
|
||||
TIM_OC2Init(TIMDAC_HW_TIMER, &oci);
|
||||
TIM_OC2PreloadConfig(TIMDAC_HW_TIMER, TIM_OCPreload_Disable);
|
||||
break;
|
||||
case 3:
|
||||
TIM_OC3Init(TIMDAC_HW_TIMER, &oci);
|
||||
TIM_OC3PreloadConfig(TIMDAC_HW_TIMER, TIM_OCPreload_Disable);
|
||||
break;
|
||||
case 4:
|
||||
TIM_OC4Init(TIMDAC_HW_TIMER, &oci);
|
||||
TIM_OC4PreloadConfig(TIMDAC_HW_TIMER, TIM_OCPreload_Disable);
|
||||
break;
|
||||
}
|
||||
|
||||
TIM_SelectOnePulseMode(TIMDAC_HW_TIMER, TIM_OPMode_Single);
|
||||
TIM_CtrlPWMOutputs(TIMDAC_HW_TIMER, ENABLE);
|
||||
|
||||
#ifdef TIMDAC_HW_TIMER_IRQn
|
||||
NVIC_InitTypeDef tim_it_init = {
|
||||
.NVIC_IRQChannel = TIMDAC_HW_TIMER_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = TIMDAC_HW_TIMER_IRQPRIO,
|
||||
.NVIC_IRQChannelSubPriority = TIMDAC_HW_TIMER_IRQSUBPRIO,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
};
|
||||
NVIC_Init(&tim_it_init);
|
||||
TIM_ITConfig(TIMDAC_HW_TIMER, TIM_IT_Update, ENABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void timdac_hw_timer_set_period(uint16_t counts)
|
||||
{
|
||||
TIM_SetAutoreload(TIMDAC_HW_TIMER, counts);
|
||||
}
|
||||
|
||||
void timdac_hw_timer_set_compare(uint16_t counts)
|
||||
{
|
||||
// Our timer mode results in a pulse that is LOW for `counts`, so
|
||||
// invert the duration:
|
||||
uint16_t value = UINT16_MAX - counts;
|
||||
|
||||
switch (TIMDAC_HW_TIMER_CHANNEL) {
|
||||
case 1:
|
||||
TIM_SetCompare1(TIMDAC_HW_TIMER, value);
|
||||
break;
|
||||
case 2:
|
||||
TIM_SetCompare2(TIMDAC_HW_TIMER, value);
|
||||
break;
|
||||
case 3:
|
||||
TIM_SetCompare3(TIMDAC_HW_TIMER, value);
|
||||
break;
|
||||
case 4:
|
||||
TIM_SetCompare4(TIMDAC_HW_TIMER, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void timdac_hw_timer_set_oc_enabled(bool en)
|
||||
{
|
||||
TIM_CCxCmd(TIMDAC_HW_TIMER, _ntim_to_channel(TIMDAC_HW_TIMER_CHANNEL),
|
||||
en ? TIM_CCx_Enable : TIM_CCx_Disable);
|
||||
}
|
||||
|
||||
void timdac_hw_timer_start(void)
|
||||
{
|
||||
TIM_Cmd(TIMDAC_HW_TIMER, ENABLE);
|
||||
}
|
||||
|
||||
bool timdac_hw_timer_running(void)
|
||||
{
|
||||
return TIMDAC_HW_TIMER->CTLR1 & TIM_CEN;
|
||||
}
|
||||
|
||||
void timdac_hw_gpio_muxinhibit(bool en)
|
||||
{
|
||||
GPIO_WriteBit(TIMDAC_HW_GPIO_MUXINHIBIT, en ? Bit_SET : Bit_RESET);
|
||||
}
|
||||
|
||||
void timdac_hw_gpio_discharge(bool en)
|
||||
{
|
||||
GPIO_WriteBit(TIMDAC_HW_GPIO_DISCHARGE, en ? Bit_SET : Bit_RESET);
|
||||
}
|
||||
|
||||
void timdac_hw_gpio_polarity(bool pos)
|
||||
{
|
||||
#ifdef TIMDAC_HW_GPIO_POLARITY
|
||||
GPIO_WriteBit(TIMDAC_HW_GPIO_POLARITY, !pos ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
void timdac_hw_gpio_select_channel(uint8_t chan)
|
||||
{
|
||||
#ifdef TIMDAC_HW_GPIO_CHAN_PORT
|
||||
uint16_t chan_mask = 0, chan_bits = 0;
|
||||
|
||||
for (uint8_t i = 0; i < N_CHANSEL; i++, chan >>= 1)
|
||||
{
|
||||
if (!_chansel[i]) break;
|
||||
uint16_t bit = _chansel[i];
|
||||
chan_mask |= bit;
|
||||
if (chan & 1) chan_bits |= bit;
|
||||
}
|
||||
|
||||
GPIO_ResetBits(TIMDAC_HW_GPIO_CHAN_PORT, chan_mask);
|
||||
GPIO_SetBits(TIMDAC_HW_GPIO_CHAN_PORT, chan_bits);
|
||||
#else
|
||||
(void) chan;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool timdac_hw_gpio_tune_is_high(void)
|
||||
{
|
||||
return GPIO_ReadInputDataBit(TIMDAC_HW_GPIO_TUNE)
|
||||
^ !TIMDAC_HW_GPIO_TUNE_POL;
|
||||
}
|
||||
|
||||
static uint16_t _ntim_to_channel(uint16_t ntim)
|
||||
{
|
||||
return (ntim - 1) << 2;
|
||||
}
|
||||
|
||||
static void _init_gpio(
|
||||
GPIO_TypeDef * gpio,
|
||||
uint16_t pin,
|
||||
GPIOMode_TypeDef mode,
|
||||
bool fast,
|
||||
bool value
|
||||
)
|
||||
{
|
||||
if (mode == GPIO_Mode_Out_OD || mode == GPIO_Mode_Out_PP)
|
||||
GPIO_WriteBit(gpio, pin, value);
|
||||
|
||||
GPIO_InitTypeDef init = {
|
||||
.GPIO_Pin = pin,
|
||||
.GPIO_Speed = fast ? GPIO_Speed_50MHz : GPIO_Speed_2MHz,
|
||||
.GPIO_Mode = mode,
|
||||
};
|
||||
|
||||
GPIO_Init(gpio, &init);
|
||||
}
|
||||
|
@ -0,0 +1,290 @@
|
||||
// intellectual property is bullshit bgdc
|
||||
|
||||
#include "timdac_ll.h"
|
||||
#include "timdac_config.h"
|
||||
|
||||
#include "light_output.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
// State definitions
|
||||
enum state {
|
||||
STATE_IDLE,
|
||||
STATE_DISCHARGE,
|
||||
STATE_PULSE,
|
||||
STATE_STABILIZE,
|
||||
STATE_TRANSFER,
|
||||
};
|
||||
|
||||
#define CHAN_TUNE UINT8_MAX
|
||||
|
||||
#define VALUE_FLAG_NEG 0x10000000u
|
||||
#define VALUE_MASK 0x0000FFFFu
|
||||
|
||||
// Compute the tuned compare value for a given DAC code
|
||||
static uint16_t _compare_for_value(timdac_ll_t * td, uint16_t value);
|
||||
|
||||
// Start the external hardware cycle for the current state
|
||||
static void _select(timdac_ll_t * td);
|
||||
|
||||
// Perform one step of the tuning SAR binary search
|
||||
static void _sar_search(timdac_ll_t * td);
|
||||
|
||||
#define NTIM_VALID(ntim) ((ntim) >= 1 && (ntim) <= 4)
|
||||
|
||||
void timdac_ll_init(timdac_ll_t * td)
|
||||
{
|
||||
td->chan = 0;
|
||||
td->state = STATE_IDLE;
|
||||
td->tune = 0;
|
||||
td->slow = false;
|
||||
td->tune_sar_top = UINT16_MAX;
|
||||
td->tune_sar_bot = 0;
|
||||
|
||||
if (td->tunediag)
|
||||
{
|
||||
td->tunediag->cycles = 0;
|
||||
td->tunediag->clobbers = 0;
|
||||
td->tunediag->noise = 0;
|
||||
td->tunediag->tune = 0;
|
||||
}
|
||||
|
||||
timdac_hw_init();
|
||||
}
|
||||
|
||||
bool timdac_ll_tune(timdac_ll_t * td)
|
||||
{
|
||||
if (td->tune_sar_top == 0 || td->tune_sar_bot == UINT16_MAX)
|
||||
{
|
||||
// Start fresh.
|
||||
td->tune_sar_top = UINT16_MAX;
|
||||
td->tune_sar_bot = 0;
|
||||
return false;
|
||||
}
|
||||
else if (td->tune_sar_top != td->tune_sar_bot)
|
||||
{
|
||||
// A SAR tuning is starting or in progress.
|
||||
|
||||
uint16_t guess =
|
||||
((uint32_t) td->tune_sar_top
|
||||
+ (uint32_t) td->tune_sar_bot) / 2;
|
||||
|
||||
timdac_hw_timer_set_compare(guess);
|
||||
td->chan = CHAN_TUNE;
|
||||
td->state = STATE_DISCHARGE;
|
||||
_select(td);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Done tuning, enter the new value
|
||||
uint16_t tunevalue = td->tune_sar_top;
|
||||
|
||||
int32_t err = tunevalue * 65536L - td->tune;
|
||||
|
||||
bool clobber =
|
||||
err > (TIMDAC_TUNE_NTHRESH * 65536L)
|
||||
|| err < (TIMDAC_TUNE_NTHRESH * -65536L);
|
||||
|
||||
if (clobber)
|
||||
{
|
||||
td->tune = tunevalue * 65536UL;
|
||||
}
|
||||
else
|
||||
{
|
||||
err *= TIMDAC_TUNE_WEIGHT;
|
||||
err /= UINT16_MAX;
|
||||
|
||||
td->tune += err;
|
||||
}
|
||||
|
||||
if (td->tunediag)
|
||||
{
|
||||
td->tunediag->tune = td->tune >> 16;
|
||||
td->tunediag->cycles++;
|
||||
if (td->tunediag->cycles == 0)
|
||||
td->tunediag->noise = 0;
|
||||
|
||||
uint64_t noise = td->tunediag->noise + err * err;
|
||||
if (noise < td->tunediag->noise && !clobber)
|
||||
{
|
||||
// Overflow
|
||||
td->tunediag->cycles = 1;
|
||||
td->tunediag->noise = err * err;
|
||||
}
|
||||
else if (!clobber)
|
||||
td->tunediag->noise = noise;
|
||||
else
|
||||
td->tunediag->clobbers++;
|
||||
}
|
||||
|
||||
td->tune_sar_top = UINT16_MAX;
|
||||
td->tune_sar_bot = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void timdac_ll_emit(timdac_ll_t * td, uint8_t chan, int16_t value, bool slow)
|
||||
{
|
||||
uint16_t const value_pos =
|
||||
value >= 0 ? value :
|
||||
value == INT16_MIN ? INT16_MAX : -value;
|
||||
|
||||
uint16_t const comp = _compare_for_value(td, value_pos);
|
||||
|
||||
timdac_hw_gpio_polarity(value >= 0);
|
||||
timdac_hw_timer_set_compare(comp);
|
||||
|
||||
td->chan = chan;
|
||||
td->state = STATE_DISCHARGE;
|
||||
td->slow = slow;
|
||||
|
||||
_select(td);
|
||||
}
|
||||
|
||||
bool timdac_ll_poll(timdac_ll_t * td)
|
||||
{
|
||||
if (timdac_hw_timer_running())
|
||||
return true;
|
||||
|
||||
switch (td->state)
|
||||
{
|
||||
default:
|
||||
// fall through
|
||||
case STATE_IDLE:
|
||||
td->state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_DISCHARGE:
|
||||
// We just finished discharging the cap. Now, output the pulse.
|
||||
td->state = STATE_PULSE;
|
||||
break;
|
||||
|
||||
case STATE_PULSE:
|
||||
// The ramp is complete; give the integrator some time to settle
|
||||
td->state = STATE_STABILIZE;
|
||||
break;
|
||||
|
||||
case STATE_STABILIZE:
|
||||
// The ramp has now reached its final value. Transfer it to
|
||||
// the holding capacitor or save a tuning.
|
||||
if (td->chan == CHAN_TUNE)
|
||||
{
|
||||
td->state = STATE_IDLE;
|
||||
_sar_search(td);
|
||||
}
|
||||
else
|
||||
{
|
||||
td->state = STATE_TRANSFER;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_TRANSFER:
|
||||
// Done!
|
||||
td->state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
_select(td);
|
||||
return td->state != STATE_IDLE;
|
||||
}
|
||||
|
||||
void timdac_ll_idle(timdac_ll_t * td)
|
||||
{
|
||||
if (td->state != STATE_IDLE)
|
||||
return;
|
||||
|
||||
_select(td);
|
||||
timdac_hw_timer_set_period(100);
|
||||
timdac_hw_timer_start();
|
||||
}
|
||||
|
||||
static uint16_t _compare_for_value(timdac_ll_t * td, uint16_t value)
|
||||
{
|
||||
// Scale value according to tuning, with rounding.
|
||||
return (value * (td->tune / UINT16_MAX) + 16384) / 32768;
|
||||
}
|
||||
|
||||
static void _select(timdac_ll_t * td)
|
||||
{
|
||||
bool inhibit = false;
|
||||
bool discharge = false;
|
||||
uint16_t period = UINT16_MAX;
|
||||
|
||||
uint8_t state = td->state;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case STATE_DISCHARGE:
|
||||
period = TIMDAC_DISCHARGE_TIME;
|
||||
// fall through
|
||||
case STATE_IDLE:
|
||||
inhibit = true;
|
||||
discharge = true;
|
||||
break;
|
||||
|
||||
case STATE_STABILIZE:
|
||||
period = TIMDAC_STABILIZATION_TIME;
|
||||
// fall through
|
||||
case STATE_PULSE:
|
||||
inhibit = true;
|
||||
discharge = false;
|
||||
break;
|
||||
|
||||
case STATE_TRANSFER:
|
||||
period = td->slow
|
||||
? TIMDAC_SLOW_TRANSFER_TIME
|
||||
: TIMDAC_FAST_TRANSFER_TIME;
|
||||
inhibit = false;
|
||||
discharge = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Perform channel switching
|
||||
if (inhibit) timdac_hw_gpio_muxinhibit(true);
|
||||
if (state == STATE_DISCHARGE) timdac_hw_gpio_select_channel(td->chan);
|
||||
if (!inhibit) timdac_hw_gpio_muxinhibit(false);
|
||||
timdac_hw_gpio_discharge(discharge);
|
||||
|
||||
// Timer is not going to run now if we're in idle.
|
||||
if (state == STATE_IDLE)
|
||||
return;
|
||||
|
||||
// Set up the timer and go!
|
||||
timdac_hw_timer_set_oc_enabled(state == STATE_PULSE);
|
||||
timdac_hw_timer_set_period(period);
|
||||
timdac_hw_timer_start();
|
||||
}
|
||||
|
||||
static void _sar_search(timdac_ll_t * td)
|
||||
{
|
||||
bool tune_high = timdac_hw_gpio_tune_is_high();
|
||||
|
||||
uint16_t guess =
|
||||
((uint32_t) td->tune_sar_top
|
||||
+ (uint32_t) td->tune_sar_bot) / 2;
|
||||
|
||||
if (tune_high)
|
||||
{
|
||||
if (td->tune_sar_top == guess)
|
||||
td->tune_sar_bot = guess;
|
||||
else
|
||||
td->tune_sar_top = guess;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (td->tune_sar_bot == guess)
|
||||
td->tune_sar_top = guess;
|
||||
else
|
||||
td->tune_sar_bot = guess;
|
||||
}
|
||||
|
||||
if (td->tune_sar_bot > td->tune_sar_top)
|
||||
{
|
||||
uint16_t temp = td->tune_sar_bot;
|
||||
td->tune_sar_bot = td->tune_sar_top;
|
||||
td->tune_sar_top = temp;
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
// intellectual property is bullshit bgdc
|
||||
|
||||
#ifndef TIMDAC_LL_H
|
||||
#define TIMDAC_LL_H
|
||||
|
||||
// ============================================================================
|
||||
// TIMDAC LOW LEVEL DRIVER
|
||||
// ----------------------------------------------------------------------------
|
||||
// This module provides the low-level (but still hardware-abstracted) part of
|
||||
// TIMDAC. It implements the state machine that provides each individual cycle
|
||||
// of operation, but must be sequenced through cycles correctly. You are not
|
||||
// generally meant to use it directly; see the high-level interface in timdac.h
|
||||
// which handles this sequencing.
|
||||
// ============================================================================
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <ch32v10x_tim.h>
|
||||
#include <ch32v10x_gpio.h>
|
||||
|
||||
#define TIMDAC_MAX_SELCHAN 5
|
||||
|
||||
typedef struct timdac_tunediag_s {
|
||||
// Total number of calibration cycles run (will overflow)
|
||||
uint16_t cycles;
|
||||
|
||||
// Tuning clobber counter.
|
||||
uint16_t clobbers;
|
||||
|
||||
// Current tune value.
|
||||
uint16_t tune;
|
||||
|
||||
// Tuning noise accumulator. This will hold the sum of the squares of
|
||||
// the tuning errors, and will be reset to zero each time cycles wraps
|
||||
// around. To get the RMS tuning noise, divide this by cycles, take
|
||||
// the square root, then divide by 65536.
|
||||
uint64_t noise;
|
||||
} timdac_tunediag_t;
|
||||
|
||||
typedef struct timdac_ll_s {
|
||||
// #### BEGIN - USER MUST INITIALIZE ####
|
||||
// ---- Diagnostics ----
|
||||
|
||||
// Optional calibration diagnostics struct
|
||||
timdac_tunediag_t * tunediag;
|
||||
|
||||
// #### END - USER MUST INITIALIZE ####
|
||||
|
||||
// Tuning value. This is 65536 times the timer setting required to
|
||||
// produce Vref. May be read but should not be written; if zero, the
|
||||
// first tuning has not completed yet.
|
||||
uint32_t tune;
|
||||
|
||||
// Tuning successive approximation upper bound
|
||||
uint16_t tune_sar_top;
|
||||
|
||||
// Tuning successive approximation lower bound
|
||||
uint16_t tune_sar_bot;
|
||||
|
||||
// Current channel number
|
||||
uint8_t chan;
|
||||
|
||||
// Current state
|
||||
uint8_t state;
|
||||
|
||||
// Whether performing a slow update
|
||||
bool slow;
|
||||
} timdac_ll_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.
|
||||
//
|
||||
// td: timdac instance
|
||||
void timdac_ll_init(timdac_ll_t * td);
|
||||
|
||||
// Start to tune the DAC. After calling, run timdac_poll() until it
|
||||
// reports idle.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Returns whether the timer is now busy.
|
||||
bool timdac_ll_tune(timdac_ll_t * td);
|
||||
|
||||
// Start to emit a voltage on a channel. The integrating ramp will be started.
|
||||
// After calling, run timdac_poll() until it reports idle.
|
||||
//
|
||||
// If timdac_poll() was not already reporting idle, the previous cycle will be
|
||||
// aborted.
|
||||
//
|
||||
// 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
|
||||
void timdac_ll_emit(timdac_ll_t * td, 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.
|
||||
//
|
||||
// td: timdac instance
|
||||
// return: true if busy, false if idle
|
||||
bool timdac_ll_poll(timdac_ll_t * td);
|
||||
|
||||
// Run a single idle cycle. This can be used to kickstart an interrupt driven
|
||||
// timdac. If the timdac is not already at the idle state, does nothing.
|
||||
void timdac_ll_idle(timdac_ll_t * td);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// HARDWARE DRIVER INTERFACE
|
||||
// These functions are implemented by each hardware driver.
|
||||
|
||||
void timdac_hw_init(void);
|
||||
void timdac_hw_timer_set_period(uint16_t counts);
|
||||
void timdac_hw_timer_set_compare(uint16_t counts);
|
||||
void timdac_hw_timer_set_oc_enabled(bool en);
|
||||
void timdac_hw_timer_start(void);
|
||||
bool timdac_hw_timer_running(void);
|
||||
void timdac_hw_gpio_muxinhibit(bool en);
|
||||
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);
|
||||
|
||||
#endif // !defined(TIMDAC_LL_H)
|
Loading…
Reference in New Issue