找回密码
 立即注册
查看: 74|回复: 5

关于STC8H-ADC按键遥控器程序的提问

[复制链接]
  • 打卡等级:以坛为家III
  • 打卡总天数:661
  • 最近打卡:2025-12-16 09:08:52

22

主题

133

回帖

3050

积分

论坛元老

积分
3050
发表于 2025-12-5 17:40:51 | 显示全部楼层 |阅读模式
用stc8H单片机ADC按键做遥控器程序,用PWM方式操作正常。改成用定时器0发送载波,定时器1空闲延时的方式,不能正常的进行键码的发送(接收的单片机不识别键码)。硬件电路无问题,ADC遥控按键部分也不问题,现请教键码发送这部分程序,主要分析IR_SendFrame(void)子程序是否有问题,望做过类似项目的老师指点迷津,感谢。先贴出遥控发射原理图和发送键码的程序如下:



#define MAIN_Fosc        24000000UL        //主频
#define Baudrate  115200L        //波特率
#define TM        (65536 -(MAIN_Fosc/Baudrate/4))        //自适应定时器初值



//引脚定义
sbit IR_TX=P3^3;        //红外发射引脚

//NEC协议参数
#define CARRIER_FREQ 38000UL        //38kHz载波
#define Timer0_Reload        MAIN_Fosc/(2*CARRIER_FREQ)        //Timer 0中断频率,38kHz载波1/2占空比
#define Timer1_Reload        MAIN_Fosc/1000000        //Timer 1中断频率1MHZ,定时1/1000000s=1us
#define REPEAT_INTERVAL 108000UL        //重发间隔108ms(NEC标准)
#define User_code        0x0F00        //定义红外用户码

extern u16 REPEAT_CNT;        //重发间隔计数器



u8 code KEYNUM[17]=        //红外发射码: KEYNUM[0]为按键松开时的值,无效,红外发射码从KEYNUM[1]开始算
{0xFF,0x50,0x66,0x24,0x5b,0x55,0x5c,0x23,0x59,0x58,0x54,0x61,0x60,0x71,0x72,0x73,0x74};       

u16 DELAY_CNT=0;        //延时计数器(定时器1用)
bit IR_SENDING=0;        //发送忙标志
u16 REPEAT_CNT=0;        //重发间隔计数器

void Timer0_Init(void)        //定时器0初始化:生成38kHz载波(16位自动重载中断模式)
{
        AUXR|=0x80;        //定时器0时钟1T模式
  TMOD&=0xF0;        //设置定时器0为16位自动重载
  TH0=(u8)((65536UL-Timer0_Reload)>>8);        //定时初值高位
  TL0=(u8)(65536UL-Timer0_Reload);        //定时初值低位
  ET0=1;        //使能中断
  TR0=0;        //初始关闭载波
}

void Timer1_Init(void)        //定时器1初始化:1us精度延时/计数(16位自动重载中断模式)
{
        AUXR|=0x40;        //定时器1时钟1T模式
        TMOD&=0x0F;        //设置定时器1为16位自动重载
        TH1=(u8)((65536UL-Timer1_Reload)>>8);        //定时初值高位
  TL1=(u8)(65536UL-Timer1_Reload);        //定时初值低位
  ET1=1;        //使能中断
  TR1=0;        //先关闭定时器1,需要延时时再开启
}

void NEC_Init(void)        //NEC遥控协议初始化
{
        P3M0 &= ~0x08;
        P3M1 &= ~0x08;        //P3.3配置为准双向口
//                P3M0 |= 0x08;
//        P3M1 &= ~0x08;        //P3.3配置为推挽输出
        IR_TX=0;        //三极管截止,关闭遥控发射,此时遥控接收头是高电平
        Timer0_Init();        //定时器0初始化
        Timer1_Init();        //定时器1初始化
}

void Delay_US(u16 us)        //延时us
{
        DELAY_CNT=us;        //延时时间写入
        TR1=1;        //交给定时器1处理延时
  while(DELAY_CNT>0);        //等待延时结束
        TR1=0;        //关闭定时器1
}

void IR_TxByte(u8 dat)        //发送一个字节数据
{
        u8 i;

  for(i=0;i<8;i++)
  {
                IR_TX=1;        //发射管打开
                TR0=1;        //开启载波
                Delay_US(560);        //先发送562us的载波
                TR0=0;        //关闭载波
               
                IR_TX=0;        //三极管截止,关闭遥控发射,此时遥控接收头是高电平
                if(dat&0x01)Delay_US(1680);        //1码:560us的载波+1687us的空闲(高电平)
                else Delay_US(560);        //0码:560us的载波+560us的空闲(高电平)
    dat>>=1;        //数据的下一位
  }
}

void IR_SendFrame(void)        //发送完整NEC帧(用户码高低位+数据码正码+数据码反码)
{
//  if(IR_SENDING) return;
//       
//        IR_SENDING=1;
        IR_TX=1;        //发射管打开
        TR0=1;        //开启载波
        Delay_US(9000);        //发送9ms载波
        TR0=0;        //关闭载波

        IR_TX=0;        //三极管截止,关闭遥控发射,此时遥控接收头是高电平
        Delay_US(4500);        //停顿大约2ms--4.5ms空闲(高电平),让单片机有足够的响应时间
       
        IR_TxByte((u8)(User_code%256));        //发用户码低字节
        IR_TxByte((u8)(User_code/256)); //发用户码高字节
        IR_TxByte(KEYNUM[KeyCode]);        //发数据码正码
        IR_TxByte(~KEYNUM[KeyCode]);        //发数据码反码

        IR_TX=1;        //发射管打开
        TR0=1;        //开启载波
        Delay_US(560);        //发送0.56ms载波作为结束位
        TR0=0;        //关闭载波
       
        IR_TX=0;        //三极管截止,关闭遥控发射,此时遥控接收头是高电平
        Delay_US(31000);        //发送31ms作为108ms总延时的补偿
       
//        IR_SENDING=0;
}

