mirror of
https://github.com/andreili/klipper.git
synced 2025-09-19 03:24:40 +02:00
156 lines
4.0 KiB
C
156 lines
4.0 KiB
C
// ADC functions on Huada HC32F460
|
|
//
|
|
// Copyright (C) 2022 Steven Gotthardt <gotthardt@gmail.com>
|
|
//
|
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
#include "generic/misc.h" // timer_from_us
|
|
#include "command.h" // shutdown
|
|
#include "board/gpio.h" // gpio_adc_setup
|
|
#include "board/internal.h" // GPIO
|
|
#include "sched.h" // sched_shutdown
|
|
|
|
// library
|
|
#include "hc32f460_adc.h"
|
|
#include "hc32f460_pwc.h"
|
|
#include "hc32f460_gpio.h"
|
|
|
|
#define ADC_RESOLUTION_12BIT (12u)
|
|
#define ADC_RESOLUTION_10BIT (10u)
|
|
#define ADC_RESOLUTION_8BIT (8u)
|
|
|
|
#define ADC1_RESOLUTION (ADC_RESOLUTION_12BIT)
|
|
#define ADC1_PRECISION (1ul << ADC1_RESOLUTION)
|
|
|
|
#if ADC1_RESOLUTION == ADC_RESOLUTION_12BIT
|
|
#define AdcResolution AdcResolution_12Bit
|
|
#elif ADC1_RESOLUTION == ADC_RESOLUTION_10BIT
|
|
#define AdcResolution AdcResolution_10Bit
|
|
#else
|
|
#define AdcResolution AdcResolution_8Bit
|
|
#endif
|
|
|
|
|
|
/* Timeout value definitions. Found in example code */
|
|
#define TIMEOUT_VAL (30u)
|
|
|
|
DECL_CONSTANT("ADC_MAX", ADC1_PRECISION-1);
|
|
|
|
// These pins can be used for ADC
|
|
static const uint8_t adc_gpio[] = {
|
|
GPIO('A', 0), // Chan 0
|
|
GPIO('A', 1), // Chan 1
|
|
GPIO('A', 2), // Chan 2
|
|
GPIO('A', 3), // Chan 3
|
|
GPIO('A', 4), // Chan 4
|
|
GPIO('A', 5), // Chan 5
|
|
GPIO('A', 6), // Chan 6
|
|
GPIO('A', 7), // Chan 7
|
|
GPIO('B', 0), // Chan 8
|
|
GPIO('B', 1), // Chan 9
|
|
GPIO('C', 0), // Chan 10 // TBed on TriGorilla
|
|
GPIO('C', 1), // Chan 11 // THead on TriGorilla
|
|
GPIO('C', 2), // Chan 12
|
|
GPIO('C', 3), // Chan 13
|
|
GPIO('C', 4), // Chan 14 // TBed on aquilla
|
|
GPIO('C', 5), // Chan 15 // THead on aquilla
|
|
};
|
|
|
|
|
|
struct gpio_adc
|
|
gpio_adc_setup(uint32_t gpio)
|
|
{
|
|
// validate pin in adc_pins table
|
|
int chan;
|
|
for (chan=0; ; chan++)
|
|
{
|
|
if (chan >= ARRAY_SIZE(adc_gpio))
|
|
{
|
|
shutdown("Not a valid ADC pin");
|
|
}
|
|
if (adc_gpio[chan] == gpio)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// set as analog
|
|
gpio_peripheral(gpio, Pin_Mode_Ana, 0);
|
|
|
|
uint8_t sampleTime[ARRAY_SIZE(adc_gpio)] = { TIMEOUT_VAL }; // all chans
|
|
stc_adc_ch_cfg_t stcAdcChan;
|
|
stcAdcChan.u32Channel = 1 << chan;
|
|
stcAdcChan.u8Sequence = ADC_SEQ_A; // all conversions are in SEQ A
|
|
stcAdcChan.pu8SampTime = sampleTime;
|
|
ADC_AddAdcChannel(M4_ADC1, &stcAdcChan);
|
|
|
|
return (struct gpio_adc){ .chan = chan };
|
|
}
|
|
|
|
|
|
// Try to sample a value. Returns zero if sample ready, otherwise
|
|
// returns the number of clock ticks the caller should wait before
|
|
// retrying this function.
|
|
uint32_t
|
|
gpio_adc_sample(struct gpio_adc g)
|
|
{
|
|
// true if the sequence is finished
|
|
if (ADC_GetEocFlag(M4_ADC1, ADC_SEQ_A))
|
|
{
|
|
// all conversions are done - clear the flag
|
|
ADC_ClrEocFlag(M4_ADC1, ADC_SEQ_A);
|
|
return 0;
|
|
}
|
|
else if (M4_ADC1->STR & 1)
|
|
{
|
|
// running but not done yet
|
|
return timer_from_us(TIMEOUT_VAL/2);
|
|
}
|
|
else
|
|
{
|
|
// not running - so start
|
|
ADC_StartConvert(M4_ADC1);
|
|
}
|
|
|
|
return timer_from_us(TIMEOUT_VAL);
|
|
}
|
|
|
|
|
|
// Read a value; use only after gpio_adc_sample() returns zero
|
|
uint16_t
|
|
gpio_adc_read(struct gpio_adc g)
|
|
{
|
|
// return the one we want...
|
|
return ADC_GetValue(M4_ADC1, g.chan);
|
|
}
|
|
|
|
|
|
// Cancel a sample that may have been started with gpio_adc_sample()
|
|
void
|
|
gpio_adc_cancel_sample(struct gpio_adc g)
|
|
{
|
|
ADC_StopConvert(M4_ADC1);
|
|
}
|
|
|
|
|
|
// The clocks are already set by the loader.
|
|
// There is ADC1 and ADC2. Sequences do all channels at once.
|
|
void
|
|
adc_init(void)
|
|
{
|
|
// PCLK2 (ADC clock) is 'divide by 4', Max ADC clock is 60MHz
|
|
stc_adc_init_t stcAdcInit = {0};
|
|
stcAdcInit.enResolution = AdcResolution; // see define above
|
|
stcAdcInit.enDataAlign = AdcDataAlign_Right;
|
|
stcAdcInit.enAutoClear = AdcClren_Disable;
|
|
stcAdcInit.enScanMode = AdcMode_SAOnce;
|
|
|
|
// power-on ADC
|
|
PWC_Fcg3PeriphClockCmd(PWC_FCG3_PERIPH_ADC1, Enable);
|
|
|
|
// only using ADC1
|
|
ADC_Init(M4_ADC1, &stcAdcInit);
|
|
}
|
|
|
|
DECL_INIT(adc_init);
|