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

699 lines
16 KiB
C
Raw Normal View History

2024-05-07 10:09:20 +00:00
/*
* 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_pwm.h"
#include "driver/chip/hal_gpio.h"
#include "driver/chip/hal_pwm.h"
typedef enum {
PWM_COMPLE,
PWM_PLUSE,
PWM_CYCLE,
PWM_CAPTURE,
PWM_DEADZONE,
PWM_MODENUM,
} Cmd_PwmMode;
typedef struct {
uint32_t high_time;
uint32_t low_time;
uint16_t num;
uint16_t count;
uint8_t d_value;
uint8_t d_hz;
uint8_t input_ch;
uint8_t cap_ch;
Cmd_PwmMode input_mode;
} Cmd_Capinfo;
static Cmd_Capinfo private_cap[8];
static Cmd_PwmMode cmd_pwm_mode_analytic(char *data)
{
if (cmd_strcmp(data, "comple") == 0) {
return PWM_COMPLE;
} else if (cmd_strcmp(data, "pluse") == 0) {
return PWM_PLUSE;
} else if (cmd_strcmp(data, "cycle") == 0) {
return PWM_CYCLE;
} else if (cmd_strcmp(data, "capture") == 0) {
return PWM_CAPTURE;
} else if (cmd_strcmp(data, "deadzone") == 0) {
return PWM_DEADZONE;
}
return PWM_MODENUM;
}
typedef struct {
PWM_GROUP_ID group;
PWM_CH_ID ch;
} PWM_IoInfo;
static int cmd_pwm_init(Cmd_PwmMode mode, PWM_IoInfo info, uint32_t hz)
{
HAL_Status ret = HAL_ERROR;
PWM_ClkParam clk_cfg;
PWM_CompInitParam comp_cfg;
PWM_ChInitParam ch_cfg;
int cycle = 0;
clk_cfg.clk = PWM_CLK_HOSC;
clk_cfg.div = PWM_SRC_CLK_DIV_1;
ret = HAL_PWM_GroupClkCfg(info.group, &clk_cfg);
if (ret != HAL_OK)
return -1;
switch (mode) {
case PWM_COMPLE:
comp_cfg.hz = hz;
comp_cfg.polarity = PWM_HIGHLEVE;
cycle = HAL_PWM_ComplementaryInit(info.group, &comp_cfg);
if (cycle == -1)
return -1;
ret = HAL_PWM_ComplementarySetDutyRatio(info.group, cycle / 4);
if (ret != HAL_OK)
return -1;
break;
case PWM_CYCLE:
ch_cfg.hz = hz;
ch_cfg.mode = PWM_CYCLE_MODE;
ch_cfg.polarity = PWM_HIGHLEVE;
cycle = HAL_PWM_ChInit(info.ch, &ch_cfg);
if (cycle == -1)
return -1;
ret = HAL_PWM_ChSetDutyRatio(info.ch, cycle / 2);
if (ret != HAL_OK)
return -1;
break;
case PWM_PLUSE:
ch_cfg.hz = hz;
ch_cfg.mode = PWM_PLUSE_MODE;
ch_cfg.polarity = PWM_HIGHLEVE;
cycle = HAL_PWM_ChInit(info.ch, &ch_cfg);
if (cycle == -1)
return -1;
break;
case PWM_CAPTURE:
ch_cfg.hz = hz;
ch_cfg.mode = PWM_CAPTURE_MODE;
ch_cfg.polarity = PWM_HIGHLEVE;
cycle = HAL_PWM_ChInit(info.ch, &ch_cfg);
if (cycle == -1)
return -1;
break;
default:
CMD_DBG("invalid PWM mode\n");
return -1;
}
return 0;
}
static PWM_IoInfo cmd_pwm_channel_analytic(Cmd_PwmMode mode, uint32_t channel)
{
PWM_IoInfo info;
if (mode == PWM_COMPLE || mode == PWM_DEADZONE) {
if (channel > 3) {
info.ch = PWM_CH_NULL;
info.group = PWM_GROUP_NULL;
return info;
}
info.group = channel;
info.ch = PWM_CH_NULL;
return info;
} else if (channel > 7) {
info.ch = PWM_CH_NULL;
info.group = PWM_GROUP_NULL;
return info;
}
info.ch = channel;
info.group = channel / 2;
return info;
}
static enum cmd_status cmd_pwm_config_exec(char *cmd)
{
uint32_t hz;
uint32_t channel;
char mode_char[8];
int cnt;
cnt = cmd_sscanf(cmd, "c=%u m=%s h=%u", &channel, mode_char, &hz);
if (cnt != 3) {
return CMD_STATUS_INVALID_ARG;
}
Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char);
if (mode >= PWM_MODENUM) {
CMD_ERR("invalid pwm mode %u\n", mode);
return CMD_STATUS_INVALID_ARG;
}
uint32_t mix_freq, max_freq;
max_freq = HAL_GetHFClock();
if (max_freq == 24000000)
mix_freq = 2;
else if (max_freq == 40000000)
mix_freq = 3;
else {
CMD_ERR("invalid HF\n");
return CMD_STATUS_INVALID_ARG;
}
if (hz < mix_freq || hz > max_freq) {
CMD_ERR("pwm hz out of range %u\n", hz);
return CMD_STATUS_FAIL;
}
PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel);
if (info.group >= PWM_GROUP_NULL)
return CMD_STATUS_INVALID_ARG;
if (cmd_pwm_init(mode, info, hz) == -1)
return CMD_STATUS_FAIL;
return CMD_STATUS_OK;
}
enum cmd_status cmd_pwm_deinit_exec(char *cmd)
{
int cnt = 0;
uint32_t channel;
char mode_char[8];
HAL_Status ret = HAL_ERROR;
cnt = cmd_sscanf(cmd, "c=%u m=%s", &channel, mode_char);
if (cnt != 2)
return CMD_STATUS_INVALID_ARG;
Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char);
if (mode >= PWM_MODENUM) {
CMD_ERR("invalid pwm mode %u\n", mode);
return CMD_STATUS_INVALID_ARG;
}
PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel);
if (info.group == PWM_GROUP_NULL) {
CMD_ERR("invalid pwm channel\n");
return CMD_STATUS_INVALID_ARG;
}
if (mode == PWM_COMPLE) {
ret = HAL_PWM_ComplementaryDeInit(info.group);
if (ret != HAL_OK)
return CMD_STATUS_FAIL;
} else {
ret = HAL_PWM_ChDeinit(info.ch);
if (ret != HAL_OK)
return CMD_STATUS_FAIL;
}
return CMD_STATUS_OK;
}
enum cmd_status cmd_pwm_start_exec(char *cmd)
{
HAL_Status ret;
char mode_char[8];
uint32_t channel;
int cnt;
cnt = cmd_sscanf(cmd, "c=%u m=%s", &channel, mode_char);
if (cnt != 2)
return CMD_STATUS_INVALID_ARG;
Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char);
if (mode >= PWM_MODENUM) {
CMD_ERR("invalid pwm mode %u\n", mode);
return CMD_STATUS_INVALID_ARG;
}
PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel);
if (info.group == PWM_GROUP_NULL) {
CMD_ERR("invalid pwm channel\n");
return CMD_STATUS_INVALID_ARG;
}
switch (mode) {
case PWM_COMPLE:
ret = HAL_PWM_EnableComplementary(info.group, 1);
if (ret != HAL_OK) {
CMD_ERR("Enable comple error\n");
return CMD_STATUS_FAIL;
}
break;
case PWM_PLUSE:
ret = HAL_PWM_EnableCh(info.ch, PWM_PLUSE_MODE, 1);
if (ret != HAL_OK) {
CMD_ERR("Enable channel error\n");
return CMD_STATUS_FAIL;
}
cmd_msleep(1);
ret = HAL_PWM_OutputPluse(info.ch);
if (ret != HAL_OK) {
CMD_ERR("start pluse fail, it's busy\n");
return CMD_STATUS_FAIL;
}
break;
case PWM_CYCLE:
ret = HAL_PWM_EnableCh(info.ch, PWM_CYCLE_MODE, 1);
if (ret != HAL_OK) {
CMD_ERR("Enable channel error\n");
return CMD_STATUS_FAIL;
}
break;
case PWM_CAPTURE:
ret = HAL_PWM_EnableCh(info.ch, PWM_CAPTURE_MODE, 1);
if (ret != HAL_OK) {
CMD_ERR("Enable channel error\n");
return CMD_STATUS_FAIL;
}
break;
case PWM_DEADZONE:
ret = HAL_PWM_EnableDeadZone(info.group, 1);
if (ret != HAL_OK) {
CMD_ERR("Enable dead zone error\n");
return CMD_STATUS_FAIL;
}
break;
default:
CMD_DBG("invalid pwm mode %u\n", mode);
return CMD_STATUS_INVALID_ARG;
}
return CMD_STATUS_OK;
}
enum cmd_status cmd_pwm_set_exec(char *cmd)
{
uint32_t value;
uint32_t channel;
char function[10];
int cnt;
PWM_IoInfo info;
cnt = cmd_sscanf(cmd, "c=%u m=%s v=%u", &channel, function, &value);
if (cnt != 3)
return CMD_STATUS_INVALID_ARG;
if (cmd_strcmp(function, "comple") == 0 ||
cmd_strcmp(function, "deadzone") == 0)
info = cmd_pwm_channel_analytic(PWM_COMPLE, channel);
else
info = cmd_pwm_channel_analytic(PWM_CYCLE, channel);
if (info.group == PWM_GROUP_NULL) {
CMD_ERR("invalid pwm channel\n");
return CMD_STATUS_INVALID_ARG;
}
if (cmd_strcmp(function, "deadzone") == 0) {
if (value > 255) {
CMD_ERR("deadzone value out of range\n");
return CMD_STATUS_INVALID_ARG;
}
HAL_PWM_SetDeadZoneTime(info.group, value);
} else if (cmd_strcmp(function, "comple") == 0) {
__IO uint32_t *reg;
if (value > 0) {
info.ch = info.group * 2;
reg = &PWM->CH_REG[info.ch].PPR;
value = value * ((*reg & PWM_PPR_ENTIER_CYCLE) >> 16) / 10000;
if (value < 1)
value = 1;
}
HAL_PWM_ComplementarySetDutyRatio(info.group, value);
} else if (cmd_strcmp(function, "cycle") == 0) {
__IO uint32_t *reg;
if (value > 0) {
reg = &PWM->CH_REG[info.ch].PPR;
value = value * ((*reg & PWM_PPR_ENTIER_CYCLE) >> 16) / 10000;
if (value < 1)
value = 1;
}
HAL_PWM_ChSetDutyRatio(info.ch, value);
} else if (cmd_strcmp(function, "pluse") == 0) {
__IO uint32_t *reg;
if (value > 0) {
reg = &PWM->CH_REG[info.ch].PPR;
value = value * ((*reg & PWM_PPR_ENTIER_CYCLE) >> 16) / 10000;
if (value < 1)
value = 1;
}
HAL_PWM_ChSetDutyRatio(info.ch, value);
HAL_PWM_EnableCh(info.ch, PWM_PLUSE_MODE, 1);
} else
return CMD_STATUS_INVALID_ARG;
return CMD_STATUS_OK;
}
static int PWM_DeadTime(uint8_t chGroup)
{
if (chGroup >= PWM_GROUP_NUM)
return -1;
uint32_t p = PWM->PDZCR[chGroup];
p &= PWM_CH_DZ_INV;
return (p >> 8);
}
static int PWM_ReadActCycle(uint8_t ch)
{
if (ch >= PWM_CH_NUM)
return -1;
int p = (int)PWM->CH_REG[ch].PPR;
return p;
}
static PWM_Polarity _PWM_Polarity(uint8_t ch)
{
if ((PWM->CH_REG[ch].PCR & PWM_PCR_ACT_STA) > 0)
return PWM_HIGHLEVE;
return PWM_LOWLEVE;
}
static Cmd_PwmMode PWM_ChMode(uint8_t ch)
{
__IO uint32_t *reg = NULL;
reg = &PWM->CH_REG[ch].PCR;
if ((*reg & PWM_PCR_MODE) > 0)
return PWM_PLUSE;
return PWM_CYCLE;
}
int PWM_DeadZoneEnable(PWM_GROUP_ID chGroup)
{
if (chGroup >= PWM_GROUP_NUM)
return -1;
return PWM->PDZCR[chGroup]&PWM_CH_DZ_EN;
}
void capture_irq(void *arg, PWM_IrqEvent event)
{
PWM_CapResult result;
Cmd_Capinfo *cap_info = (Cmd_Capinfo *)arg;
if ((event != PWM_IRQ_RISEEDGE)
&& (PWM_ChMode(cap_info->input_ch) != PWM_PLUSE))
return;
if (cap_info->count < cap_info->num) {
if (PWM_ChMode(cap_info->input_ch) == PWM_PLUSE)
result = HAL_PWM_CaptureResult(PWM_CAP_PLUSE, cap_info->cap_ch);
else
result = HAL_PWM_CaptureResult(PWM_CAP_CYCLE, cap_info->cap_ch);
if (result.highLevelTime) {
cap_info->count += 1;
if ((cap_info->count == 1)
&& (PWM_ChMode(cap_info->input_ch) != PWM_PLUSE))
return;
cap_info->high_time += result.highLevelTime;
cap_info->low_time += result.lowLevelTime;
}
} else
HAL_PWM_DisableIRQ(cap_info->cap_ch);
}
enum cmd_status cmd_pwm_get_exec(char *cmd)
{
uint32_t channel;
uint32_t src_signal;
char function[8];
uint32_t num = 0;
uint32_t value_deviation = 0;
uint32_t hz_deviation = 0;
int cnt;
HAL_Status ret;
PWM_IrqParam irq_cfg;
cnt = cmd_sscanf(cmd, "c=%u m=%s n=%u input_ch=%u dv=%u dh=%u", &channel,
function, &num, &src_signal, &value_deviation,
&hz_deviation);
if (cnt != 6) {
CMD_ERR("miss param, %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
if (num > 1000) {
CMD_ERR("The n value out of range\n");
return CMD_STATUS_INVALID_ARG;
}
if (value_deviation > 100 || hz_deviation > 100) {
CMD_ERR("The value out of range\n");
return CMD_STATUS_FAIL;
}
PWM_IoInfo info;
info = cmd_pwm_channel_analytic(PWM_CYCLE, src_signal);
if (info.group == PWM_GROUP_NULL) {
CMD_ERR("invalid src_signal\n");
return CMD_STATUS_INVALID_ARG;
}
info = cmd_pwm_channel_analytic(PWM_CAPTURE, channel);
if (info.group == PWM_GROUP_NULL) {
CMD_ERR("invalid ch \n");
return CMD_STATUS_INVALID_ARG;
}
Cmd_Capinfo *cap_info = private_cap;
cap_info[channel].cap_ch = channel;
cap_info[channel].num = num + 1;
cap_info[channel].count = 0;
cap_info[channel].d_hz = hz_deviation;
cap_info[channel].d_value = value_deviation;
cap_info[channel].input_ch = src_signal;
cap_info[channel].high_time = 0;
cap_info[channel].low_time = 0;
cap_info[channel].input_mode = PWM_ChMode(src_signal);
if (cap_info[channel].input_mode == PWM_PLUSE)
cap_info[channel].num = num;
irq_cfg.arg = &cap_info[channel];
irq_cfg.callback = capture_irq;
irq_cfg.event = PWM_IRQ_BOTHEDGE;
ret = HAL_PWM_EnableIRQ(channel, &irq_cfg);
if (ret != HAL_OK) {
CMD_ERR("Enable irq error\n");
return CMD_STATUS_FAIL;
}
return CMD_STATUS_OK;
}
typedef enum {
CAP_ERROR,
CAP_BUSY,
CAP_OK,
} CAP_RESULT;
CAP_RESULT cmd_CapResult(uint8_t ch)
{
CAP_RESULT ret;
uint16_t input_right_period = 0, input_right_h_time = 0;
uint8_t deadzone_time = 0;
Cmd_Capinfo *info = &private_cap[ch];
if (PWM_DeadZoneEnable(info->input_ch/ 2))
deadzone_time = PWM_DeadTime(info->input_ch / 2);
if (info->count == info->num) {
uint32_t cap_period = 0;
uint32_t cap_h_time = 0;
__IO uint32_t *reg = &PWM->CH_REG[info->input_ch].PPR;
input_right_period = (*reg & PWM_PPR_ENTIER_CYCLE) >> 16;
input_right_h_time = PWM_ReadActCycle(info->input_ch);
if (_PWM_Polarity(info->input_ch) == PWM_LOWLEVE)
input_right_h_time = input_right_period- input_right_h_time;
if (deadzone_time > 0) {
input_right_h_time -= deadzone_time;
}
if (PWM_ChMode(info->input_ch) == PWM_CYCLE) {
cap_h_time = info->high_time / (info->num - 1);
cap_period = cap_h_time + info->low_time / (info->num - 1);
if (abs(cap_h_time - input_right_h_time) <= info->d_value &&
abs(cap_period - input_right_period) <= info->d_hz)
ret = CAP_OK;
else
ret = CAP_ERROR;
} else {
cap_h_time = info->high_time / info->num;
if (abs(cap_h_time - input_right_h_time) <= info->d_value)
ret = CAP_OK;
else
ret = CAP_ERROR;
}
memset(info, 0, sizeof(Cmd_Capinfo));
return ret;
}
return CAP_BUSY;
}
enum cmd_status cmd_pwm_get_result_exec(char *cmd)
{
uint32_t channel;
int cnt;
cnt = cmd_sscanf(cmd, "c=%u ", &channel);
if (cnt != 1)
return CMD_STATUS_INVALID_ARG;
PWM_IoInfo info = cmd_pwm_channel_analytic(PWM_CAPTURE, channel);
if (info.group == PWM_GROUP_NULL) {
CMD_ERR("invalid pwm channel\n");
return CMD_STATUS_INVALID_ARG;
}
CAP_RESULT ret = cmd_CapResult(channel);
if (ret == CAP_BUSY) {
CMD_ERR("PWM%d Capture BUSY\n", info.ch);
return CMD_STATUS_ERROR_MIN;
} else if (ret == CAP_ERROR) {
CMD_ERR("PWM%d Capture ERROR\n", info.ch);
return CMD_STATUS_FAIL;
} else if (ret == CAP_OK) {
return CMD_STATUS_OK;
}
return CMD_STATUS_OK;
}
enum cmd_status cmd_pwm_stop_exec(char *cmd)
{
char mode_char[8];
uint32_t channel;
int cnt;
cnt = cmd_sscanf(cmd, "c=%u m=%s", &channel, mode_char);
if (cnt != 2)
return CMD_STATUS_INVALID_ARG;
Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char);
if (mode >= PWM_MODENUM) {
CMD_ERR("invalid pwm mode %u\n", mode);
return CMD_STATUS_INVALID_ARG;
}
PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel);
if (info.group == PWM_GROUP_NULL) {
CMD_ERR("invalid pwm channel\n");
return CMD_STATUS_INVALID_ARG;
}
switch (mode) {
case PWM_COMPLE:
HAL_PWM_EnableComplementary(info.group, 0);
HAL_PWM_SetDeadZoneTime(info.group, 0);
HAL_PWM_EnableDeadZone(info.group, 0);
break;
case PWM_PLUSE:
HAL_PWM_EnableCh(info.ch, PWM_PLUSE_MODE, 0);
break;
case PWM_CYCLE:
HAL_PWM_EnableCh(info.ch, PWM_CYCLE_MODE, 0);
break;
case PWM_CAPTURE:
HAL_PWM_EnableCh(info.ch, PWM_CAPTURE_MODE, 0);
HAL_PWM_DisableIRQ(info.ch);
break;
case PWM_DEADZONE:
HAL_PWM_SetDeadZoneTime(info.group, 0);
HAL_PWM_EnableDeadZone(info.group, 0);
break;
default:
CMD_ERR("invalid pwm mode\n");
return CMD_STATUS_INVALID_ARG;
}
return CMD_STATUS_OK;
}
/*
* driver commands
*/
static const struct cmd_data g_pwm_cmds[] = {
{ "deinit", cmd_pwm_deinit_exec },
{ "config", cmd_pwm_config_exec },
{ "start", cmd_pwm_start_exec },
{ "set", cmd_pwm_set_exec },
{ "get", cmd_pwm_get_exec },
{ "get_result", cmd_pwm_get_result_exec },
{ "stop", cmd_pwm_stop_exec },
};
enum cmd_status cmd_pwm_exec(char *cmd)
{
return cmd_exec(cmd, g_pwm_cmds, cmd_nitems(g_pwm_cmds));
}