Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

162 lines
4.9 KiB

/*
* Copyright (c) 2025 Michael Hope
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT wch_adc
#include <hal_ch32fun.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control.h>
struct adc_ch32v00x_config {
ADC_TypeDef *regs;
const struct pinctrl_dev_config *pin_cfg;
const struct device *clock_dev;
uint8_t clock_id;
};
static int adc_ch32v00x_channel_setup(const struct device *dev,
const struct adc_channel_cfg *channel_cfg)
{
if (channel_cfg->gain != ADC_GAIN_1) {
return -EINVAL;
}
if (channel_cfg->reference != ADC_REF_INTERNAL) {
return -EINVAL;
}
if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
return -EINVAL;
}
if (channel_cfg->differential) {
return -EINVAL;
}
if (channel_cfg->channel_id >= 10) {
return -EINVAL;
}
return 0;
}
static int adc_ch32v00x_read(const struct device *dev, const struct adc_sequence *sequence)
{
const struct adc_ch32v00x_config *config = dev->config;
ADC_TypeDef *regs = config->regs;
uint32_t channels = sequence->channels;
int rsqr = 2;
int sequence_id = 0;
int total_channels = 0;
int i;
uint16_t *samples = sequence->buffer;
if (sequence->options != NULL) {
return -ENOTSUP;
}
if (sequence->resolution != 10) {
return -EINVAL;
}
if (sequence->oversampling != 0) {
return -ENOTSUP;
}
if (sequence->channels >= (1 << 10)) {
return -EINVAL;
}
if (sequence->calibrate) {
regs->CTLR2 |= ADC_RSTCAL;
while ((regs->CTLR2 & ADC_RSTCAL) != 0) {
}
regs->CTLR2 |= ADC_CAL;
while ((regs->CTLR2 & ADC_CAL) != 0) {
}
}
/*
* Build the sample sequence. The channel IDs are packed 5 bits at a time starting in RSQR3
* and working down in memory to RSQR1.
*/
regs->RSQR1 = 0;
regs->RSQR2 = 0;
regs->RSQR3 = 0;
for (i = 0; channels != 0; i++, channels >>= 1) {
if ((channels & 1) != 0) {
total_channels++;
(&regs->RSQR1)[rsqr] |= i << sequence_id;
/* Each channel ID is 5 bits wide */
sequence_id += 5;
/* Each sequence register can hold 6 x 5 bit channel IDs */
if (sequence_id >= 30) {
/* Move on to the next RSQRn register, i.e. RSQR(n-1) */
sequence_id = 0;
rsqr--;
}
}
}
if (total_channels == 0) {
return 0;
}
if (sequence->buffer_size < total_channels * sizeof(*samples)) {
return -ENOMEM;
}
/* Set the number of channels to read. Note that '0' means 'one channel'. */
regs->RSQR1 |= (total_channels - 1) * ADC_L_0;
regs->CTLR2 |= ADC_SWSTART;
for (i = 0; i < total_channels; i++) {
while ((regs->STATR & ADC_EOC) == 0) {
}
*samples++ = regs->RDATAR;
}
return 0;
}
static int adc_ch32v00x_init(const struct device *dev)
{
const struct adc_ch32v00x_config *config = dev->config;
ADC_TypeDef *regs = config->regs;
int err;
clock_control_on(config->clock_dev, (clock_control_subsys_t)(uintptr_t)config->clock_id);
err = pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT);
if (err != 0) {
return err;
}
/*
* The default sampling time is 3 cycles and shows coupling between channels. Use 15 cycles
* instead. Arbitrary.
*/
regs->SAMPTR2 = ADC_SMP0_1 | ADC_SMP1_1 | ADC_SMP2_1 | ADC_SMP3_1 | ADC_SMP4_1 |
ADC_SMP5_1 | ADC_SMP6_1 | ADC_SMP7_1 | ADC_SMP8_1 | ADC_SMP9_1;
regs->CTLR2 = ADC_ADON | ADC_EXTSEL;
return 0;
}
#define ADC_CH32V00X_DEVICE(n) \
PINCTRL_DT_INST_DEFINE(n); \
\
static DEVICE_API(adc, adc_ch32v00x_api_##n) = { \
.channel_setup = adc_ch32v00x_channel_setup, \
.read = adc_ch32v00x_read, \
.ref_internal = DT_INST_PROP(n, vref_mv), \
}; \
\
static const struct adc_ch32v00x_config adc_ch32v00x_config_##n = { \
.regs = (ADC_TypeDef *)DT_INST_REG_ADDR(n), \
.pin_cfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.clock_id = DT_INST_CLOCKS_CELL(n, id), \
}; \
\
DEVICE_DT_INST_DEFINE(n, adc_ch32v00x_init, NULL, NULL, &adc_ch32v00x_config_##n, \
POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, &adc_ch32v00x_api_##n);
DT_INST_FOREACH_STATUS_OKAY(ADC_CH32V00X_DEVICE)