//****************************************************************
//STC32G12K128光伏充电灯具控制程序
//晶振22.1184M
//版本:SUN2026-04-20GBU
//广东省中山市
//****************************************************************
#include <STC32G12K128.h> // 包含STC32G系列单片机寄存器定义头文件
#include <hong.h> // 包含自定义功能驱动头文件
#define YDM_MIN 88 // 红外引导码总时间最小值,标准值90
#define YDM_MAX 90 // 红外引导码总时间最大值
#define SJZ_MIN 5 // 数据码"0"高电平最小值,标准7
#define SJZ_MAX 8 // 数据码"0"高电平最大值
#define SJK_MIN 19 // 数据码"1"高电平最小值,标准21
#define SJK_MAX 22 // 数据码"1"高电平最大值
#define DATA_NUM 32 // 红外一帧数据总位数32位
#define IR_KEY_COUNT (sizeof(ir_code_array)/sizeof(ir_code_array[0])) // 自动计算红外码表数量
sfr BRT = 0x9C; // 独立波特率发生器寄存器
sfr WDT_CONTR = 0xC1; // 看门狗控制寄存器
//****************************************************************
//【本程序用到的所有函数声明】
//****************************************************************
void buzz(void); // 蜂鸣器短响函数声明
void DelayMs(u16 ms); // 毫秒级延时函数声明
void Delay(u8 us); // 微秒级延时函数声明
void UART_SendNum(u16 num); // 串口数字发送函数声明
void UART_FS(void); // 预留函数
void Key_Scan(void); // 按键扫描函数
void IR_Code_Match(u32 now_code); // 红外解码匹配函数
//****************************************************************
//【本程序用到的所有全局变量】
//****************************************************************
bit Flag0=0; // 充电允许控制标志
bit Flag1=0; // 输出使能控制标志
bit Flag2=0; // 系统关机标志位,0=电池禁止放电(出厂模式)
bit Flag3=0; // 红外接收完成、允许打印标志
bit ydmgdbzw = 0; // 引导码高低电平过渡标志
u8 ydordata = 0; // 0=等待引导码 1=接收数据码
u8 ydm_low = 0; // 引导码低电平计时值
u8 ydm_high = 0; // 引导码高电平计时值
u16 ydmtime = 0; // 引导码总时间(低+高)
u8 sjm_high = 0; // 数据码高电平计时
u8 code_low = 0; // 引导码低电平暂存
u8 code_high = 0; // 引导码高电平暂存
u32 irdata = 0; // 32位红外原始数据暂存
u8 capture_en = 0; // 脉宽捕获使能标志
u8 data_cnt = 0; // 已接收数据位计数
u16 ir_data[DATA_NUM]; // 存储32位脉宽数据
//****************************************************************
//【所有IO口定义】
//****************************************************************
sbit IR_IN = P3^2; // 红外接收信号输入脚
sbit BEEP = P7^1; // 蜂鸣器控制脚
sbit LED = P7^0; // 状态指示灯控制脚
sbit KEY1 = P0^2; // 按键1输入脚
sbit KEY2 = P0^3; // 按键2输入脚
sbit KEY3 = P0^4; // 按键3输入脚
sbit KEY4 = P5^2; // 按键4输入脚
//****************************************************************
//【红外按键码表】
//****************************************************************
u32 ir_code_array[] =
{
0x00FFAA5D, // 红色键
0x00FF629D, // 开键
0x00FF22DD, // 关键
0x00FFC23D, // 上
0x00FFB04F, // 下
0x00FFE01F, // 左
0x00FF906F, // 右
0x00FF10EF, // 中
0x00FF9867, // 长条
0x00FFA857, // 3H
0x00FF6897, // 5H
0x00FF18E7 // 8H
};
//****************************************************************
//【外部中断0服务函数】
//【红外解码核心:引导码+32位数据码】
//****************************************************************
void External0_ISR(void) interrupt 0
{
EA = 0; // 关闭总中断,防止中断嵌套干扰
if (Flag3==0) // 仅当未接收完成时才解码
{
//【引导码解码部分】
//****************************************************************
if(ydordata==0&&IR_IN==0&&ydmgdbzw==0) // 检测到引导码起始低电平
{
ydm_low = 0; // 清空引导码低电平计数
ydm_high = 0; // 清空引导码高电平计数
TR0=1; // 启动定时器0开始计时
ET0=1; // 使能定时器0中断
ydmgdbzw=0; // 清除引导码过渡标志
}
else if(ydordata==0&&IR_IN==1&&ydmgdbzw==0) // 引导码低转高
{
ydmgdbzw=1; // 标记进入高电平阶段
}
else if(ydordata==0&&IR_IN==0&&ydmgdbzw==1) // 引导码高转低,结束
{
code_low=ydm_low; // 保存引导码低电平时间
code_high=ydm_high; // 保存引导码高电平时间
ydmtime=ydm_low+ydm_high; // 计算引导码总时长
TR0=0; // 停止定时器0
ET0=0; // 关闭定时器0中断
ydmgdbzw=0; // 清除过渡标志
if(ydmtime>YDM_MIN&&ydmtime<YDM_MAX) // 判断是否为有效引导码
{
ydordata=1; // 标记引导码完成,准备接收数据
sjm_high = 0; // 清空数据码高电平计数
ydmgdbzw = 0; // 清除过渡标志
data_cnt = 0; // 数据位计数清零
irdata = 0; // 32位数据寄存器清零
}
}
//****************************************************************
//【数据码解码部分,只判断高电平宽度】
//****************************************************************
else if(ydordata == 1) // 引导码已完成,开始接收32位数据
{
if(IR_IN == 1) // 上升沿:开始计高电平
{
sjm_high = 0; // 清零计时
TR1 = 1; // 启动定时器1
ET1 = 1; // 打开定时器1中断
}
else if(IR_IN == 0) // 下降沿:高电平结束
{
TR1 = 0; // 停止定时器1
ET1 = 0; // 关闭定时器1中断
if(data_cnt < 32) // 只接收32位数据
{
// 判断为数据0
if(sjm_high > SJZ_MIN && sjm_high < SJZ_MAX)
{
ir_data[data_cnt] = sjm_high;
irdata = (irdata << 1) | 0;
data_cnt++;
}
// 判断为数据1
else if(sjm_high > SJK_MIN && sjm_high < SJK_MAX)
{
ir_data[data_cnt] = sjm_high;
irdata = (irdata << 1) | 1;
data_cnt++;
}
}
if(data_cnt >= 32) // 32位数据接收完成
{
TR1 = 0; // 停止定时器
ET1 = 0; // 关闭中断
Flag3 = 1; // 置位接收完成标志
EX0=0; // 关闭外部中断0
TCON &= 0xFE; // 清除IE0中断标志,防止重复触发
}
}
}
}
EA=1; // 恢复总中断使能
}
//****************************************************************
// 【红外对码匹配函数】自动匹配按键并执行动作
//****************************************************************
void IR_Code_Match(u32 now_code)
{
unsigned char i;
for(i=0; i<IR_KEY_COUNT; i++)
{
if(now_code == ir_code_array[i])
{
buzz(); // 匹配成功,蜂鸣器提示
switch(i) // 根据按键执行动作
{
//case 0: LED = 1; break; // 红色键:LED翻转
case 1: LED = 1; break; // 开键:LED亮
case 2: LED = 0; break; // 关键:LED灭
}
break;
}
}
}
//****************************************************************
// 【蜂鸣器提示函数】响一声
//****************************************************************
void buzz(void)
{
unsigned char i;
BEEP=0; // 蜂鸣器响
for(i=0;i<200;i++)
{
Delay(20);
}
BEEP=1; // 蜂鸣器停
}
//****************************************************************
// 毫秒级延时函数
//****************************************************************
void DelayMs(u16 ms)
{
unsigned int i;
do{
i = MAIN_Fosc / 7500;
while(--i);
}while(--ms);
}
//****************************************************************
// 微秒级延时函数
//****************************************************************
void Delay(u8 us)
{
unsigned int j;
do{
j = MAIN_Fosc / 750000;
while(--j);
}while(--us);
}
//****************************************************************
// 定时器1初始化 定时80us 用于数据码计时
//****************************************************************
void Timer1_Init(void)
{
AUXR |= 0x40; // 定时器1时钟不分频
TMOD &= 0x0F; // 配置为16位自动重装模式
TL1 = 0xA0; // 定时初值低8位
TH1 = 0xF6; // 定时初值高8位
TF1 = 0; // 清除溢出标志
TR1 = 0; // 先不启动
ET1 = 0; // 先不使能中断
}
//****************************************************************
// 定时器0初始化 定时150us 用于引导码计时
//****************************************************************
void Timer0_Init(void)
{
AUXR |= 0x80; // 定时器0时钟不分频
TMOD &= 0xF0; // 配置为16位自动重装模式
TL0 = 0x6C; // 定时初值低8位
TH0 = 0xEE; // 定时初值高8位
TF0 = 0; // 清除溢出标志
TR0 = 0; // 先不启动
ET0 = 0; // 先不使能中断
}
//****************************************************************
// 【定时器0中断服务函数】引导码电平计时
//****************************************************************
void Timer0_ISR(void) interrupt 1
{
TF0 = 0; // 清除定时器0溢出标志
if(IR_IN==0&&ydordata==0)
{
ydm_low++; // 低电平计时
}
else if(IR_IN==1&&ydordata==0)
{
ydm_high++; // 高电平计时
}
}
//****************************************************************
// 【定时器1中断服务函数】数据码高电平计时
//****************************************************************
void Timer1_ISR(void) interrupt 3
{
TF1 = 0; // 清除定时器1溢出标志
if(IR_IN==1&&ydordata==1)
{
sjm_high++; // 高电平计时
}
}
//****************************************************************
// 串口1初始化 波特率9600 模式1
//****************************************************************
void Uart1_Init(void)
{
SCON = 0x50; // 串口模式1,允许接收
AUXR |= 0x01; // 串口1选择定时器2作为波特率发生器
AUXR |= 0x04; // 定时器2时钟不分频
T2L = 0xBF; // 波特率重装值低8位
T2H = 0xFF; // 波特率重装值高8位
AUXR |= 0x10; // 启动定时器2
}
//****************************************************************
// 串口发送单个字节
//****************************************************************
void UART_SendByte(u8 dat)
{
SBUF = dat;
while(!TI); // 等待发送完成
TI = 0; // 清除发送完成标志
}
//****************************************************************
// 串口发送16位整数
//****************************************************************
void UART_SendInt(u16 num)
{
u8 buf[6], i = 0;
if(num == 0) { UART_SendByte('0'); return; }
while(num) { buf[i++] = num % 10 + '0'; num /= 10; }
while(i--) UART_SendByte(buf[i]);
}
//****************************************************************
// 串口发送字符串
//****************************************************************
void UART_SendStr(u8 *s)
{
while(*s) UART_SendByte(*s++);
}
//****************************************************************
// 【主函数】系统初始化+主循环
//****************************************************************
void main(void)
{
u8 i;
u32 now_code;
WTST = 0; // 总线等待周期设为0
EAXFR = 1; // 使能扩展寄存器访问
CKCON = 0; // 时钟控制寄存器初始化
// IO口模式初始化
P0M0 = 0x20; P0M1 = 0x00;
P1M1 = 0x00; P1M0 = 0x02;
P2M1 = 0x00; P2M0 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
P6M1 = 0x00; P6M0 = 0x00;
P7M1 = 0x00; P7M0 = 0x00;
XOSCCR = 0xc0; // 启动外部晶振
while (!(XOSCCR&1)); // 等待晶振稳定
CLKDIV = 0x00; // 时钟不分频
CLKSEL = 0x01; // 选择外部晶振作为系统时钟
IP = 0x01; // 中断优先级设置
IPH = 0x01;
IT0 = 0; // 外部中断0:双边沿触发
EX0 = 1; // 使能外部中断0
Timer0_Init(); // 初始化定时器0
Timer1_Init(); // 初始化定时器1
Uart1_Init(); // 初始化串口1
EA = 1; // 使能总中断
LED = 0; // 指示灯初始灭
buzz(); // 开机蜂鸣器提示
while(1) // 系统主循环
{
unsigned int user_code = 0;
unsigned int key_code = 0;
unsigned char k;
if(Flag3 == 1) // 判断是否接收完一帧红外数据
{
EX0=0; // 关闭外部中断,防止打印时被打断
DelayMs(10);
// ===================== 解码控制 =====================
user_code = 0;
for(k=0; k<16; k++)
{
user_code <<= 1;
if(ir_data[k] > 10) user_code |= 1;
}
key_code = 0;
for(k=16; k<32; k++)
{
key_code <<= 1;
if(ir_data[k] > 10) key_code |= 1;
}
now_code = ((u32)user_code << 16) | key_code;
IR_Code_Match(now_code);
// ===================== 串口打印信息 =====================
UART_SendStr("红外遥控解码准备已就绪\r\n");
DelayMs(50);
UART_SendStr("低电平=");
UART_SendInt(code_low);
UART_SendStr(" 高电平=");
UART_SendInt(code_high);
UART_SendStr("\r\n");
DelayMs(250);
for(i=0; i<32; i++)
{
UART_SendStr("通道");
UART_SendInt(i+1);
UART_SendStr(":");
UART_SendInt(ir_data[i]);
UART_SendStr("\r\n");
DelayMs(150);
}
// 打印用户码
UART_SendStr("用户码:");
for(i=0; i<16; i++)
{
if(ir_data[i] > 18&&ir_data[i] < 23)
{
UART_SendByte('1');
}
else if(ir_data[i]>5&&ir_data[i] <8)
{
UART_SendByte('0');
}
}
UART_SendStr("\r\n");
// 打印键值码
UART_SendStr("键值码:");
for(i=16; i<32; i++)
{
if(ir_data[i] > 18&&ir_data[i] < 23)
{
UART_SendByte('1');
}
else if(ir_data[i]>5&&ir_data[i] <8)
{
UART_SendByte('0');
}
}
UART_SendStr("\r\n");
// ===================== 全部变量清零 =====================
for(i = 0; i < DATA_NUM; i++) ir_data[i] = 0;
ydmgdbzw = 0;
ydordata = 0;
ydm_low = 0;
ydm_high = 0;
ydmtime = 0;
sjm_high = 0;
code_low = 0;
code_high = 0;
irdata = 0;
capture_en = 0;
data_cnt = 0;
Flag3 = 0;
UART_SendStr("串口打印完成,等\xb4\xfd下一次中断");
IR_IN=1;
}
}
}
这个程序解码出来的数据都是对的,但送串口打印一次后,再按其它任何键没反应了,求教各位大神,坐等回复中!!!
|