void Timer0_ISR(void) interrupt 1        //定时器0中断:生成载波(引脚翻转)
{
  IR_TX=~IR_TX;        //引脚翻转
}

void Timer1_ISR(void) interrupt 3        //定时器1中断:计数器递减
{
        if(DELAY_CNT>0) DELAY_CNT--;
  if(REPEAT_CNT>0) REPEAT_CNT--;       
}




void ADC_KeyRead_Allot(void)        //ADC按键读取数据分配
{
        static xdata u16 count=0;
       
        count++;
        if(count>2000)        //配合ADC_KeyHoldCnt变量来实现消抖,检测太频繁会死机
        {
                count=0;
                ADC_Read(2);        //读取ADC转换值
                CalculateAdcKey(adc_val);        //ADC按键值读取:输入的是AD转换值,返回按键值KeyCode:1-16
        }               
}

void RemoteCon_Allot(void)        //遥控数据分配
{
        if(KeyCode!=0)
        {               
                IR_SendFrame();
                REPEAT_CNT = REPEAT_INTERVAL;  // 初始化重发间隔
                printf("KEY=%u\r\n",(u16)KeyCode); //打印按键值               
                KeyCode=0;
        }
}



void main(void)        //主函数
{
        P_SW2 |= 0x80; //扩展寄存器(XFR)访问使能
        ADC_Init();        //ADC初始化
        NEC_Init();        //NEC遥控协议初始化
        Uart1_Init(); //串口1初始化函数,可以使用printf
  EA = 1;        //打开总中断
       
  while(1)
        {
                ADC_KeyRead_Allot();        //ADC按键读取数据分配
                RemoteCon_Allot();        //遥控数据分配               
        }
}

屏幕截图 2025-12-05 172904.png
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:641
  • 最近打卡:2025-12-17 10:15:23
已绑定手机
已实名认证

123

主题

3278

回帖

8316

积分

版主

积分
8316
发表于 2025-12-5 23:34:23 | 显示全部楼层
可以用示波器看一看波形,对比使用pwm和定时器的波形
你这个里面有很大问题的就是us延时函数
如果不执行其他任务,那就直接用软件us延时
如果想用定时器实现,那就不要使用1us进一次中断的方式,这个进中断速度太快了,很容易导致芯片反应不过来,因为进中断需要保存现场,从中断返回也需要时间。
你可以将需要定时的时间直接写入定时器重载寄存器,然后等待溢出中断就是定时完成。尽量不要使用这种每个短时间片进一次中断的方式,这种方式推荐1ms时基进一次比较合适
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:661
  • 最近打卡:2025-12-16 09:08:52

22

主题

133

回帖

3050

积分

论坛元老

积分
3050
发表于 2025-12-6 10:46:17 来自手机 | 显示全部楼层
王昱顺 发表于 2025-12-5 23:34
可以用示波器看一看波形,对比使用pwm和定时器的波形
你这个里面有很大问题的就是us延时函数
如果不执行其 ...

和这个1 us中断关系不大,我用延时函数效果一样的,可能是38k的载波占空比有关,我设置的1/2,一般的是1/3占空比,到时我再试试
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:253
  • 最近打卡:2025-12-17 11:28:25

84

主题

7156

回帖

1万

积分

超级版主

积分
15260
发表于 2025-12-6 11:27:06 | 显示全部楼层
用示波器看一下发送波形即可知道原因,要么载波不对,要么调制数据的时序不对。
使用PWM+定时器 处理38K红外发送基本是最优方案,强烈推荐这种组合。
红外发射载波一般使用1/3占空比为最优。
条件:主频24MHz,红外载波38KHz,NEC码,PWM1,Timer0。
初始化PWM:PWM时钟分频 = 0, 周期 = 632-1,  占空比=210,则PWM频率=24000/632=37.975KHz,占空比为1/3。
初始化Timer0: 数据0为 发送0.5625ms,暂停0.5625ms,
                      数据1为 发送0.5625ms,暂停1.6875ms,
则Timer0中断周期为0.5625ms,重装值=65536-13500,1T模式。
在此中断率下,处理数据调制。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:661
  • 最近打卡:2025-12-16 09:08:52

22

主题

133

回帖

3050

积分

论坛元老

积分
3050
发表于 2025-12-6 14:43:16 来自手机 | 显示全部楼层
梁工 发表于 2025-12-6 11:27
用示波器看一下发送波形即可知道原因,要么载波不对,要么调制数据的时序不对。
使用PWM+定时器 处理38K红 ...

谢谢梁工解答,pwm+定时器的方式已实现,目前调试定时器0+定时器1的方式,载波的占空比我设置为1/2的,回头我再试试1/3的,感谢梁工
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:661
  • 最近打卡:2025-12-16 09:08:52

22

主题

133

回帖

3050

积分

论坛元老

积分
3050
发表于 2025-12-8 09:48:06 | 显示全部楼层
感谢两位老师赐教,问题已解决:
38KHZ的载波占空比改为1/3;
1us中断时间太短,改为560us中断来计时。
再次感谢老师
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-12-18 10:25 , Processed in 0.107793 second(s), 68 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表