Files
JBao_Door_Clamp_FW/apps/usr_periph/bsp_hw.c
T
wuchuyuan 93c1943992 1、充电时不再叠待机绿灯;意外复位不再跳过 3s 开机按键判定
2、电机停转去掉反接刹车改自由滑行,避免电流冲击触发 LVD 复位;
3、关机直接 power_set_soft_poweroff,跳过耳机 SDK 优雅关机流程,按键关机从十几秒压到瞬间掉电
2026-08-25 20:05:59 +08:00

520 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @file bsp_hw.c
* @brief CBC2 板级 GPIO / ADC:电机 H 桥、LED、按键、充电检测
* @author cyWu <1917507415@qq.com>
* @date 2026.08.24
* @version V1.5.0
* @history
* - V1.0.0, 2026.08.21, cyWu, 首次发布
* - V1.1.0, 2026.08.24, cyWu, 停转反转刹车;去掉限位脚
* - V1.3.0, 2026.08.24, cyWu, 电机改 MCPWM 占空比降速
* - V1.4.0, 2026.08.24, cyWu, 增加按占空比驱动,供玩法变速
* - V1.5.0, 2026.08.25, cyWu, 空闲关闭 MCPWM 时钟
* - V1.6.0, 2026.08.25, cyWu, 去掉停转反接刹车,改自由滑行停止,避免刹车
* 电流冲击拉垮供电轨触发 LVD 复位
******************************************************************************/
#include "system/includes.h"
#include "asm/gpio.h"
#include "asm/adc_api.h"
#include "asm/charge.h"
#include "asm/mcpwm.h"
#include "bsp_hw.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[BSP_HW]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
static uint8_t s_initialized;
/** 电机状态,替代原来散落的 7 个 static */
typedef struct {
BspMotorDir_t dir; /**< 当前实际方向 */
BspMotorDir_t pending; /**< 换向死区结束后要切到的方向 */
uint16_t duty; /**< 当前实际占空比 */
uint16_t pending_duty; /**< 换向死区结束后要用的占空比 */
uint16_t switch_ms; /**< 换向死区剩余时长,0=空闲/不在换向中 */
u32 switch_t0; /**< 换向死区起始时间戳 */
uint8_t pwm_clk_on; /**< 1=MCPWM 时钟已打开 */
} BspMotorCtx_t;
static BspMotorCtx_t s_motor;
extern u32 timer_get_ms(void);
#if (BOARD_LED_ENABLE || BOARD_MOTOR_ENABLE)
/**
* @brief 配置 GPIO 为推挽输出
* @param pin GPIO 编号
* @param value 输出初值
* @return 无
*/
static void bsp_pin_output(u32 pin, int value)
{
gpio_set_pull_up(pin, 0);
gpio_set_pull_down(pin, 0);
gpio_set_die(pin, 1);
gpio_direction_output(pin, value);
}
#endif
#if (BOARD_KEY_ENABLE || BOARD_CHG_ENABLE)
/**
* @brief 配置 GPIO 为数字输入(可内部上拉)
* @param pin GPIO 编号
* @param pull_up 1=内部上拉,0=不上拉
* @return 无
*/
static void bsp_pin_input(u32 pin, int pull_up)
{
gpio_direction_output(pin, 1);
gpio_set_direction(pin, 1);
gpio_set_pull_down(pin, 0);
gpio_set_pull_up(pin, pull_up ? 1 : 0);
gpio_set_die(pin, 1);
}
#endif
#if BOARD_MOTOR_ENABLE
/**
* @brief 初始化 INA/INB 两路 MCPWM,初始占空比 0(停转)
* @return 无
*/
static void bsp_motor_pwm_init(void)
{
struct pwm_platform_data pwm;
gpio_set_pull_up(BOARD_MOTOR_INA_PIN, 0);
gpio_set_pull_down(BOARD_MOTOR_INA_PIN, 0);
gpio_set_die(BOARD_MOTOR_INA_PIN, 1);
gpio_set_pull_up(BOARD_MOTOR_INB_PIN, 0);
gpio_set_pull_down(BOARD_MOTOR_INB_PIN, 0);
gpio_set_die(BOARD_MOTOR_INB_PIN, 1);
pwm.pwm_aligned_mode = pwm_edge_aligned;
pwm.frequency = BOARD_MOTOR_PWM_HZ;
pwm.duty = 0;
pwm.l_pin = (u8) - 1;
pwm.complementary_en = 0;
pwm.pwm_ch_num = pwm_ch0;
pwm.h_pin = BOARD_MOTOR_INA_PIN;
mcpwm_init(&pwm);
pwm.pwm_ch_num = pwm_ch1;
pwm.h_pin = BOARD_MOTOR_INB_PIN;
mcpwm_init(&pwm);
mcpwm_set_duty(pwm_ch0, 0);
mcpwm_set_duty(pwm_ch1, 0);
s_motor.pwm_clk_on = 1;
log_info("motor pwm %uHz duty=%u/10000\n",
(unsigned)BOARD_MOTOR_PWM_HZ, (unsigned)BOARD_MOTOR_PWM_DUTY);
}
/**
* @brief 打开 MCPWM 并绑回 INA/INB
* @return 无
*/
static void bsp_motor_pwm_clock_on(void)
{
if (s_motor.pwm_clk_on) {
return;
}
mcpwm_open(pwm_ch0);
mcpwm_open(pwm_ch1);
gpio_och_sel_output_signal(BOARD_MOTOR_INA_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
gpio_och_sel_output_signal(BOARD_MOTOR_INB_PIN, OUTPUT_CH_SIGNAL_MC_PWM1_H);
gpio_set_direction(BOARD_MOTOR_INA_PIN, 0);
gpio_set_direction(BOARD_MOTOR_INB_PIN, 0);
s_motor.pwm_clk_on = 1;
}
/**
* @brief 关闭 MCPWM 时钟,INA/INB 拉低(待机必关)
* @return 无
*/
static void bsp_motor_pwm_clock_off(void)
{
mcpwm_set_duty(pwm_ch0, 0);
mcpwm_set_duty(pwm_ch1, 0);
if (s_motor.pwm_clk_on) {
mcpwm_close(pwm_ch0);
mcpwm_close(pwm_ch1);
gpio_och_disable_output_signal(BOARD_MOTOR_INA_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
gpio_och_disable_output_signal(BOARD_MOTOR_INB_PIN, OUTPUT_CH_SIGNAL_MC_PWM1_H);
JL_MCPWM->MCPWM_CON0 = 0;
s_motor.pwm_clk_on = 0;
}
gpio_direction_output(BOARD_MOTOR_INA_PIN, 0);
gpio_direction_output(BOARD_MOTOR_INB_PIN, 0);
}
/**
* @brief 立刻改 INA/INB PWM。运转脚输出 duty,另一脚 0%STOP=两路 0%
* @param dir 目标方向
* @param duty 占空比 0~10000
* @return 无
*/
static void bsp_motor_gpio(BspMotorDir_t dir, uint16_t duty)
{
u16 ina_duty = 0;
u16 inb_duty = 0;
if (duty > 10000) {
duty = 10000;
}
if (dir == BSP_MOTOR_IN) {
if (BOARD_MOTOR_IN_INA_HIGH) {
ina_duty = duty;
} else {
inb_duty = duty;
}
} else if (dir == BSP_MOTOR_OUT) {
if (BOARD_MOTOR_IN_INA_HIGH) {
inb_duty = duty;
} else {
ina_duty = duty;
}
}
if ((ina_duty == 0) && (inb_duty == 0)) {
/* 换向死区(BOARD_MOTOR_REVERSE_MS)、刹车瞬间也会短暂占空比为 0,
* 但马上(1~30ms 内)还要再驱动,不能当成“真空闲”去关时钟:
* 频繁 close/open MCPWM 外设比单纯调占空比激进得多,玩法换向越快
* 关得越勤,有卡死风险。这里只清占空比(两脚拉低,电气效果一样),
* 真正的空闲关时钟统一交给 bsp_hw_sleep_pwm_off()——它在系统真的
* 要进轻睡前才调用,并且会先查 bsp_motor_is_busy()。 */
mcpwm_set_duty(pwm_ch0, 0);
mcpwm_set_duty(pwm_ch1, 0);
return;
}
bsp_motor_pwm_clock_on();
mcpwm_set_duty(pwm_ch0, ina_duty);
mcpwm_set_duty(pwm_ch1, inb_duty);
}
#endif
/**
* @brief 初始化 GPIO / 充电 ADC
* @return BSP_HW_OK=成功
*/
BspHw_Ret_t bsp_hw_init(void)
{
if (s_initialized) {
return BSP_HW_OK;
}
#if BOARD_LED_ENABLE
bsp_pin_output(BOARD_LED_RED_PIN, !BOARD_LED_ON_LEVEL);
bsp_pin_output(BOARD_LED_GREEN_PIN, !BOARD_LED_ON_LEVEL);
#endif
#if BOARD_KEY_ENABLE
bsp_pin_input(BOARD_KEY_PIN, 1);
#endif
#if BOARD_CHG_ENABLE
/* PORTG 作数字输入必须开 dieh,否则读不到 ME4054 CHG */
gpio_disable_fun_output_port(BOARD_CHG_PIN);
gpio_set_pull_up(BOARD_CHG_PIN, 0);
gpio_set_pull_down(BOARD_CHG_PIN, 0);
gpio_set_die(BOARD_CHG_PIN, 1);
gpio_set_dieh(BOARD_CHG_PIN, 1);
gpio_direction_input(BOARD_CHG_PIN);
adc_add_sample_ch(AD_CH_LDO5V);
adc_set_sample_freq(AD_CH_LDO5V, 500);
#endif
#if BOARD_MOTOR_ENABLE
bsp_motor_pwm_init();
s_motor.dir = BSP_MOTOR_STOP;
s_motor.pending = BSP_MOTOR_STOP;
s_motor.duty = 0;
s_motor.pending_duty = 0;
s_motor.switch_ms = 0;
#if BOARD_MOTOR_PWM_IDLE_CLOSE
bsp_motor_pwm_clock_off();
#endif
#endif
s_initialized = 1;
log_info("hw init led=%d key=%d chg=%d motor=%d\n",
BOARD_LED_ENABLE, BOARD_KEY_ENABLE, BOARD_CHG_ENABLE,
BOARD_MOTOR_ENABLE);
return BSP_HW_OK;
}
/**
* @brief 查询板级硬件是否已初始化
* @return BSP_HW_OK=已初始化,BSP_HW_ERR_NOT_INIT=未初始化
*/
BspHw_Ret_t bsp_hw_get_status(void)
{
return s_initialized ? BSP_HW_OK : BSP_HW_ERR_NOT_INIT;
}
/**
* @brief 红灯开关
* @param on 1=亮,0=灭
* @return 无
*/
void bsp_led_red_set(uint8_t on)
{
#if BOARD_LED_ENABLE
gpio_direction_output(BOARD_LED_RED_PIN, on ? BOARD_LED_ON_LEVEL : !BOARD_LED_ON_LEVEL);
#else
(void)on;
#endif
}
/**
* @brief 绿灯开关
* @param on 1=亮,0=灭
* @return 无
*/
void bsp_led_green_set(uint8_t on)
{
#if BOARD_LED_ENABLE
gpio_direction_output(BOARD_LED_GREEN_PIN, on ? BOARD_LED_ON_LEVEL : !BOARD_LED_ON_LEVEL);
#else
(void)on;
#endif
}
/**
* @brief 红绿灯全灭
* @return 无
*/
void bsp_led_all_off(void)
{
bsp_led_red_set(0);
bsp_led_green_set(0);
}
/**
* @brief 设置电机方向。STOP 时直接切断驱动,自由滑行到停(不做反接刹车)
* @param dir 收绳 / 放绳 / 停转
* @return 无
*/
void bsp_motor_set(BspMotorDir_t dir)
{
bsp_motor_set_pwm(dir, (dir == BSP_MOTOR_STOP) ? 0 : BOARD_MOTOR_PWM_DUTY);
}
/**
* @brief 设置电机方向与 PWM 占空比
* @param dir 收绳 / 放绳 / 停转
* @param duty 占空比 0~10000STOP 时按 0 处理
* @return 无
*/
void bsp_motor_set_pwm(BspMotorDir_t dir, uint16_t duty)
{
#if !BOARD_MOTOR_ENABLE
(void)dir;
(void)duty;
return;
#else
if (duty > 10000) {
duty = 10000;
}
if (dir == BSP_MOTOR_STOP) {
duty = 0;
}
/* 同向只改占空比:直接改 PWM,不走换向死区 */
if (dir == s_motor.dir && s_motor.switch_ms == 0) {
if (duty == s_motor.duty) {
return;
}
s_motor.duty = duty;
s_motor.pending = dir;
s_motor.pending_duty = duty;
bsp_motor_gpio(dir, duty);
return;
}
/* 停转:直接切断驱动,让电机自由滑行到停,不做反接刹车——反接刹车电流比
* 正常驱动大(电池要同时顶住反向驱动电流和电机反电动势),实测会把供电轨
* 拉低触发 LVD 复位。哪怕正在换向死区里,也直接取消死区改成停。 */
if (dir == BSP_MOTOR_STOP) {
s_motor.switch_ms = 0;
s_motor.dir = BSP_MOTOR_STOP;
s_motor.pending = BSP_MOTOR_STOP;
s_motor.duty = 0;
s_motor.pending_duty = 0;
bsp_motor_gpio(BSP_MOTOR_STOP, 0);
return;
}
/* 换向:先松开再输出,避免 H 桥直通 */
if (s_motor.dir != BSP_MOTOR_STOP && dir != s_motor.dir) {
bsp_motor_gpio(BSP_MOTOR_STOP, 0);
s_motor.pending = dir;
s_motor.pending_duty = duty;
s_motor.switch_ms = BOARD_MOTOR_REVERSE_MS;
s_motor.switch_t0 = timer_get_ms();
s_motor.dir = BSP_MOTOR_STOP;
s_motor.duty = 0;
return;
}
s_motor.switch_ms = 0;
s_motor.dir = dir;
s_motor.pending = dir;
s_motor.duty = duty;
s_motor.pending_duty = duty;
bsp_motor_gpio(dir, duty);
#endif
}
/**
* @brief 推进换向死区计时,按真实毫秒计时;死区结束后切到目标方向/占空比
* @return 无
*/
void bsp_motor_switch_tick(void)
{
#if !BOARD_MOTOR_ENABLE
return;
#else
if (s_motor.switch_ms == 0) {
return;
}
if ((timer_get_ms() - s_motor.switch_t0) < s_motor.switch_ms) {
return;
}
s_motor.switch_ms = 0;
s_motor.dir = s_motor.pending;
s_motor.duty = s_motor.pending_duty;
bsp_motor_gpio(s_motor.dir, s_motor.duty);
#endif
}
/**
* @brief 读取按键电平
* @return 1=按下,0=未按下
*/
uint8_t bsp_key_is_pressed(void)
{
#if BOARD_KEY_ENABLE
return (uint8_t)(gpio_read(BOARD_KEY_PIN) == BOARD_KEY_ACTIVE_LEVEL);
#else
return 0;
#endif
}
/**
* @brief 读取 ME4054 CHG 脚:充电中为低
* @return 1=充电中,0=未充电
*/
uint8_t bsp_chg_is_low(void)
{
#if BOARD_CHG_ENABLE
return (uint8_t)(gpio_read(BOARD_CHG_PIN) == BOARD_CHG_CHARGING_LEVEL);
#else
return 0;
#endif
}
/**
* @brief 读取电池电压
* @return 电池电压,单位 mV
*/
uint16_t bsp_vbat_mv(void)
{
return (uint16_t)(adc_get_voltage(AD_CH_VBAT) * 4);
}
/**
* @brief 读取 5V 口电压,用于判断 USB
* @return VPWR 电压,单位 mV
*/
uint16_t bsp_vpwr_mv(void)
{
return (uint16_t)(adc_get_voltage(AD_CH_LDO5V) * 4);
}
/**
* @brief USB 是否在位(以 ADC 为主,比较器为辅)
* @return 1=插入,0=未插入
*/
uint8_t bsp_vpwr_is_online(void)
{
#if BOARD_CHG_ENABLE
uint16_t mv = bsp_vpwr_mv();
/* 以 ADC 为准:拔电后比较器可能仍为 1,但电压已经掉到几百 mV */
if (mv >= BOARD_VPWR_ONLINE_MV) {
return 1;
}
if (mv <= BOARD_VPWR_OFF_MV) {
return 0;
}
if (get_ldo5v_online_hw() || get_lvcmp_det()) {
return 1;
}
return 0;
#else
return 0;
#endif
}
/**
* @brief 轻睡前关掉空闲电机 PWM(电机忙碌时不关,避免玩法被掐断)
* @return 无
*/
void bsp_hw_sleep_pwm_off(void)
{
#if BOARD_MOTOR_ENABLE && BOARD_MOTOR_PWM_IDLE_CLOSE
if (!s_initialized) {
return;
}
if (bsp_motor_is_busy()) {
return;
}
bsp_motor_pwm_clock_off();
#endif
}
/**
* @brief 电机是否还在转或处于换向死区
* @return 1=忙碌,0=已停且 PWM 已关
*/
uint8_t bsp_motor_is_busy(void)
{
#if !BOARD_MOTOR_ENABLE
return 0;
#else
if (s_motor.switch_ms) {
return 1;
}
if (s_motor.dir != BSP_MOTOR_STOP) {
return 1;
}
if (s_motor.pending != BSP_MOTOR_STOP) {
return 1;
}
return 0;
#endif
}
/**
* @brief 进深睡前把电机 PWM 关掉并拉低,灯全灭
* @return 无
*/
void bsp_hw_enter_sleep_io(void)
{
bsp_motor_set(BSP_MOTOR_STOP);
#if BOARD_MOTOR_ENABLE
s_motor.switch_ms = 0;
s_motor.dir = BSP_MOTOR_STOP;
s_motor.pending = BSP_MOTOR_STOP;
s_motor.duty = 0;
bsp_motor_pwm_clock_off();
#endif
bsp_led_all_off();
}