|
爱科技、爱创意、爱折腾、爱极致,我们都是技术控
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 devcang 于 2025-5-19 21:00 编辑
- #include <STC8H2K12U.h>
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- // 引脚定义
- sbit IR_IN = P3^4; // 红外接收引脚(中断方式)
- sbit BTN = P3^2; // 按钮引脚(中断方式)
- sbit IR_OUT = P3^3; // 红外发送引脚
- sbit RED_LED = P3^5; // 状态指示LED(红光)
- // 系统状态定义
- typedef enum {
- STATE_IDLE, // 空闲状态
- STATE_LEARNING, // 学习模式
- STATE_SENDING, // 发送状态
- STATE_SLEEPING // 休眠状态
- } SystemState;
- // 红外接收状态
- typedef enum {
- IR_STATE_IDLE, // 空闲状态
- IR_STATE_HEADER, // 接收引导码
- IR_STATE_DATA, // 接收数据
- IR_STATE_DONE // 接收完成
- } IRState;
- // NEC协议参数(时间根据22.184MHz晶振调整)
- #define NEC_HEADER_HIGH_TIME 9000 // 引导码高电平时间(us)
- #define NEC_HEADER_LOW_TIME 4500 // 引导码低电平时间(us)
- #define NEC_BIT_1_HIGH_TIME 560 // 数据位1高电平时间(us)
- #define NEC_BIT_1_LOW_TIME 1690 // 数据位1低电平时间(us)
- #define NEC_BIT_0_HIGH_TIME 560 // 数据位0高电平时间(us)
- #define NEC_BIT_0_LOW_TIME 560 // 数据位0低电平时间(us)
- #define NEC_REPEAT_HIGH_TIME 9000 // 重复码高电平时间(us)
- #define NEC_REPEAT_LOW_TIME 2250 // 重复码低电平时间(us)
- #define NEC_REPEAT_PULSE_TIME 560 // 重复码脉冲时间(us)
- #define NEC_FRAME_INTERVAL 100000 // 帧间隔时间(us)
- // 内部EEPROM参数
- #define EEPROM_START_ADDR 0x2000 // EEPROM起始地址
- #define IR_DATA_ADDR 0x2000 // 红外数据存储地址
- // 按钮参数
- #define BTN_DEBOUNCE_TIME 20 // 消抖时间(ms)
- #define BTN_LONG_PRESS_TIME 2000 // 长按时间(ms)
- // 全局变量
- SystemState g_SystemState = STATE_IDLE; // 系统当前状态
- uint g_TimerCount = 0; // 定时器计数
- uint g_BtnPressTime = 0; // 按钮按下时间
- uint g_IRData[4] = {0}; // 存储红外编码数据
- uchar g_IRDataReady = 0; // 红外数据就绪标志
- uchar g_HourCounter = 0; // 小时计数器
- uchar g_BtnDebounce = 0; // 按键消抖标志
- uchar g_BtnPressed = 0; // 按键按下标志
- // 红外接收相关变量
- IRState g_IRState = IR_STATE_IDLE; // 红外接收状态
- uint g_IRHighTime = 0; // 高电平时间
- uint g_IRLowTime = 0; // 低电平时间
- uint g_IRDataBuffer = 0; // 红外数据缓冲区
- uchar g_IRBitCount = 0; // 红外位计数器
- uchar g_IRByteIndex = 0; // 红外字节索引
- // 函数声明
- void System_Init(void);
- void Timer0_Init(void);
- void Ext0_Init(void);
- void Ext1_Init(void);
- void Disable_Ext0(void);
- void Enable_Ext0(void);
- void Disable_Ext1(void);
- void Enable_Ext1(void);
- void EEPROM_Init(void);
- uchar EEPROM_ReadByte(uint addr);
- void EEPROM_WriteByte(uint addr, uchar dat);
- void EEPROM_EraseSector(uint addr);
- void EEPROM_WriteIRData(uint *data);
- void EEPROM_ReadIRData(uint *data);
- void Handle_Button(void);
- void Enter_Sleep_Mode(void);
- void Exit_Sleep_Mode(void);
- void Update_LED(void);
- void Delay_us(uint us);
- void Delay_ms(uint ms);
- void Send_NEC_Frame(uint *data);
- void Generate_38KHz_Pulse(uint time_us);
- void Process_IR_Data(void);
- // 系统初始化
- void System_Init(void) {
- P3M0 = 0x20; // P3.5(LED)设置为推挽输出
- P3M1 = 0x00;
-
- Timer0_Init();
- Ext0_Init(); // 初始化外部中断0(按钮)
- Ext1_Init(); // 初始化外部中断1(红外)
- EEPROM_Init();
- RED_LED = 0; // 初始LED熄灭
- }
- // 定时器0初始化(100us)
- void Timer0_Init(void) {
- TMOD &= 0xF0; // 清除定时器0模式位
- TMOD |= 0x01; // 设置定时器0为模式1(16位自动重装载)
- TH0 = 0xFD; // 定时初值高8位(22.184MHz晶振)
- TL0 = 0x40; // 定时初值低8位(100us@22.184MHz)
- ET0 = 1; // 使能定时器0中断
- TR0 = 1; // 启动定时器0
- EA = 1; // 开总中断
- }
- // 外部中断0初始化(按钮,下降沿触发)
- void Ext0_Init(void) {
- IT0 = 1; // 设置INT0为边沿触发
- EX0 = 1; // 使能外部中断0
- }
- // 外部中断1初始化(红外,下降沿触发)
- void Ext1_Init(void) {
- IT1 = 1; // 设置INT1为边沿触发
- EX1 = 1; // 使能外部中断1
- }
- // 禁用外部中断0
- void Disable_Ext0(void) {
- EX0 = 0; // 禁用外部中断0
- }
- // 启用外部中断0
- void Enable_Ext0(void) {
- EX0 = 1; // 启用外部中断0
- }
- // 禁用外部中断1
- void Disable_Ext1(void) {
- EX1 = 0; // 禁用外部中断1
- }
- // 启用外部中断1
- void Enable_Ext1(void) {
- EX1 = 1; // 启用外部中断1
- }
- // EEPROM初始化(STC8H系列)
- void EEPROM_Init(void) {
- IAP_CONTR = 0x80; // 使能IAP功能,Fosc≤24MHz
- }
- // 从EEPROM读取一个字节
- uchar EEPROM_ReadByte(uint addr) {
- uchar dat;
- EA = 0; // 关中断
-
- IAP_CONTR = 0x81; // 使能IAP功能,Fosc≤24MHz
- IAP_CMD = 1; // 读命令
- IAP_ADDRH = (uchar)(addr >> 8); // 设置地址高字节
- IAP_ADDRL = (uchar)(addr); // 设置地址低字节
- IAP_TRIG = 0x46; // 触发命令
- IAP_TRIG = 0xB9; // 触发命令
-
- dat = IAP_DATA; // 读取数据
-
- IAP_CONTR = 0; // 关闭IAP功能
- EA = 1; // 开中断
-
- return dat;
- }
- // 向EEPROM写入一个字节
- void EEPROM_WriteByte(uint addr, uchar dat) {
- EA = 0; // 关中断
-
- IAP_CONTR = 0x81; // 使能IAP功能,Fosc≤24MHz
- IAP_CMD = 2; // 写命令
- IAP_ADDRH = (uchar)(addr >> 8); // 设置地址高字节
- IAP_ADDRL = (uchar)(addr); // 设置地址低字节
- IAP_DATA = dat; // 设置要写入的数据
- IAP_TRIG = 0x46; // 触发命令
- IAP_TRIG = 0xB9; // 触发命令
-
- IAP_CONTR = 0; // 关闭IAP功能
- EA = 1; // 开中断
-
- Delay_ms(5); // 等待写入完成
- }
- // 擦除EEPROM扇区(512字节)
- void EEPROM_EraseSector(uint addr) {
- EA = 0; // 关中断
-
- IAP_CONTR = 0x81; // 使能IAP功能,Fosc≤24MHz
- IAP_CMD = 3; // 擦除命令
- IAP_ADDRH = (uchar)(addr >> 8); // 设置地址高字节
- IAP_ADDRL = (uchar)(addr); // 设置地址低字节
- IAP_TRIG = 0x46; // 触发命令
- IAP_TRIG = 0xB9; // 触发命令
-
- IAP_CONTR = 0; // 关闭IAP功能
- EA = 1; // 开中断
-
- Delay_ms(5); // 等待擦除完成
- }
- // 保存红外数据到EEPROM
- void EEPROM_WriteIRData(uint *data) {
- uchar i;
- uchar *p = (uchar *)data;
-
- // 擦除扇区
- EEPROM_EraseSector(IR_DATA_ADDR);
-
- // 写入8个字节的红外数据
- for(i = 0; i < 8; i++) {
- EEPROM_WriteByte(IR_DATA_ADDR + i, p[i]);
- }
-
- // 写入校验字节
- uchar checksum = 0;
- for(i = 0; i < 8; i++) {
- checksum ^= p[i];
- }
- EEPROM_WriteByte(IR_DATA_ADDR + 8, checksum);
- }
- // 从EEPROM读取红外数据
- void EEPROM_ReadIRData(uint *data) {
- uchar i;
- uchar *p = (uchar *)data;
- uchar checksum = 0;
-
- // 读取8个字节的红外数据
- for(i = 0; i < 8; i++) {
- p[i] = EEPROM_ReadByte(IR_DATA_ADDR + i);
- checksum ^= p[i];
- }
-
- // 读取校验字节
- uchar stored_checksum = EEPROM_ReadByte(IR_DATA_ADDR + 8);
-
- // 验证校验和
- if(checksum == stored_checksum) {
- g_IRDataReady = 1;
- } else {
- g_IRDataReady = 0;
- }
- }
- // 处理按键事件
- void Handle_Button(void) {
- if(g_BtnPressed) {
- uint press_time = g_TimerCount - g_BtnPressTime;
-
- // 长按进入/退出休眠模式
- if(press_time > (BTN_LONG_PRESS_TIME * 10)) {
- if(g_SystemState == STATE_SLEEPING) {
- Exit_Sleep_Mode();
- } else {
- Enter_Sleep_Mode();
- }
- g_BtnPressed = 0;
- }
- }
- }
- // 进入休眠模式
- void Enter_Sleep_Mode(void) {
- g_SystemState = STATE_SLEEPING;
- Disable_Ext0(); // 禁用外部中断0(按钮)
- Disable_Ext1(); // 禁用外部中断1(红外)
- TR0 = 0; // 停止定时器0
- RED_LED = 0; // 熄灭LED
-
- // 配置唤醒方式(低电平触发按钮)
- IT0 = 0; // 设置INT0为电平触发
- Enable_Ext0(); // 启用外部中断0(按钮)
-
- PCON |= 0x02; // 进入掉电模式
- _nop_(); // 确保指令执行
- }
- // 退出休眠模式
- void Exit_Sleep_Mode(void) {
- Disable_Ext0(); // 禁用外部中断0
-
- // 恢复系统状态
- g_SystemState = STATE_IDLE;
- Ext0_Init(); // 重新初始化外部中断0(按钮,下降沿触发)
- Ext1_Init(); // 重新初始化外部中断1(红外,下降沿触发)
- Timer0_Init(); // 重新初始化定时器0
- }
- // 更新LED状态
- void Update_LED(void) {
- switch(g_SystemState) {
- case STATE_IDLE:
- RED_LED = 0; // 熄灭
- break;
- case STATE_LEARNING:
- // 慢速闪烁(0.5Hz)
- if((g_TimerCount / 20000) % 2 == 0) {
- RED_LED = 1;
- } else {
- RED_LED = 0;
- }
- break;
- case STATE_SENDING:
- RED_LED = 1; // 点亮
- break;
- case STATE_SLEEPING:
- RED_LED = 0; // 熄灭
- break;
- }
- }
- // 微秒延时函数(针对22.184MHz晶振调整)
- void Delay_us(uint us) {
- while(us--) {
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- }
- }
- // 毫秒延时函数
- void Delay_ms(uint ms) {
- while(ms--) {
- Delay_us(1000);
- }
- }
- // 生成38KHz载波
- void Generate_38KHz_Pulse(uint time_us) {
- uint i;
- uint cycles = time_us / 26; // 38KHz周期约26us
-
- for(i = 0; i < cycles; i++) {
- IR_OUT = 1;
- Delay_us(13);
- IR_OUT = 0;
- Delay_us(13);
- }
- }
- // 发送NEC格式帧
- void Send_NEC_Frame(uint *data) {
- uchar i, j;
- uint temp;
-
- // 发送引导码
- Generate_38KHz_Pulse(NEC_HEADER_HIGH_TIME);
- Delay_us(NEC_HEADER_LOW_TIME);
-
- // 发送用户码(16位)
- for(i = 0; i < 2; i++) {
- temp = data[i];
- for(j = 0; j < 8; j++) {
- // 发送数据位
- Generate_38KHz_Pulse(NEC_BIT_1_HIGH_TIME);
- if(temp & 0x01) {
- Delay_us(NEC_BIT_1_LOW_TIME);
- } else {
- Delay_us(NEC_BIT_0_LOW_TIME);
- }
- temp >>= 1;
- }
- }
-
- // 发送键数据码和键数据反码(16位)
- for(i = 2; i < 4; i++) {
- temp = data[i];
- for(j = 0; j < 8; j++) {
- // 发送数据位
- Generate_38KHz_Pulse(NEC_BIT_1_HIGH_TIME);
- if(temp & 0x01) {
- Delay_us(NEC_BIT_1_LOW_TIME);
- } else {
- Delay_us(NEC_BIT_0_LOW_TIME);
- }
- temp >>= 1;
- }
- }
-
- // 发送结束脉冲
- Generate_38KHz_Pulse(NEC_BIT_1_HIGH_TIME);
- }
- // 处理接收到的红外数据
- void Process_IR_Data(void) {
- // 验证数据(用户码取反和键码取反)
- if((g_IRData[0] != (~g_IRData[1] & 0xFFFF)) ||
- (g_IRData[2] != (~g_IRData[3] & 0xFFFF))) {
- return; // 数据校验失败
- }
-
- // 保存接收到的数据
- EEPROM_WriteIRData(g_IRData);
- g_IRDataReady = 1;
-
- // 学习完成,返回空闲状态
- g_SystemState = STATE_IDLE;
- }
- // 定时器0中断服务函数(100us)
- void Timer0_ISR(void) interrupt 1 {
- TH0 = 0xFD; // 重新加载初值(22.184MHz)
- TL0 = 0x40;
-
- g_TimerCount++;
-
- // 每小时发送一次红外编码
- if(g_SystemState != STATE_SLEEPING && g_SystemState != STATE_LEARNING) {
- if(g_TimerCount >= 36000000) { // 1小时 = 3600秒 = 36000000 * 100us
- g_TimerCount = 0;
- g_HourCounter++;
-
- if(g_IRDataReady) {
- g_SystemState = STATE_SENDING;
- Send_NEC_Frame(g_IRData);
- Delay_ms(100); // 确保发送完成
- g_SystemState = STATE_IDLE;
- }
- }
- }
-
- // 按键消抖计数
- if(g_BtnDebounce > 0) {
- g_BtnDebounce--;
- }
- }
- // 外部中断0服务函数(按钮)
- void Ext0_ISR(void) interrupt 0 {
- static uchar last_state = 1; // 上次引脚状态
- uchar current_state = BTN; // 当前引脚状态
-
- // 状态变化检测
- if(current_state != last_state) {
- // 记录状态变化时间
- uint change_time = g_TimerCount;
-
- // 消抖处理
- if(g_BtnDebounce == 0) {
- if(current_state == 0) { // 按键按下
- g_BtnPressTime = change_time;
- g_BtnPressed = 1;
- g_BtnDebounce = BTN_DEBOUNCE_TIME;
- } else { // 按键释放
- // 短按切换学习模式
- if(g_BtnPressed && (change_time - g_BtnPressTime) < (BTN_LONG_PRESS_TIME * 10)) {
- if(g_SystemState == STATE_IDLE) {
- g_SystemState = STATE_LEARNING;
- // 初始化红外接收状态
- g_IRState = IR_STATE_IDLE;
- g_IRBitCount = 0;
- g_IRByteIndex = 0;
- g_IRDataBuffer = 0;
- } else if(g_SystemState == STATE_LEARNING) {
- g_SystemState = STATE_IDLE;
- }
- }
- g_BtnPressed = 0;
- g_BtnDebounce = BTN_DEBOUNCE_TIME;
- }
- }
- }
-
- last_state = current_state;
-
- // 如果是从休眠中唤醒
- if(g_SystemState == STATE_SLEEPING) {
- Exit_Sleep_Mode();
- }
- }
- // 外部中断1服务函数(红外接收)
- void Ext1_ISR(void) interrupt 2 {
- static uchar last_state = 1; // 上次引脚状态
- uchar current_state = IR_IN; // 当前引脚状态
-
- // 状态变化检测
- if(current_state != last_state) {
- // 记录状态变化时间
- uint change_time = g_TimerCount;
-
- // 红外接收处理(仅在学习模式下)
- if(g_SystemState == STATE_LEARNING) {
- if(current_state == 1) { // 上升沿
- // 记录高电平时间
- g_IRHighTime = change_time;
- } else { // 下降沿
- // 计算高电平持续时间
- uint high_duration = change_time - g_IRHighTime;
-
- switch(g_IRState) {
- case IR_STATE_IDLE:
- // 等待引导码高电平
- if(high_duration > ((NEC_HEADER_HIGH_TIME - 1000) / 100) &&
- high_duration < ((NEC_HEADER_HIGH_TIME + 1000) / 100)) {
- g_IRState = IR_STATE_HEADER;
- g_IRLowTime = change_time;
- }
- break;
-
- case IR_STATE_HEADER:
- // 检测引导码低电平
- if(high_duration > ((NEC_HEADER_LOW_TIME - 1000) / 100) &&
- high_duration < ((NEC_HEADER_LOW_TIME + 1000) / 100)) {
- g_IRState = IR_STATE_DATA;
- g_IRBitCount = 0;
- g_IRByteIndex = 0;
- g_IRDataBuffer = 0;
- } else {
- g_IRState = IR_STATE_IDLE; // 错误,重新开始
- }
- break;
-
- case IR_STATE_DATA:
- // 接收数据位
- if(g_IRBitCount < 8) {
- // 存储数据位
- if(high_duration > ((NEC_BIT_1_LOW_TIME - 300) / 100)) {
- g_IRDataBuffer |= (1 << g_IRBitCount); // 位1
- }
-
- g_IRBitCount++;
-
- // 一个字节接收完成
- if(g_IRBitCount >= 8) {
- g_IRData[g_IRByteIndex] = g_IRDataBuffer;
- g_IRByteIndex++;
- g_IRBitCount = 0;
- g_IRDataBuffer = 0;
-
- // 32位数据全部接收完成
- if(g_IRByteIndex >= 4) {
- g_IRState = IR_STATE_DONE;
- Process_IR_Data();
- }
- }
- }
- break;
-
- default:
- g_IRState = IR_STATE_IDLE;
- break;
- }
- }
- }
- }
-
- last_state = current_state;
- }
- // 主函数
- void main(void) {
- // 系统初始化
- System_Init();
-
- // 从EEPROM读取保存的红外数据
- EEPROM_ReadIRData(g_IRData);
-
- // 如果没有有效数据,进入学习模式
- if(!g_IRDataReady) {
- g_SystemState = STATE_LEARNING;
- }
-
- while(1) {
- // 只有在非休眠模式下才检测按钮
- if(g_SystemState != STATE_SLEEPING) {
- Handle_Button();
- Update_LED();
- }
-
- // 其他低优先级任务
- // ...
- }
- }
复制代码
STC8H2K12U
外围很少的
22.1184MHZ
P3^2 连接按钮
P3^4红外接收
上边代码是AI自动生成的,raw-copy、没修改过,大家说,可以用么
|
|