接入温湿度风扇产品固件(仲裁/条件/定时/显示),并补齐规格书与外设驱动
新增 usr_app 产品逻辑:手动>定时>条件仲裁、条件控温湿、定时任务、数码管显示、 按键/LED、风扇档位、NVM 掉电恢复、配对与开关机,上电按规格恢复上次状态。 新增 usr_periph:AHT20、CT1642、板级引脚与硬件封装;见宝协议 cond_config 改为 7 字节编解码,并接入产品 DP。 补充《温湿度风扇_固件需求规格书》,工程加入对应源文件。 .gitignore 忽略 WorkBuddy/Cursor 本地会话
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
/******************************************************************************
|
||||
* @file board_pin.h
|
||||
* @brief CBR3 温湿度风扇板级引脚、电平与时序(换板只改本文件)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 Bring-up)
|
||||
*
|
||||
* @note 产品 GPIO 全部集中到本文件;板级 board_jl701n_demo_cfg.h 只保留
|
||||
* UART0_TX=PG6 一项改动。CT1642 用 PG0(CLK)/PG1(DATA),AHT20 用
|
||||
* PC4(SCL)/PC5(SDA),两路 IO 独立、无总线共享(2026-08-29 改板飞线分离,
|
||||
* 原 PC4/PC5 共线 + bsp_bus 分时机制整体删除)。
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __BOARD_PIN_H__
|
||||
#define __BOARD_PIN_H__
|
||||
|
||||
#include "asm/gpio.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 功能开关:某路硬件异常时可单独关掉,不影响配网 */
|
||||
/* 0 时对应模块 init/run 直接 return,GPIO 不配,日志打一次 xxx disabled */
|
||||
/*============================================================================*/
|
||||
|
||||
#define BOARD_LED_ENABLE 1 /**< 1=状态 LED PC2(低亮) */
|
||||
#define BOARD_KEY_ENABLE 1 /**< 1=按键 PB1(低按下) */
|
||||
#define BOARD_FAN_ENABLE 1 /**< 1=风扇 MCPWM PC3(高导通) */
|
||||
#define BOARD_DISP_ENABLE 1 /**< 1=CT1642 数码管(PG0/PG1 独立) */
|
||||
#define BOARD_TH_ENABLE 1 /**< 1=AHT20 温湿度(PC4/PC5 独立) */
|
||||
#define BOARD_MODE_ENABLE 1 /* 通风/强力状态机;P1 开 */
|
||||
#define BOARD_COND_ENABLE 1 /* 条件引擎;P2 开 */
|
||||
#define BOARD_TIMER_ENABLE 1 /* 定时引擎;P2 开 */
|
||||
#define BOARD_NVM_ENABLE 1 /* VM 持久化;P1 开 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* GPIO */
|
||||
/*============================================================================*/
|
||||
|
||||
#define BOARD_UART_TX_PIN IO_PORTG_06 /**< 调试 UART TX(只发不占 PC5) */
|
||||
#define BOARD_DISP_CLK_PIN IO_PORTG_00 /**< CT1642 CLK(独立,飞线 G0) */
|
||||
#define BOARD_DISP_DATA_PIN IO_PORTG_01 /**< CT1642 DATA(独立,飞线 G1) */
|
||||
#define BOARD_TH_SCL_PIN IO_PORTC_04 /**< AHT20 SCLK(独立,推挽驱动) */
|
||||
#define BOARD_TH_SDA_PIN IO_PORTC_05 /**< AHT20 SDA(开漏+内部上拉,外部 4.7~10k 双保险) */
|
||||
#define BOARD_FAN_PIN IO_PORTC_03 /**< 风扇 PWM,高电平导通,MCPWM */
|
||||
#define BOARD_LED_PIN IO_PORTC_02 /**< 状态 LED,低电平亮 */
|
||||
#define BOARD_KEY_PIN IO_PORTB_01 /**< 按键,低电平按下 */
|
||||
|
||||
#define BOARD_LED_ON_LEVEL 0 /**< LED 点亮电平:0=低亮 */
|
||||
#define BOARD_FAN_ON_LEVEL 1 /**< 风扇有效电平:1=高导通 */
|
||||
#define BOARD_KEY_ACTIVE_LEVEL 0 /**< 按键有效电平:0=按下为低 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 按键 / 开机 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define BOARD_KEY_CLICK_MS 1000 /**< 开机后:按下 <1s 松开为短按切档 */
|
||||
#define BOARD_KEY_POWER_MS 3000 /**< 关机态:按住 3s 开机;开机态:按住 3s 关机 */
|
||||
#define BOARD_KEY_PAIR_MS 5000 /**< 关机态:按住 5s 开机并进配对 */
|
||||
#define BOARD_KEY_OTA_MS 12000 /**< 关机态:按住 12s 开机并进 OTA */
|
||||
#define BOARD_KEY_DEBOUNCE_MS 20 /**< 按键消抖 */
|
||||
#define BOARD_PAIR_TIMEOUT_MS (120u * 1000u) /**< 配对超时 120s,失败后保持开机 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* LED(单灯 PC2,事件表见 led_manager.c) */
|
||||
/*============================================================================*/
|
||||
|
||||
#define BOARD_LED_FAST_ON_MS 100 /**< 配对快闪:亮 */
|
||||
#define BOARD_LED_FAST_OFF_MS 100 /**< 配对快闪:灭 */
|
||||
#define BOARD_LED_OTA_ON_MS 200 /**< OTA 闪:亮 */
|
||||
#define BOARD_LED_OTA_OFF_MS 200 /**< OTA 闪:灭 */
|
||||
#define BOARD_LED_PAIR_OK_MS 3000 /**< 配对成功常亮时长(允许直接常亮) */
|
||||
#define BOARD_LED_PAIR_FAIL_MS 3000 /**< 配对失败 200ms 闪提示时长 */
|
||||
#define BOARD_LED_STANDBY_ON 1 /**< 1=开机常亮 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 风扇 PWM */
|
||||
/*============================================================================*/
|
||||
|
||||
#define BOARD_FAN_PWM_HZ 20000 /**< MCPWM 频率(Hz),20k 避开可听啸叫 */
|
||||
#define BOARD_FAN_DUTY_GEAR0 0 /**< 0% */
|
||||
#define BOARD_FAN_DUTY_GEAR1 5000 /**< 50%(用户拍板,2026-08-31) */
|
||||
#define BOARD_FAN_DUTY_GEAR2 7500 /**< 75%(用户拍板,2026-08-31) */
|
||||
#define BOARD_FAN_DUTY_GEAR3 10000 /**< 100% */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 显示(CT1642) */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* CT1642 动态扫描节拍(ms)。
|
||||
* ⚠ CT1642 内部只有单个 12 位锁存器(4 位选 + 8 段码),无按位显示 RAM、不自动扫描,
|
||||
* 一次只点亮当前锁存的那 1 位。必须 MCU 持续轮询 4 位才能 4 位同亮、不闪。
|
||||
* 本节拍每 BOARD_DISP_SCAN_MS 点亮 1 位、4 位轮询 → 每位 4*BOARD_DISP_SCAN_MS 一轮。
|
||||
* 4ms:每位 16ms/62.5Hz、25% 占空,均匀不闪,且不饿死 priority=1 扫描中断里的
|
||||
* 主循环喂狗(实测 2ms 节拍会导致上电硬复位,禁止)。
|
||||
* 注:SCAN_TEST=1 段扫描用"值变化才刷"机制,本节拍不敏感;正常显示(0)与
|
||||
* 位选验证(2)用分时轮询,必须 ≤4ms 否则肉眼可见闪烁。
|
||||
*/
|
||||
#define BOARD_DISP_SCAN_MS 4
|
||||
|
||||
#define BOARD_DISP_BLINK_MS 500 /**< 告警闪烁半周期(P1 用) */
|
||||
|
||||
/**
|
||||
* 告警闪烁调试宏(P1 台上验证用,正常固件保持 0):
|
||||
* 0 = 关:闪烁由 app_disp_set_temp/humi_alarm 驱动(P2 条件引擎喂)
|
||||
* 1 = 强制温度管闪(模拟温度比较成立)
|
||||
* 2 = 强制湿度管闪(模拟湿度比较成立)
|
||||
*/
|
||||
#define BOARD_DISP_ALARM_TEST 0
|
||||
|
||||
/**
|
||||
* CT1642 协议(18bit/帧,CLK 上升沿采样,DATA 仅在 CLK=低时改变):
|
||||
* [0:3] C1-C4 位选(低有效,入参 com 取反后先发 C1)
|
||||
* [4:9] 6 位保留位(固定填 1)
|
||||
* [10:17] Q2-Q9 段码(LSB 先,seg 的 bit0 → Q2 引脚)
|
||||
* 发完 18bit:CLK=高 时 DATA 0→1 = 锁存;CLK=低 时 DATA 0→1 = 输出刷新。
|
||||
* 刷新一位数码管 = 发一帧;4 位连发 4 帧(每帧仅一个 Cx 位选生效)。
|
||||
*
|
||||
* ⚠ P0 扫段:段码/位选与硬件物理映射为"待实测"值,台上用 SCAN_TEST 扫清后修正。
|
||||
* seg_tbl 段码位定义:bit7=DP bit6=G bit5=F bit4=E bit3=D bit2=C bit1=B bit0=A
|
||||
* (bit0 先发 → 落到 Q2 引脚;PCB 上 Q2 接哪段即"软件 A 段"对应的物理段)
|
||||
* COM 位选 → 物理管位映射(2026-08-29 SCAN_TEST=2 实测定型):
|
||||
* C1=湿度十位 C2=湿度个位 C3=温度十位 C4=温度个位(默认假定温/湿相反,已对调)
|
||||
*/
|
||||
#define BOARD_DISP_COM_TEMP_TEN 0x04 /**< C3 = 温度十位(实测定型) */
|
||||
#define BOARD_DISP_COM_TEMP_ONE 0x08 /**< C4 = 温度个位(实测定型) */
|
||||
#define BOARD_DISP_COM_HUMI_TEN 0x01 /**< C1 = 湿度十位(实测定型) */
|
||||
#define BOARD_DISP_COM_HUMI_ONE 0x02 /**< C2 = 湿度个位(实测定型) */
|
||||
|
||||
/**
|
||||
* 扫段调试模式(P0 台上定型用,2026-08-29 已全部定型,正常固件保持 0):
|
||||
* 0 = 正常:无采样时写死 25/60(分时轮询 + 提速移位)
|
||||
* 1 = 段扫描:4 位同显一段,值变化才刷(无鬼影);段序已实测确认 A..G 顺序正确
|
||||
* 2 = 位扫描:4 位分显 1/2/3/4(分时轮询);位序已实测确认 C1=湿十/C2=湿个/C3=温十/C4=温个
|
||||
*/
|
||||
#define BOARD_DISP_SCAN_TEST 0
|
||||
|
||||
/*============================================================================*/
|
||||
/* 传感器(AHT20) */
|
||||
/*============================================================================*/
|
||||
|
||||
#define BOARD_TH_PERIOD_MS 2000 /**< 采集周期 2s(手册要求 >1s,规格书第 2.3/18 章) */
|
||||
#define BOARD_TH_FAIL_MAX 3 /**< 连续失败 3 次判传感器故障 */
|
||||
#define BOARD_AHT20_WAIT_MS 80 /**< 测量等待时长 */
|
||||
#define BOARD_AHT20_I2C_HZ 100000 /**< 软件 I2C 时钟 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 模式(P1 起用,P0 关闭) */
|
||||
/*============================================================================*/
|
||||
|
||||
#define BOARD_MODE_VENT_ON_MS (25u * 60u * 1000u) /**< 通风开段 25min */
|
||||
#define BOARD_MODE_VENT_OFF_MS (5u * 60u * 1000u) /**< 通风停段 5min */
|
||||
#define BOARD_MODE_POWER_ON_MS (15u * 60u * 1000u) /**< 强力一轮 15min */
|
||||
|
||||
#endif /* __BOARD_PIN_H__ */
|
||||
@@ -0,0 +1,436 @@
|
||||
/******************************************************************************
|
||||
* @file bsp_aht20.c
|
||||
* @brief CBR3 AHT20 软件 I2C 驱动实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.3
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布
|
||||
* - V1.0.1, 2026.08.29, cyWu, 修复 SCL 高电平期间推挽输出 SDA 导致的假 STOP
|
||||
* (读数据必 CRC fail 的根因);补 AHT20 0x71 校准检查 + 0xBE 初始化
|
||||
* - V1.0.2, 2026.08.29, cyWu, 排障结束:回退诊断代码(电平自检/地址扫描/IN 直读),
|
||||
* 保留时序修复、内部上拉依赖、总线恢复、0x71/0xBE 初始化流程
|
||||
* - V1.0.3, 2026.08.29, cyWu, IO 分离:AHT20 独占 PC4(SCL)/PC5(SDA),删除
|
||||
* bsp_bus 共线锁与 measure_start/finish 拆分锁,采集恢复一次完成;
|
||||
* PC4/PC5 上拉配置从 bsp_bus_init 移入本文件 init
|
||||
******************************************************************************/
|
||||
|
||||
#include "bsp_aht20.h"
|
||||
#include "board_pin.h"
|
||||
#include "asm/gpio.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[AHT20]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#define AHT20_ADDR_W 0x70 /**< 写地址(7bit 0x38 << 1) */
|
||||
#define AHT20_ADDR_R 0x71 /**< 读地址 */
|
||||
#define AHT20_CMD_MEASURE 0xAC
|
||||
#define AHT20_CMD_INIT 0xBE /**< 初始化校准寄存器命令 */
|
||||
#define AHT20_CMD_SOFTRESET 0xBA /**< 软复位命令(备用) */
|
||||
#define AHT20_READ_LEN 7 /**< status + 20bit humi + 20bit temp + crc */
|
||||
|
||||
static uint8_t s_inited;
|
||||
|
||||
/* 软件 I2C:PC5=SDA(三态开漏,内部上拉 + 外部 4.7~10k 双保险),PC4=SCL(推挽)
|
||||
*
|
||||
* ⚠ 时序铁律:SDA 的方向切换 / 推挽输出只能在 SCL=0 时进行!
|
||||
* 若在 SCL=1 期间把 SDA 推挽输出为高,而从机正拉低 SDA(数据位 0 或 ACK),
|
||||
* 会在 SCL 高电平期间制造 SDA 上升沿 = 从机误判 STOP,传输中止 →
|
||||
* 之后读到的全是上拉的垃圾字节 → CRC fail(V1.0.0 实测 bug)。
|
||||
*
|
||||
* ⚠ IO 分离(V1.0.3):CT1642 已移到 PG0/PG1,本驱动独占 PC4/PC5,
|
||||
* 无总线锁、无模式切换,可直接调用。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief ~2us 延时(24MHz,约 48 条指令),对应 I2C ≤250kHz
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_delay(void)
|
||||
{
|
||||
volatile uint32_t i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
/* nop */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SCL 拉高/拉低(SCL 推挽输出,主控独占驱动,无方向切换)
|
||||
* @param level 1=高,0=低
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_scl(uint8_t level)
|
||||
{
|
||||
gpio_direction_output(BOARD_TH_SCL_PIN, level ? 1 : 0);
|
||||
i2c_delay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置 SDA 输出电平(内部含"输出高=推 1 后切高阻")
|
||||
* @note ⚠ 仅允许在 SCL=0 时调用(START/STOP 条件除外,那两处从机不驱动总线)
|
||||
* @param level 1=高(推 1 后切高阻,靠外部上拉保持),0=低(推挽拉低)
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_sda_out(uint8_t level)
|
||||
{
|
||||
if (level) {
|
||||
gpio_direction_output(BOARD_TH_SDA_PIN, 1);
|
||||
gpio_set_direction(BOARD_TH_SDA_PIN, 1); /* 高阻,外部上拉拉高 */
|
||||
} else {
|
||||
gpio_direction_output(BOARD_TH_SDA_PIN, 0);
|
||||
}
|
||||
i2c_delay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放 SDA 为输入(高阻)
|
||||
* @note ⚠ 仅允许在 SCL=0 时调用;释放后用 i2c_sda_read() 采样
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_sda_release(void)
|
||||
{
|
||||
gpio_direction_output(BOARD_TH_SDA_PIN, 1);
|
||||
gpio_set_direction(BOARD_TH_SDA_PIN, 1);
|
||||
i2c_delay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 采样 SDA 电平(调用时 SDA 必处于输入态)
|
||||
* @return 1=高,0=低
|
||||
*/
|
||||
static uint8_t i2c_sda_read(void)
|
||||
{
|
||||
return (uint8_t)(gpio_read(BOARD_TH_SDA_PIN) ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C START:SCL 高时 SDA 高→低
|
||||
* @note 起始时 SCL 必为低(上一帧收尾),先释放 SDA 再抬 SCL
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_start(void)
|
||||
{
|
||||
i2c_scl(0);
|
||||
i2c_sda_out(1);
|
||||
i2c_scl(1);
|
||||
i2c_sda_out(0); /* SCL 高时 1→0 = START */
|
||||
i2c_scl(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C STOP:SCL 高时 SDA 低→高
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_stop(void)
|
||||
{
|
||||
i2c_scl(0);
|
||||
i2c_sda_out(0);
|
||||
i2c_scl(1);
|
||||
i2c_sda_out(1); /* SCL 高时 0→1 = STOP;此时从机已释放总线,推 1 无冲突 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写一个字节(MSB 先;每位 SDA 都在 SCL 低电平期间变化)
|
||||
* @param byte 数据
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_write_byte(uint8_t byte)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
i2c_sda_out((uint8_t)((byte & 0x80u) ? 1 : 0));
|
||||
byte <<= 1;
|
||||
i2c_scl(1);
|
||||
i2c_scl(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 等待从机 ACK(第 9 个时钟;SDA 方向切换只在 SCL=0 时做)
|
||||
* @return 0=收到 ACK,1=NACK
|
||||
*/
|
||||
static uint8_t i2c_wait_ack(void)
|
||||
{
|
||||
uint8_t ack;
|
||||
|
||||
i2c_scl(0);
|
||||
i2c_sda_release(); /* SCL 低时切输入,绝不污染 SCL 高电平区间 */
|
||||
i2c_scl(1);
|
||||
ack = i2c_sda_read();
|
||||
i2c_scl(0);
|
||||
return ack; /* 0=ACK(从机拉低),1=NACK */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读一个字节(先在 SCL=0 时释放 SDA,循环内只采样不切方向)
|
||||
* @param ack 1=收完发 ACK(继续读),0=发 NACK(最后一字节)
|
||||
* @return 读到的字节
|
||||
*/
|
||||
static uint8_t i2c_read_byte(uint8_t ack)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t byte = 0;
|
||||
|
||||
i2c_sda_release();
|
||||
for (i = 0; i < 8; i++) {
|
||||
i2c_scl(1);
|
||||
byte = (uint8_t)((byte << 1) | i2c_sda_read());
|
||||
i2c_scl(0);
|
||||
}
|
||||
i2c_sda_out(ack ? 0 : 1); /* 第 9 位:ACK 拉低 / NACK 释放(SCL 低时设置) */
|
||||
i2c_scl(1);
|
||||
i2c_scl(0);
|
||||
i2c_sda_out(1);
|
||||
return byte;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CRC8(poly 0x31,init 0xFF)
|
||||
* @param data 数据指针
|
||||
* @param len 长度
|
||||
* @return CRC 值
|
||||
*/
|
||||
static uint8_t aht20_crc8(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0xFF;
|
||||
uint8_t i, b;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
crc ^= data[i];
|
||||
for (b = 0; b < 8; b++) {
|
||||
crc = (uint8_t)((crc & 0x80u) ? ((crc << 1) ^ 0x31u) : (crc << 1));
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 总线恢复:发 9 个 SCL 时钟 + STOP,解开"从机拉死 SDA"的卡死态
|
||||
* @note 卡死场景:之前某次传输中途被复位/打断,从机停在"还有 N 位没发完",
|
||||
* 持续拉低 SDA,此后所有 START/ACK 全废。标准 I2C 恢复手段。
|
||||
* @return 无
|
||||
*/
|
||||
static void i2c_bus_recover(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
i2c_scl(0);
|
||||
i2c_sda_release(); /* 释放 SDA(SCL=低时切输入) */
|
||||
for (i = 0; i < 9; i++) {
|
||||
i2c_scl(1);
|
||||
if (i2c_sda_read()) {
|
||||
break; /* SDA 回到高:从机已释放 */
|
||||
}
|
||||
i2c_scl(0);
|
||||
}
|
||||
i2c_stop(); /* 补一个 STOP 收尾 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读状态寄存器(0x71):检查校准使能位,未校准则发 0xBE 0x08 0x00
|
||||
* @note IO 分离后 AHT20 独占 PC4/PC5,无需总线锁,直接调用
|
||||
* @return 0=就绪(已校准),-1=通讯失败
|
||||
*/
|
||||
static int8_t aht20_check_and_calib(void)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* 写 0x71 读状态命令 */
|
||||
i2c_start();
|
||||
i2c_write_byte(AHT20_ADDR_W);
|
||||
if (i2c_wait_ack()) {
|
||||
goto fail;
|
||||
}
|
||||
i2c_write_byte(0x71);
|
||||
if (i2c_wait_ack()) {
|
||||
goto fail;
|
||||
}
|
||||
i2c_stop();
|
||||
|
||||
/* 读 1 字节状态 */
|
||||
i2c_start();
|
||||
i2c_write_byte(AHT20_ADDR_R);
|
||||
if (i2c_wait_ack()) {
|
||||
goto fail;
|
||||
}
|
||||
status = i2c_read_byte(0);
|
||||
i2c_stop();
|
||||
|
||||
if (status & 0x08u) {
|
||||
return 0; /* Bit3 Calibration Enabled:出厂默认已校准 */
|
||||
}
|
||||
|
||||
/* 未校准:发初始化命令 0xBE 0x08 0x00 */
|
||||
log_error("aht20 not calib(0x%02x), init 0xBE\n", status);
|
||||
i2c_start();
|
||||
i2c_write_byte(AHT20_ADDR_W);
|
||||
if (i2c_wait_ack()) {
|
||||
goto fail;
|
||||
}
|
||||
i2c_write_byte(AHT20_CMD_INIT);
|
||||
if (i2c_wait_ack()) {
|
||||
goto fail;
|
||||
}
|
||||
i2c_write_byte(0x08);
|
||||
if (i2c_wait_ack()) {
|
||||
goto fail;
|
||||
}
|
||||
i2c_write_byte(0x00);
|
||||
if (i2c_wait_ack()) {
|
||||
goto fail;
|
||||
}
|
||||
i2c_stop();
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
i2c_stop();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化:配置 PC4/PC5 + 软件 I2C 总线复位 + 0x71 校准检查 + 0xBE 初始化
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_aht20_init(void)
|
||||
{
|
||||
if (s_inited) {
|
||||
return;
|
||||
}
|
||||
#if BOARD_TH_ENABLE
|
||||
/* 上电后等待 ≥100ms 由 app 启动节奏保证,此处直接操作 */
|
||||
/* PC5(SDA) 开内部上拉:I2C 释放 SDA 后必须有上拉,否则浮空
|
||||
* (实测无上拉时:init 读状态=0xFF 假 init ok;等 80ms 后漏电读全 0 → crc fail)。
|
||||
* 板上有外部 4.7~10k 上拉时双保险。PC4(SCL) 推挽驱动,上拉无害防浮空。 */
|
||||
gpio_set_pull_up(BOARD_TH_SDA_PIN, 1);
|
||||
gpio_set_pull_down(BOARD_TH_SDA_PIN, 0);
|
||||
gpio_set_die(BOARD_TH_SDA_PIN, 1);
|
||||
gpio_set_pull_up(BOARD_TH_SCL_PIN, 1);
|
||||
gpio_set_pull_down(BOARD_TH_SCL_PIN, 0);
|
||||
gpio_set_die(BOARD_TH_SCL_PIN, 1);
|
||||
|
||||
/* 总线复位:先 9 clock + STOP 解开可能的从机卡死,再补一次 STOP */
|
||||
i2c_bus_recover();
|
||||
i2c_stop();
|
||||
|
||||
if (aht20_check_and_calib() == 0) {
|
||||
log_info("aht20 init ok\n");
|
||||
} else {
|
||||
/* 校准检查失败不阻塞测量(多数 AHT20 出厂已校准),后续采样重试;
|
||||
* 顺带做一次总线恢复,解开可能的从机卡死 */
|
||||
log_error("aht20 calib check fail\n");
|
||||
i2c_bus_recover();
|
||||
}
|
||||
s_inited = 1;
|
||||
#else
|
||||
log_info("aht20 disabled\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发起一次测量:发 0xAC 0x33 0x00
|
||||
* @note 调用后必须等 ≥80ms 再 bsp_aht20_measure_finish。等待期间 I2C 总线
|
||||
* 空闲(AHT20 内部测量),CT1642 在 PG0/PG1 独立刷新,互不影响。
|
||||
* @return 0=命令已发出,非 0=失败
|
||||
*/
|
||||
int8_t bsp_aht20_measure_start(void)
|
||||
{
|
||||
#if !BOARD_TH_ENABLE
|
||||
return -1;
|
||||
#else
|
||||
if (!s_inited) {
|
||||
bsp_aht20_init(); /* init 失败时此处补做 */
|
||||
if (!s_inited) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_start();
|
||||
i2c_write_byte(AHT20_ADDR_W);
|
||||
if (i2c_wait_ack()) {
|
||||
log_error("addr nack\n");
|
||||
goto fail;
|
||||
}
|
||||
i2c_write_byte(AHT20_CMD_MEASURE);
|
||||
if (i2c_wait_ack()) {
|
||||
log_error("cmd nack\n");
|
||||
goto fail;
|
||||
}
|
||||
i2c_write_byte(0x33);
|
||||
if (i2c_wait_ack()) {
|
||||
log_error("param1 nack\n");
|
||||
goto fail;
|
||||
}
|
||||
i2c_write_byte(0x00);
|
||||
if (i2c_wait_ack()) {
|
||||
log_error("param2 nack\n");
|
||||
goto fail;
|
||||
}
|
||||
i2c_stop();
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
i2c_stop();
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取测量结果:读 7 字节 + CRC8 校验 + 换算
|
||||
* @param temp_x10 输出温度 ×10(如 25.3℃ → 253)
|
||||
* @param humi_pct 输出湿度 0~100 整数 %
|
||||
* @return 0=成功;-1=通讯失败(CRC 错/超时/忙)
|
||||
*/
|
||||
int8_t bsp_aht20_measure_finish(int16_t *temp_x10, uint8_t *humi_pct)
|
||||
{
|
||||
#if !BOARD_TH_ENABLE
|
||||
return -1;
|
||||
#else
|
||||
uint8_t buf[AHT20_READ_LEN];
|
||||
uint8_t i;
|
||||
uint32_t humi_raw;
|
||||
uint32_t temp_raw;
|
||||
int32_t temp_10;
|
||||
int8_t ret = -1;
|
||||
|
||||
i2c_start();
|
||||
i2c_write_byte(AHT20_ADDR_R);
|
||||
if (i2c_wait_ack()) {
|
||||
log_error("read addr nack\n");
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < AHT20_READ_LEN; i++) {
|
||||
buf[i] = i2c_read_byte((uint8_t)((i < (AHT20_READ_LEN - 1)) ? 1 : 0));
|
||||
}
|
||||
i2c_stop();
|
||||
|
||||
if (aht20_crc8(buf, AHT20_READ_LEN - 1) != buf[AHT20_READ_LEN - 1]) {
|
||||
log_error("crc fail: %02X %02X %02X %02X %02X %02X crc=%02X\n",
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
|
||||
goto out;
|
||||
}
|
||||
if (buf[0] & 0x80u) { /* busy 位 */
|
||||
log_error("sensor busy\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
humi_raw = ((uint32_t)buf[1] << 12u) | ((uint32_t)buf[2] << 4u) | ((uint32_t)buf[3] >> 4u);
|
||||
temp_raw = (((uint32_t)buf[3] & 0x0Fu) << 16u) | ((uint32_t)buf[4] << 8u) | (uint32_t)buf[5];
|
||||
|
||||
/* T = raw * 200 / 2^20 - 50(℃);×10 输出。整数运算:raw*2000 最大 ~2e7,int32 安全 */
|
||||
temp_10 = (int32_t)((temp_raw * 2000u) / 1048576u) - 500;
|
||||
*temp_x10 = (int16_t)temp_10;
|
||||
*humi_pct = (uint8_t)((humi_raw * 100u) / 1048576u);
|
||||
if (*humi_pct > 100) {
|
||||
*humi_pct = 100;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/******************************************************************************
|
||||
* @file bsp_aht20.h
|
||||
* @brief CBR3 AHT20 温湿度传感器:软件 I2C + 测量 + CRC8
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.3
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布
|
||||
* - V1.0.1, 2026.08.29, cyWu, 修复 SCL 高电平期间推挽输出 SDA 导致的假 STOP
|
||||
* - V1.0.2, 2026.08.29, cyWu, 回退诊断代码,保留时序修复/上拉/总线恢复/0x71 校准
|
||||
* - V1.0.3, 2026.08.29, cyWu, IO 分离:AHT20 独占 PC4(SCL)/PC5(SDA),删除
|
||||
* bsp_bus 共线锁与 measure_start/finish 拆分锁,恢复一次完成采集
|
||||
*
|
||||
* @note I2C 7 位地址 0x38(写 0x70 / 读 0x71)。测量命令 0xAC 0x33 0x00,
|
||||
* 等 ≥80ms 后读 7 字节:Status + 20bit 湿度 + 20bit 温度 + CRC8。
|
||||
* CRC8 多项式 0x31,初值 0xFF,覆盖前 6 字节。
|
||||
* PC4=SCL(推挽)/ PC5=SDA(开漏+内部上拉),与 CT1642 的 PG0/PG1
|
||||
* 完全独立,无总线竞争(2026-08-29 改板分离)。
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __BSP_AHT20_H__
|
||||
#define __BSP_AHT20_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 初始化:配置 PC4/PC5(上拉+die)+ 软件 I2C 总线复位 + 0x71 校准检查
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_aht20_init(void);
|
||||
|
||||
/**
|
||||
* @brief 发起一次测量:发 0xAC 0x33 0x00
|
||||
* @note 调用后必须等 ≥80ms 再 bsp_aht20_measure_finish。
|
||||
* 等待期间 I2C 总线空闲(AHT20 内部测量),CT1642 显示不受影响。
|
||||
* @return 0=命令已发出,非 0=失败(未初始化/通讯失败)
|
||||
*/
|
||||
int8_t bsp_aht20_measure_start(void);
|
||||
|
||||
/**
|
||||
* @brief 读取测量结果:读 7 字节 + CRC8 校验 + 换算
|
||||
* @param temp_x10 输出温度 ×10(如 25.3℃ → 253)
|
||||
* @param humi_pct 输出湿度 0~100 整数 %
|
||||
* @return 0=成功,非 0=失败(CRC 错/时序错,本次采样视为失败)
|
||||
*/
|
||||
int8_t bsp_aht20_measure_finish(int16_t *temp_x10, uint8_t *humi_pct);
|
||||
|
||||
#endif /* __BSP_AHT20_H__ */
|
||||
@@ -0,0 +1,153 @@
|
||||
/******************************************************************************
|
||||
* @file bsp_ct1642.c
|
||||
* @brief CBR3 CT1642 类数码管驱动实现(软件移位)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.1
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布
|
||||
* - V1.0.1, 2026.08.29, cyWu, IO 分离:CT1642 独立 PG0=CLK/PG1=DATA,删除
|
||||
* bsp_bus 共线机制;删调试遗留 latch/show4/scan_seg/scan_com,
|
||||
* 新增 bsp_ct1642_init()
|
||||
*
|
||||
* @note 驱动协议严格参照 CT1642 手册(与 CSDN yitx 一致):
|
||||
* 一帧 = 18bit,CLK 上升沿采样 DATA(DATA 只在 CLK=低时改变):
|
||||
* [0:3] C1-C4 位选(低有效,入参 com 取反后发送,先发 C1)
|
||||
* [4:9] 6 位保留位(固定填 1)
|
||||
* [10:17] Q2-Q9 段码(LSB 先,seg 的 bit0 → Q2 引脚)
|
||||
* 发完 18bit 后:CLK=高时 DATA 0→1 = 锁存;CLK=低时 DATA 0→1 = 输出。
|
||||
* 段码/位选物理映射由 PCB 决定,SCAN_TEST 扫段/扫位已定型(见 board_pin.h)。
|
||||
* 本驱动独立使用 PG0/PG1,与 AHT20 的 PC4/PC5 互不干扰。
|
||||
******************************************************************************/
|
||||
|
||||
#include "bsp_ct1642.h"
|
||||
#include "board_pin.h"
|
||||
#include "asm/gpio.h"
|
||||
|
||||
/* 软件延时:1 次空循环(24MHz 下 ~0.1us 量级,gpio 调用本身开销另计)。
|
||||
* ⚠ 提速历程:8→2(鬼影明显→一点点)→1(继续压残余微亮)。
|
||||
* 若出现显示乱码/错位(低于 CT1642 最小脉宽),回调到 2。
|
||||
* 软件压到极限后残余的微亮 = 硬件层(Q2-Q9 段线加 1~10k 下拉可根除)。 */
|
||||
static void ct1642_delay(void)
|
||||
{
|
||||
volatile uint32_t i;
|
||||
|
||||
for (i = 0; i < 1; i++) {
|
||||
/* nop */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 PG0(CLK)/PG1(DATA) 为推挽输出,初始 0
|
||||
* @note ⚠ 必须在第一次发帧前调用(app_disp_init 内)。
|
||||
* CT1642 为纯输入从机,两根线全程推挽驱动即可,无需上拉/开漏。
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_ct1642_init(void)
|
||||
{
|
||||
gpio_set_pull_up(BOARD_DISP_CLK_PIN, 0);
|
||||
gpio_set_pull_down(BOARD_DISP_CLK_PIN, 0);
|
||||
gpio_set_die(BOARD_DISP_CLK_PIN, 1);
|
||||
gpio_direction_output(BOARD_DISP_CLK_PIN, 0);
|
||||
|
||||
gpio_set_pull_up(BOARD_DISP_DATA_PIN, 0);
|
||||
gpio_set_pull_down(BOARD_DISP_DATA_PIN, 0);
|
||||
gpio_set_die(BOARD_DISP_DATA_PIN, 1);
|
||||
gpio_direction_output(BOARD_DISP_DATA_PIN, 0);
|
||||
}
|
||||
|
||||
static void ct1642_clk(uint8_t level)
|
||||
{
|
||||
gpio_direction_output(BOARD_DISP_CLK_PIN, level);
|
||||
ct1642_delay();
|
||||
}
|
||||
|
||||
static void ct1642_data(uint8_t level)
|
||||
{
|
||||
gpio_direction_output(BOARD_DISP_DATA_PIN, level);
|
||||
ct1642_delay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 7 段数码管数字转段码(共阴,A=bit0 ... G=bit6,DP=bit7)
|
||||
* @param digit 0~9;10='-'(G 段);11=全灭
|
||||
* @return 段码
|
||||
*/
|
||||
uint8_t bsp_ct1642_seg(uint8_t digit)
|
||||
{
|
||||
static const uint8_t seg_tbl[12] = {
|
||||
0x3F, /* 0: abcdef */
|
||||
0x06, /* 1: bc */
|
||||
0x5B, /* 2: abdeg */
|
||||
0x4F, /* 3: abcdg */
|
||||
0x66, /* 4: bcfg */
|
||||
0x6D, /* 5: acdfg */
|
||||
0x7D, /* 6: acdefg */
|
||||
0x07, /* 7: abc */
|
||||
0x7F, /* 8: abcdefg */
|
||||
0x6F, /* 9: abcdfg */
|
||||
0x40, /* '-': g */
|
||||
0x00, /* 灭 */
|
||||
};
|
||||
|
||||
if (digit > 11) {
|
||||
digit = 11;
|
||||
}
|
||||
return seg_tbl[digit];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发一帧 18bit(不含消影):C1-C4 → 6保留 → Q2-Q9,再锁存+输出
|
||||
* @note 内部函数;com/seg 的消影由 bsp_ct1642_send_frame 负责。
|
||||
*/
|
||||
static void ct1642_xfer(uint8_t seg, uint8_t com)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t posn = (uint8_t)(~com); /* C1-C4 低有效:位0=选中位 */
|
||||
|
||||
/* 起始空闲:CLK=低,DATA=高(规格“上电 CLK 低、DATA 高”) */
|
||||
ct1642_data(1);
|
||||
ct1642_clk(0);
|
||||
|
||||
/* 1. 移位 18bit:C1-C4(4) → 6位保留(填1) → Q2-Q9(8,LSB=Q2=seg bit0) */
|
||||
for (i = 0; i < 4; i++) { /* C1-C4,先发 C1 */
|
||||
ct1642_data((uint8_t)(posn & 0x01u));
|
||||
ct1642_clk(0); ct1642_clk(1); /* 下降沿改完,上升沿采样 */
|
||||
posn >>= 1;
|
||||
}
|
||||
for (i = 0; i < 6; i++) { /* 6 位保留,固定填 1 */
|
||||
ct1642_data(1);
|
||||
ct1642_clk(0); ct1642_clk(1);
|
||||
}
|
||||
for (i = 0; i < 8; i++) { /* Q2-Q9 段码,先发 Q2(=seg bit0) */
|
||||
ct1642_data((uint8_t)(seg & 0x01u));
|
||||
ct1642_clk(0); ct1642_clk(1);
|
||||
seg >>= 1;
|
||||
}
|
||||
|
||||
/* 2. 锁存:CLK 保持高,DATA 给 0→1 上升沿 */
|
||||
ct1642_clk(1);
|
||||
ct1642_data(0);
|
||||
ct1642_data(1);
|
||||
|
||||
/* 3. 输出:CLK 拉低,DATA 给 0→1 上升沿(刷新该位显示) */
|
||||
ct1642_clk(0);
|
||||
ct1642_data(0);
|
||||
ct1642_data(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发一帧刷新一位数码管(CT1642 18bit 协议)
|
||||
* @param seg 段码(bit7=DP ... bit0=A,与 seg_tbl 一致;先发 bit0→Q2)
|
||||
* @param com COM 位选掩码(bit3=C4 ... bit0=C1,仅 1 位置位;硬件低有效内部取反)
|
||||
* @note ⚠ 不做任何消影预帧:CT1642 移位过程实时输出,“持续刷新”才是鬼影根因
|
||||
* (切位瞬间杂段),预帧/全灭帧都无法根除,且每帧翻倍会饿死 ISR 喂狗导致
|
||||
* 上电复位(实测)。鬼影的正确解法在应用层:数字没变就不刷新(值变化才
|
||||
* 刷一次,其余时间锁存静止显示)。本函数保持单帧、极短,适配 ISR 上下文。
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_ct1642_send_frame(uint8_t seg, uint8_t com)
|
||||
{
|
||||
// ct1642_xfer(0, 0);
|
||||
ct1642_xfer(seg, com); /* 单帧:18bit 移位 + 锁存 + 输出,无消影预帧 */
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* @file bsp_ct1642.h
|
||||
* @brief CBR3 CT1642 类数码管驱动:18bit 移位、锁存、送出
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.1
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布
|
||||
* - V1.0.1, 2026.08.29, cyWu, IO 分离:CT1642 独立 PG0=CLK/PG1=DATA(飞线),
|
||||
* AHT20 独占 PC4/PC5,删除 bsp_bus 共线机制;删调试遗留
|
||||
* latch/show4/scan_seg/scan_com,新增 bsp_ct1642_init()
|
||||
*
|
||||
* @note PG0=CLK,PG1=DATA,独立引脚、无总线锁,可随时调用。
|
||||
* 协议:一帧 18bit(C1-C4 位选 + 6 位保留 + Q2-Q9 段码),CLK 上升沿采样
|
||||
* DATA(DATA 只在 CLK=低时改变);发完 CLK=高 时 DATA 上升沿=锁存,
|
||||
* CLK=低 时 DATA 上升沿=输出刷新。
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __BSP_CT1642_H__
|
||||
#define __BSP_CT1642_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 初始化 PG0/PG1 为推挽输出(CLK/DATA 初始 0)
|
||||
* @note app_disp_init() 内调用;被 BOARD_DISP_ENABLE 编译开关保护
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_ct1642_init(void);
|
||||
|
||||
/**
|
||||
* @brief 7 段数码管数字转段码(共阴,A=bit0 ... G=bit6,DP=bit7)
|
||||
* @param digit 0~9;10='-'(G 段);11=全灭
|
||||
* @return 段码
|
||||
*/
|
||||
uint8_t bsp_ct1642_seg(uint8_t digit);
|
||||
|
||||
/**
|
||||
* @brief 发一帧刷新一位:18bit(C1-C4 位选 + 6 保留 + Q2-Q9 段码,LSB 先)
|
||||
* @param seg 段码(bit7=DP ... bit0=A,先发 bit0→Q2)
|
||||
* @param com COM 位选掩码(bit3=C4 ... bit0=C1,仅 1 位置位;硬件低有效内部取反)
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_ct1642_send_frame(uint8_t seg, uint8_t com);
|
||||
|
||||
#endif /* __BSP_CT1642_H__ */
|
||||
@@ -0,0 +1,225 @@
|
||||
/******************************************************************************
|
||||
* @file bsp_hw.c
|
||||
* @brief CBR3 板级 GPIO / MCPWM:LED、按键、风扇
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删电机/充电/电量,改单 LED + 风扇 PWM)
|
||||
*
|
||||
* @note CT1642 在 PG0/PG1、AHT20 在 PC4/PC5,各自独立驱动,
|
||||
* bsp_hw 只管 LED/KEY/FAN,不碰这两组脚。
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "asm/gpio.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;
|
||||
|
||||
/** 风扇 PWM 状态 */
|
||||
typedef struct {
|
||||
uint8_t pwm_clk_on; /**< 1=MCPWM 时钟已打开 */
|
||||
uint16_t duty; /**< 当前占空比 */
|
||||
} BspFanCtx_t;
|
||||
|
||||
static BspFanCtx_t s_fan;
|
||||
|
||||
#if (BOARD_LED_ENABLE || BOARD_KEY_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
|
||||
/**
|
||||
* @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_FAN_ENABLE
|
||||
/**
|
||||
* @brief 初始化风扇 MCPWM ch0,初始占空比 0(关)
|
||||
* @return 无
|
||||
*/
|
||||
static void bsp_fan_pwm_init(void)
|
||||
{
|
||||
struct pwm_platform_data pwm;
|
||||
|
||||
gpio_set_pull_up(BOARD_FAN_PIN, 0);
|
||||
gpio_set_pull_down(BOARD_FAN_PIN, 0);
|
||||
gpio_set_die(BOARD_FAN_PIN, 1);
|
||||
|
||||
pwm.pwm_aligned_mode = pwm_edge_aligned;
|
||||
pwm.frequency = BOARD_FAN_PWM_HZ;
|
||||
pwm.duty = 0;
|
||||
pwm.l_pin = (u8) - 1;
|
||||
pwm.complementary_en = 0;
|
||||
pwm.pwm_ch_num = pwm_ch0;
|
||||
pwm.h_pin = BOARD_FAN_PIN;
|
||||
mcpwm_init(&pwm);
|
||||
|
||||
mcpwm_set_duty(pwm_ch0, 0);
|
||||
s_fan.pwm_clk_on = 1;
|
||||
s_fan.duty = 0;
|
||||
log_info("fan pwm %uHz duty=0\n", (unsigned)BOARD_FAN_PWM_HZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 打开 MCPWM 并绑回 PC3
|
||||
* @return 无
|
||||
*/
|
||||
static void bsp_fan_pwm_clock_on(void)
|
||||
{
|
||||
if (s_fan.pwm_clk_on) {
|
||||
return;
|
||||
}
|
||||
mcpwm_open(pwm_ch0);
|
||||
gpio_och_sel_output_signal(BOARD_FAN_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
|
||||
gpio_set_direction(BOARD_FAN_PIN, 0);
|
||||
s_fan.pwm_clk_on = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭 MCPWM 时钟并拉低(待机/关机必关)
|
||||
* @return 无
|
||||
*/
|
||||
static void bsp_fan_pwm_clock_off(void)
|
||||
{
|
||||
mcpwm_set_duty(pwm_ch0, 0);
|
||||
if (s_fan.pwm_clk_on) {
|
||||
mcpwm_close(pwm_ch0);
|
||||
gpio_och_disable_output_signal(BOARD_FAN_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
|
||||
JL_MCPWM->MCPWM_CON0 = 0;
|
||||
s_fan.pwm_clk_on = 0;
|
||||
}
|
||||
gpio_direction_output(BOARD_FAN_PIN, 0); /* 关 = 拉低(风扇高导通) */
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化 GPIO / 风扇 MCPWM
|
||||
* @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_PIN, !BOARD_LED_ON_LEVEL); /* 初始灭 */
|
||||
#endif
|
||||
|
||||
#if BOARD_KEY_ENABLE
|
||||
bsp_pin_input(BOARD_KEY_PIN, 1);
|
||||
#endif
|
||||
|
||||
#if BOARD_FAN_ENABLE
|
||||
bsp_fan_pwm_init();
|
||||
bsp_fan_pwm_clock_off(); /* 初始关,待机不耗电 */
|
||||
#endif
|
||||
|
||||
s_initialized = 1;
|
||||
log_info("hw init led=%d key=%d fan=%d\n",
|
||||
BOARD_LED_ENABLE, BOARD_KEY_ENABLE, BOARD_FAN_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 状态 LED 开关(PC2,低电平亮)
|
||||
* @param on 1=亮,0=灭
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_led_set(uint8_t on)
|
||||
{
|
||||
#if BOARD_LED_ENABLE
|
||||
gpio_direction_output(BOARD_LED_PIN, on ? BOARD_LED_ON_LEVEL : !BOARD_LED_ON_LEVEL);
|
||||
#else
|
||||
(void)on;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 风扇 PWM 占空比(PC3,高有效,20kHz)
|
||||
* @param duty 0~10000(0%=关 MCPWM 并拉低;>0 打开时钟输出)
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_fan_set_duty(uint16_t duty)
|
||||
{
|
||||
#if BOARD_FAN_ENABLE
|
||||
if (duty > 10000) {
|
||||
duty = 10000;
|
||||
}
|
||||
if (duty == 0) {
|
||||
bsp_fan_pwm_clock_off();
|
||||
s_fan.duty = 0;
|
||||
return;
|
||||
}
|
||||
bsp_fan_pwm_clock_on();
|
||||
mcpwm_set_duty(pwm_ch0, duty);
|
||||
s_fan.duty = duty;
|
||||
#else
|
||||
(void)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 进深睡前:风扇 PWM 关掉并拉低,灯全灭
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_hw_enter_sleep_io(void)
|
||||
{
|
||||
bsp_fan_set_duty(0);
|
||||
bsp_led_set(0);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/******************************************************************************
|
||||
* @file bsp_hw.h
|
||||
* @brief CBR3 板级硬件:LED / 按键 / 风扇 PWM
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删电机/充电/电量,改单 LED + 风扇 PWM)
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __BSP_HW_H__
|
||||
#define __BSP_HW_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "board_pin.h"
|
||||
|
||||
typedef enum {
|
||||
BSP_HW_OK = 0,
|
||||
BSP_HW_ERR_PARAM,
|
||||
BSP_HW_ERR_NOT_INIT
|
||||
} BspHw_Ret_t;
|
||||
|
||||
/**
|
||||
* @brief 初始化 GPIO / 风扇 MCPWM
|
||||
* @return BSP_HW_OK=成功
|
||||
*/
|
||||
BspHw_Ret_t bsp_hw_init(void);
|
||||
|
||||
/**
|
||||
* @brief 查询是否已初始化
|
||||
* @return BSP_HW_OK=已初始化,BSP_HW_ERR_NOT_INIT=未初始化
|
||||
*/
|
||||
BspHw_Ret_t bsp_hw_get_status(void);
|
||||
|
||||
/**
|
||||
* @brief 状态 LED 开关(PC2,低电平亮)
|
||||
* @param on 1=亮,0=灭
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_led_set(uint8_t on);
|
||||
|
||||
/**
|
||||
* @brief 风扇 PWM 占空比(PC3,高有效,20kHz)
|
||||
* @param duty 0~10000(0%=关 MCPWM 并拉低;>0 打开时钟输出)
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_fan_set_duty(uint16_t duty);
|
||||
|
||||
/**
|
||||
* @brief 读取按键
|
||||
* @return 1=按下,0=未按下
|
||||
*/
|
||||
uint8_t bsp_key_is_pressed(void);
|
||||
|
||||
/**
|
||||
* @brief 进深睡前:风扇 PWM 关掉并拉低,灯全灭
|
||||
* @return 无
|
||||
*/
|
||||
void bsp_hw_enter_sleep_io(void);
|
||||
|
||||
#endif /* __BSP_HW_H__ */
|
||||
Reference in New Issue
Block a user