EM78P153 智能风扇控制程序(支持调速调档)
基于 EM78P153(Elan 1KB OTP MCU)编写,使用纯 C 语言实现。程序包含 按键调档(4档)、温度传感器自动调速(NTC) 以及 软件 PWM 无级调速 功能。由于 EM78P153 资源极度受限(RAM: 36 Bytes, ROM: 1K x 12 bit),代码经过高度精简,未使用任何浮点运算和标准库函数。
一、 硬件资源配置与连接
| EM78P153 引脚 | 硬件连接 | 功能说明 |
|---|---|---|
| P60 (IO) | 风扇 MOSFET/三极管 | PWM 输出控制风扇风速 |
| P61 (IO) | 按键 1 | 档位切换键 (短按) / 开关机 (长按) |
| P62 (IO) | 按键 2 | 手动风速加 (仅限手动模式) |
| P63 (IO) | 按键 3 | 手动风速减 (仅限手动模式) |
| P64 (IO) | NTC 温度传感器 | 接入 RC 充放电电路,检测环境温度 |
| P67 (IO) | LED / 数码管 | 显示当前档位或风速等级 |
二、 核心宏定义与全局变量 (main.c)
#include "EM78P153.h" // 引入 EM78P153 专用头文件 (需对应编译器)
// ===== 硬件抽象层宏定义 =====
#define FAN_PWM_PORT P6CR // 风扇 PWM 端口控制寄存器
#define FAN_PWM_PIN 0 // P60 控制风扇
#define KEY_PORT P6 // 按键端口
#define TEMP_PORT P6 // 温度端口
#define TEMP_PIN 4 // P64 连接 NTC
// ===== 系统参数定义 =====
#define SYS_CLK 4000000 // 假设系统时钟 4MHz
#define PWM_PERIOD 100 // PWM 周期分为 100 份 (0~99)
#define TEMP_SAMPLE_TIME 1000 // 温度采样间隔 (ms)
// ===== 全局变量 (EM78P153 RAM极小,需谨慎使用) =====
unsigned char g_fan_level = 0; // 当前档位: 0=关机, 1=低, 2=中, 3=高, 4=自动
unsigned char g_pwm_duty = 0; // 当前 PWM 占空比 (0-100)
unsigned char g_adc_value = 0; // 温度 ADC 采样值 (0-255)
bit gb_auto_mode = 0; // 自动模式标志位
bit gb_key_pressed = 0; // 按键按下标志
unsigned int gui_system_ticks = 0; // 系统滴答计数器 (需用定时器中断或软件计数)
三、 底层驱动函数
3.1 简易软件延时函数
(注意:EM78P153 指令集简单,通常依靠 _nop() 或空循环实现粗略延时)
/**
* @brief 粗略毫秒级延时 (基于 4MHz 主频估算)
*/
void delay_ms(unsigned int ms) {
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 1141; j++) // 1141 约为 4MHz 下 1ms 的指令周期数
_nop_(); // 空指令,占用一个机器周期
}
3.2 RC 充放电法读取 NTC 温度
由于 EM78P153 没有内置 ADC,利用 IO 口的电平翻转和内部定时器来测量 NTC 电阻值,从而计算温度。
/**
* @brief 读取 NTC 温度值 (返回 0-255 的相对数值)
*/
unsigned char read_temperature(void) {
unsigned char count = 0;
// 1. 放电:将引脚设为输出,并拉低电平
TEMP_PORT &= ~(1 << TEMP_PIN);
P6CR |= (1 << TEMP_PIN); // 设为输出
delay_ms(10); // 等待电容放电完毕
// 2. 充电:将引脚设为输入(高阻态),电容通过 NTC 充电
P6CR &= ~(1 << TEMP_PIN); // 设为输入
// 3. 计时:等待引脚电平升高至高电平阈值
while((TEMP_PORT & (1 << TEMP_PIN)) == 0) {
count++;
if(count >= 250) break; // 防止死循环,最大计时约 250us
delay_us(10); // 粗略延时 10us
}
// 4. 恢复引脚状态(根据后续电路需求,通常恢复为输出低或输入)
P6CR |= (1 << TEMP_PIN);
TEMP_PORT &= ~(1 << TEMP_PIN);
return count; // count 越大,说明 NTC 阻值越大,温度越低
}
3.3 按键扫描函数 (三键独立)
/**
* @brief 扫描按键并返回按下的键值
* @return 0:无按键 1:按键1 2:按键2 3:按键3
*/
unsigned char key_scan(void) {
static unsigned char s_last_key = 0;
unsigned char key_val = 0;
// 读取按键引脚状态 (假设按下为低电平 0)
if((KEY_PORT & 0x06) != 0x06) { // 检查 P61 和 P62 (0110)
if((KEY_PORT & 0x02) == 0) key_val = 1; // P61 按下
if((KEY_PORT & 0x04) == 0) key_val = 2; // P62 按下
}
// 消抖处理:只有当前状态与上次状态不同且稳定时才确认按键
if(key_val == s_last_key) {
return 0;
} else {
s_last_key = key_val;
delay_ms(20); // 消抖延时
return key_val;
}
}
四、 业务逻辑控制层
4.1 PWM 波形生成与风扇驱动
在主循环或定时中断中调用此函数,产生 100 级可调的 PWM 波。
/**
* @brief 更新风扇 PWM 输出
* @param duty 占空比 (0-100)
*/
void fan_update_pwm(unsigned char duty) {
static unsigned char pwm_counter = 0;
pwm_counter++;
if(pwm_counter >= PWM_PERIOD) {
pwm_counter = 0;
}
if(g_fan_level == 0) { // 关机状态
FAN_PWM_PORT &= ~(1 << FAN_PWM_PIN);
} else {
if(pwm_counter < duty) {
FAN_PWM_PORT |= (1 << FAN_PWM_PIN); // 输出高电平
} else {
FAN_PWM_PORT &= ~(1 << FAN_PWM_PIN); // 输出低电平
}
}
}
4.2 智能温控与档位管理
/**
* @brief 根据温度自动调节 PWM 占空比
*/
void auto_temp_control(void) {
unsigned char target_duty = 0;
if(gb_auto_mode) {
g_adc_value = read_temperature();
// 简单的分段线性控制 (根据实际 NTC 特性校准)
// 假设 g_adc_value: 小于50为高温,大于200为低温
if(g_adc_value < 50) {
target_duty = 30; // 高温 -> 低速 (省电)
} else if(g_adc_value < 150) {
target_duty = 60; // 中温 -> 中速
} else {
target_duty = 95; // 低温 -> 高速 (强力制热/吹风)
}
// 平滑过渡:防止风速突变引起电机异响
if(g_pwm_duty < target_duty) g_pwm_duty++;
else if(g_pwm_duty > target_duty) g_pwm_duty--;
}
}
/**
* @brief 处理按键事件并更新档位
*/
void process_keys(void) {
unsigned char key = key_scan();
if(key == 0) return;
switch(key) {
case 1: // 按键1:切换档位 (循环: 关 -> 低 -> 中 -> 高 -> 自动)
g_fan_level++;
if(g_fan_level > 4) g_fan_level = 0;
gb_auto_mode = (g_fan_level == 4) ? 1 : 0;
// 设定各档位固定风速
if(g_fan_level == 1) g_pwm_duty = 30; // 低档
else if(g_fan_level == 2) g_pwm_duty = 60; // 中档
else if(g_fan_level == 3) g_pwm_duty = 90; // 高档
break;
case 2: // 按键2:手动加速 (仅在非自动模式下有效)
if(!gb_auto_mode && g_fan_level > 0) {
if(g_pwm_duty < 90) g_pwm_duty += 10;
}
break;
case 3: // 按键3:手动减速
if(!gb_auto_mode && g_fan_level > 0) {
if(g_pwm_duty > 10) g_pwm_duty -= 10;
}
break;
}
}
五、 主函数入口 (main)
void main(void) {
// ===== 1. 芯片初始化 =====
WDTC = 0x07; // 关闭看门狗定时器 (根据具体型号配置)
P6CR = 0x00; // 默认所有 P6 口为输入
P6PH = 0xFF; // 开启 P6 上拉电阻
P6IE = 0x00; // 关闭 P6 中断
// 配置控制引脚为输出
P6CR |= (1 << FAN_PWM_PIN);
// ===== 2. 上电默认状态 =====
g_fan_level = 1; // 默认低档启动
g_pwm_duty = 30;
gui_system_ticks = 0;
// ===== 3. 主循环 =====
while(1) {
// 任务1:处理按键输入
process_keys();
// 任务2:智能温控调节 (每 100ms 调整一次,避免过于频繁)
if((gui_system_ticks % 100) == 0) {
auto_temp_control();
}
// 任务3:更新 PWM 输出 (建议在定时器中断中做,此处用循环延时模拟)
fan_update_pwm(g_pwm_duty);
// 任务4:系统节拍延时 (替代简易 RTOS 的时基)
delay_ms(1);
gui_system_ticks++;
if(gui_system_ticks >= 60000) gui_system_ticks = 0; // 防止溢出
}
}
参考代码 EM78P153 程序可以调速调档智能风扇 www.youwenfan.com/contentcnv/72248.html
六、 特点与移植
- 极低资源占用:
- 全程采用
unsigned char(单字节) 进行计算,避免多字节运算占用宝贵的指令周期和 RAM。 - 舍弃复杂的浮点运算,PWM 采用 0-100 的整数占比。
- 全程采用
- 无硬件依赖抽象:
- 代码中
EM78P153.h需替换为您实际使用的编译器提供的头文件。 delay_us/delay_ms需根据实际晶振频率调整内循环次数。
- 代码中
- 扩展建议:
- 休眠节能:若风扇长时间无操作,可在主循环中进入
HALT模式,通过外部按键中断唤醒。 - IR 遥控:可复用按键处理逻辑,在 P63 接入红外接收头,解码 NEC 协议实现遥控调速。
- 休眠节能:若风扇长时间无操作,可在主循环中进入
浙公网安备 33010602011771号