283 lines
7.5 KiB
C
283 lines
7.5 KiB
C
// 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);
|
|
}
|
|
|