sdk-hwV1.3/lichee/xr806/appos/project/common/cmd/cmd_adc.c

471 lines
13 KiB
C
Executable File

/*
* 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 "cmd_util.h"
#include "cmd_adc.h"
#include "driver/chip/hal_gpio.h"
#include "driver/chip/hal_adc.h"
#include "pm/pm.h"
#define CMD_REF_VOL 2500
#define CMD_VBAT_DIV_FACTOR 3
#define CMD_ADC_TEST_DBG 0
static struct cmd_adc_priv {
uint32_t channel;
uint32_t enable;
uint32_t irq_mode;
uint32_t low_value;
uint32_t high_value;
uint32_t voltage;
uint32_t deviation;
uint32_t pass;
} adc_priv[ADC_CHANNEL_NUM];
static struct cmd_adc_common {
uint32_t work_mode;
uint32_t work_clk;
uint32_t freq;
uint32_t delay;
} adc_common;
static enum cmd_status cmd_adc_output_check(uint32_t data, uint32_t voltage, uint32_t deviation)
{
uint32_t voltage_low, voltage_high;
voltage_low = (voltage < deviation) ? 0 : (voltage - deviation);
voltage_high = ((voltage + deviation) > CMD_REF_VOL) ? CMD_REF_VOL : (voltage + deviation);
voltage_low = voltage_low * 4096 / CMD_REF_VOL;
voltage_high = voltage_high * 4096 / CMD_REF_VOL;
if ((data >= voltage_low) && (data <= voltage_high)) {
#if CMD_ADC_TEST_DBG
CMD_DBG("ADC: data = %u, voltage_low = %u, voltage_high = %u\n",
data, voltage_low, voltage_high);
#endif
return CMD_STATUS_OK;
} else {
CMD_ERR("ADC: data = %u, voltage_low = %u, voltage_high = %u\n",
data, voltage_low, voltage_high);
return CMD_STATUS_FAIL;
}
}
static void cmd_adc_irq_cb(void *arg)
{
uint32_t data;
uint32_t channel;
ADC_IRQState irq_state;
enum cmd_status status;
struct cmd_adc_priv *chan_priv = arg;
chan_priv->pass = 1;
if (adc_common.work_mode == ADC_BURST_CONV) {
uint8_t i, num;
num = HAL_ADC_GetFifoDataCount();
for (i = 0; i < (num - 1); i++) {
data = HAL_ADC_GetFifoData();
status = cmd_adc_output_check(data, chan_priv->voltage, chan_priv->deviation);
if (status != CMD_STATUS_OK) {
chan_priv->pass = 0;
break;
}
}
} else {
channel = chan_priv->channel;
data = HAL_ADC_GetValue((ADC_Channel)channel);
irq_state = HAL_ADC_GetIRQState((ADC_Channel)channel);
#if CMD_ADC_TEST_DBG
CMD_DBG("channel = %u, data = %u, irq_state = %u\n", chan_priv->channel, data, irq_state);
#endif
status = cmd_adc_output_check(data, chan_priv->voltage, chan_priv->deviation);
if (status != CMD_STATUS_OK) {
chan_priv->pass = 0;
}
switch (chan_priv->irq_mode) {
case ADC_IRQ_NONE:
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_NONE, irq_state = %d\n", irq_state);
break;
case ADC_IRQ_DATA:
if (irq_state != ADC_DATA_IRQ) {
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_DATA, irq_state = %d\n", irq_state);
}
break;
case ADC_IRQ_LOW:
if ((irq_state != ADC_LOW_IRQ) || (data > chan_priv->low_value)) {
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_LOW, irq_state = %d, low_value = %u, data = %u\n",
irq_state, chan_priv->low_value, data);
}
break;
case ADC_IRQ_HIGH:
if ((irq_state != ADC_HIGH_IRQ) || (data < chan_priv->high_value)) {
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_HIGH, irq_state = %d, high_value = %u, data = %u\n",
irq_state, chan_priv->high_value, data);
}
break;
case ADC_IRQ_LOW_DATA:
if (irq_state != ADC_LOW_DATA_IRQ && irq_state != ADC_DATA_IRQ) {
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_LOW_DATA, irq_state = %d\n", irq_state);
}
break;
case ADC_IRQ_HIGH_DATA:
if (irq_state != ADC_HIGH_DATA_IRQ && irq_state != ADC_DATA_IRQ) {
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_HIGH_DATA, irq_state = %d\n", irq_state);
}
break;
case ADC_IRQ_LOW_HIGH:
if (irq_state != ADC_LOW_IRQ && irq_state != ADC_HIGH_IRQ) {
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_LOW_HIGH, irq_state = %d\n", irq_state);
}
break;
case ADC_IRQ_LOW_HIGH_DATA:
if (irq_state != ADC_LOW_DATA_IRQ && irq_state != ADC_HIGH_DATA_IRQ
&& irq_state != ADC_DATA_IRQ) {
chan_priv->pass = 0;
CMD_ERR("irq_mode: ADC_IRQ_LOW_HIGH_DATA, irq_state = %d\n", irq_state);
}
break;
default:
chan_priv->pass = 0;
CMD_ERR("channel = %u, irq_mode = %u\n", channel, chan_priv->irq_mode);
break;
}
}
}
static enum cmd_status cmd_adc_init_exec(char *cmd)
{
int cnt;
uint32_t work_clk, freq;
uint32_t delay;
uint32_t work_mode;
ADC_InitParam adc_param;
HAL_Status hal_status;
cnt = cmd_sscanf(cmd, "m=%u w=%u f=%u d=%u", &work_mode, &work_clk, &freq, &delay);
if (cnt != 4) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
if (work_mode > 1) {
CMD_ERR("invalid work mode %u\n", work_mode);
return CMD_STATUS_INVALID_ARG;
}
if (work_clk > 1) {
CMD_ERR("invalid work clock %u\n", work_clk);
return CMD_STATUS_INVALID_ARG;
}
if ((freq < 1000) || (freq > 1000000)) {
CMD_ERR("invalid freq %u\n", freq);
return CMD_STATUS_INVALID_ARG;
}
if ((delay >> 8) != 0) {
CMD_ERR("invalid delay %u\n", delay);
return CMD_STATUS_INVALID_ARG;
}
cmd_memset(adc_priv, 0, ADC_CHANNEL_NUM * sizeof(struct cmd_adc_priv));
adc_common.work_mode = work_mode ? ADC_BURST_CONV : ADC_CONTI_CONV;
adc_common.work_clk = work_clk;
adc_common.freq = freq;
adc_common.delay = delay;
#if (CONFIG_CHIP_ARCH_VER == 3)
adc_param.work_clk = (ADC_WorkClk)work_clk;
#endif
adc_param.freq = freq;
adc_param.delay = delay;
adc_param.mode = work_mode ? ADC_BURST_CONV : ADC_CONTI_CONV;
#if (CONFIG_CHIP_ARCH_VER > 1)
adc_param.vref_mode = ADC_VREF_MODE_1;
#endif
hal_status = HAL_ADC_Init(&adc_param);
if (hal_status == HAL_OK) {
return CMD_STATUS_OK;
} else {
CMD_ERR("HAL_ADC_Init return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
}
static enum cmd_status cmd_adc_deinit_exec(char *cmd)
{
HAL_Status hal_status;
hal_status = HAL_ADC_DeInit();
if (hal_status == HAL_OK) {
return CMD_STATUS_OK;
} else {
CMD_ERR("HAL_ADC_DeInit return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
}
static enum cmd_status cmd_adc_input_exec(char *cmd)
{
int cnt;
uint32_t channel, voltage, deviation;
cnt = cmd_sscanf(cmd, "c=%u v=%u d=%u", &channel, &voltage, &deviation);
if (cnt != 3) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
if (channel >= ADC_CHANNEL_NUM) {
CMD_ERR("invalid channel %u\n", channel);
return CMD_STATUS_INVALID_ARG;
}
if ((voltage > CMD_REF_VOL) && (channel != ADC_CHANNEL_VBAT)) {
CMD_ERR("invalid voltage %u\n", voltage);
return CMD_STATUS_INVALID_ARG;
}
if (deviation > CMD_REF_VOL) {
CMD_ERR("invalid deviation %u\n", deviation);
return CMD_STATUS_INVALID_ARG;
}
if (channel == ADC_CHANNEL_VBAT) {
voltage /= CMD_VBAT_DIV_FACTOR;
}
adc_priv[channel].voltage = voltage;
adc_priv[channel].deviation = deviation;
return CMD_STATUS_OK;
}
static enum cmd_status cmd_adc_conv_polling_exec(char *cmd)
{
int cnt;
uint32_t channel;
uint32_t data;
HAL_Status hal_status;
cnt = cmd_sscanf(cmd, "c=%u", &channel);
if (cnt != 1) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
if (channel >= ADC_CHANNEL_NUM) {
CMD_ERR("invalid channel %u\n", channel);
return CMD_STATUS_INVALID_ARG;
}
hal_status = HAL_ADC_Conv_Polling((ADC_Channel)channel, &data, 10000);
if (hal_status != HAL_OK) {
CMD_ERR("HAL_ADC_Conv_Polling return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
return cmd_adc_output_check(data, adc_priv[channel].voltage, adc_priv[channel].deviation);
}
static enum cmd_status cmd_adc_config_exec(char *cmd)
{
int cnt;
uint32_t channel, enable, irq_mode, low_value, high_value;
HAL_Status hal_status;
cnt = cmd_sscanf(cmd, "c=%u e=%u i=%u l=%u h=%u", &channel, &enable,
&irq_mode, &low_value, &high_value);
if (cnt != 5) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
if (channel >= ADC_CHANNEL_NUM) {
CMD_ERR("invalid channel %u\n", channel);
return CMD_STATUS_INVALID_ARG;
}
if (enable > 1) {
CMD_ERR("invalid enable %u\n", enable);
return CMD_STATUS_INVALID_ARG;
}
if (irq_mode > 7) {
CMD_ERR("invalid irq_mode %u\n", irq_mode);
return CMD_STATUS_INVALID_ARG;
}
if ((low_value >> 12) != 0) {
CMD_ERR("invalid low_value %u\n", low_value);
return CMD_STATUS_INVALID_ARG;
}
if ((high_value >> 12) != 0) {
CMD_ERR("invalid high_value %u\n", high_value);
return CMD_STATUS_INVALID_ARG;
}
if (adc_common.work_mode == ADC_BURST_CONV) {
hal_status = HAL_ADC_FifoConfigChannel((ADC_Channel)channel, (ADC_Select)enable);
if (hal_status != HAL_OK) {
CMD_ERR("HAL_ADC_FifoConfigChannel return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
} else {
hal_status = HAL_ADC_ConfigChannel((ADC_Channel)channel, (ADC_Select)enable,
(ADC_IRQMode)irq_mode, low_value, high_value);
if (hal_status != HAL_OK) {
CMD_ERR("HAL_ADC_ConfigChannel return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
}
adc_priv[channel].channel = channel;
adc_priv[channel].enable = enable;
adc_priv[channel].irq_mode = irq_mode;
adc_priv[channel].low_value = low_value;
adc_priv[channel].high_value = high_value;
adc_priv[channel].pass = 1;
#if CMD_ADC_TEST_DBG
CMD_DBG("ADC config: channel = %u, enable = %u, irq_mode = %u, low_value = %u, high_value = %u\n",
channel, enable, irq_mode, low_value, high_value);
#endif
return CMD_STATUS_OK;
}
static enum cmd_status cmd_adc_conv_it_start_exec(char *cmd)
{
ADC_Channel chan;
HAL_Status hal_status;
for (chan = ADC_CHANNEL_0; chan < ADC_CHANNEL_NUM; chan++) {
if (adc_priv[chan].enable) {
hal_status = HAL_ADC_EnableIRQCallback(chan, cmd_adc_irq_cb, &adc_priv[chan]);
if (hal_status != HAL_OK) {
CMD_ERR("HAL_ADC_EnableIRQCallback return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
}
}
hal_status = HAL_ADC_Start_Conv_IT();
if (hal_status != HAL_OK) {
CMD_ERR("HAL_ADC_Start_Conv_IT return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
return CMD_STATUS_OK;
}
static enum cmd_status cmd_adc_conv_it_stop_exec(char *cmd)
{
ADC_Channel chan;
HAL_Status hal_status;
hal_status = HAL_ADC_Stop_Conv_IT();
if (hal_status != HAL_OK) {
CMD_ERR("HAL_ADC_Stop_Conv_IT return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
for (chan = ADC_CHANNEL_0; chan < ADC_CHANNEL_NUM; chan++) {
if (adc_priv[chan].enable) {
hal_status = HAL_ADC_DisableIRQCallback(chan);
if (hal_status != HAL_OK) {
CMD_ERR("HAL_ADC_DisableIRQCallback return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
}
}
for (chan = ADC_CHANNEL_0; chan < ADC_CHANNEL_NUM; chan++) {
if ((adc_priv[chan].enable) && (adc_priv[chan].pass == 0)) {
return CMD_STATUS_FAIL;
}
}
return CMD_STATUS_OK;
}
static enum cmd_status cmd_adc_wakeup_exec(char *cmd)
{
int cnt;
enum cmd_status status;
uint32_t pm_mode;
cnt = cmd_sscanf(cmd, "m=%u", &pm_mode);
if (cnt != 1) {
return CMD_STATUS_FAIL;
}
status = CMD_STATUS_OK;
switch (pm_mode) {
case 0:
HAL_ADC_SetBypassPmMode(PM_SUPPORT_SLEEP);
break;
case 1:
HAL_ADC_SetBypassPmMode(PM_SUPPORT_STANDBY);
break;
default:
CMD_ERR("invalid pm mode:%u\n", pm_mode);
status = CMD_STATUS_FAIL;
break;
}
return status;
}
static const struct cmd_data g_adc_cmds[] = {
{ "init", cmd_adc_init_exec },
{ "deinit", cmd_adc_deinit_exec },
{ "input", cmd_adc_input_exec },
{ "conv-polling", cmd_adc_conv_polling_exec },
{ "config", cmd_adc_config_exec },
{ "conv-it-start", cmd_adc_conv_it_start_exec },
{ "conv-it-stop", cmd_adc_conv_it_stop_exec },
{ "wakeup", cmd_adc_wakeup_exec },
};
enum cmd_status cmd_adc_exec(char *cmd)
{
return cmd_exec(cmd, g_adc_cmds, cmd_nitems(g_adc_cmds));
}