| /**************************************************************************** |
| * arch/arm/src/tlsr82/tlsr82_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 <nuttx/config.h> |
| |
| #include <inttypes.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <sys/param.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <nuttx/analog/adc.h> |
| #include <nuttx/analog/ioctl.h> |
| #include <nuttx/mutex.h> |
| #include <arch/board/board.h> |
| |
| #include "tlsr82_adc.h" |
| #include "tlsr82_gpio.h" |
| #include "tlsr82_analog.h" |
| #include "tlsr82_flash.h" |
| #include "tlsr82_timer.h" |
| #include "hardware/tlsr82_dfifo.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Default reference voltage 1175 mV */ |
| |
| #define ADC_DEFAULT_VREF 1175 |
| |
| #define ADC_FILT_NUM CONFIG_TLSR82_ADC_FILT_NUM |
| |
| #if (ADC_FILT_NUM & 0x3) != 0 |
| # error "The filter number must be multiple of 4 !" |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* ADC Information data */ |
| |
| struct adc_info_s |
| { |
| uint32_t base_vref; /* The reference voltage (mV) or gain for base/gpio mode */ |
| int base_off; /* The offset for base/gpio mode two-point calibration */ |
| uint32_t vbat_vref; /* The reference voltage (mV) for vbat mode */ |
| bool base_two; /* Base/Gpio mode two-point calibration or not */ |
| bool registered; /* Have registered a adc device */ |
| bool configured; /* Adc has been configured or not */ |
| uint8_t channel; /* Adc current channel */ |
| uint8_t channeltype; /* Adc current channel type */ |
| }; |
| |
| /* ADC Private Data */ |
| |
| struct adc_chan_s |
| { |
| uint32_t ref; /* Reference count */ |
| struct adc_info_s *info; /* Adc information */ |
| const uint8_t channeltype; /* Channel number */ |
| const uint8_t channel; /* Channel number */ |
| const uint32_t pinset; /* GPIO pin number */ |
| const struct adc_callback_s *cb; /* Upper driver callback */ |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static void tlsr82_adc_reset(void); |
| static void tlsr82_adc_power_ctrl(bool enable); |
| static void tlsr82_adc_clk_ctrl(bool enable); |
| static void tlsr82_adc_config(struct adc_chan_s *priv); |
| static void tlsr82_adc_pin_config(uint32_t pinset); |
| static void tlsr82_adc_chan_config(uint32_t pinset); |
| |
| static void adc_dump(const char *msg); |
| static int adc_bind(struct adc_dev_s *dev, |
| const struct adc_callback_s *callback); |
| static void adc_reset(struct adc_dev_s *dev); |
| static int adc_setup(struct adc_dev_s *dev); |
| static void adc_shutdown(struct adc_dev_s *dev); |
| static void adc_rxint(struct adc_dev_s *dev, bool enable); |
| static int adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg); |
| static void adc_read_work(struct adc_dev_s *dev); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* ADC module information */ |
| |
| static struct adc_info_s g_adc_module0_info = |
| { |
| .base_vref = ADC_DEFAULT_VREF, |
| .base_off = 0, |
| .vbat_vref = ADC_DEFAULT_VREF, |
| .base_two = false, |
| .registered = false, |
| .configured = false, |
| .channel = ADC_CHAN_NONE, |
| .channeltype = ADC_CHAN_TYPE_NONE, |
| }; |
| |
| /* ADC interface operations */ |
| |
| static const struct adc_ops_s g_adcops = |
| { |
| .ao_bind = adc_bind, |
| .ao_reset = adc_reset, |
| .ao_setup = adc_setup, |
| .ao_shutdown = adc_shutdown, |
| .ao_rxint = adc_rxint, |
| .ao_ioctl = adc_ioctl, |
| }; |
| |
| /* ADC normal channel, for gpio sample */ |
| |
| #ifdef CONFIG_TLSR82_ADC_CHAN0 |
| static struct adc_chan_s g_adc_chan0 = |
| { |
| .info = &g_adc_module0_info, |
| .channeltype = BOARD_ADC0_CHAN_TYPE, |
| .channel = BOARD_ADC0_CHAN, |
| .pinset = BOARD_ADC0_PIN, |
| }; |
| |
| static struct adc_dev_s g_adc_chan0_dev = |
| { |
| .ad_ops = &g_adcops, |
| .ad_priv = &g_adc_chan0, |
| }; |
| #endif |
| |
| #ifdef CONFIG_TLSR82_ADC_CHAN1 |
| static struct adc_chan_s g_adc_chan1 = |
| { |
| .info = &g_adc_module0_info, |
| .channeltype = BOARD_ADC1_CHAN_TYPE, |
| .channel = BOARD_ADC1_CHAN, |
| .pinset = BOARD_ADC1_PIN, |
| }; |
| |
| static struct adc_dev_s g_adc_chan1_dev = |
| { |
| .ad_ops = &g_adcops, |
| .ad_priv = &g_adc_chan1, |
| }; |
| #endif |
| |
| #ifdef CONFIG_TLSR82_ADC_CHAN2 |
| static struct adc_chan_s g_adc_chan2 = |
| { |
| .info = &g_adc_module0_info, |
| .channeltype = BOARD_ADC2_CHAN_TYPE, |
| .channel = BOARD_ADC2_CHAN, |
| .pinset = BOARD_ADC2_PIN, |
| }; |
| |
| static struct adc_dev_s g_adc_chan2_dev = |
| { |
| .ad_ops = &g_adcops, |
| .ad_priv = &g_adc_chan2, |
| }; |
| #endif |
| |
| /* ADC Bat channel, for chip battery sample */ |
| |
| #ifdef CONFIG_TLSR82_ADC_VBAT |
| static struct adc_chan_s g_adc_chanbat = |
| { |
| .info = &g_adc_module0_info, |
| .channeltype = BOARD_ADCVBAT_CHAN_TYPE, |
| .channel = BOARD_ADCVBAT_CHAN, |
| .pinset = BOARD_ADCVBAT_PIN, |
| }; |
| |
| static struct adc_dev_s g_adc_chanbat_dev = |
| { |
| .ad_ops = &g_adcops, |
| .ad_priv = &g_adc_chanbat, |
| }; |
| #endif |
| |
| static mutex_t g_lock = NXMUTEX_INITIALIZER; |
| |
| /**************************************************************************** |
| * Inline Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_dfifo_enable |
| * |
| * Description: |
| * Enable the adc dfifo/dfifo2, the dfifo2 will copy the adc sample data |
| * to the address in DFIFO_ADC_ADDR_REG. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void tlsr82_adc_dfifo_enable(void) |
| { |
| DFIFO_MODE_REG |= DFIFO_MODE_DFIFO2_IN; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_dfifo_disable |
| * |
| * Description: |
| * Disable the adc dfifo/dfifo2 |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void tlsr82_adc_dfifo_disable(void) |
| { |
| DFIFO_MODE_REG &= ~DFIFO_MODE_DFIFO2_IN; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_dfifo_config |
| * |
| * Description: |
| * Config the data buffer, so DFIFO2 can copy sample value to buffer |
| * |
| * Input Parameters: |
| * buffer - the buffer to store the adc sample data |
| * size - the buffer size, this size must be multiple of 4 |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void tlsr82_adc_dfifo_config(uint8_t *buffer, size_t size) |
| { |
| /* Config the data buffer, so DFIFO2 can copy sample value to buffer |
| * DFIFO buffer address : only need low 16 bit, because the high 16 bit |
| * must be 0x0084 |
| * DFIFO buffer size : DFIFO_ADC_SIZE_REG = n ==> 4 * (n + 1) size |
| * DFIFO_ADC_SIZE_REG = size / 4 - 1 |
| */ |
| |
| DFIFO_ADC_ADDR_REG = (uint16_t)((uint32_t)buffer & 0xffff); |
| DFIFO_ADC_SIZE_REG = (size >> 2) - 1; |
| |
| /* Clear the dfifo write pointer */ |
| |
| DFIFO2_WPTR_REG = 0; |
| } |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_reset |
| * |
| * Description: |
| * Reset the adc, reconfig the adc. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void tlsr82_adc_reset(void) |
| { |
| BM_SET(RESET_RST1_REG, RESET_RST1_ADC); |
| BM_CLR(RESET_RST1_REG, RESET_RST1_ADC); |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_power_ctrl |
| * |
| * Description: |
| * Enable/Disable the ADC power, the power should be power up before |
| * config the adc. |
| * |
| * Input Parameters: |
| * enable - true : power up |
| * false: power down |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void tlsr82_adc_power_ctrl(bool enable) |
| { |
| tlsr82_analog_write(ADC_PAG_CTRL_REG, |
| (tlsr82_analog_read(ADC_PAG_CTRL_REG) & |
| (~ADC_POWER_MASK)) | |
| (!enable) << ADC_POWER_SHIFT); |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_clk_ctrl |
| * |
| * Description: |
| * Enable/Disable the ADC clock. |
| * |
| * Input Parameters: |
| * enable - true : enable adc clock |
| * false: disable adc clock |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void tlsr82_adc_clk_ctrl(bool enable) |
| { |
| if (enable) |
| { |
| tlsr82_analog_write(ADC_CLK_REG, |
| tlsr82_analog_read(ADC_CLK_REG) | |
| ADC_CLK_24M_EN); |
| } |
| else |
| { |
| tlsr82_analog_write(ADC_CLK_REG, |
| tlsr82_analog_read(ADC_CLK_REG) & |
| (~ADC_CLK_24M_EN)); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_config |
| * |
| * Description: |
| * Config the adc to different mode, after this, the adc can start sample |
| * the voltage in the gpio pin (Base mode) or the chip voltage (Vbat |
| * channel mode). |
| * Five configuration conditions: |
| * 1. Same channel, do not need do not need re-configuration; |
| * 2. adc module has not been configured, must configure it; |
| * 3. adc module has been configured, but current channel and configured |
| * channel are both vbat channel, do not need re-configuration; |
| * 4. adc module has been configured, but current channel and configured |
| * channel are both base channel, only configure the channel; |
| * 5. others, need re-configuration. |
| * |
| * Input Parameters: |
| * priv - adc channel handler |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void tlsr82_adc_config(struct adc_chan_s *priv) |
| { |
| uint8_t channel = priv->info->channel; |
| uint8_t channeltype = priv->info->channeltype; |
| |
| ainfo("Current channel=%u, channeltype=%u\n", channel, channeltype); |
| ainfo("Input channel=%u, channeltype=%u\n", |
| priv->channel, priv->channeltype); |
| |
| DEBUGASSERT(priv != NULL && priv->channel != ADC_CHAN_NONE); |
| |
| /* If current channel type is same as the priv channel type, do not |
| * need re-configure all the register. |
| */ |
| |
| if (channeltype == priv->channeltype) |
| { |
| if (channeltype == ADC_CHAN_TYPE_BASE && channel != priv->channel) |
| { |
| /* The channel type is base and the input channel (GPIO pin) is |
| * different, only need re-configure the adc input channel. |
| */ |
| |
| tlsr82_adc_chan_config(priv->pinset); |
| priv->info->channel = priv->channel; |
| } |
| |
| return; |
| } |
| |
| ainfo("Start config\n"); |
| |
| /* Follow the sdk code adc_base_init() and adc_vbat_channel_init() |
| * to config the adc, VBAT_CHAN mode is samiler to BASE mode, the |
| * differences are: |
| * 1. adc divider : VBAT_CHAN mode, 1/3 |
| * BASE mode, 1 |
| * 2. pre-scale : VBAT_CHAN mode, 1 |
| * BASE mode, 1/8 |
| */ |
| |
| /* Enable misc channel and set totaol length for sampling state be 2 */ |
| |
| tlsr82_analog_write(ADC_CTRL0_REG, ADC_CTRL0_CHANEN_ENABLE | |
| (2 << ADC_CTRL0_SAMPLEN_SHIFT)); |
| |
| /* Set the max capture time and max set time, time = count / 24Mhz |
| * r_max_s (Set) : ADC_SAMP3_REG_0xf1<3:0> |
| * r_max_mc(Capture) : ADC_SAMP3_REG_0xf1<7:6>, ADC_SAMP1_REG_0xef<7:0> |
| * Here, set r_max_s = 10 (0.417us), r_max_mc = 240 (10us) |
| * So, total duration = 10.417us, sample frequency = 24Mhz / 250 = 96Khz |
| */ |
| |
| tlsr82_analog_write(ADC_SAMP1_REG, 240 & 0xff); |
| tlsr82_analog_write(ADC_SAMP3_REG, ((240 >> 8) << 6) | (10 & 0xff)); |
| |
| /* Divider select 1/3 or OFF */ |
| |
| #ifdef CONFIG_TLSR82_ADC_VBAT |
| if (priv->channeltype == ADC_CHAN_TYPE_VBAT) |
| { |
| tlsr82_analog_modify(ADC_DIVIDER_REG, ADC_DIVIDER_SEL_MASK, |
| ADC_DIVIDER_SEL_1F3); |
| } |
| else |
| #endif |
| { |
| tlsr82_analog_modify(ADC_DIVIDER_REG, ADC_DIVIDER_SEL_MASK, |
| ADC_DIVIDER_SEL_OFF); |
| } |
| |
| /* Set the adc differential channel */ |
| |
| #ifdef CONFIG_TLSR82_ADC_VBAT |
| if (priv->channeltype == ADC_CHAN_TYPE_VBAT) |
| { |
| tlsr82_analog_write(ADC_CHAN_REG, ADC_CHAN_POS_VBAT | |
| ADC_CHAN_NEG_GND); |
| } |
| else |
| #endif |
| { |
| tlsr82_adc_chan_config(priv->pinset); |
| } |
| |
| /* Enable the Different input mode */ |
| |
| tlsr82_analog_modify(ADC_MODE_REG, ADC_MODE_INPUT_MASK, |
| ADC_MODE_INPUT_DIFF); |
| |
| /* Reference voltage select, 1.2V */ |
| |
| tlsr82_analog_modify(ADC_VREF_REG, ADC_VREF_MASK, ADC_VREF_1P2V); |
| |
| /* Adc resolution select, 14bit */ |
| |
| tlsr82_analog_modify(ADC_MODE_REG, ADC_MODE_RES_MASK, |
| ADC_MODE_RES_14BIT); |
| |
| /* Adc sample cycle number, 6 */ |
| |
| tlsr82_analog_modify(ADC_SAMP0_REG, ADC_SAMP0_CYCLE_MASK, |
| ADC_SAMP0_CYCLE_6); |
| |
| /* Adc pre-scale select, 1/8 |
| * When pre-scaling is 1 , the ADC_DIVIDER_REG_0xf9 <4:5> must be 0 |
| * When pre-scaling is 1/8, the ADC_DIVIDER_REG_0xf9 <4> must be 1 |
| */ |
| |
| #ifdef CONFIG_TLSR82_ADC_VBAT |
| if (priv->channeltype == ADC_CHAN_TYPE_VBAT) |
| { |
| tlsr82_analog_modify(ADC_SCALE_REG, ADC_SCALE_MASK, ADC_SCALE_1); |
| tlsr82_analog_modify(ADC_DIVIDER_REG, BIT_RNG(4, 5), 0); |
| } |
| else |
| #endif |
| { |
| tlsr82_analog_modify(ADC_SCALE_REG, ADC_SCALE_MASK, ADC_SCALE_1F8); |
| tlsr82_analog_modify(ADC_DIVIDER_REG, BIT(4), BIT(4)); |
| } |
| |
| /* Set current channel and current type */ |
| |
| priv->info->channel = priv->channel; |
| priv->info->channeltype = priv->channeltype; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_pin_config |
| * |
| * Description: |
| * Configure the the input pin as adc function, this function should be |
| * called before used this pin as adc. |
| * |
| * Input Parameters: |
| * pinset - adc pinset |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void tlsr82_adc_pin_config(uint32_t pinset) |
| { |
| uint32_t cfg = GPIO_CFG2PIN(pinset); |
| |
| GPIO_SET_AS_GPIO(GPIO_GET(GROUP, cfg), GPIO_GET(PIN, cfg)); |
| |
| /* Base mode pin config, disable input, disable output, output set low */ |
| |
| tlsr82_gpio_input_ctrl(cfg, false); |
| |
| tlsr82_gpio_output_ctrl(cfg, false); |
| |
| tlsr82_gpiowrite(cfg, false); |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_chan_config |
| * |
| * Description: |
| * Configure the the input pin as the adc channel input, tlsr82 only have |
| * one adc conversion channel. |
| * |
| * Input Parameters: |
| * pinset - adc pinset |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void tlsr82_adc_chan_config(uint32_t pinset) |
| { |
| uint16_t pinnum = GPIO_PIN2NUM(pinset); |
| uint16_t pinadc; |
| |
| /* Convert the gpio pin index to adc register defined pin index */ |
| |
| if (pinnum >= 8 && pinnum <= 15) |
| { |
| /* PB0 ~ PB7 */ |
| |
| pinadc = pinnum - 7; |
| } |
| else if (pinnum >= 20 && pinnum <= 21) |
| { |
| /* PC4 ~ PC5 */ |
| |
| pinadc = pinnum - 11; |
| } |
| else |
| { |
| aerr("Adc pin number error, pinnum=%u\n", pinnum); |
| return; |
| } |
| |
| /* Config gpio */ |
| |
| tlsr82_adc_pin_config(pinset); |
| |
| /* Config the positive and negative input, here, the negative input |
| * is always configured to GND (0x0f). |
| */ |
| |
| tlsr82_analog_write(ADC_CHAN_REG, (pinadc << ADC_CHAN_POS_SHIFT) | |
| ADC_CHAN_NEG_GND); |
| } |
| |
| /**************************************************************************** |
| * Name: adc_dump |
| * |
| * Description: |
| * Dump all the adc register value. |
| * |
| * Input Parameters: |
| * msg - Message need to print. |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void adc_dump(const char *msg) |
| { |
| if (msg) |
| { |
| ainfo("%s, dump all adc regs:\n", msg); |
| } |
| else |
| { |
| ainfo("Dump all adc regs:\n"); |
| } |
| |
| ainfo("ADC_VREF_REG : addr=0x%x, val=0x%x\n", |
| ADC_VREF_REG, tlsr82_analog_read(ADC_VREF_REG)); |
| ainfo("ADC_CHAN_REG : addr=0x%x, val=0x%x\n", |
| ADC_CHAN_REG, tlsr82_analog_read(ADC_CHAN_REG)); |
| ainfo("ADC_MODE_REG : addr=0x%x, val=0x%x\n", |
| ADC_MODE_REG, tlsr82_analog_read(ADC_MODE_REG)); |
| ainfo("ADC_SAMP0_REG : addr=0x%x, val=0x%x\n", |
| ADC_SAMP0_REG, tlsr82_analog_read(ADC_SAMP0_REG)); |
| ainfo("ADC_SAMP1_REG : addr=0x%x, val=0x%x\n", |
| ADC_SAMP1_REG, tlsr82_analog_read(ADC_SAMP1_REG)); |
| ainfo("ADC_SAMP2_REG : addr=0x%x, val=0x%x\n", |
| ADC_SAMP2_REG, tlsr82_analog_read(ADC_SAMP2_REG)); |
| ainfo("ADC_SAMP3_REG : addr=0x%x, val=0x%x\n", |
| ADC_SAMP3_REG, tlsr82_analog_read(ADC_SAMP3_REG)); |
| ainfo("ADC_CTRL0_REG : addr=0x%x, val=0x%x\n", |
| ADC_CTRL0_REG, tlsr82_analog_read(ADC_CTRL0_REG)); |
| ainfo("ADC_CTRL1_REG : addr=0x%x, val=0x%x\n", |
| ADC_CTRL1_REG, tlsr82_analog_read(ADC_CTRL1_REG)); |
| ainfo("ADC_CLKDIV_REG : addr=0x%x, val=0x%x\n", |
| ADC_CLKDIV_REG, tlsr82_analog_read(ADC_CLKDIV_REG)); |
| ainfo("ADC_STATUS_REG : addr=0x%x, val=0x%x\n", |
| ADC_STATUS_REG, tlsr82_analog_read(ADC_STATUS_REG)); |
| ainfo("ADC_DATAL_REG : addr=0x%x, val=0x%x\n", |
| ADC_DATAL_REG, tlsr82_analog_read(ADC_DATAL_REG)); |
| ainfo("ADC_DATAH_REG : addr=0x%x, val=0x%x\n", |
| ADC_DATAH_REG, tlsr82_analog_read(ADC_DATAH_REG)); |
| ainfo("ADC_DIVIDER_REG : addr=0x%x, val=0x%x\n", |
| ADC_DIVIDER_REG, tlsr82_analog_read(ADC_DIVIDER_REG)); |
| ainfo("ADC_SCALE_REG : addr=0x%x, val=0x%x\n", |
| ADC_SCALE_REG, tlsr82_analog_read(ADC_SCALE_REG)); |
| ainfo("ADC_PAG_CTRL_REG : addr=0x%x, val=0x%x\n", |
| ADC_PAG_CTRL_REG, tlsr82_analog_read(ADC_PAG_CTRL_REG)); |
| ainfo("ADC_CLK_REG : addr=0x%x, val=0x%x\n", |
| ADC_CLK_REG, tlsr82_analog_read(ADC_CLK_REG)); |
| } |
| |
| /**************************************************************************** |
| * Name: adc_read |
| * |
| * Description: |
| * Start ADC sampling and read ADC value. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * Read ADC value. |
| * |
| ****************************************************************************/ |
| |
| static uint16_t adc_read(void) |
| { |
| volatile uint16_t data_buf[ADC_FILT_NUM]; |
| uint16_t tmp; |
| uint16_t max = 0; |
| uint16_t min = UINT16_MAX; |
| uint32_t sum = 0; |
| uint32_t currtime; |
| int i; |
| |
| /* Clear the data buffer and config */ |
| |
| memset((void *)data_buf, 0, ADC_FILT_NUM); |
| |
| tlsr82_adc_dfifo_config((uint8_t *)data_buf, ADC_FILT_NUM); |
| |
| /* Enable DFIFO 2 */ |
| |
| tlsr82_adc_dfifo_enable(); |
| |
| /* Wait at least 2 sample cycle, frequency = 96k, T = 10.4us */ |
| |
| currtime = tlsr82_time(); |
| while (!tlsr82_time_exceed(currtime, 25)); |
| |
| for (i = 0; i < ADC_FILT_NUM; i++) |
| { |
| while (data_buf[i] == 0 && !tlsr82_time_exceed(currtime, 25)); |
| currtime = tlsr82_time(); |
| |
| /* Bit13 is the sign bit, bit13 = 1 indicates the data |
| * is negative but we only get the low 13bit data. |
| */ |
| |
| tmp = data_buf[i]; |
| if (tmp & BIT(13)) |
| { |
| tmp = 0; |
| } |
| else |
| { |
| tmp &= 0x1fff; |
| } |
| |
| sum += tmp; |
| |
| if (tmp > max) |
| { |
| max = tmp; |
| } |
| |
| if (tmp < min) |
| { |
| min = tmp; |
| } |
| |
| ainfo("data_buf[%d]=%u\n", i, tmp); |
| } |
| |
| /* Disable DFIFO 2 */ |
| |
| tlsr82_adc_dfifo_disable(); |
| |
| ainfo("sum=%lu, max=%u, min=%u, filt_num=%d\n", |
| sum, max, min, ADC_FILT_NUM); |
| |
| /* Remove max, min value and get the average value */ |
| |
| tmp = (sum - min - max) / (ADC_FILT_NUM - 2); |
| |
| return tmp; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_calibrate |
| * |
| * Description: |
| * ADC calibration. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_TLSR82_ADC_CALI |
| static void tlsr82_adc_calibrate(struct adc_chan_s *priv) |
| { |
| uint8_t cali_data[7]; |
| |
| uint32_t base_vref; |
| uint32_t vbat_vref; |
| |
| memset((void *)cali_data, 0, sizeof(cali_data)); |
| tlsr82_flash_read_data(CONFIG_TLSR82_ADC_CALI_PARA_ADDR, cali_data, |
| sizeof(cali_data)); |
| |
| ainfo("Calibration data: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", |
| cali_data[0], cali_data[1], cali_data[2], cali_data[3], |
| cali_data[4], cali_data[5], cali_data[6]); |
| |
| /* Calibration parameters format follow the telink sdk code */ |
| |
| /* Base/gpio mode calibration parameters read */ |
| |
| if (cali_data[4] <= 0x7f && cali_data[5] != 0xff && |
| cali_data[6] != 0xff) |
| { |
| /* Two-point calibration exist, the base_vref as the gain to use |
| * Method of calculating two-point gpio calibration Flash_gain |
| * and Flash_offset: |
| * gain = (data[6] << 8) + data[5] + 1000 mv |
| * offset = data[4] - 20 mv |
| */ |
| |
| priv->info->base_vref = (cali_data[6] << 8) + cali_data[5] + 1000; |
| priv->info->base_off = (int)cali_data[4] - 20; |
| priv->info->base_two = true; |
| } |
| else |
| { |
| /* One-point calibration exist |
| * Method of calculating calibration Flash_gpio_vref value: |
| * vref = 1175 + data[0] - 255 + data[1] mV |
| * = 920 + data[0] + data[1] mV |
| */ |
| |
| base_vref = 920 + cali_data[0] + cali_data[1]; |
| if (base_vref >= 1047 && base_vref <= 1302) |
| { |
| priv->info->base_vref = base_vref; |
| } |
| |
| priv->info->base_two = false; |
| } |
| |
| /* Vbat mode calibration parameters read */ |
| |
| if (cali_data[2] != 0xff || cali_data[3] != 0xff) |
| { |
| /* One-point calibration exist |
| * Method of calculating calibration Flash_vbat_vref value: |
| * vref = 1175 + data[2] - 255 + data[3] mV |
| * = 920 + data[2] + data[3] mV |
| */ |
| |
| vbat_vref = 920 + cali_data[2] + cali_data[3]; |
| if (vbat_vref >= 1047 && vbat_vref <= 1302) |
| { |
| priv->info->vbat_vref = vbat_vref; |
| } |
| } |
| |
| ainfo("Calibration parameters:\n"); |
| ainfo(" base two-point: gain=%d, offset=%d\n", |
| priv->info->base_vref, priv->info->base_off); |
| ainfo(" base one-point: vref=%lu\n", priv->info->base_vref); |
| ainfo(" vbat one-point: vref=%lu\n", priv->info->vbat_vref); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: adc_read_work |
| * |
| * Description: |
| * Read ADC value and pass it to up. |
| * |
| * Input Parameters: |
| * dev - ADC device pointer |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void adc_read_work(struct adc_dev_s *dev) |
| { |
| int ret; |
| uint32_t value; |
| int32_t adc; |
| struct adc_chan_s *priv = (struct adc_chan_s *)dev->ad_priv; |
| |
| ret = nxmutex_lock(&g_lock); |
| if (ret < 0) |
| { |
| aerr("Failed to wait sem ret=%d\n", ret); |
| return; |
| } |
| |
| /* Config the adc */ |
| |
| tlsr82_adc_config(priv); |
| |
| /* Dump adc register for debug */ |
| |
| adc_dump("adc_read_work"); |
| |
| value = adc_read(); |
| |
| /* Transfer the adc count into voltage (mV) |
| * ADC resolution is 14bit, bit13 is a signed bit and we set the |
| * negative input be gnd, so the actual adc resolution is 13bit. |
| */ |
| |
| #ifdef CONFIG_TLSR82_ADC_VBAT |
| if (priv->channeltype == ADC_CHAN_TYPE_VBAT) |
| { |
| /* Vbat channel mode, divider = 1/3, scale = 1 ==> factor = 3 */ |
| |
| adc = (value * priv->info->vbat_vref * 3) >> 13; |
| } |
| else |
| #endif |
| { |
| /* Base/gpio mode, divider = 1, scale = 1/8 ==> factor = 8 */ |
| |
| adc = (value * priv->info->base_vref * 8) >> 13; |
| |
| if (priv->info->base_two) |
| { |
| adc += priv->info->base_off; |
| } |
| } |
| |
| /* Put adc value to the adc buffer */ |
| |
| priv->cb->au_receive(dev, priv->channel, adc); |
| |
| ainfo("channel: %" PRIu8 ", voltage: %" PRIu32 " mV\n", priv->channel, |
| adc); |
| |
| nxmutex_unlock(&g_lock); |
| } |
| |
| /**************************************************************************** |
| * Name: adc_bind |
| * |
| * Description: |
| * Bind the upper-half driver callbacks to the lower-half implementation. |
| * This must be called early in order to receive ADC event notifications. |
| * |
| ****************************************************************************/ |
| |
| static int adc_bind(struct adc_dev_s *dev, |
| const struct adc_callback_s *callback) |
| { |
| struct adc_chan_s *priv = (struct adc_chan_s *)dev->ad_priv; |
| |
| DEBUGASSERT(priv != NULL); |
| |
| ainfo("channel: %" PRIu8 "\n", priv->channel); |
| |
| priv->cb = callback; |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: adc_reset |
| * |
| * Description: |
| * Reset the ADC device. Called early to initialize the hardware. |
| * This is called, before adc_setup() and on error conditions. |
| * |
| * Input Parameters: |
| * |
| * Returned Value: |
| * |
| ****************************************************************************/ |
| |
| static void adc_reset(struct adc_dev_s *dev) |
| { |
| irqstate_t flags; |
| struct adc_chan_s *priv = (struct adc_chan_s *)dev->ad_priv; |
| |
| ainfo("channel: %" PRIu8 "\n", priv->channel); |
| |
| flags = enter_critical_section(); |
| |
| /* Do nothing if ADC instance is currently in use */ |
| |
| if (priv->ref > 0) |
| { |
| goto out; |
| } |
| |
| if (priv->info->registered) |
| { |
| goto out; |
| } |
| |
| /* Reset ADC hardware */ |
| |
| tlsr82_adc_power_ctrl(true); |
| |
| tlsr82_adc_reset(); |
| |
| tlsr82_adc_clk_ctrl(false); |
| |
| /* adc_reset() will be called in adc_register(), the same one adc |
| * device should be reset only once. |
| */ |
| |
| priv->info->registered = true; |
| |
| out: |
| leave_critical_section(flags); |
| } |
| |
| /**************************************************************************** |
| * Name: adc_setup |
| * |
| * Description: |
| * Configure the ADC. This method is called the first time that the ADC |
| * device is opened. This will occur when the port is first opened. |
| * This setup includes configuring. |
| * |
| * Input Parameters: |
| * |
| * Returned Value: |
| * |
| ****************************************************************************/ |
| |
| static int adc_setup(struct adc_dev_s *dev) |
| { |
| struct adc_chan_s *priv = (struct adc_chan_s *)dev->ad_priv; |
| |
| ainfo("channel: %" PRIu8 "\n", priv->channel); |
| |
| /* Do nothing when the ADC device is already set up */ |
| |
| if (priv->ref > 0) |
| { |
| priv->ref++; |
| return OK; |
| } |
| |
| /* The same one adc device should be configgured only once */ |
| |
| if (priv->info->configured) |
| { |
| return OK; |
| } |
| |
| /* Enable ADC clock */ |
| |
| tlsr82_adc_clk_ctrl(true); |
| |
| /* Read the calibration parameters */ |
| |
| #ifdef CONFIG_TLSR82_ADC_CALI |
| tlsr82_adc_calibrate(priv); |
| #endif |
| |
| /* Config ADC hardware */ |
| |
| ainfo("pin: 0x%" PRIx32 ", channel: %" PRIu8 "\n", |
| priv->pinset, priv->channel); |
| |
| tlsr82_adc_config(priv); |
| |
| /* The ADC device is ready */ |
| |
| priv->info->configured = true; |
| priv->ref++; |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: adc_rxint |
| * |
| * Description: |
| * Call to enable or disable RX interrupts. |
| * |
| * Input Parameters: |
| * |
| * Returned Value: |
| * |
| ****************************************************************************/ |
| |
| static void adc_rxint(struct adc_dev_s *dev, bool enable) |
| { |
| UNUSED(dev); |
| UNUSED(enable); |
| } |
| |
| /**************************************************************************** |
| * Name: adc_ioctl |
| * |
| * Description: |
| * All ioctl calls will be routed through this method. |
| * |
| * Input Parameters: |
| * dev - pointer to device structure used by the driver |
| * cmd - command |
| * arg - arguments passed with command |
| * |
| * Returned Value: |
| * |
| ****************************************************************************/ |
| |
| static int adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg) |
| { |
| int ret; |
| struct adc_chan_s *priv = (struct adc_chan_s *)dev->ad_priv; |
| |
| ainfo("channel: %" PRIu8 " cmd=%d\n", priv->channel, cmd); |
| |
| switch (cmd) |
| { |
| case ANIOC_TRIGGER: |
| { |
| /* Start sampling and read ADC value here */ |
| |
| adc_read_work(dev); |
| ret = OK; |
| } |
| break; |
| |
| case ANIOC_GET_NCHANNELS: |
| { |
| /* Return the number of configured channels */ |
| |
| ret = 1; |
| } |
| break; |
| |
| default: |
| { |
| aerr("ERROR: Unknown cmd: %d\n", cmd); |
| ret = -ENOTTY; |
| } |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: adc_shutdown |
| * |
| * Description: |
| * Disable the ADC. This method is called when the ADC device is closed. |
| * This method reverses the operation the setup method. |
| * |
| * Input Parameters: |
| * |
| * Returned Value: |
| * |
| ****************************************************************************/ |
| |
| static void adc_shutdown(struct adc_dev_s *dev) |
| { |
| struct adc_chan_s *priv = (struct adc_chan_s *)dev->ad_priv; |
| |
| ainfo("channel: %" PRIu8 "\n", priv->channel); |
| |
| /* Decrement count only when ADC device is in use */ |
| |
| if (priv->ref > 0) |
| { |
| priv->ref--; |
| |
| /* Shutdown the ADC device only when not in use */ |
| |
| if (priv->ref == 0) |
| { |
| /* Disable ADC clock */ |
| |
| tlsr82_adc_clk_ctrl(false); |
| |
| /* Clear the configured flag */ |
| |
| priv->info->configured = false; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: tlsr82_adc_init |
| * |
| * Description: |
| * Initialize the ADC. |
| * |
| * Input Parameters: |
| * channel - ADC channel number |
| * |
| * Returned Value: |
| * ADC device structure reference on success; a NULL on failure |
| * |
| ****************************************************************************/ |
| |
| int tlsr82_adc_init(const char *devpath, int minor) |
| { |
| int ret = OK; |
| struct adc_dev_s *dev; |
| |
| ainfo("ADC channel: %" PRIu8 "\n", minor); |
| |
| switch (minor) |
| { |
| #ifdef CONFIG_TLSR82_ADC_CHAN0 |
| case ADC_CHAN_0: |
| dev = &g_adc_chan0_dev; |
| break; |
| #endif |
| |
| #ifdef CONFIG_TLSR82_ADC_CHAN1 |
| case ADC_CHAN_1: |
| dev = &g_adc_chan1_dev; |
| break; |
| #endif |
| |
| #ifdef CONFIG_TLSR82_ADC_CHAN2 |
| case ADC_CHAN_2: |
| dev = &g_adc_chan2_dev; |
| break; |
| #endif |
| |
| #ifdef CONFIG_TLSR82_ADC_VBAT |
| case ADC_CHAN_VBAT: |
| dev = &g_adc_chanbat_dev; |
| break; |
| #endif |
| |
| default: |
| { |
| aerr("ERROR: No ADC interface defined\n"); |
| return -ENOTTY; |
| } |
| } |
| |
| ret = adc_register(devpath, dev); |
| if (ret < 0) |
| { |
| aerr("Adc register fail, devpath=%s, ret=%d\n", devpath, ret); |
| } |
| |
| return ret; |
| } |