471 lines
13 KiB
C
Executable File
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));
|
|
}
|