/* * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "kernel/os/os.h" #include "driver/chip/hal_adc.h" #include "driver/chip/private/hal_os.h" #include "common/framework/platform_init.h" #define ADC_BURST_MODE (0) #define ADC_IRQ_MODE ADC_IRQ_DATA #define ADC_CHL ADC_CHANNEL_VBAT #define ADC_FEQ 100000 #define ADC_FIRST_DELAY 10 HAL_Semaphore voltage_sem; #define ADC_VOL_SEM_INIT() HAL_SemaphoreInitBinary(&voltage_sem) #define ADC_VOL_SEM_WAIT() HAL_SemaphoreWait(&voltage_sem, HAL_WAIT_FOREVER) #define ADC_VOL_SEM_RELEASE() HAL_SemaphoreRelease(&voltage_sem) #define ADC_VOL_SEM_DEINIT() HAL_SemaphoreDeinit(&voltage_sem) static uint8_t adc_value_status = 1; static uint32_t adc_data[10]; static void adc_callback(void *arg) { ADC_Channel chl = *((ADC_Channel *)arg); if (adc_value_status) { #if ADC_BURST_MODE HAL_ADC_FifoConfigChannel(chl, ADC_SELECT_DISABLE); #else HAL_ADC_ConfigChannel(chl, ADC_SELECT_DISABLE, ADC_IRQ_MODE, 0, 0); #endif return; } static uint32_t count = 0; #if ADC_BURST_MODE for (count = 0; count < 10; count++) adc_data[count] = HAL_ADC_GetFifoData(); HAL_ADC_FifoConfigChannel(chl, ADC_SELECT_DISABLE); adc_value_status = 1; ADC_VOL_SEM_RELEASE(); #else if (HAL_ADC_GetIRQState(chl) == ADC_DATA_IRQ) { if (count >= 10) { count = 0; HAL_ADC_ConfigChannel(chl, ADC_SELECT_DISABLE, ADC_IRQ_MODE, 0, 0); adc_value_status = 1; ADC_VOL_SEM_RELEASE(); } else adc_data[count++] = HAL_ADC_GetValue(chl); } #endif } static void adc_init(void) { HAL_Status status = HAL_ERROR; ADC_InitParam initParam; #if (CONFIG_CHIP_ARCH_VER == 3) initParam.work_clk = ADC_WORKCLK_HFCLK; #endif initParam.delay = ADC_FIRST_DELAY; initParam.freq = ADC_FEQ; #if (CONFIG_CHIP_ARCH_VER > 1) initParam.vref_mode = ADC_VREF_MODE_1; #endif #if ADC_BURST_MODE initParam.mode = ADC_BURST_CONV; #else initParam.mode = ADC_CONTI_CONV; #endif printf("ADC init...\n"); status = HAL_ADC_Init(&initParam); if (status != HAL_OK) { printf("ADC init error %d\n", status); return; } } static void adc_deinit(void) { HAL_Status status = HAL_ERROR; status = HAL_ADC_DeInit(); if (status != HAL_OK) printf("ADC deinit error %d\n", status); } static uint32_t adc_filter(uint32_t adc_data[10]) { uint8_t j = 0, i = 0; uint32_t sum = 0; uint32_t max = 0; for (i = 0; i < 10; i++) { for (j = i; j < 9; j++) { if (adc_data[i] >= adc_data[j+1]) { max = adc_data[i]; adc_data[i] = adc_data[j+1]; adc_data[j+1] = max; } } } for (i = 0; i < 10; i++) { sum = sum + adc_data[i]; } sum = (sum - adc_data[0] - adc_data[9]) / 8; return sum; } static void adc_voltage_init(void) { ADC_VOL_SEM_INIT(); adc_init(); } static void adc_voltage_config(uint8_t channel, uint8_t en) { HAL_Status status = HAL_ERROR; ADC_Channel chl = (ADC_Channel)channel; if (en) { printf("ADC channel config...\n"); #if ADC_BURST_MODE status = HAL_ADC_FifoConfigChannel(chl, ADC_SELECT_ENABLE); #else status = HAL_ADC_ConfigChannel(chl, ADC_SELECT_ENABLE, ADC_IRQ_MODE, 0, 0); #endif if (status != HAL_OK) { printf("ADC config error %d\n", status); return; } printf("ADC callback enable...\n"); status = HAL_ADC_EnableIRQCallback(chl, adc_callback, &chl); if (status != HAL_OK) { printf("ADC IRQ Enable error %d\n", status); return; } printf("ADC convert start...\n"); status = HAL_ADC_Start_Conv_IT(); if (status != HAL_OK) { printf("ADC it mode start error %d\n", status); return; } } else { printf("ADC convert stop...\n"); HAL_ADC_Stop_Conv_IT(); printf("ADC callback disable...\n"); HAL_ADC_DisableIRQCallback(chl); } } /* * The relationship between adc value and voltage conversion is as follows: * voltage = adc_value * 2500 * ratio / 4096(mv), ratio=1 when adc channel is chl0~chl6, * ratio=3 when adc channel is chl8, */ static uint32_t adc_voltage_get(uint8_t channel) { HAL_Status status = HAL_ERROR; adc_value_status = 0; ADC_Channel chl = (ADC_Channel)channel; uint8_t ratio = (chl == ADC_CHANNEL_VBAT) ? 3 : 1; #if ADC_BURST_MODE status = HAL_ADC_FifoConfigChannel(chl, ADC_SELECT_ENABLE); #else status = HAL_ADC_ConfigChannel(chl, ADC_SELECT_ENABLE, ADC_IRQ_MODE, 0, 0); #endif if (status != HAL_OK) { printf("ADC it mode start error %d\n", status); } ADC_VOL_SEM_WAIT(); if (adc_value_status) { uint32_t adc_value = adc_filter(adc_data); return (adc_value * 2500 * ratio / 4096); } return 0; } static void adc_voltage_deinit(void) { adc_deinit(); ADC_VOL_SEM_DEINIT(); } int main(void) { platform_init(); printf("ADC demo started.\n"); OS_MSleep(500); adc_voltage_init(); adc_voltage_config(ADC_CHL, 1); printf("ADC get channel voltage...\n"); uint32_t voltage; while (1) { voltage = adc_voltage_get(ADC_CHL); if (voltage) printf("VBAT voltage: %dmV\n", voltage); OS_MSleep(1000); } adc_voltage_config(ADC_CHL, 0); adc_voltage_deinit(); printf("ADC demo over.\n"); return 0; }