From c119a975616f5e2c2fac8193e35c443b0b00311a Mon Sep 17 00:00:00 2001 From: dechao_gong Date: Mon, 20 Jul 2026 16:34:17 +0800 Subject: [PATCH 1/3] arch/arm/rtl8721dx: add shared Ameba ADC driver Add a NuttX ADC lower-half for the Ameba SoC family, wired for the amebadplus/pke8721daf as /dev/adc0. The driver uses the fwlib ROM API and drives on-demand polled conversions via ADC_ReceiveBuf with the hardware auto channel-switch FIFO, because amebadplus disables the ADC software-trigger path. Per-chip wiring (channel count, pinmux function id, APB clock bits, optional aux clock) lives in a chip header so a new IC only supplies its own values without touching the shared driver. Reports raw conversion codes per the NuttX convention (12-bit effective, 0..~3876 for 0..3.3V), consistent with the STM32/i.MXRT/ Tiva ADC drivers. Verified on hardware: 0V->121, 3.3V->3876 on CH0(PB19) while CH1(PB18) held steady, confirming sampling, full-scale and multi-channel switch-list isolation. Signed-off-by: dechao_gong Assisted-by: Claude --- .../arm/rtl8721dx/boards/pke8721daf/index.rst | 17 + arch/arm/src/common/ameba/Kconfig | 17 + arch/arm/src/common/ameba/ameba_adc.c | 495 ++++++++++++++++++ arch/arm/src/common/ameba/ameba_adc.h | 100 ++++ arch/arm/src/rtl8721dx/CMakeLists.txt | 4 + arch/arm/src/rtl8721dx/Make.defs | 4 + arch/arm/src/rtl8721dx/ameba_adc_chip.h | 103 ++++ .../pke8721daf/configs/adc/defconfig | 54 ++ .../rtl8721dx/pke8721daf/src/CMakeLists.txt | 7 +- boards/arm/rtl8721dx/pke8721daf/src/Makefile | 9 + .../rtl8721dx/pke8721daf/src/rtl8721dx_adc.c | 90 ++++ .../pke8721daf/src/rtl8721dx_bringup.c | 10 + .../pke8721daf/src/rtl8721dx_pke8721daf.h | 13 + 13 files changed, 922 insertions(+), 1 deletion(-) create mode 100644 arch/arm/src/common/ameba/ameba_adc.c create mode 100644 arch/arm/src/common/ameba/ameba_adc.h create mode 100644 arch/arm/src/rtl8721dx/ameba_adc_chip.h create mode 100644 boards/arm/rtl8721dx/pke8721daf/configs/adc/defconfig create mode 100644 boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_adc.c diff --git a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst index 14554d6ddae16..3861744e7a2c7 100644 --- a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst +++ b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst @@ -43,6 +43,8 @@ Supported in this NuttX port: on the SDK fwlib register layer * PWM output exposed as a ``/dev/pwm0`` character device, driven directly on the SDK fwlib timer register layer +* ADC channels exposed as an ``/dev/adc0`` character device, driven directly + on the SDK fwlib register layer Buttons and LEDs ================ @@ -153,6 +155,21 @@ example:: nsh> pwm -d 25 -f 1000 # 1 kHz, 25% duty on /dev/pwm0 +adc +--- + +Minimal NSH with the ADC driver and the ``adc`` example enabled (no Wi-Fi). +The board registers its channels from a table (see +``boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_adc.c``): ``/dev/adc0`` samples +CH0 on PB19 and CH1 on PB18. Edit that table -- channel numbers and the analog +pad each is wired to -- to match a board's wiring; the external channels +CH0..CH6 map to pads PB19..PB13 and are muxed to the ADC function through the +SDK ROM, while internal channels carry ``AMEBA_ADC_PIN_NC``. Every listed +channel is sampled, in order, on each trigger. Read the channels with the +example:: + + nsh> adc -n 1 # one sweep of /dev/adc0 + Wi-Fi ===== diff --git a/arch/arm/src/common/ameba/Kconfig b/arch/arm/src/common/ameba/Kconfig index 217d4d2232e41..647750224486e 100644 --- a/arch/arm/src/common/ameba/Kconfig +++ b/arch/arm/src/common/ameba/Kconfig @@ -100,4 +100,21 @@ config AMEBA_PWM The driver (arch/arm/src/common/ameba/ameba_pwm.c) sits on the SDK fwlib RTIM register layer and runs in polling task context. +config AMEBA_ADC + bool "ADC" + default n + select ANALOG + select ADC + ---help--- + Expose the Ameba SAR ADC as a NuttX ADC input at /dev/adc0. The + converter has one channel-switch list; the board selects which + channels are sampled and which analog pad each external channel is + wired to in its bring-up code. Every channel is sampled, in list + order, on each ANIOC_TRIGGER. + + The driver (arch/arm/src/common/ameba/ameba_adc.c) sits on the SDK + fwlib register layer and reads the auto channel-switch FIFO by + polling in task context (the chip does not enable a hardware + software-trigger path). + endmenu # Ameba Peripheral Support diff --git a/arch/arm/src/common/ameba/ameba_adc.c b/arch/arm/src/common/ameba/ameba_adc.c new file mode 100644 index 0000000000000..9ba17598e2635 --- /dev/null +++ b/arch/arm/src/common/ameba/ameba_adc.c @@ -0,0 +1,495 @@ +/**************************************************************************** + * arch/arm/src/common/ameba/ameba_adc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* NuttX ADC lower half for the Realtek Ameba ADC. The chip has one SAR + * converter fed by a channel-switch list; the board says which channels to + * sample and which analog pad each external channel is wired to, and this + * driver exposes them as one /dev/adc0 device. Every ANIOC_TRIGGER samples + * the whole list, in list order, and hands each raw conversion value to the + * ADC upper half tagged with the hardware channel number the sample came + * from. + * + * The converter is driven through the SDK fwlib ADC API. Those routines are + * marked _LONG_CALL_ but are linked from the fwlib RAM source ameba_adc.c + * (already compiled into every image for the clock/brown-out calibration), + * so no extra source is added to the board build for the ADC. The fwlib API + * takes no register base: it selects the secure or non-secure ADC alias + * itself with TrustZone_IsSecure(), so this driver never touches a base + * address. + * + * On amebadplus the SDK deliberately does not enable the ADC hardware + * software-trigger path (ADC_SWTrigCmd() is compiled out and only logs a + * "not supported" note). The supported on-demand read is a bounded burst of + * the auto channel-switch FIFO: ADC_ReceiveBuf() clears the FIFO, enables + * the auto channel switch, reads exactly the requested number of words and + * disables it again. Reading one word per listed channel therefore yields a + * single polled sweep of the list -- the on-demand, no-interrupt behaviour a + * software trigger would give -- which is what the ANIOC_TRIGGER handler + * does. Everything runs in task context; the ADC_IRQ line is unused. + * + * The chip-specific wiring (channel count, list depth, pad-mux code, clock + * masks) lives in the per-chip ameba_adc_chip.h. To keep the vendor headers + * out of the NuttX include world the few fwlib symbols and constants used + * here are declared locally rather than pulled in from . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ameba_adc.h" +#include "ameba_adc_chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Second/third argument to fwlib "state" style APIs. */ + +#define AMEBA_DISABLE 0x0 +#define AMEBA_ENABLE 0x1 + +/* fwlib ADC operation mode and pad values (from ameba_adc.h / ameba_gpio.h). + * The auto channel-switch mode is the one ADC_ReceiveBuf() drives; the pad + * is left un-pulled and its digital input buffer is disabled so the analog + * front end sees a clean input. + */ + +#define AMEBA_ADC_AUTO_MODE 0x1 /* ADC_AUTO_MODE */ +#define AMEBA_ADC_CLKDIV_24 0x3 /* ADC_CLK_DIV_24 */ +#define AMEBA_PAD_NOPULL 0x0 /* GPIO_PuPd_NOPULL */ + +/* 16-bit conversion word layout: channel id in [19:16], data in [15:0]. */ + +#define AMEBA_ADC_GET_CHNO(x) (((x) >> 16) & 0x0f) +#define AMEBA_ADC_GET_DATA(x) ((x) & 0xffff) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ameba_adc_dev_s +{ + const struct adc_callback_s *cb; /* Upper-half receive callback */ + uint8_t nchan; /* Channels in the switch list */ + bool enabled; /* Converter is powered/running */ + + /* Hardware channel number sampled at each list position. */ + + uint8_t chanlist[AMEBA_ADC_MAXLIST]; + + /* Analog pad per list position, AMEBA_ADC_PIN_NC for internal channels. */ + + uint8_t pins[AMEBA_ADC_MAXLIST]; +}; + +/* Layout-compatible mirror of the fwlib ADC_InitTypeDef. The driver only + * ever writes the leading opmode/cvlistlen/cvlist[16] fields (offsets + * 0..17), plus -- on amebadplus only -- the ClkDiv byte at offset 18. That + * prefix is identical on every current Ameba chip, so those writes always + * land on the right fields. + * + * Everything past offset 18 is deliberately NOT named here: the tail order + * differs per chip (amebadplus is ClkDiv/RxThresholdLevel/SpecialCh/ + * ChanInType at 28B, amebagreen2 has RxThresholdLevel/SpecialCh/ + * SamplePeriodUs at 22B, RTL8720F adds an ADC_Chan[8] sub-struct at 52B), + * and giving those bytes amebadplus-specific names would only be correct on + * one chip and misleading on the others. The driver never reads or writes + * them -- it lets ADC_StructInit() fill the WHOLE per-chip struct with the + * correct defaults for whichever fwlib is linked, then overwrites just the + * prefix (and ClkDiv). + * + * The reserved[] tail exists solely so this stack object is at least as + * large as the linked chip's real ADC_InitTypeDef -- otherwise + * ADC_StructInit(), which writes the whole struct, would overflow the + * stack. Each chip header supplies the real sizeof through + * AMEBA_ADC_INIT_SIZE and the static assertion below fails at compile time + * if a new port forgets it or under-sizes it. ClkDiv sits at a fixed + * offset 18 on amebadplus, so it stays named; the reserved run starts right + * after the named prefix. + */ + +/* Byte span of the named prefix (opmode + cvlistlen + cvlist[16] + clkdiv). + * Single source of truth for where the reserved tail begins; the offsetof + * assertion below verifies the compiler laid the prefix out with no padding + * so this really is the prefix size. + */ + +#define AMEBA_ADC_PREFIX_SIZE (1 + 1 + 16 + 1) + +struct ameba_adc_init_s +{ + uint8_t opmode; /* ADC_OpMode */ + uint8_t cvlistlen; /* ADC_CvlistLen */ + uint8_t cvlist[16]; /* ADC_Cvlist[16] */ + uint8_t clkdiv; /* ADC_ClkDiv (amebadplus only, @18) */ + + /* Chip-specific tail (RxThreshold/SpecialCh/... ) -- filled by + * ADC_StructInit(), never touched by this driver. Sized so the whole + * mirror is >= the linked chip's ADC_InitTypeDef; ternary keeps it + * non-empty when a chip's struct is <= the prefix. + */ + + uint8_t reserved[AMEBA_ADC_INIT_SIZE > AMEBA_ADC_PREFIX_SIZE ? + AMEBA_ADC_INIT_SIZE - AMEBA_ADC_PREFIX_SIZE : 1]; +}; + +static_assert(offsetof(struct ameba_adc_init_s, reserved) == + AMEBA_ADC_PREFIX_SIZE, + "ameba_adc_init_s prefix has unexpected padding"); + +static_assert(sizeof(struct ameba_adc_init_s) >= AMEBA_ADC_INIT_SIZE, + "ameba_adc_init_s smaller than the fwlib ADC_InitTypeDef"); + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* SDK fwlib ADC / pin / clock API (linked from fwlib RAM ameba_adc.c and the + * ROM pinmux table). None of the ADC calls take a register base -- the + * fwlib picks the secure/non-secure alias internally. + */ + +extern void RCC_PeriphClockCmd(uint32_t periph, uint32_t clock, + uint8_t newstate); +extern void Pinmux_Config(uint8_t pin, uint32_t func); +extern void PAD_PullCtrl(uint8_t pin, uint8_t pulltype); +extern void PAD_SleepPullCtrl(uint8_t pin, uint8_t pulltype); +extern void PAD_InputCtrl(uint8_t pin, uint32_t newstate); +extern void ADC_StructInit(struct ameba_adc_init_s *init); +extern void ADC_Init(struct ameba_adc_init_s *init); +extern void ADC_Cmd(uint32_t newstate); +extern void ADC_ReceiveBuf(uint32_t *buf, uint32_t len); + +/* ADC lower-half operations. */ + +static int ameba_adc_bind(struct adc_dev_s *dev, + const struct adc_callback_s *callback); +static void ameba_adc_reset(struct adc_dev_s *dev); +static int ameba_adc_setup(struct adc_dev_s *dev); +static void ameba_adc_shutdown(struct adc_dev_s *dev); +static void ameba_adc_rxint(struct adc_dev_s *dev, bool enable); +static int ameba_adc_ioctl(struct adc_dev_s *dev, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct adc_ops_s g_ameba_adc_ops = +{ + .ao_bind = ameba_adc_bind, + .ao_reset = ameba_adc_reset, + .ao_setup = ameba_adc_setup, + .ao_shutdown = ameba_adc_shutdown, + .ao_rxint = ameba_adc_rxint, + .ao_ioctl = ameba_adc_ioctl, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ameba_adc_hwinit + * + * Description: + * Program the converter's channel-switch list for auto channel-switch + * reads. ADC_Init() itself loads the list from the init struct, so the + * whole board list is applied in one call; the converter is left disabled + * (setup() powers it on). + * + ****************************************************************************/ + +static void ameba_adc_hwinit(struct ameba_adc_dev_s *priv) +{ + struct ameba_adc_init_s init; + int i; + + memset(&init, 0, sizeof(init)); + ADC_StructInit(&init); + + init.opmode = AMEBA_ADC_AUTO_MODE; + init.cvlistlen = (uint8_t)(priv->nchan - 1); + + /* ClkDiv exists at offset 18 only on amebadplus; on other chips that + * byte is RxThresholdLevel, so writing it would corrupt an unrelated + * field. Guard with the chip macro and keep the SDK default elsewhere. + */ + +#ifdef AMEBA_ADC_HAS_CLKDIV + init.clkdiv = AMEBA_ADC_CLKDIV_24; +#endif + + for (i = 0; i < priv->nchan; i++) + { + init.cvlist[i] = priv->chanlist[i]; + } + + ADC_Init(&init); +} + +/**************************************************************************** + * Name: ameba_adc_bind + ****************************************************************************/ + +static int ameba_adc_bind(struct adc_dev_s *dev, + const struct adc_callback_s *callback) +{ + struct ameba_adc_dev_s *priv = (struct ameba_adc_dev_s *)dev->ad_priv; + + priv->cb = callback; + return OK; +} + +/**************************************************************************** + * Name: ameba_adc_reset + * + * Description: + * Reload the channel-switch list; called before setup() and on error + * recovery. The peripheral clock and pads are latched at registration. + * + ****************************************************************************/ + +static void ameba_adc_reset(struct adc_dev_s *dev) +{ + struct ameba_adc_dev_s *priv = (struct ameba_adc_dev_s *)dev->ad_priv; + + ameba_adc_hwinit(priv); +} + +/**************************************************************************** + * Name: ameba_adc_setup + * + * Description: + * Power the converter on when the device is first opened. + * + ****************************************************************************/ + +static int ameba_adc_setup(struct adc_dev_s *dev) +{ + struct ameba_adc_dev_s *priv = (struct ameba_adc_dev_s *)dev->ad_priv; + + if (!priv->enabled) + { + ADC_Cmd(AMEBA_ENABLE); + priv->enabled = true; + } + + return OK; +} + +/**************************************************************************** + * Name: ameba_adc_shutdown + * + * Description: + * Power the converter off when the device is closed. + * + ****************************************************************************/ + +static void ameba_adc_shutdown(struct adc_dev_s *dev) +{ + struct ameba_adc_dev_s *priv = (struct ameba_adc_dev_s *)dev->ad_priv; + + if (priv->enabled) + { + ADC_Cmd(AMEBA_DISABLE); + priv->enabled = false; + } +} + +/**************************************************************************** + * Name: ameba_adc_rxint + * + * Description: + * No-op: this driver reads by polling the auto channel-switch FIFO on + * demand, so there are no RX interrupts to enable or disable. + * + ****************************************************************************/ + +static void ameba_adc_rxint(struct adc_dev_s *dev, bool enable) +{ + UNUSED(dev); + UNUSED(enable); +} + +/**************************************************************************** + * Name: ameba_adc_trigger + * + * Description: + * Sample the whole channel list once and push each raw value to the upper + * half. ADC_ReceiveBuf() reads one word per listed channel, in list + * order, through the auto channel-switch FIFO; the channel number is taken + * from each word so the sample is tagged with the channel it came from. + * + ****************************************************************************/ + +static int ameba_adc_trigger(struct adc_dev_s *dev) +{ + struct ameba_adc_dev_s *priv = (struct ameba_adc_dev_s *)dev->ad_priv; + uint32_t buf[AMEBA_ADC_MAXLIST]; + int i; + + if (priv->cb == NULL || priv->cb->au_receive == NULL) + { + return -EINVAL; + } + + ADC_ReceiveBuf(buf, priv->nchan); + + for (i = 0; i < priv->nchan; i++) + { + uint8_t ch = (uint8_t)AMEBA_ADC_GET_CHNO(buf[i]); + int32_t data = (int32_t)AMEBA_ADC_GET_DATA(buf[i]); + + priv->cb->au_receive(dev, ch, data); + } + + return OK; +} + +/**************************************************************************** + * Name: ameba_adc_ioctl + ****************************************************************************/ + +static int ameba_adc_ioctl(struct adc_dev_s *dev, int cmd, + unsigned long arg) +{ + struct ameba_adc_dev_s *priv = (struct ameba_adc_dev_s *)dev->ad_priv; + int ret; + + switch (cmd) + { + case ANIOC_TRIGGER: + ret = ameba_adc_trigger(dev); + break; + + case ANIOC_GET_NCHANNELS: + ret = priv->nchan; + break; + + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ameba_adc_register + * + * Description: + * See ameba_adc.h. + * + ****************************************************************************/ + +int ameba_adc_register(const char *path, const uint8_t *channels, + const uint8_t *pins, unsigned int nchan) +{ + struct ameba_adc_dev_s *priv; + struct adc_dev_s *dev; + unsigned int i; + int ret; + + if (nchan == 0) + { + return -EINVAL; + } + + if (nchan > AMEBA_ADC_MAXLIST) + { + nchan = AMEBA_ADC_MAXLIST; + } + + dev = kmm_zalloc(sizeof(struct adc_dev_s) + + sizeof(struct ameba_adc_dev_s)); + if (dev == NULL) + { + return -ENOMEM; + } + + priv = (struct ameba_adc_dev_s *)(dev + 1); + dev->ad_ops = &g_ameba_adc_ops; + dev->ad_priv = priv; + + priv->nchan = (uint8_t)nchan; + memset(priv->pins, AMEBA_ADC_PIN_NC, sizeof(priv->pins)); + + /* Gate the ADC peripheral clock (plus the cap-touch/CTC clock domain on + * the chips whose header defines it) and turn each external channel's pad + * into an analog input: route it to the ADC crossbar function, drop its + * pulls and disable its digital input buffer. Internal channels have no + * pad and are skipped. + */ + + RCC_PeriphClockCmd(AMEBA_ADC_APBPERIPH, AMEBA_ADC_APBPERIPH_CLK, + AMEBA_ENABLE); +#ifdef AMEBA_ADC_AUXCLK_PERIPH + RCC_PeriphClockCmd(AMEBA_ADC_AUXCLK_PERIPH, AMEBA_ADC_AUXCLK_CLK, + AMEBA_ENABLE); +#endif + + for (i = 0; i < nchan; i++) + { + priv->chanlist[i] = channels[i]; + priv->pins[i] = pins[i]; + + if (pins[i] != AMEBA_ADC_PIN_NC) + { + Pinmux_Config(pins[i], AMEBA_ADC_PINMUX_FID); + PAD_PullCtrl(pins[i], AMEBA_PAD_NOPULL); + PAD_SleepPullCtrl(pins[i], AMEBA_PAD_NOPULL); + PAD_InputCtrl(pins[i], AMEBA_DISABLE); + } + } + + ameba_adc_hwinit(priv); + + ret = adc_register(path, dev); + if (ret < 0) + { + aerr("ERROR: adc_register(%s) failed: %d\n", path, ret); + kmm_free(dev); + return ret; + } + + return OK; +} diff --git a/arch/arm/src/common/ameba/ameba_adc.h b/arch/arm/src/common/ameba/ameba_adc.h new file mode 100644 index 0000000000000..04352a13f5d90 --- /dev/null +++ b/arch/arm/src/common/ameba/ameba_adc.h @@ -0,0 +1,100 @@ +/**************************************************************************** + * arch/arm/src/common/ameba/ameba_adc.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_ADC_H +#define __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_ADC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The Ameba ADC is a single converter with one channel-switch list: the + * board hands ameba_adc_register() a set of channels to sample and, for each + * external channel, the analog pad it is wired to. A single /dev/adc0 + * device then samples the whole list on every ANIOC_TRIGGER (the SDK does + * not enable the hardware software-trigger path on this chip, so the driver + * reads one sweep of the list through the auto channel-switch FIFO -- see + * ameba_adc.c). + * + * Channels the board wires to an external pad give that pad here (encoded + * with the AMEBA_PA()/AMEBA_PB() PinName codes the GPIO driver uses); the + * chip's internal channels (temperature, VBAT, ...) have no pad and carry + * AMEBA_ADC_PIN_NC. + */ + +/* Sentinel pad code for an internal ADC channel that has no analog pad. */ + +#define AMEBA_ADC_PIN_NC 0xff + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: ameba_adc_register + * + * Description: + * Configure the Ameba ADC for a list of channels and register it with the + * NuttX ADC character driver at the given path (typically "/dev/adc0"). + * Every channel in the list is sampled, in list order, on each + * ANIOC_TRIGGER; the raw conversion value of each is delivered to the + * upper half tagged with the hardware channel number. + * + * Input Parameters: + * path - The device path to register, e.g. "/dev/adc0". + * channels - Array of hardware ADC channel numbers to sample. + * pins - Array giving, for each channel, the analog pad it is wired to + * (encoded with AMEBA_PA()/AMEBA_PB()), or AMEBA_ADC_PIN_NC for + * an internal channel that has no pad. + * nchan - Number of entries in channels/pins (clamped to the + * converter's channel-list length). + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int ameba_adc_register(const char *path, const uint8_t *channels, + const uint8_t *pins, unsigned int nchan); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_ADC_H */ diff --git a/arch/arm/src/rtl8721dx/CMakeLists.txt b/arch/arm/src/rtl8721dx/CMakeLists.txt index d480f2b107430..71b7f7203a9e9 100644 --- a/arch/arm/src/rtl8721dx/CMakeLists.txt +++ b/arch/arm/src/rtl8721dx/CMakeLists.txt @@ -60,6 +60,10 @@ if(CONFIG_AMEBA_PWM) list(APPEND SRCS ${AMEBA_COMMON}/ameba_pwm.c) endif() +if(CONFIG_AMEBA_ADC) + list(APPEND SRCS ${AMEBA_COMMON}/ameba_adc.c) +endif() + target_include_directories(arch PRIVATE ${AMEBA_COMMON}) target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/arm/src/rtl8721dx/Make.defs b/arch/arm/src/rtl8721dx/Make.defs index 64adf3980e4e7..60ebeface1d5c 100644 --- a/arch/arm/src/rtl8721dx/Make.defs +++ b/arch/arm/src/rtl8721dx/Make.defs @@ -70,6 +70,10 @@ ifeq ($(CONFIG_AMEBA_PWM),y) CHIP_CSRCS += ameba_pwm.c endif +ifeq ($(CONFIG_AMEBA_ADC),y) +CHIP_CSRCS += ameba_adc.c +endif + ############################################################################ # Realtek RTL8721Dx SDK integration # diff --git a/arch/arm/src/rtl8721dx/ameba_adc_chip.h b/arch/arm/src/rtl8721dx/ameba_adc_chip.h new file mode 100644 index 0000000000000..47a456f0d6435 --- /dev/null +++ b/arch/arm/src/rtl8721dx/ameba_adc_chip.h @@ -0,0 +1,103 @@ +/**************************************************************************** + * arch/arm/src/rtl8721dx/ameba_adc_chip.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_RTL8721DX_AMEBA_ADC_CHIP_H +#define __ARCH_ARM_SRC_RTL8721DX_AMEBA_ADC_CHIP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Per-chip ADC wiring for RTL8721DX (amebadplus). The shared driver + * (arch/arm/src/common/ameba/ameba_adc.c) includes this header to learn how + * many channels the converter can list, the crossbar pad-mux code that turns + * a pad into an analog input, and the peripheral-clock masks. A port to + * another Ameba chip supplies a same-named header on the chip include path; + * the shared driver is never edited -- it reads only the macros below, and + * it drives the ADC through the SDK fwlib API, which internally selects the + * secure/non-secure register alias (via TrustZone_IsSecure()), so no + * register base appears here at all. + * + * What differs per chip, from the SDK fwlib headers and the vendor + * example_adc_ext.h (verified, not guessed): + * + * chip pad-mux code for analog in extra clock to gate + * ----------- ------------------------------ ------------------- + * amebadplus PINMUX_FUNCTION_ADC = 6 (none) + * amebalite PINMUX_FUNCTION_AUXIN APBPeriph_CTC + * amebasmart PINMUX_FUNCTION_CAPTOUCH APBPeriph_CTC + * amebagreen2 PINMUX_FUNCTION_ADC (none) + * RTL8720F PINMUX_FUNCTION_ADC (none) + * + * Both differences are expressed as macros so a new chip only edits this + * header: AMEBA_ADC_PINMUX_FID carries the pad-mux code, and a chip that + * needs a second clock domain (amebalite/amebasmart route the ADC through + * the cap-touch/CTC block) also defines AMEBA_ADC_AUXCLK_PERIPH / _CLK, + * which the driver gates only when present. amebadplus needs neither, so + * it leaves those two undefined -- see the SPI/I2C/PWM chip headers for the + * same data-driven, never-computed pattern. + * + * External channels CH0..CH6 map to pads PB19,PB18,PB17,PB16,PB15,PB14,PB13 + * (from ameba_adc.h ADC_CHx_PIN); CH7..CH10 are fixed internal channels with + * no pad. The 16-bit conversion word (channel id in [19:16], data in + * [15:0]) is identical on every current Ameba chip, so the driver extracts + * it directly and no macro is needed here. + */ + +#define AMEBA_ADC_NCHAN 11 /* ADC_CH_NUM: CH0..CH6 ext, 7..10 int */ +#define AMEBA_ADC_MAXLIST 16 /* Channel-switch list depth (Cvlist) */ +#define AMEBA_ADC_PINMUX_FID 6 /* PINMUX_FUNCTION_ADC */ +#define AMEBA_ADC_IRQ 47 /* ADC_IRQ (unused by this polling drv) */ + +/* sizeof(fwlib ADC_InitTypeDef): OpMode/CvlistLen/Cvlist[16] then ClkDiv/ + * RxThresholdLevel/SpecialCh/ChanInType(u32) -> 28 bytes. The shared driver + * sizes its stack mirror from this so ADC_StructInit() cannot overflow it. + */ + +#define AMEBA_ADC_INIT_SIZE 28 + +/* APBPeriph_ADC (function) and APBPeriph_ADC_CLOCK masks. Equal on this + * chip; both set the bit30 group selector and bit23 for the LP ADC block. + * amebadplus needs no second clock domain, so AMEBA_ADC_AUXCLK_* are left + * undefined (amebalite/amebasmart define them to gate APBPeriph_CTC too). + */ + +#define AMEBA_ADC_APBPERIPH (((uint32_t)1 << 30) | ((uint32_t)1 << 23)) +#define AMEBA_ADC_APBPERIPH_CLK (((uint32_t)1 << 30) | ((uint32_t)1 << 23)) + +/* amebadplus ADC_InitTypeDef has a ClkDiv field at offset 18; the other + * Ameba chips (amebagreen2, RTL8720F) do not -- their offset 18 is + * RxThresholdLevel. The shared driver guards the ClkDiv write with this + * macro so it only runs on chips that actually have the field. + */ + +#define AMEBA_ADC_HAS_CLKDIV 1 + +#endif /* __ARCH_ARM_SRC_RTL8721DX_AMEBA_ADC_CHIP_H */ diff --git a/boards/arm/rtl8721dx/pke8721daf/configs/adc/defconfig b/boards/arm/rtl8721dx/pke8721daf/configs/adc/defconfig new file mode 100644 index 0000000000000..18b175ba638e5 --- /dev/null +++ b/boards/arm/rtl8721dx/pke8721daf/configs/adc/defconfig @@ -0,0 +1,54 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_DEBUG_WARN is not set +CONFIG_AMEBA_ADC=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="pke8721daf" +CONFIG_ARCH_BOARD_PKE8721DAF=y +CONFIG_ARCH_CHIP="rtl8721dx" +CONFIG_ARCH_CHIP_RTL8721DX=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV8M_SYSTICK=y +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_TASK_STACKSIZE=4096 +CONFIG_EXAMPLES_ADC=y +CONFIG_EXAMPLES_ADC_GROUPSIZE=2 +CONFIG_EXAMPLES_ADC_NSAMPLES=1 +CONFIG_EXAMPLES_ADC_SWTRIG=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_FS_PROCFS=y +CONFIG_FS_TMPFS=y +CONFIG_IDLETHREAD_STACKSIZE=4096 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_LIBC_MEMFD_ERROR=y +CONFIG_MM_DEFAULT_ALIGNMENT=32 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=294912 +CONFIG_RAM_START=0x20020000 +CONFIG_RR_INTERVAL=200 +CONFIG_RTL8721DX_FLASH_FS=y +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=192 +CONFIG_SCHED_LPWORK=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=16 +CONFIG_START_MONTH=6 +CONFIG_START_YEAR=2026 +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_NSH_STACKSIZE=2500 +CONFIG_TIMER=y +CONFIG_TIMER_ARCH=y +CONFIG_USEC_PER_TICK=1000 diff --git a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt index b4722787e6c60..44926ad4edb05 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt +++ b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt @@ -42,13 +42,18 @@ if(CONFIG_AMEBA_PWM) list(APPEND SRCS rtl8721dx_pwm.c) endif() +if(CONFIG_AMEBA_ADC) + list(APPEND SRCS rtl8721dx_adc.c) +endif() + target_sources(board PRIVATE ${SRCS}) if(CONFIG_AMEBA_GPIO OR CONFIG_AMEBA_UART OR CONFIG_AMEBA_I2C OR CONFIG_AMEBA_SPI - OR CONFIG_AMEBA_PWM) + OR CONFIG_AMEBA_PWM + OR CONFIG_AMEBA_ADC) # The board pin/UART tables pull in the shared driver's public headers from # arch/arm/src/common/ameba/, not on the default board include path. target_include_directories(board diff --git a/boards/arm/rtl8721dx/pke8721daf/src/Makefile b/boards/arm/rtl8721dx/pke8721daf/src/Makefile index 158e7fa644686..c841ac4da3d7b 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/Makefile +++ b/boards/arm/rtl8721dx/pke8721daf/src/Makefile @@ -69,4 +69,13 @@ CSRCS += rtl8721dx_pwm.c CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba endif +ifeq ($(CONFIG_AMEBA_ADC),y) +CSRCS += rtl8721dx_adc.c + +# The board ADC table pulls in the shared driver's public header from +# arch/arm/src/common/ameba/, which is not on the default board include path. + +CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_adc.c b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_adc.c new file mode 100644 index 0000000000000..81b3e0ddfd187 --- /dev/null +++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_adc.c @@ -0,0 +1,90 @@ +/**************************************************************************** + * boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_adc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "ameba_gpio.h" +#include "ameba_adc.h" +#include "rtl8721dx_pke8721daf.h" + +#ifdef CONFIG_AMEBA_ADC + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Channels sampled on /dev/adc0 and the analog pad each is wired to. The + * external channels CH0..CH6 map to pads PB19..PB13; this board exposes CH0 + * on PB19 and CH1 on PB18. Any external channel can be listed here; + * internal channels (CH7..CH10: temperature, VBAT) would carry + * AMEBA_ADC_PIN_NC. + */ + +static const uint8_t g_adc_channels[] = +{ + 0, /* ADC_CH0 */ + 1, /* ADC_CH1 */ +}; + +static const uint8_t g_adc_pins[] = +{ + AMEBA_PB(19), /* CH0 -> PB19 */ + AMEBA_PB(18), /* CH1 -> PB18 */ +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rtl8721dx_adc_initialize + * + * Description: + * Register the board's ADC channels at /dev/adc0. + * + ****************************************************************************/ + +int rtl8721dx_adc_initialize(void) +{ + int ret; + + ret = ameba_adc_register("/dev/adc0", g_adc_channels, g_adc_pins, + nitems(g_adc_channels)); + if (ret < 0) + { + syslog(LOG_ERR, + "ERROR: ameba_adc_register(/dev/adc0) failed: %d\n", ret); + return ret; + } + + return OK; +} + +#endif /* CONFIG_AMEBA_ADC */ diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c index bc4f3847d394a..69a45c44ccc92 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c +++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c @@ -158,6 +158,16 @@ int rtl8721dx_bringup(void) } #endif +#ifdef CONFIG_AMEBA_ADC + /* Register the board's ADC channels at /dev/adc0. */ + + ret = rtl8721dx_adc_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: rtl8721dx_adc_initialize failed: %d\n", ret); + } +#endif + /* Install the inter-core HW IPC-semaphore RTOS hooks LAST -- after all the * flash / WHC bring-up above, and just before this (board_late_initialize) * path returns and nx_start() hands off to the init task. diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h index f712e50da38d4..f185ef3ab9a8f 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h +++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h @@ -134,6 +134,19 @@ int rtl8721dx_spi_initialize(void); int rtl8721dx_pwm_initialize(void); #endif +#ifdef CONFIG_AMEBA_ADC +/**************************************************************************** + * Name: rtl8721dx_adc_initialize + * + * Description: + * Register the board's ADC channels at /dev/adc0 + * (boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_adc.c). + * + ****************************************************************************/ + +int rtl8721dx_adc_initialize(void); +#endif + #ifdef CONFIG_RTL8721DX_FLASH_FS /**************************************************************************** * Name: ameba_flash_fs_initialize From 06adde6079adcb4044544df1575006f979710e01 Mon Sep 17 00:00:00 2001 From: dechao_gong Date: Mon, 17 Aug 2026 17:09:20 +0800 Subject: [PATCH 2/3] arch/arm/rtl8720f: add ADC driver support Wire the shared Ameba ADC driver into the RTL8720F build: add the per-chip ameba_adc_chip.h (9 channels, CH0..CH5 external on PA13..PA18, PINMUX function 5, APB clock on bit24), the board ADC table and registration, the adc board config, and build glue for both cmake and make (including the fwlib RAM-layer ameba_adc.c in ameba_board.mk). Document the ADC on the board index. Signed-off-by: dechao_gong Assisted-by: Claude --- .../rtl8720f/boards/rtl8720f_evb/index.rst | 17 ++++ arch/arm/src/rtl8720f/CMakeLists.txt | 12 +++ arch/arm/src/rtl8720f/Make.defs | 4 + arch/arm/src/rtl8720f/ameba_adc_chip.h | 86 ++++++++++++++++++ arch/arm/src/rtl8720f/ameba_board.mk | 8 ++ .../rtl8720f_evb/configs/adc/defconfig | 53 +++++++++++ .../rtl8720f/rtl8720f_evb/src/CMakeLists.txt | 7 +- boards/arm/rtl8720f/rtl8720f_evb/src/Makefile | 6 +- .../rtl8720f/rtl8720f_evb/src/rtl8720f_adc.c | 89 +++++++++++++++++++ .../rtl8720f_evb/src/rtl8720f_bringup.c | 10 +++ .../rtl8720f_evb/src/rtl8720f_rtl8720f_evb.h | 12 +++ 11 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 arch/arm/src/rtl8720f/ameba_adc_chip.h create mode 100644 boards/arm/rtl8720f/rtl8720f_evb/configs/adc/defconfig create mode 100644 boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_adc.c diff --git a/Documentation/platforms/arm/rtl8720f/boards/rtl8720f_evb/index.rst b/Documentation/platforms/arm/rtl8720f/boards/rtl8720f_evb/index.rst index 64fd298d08f61..b917e20a05380 100644 --- a/Documentation/platforms/arm/rtl8720f/boards/rtl8720f_evb/index.rst +++ b/Documentation/platforms/arm/rtl8720f/boards/rtl8720f_evb/index.rst @@ -41,6 +41,8 @@ Supported in this NuttX port: on the SDK fwlib register layer * PWM output exposed as a ``/dev/pwm0`` character device, driven directly on the SDK fwlib timer register layer +* ADC channels exposed as an ``/dev/adc0`` character device, driven directly + on the SDK fwlib register layer Buttons and LEDs ================ @@ -144,6 +146,21 @@ example:: nsh> pwm -d 25 -f 1000 # 1 kHz, 25% duty on /dev/pwm0 +adc +--- + +Minimal NSH with the ADC driver and the ``adc`` example enabled (no Wi-Fi). +The board registers its channels from a table (see +``boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_adc.c``): ``/dev/adc0`` samples +CH0 on PA13 and CH1 on PA14. Edit that table -- channel numbers and the analog +pad each is wired to -- to match a board's wiring; the external channels +CH0..CH5 map to pads PA13..PA18 and are muxed to the ADC function through the +SDK ROM, while internal channels carry ``AMEBA_ADC_PIN_NC``. Every listed +channel is sampled, in order, on each trigger. Read the channels with the +example:: + + nsh> adc -n 1 # one sweep of /dev/adc0 + nsh --- diff --git a/arch/arm/src/rtl8720f/CMakeLists.txt b/arch/arm/src/rtl8720f/CMakeLists.txt index 7918bc09a10e7..a9bfd641aac9d 100644 --- a/arch/arm/src/rtl8720f/CMakeLists.txt +++ b/arch/arm/src/rtl8720f/CMakeLists.txt @@ -66,6 +66,10 @@ if(CONFIG_AMEBA_PWM) list(APPEND SRCS ${AMEBA_COMMON}/ameba_pwm.c) endif() +if(CONFIG_AMEBA_ADC) + list(APPEND SRCS ${AMEBA_COMMON}/ameba_adc.c) +endif() + target_include_directories(arch PRIVATE ${AMEBA_COMMON}) target_sources(arch PRIVATE ${SRCS}) @@ -144,6 +148,14 @@ if(CONFIG_AMEBA_PWM) list(APPEND AMEBA_FWLIB_SRCS ${AMEBA_SOC}/fwlib/ram_common/ameba_tim.c) endif() +# ADC (SAR) register layer. The ADC driver (arch/.../common/ameba/ameba_adc.c) +# calls the fwlib ADC API; the data tables and helpers it indexes live in this +# RAM source and must be compiled in (--gc-sections drops the unused +# interrupt/timer-trigger helpers). +if(CONFIG_AMEBA_ADC) + list(APPEND AMEBA_FWLIB_SRCS ${AMEBA_SOC}/fwlib/ram_common/ameba_adc.c) +endif() + # Silence a couple of warnings the vendored SDK sources trip under NuttX's # warning set, scoped to this fwlib compile only (never relaxing NuttX's own): # -Wno-int-conversion: the SDK passes NULL to irq_register()'s u32 "Data" diff --git a/arch/arm/src/rtl8720f/Make.defs b/arch/arm/src/rtl8720f/Make.defs index ba3dc354b8148..58a8959944d6b 100644 --- a/arch/arm/src/rtl8720f/Make.defs +++ b/arch/arm/src/rtl8720f/Make.defs @@ -76,6 +76,10 @@ ifeq ($(CONFIG_AMEBA_PWM),y) CHIP_CSRCS += ameba_pwm.c endif +ifeq ($(CONFIG_AMEBA_ADC),y) +CHIP_CSRCS += ameba_adc.c +endif + ############################################################################ # Realtek RTL8720F SDK integration # diff --git a/arch/arm/src/rtl8720f/ameba_adc_chip.h b/arch/arm/src/rtl8720f/ameba_adc_chip.h new file mode 100644 index 0000000000000..a74b26d45c72e --- /dev/null +++ b/arch/arm/src/rtl8720f/ameba_adc_chip.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * arch/arm/src/rtl8720f/ameba_adc_chip.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_RTL8720F_AMEBA_ADC_CHIP_H +#define __ARCH_ARM_SRC_RTL8720F_AMEBA_ADC_CHIP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Per-chip ADC wiring for RTL8720F. The shared driver + * (arch/arm/src/common/ameba/ameba_adc.c) includes this header to learn how + * many channels the converter can list, the crossbar pad-mux code that turns + * a pad into an analog input, and the peripheral-clock masks. A port to + * another Ameba chip supplies a same-named header on the chip include path; + * the shared driver is never edited -- it reads only the macros below, and + * it drives the ADC through the SDK fwlib API, which internally selects the + * secure/non-secure register alias (via TrustZone_IsSecure()), so no + * register base appears here at all. + * + * Values below are taken from the RTL8720F fwlib headers (verified, not + * guessed): PINMUX_FUNCTION_ADC=5 (ameba_pinmux.h), ADC_IRQ=34 + * (ameba_vector_table.h), APBPeriph_ADC and APBPeriph_ADC_CLOCK + * (sysreg_lsys.h) -- note the clock mask uses bit24 while the function mask + * uses bit23, unlike amebadplus where both are bit23. RTL8720F does not + * route the ADC through the cap-touch/CTC block, so no second clock domain + * is needed and AMEBA_ADC_AUXCLK_* stay undefined. + * + * External channels CH0..CH5 map to pads PA13,PA14,PA15,PA16,PA17,PA18 + * (from ameba_adc.h ADC_CHx_PIN); CH6..CH8 are fixed internal channels with + * no pad. The 16-bit conversion word (channel id in [19:16], data in + * [15:0]) is identical on every current Ameba chip, so the driver extracts + * it directly and no macro is needed here. + */ + +#define AMEBA_ADC_NCHAN 9 /* ADC_CH_NUM: CH0..CH5 ext, 6..8 int */ +#define AMEBA_ADC_MAXLIST 16 /* Channel-switch list depth (Cvlist) */ +#define AMEBA_ADC_PINMUX_FID 5 /* PINMUX_FUNCTION_ADC */ +#define AMEBA_ADC_IRQ 34 /* ADC_IRQ (unused by this polling drv) */ + +/* sizeof(fwlib ADC_InitTypeDef): OpMode/CvlistLen/Cvlist[16] then + * RxThresholdLevel/SpecialCh and an ADC_Chan[8] sub-struct ({u8,u16}=4 bytes + * each) -> 52 bytes, the largest of any current Ameba chip. The shared + * driver sizes its stack mirror from this so ADC_StructInit() cannot + * overflow it. + */ + +#define AMEBA_ADC_INIT_SIZE 52 + +/* APBPeriph_ADC (function) and APBPeriph_ADC_CLOCK masks. On this chip the + * function selector is bit30|bit23 and the clock selector is bit30|bit24. + * RTL8720F needs no second clock domain, so AMEBA_ADC_AUXCLK_* are left + * undefined (amebalite/amebasmart define them to gate APBPeriph_CTC too). + */ + +#define AMEBA_ADC_APBPERIPH (((uint32_t)1 << 30) | ((uint32_t)1 << 23)) +#define AMEBA_ADC_APBPERIPH_CLK (((uint32_t)1 << 30) | ((uint32_t)1 << 24)) + +#endif /* __ARCH_ARM_SRC_RTL8720F_AMEBA_ADC_CHIP_H */ diff --git a/arch/arm/src/rtl8720f/ameba_board.mk b/arch/arm/src/rtl8720f/ameba_board.mk index 9931c1fe31707..8384e73b29e22 100644 --- a/arch/arm/src/rtl8720f/ameba_board.mk +++ b/arch/arm/src/rtl8720f/ameba_board.mk @@ -153,6 +153,14 @@ ifeq ($(CONFIG_AMEBA_PWM),y) AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_tim.c endif +# ADC (SAR) register layer. The ADC driver +# (arch/.../common/ameba/ameba_adc.c) calls the fwlib ADC API; the data tables +# and helpers it indexes live in this RAM source and must be compiled in +# (--gc-sections drops the unused interrupt/timer-trigger helpers). +ifeq ($(CONFIG_AMEBA_ADC),y) +AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_adc.c +endif + # -Wno-int-conversion: the vendored SDK passes NULL to irq_register()'s u32 # "Data" (interrupt context) argument in many places -- an intentional # NULL-as-context idiom. Silence -Wint-conversion for the SDK fwlib sources diff --git a/boards/arm/rtl8720f/rtl8720f_evb/configs/adc/defconfig b/boards/arm/rtl8720f/rtl8720f_evb/configs/adc/defconfig new file mode 100644 index 0000000000000..84cc266a5863d --- /dev/null +++ b/boards/arm/rtl8720f/rtl8720f_evb/configs/adc/defconfig @@ -0,0 +1,53 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_DEBUG_WARN is not set +CONFIG_AMEBA_ADC=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="rtl8720f_evb" +CONFIG_ARCH_BOARD_RTL8720F_EVB=y +CONFIG_ARCH_CHIP="rtl8720f" +CONFIG_ARCH_CHIP_RTL8720F=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV8M_SYSTICK=y +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_TASK_STACKSIZE=4096 +CONFIG_EXAMPLES_ADC=y +CONFIG_EXAMPLES_ADC_GROUPSIZE=2 +CONFIG_EXAMPLES_ADC_NSAMPLES=1 +CONFIG_EXAMPLES_ADC_SWTRIG=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_FS_PROCFS=y +CONFIG_FS_TMPFS=y +CONFIG_IDLETHREAD_STACKSIZE=4096 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_LIBC_MEMFD_ERROR=y +CONFIG_MM_DEFAULT_ALIGNMENT=32 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=262144 +CONFIG_RAM_START=0x30008000 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=192 +CONFIG_SCHED_LPWORK=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=16 +CONFIG_START_MONTH=6 +CONFIG_START_YEAR=2026 +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_NSH_STACKSIZE=2500 +CONFIG_TIMER=y +CONFIG_TIMER_ARCH=y +CONFIG_USEC_PER_TICK=1000 diff --git a/boards/arm/rtl8720f/rtl8720f_evb/src/CMakeLists.txt b/boards/arm/rtl8720f/rtl8720f_evb/src/CMakeLists.txt index e09dd0664574d..514f1b66d9641 100644 --- a/boards/arm/rtl8720f/rtl8720f_evb/src/CMakeLists.txt +++ b/boards/arm/rtl8720f/rtl8720f_evb/src/CMakeLists.txt @@ -42,13 +42,18 @@ if(CONFIG_AMEBA_PWM) list(APPEND SRCS rtl8720f_pwm.c) endif() +if(CONFIG_AMEBA_ADC) + list(APPEND SRCS rtl8720f_adc.c) +endif() + target_sources(board PRIVATE ${SRCS}) if(CONFIG_AMEBA_GPIO OR CONFIG_AMEBA_UART OR CONFIG_AMEBA_I2C OR CONFIG_AMEBA_SPI - OR CONFIG_AMEBA_PWM) + OR CONFIG_AMEBA_PWM + OR CONFIG_AMEBA_ADC) # The board pin tables pull in the shared drivers' public headers from # arch/arm/src/common/ameba/, not on the default board include path. target_include_directories(board diff --git a/boards/arm/rtl8720f/rtl8720f_evb/src/Makefile b/boards/arm/rtl8720f/rtl8720f_evb/src/Makefile index 08dd553040dfc..8e0f450801450 100644 --- a/boards/arm/rtl8720f/rtl8720f_evb/src/Makefile +++ b/boards/arm/rtl8720f/rtl8720f_evb/src/Makefile @@ -44,10 +44,14 @@ ifeq ($(CONFIG_AMEBA_PWM),y) CSRCS += rtl8720f_pwm.c endif +ifeq ($(CONFIG_AMEBA_ADC),y) +CSRCS += rtl8720f_adc.c +endif + # The board pin tables pull in the shared drivers' public headers from # arch/arm/src/common/ameba/, which is not on the default board include path. -ifneq ($(CONFIG_AMEBA_GPIO)$(CONFIG_AMEBA_UART)$(CONFIG_AMEBA_I2C)$(CONFIG_AMEBA_SPI)$(CONFIG_AMEBA_PWM),) +ifneq ($(CONFIG_AMEBA_GPIO)$(CONFIG_AMEBA_UART)$(CONFIG_AMEBA_I2C)$(CONFIG_AMEBA_SPI)$(CONFIG_AMEBA_PWM)$(CONFIG_AMEBA_ADC),) CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba endif diff --git a/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_adc.c b/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_adc.c new file mode 100644 index 0000000000000..22175e6220c30 --- /dev/null +++ b/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_adc.c @@ -0,0 +1,89 @@ +/**************************************************************************** + * boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_adc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "ameba_gpio.h" +#include "ameba_adc.h" +#include "rtl8720f_rtl8720f_evb.h" + +#ifdef CONFIG_AMEBA_ADC + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Channels sampled on /dev/adc0 and the analog pad each is wired to. The + * external channels CH0..CH5 map to pads PA13..PA18; this board exposes CH0 + * on PA13 and CH1 on PA14. Any external channel can be listed here; + * internal channels (CH6..CH8) would carry AMEBA_ADC_PIN_NC. + */ + +static const uint8_t g_adc_channels[] = +{ + 0, /* ADC_CH0 */ + 1, /* ADC_CH1 */ +}; + +static const uint8_t g_adc_pins[] = +{ + AMEBA_PA(13), /* CH0 -> PA13 */ + AMEBA_PA(14), /* CH1 -> PA14 */ +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rtl8720f_adc_initialize + * + * Description: + * Register the board's ADC channels at /dev/adc0. + * + ****************************************************************************/ + +int rtl8720f_adc_initialize(void) +{ + int ret; + + ret = ameba_adc_register("/dev/adc0", g_adc_channels, g_adc_pins, + nitems(g_adc_channels)); + if (ret < 0) + { + syslog(LOG_ERR, + "ERROR: ameba_adc_register(/dev/adc0) failed: %d\n", ret); + return ret; + } + + return OK; +} + +#endif /* CONFIG_AMEBA_ADC */ diff --git a/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_bringup.c b/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_bringup.c index aab22384188be..3f5667edd89d0 100644 --- a/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_bringup.c +++ b/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_bringup.c @@ -170,6 +170,16 @@ int rtl8720f_bringup(void) } #endif +#ifdef CONFIG_AMEBA_ADC + /* Register the board's ADC channels at /dev/adc0. */ + + ret = rtl8720f_adc_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: rtl8720f_adc_initialize failed: %d\n", ret); + } +#endif + /* Install the inter-core HW IPC-semaphore RTOS hooks LAST -- after all the * flash / WHC bring-up above, and just before this (board_late_initialize) * path returns and nx_start() hands off to the init task. diff --git a/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_rtl8720f_evb.h b/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_rtl8720f_evb.h index d025a111b9b21..e7fe186adbced 100644 --- a/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_rtl8720f_evb.h +++ b/boards/arm/rtl8720f/rtl8720f_evb/src/rtl8720f_rtl8720f_evb.h @@ -79,6 +79,18 @@ int rtl8720f_spi_initialize(void); int rtl8720f_pwm_initialize(void); #endif +#ifdef CONFIG_AMEBA_ADC +/**************************************************************************** + * Name: rtl8720f_adc_initialize + * + * Description: + * Register the board's ADC channels at /dev/adc0. + * + ****************************************************************************/ + +int rtl8720f_adc_initialize(void); +#endif + #ifdef CONFIG_RTL8720F_WIFI /**************************************************************************** * Name: rtl8720f_wifi_initialize From 9854afb4029c62c95ce6148c7c183a99279e598f Mon Sep 17 00:00:00 2001 From: dechao_gong Date: Mon, 17 Aug 2026 17:09:31 +0800 Subject: [PATCH 3/3] arch/arm/rtl8721f: add ADC driver support Wire the shared Ameba ADC driver into the RTL8721F build: add the per-chip ameba_adc_chip.h (12 channels, CH0..CH7 external on PA20,PA19,PA18,PA17,PA15,PA14,PA13,PA12, PINMUX function 5), the board ADC table and registration, the adc board config, and build glue for both cmake and make (including the fwlib RAM-layer ameba_adc.c in ameba_board.mk). Document the ADC on the board index. Signed-off-by: dechao_gong Assisted-by: Claude --- .../rtl8721f/boards/rtl8721f_evb/index.rst | 17 ++++ arch/arm/src/rtl8721f/CMakeLists.txt | 12 +++ arch/arm/src/rtl8721f/Make.defs | 4 + arch/arm/src/rtl8721f/ameba_adc_chip.h | 84 +++++++++++++++++ arch/arm/src/rtl8721f/ameba_board.mk | 8 ++ .../rtl8721f_evb/configs/adc/defconfig | 53 +++++++++++ .../rtl8721f/rtl8721f_evb/src/CMakeLists.txt | 7 +- boards/arm/rtl8721f/rtl8721f_evb/src/Makefile | 6 +- .../rtl8721f/rtl8721f_evb/src/rtl8721f_adc.c | 90 +++++++++++++++++++ .../rtl8721f_evb/src/rtl8721f_bringup.c | 10 +++ .../rtl8721f_evb/src/rtl8721f_rtl8721f_evb.h | 12 +++ 11 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 arch/arm/src/rtl8721f/ameba_adc_chip.h create mode 100644 boards/arm/rtl8721f/rtl8721f_evb/configs/adc/defconfig create mode 100644 boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_adc.c diff --git a/Documentation/platforms/arm/rtl8721f/boards/rtl8721f_evb/index.rst b/Documentation/platforms/arm/rtl8721f/boards/rtl8721f_evb/index.rst index a3da36d03d6ac..a60108ad0f9ca 100644 --- a/Documentation/platforms/arm/rtl8721f/boards/rtl8721f_evb/index.rst +++ b/Documentation/platforms/arm/rtl8721f/boards/rtl8721f_evb/index.rst @@ -42,6 +42,8 @@ Supported in this NuttX port: on the SDK fwlib register layer * PWM output exposed as a ``/dev/pwm0`` character device, driven directly on the SDK fwlib timer register layer +* ADC channels exposed as an ``/dev/adc0`` character device, driven directly + on the SDK fwlib register layer Buttons and LEDs ================ @@ -147,6 +149,21 @@ example:: nsh> pwm -d 25 -f 1000 # 1 kHz, 25% duty on /dev/pwm0 +adc +--- + +Minimal NSH with the ADC driver and the ``adc`` example enabled (no Wi-Fi). +The board registers its channels from a table (see +``boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_adc.c``): ``/dev/adc0`` samples +CH0 on PA20 and CH1 on PA19. Edit that table -- channel numbers and the analog +pad each is wired to -- to match a board's wiring; the external channels +CH0..CH7 map to pads PA20,PA19,PA18,PA17,PA15,PA14,PA13,PA12 and are muxed to +the ADC function through the SDK ROM, while internal channels carry +``AMEBA_ADC_PIN_NC``. Every listed channel is sampled, in order, on each +trigger. Read the channels with the example:: + + nsh> adc -n 1 # one sweep of /dev/adc0 + nsh --- diff --git a/arch/arm/src/rtl8721f/CMakeLists.txt b/arch/arm/src/rtl8721f/CMakeLists.txt index b27e3b4a4e0be..97fd6cd291fc4 100644 --- a/arch/arm/src/rtl8721f/CMakeLists.txt +++ b/arch/arm/src/rtl8721f/CMakeLists.txt @@ -66,6 +66,10 @@ if(CONFIG_AMEBA_PWM) list(APPEND SRCS ${AMEBA_COMMON}/ameba_pwm.c) endif() +if(CONFIG_AMEBA_ADC) + list(APPEND SRCS ${AMEBA_COMMON}/ameba_adc.c) +endif() + target_include_directories(arch PRIVATE ${AMEBA_COMMON}) target_sources(arch PRIVATE ${SRCS}) @@ -145,6 +149,14 @@ if(CONFIG_AMEBA_PWM) list(APPEND AMEBA_FWLIB_SRCS ${AMEBA_SOC}/fwlib/ram_common/ameba_tim.c) endif() +# ADC (SAR) register layer. The ADC driver (arch/.../common/ameba/ameba_adc.c) +# calls the fwlib ADC API; the data tables and helpers it indexes live in this +# RAM source and must be compiled in (--gc-sections drops the unused +# interrupt/timer-trigger helpers). +if(CONFIG_AMEBA_ADC) + list(APPEND AMEBA_FWLIB_SRCS ${AMEBA_SOC}/fwlib/ram_common/ameba_adc.c) +endif() + # Silence a couple of warnings the vendored SDK sources trip under NuttX's # warning set, scoped to this fwlib compile only (never relaxing NuttX's own): # -Wno-int-conversion: the SDK passes NULL to irq_register()'s u32 "Data" diff --git a/arch/arm/src/rtl8721f/Make.defs b/arch/arm/src/rtl8721f/Make.defs index d415e3d7723b9..a110e7c0abebb 100644 --- a/arch/arm/src/rtl8721f/Make.defs +++ b/arch/arm/src/rtl8721f/Make.defs @@ -76,6 +76,10 @@ ifeq ($(CONFIG_AMEBA_PWM),y) CHIP_CSRCS += ameba_pwm.c endif +ifeq ($(CONFIG_AMEBA_ADC),y) +CHIP_CSRCS += ameba_adc.c +endif + ############################################################################ # Realtek RTL8721F SDK integration # diff --git a/arch/arm/src/rtl8721f/ameba_adc_chip.h b/arch/arm/src/rtl8721f/ameba_adc_chip.h new file mode 100644 index 0000000000000..17749c7819abf --- /dev/null +++ b/arch/arm/src/rtl8721f/ameba_adc_chip.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * arch/arm/src/rtl8721f/ameba_adc_chip.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_RTL8721F_AMEBA_ADC_CHIP_H +#define __ARCH_ARM_SRC_RTL8721F_AMEBA_ADC_CHIP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Per-chip ADC wiring for RTL8721F (amebagreen2). The shared driver + * (arch/arm/src/common/ameba/ameba_adc.c) includes this header to learn how + * many channels the converter can list, the crossbar pad-mux code that turns + * a pad into an analog input, and the peripheral-clock masks. A port to + * another Ameba chip supplies a same-named header on the chip include path; + * the shared driver is never edited -- it reads only the macros below, and + * it drives the ADC through the SDK fwlib API, which internally selects the + * secure/non-secure register alias (via TrustZone_IsSecure()), so no + * register base appears here at all. + * + * Values below are taken from the amebagreen2 fwlib headers (verified, not + * guessed): PINMUX_FUNCTION_ADC=5 (ameba_pinmux.h), ADC_IRQ=42 + * (ameba_vector_table.h), APBPeriph_ADC and APBPeriph_ADC_CLOCK both + * bit30|bit23 (sysreg_lsys.h). RTL8721F does not route the ADC through the + * cap-touch/CTC block, so no second clock domain is needed and + * AMEBA_ADC_AUXCLK_* stay undefined. + * + * External channels CH0..CH7 map to pads PA20,PA19,PA18,PA17,PA15,PA14, + * PA13,PA12 (from ameba_adc.h ADC_CHx_PIN); CH8..CH10 and CH14 are fixed + * internal channels with no pad. The 16-bit conversion word (channel id in + * [19:16], data in [15:0]) is identical on every current Ameba chip, so the + * driver extracts it directly and no macro is needed here. + */ + +#define AMEBA_ADC_NCHAN 12 /* ADC_CH_NUM: CH0..CH7 ext, rest int */ +#define AMEBA_ADC_MAXLIST 16 /* Channel-switch list depth (Cvlist) */ +#define AMEBA_ADC_PINMUX_FID 5 /* PINMUX_FUNCTION_ADC */ +#define AMEBA_ADC_IRQ 42 /* ADC_IRQ (unused by this polling drv) */ + +/* sizeof(fwlib ADC_InitTypeDef): OpMode/CvlistLen/Cvlist[16] then + * RxThresholdLevel/SpecialCh/SamplePeriodUs(u16) -> 22 bytes. The shared + * driver sizes its stack mirror from this so ADC_StructInit() cannot + * overflow it. + */ + +#define AMEBA_ADC_INIT_SIZE 22 + +/* APBPeriph_ADC (function) and APBPeriph_ADC_CLOCK masks. Equal on this + * chip; both set the bit30 group selector and bit23 for the LP ADC block. + * RTL8721F needs no second clock domain, so AMEBA_ADC_AUXCLK_* are left + * undefined (amebalite/amebasmart define them to gate APBPeriph_CTC too). + */ + +#define AMEBA_ADC_APBPERIPH (((uint32_t)1 << 30) | ((uint32_t)1 << 23)) +#define AMEBA_ADC_APBPERIPH_CLK (((uint32_t)1 << 30) | ((uint32_t)1 << 23)) + +#endif /* __ARCH_ARM_SRC_RTL8721F_AMEBA_ADC_CHIP_H */ diff --git a/arch/arm/src/rtl8721f/ameba_board.mk b/arch/arm/src/rtl8721f/ameba_board.mk index b4d0b7f5a0953..be40f9c9d3829 100644 --- a/arch/arm/src/rtl8721f/ameba_board.mk +++ b/arch/arm/src/rtl8721f/ameba_board.mk @@ -176,6 +176,14 @@ ifeq ($(CONFIG_AMEBA_PWM),y) AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_tim.c endif +# ADC (SAR) register layer. The ADC driver +# (arch/.../common/ameba/ameba_adc.c) calls the fwlib ADC API; the data tables +# and helpers it indexes live in this RAM source and must be compiled in +# (--gc-sections drops the unused interrupt/timer-trigger helpers). +ifeq ($(CONFIG_AMEBA_ADC),y) +AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_adc.c +endif + # -Wno-int-conversion: the vendored SDK passes NULL to irq_register()'s u32 # "Data" (interrupt context) argument in many places -- an intentional # NULL-as-context idiom. Silence -Wint-conversion for the SDK fwlib sources diff --git a/boards/arm/rtl8721f/rtl8721f_evb/configs/adc/defconfig b/boards/arm/rtl8721f/rtl8721f_evb/configs/adc/defconfig new file mode 100644 index 0000000000000..8e561d5ad4517 --- /dev/null +++ b/boards/arm/rtl8721f/rtl8721f_evb/configs/adc/defconfig @@ -0,0 +1,53 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_DEBUG_WARN is not set +CONFIG_AMEBA_ADC=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="rtl8721f_evb" +CONFIG_ARCH_BOARD_RTL8721F_EVB=y +CONFIG_ARCH_CHIP="rtl8721f" +CONFIG_ARCH_CHIP_RTL8721F=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV8M_SYSTICK=y +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_TASK_STACKSIZE=4096 +CONFIG_EXAMPLES_ADC=y +CONFIG_EXAMPLES_ADC_GROUPSIZE=2 +CONFIG_EXAMPLES_ADC_NSAMPLES=1 +CONFIG_EXAMPLES_ADC_SWTRIG=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_FS_PROCFS=y +CONFIG_FS_TMPFS=y +CONFIG_IDLETHREAD_STACKSIZE=4096 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_LIBC_MEMFD_ERROR=y +CONFIG_MM_DEFAULT_ALIGNMENT=32 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=401408 +CONFIG_RAM_START=0x20006000 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=192 +CONFIG_SCHED_LPWORK=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=16 +CONFIG_START_MONTH=6 +CONFIG_START_YEAR=2026 +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_NSH_STACKSIZE=2500 +CONFIG_TIMER=y +CONFIG_TIMER_ARCH=y +CONFIG_USEC_PER_TICK=1000 diff --git a/boards/arm/rtl8721f/rtl8721f_evb/src/CMakeLists.txt b/boards/arm/rtl8721f/rtl8721f_evb/src/CMakeLists.txt index 0188e21f7e772..b0befd38ddb68 100644 --- a/boards/arm/rtl8721f/rtl8721f_evb/src/CMakeLists.txt +++ b/boards/arm/rtl8721f/rtl8721f_evb/src/CMakeLists.txt @@ -42,13 +42,18 @@ if(CONFIG_AMEBA_PWM) list(APPEND SRCS rtl8721f_pwm.c) endif() +if(CONFIG_AMEBA_ADC) + list(APPEND SRCS rtl8721f_adc.c) +endif() + target_sources(board PRIVATE ${SRCS}) if(CONFIG_AMEBA_GPIO OR CONFIG_AMEBA_UART OR CONFIG_AMEBA_I2C OR CONFIG_AMEBA_SPI - OR CONFIG_AMEBA_PWM) + OR CONFIG_AMEBA_PWM + OR CONFIG_AMEBA_ADC) # The board pin tables pull in the shared drivers' public headers from # arch/arm/src/common/ameba/, not on the default board include path. target_include_directories(board diff --git a/boards/arm/rtl8721f/rtl8721f_evb/src/Makefile b/boards/arm/rtl8721f/rtl8721f_evb/src/Makefile index 308df01fcd2b0..65d9785ecda80 100644 --- a/boards/arm/rtl8721f/rtl8721f_evb/src/Makefile +++ b/boards/arm/rtl8721f/rtl8721f_evb/src/Makefile @@ -44,10 +44,14 @@ ifeq ($(CONFIG_AMEBA_PWM),y) CSRCS += rtl8721f_pwm.c endif +ifeq ($(CONFIG_AMEBA_ADC),y) +CSRCS += rtl8721f_adc.c +endif + # The board pin tables pull in the shared drivers' public headers from # arch/arm/src/common/ameba/, which is not on the default board include path. -ifneq ($(CONFIG_AMEBA_GPIO)$(CONFIG_AMEBA_UART)$(CONFIG_AMEBA_I2C)$(CONFIG_AMEBA_SPI)$(CONFIG_AMEBA_PWM),) +ifneq ($(CONFIG_AMEBA_GPIO)$(CONFIG_AMEBA_UART)$(CONFIG_AMEBA_I2C)$(CONFIG_AMEBA_SPI)$(CONFIG_AMEBA_PWM)$(CONFIG_AMEBA_ADC),) CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba endif diff --git a/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_adc.c b/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_adc.c new file mode 100644 index 0000000000000..c8bda33d67eed --- /dev/null +++ b/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_adc.c @@ -0,0 +1,90 @@ +/**************************************************************************** + * boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_adc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "ameba_gpio.h" +#include "ameba_adc.h" +#include "rtl8721f_rtl8721f_evb.h" + +#ifdef CONFIG_AMEBA_ADC + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Channels sampled on /dev/adc0 and the analog pad each is wired to. The + * external channels CH0..CH7 map to pads PA20,PA19,PA18,PA17,PA15,PA14, + * PA13,PA12; this board exposes CH5 on PA14 and CH6 on PA13. Any external + * channel can be listed here; internal channels would carry + * AMEBA_ADC_PIN_NC. + */ + +static const uint8_t g_adc_channels[] = +{ + 5, /* ADC_CH5 */ + 6, /* ADC_CH6 */ +}; + +static const uint8_t g_adc_pins[] = +{ + AMEBA_PA(14), /* CH5 -> PA14 */ + AMEBA_PA(13), /* CH6 -> PA13 */ +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rtl8721f_adc_initialize + * + * Description: + * Register the board's ADC channels at /dev/adc0. + * + ****************************************************************************/ + +int rtl8721f_adc_initialize(void) +{ + int ret; + + ret = ameba_adc_register("/dev/adc0", g_adc_channels, g_adc_pins, + nitems(g_adc_channels)); + if (ret < 0) + { + syslog(LOG_ERR, + "ERROR: ameba_adc_register(/dev/adc0) failed: %d\n", ret); + return ret; + } + + return OK; +} + +#endif /* CONFIG_AMEBA_ADC */ diff --git a/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_bringup.c b/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_bringup.c index ab3b5bdb5cf3c..d03f81737599b 100644 --- a/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_bringup.c +++ b/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_bringup.c @@ -184,6 +184,16 @@ int rtl8721f_bringup(void) } #endif +#ifdef CONFIG_AMEBA_ADC + /* Register the board's ADC channels at /dev/adc0. */ + + ret = rtl8721f_adc_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: rtl8721f_adc_initialize failed: %d\n", ret); + } +#endif + IPC_patch_function(rtos_critical_enter, rtos_critical_exit, AMEBA_RTOS_CRITICAL_SEMA); IPC_SEMDelayStub(rtos_time_delay_ms); diff --git a/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_rtl8721f_evb.h b/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_rtl8721f_evb.h index 04633cd2a4a89..bc4ea8f380f64 100644 --- a/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_rtl8721f_evb.h +++ b/boards/arm/rtl8721f/rtl8721f_evb/src/rtl8721f_rtl8721f_evb.h @@ -148,6 +148,18 @@ int rtl8721f_spi_initialize(void); int rtl8721f_pwm_initialize(void); #endif +#ifdef CONFIG_AMEBA_ADC +/**************************************************************************** + * Name: rtl8721f_adc_initialize + * + * Description: + * Register the board's ADC channels at /dev/adc0. + * + ****************************************************************************/ + +int rtl8721f_adc_initialize(void); +#endif + #ifdef CONFIG_RTL8721F_FLASH_FS /**************************************************************************** * Name: ameba_flash_fs_initialize