好的,我已经获取了足够的手册信息。现在我来编写完整的电动牙刷演示程序。这个程序将包含以下功能模块:
- PWM电机驱动 - 使用PWMA产生电机驱动PWM信号
- 按键扫描 - 模式切换/开关机
- 电池电压检测 - 使用ADC检测电池电量
- 低功耗管理 - 掉电模式与定时唤醒
- LED指示灯 - 显示工作状态
- 定时控制 - 2分钟自动关机
以下是完整的程序代码:
/******************************************************************************
* 文件名: Electric_Toothbrush_Demo.c
* 芯片型号: STC32G8K64
* 编译环境: Keil C251
* 系统时钟: 内部IRC 24MHz (可根据实际调整)
*
* 功能描述:
* 基于STC32G8K64的电动牙刷演示程序
*
* 硬件连接:
* - P1.0: 电机PWM输出 (PWMA_CH1P)
* - P3.2: 按键输入 (模式切换/开关机, 低电平有效)
* - P3.3: 按键输入 (力度调节, 低电平有效)
* - P1.1: LED指示灯 (电量/状态指示)
* - P1.2: 电池电压检测 (ADC通道2)
* - P5.4: ADC_ETR (外部触发, 本程序未使用)
*
* 工作模式:
* 模式0 - 关机 (掉电模式, 超低功耗)
* 模式1 - 轻柔模式 (低转速)
* 模式2 - 标准模式 (中转速)
* 模式3 - 强劲模式 (高转速)
* 模式4 - 按摩模式 (脉冲变速)
*
* 功能特性:
* 1. 按键开关机与模式切换
* 2. 2分钟智能定时 (30秒分区提醒)
* 3. 电池电量检测与低电量指示
* 4. 低功耗待机 (掉电模式)
* 5. 看门狗保护
* 6. 力度调节 (多档PWM占空比)
*
* 使用说明:
* 1. 使用STC-ISP软件下载程序, 选择内部IRC 24MHz
* 2. 短按P3.2按键开机/切换模式
* 3. 长按P3.2按键关机
* 4. 短按P3.3按键调节力度 (3档)
* 5. 运行2分钟后自动关机
* 6. 低电量时LED快闪提示
*
* 注意事项:
* 1. 下载时在STC-ISP中设置"允许低压复位(禁止低压中断)"为不勾选
* 2. ADC_VRef+引脚必须接VCC或外部参考电压
* 3. 电机驱动需外接MOS管驱动电路
******************************************************************************/
#include "stc32g.h" // STC32G系列头文件
#include "intrins.h" // 内部函数库 (用于_nop_())
//=============================================================================
// 系统时钟与定时参数定义
//=============================================================================
#define FOSC 24000000UL // 系统工作频率 24MHz
#define T1MS (65536 - FOSC/1000) // 定时器1ms重载值 (24MHz/12/1000=2000)
// 65536 - 2000 = 63536 = 0xF830
//=============================================================================
// 电机PWM参数定义 (使用PWMA)
//=============================================================================
// PWM频率 = SYSclk / (PSCR+1) / (ARR+1)
// 目标频率: 20000Hz (20kHz, 超声波马达常用频率)
// 设 PSCR = 0, 则 ARR = 24000000/1/20000 - 1 = 1199
#define PWM_PERIOD 1199 // PWM周期 (ARR值)
#define PWM_LEVEL_1 300 // 轻柔模式占空比 (~25%)
#define PWM_LEVEL_2 600 // 标准模式占空比 (~50%)
#define PWM_LEVEL_3 900 // 强劲模式占空比 (~75%)
#define PWM_LEVEL_4 0 // 关机/停止
//=============================================================================
// 电池电压检测参数
//=============================================================================
// 使用ADC通道2 (P1.2) 检测电池电压
// 电池通过2个10K电阻分压后接入ADC (分压比1/2)
// 锂电池满电4.2V, 分压后2.1V
// 锂电池保护电压3.0V, 分压后1.5V
// ADC参考电压 = VCC (假设3.7V锂电池经LDO稳压到3.3V)
// ADC值 = 4096 * Vin / Vref
// 电池电压 = ADC值 * Vref / 4096 * 2 (分压补偿)
#define BAT_FULL 3100 // 满电电压(mV) - 对应ADC约1920
#define BAT_LOW 2400 // 低电报警电压(mV) - 对应ADC约1455
#define BAT_SHUTDOWN 2200 // 关机保护电压(mV)
//=============================================================================
// 时间参数定义
//=============================================================================
#define WORK_TIME_MAX 120 // 最大工作时间 (秒)
#define ZONE_TIME 30 // 分区提醒时间 (秒)
#define POWER_OFF_HOLD 2000 // 长按关机时间 (ms)
//=============================================================================
// 全局变量定义
//=============================================================================
unsigned char g_WorkMode; // 当前工作模式 (0=关机, 1~4=工作模式)
unsigned char g_PowerLevel; // 力度等级 (0~2)
unsigned int g_WorkTimer; // 工作时间计数器 (秒)
unsigned char g_ZoneCounter; // 分区计时 (秒)
unsigned char g_BatteryLevel; // 电池电量等级 (0~3)
unsigned int g_AdcResult; // ADC转换结果
unsigned char g_KeyPressFlag; // 按键按下标志
unsigned int g_KeyHoldCount; // 按键保持计数 (ms)
unsigned char g_KeyLongPressFlag; // 长按标志
unsigned char g_LedBlinkCounter; // LED闪烁计数器
unsigned char g_LedState; // LED状态
//=============================================================================
// 函数声明
//=============================================================================
void System_Init(void);
void GPIO_Init(void);
void PWMA_Init(void);
void Timer0_Init(void);
void ADC_Init(void);
void Delay1ms(unsigned int ms);
unsigned int Get_BatteryVoltage(void);
void Set_MotorSpeed(unsigned int duty);
void Stop_Motor(void);
void Key_Scan(void);
void Mode_Switch(void);
void Power_Level_Adjust(void);
void Work_Timer_Handler(void);
void Led_Indicator(void);
void Enter_SleepMode(void);
void Check_Battery(void);
//=============================================================================
// 系统初始化函数
//=============================================================================
void System_Init(void)
{
// 使能访问扩展特殊功能寄存器 (XFR)
// STC32G系列访问XFR前必须设置此位
EAXFR = 1;
// 设置外部数据总线速度为最快
// CKCON: 访问片外xdata的等待时间, 0=最快
CKCON = 0x00;
// 设置程序代码等待参数
// WTST: 取程序代码等待时间, 0=不等待, 程序以最快速度运行
WTST = 0x00;
// 设置内部高速IRC从掉电模式唤醒后的等待时钟数
// 运行在33MHz以下时, 建议设置为0x10
IRCDB = 0x10;
// 设置IAP等待时间参数
// IAP_TPS = 系统工作频率(MHz), 用于IAP/EEPROM操作时序
IAP_TPS = 24; // 系统工作频率24MHz
}
//=============================================================================
// GPIO初始化函数
//=============================================================================
void GPIO_Init(void)
{
// 将所有I/O口设置为准双向口模式 (默认模式)
// PxM1和PxM0组合: 00=准双向口, 01=推挽输出, 10=高阻输入, 11=开漏输出
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
// 配置P1.0为推挽输出 (电机PWM输出脚)
// PWMA_CH1P默认在P1.0, 需要推挽输出驱动MOS管
P1M0 |= 0x01; // P1.0: 推挽输出
P1M1 &= ~0x01;
// 配置P1.1为准双向口 (LED指示灯)
// 默认即为准双向口, 无需额外配置
// 配置P1.2为高阻输入 (ADC输入通道)
// ADC输入通道必须设置为高阻输入模式
P1M0 &= ~0x04; // P1.2: 高阻输入
P1M1 |= 0x04;
// 配置P3.2和P3.3为准双向口 (按键输入)
// 默认即为准双向口, 内部有上拉电阻
// 初始状态: LED灭, 电机停止
P11 = 1; // LED高电平灭 (共阳极或低电平驱动)
}
//=============================================================================
// PWMA (高级PWM定时器) 初始化函数
// 用于产生电机驱动PWM信号
//=============================================================================
void PWMA_Init(void)
{
unsigned int pscr_val;
unsigned int arr_val;
// 计算PWM参数
// PWM频率 = SYSclk / (PSCR+1) / (ARR+1)
// 目标: 20kHz, 系统时钟24MHz
// 设 PSCR = 0 (不分频), 则 ARR = 24000000/1/20000 - 1 = 1199
pscr_val = 0; // 预分频器值
arr_val = PWM_PERIOD; // 自动重装载值 = 1199
// 配置PWMA_CCMR1: 通道1为PWM模式1输出
// OC1M[2:0] = 110 (PWM模式1): 向上计数时CNT<CCR1时输出高, 否则低
// OC1PE = 1: 使能预装载功能
// CC1S[1:0] = 00: 配置为输出模式
PWMA_CCMR1 = 0x60; // 0110 0000: OC1M=110, OC1PE=1, CC1S=00
// 配置PWMA_CCER1: 使能通道1输出, 高电平有效
// CC1E = 1: 使能OC1输出
// CC1P = 0: 高电平有效
PWMA_CCER1 = 0x01; // 0000 0001: CC1E=1
// 配置PWMA_BKR: 刹车寄存器
// MOEA = 1: 使能主输出 (必须置1, 否则PWM无输出)
PWMA_BKR = 0x80; // 1000 0000: MOEA=1
// 设置预分频器
PWMA_PSCRH = (unsigned char)(pscr_val >> 8); // 高8位
PWMA_PSCRL = (unsigned char)(pscr_val); // 低8位
// 设置自动重装载寄存器 (决定PWM周期)
PWMA_ARRH = (unsigned char)(arr_val >> 8); // 高8位
PWMA_ARRL = (unsigned char)(arr_val); // 低8位
// 初始占空比为0 (电机停止)
PWMA_CCR1H = 0;
PWMA_CCR1L = 0;
// 产生更新事件, 使预分频器和ARR值立即生效
// PWMA_EGR的UG位: 产生更新事件, 重新初始化计数器
PWMA_EGR |= 0x01; // UG=1
// 等待更新事件完成
_nop_(); _nop_();
// 配置PWMA_CR1: 使能计数器
// CEN = 1: 使能计数器
// ARPE = 1: 使能自动重装载预装载
PWMA_CR1 = 0x01; // 0000 0001: CEN=1
// 注意: 此时PWM已经开始工作, 但占空比为0, 电机不转
}
//=============================================================================
// 设置电机转速
// 参数: duty - 占空比比较值 (0 ~ PWM_PERIOD)
//=============================================================================
void Set_MotorSpeed(unsigned int duty)
{
// 限制占空比范围
if (duty > PWM_PERIOD)
{
duty = PWM_PERIOD;
}
// 设置捕获/比较寄存器 (决定PWM占空比)
PWMA_CCR1H = (unsigned char)(duty >> 8); // 高8位
PWMA_CCR1L = (unsigned char)(duty); // 低8位
}
//=============================================================================
// 停止电机
//=============================================================================
void Stop_Motor(void)
{
// 将占空比设置为0
PWMA_CCR1H = 0;
PWMA_CCR1L = 0;
}
//=============================================================================
// 定时器0初始化函数
// 用于产生1ms定时中断, 作为系统时基
//=============================================================================
void Timer0_Init(void)
{
// 配置TMOD: 定时器0工作模式
// T0_M1=0, T0_M0=1: 模式1 (16位不可重装载模式)
// 但我们使用自动重装载, 所以实际使用模式0 (16位自动重装载)
// 通过AUXR寄存器的T0x12位控制分频
TMOD = 0x00; // 定时器0: 模式0 (16位自动重装载)
// 设置定时器0为1T模式 (不分频)
// T0x12 = 1: 定时器0时钟 = 系统时钟/1
// 这样定时精度更高
AUXR |= 0x80; // T0x12 = 1
// 计算重载值
// 定时时间 = (65536 - 重载值) / 系统时钟频率
// 1ms = (65536 - 重载值) / 24000000
// 重载值 = 65536 - 24000000/1000 = 65536 - 24000 = 41536 = 0xA240
// 但使用1T模式时, 重载值 = 65536 - 24000 = 41536
// 注意: 由于中断响应和指令执行时间, 实际会有微小误差
TL0 = 0x40; // 低8位: 0xA240的低8位
TH0 = 0xA2; // 高8位: 0xA240的高8位
// 启动定时器0
TR0 = 1; // TCON.4: 定时器0运行控制位
// 使能定时器0中断
ET0 = 1; // IE.1: 定时器0中断使能
}
//=============================================================================
// ADC初始化函数
// 用于检测电池电压
//=============================================================================
void ADC_Init(void)
{
// 配置ADC时序控制寄存器
// CSSETUP=0: 通道选择建立时间1个ADC时钟
// CSHOLD[1:0]=01: 通道选择保持时间2个ADC时钟
// SMPDUTY[4:0]=11111: 采样时间32个ADC时钟 (建议>=15)
ADCTIM = 0x3F; // 0011 1111
// 配置ADC配置寄存器
// RESFMT=0: 转换结果左对齐 (ADC_RES高8位, ADC_RESL低4位)
// SPEED[3:0]=1111: ADC时钟 = SYSclk/2/16 = 24MHz/32 = 750kHz
// 12位ADC最高速度800kHz, 750kHz符合要求
ADCCFG = 0x0F; // 0000 1111
// 选择ADC通道2 (P1.2) 并打开ADC电源
// ADC_POWER=1: 打开ADC电源
// ADC_CHS[3:0]=0010: 选择通道2 (P1.2)
ADC_CONTR = 0x82; // 1000 0010
// 等待ADC电源稳定 (建议等待1ms)
Delay1ms(1);
}
//=============================================================================
// 读取ADC转换结果 (查询方式)
// 返回: 12位ADC值 (0~4095)
//=============================================================================
unsigned int Read_ADC(void)
{
unsigned int adc_value;
// 启动ADC转换
ADC_CONTR |= 0x40; // ADC_START = 1
// 等待转换完成
// ADC_FLAG位在转换完成后由硬件置1
while (!(ADC_CONTR & 0x20)); // 等待ADC_FLAG=1
// 清除转换完成标志 (必须软件清零)
ADC_CONTR &= ~0x20; // ADC_FLAG = 0
// 读取转换结果 (左对齐格式)
// ADC_RES[7:0] = 结果高8位
// ADC_RESL[7:4] = 结果低4位, ADC_RESL[3:0] = 0
adc_value = ((unsigned int)ADC_RES << 8) | (unsigned int)ADC_RESL;
return adc_value;
}
//=============================================================================
// 获取电池电压 (mV)
// 使用ADC第15通道 (内部1.19V参考) 反推VCC电压
// 然后通过ADC通道2的分压值计算电池电压
//=============================================================================
unsigned int Get_BatteryVoltage(void)
{
unsigned int adc_bg; // 内部参考源ADC值
unsigned int adc_bat; // 电池分压ADC值
unsigned long vcc_mv; // VCC电压 (mV)
unsigned long bat_mv; // 电池电压 (mV)
unsigned char i;
unsigned long sum_bg;
unsigned long sum_bat;
// 多次采样取平均, 提高精度
sum_bg = 0;
sum_bat = 0;
for (i = 0; i < 8; i++)
{
// 切换通道到第15通道 (内部1.19V参考源)
ADC_CONTR = (ADC_CONTR & 0x0F) | 0x8F; // ADC_POWER=1, ADC_CHS=1111
Delay1ms(1); // 等待通道切换稳定
adc_bg = Read_ADC();
sum_bg += adc_bg;
// 切换通道到通道2 (P1.2, 电池分压)
ADC_CONTR = (ADC_CONTR & 0x0F) | 0x82; // ADC_POWER=1, ADC_CHS=0010
Delay1ms(1); // 等待通道切换稳定
adc_bat = Read_ADC();
sum_bat += adc_bat;
}
// 取平均值
adc_bg = (unsigned int)(sum_bg >> 3);
adc_bat = (unsigned int)(sum_bat >> 3);
// 计算VCC电压
// 内部参考源电压约为1.19V (1190mV)
// VCC = 1190 * 4096 / adc_bg (mV)
// 使用长整型防止溢出
vcc_mv = 1190UL * 4096UL / adc_bg;
// 计算电池电压
// 电池分压比: 2个10K电阻分压, 分压比 = 1/2
// 电池电压 = VCC * adc_bat / 4096 * 2
bat_mv = vcc_mv * adc_bat / 4096UL * 2UL;
return (unsigned int)bat_mv;
}
//=============================================================================
// 检查电池电量并更新电量等级
//=============================================================================
void Check_Battery(void)
{
unsigned int bat_mv;
// 读取电池电压
bat_mv = Get_BatteryVoltage();
// 判断电量等级
if (bat_mv >= BAT_FULL)
{
g_BatteryLevel = 3; // 满电
}
else if (bat_mv >= 2800)
{
g_BatteryLevel = 2; // 中等
}
else if (bat_mv >= BAT_LOW)
{
g_BatteryLevel = 1; // 低电量
}
else
{
g_BatteryLevel = 0; // 严重亏电
}
// 如果电池电压低于关机保护电压, 强制关机
if (bat_mv < BAT_SHUTDOWN)
{
g_WorkMode = 0; // 关机
Stop_Motor();
}
}
//=============================================================================
// 毫秒延时函数 (使用定时器0查询方式)
// 参数: ms - 延时毫秒数
//=============================================================================
void Delay1ms(unsigned int ms)
{
unsigned int i;
unsigned char tl0_save, th0_save;
// 保存定时器0当前值
tl0_save = TL0;
th0_save = TH0;
for (i = 0; i < ms; i++)
{
// 重新装载定时器0 (使用1T模式)
// 1ms = 24000个时钟周期
TL0 = 0x40; // 65536 - 24000 = 41536 = 0xA240
TH0 = 0xA2;
// 等待定时器0溢出
while (!TF0); // TCON.5: 定时器0溢出标志
TF0 = 0; // 清除溢出标志
}
// 恢复定时器0值
TL0 = tl0_save;
TH0 = th0_save;
}
//=============================================================================
// 按键扫描函数
// 检测P3.2和P3.3按键状态
// P3.2: 模式切换/开关机
// P3.3: 力度调节
//=============================================================================
void Key_Scan(void)
{
// 检测P3.2按键 (模式切换/开关机)
if (P32 == 0) // 按键按下 (低电平有效)
{
if (g_KeyPressFlag == 0)
{
// 按键首次按下
g_KeyPressFlag = 1;
g_KeyHoldCount = 0;
g_KeyLongPressFlag = 0;
}
else
{
// 按键持续按下, 计时
g_KeyHoldCount++;
// 检测长按 (2秒)
if (g_KeyHoldCount >= POWER_OFF_HOLD)
{
if (g_KeyLongPressFlag == 0)
{
g_KeyLongPressFlag = 1;
// 长按P3.2: 关机
if (g_WorkMode != 0)
{
g_WorkMode = 0; // 切换到关机模式
Stop_Motor(); // 停止电机
P11 = 1; // LED灭
}
}
}
}
}
else // 按键释放
{
if (g_KeyPressFlag == 1)
{
// 按键释放, 判断是否为短按
if (g_KeyLongPressFlag == 0)
{
// 短按P3.2: 模式切换
Mode_Switch();
}
g_KeyPressFlag = 0;
g_KeyHoldCount = 0;
}
}
// 检测P3.3按键 (力度调节)
if (P33 == 0) // 按键按下
{
// 简单消抖: 等待10ms再判断
Delay1ms(10);
if (P33 == 0)
{
// 等待按键释放
while (P33 == 0);
// 只有在工作模式下才调节力度
if (g_WorkMode != 0)
{
Power_Level_Adjust();
}
}
}
}
//=============================================================================
// 模式切换函数
// 循环切换: 关机->轻柔->标准->强劲->按摩->关机
//=============================================================================
void Mode_Switch(void)
{
// 模式切换
g_WorkMode++;
// 模式范围: 0~4, 超过4回到0
if (g_WorkMode > 4)
{
g_WorkMode = 0;
}
// 根据新模式设置电机转速
switch (g_WorkMode)
{
case 0: // 关机
Stop_Motor();
P11 = 1; // LED灭
break;
case 1: // 轻柔模式
Set_MotorSpeed(PWM_LEVEL_1);
P11 = 0; // LED亮
break;
case 2: // 标准模式
Set_MotorSpeed(PWM_LEVEL_2);
P11 = 0; // LED亮
break;
case 3: // 强劲模式
Set_MotorSpeed(PWM_LEVEL_3);
P11 = 0; // LED亮
break;
case 4: // 按摩模式 (脉冲变速, 在主循环中处理)
Set_MotorSpeed(PWM_LEVEL_2); // 初始中速
P11 = 0; // LED亮
break;
default:
g_WorkMode = 0;
break;
}
// 如果开机, 重置工作计时器
if (g_WorkMode != 0)
{
g_WorkTimer = 0;
g_ZoneCounter = 0;
}
}
//=============================================================================
// 力度调节函数
// 循环切换: 低->中->高
//=============================================================================
void Power_Level_Adjust(void)
{
// 力度等级递增
g_PowerLevel++;
// 3档力度: 0=低, 1=中, 2=高
if (g_PowerLevel > 2)
{
g_PowerLevel = 0;
}
// 根据当前模式和力度等级重新设置PWM占空比
switch (g_WorkMode)
{
case 1: // 轻柔模式
if (g_PowerLevel == 0) Set_MotorSpeed(PWM_LEVEL_1 - 100);
else if (g_PowerLevel == 1) Set_MotorSpeed(PWM_LEVEL_1);
else Set_MotorSpeed(PWM_LEVEL_1 + 100);
break;
case 2: // 标准模式
if (g_PowerLevel == 0) Set_MotorSpeed(PWM_LEVEL_2 - 100);
else if (g_PowerLevel == 1) Set_MotorSpeed(PWM_LEVEL_2);
else Set_MotorSpeed(PWM_LEVEL_2 + 100);
break;
case 3: // 强劲模式
if (g_PowerLevel == 0) Set_MotorSpeed(PWM_LEVEL_3 - 100);
else if (g_PowerLevel == 1) Set_MotorSpeed(PWM_LEVEL_3);
else Set_MotorSpeed(PWM_LEVEL_3);
break;
case 4: // 按摩模式
// 力度影响脉冲幅度
break;
default:
break;
}
}
//=============================================================================
// 工作计时器处理函数
// 每秒调用一次, 管理工作时间
//=============================================================================
void Work_Timer_Handler(void)
{
if (g_WorkMode == 0)
{
return; // 关机状态不处理
}
// 工作时间累加
g_WorkTimer++;
g_ZoneCounter++;
// 30秒分区提醒 (LED闪烁)
if (g_ZoneCounter >= ZONE_TIME)
{
g_ZoneCounter = 0;
// LED闪烁3次提示换区
P11 = 0; // LED亮
Delay1ms(100);
P11 = 1; // LED灭
Delay1ms(100);
P11 = 0; // LED亮
Delay1ms(100);
P11 = 1; // LED灭
Delay1ms(100);
P11 = 0; // LED亮
Delay1ms(100);
P11 = 1; // LED灭
}
// 2分钟自动关机
if (g_WorkTimer >= WORK_TIME_MAX)
{
g_WorkMode = 0; // 关机
Stop_Motor(); // 停止电机
P11 = 1; // LED灭
}
}
//=============================================================================
// LED指示灯控制函数
// 根据电池电量和工作状态控制LED
//=============================================================================
void Led_Indicator(void)
{
// 低电量指示 (LED快闪)
if (g_BatteryLevel <= 1)
{
g_LedBlinkCounter++;
if (g_LedBlinkCounter >= 20) // 每20个1ms周期翻转一次
{
g_LedBlinkCounter = 0;
P11 = ~P11; // LED闪烁
}
}
else
{
// 正常电量, LED常亮 (由模式切换时控制)
if (g_WorkMode != 0)
{
P11 = 0; // LED亮
}
}
}
//=============================================================================
// 进入掉电模式 (省电模式)
// 在关机状态下调用, 实现超低功耗
//=============================================================================
void Enter_SleepMode(void)
{
// 进入掉电模式前的准备工作
// 1. 关闭ADC电源以降低功耗
ADC_CONTR &= ~0x80; // ADC_POWER = 0
// 2. 设置I/O口为低功耗状态
// 不用的I/O口设置为高阻输入并关闭数字输入
// 这里简化处理, 实际产品需根据硬件设计优化
// 3. 设置掉电唤醒定时器 (约1秒唤醒一次)
// 掉电唤醒定时器时钟约32kHz
// 计数时间 = 10^6 * 16 * (计数值+1) / 32000 (us)
// 设计数值=1999, 则时间 ≈ 1秒
WKTCL = 0xCF; // 低8位: 1999的低8位
WKTCH = 0x87; // 高7位+使能位: WKTEN=1, 高7位=0x07
// 4. 清除唤醒标志
// 掉电唤醒后, 程序从进入掉电的下一条语句继续执行
// 5. 进入掉电模式
// PD=1: 进入主时钟停振/省电模式
// CPU及大部分外设停止工作, 功耗降至约0.4uA
PCON |= 0x02; // PD = 1
// 注意: 执行完PCON |= 0x02后, MCU立即进入掉电模式
// 被唤醒后, 从下一行代码继续执行
_nop_(); // 掉电唤醒后首先执行此语句
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
// 唤醒后重新初始化ADC
ADC_Init();
}
//=============================================================================
// 按摩模式处理函数
// 产生脉冲变速效果: 占空比周期性变化
//=============================================================================
void Massage_Mode_Handler(void)
{
static unsigned int massage_counter = 0;
unsigned int duty;
if (g_WorkMode != 4)
{
massage_counter = 0;
return;
}
// 脉冲周期: 约500ms
// 使用正弦波近似: 占空比在200~900之间变化
massage_counter++;
if (massage_counter >= 500) // 500ms周期
{
massage_counter = 0;
}
// 简单的三角波调制
if (massage_counter < 250)
{
// 上升段: 200 -> 900
duty = 200 + (unsigned int)massage_counter * 700 / 250;
}
else
{
// 下降段: 900 -> 200
duty = 900 - (unsigned int)(massage_counter - 250) * 700 / 250;
}
Set_MotorSpeed(duty);
}
//=============================================================================
// 主函数
//=============================================================================
void main(void)
{
unsigned int bat_voltage;
unsigned char timer1s_counter;
// 系统初始化
System_Init();
// GPIO初始化
GPIO_Init();
// PWM初始化 (电机驱动)
PWMA_Init();
// 定时器0初始化 (1ms时基)
Timer0_Init();
// ADC初始化 (电池检测)
ADC_Init();
// 初始化全局变量
g_WorkMode = 0; // 初始关机
g_PowerLevel = 1; // 默认中档力度
g_WorkTimer = 0;
g_ZoneCounter = 0;
g_BatteryLevel = 3;
g_KeyPressFlag = 0;
g_KeyHoldCount = 0;
g_KeyLongPressFlag = 0;
g_LedBlinkCounter = 0;
g_LedState = 0;
timer1s_counter = 0;
// 开机检测电池
Check_Battery();
// 使能总中断
EA = 1;
//=========================================================================
// 主循环
//=========================================================================
while (1)
{
// 按键扫描
Key_Scan();
// 如果处于工作模式
if (g_WorkMode != 0)
{
// 按摩模式特殊处理
if (g_WorkMode == 4)
{
Massage_Mode_Handler();
}
// 每1秒执行一次的任务
// 利用定时器0中断计数, 每1000次中断为1秒
if (timer1s_counter >= 1000)
{
timer1s_counter = 0;
// 工作计时
Work_Timer_Handler();
// 每10秒检测一次电池
if ((g_WorkTimer % 10) == 0)
{
Check_Battery();
}
}
// LED指示
Led_Indicator();
}
else
{
// 关机状态: 进入掉电模式
// 掉电唤醒定时器会定期唤醒MCU
// 唤醒后检测按键和电池
// 进入掉电模式前关闭LED
P11 = 1;
// 进入掉电模式
Enter_SleepMode();
// 唤醒后检测电池
Check_Battery();
// 如果电池电压过低, 继续休眠
if (g_BatteryLevel == 0)
{
// 低电量指示: LED快闪3次
P11 = 0; Delay1ms(50);
P11 = 1; Delay1ms(50);
P11 = 0; Delay1ms(50);
P11 = 1; Delay1ms(50);
P11 = 0; Delay1ms(50);
P11 = 1; Delay1ms(50);
}
}
// 喂狗 (看门狗清零)
// 如果启用了看门狗, 需要在主循环中定期清零
// WDT_CONTR |= 0x10; // CLR_WDT = 1
// 简单延时, 防止主循环过快
Delay1ms(1);
timer1s_counter++;
}
}
//=============================================================================
// 定时器0中断服务函数
// 每1ms中断一次
// 中断号: 1 (定时器0)
//=============================================================================
void Timer0_ISR(void) interrupt 1
{
// 定时器0为16位自动重装载模式
// 溢出后自动重装TH0和TL0的值
// 无需手动重装
// 注意: 中断服务程序尽量简洁
// 复杂的处理放在主循环中轮询
// 清除定时器0中断标志 (硬件自动清除, 但为了兼容性手动清除)
// TF0 = 0; // 硬件自动清除
}
//=============================================================================
// 注意: 以下为可选功能, 可根据需要启用
//=============================================================================
#if 0 // 如果需要看门狗功能, 将#if 0改为#if 1
/******************************************************************************
* 看门狗初始化函数
* 防止程序跑飞, 增强系统可靠性
* 溢出时间约4秒 (24MHz, WDT_PS=110)
******************************************************************************/
void WDT_Init(void)
{
// 配置看门狗
// EN_WDT = 1: 使能看门狗
// CLR_WDT = 1: 清零看门狗定时器
// IDL_WDT = 0: IDLE模式时看门狗停止计数
// WDT_PS[2:0] = 110: 分频系数128
// 溢出时间 = 12 * 32768 * 2^(6+1) / 24000000 ≈ 4.19秒
WDT_CONTR = 0x26; // 0010 0110
}
/******************************************************************************
* 喂狗函数
* 在主循环中定期调用, 防止看门狗复位
******************************************************************************/
void Feed_Dog(void)
{
// 清零看门狗定时器
WDT_CONTR |= 0x10; // CLR_WDT = 1
}
#endif
#if 0 // 如果需要串口调试功能, 将#if 0改为#if 1
/******************************************************************************
* 串口1初始化函数 (调试用)
* 波特率: 115200, 8N1
* 使用定时器2作为波特率发生器
******************************************************************************/
void UART1_Init(void)
{
// 设置串口1引脚: 使用P3.0(RXD)和P3.1(TXD)
// S1_S[1:0] = 00: 串口1使用P3.0/P3.1
P_SW1 &= ~0xC0; // S1_S[1:0] = 00
// 配置串口1: 模式1 (8位UART, 可变波特率)
SCON = 0x50; // SM0=0, SM1=1, REN=1
// 使用定时器2作为波特率发生器
// 波特率 = SYSclk / 4 / (65536 - 重载值)
// 115200 = 24000000 / 4 / (65536 - 重载值)
// 重载值 = 65536 - 24000000/4/115200 = 65536 - 52 = 65484 = 0xFFCC
T2L = 0xCC; // 低8位
T2H = 0xFF; // 高8位
AUXR |= 0x14; // T2R=1 (启动定时器2), T2_C/T=0 (定时模式)
// 使能串口1中断
ES = 1;
}
/******************************************************************************
* 串口1发送一个字节
******************************************************************************/
void UART1_SendByte(unsigned char dat)
{
SBUF = dat; // 写入发送缓冲器
while (!TI); // 等待发送完成
TI = 0; // 清除发送完成标志
}
/******************************************************************************
* 串口1发送字符串
******************************************************************************/
void UART1_SendString(char *str)
{
while (*str != '\0')
{
UART1_SendByte(*str);
str++;
}
}
/******************************************************************************
* 串口1中断服务函数
******************************************************************************/
void UART1_ISR(void) interrupt 4
{
if (RI) // 接收中断
{
RI = 0; // 清除接收中断标志
// 处理接收到的数据
// unsigned char dat = SBUF;
}
if (TI) // 发送中断
{
TI = 0; // 清除发送中断标志
}
}
#endif
/******************************************************************************
* 文件结束
******************************************************************************/
程序说明
一、硬件资源分配
| 外设 |
功能 |
引脚 |
| PWMA_CH1P |
电机PWM驱动 |
P1.0 |
| GPIO |
按键输入(模式/开关) |
P3.2 |
| GPIO |
按键输入(力度调节) |
P3.3 |
| GPIO |
LED指示灯 |
P1.1 |
| ADC通道2 |
电池电压检测 |
P1.2 |
| 定时器0 |
1ms系统时基 |
内部 |
二、功能模块详解
-
电机驱动 (PWMA)
- 使用STC32G8K64的16位高级PWM定时器PWMA
- 输出20kHz PWM信号,适合超声波马达
- 通过改变CCR1值调节占空比,实现无级调速
-
电池检测 (ADC)
- 使用ADC第15通道(内部1.19V参考源)反推VCC电压
- 再通过分压比计算实际电池电压
- 8次采样取平均,提高测量精度
-
低功耗管理
- 关机状态下进入掉电模式(功耗约0.4μA)
- 使用掉电唤醒定时器(约1秒唤醒一次)
- 唤醒后快速检测按键和电池状态
-
智能定时
- 2分钟自动关机(牙医推荐标准刷牙时间)
- 30秒分区提醒(口腔4个区域各30秒)
- LED闪烁提示换区
-
多模式设计
- 轻柔/标准/强劲/按摩四种工作模式
- 每种模式支持3档力度调节
- 按摩模式实现脉冲变速效果
三、使用说明
-
下载设置
- 在STC-ISP软件中选择芯片型号:STC32G8K64
- 选择内部IRC时钟:24MHz
- 硬件选项中取消勾选"允许低压复位(禁止低压中断)"
-
硬件连接
- P1.0通过MOS管驱动电路连接电机
- P1.2通过2个10K电阻分压后连接电池正极
- P3.2和P3.3接按键到GND
- P1.1串接LED和限流电阻到GND
-
操作方式
- 短按P3.2:开机/切换模式
- 长按P3.2(2秒):关机
- 短按P3.3:调节力度
四、注意事项
- 电机驱动需要外接N-MOS管和续流二极管,不可直接驱动电机
- ADC_VRef+引脚必须连接VCC或外部参考电压
- 进入掉电模式前确保所有I/O口处于低功耗状态
- 如需启用看门狗,取消代码中
#if 0的注释并改为 #if 1
如需进一步调整某个功能模块或增加新功能(如蓝牙通信、充电管理、USB-CDC调试等),请告诉我,我可以为您扩展代码。
🔍 搜索 "用 STC32G8K64生成一段完整的演示程序 用于电动牙刷项目 并含详细程序注解"
📌 相关论坛讨论
- 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】
⏱️ 查找用时 23.7s | 总回答用时 100.4s