524 lines
13 KiB
C
524 lines
13 KiB
C
/******************************************************************************
|
||
* @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 时钟
|
||
******************************************************************************/
|
||
|
||
#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 brake_ms; /**< 换向死区/反转刹车剩余时长,0=空闲 */
|
||
u32 brake_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)) {
|
||
#if BOARD_MOTOR_PWM_IDLE_CLOSE
|
||
bsp_motor_pwm_clock_off();
|
||
return;
|
||
#endif
|
||
} else {
|
||
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.brake_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~10000;STOP 时按 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.brake_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;
|
||
}
|
||
|
||
/* 已经在反转刹车等到停,忽略重复 STOP */
|
||
if (dir == BSP_MOTOR_STOP && s_motor.pending == BSP_MOTOR_STOP && s_motor.brake_ms) {
|
||
return;
|
||
}
|
||
|
||
/* 正在转时要停:先反向驱动再滑行,短接对此驱动无效 */
|
||
if (dir == BSP_MOTOR_STOP
|
||
&& (s_motor.dir == BSP_MOTOR_IN || s_motor.dir == BSP_MOTOR_OUT)) {
|
||
BspMotorDir_t rev = (s_motor.dir == BSP_MOTOR_OUT) ? BSP_MOTOR_IN : BSP_MOTOR_OUT;
|
||
uint16_t brake_duty = s_motor.duty ? s_motor.duty : BOARD_MOTOR_PWM_DUTY;
|
||
|
||
bsp_motor_gpio(rev, brake_duty);
|
||
s_motor.dir = rev;
|
||
s_motor.duty = brake_duty;
|
||
s_motor.pending = BSP_MOTOR_STOP;
|
||
s_motor.pending_duty = 0;
|
||
s_motor.brake_ms = BOARD_MOTOR_STOP_BRAKE_MS;
|
||
s_motor.brake_t0 = timer_get_ms();
|
||
log_info("motor rev-brake %ums duty=%u\n",
|
||
(unsigned)BOARD_MOTOR_STOP_BRAKE_MS, (unsigned)brake_duty);
|
||
return;
|
||
}
|
||
|
||
/* 换向:先松开再输出,避免 H 桥直通 */
|
||
if (dir != BSP_MOTOR_STOP && 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.brake_ms = BOARD_MOTOR_REVERSE_MS;
|
||
s_motor.brake_t0 = timer_get_ms();
|
||
s_motor.dir = BSP_MOTOR_STOP;
|
||
s_motor.duty = 0;
|
||
return;
|
||
}
|
||
|
||
s_motor.brake_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_brake_tick(void)
|
||
{
|
||
#if !BOARD_MOTOR_ENABLE
|
||
return;
|
||
#else
|
||
if (s_motor.brake_ms == 0) {
|
||
return;
|
||
}
|
||
if ((timer_get_ms() - s_motor.brake_t0) < s_motor.brake_ms) {
|
||
return;
|
||
}
|
||
s_motor.brake_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.brake_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.brake_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();
|
||
}
|