Compare commits
2 commits
358113b114
...
ce9da01428
Author | SHA1 | Date | |
---|---|---|---|
ce9da01428 | |||
bd58007d73 |
14 changed files with 400 additions and 6 deletions
|
@ -15,7 +15,7 @@
|
|||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,console = &usart1;
|
||||
zephyr,console = &debug0;
|
||||
zephyr,shell-uart = &usart1;
|
||||
};
|
||||
|
||||
|
@ -32,6 +32,10 @@
|
|||
};
|
||||
};
|
||||
|
||||
&debug0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpiod {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -54,3 +54,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_GAU adc_mcux_gau_adc.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_ADC_AMBIQ adc_ambiq.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ADC_RENESAS_RA adc_renesas_ra.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ADC_MAX32 adc_max32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ADC_CH32V00X adc_ch32v00x.c)
|
||||
|
|
|
@ -134,4 +134,6 @@ source "drivers/adc/Kconfig.renesas_ra"
|
|||
|
||||
source "drivers/adc/Kconfig.max32"
|
||||
|
||||
source "drivers/adc/Kconfig.ch32v00x"
|
||||
|
||||
endif # ADC
|
||||
|
|
9
drivers/adc/Kconfig.ch32v00x
Normal file
9
drivers/adc/Kconfig.ch32v00x
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2024 Google LLC.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config ADC_CH32V00X
|
||||
bool "WCH CH32V00x ADC Driver"
|
||||
default y
|
||||
depends on DT_HAS_WCH_ADC_ENABLED
|
||||
help
|
||||
Enable the WCH CH32V00x family Analog-to-Digital Converter (ADC) driver.
|
148
drivers/adc/adc_ch32v00x.c
Normal file
148
drivers/adc/adc_ch32v00x.c
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Google LLC.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT wch_adc
|
||||
|
||||
#include <soc.h>
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/irq.h>
|
||||
LOG_MODULE_REGISTER(adc_ch32v00x, CONFIG_ADC_LOG_LEVEL);
|
||||
|
||||
#include <ch32_adc.h>
|
||||
|
||||
struct adc_ch32v00x_data {
|
||||
};
|
||||
|
||||
struct adc_ch32v00x_config {
|
||||
ADC_TypeDef *regs;
|
||||
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 ((ADC1->CTLR2 & ADC_CAL) != 0) {
|
||||
}
|
||||
}
|
||||
|
||||
regs->RSQR1 = 0;
|
||||
regs->RSQR2 = 0;
|
||||
regs->RSQR3 = 0;
|
||||
|
||||
for (i = 0; channels != 0; i++, channels >>= 1) {
|
||||
if ((channels & 1) != 0) {
|
||||
total_channels++;
|
||||
(®s->RSQR1)[rsqr] |= i << sequence_id;
|
||||
/* The channel IDs are packed 5 bits at a time into RSQR3 down to RSQR1.
|
||||
*/
|
||||
sequence_id += ADC_SQ2_0;
|
||||
if (sequence_id >= 32) {
|
||||
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;
|
||||
|
||||
clock_control_on(config->clock_dev, (clock_control_subsys_t *)(uintptr_t)config->clock_id);
|
||||
|
||||
regs->CTLR2 = ADC_ADON | ADC_EXTSEL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADC_CH32V00X_DEVICE(n) \
|
||||
static const struct adc_driver_api 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), \
|
||||
.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)
|
|
@ -15,3 +15,4 @@ zephyr_library_sources_ifdef(CONFIG_UART_MCUMGR uart_mcumgr.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_XTENSA_SIM_CONSOLE xtensa_sim_console.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_EFI_CONSOLE efi_console.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_WINSTREAM_CONSOLE winstream_console.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_QINGKEV2_DEBUG_CONSOLE qingkev2_debug_console.c)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# Console driver configuration options
|
||||
#Console driver configuration options
|
||||
|
||||
# Copyright (c) 2014-2015 Wind River Systems, Inc.
|
||||
# Copyright (c) 2016 Cadence Design Systems, Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#Copyright(c) 2014 - 2015 Wind River Systems, Inc.
|
||||
#Copyright(c) 2016 Cadence Design Systems, Inc.
|
||||
#SPDX - License - Identifier : Apache - 2.0
|
||||
|
||||
# Setting shared by different subsystems
|
||||
#Setting shared by different subsystems
|
||||
|
||||
menuconfig CONSOLE
|
||||
bool "Console drivers"
|
||||
|
@ -284,4 +284,13 @@ config WINSTREAM_CONSOLE
|
|||
|
||||
See the WINSTREAM Kconfig help for more information.
|
||||
|
||||
config QINGKEV2_DEBUG_CONSOLE
|
||||
bool "Use the QingKeV2 debug module as a console"
|
||||
depends on DT_HAS_WCH_QINGKEV2_DEBUG_ENABLED
|
||||
select CONSOLE_HAS_DRIVER
|
||||
help
|
||||
Emit console messages to the host via the debug unit which can be displayed
|
||||
using `minichlink`. Text is buffered and, in the worst case, flushed when a
|
||||
space or ASCII control character is printed.
|
||||
|
||||
endif # CONSOLE
|
||||
|
|
114
drivers/console/qingkev2_debug_console.c
Normal file
114
drivers/console/qingkev2_debug_console.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Google LLC.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT wch_qingkev2_debug
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/winstream.h>
|
||||
|
||||
#define TX_TIMEOUT 1000000
|
||||
#define TX_FULL BIT(7)
|
||||
#define TX_VALID BIT(2)
|
||||
#define TX_SIZE_MASK 0x03
|
||||
|
||||
/*
|
||||
* Tracks if the host has been detected. Used to prevent spinning when the console is enabled but
|
||||
* the host is not connected.
|
||||
*/
|
||||
enum qingkev2_debug_state {
|
||||
QINGKEV2_DEBUG_STATE_INITIAL,
|
||||
/* The first buffer has been written to the host. */
|
||||
QINGKEV2_DEBUG_STATE_FIRST,
|
||||
/* The first buffer was acknowledged by the host. */
|
||||
QINGKEV2_DEBUG_STATE_ESTABLISHED,
|
||||
/* Timeout while trying to send the second buffer to the host. */
|
||||
QINGKEV2_DEBUG_STATE_MISSING,
|
||||
};
|
||||
|
||||
struct qingkeyv2_debug_regs {
|
||||
uint32_t unused;
|
||||
uint32_t data0;
|
||||
uint32_t data1;
|
||||
};
|
||||
|
||||
struct qingkeyv2_debug_data {
|
||||
/* Encoded text to be written to the host. */
|
||||
uint32_t buffer;
|
||||
enum qingkev2_debug_state state;
|
||||
};
|
||||
|
||||
static struct qingkeyv2_debug_data qingkeyv2_debug_data_0;
|
||||
|
||||
#if defined(CONFIG_STDOUT_CONSOLE)
|
||||
extern void __stdout_hook_install(int (*hook)(int));
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PRINTK)
|
||||
extern void __printk_hook_install(int (*fn)(int));
|
||||
#endif
|
||||
|
||||
static int qingkev2_debug_console_putc(int ch)
|
||||
{
|
||||
/*
|
||||
* `minichlink` encodes the incoming and outgoing characters into `data0`. The least
|
||||
* significant byte is used for control and length, while the upper bytes are used for
|
||||
* up to three characters. The `TX_FULL` bit is used to pass ownership back and forth
|
||||
* between the host and device.
|
||||
*/
|
||||
volatile struct qingkeyv2_debug_regs *regs =
|
||||
(volatile struct qingkeyv2_debug_regs *)DT_INST_REG_ADDR(0);
|
||||
struct qingkeyv2_debug_data *data = &qingkeyv2_debug_data_0;
|
||||
int count = data->buffer & TX_SIZE_MASK;
|
||||
int timeout;
|
||||
|
||||
data->buffer |= (ch << (++count * 8));
|
||||
data->buffer++;
|
||||
|
||||
/*
|
||||
* Try to send if the buffer is full, or the character is a space or a control character, or
|
||||
* the host is ready.
|
||||
*/
|
||||
if (count == 3 || ch <= ' ' || (regs->data0 & TX_FULL) == 0) {
|
||||
if (data->state != QINGKEV2_DEBUG_STATE_MISSING) {
|
||||
for (timeout = 0; timeout != TX_TIMEOUT && (regs->data0 & TX_FULL) != 0;
|
||||
timeout++) {
|
||||
}
|
||||
}
|
||||
if ((regs->data0 & TX_FULL) == 0) {
|
||||
regs->data0 = data->buffer | TX_FULL | TX_VALID;
|
||||
if (data->state < QINGKEV2_DEBUG_STATE_ESTABLISHED) {
|
||||
/* Transitions from INITIAL -> FIRST -> ESTABLISHED */
|
||||
data->state++;
|
||||
} else if (data->state == QINGKEV2_DEBUG_STATE_MISSING) {
|
||||
/* The host has caught up. */
|
||||
data->state = QINGKEV2_DEBUG_STATE_ESTABLISHED;
|
||||
}
|
||||
} else {
|
||||
if (data->state == QINGKEV2_DEBUG_STATE_FIRST) {
|
||||
data->state = QINGKEV2_DEBUG_STATE_MISSING;
|
||||
}
|
||||
}
|
||||
data->buffer = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int qingkev2_debug_console_init(void)
|
||||
{
|
||||
#if defined(CONFIG_STDOUT_CONSOLE)
|
||||
__stdout_hook_install(qingkev2_debug_console_putc);
|
||||
#endif
|
||||
#if defined(CONFIG_PRINTK)
|
||||
__printk_hook_install(qingkev2_debug_console_putc);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(qingkev2_debug_console_init, PRE_KERNEL_1, CONFIG_CONSOLE_INIT_PRIORITY);
|
30
dts/bindings/adc/wch,adc.yaml
Normal file
30
dts/bindings/adc/wch,adc.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2024 Google LLC.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: WCH CH32V00x Analog-to-digital Converter (ADC)
|
||||
|
||||
compatible: "wch,adc"
|
||||
|
||||
include: adc-controller.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
clocks:
|
||||
required: true
|
||||
|
||||
vref-mv:
|
||||
type: int
|
||||
default: 3300
|
||||
description: |
|
||||
Reference voltage of in mV. This is typically VDD.
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
||||
io-channel-cells:
|
||||
- input
|
8
dts/bindings/debug/wch,qingkev2-debug.yaml
Normal file
8
dts/bindings/debug/wch,qingkev2-debug.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2024 Google LLC.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: WCH QingKeV2 Debug
|
||||
|
||||
compatible: "wch,qingkev2-debug"
|
||||
|
||||
include: base.yaml
|
|
@ -30,6 +30,11 @@
|
|||
compatible = "simple-bus";
|
||||
ranges;
|
||||
|
||||
debug0: debug@e00000f0 {
|
||||
compatible = "wch,qingkev2-debug";
|
||||
reg = <0xe00000f0 0x40>;
|
||||
};
|
||||
|
||||
sram0: memory@20000000 {
|
||||
compatible = "mmio-sram";
|
||||
reg = <0x20000000 0x800>;
|
||||
|
@ -105,6 +110,15 @@
|
|||
};
|
||||
};
|
||||
|
||||
adc1: adc@40012400 {
|
||||
compatible = "wch,adc";
|
||||
reg = <0x40012400 16>;
|
||||
clocks = <&rcc CH32V00X_CLOCK_ADC1>;
|
||||
interrupt-parent = <&pfic>;
|
||||
interrupts = <29>;
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
|
||||
usart1: uart@40013800 {
|
||||
compatible = "wch,usart";
|
||||
reg = <0x40013800 16>;
|
||||
|
|
16
modules/hal_wch/ch32_adc.h
Normal file
16
modules/hal_wch/ch32_adc.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Google LLC.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _CH32_ADC_H
|
||||
#define _CH32_ADC_H
|
||||
|
||||
#ifdef CONFIG_SOC_CH32V003
|
||||
#include <ch32v003fun.h>
|
||||
#else
|
||||
#error "SoC not supported!"
|
||||
#endif
|
||||
|
||||
#endif
|
37
samples/drivers/adc/adc_dt/boards/wch_ch32v003evt.overlay
Normal file
37
samples/drivers/adc/adc_dt/boards/wch_ch32v003evt.overlay
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Google LLC.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/adc/adc.h>
|
||||
|
||||
/ {
|
||||
zephyr,user {
|
||||
/* Vcal (~1.20 V) and 1/2 Vref (~1.65 V). */
|
||||
io-channels = <&adc1 8 &adc1 9>;
|
||||
};
|
||||
};
|
||||
|
||||
&adc1 {
|
||||
status = "okay";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
vcal@8 {
|
||||
reg = <8>;
|
||||
zephyr,gain = "ADC_GAIN_1";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
|
||||
vref_div_2@9 {
|
||||
reg = <9>;
|
||||
zephyr,gain = "ADC_GAIN_1";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
|
@ -35,6 +35,7 @@ tests:
|
|||
- frdm_mcxn947/mcxn947/cpu0
|
||||
- frdm_mcxc242
|
||||
- ucans32k1sic
|
||||
- wch_ch32v003evt
|
||||
integration_platforms:
|
||||
- nucleo_l073rz
|
||||
- nrf52840dk/nrf52840
|
||||
|
|
Loading…
Reference in a new issue