HyunYong_7782 发表于 2025-10-28 19:16:55

检测下降沿发送脉冲

// 方案005
/*
上电后 进入中转待机模式 发送一次脉冲 启动外部中断下降沿检测 启动5分钟倒计时

当外部中断检测到下降沿以后 改为启动工作模式 查询反馈信号是否是低电平 是否被锁定 查询上一次脉冲是否完成
上一次脉冲未完成则直接停机

当低电平 没锁定 且上一次脉冲完成 则发送一次脉冲 然后检测脉冲是否完成
脉冲完成则然后进入中转待机模式

同时 5分钟计时到以后 启动ADC检测硬件电压 当低于0.1V时 默认无设备在充电 停机锁定
反之不干涉
*/
// 头文件
#include "STC8Hxxx.h"


// 引脚定义
sbit Pulse = P3^4;                      // PWM脉冲输出引脚
sbit OUT_INT0 = P3^2;                   // 外部中断0输入引脚
sbit CMP   = P3^7;                      // 比较器正输入端
sbit ADC13 = P3^5;                      // ADC13输入引脚


// 宏定义
#define MAIN_Fosc 35000000L             // 主频35MHz


// 声明
// 数组
// 函数
void Ext_Init(void);
void ADC_INIT(void);
u16 GET_ADC_DATA(void);
void Timer1_Init(void);
void PWMB_config(void);
// 变量
bit trigger_lock = 1;                   // 锁存标志:0=允许触发,1=已触发等待解锁
bit cmp_now = 1;                        // 0表示 正输入 小于 负输入1表示 正输入 大于 负输入
bit FiveMinuteFlag = 0;               // 5分钟标志位

u8 low_cnt = 0;                         // 连续低电平计数
u8 mode = 1;                            // 0 锁定停机1 中转待机2 工作模式
u8 count_20ms = 0;                     
u8 count_200ms = 0;
u8 count_2000ms = 0;

u16 ADC_value = 0;                      // ADC转换值
u16 voltage = 0;                        // 实际电压值


// 函数模块
// 描述: PWM配置函数
void PWMB_config(void)
{
        P_SW2 |= 0x80;                        // SFR enable

        PWMB_PSCR   = 0;                // 预分频寄存器, 分频 Fck_cnt = Fck_psc/(PSCR+1), 边沿对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1))
                              // 中央对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)*2).
        PWMB_DTR    = 0;                // 死区时间配置, n=0~127: DTR= n T,   0x80 ~(0x80+n), n=0~63: DTR=(64+n)*2T
                                                        //                                0xc0 ~(0xc0+n), n=0~31: DTR=(32+n)*8T,   0xE0 ~(0xE0+n), n=0~31: DTR=(32+n)*16T
        PWMB_ARR    = 205;                        // 自动重装载寄存器,控制PWM周期
        PWMB_CCER1= 0;
        PWMB_CCER2= 0;
        PWMB_SR1    = 0;
        PWMB_SR2    = 0;
        PWMB_ENO    = 0;
        PWMB_PS   = 0;
        PWMB_IER    = 0;
        PWMB_CR1    = 0;

        PWMB_CCR4   = 103;                // 计数器比较值, 匹配时刻. 输出单脉冲延时1750us, 宽度=2000-1750=250us.
        // 通道1模式配置: 0: 无输出, 1:匹配时输出高, 2:匹配时输出低, 3:匹配时输出翻转, 4:固定输出低, 5:固定输出高, 6:PWM模式1(正脉冲), 7:PWM模式2(负脉冲).
    // +8:允许CCRn预装载, +0:禁止预装载. +4:允许输出比较快速使能, +0:禁止. +0:输出, +1:输入映射在TI1FP1,+3:输入映射在TI2FP1,+3:输入映射在TRC.
    PWMB_CCMR4= (7<<4)+8;       
        PWMB_CCER2 |= 0x50;                        // 开启比较输出, 高电平有效
        PWMB_PS    |= (1<<6);                // 选择IO, 0:选择P2.3, 1:选择P3.4, 2:选择P0.3, 3:选择P7.7
        PWMB_ENO   |= 0x40;                        // IO输出允许,bit6: ENO8P, bit4: ENO7P,bit2: ENO6P,bit0: ENO5P

        PWMB_BKR    = 0x80;                        // 主输出使能 相当于总开关
        PWMB_CR1   |= 0x08;                        // 单脉冲模式
}

// 外部中断0初始化 (下降沿触发)
void Ext_Init(void)
{
    IT0 = 1;                            // 下降沿触发
    EX0 = 1;                            // 允许INT0中断
}

// ADC初始化函数
void ADC_INIT(void)
{
    ADC_CONTR = 0x8D;                   // 使能ADC电源,选择P3.5(ADC13)
    ADCCFG &= ~0x0F;                  // 清速度设置位
    ADCCFG |= 0x02;                     // 50KSPS采样速率
    ADCTIM = 0x35;                      // 采样保持参数设置
    ADC_CONTR &= 0xDF;                  // 清转换完成标志
    EADC = 0;                           // 禁止ADC中断
    ADCCFG |= 0x20;                     // 左对齐(10位有效)
    ADC_CONTR |= 0x40;                  // 启动第一次ADC转换
}

// 获取ADC转换值函数
u16 GET_ADC_DATA(void)
{
    u16 AD_DATA = 0;
    ADC_CONTR &= 0xDF;                  // 清完成标志
    ADC_CONTR &= 0xBF;                  // 停止ADC转换
    AD_DATA = ADC_RES;                  // 读取高8位
    AD_DATA <<= 8;
    AD_DATA |= ADC_RESL;                // 读取低2位
    ADC_CONTR |= 0x40;                  // 启动下一次转换
    return AD_DATA;
}

// TIM1初始化
void Timer1_Init(void)                              // 20毫秒@35.000MHz
{
        AUXR &= 0xBF;                                    // 定时器时钟12T模式
        TMOD &= 0x0F;                                    // 设置定时器模式
        TL1 = 0x23;                                            // 设置定时初始值
        TH1 = 0x1C;                                            // 设置定时初始值
        TF1 = 0;                                            // 清除TF1标志
        TR1 = 1;                                            // 定时器1开始计时
        ET1 = 1;                                            // 使能定时器1中断
}


// 主函数
void main(void)
{
    P_SW2 |= 0x80;                        // 允许XFR访问

    // I/O 初始化
    P3M0 = (P3M0 & ~0xa4) | 0x10; P3M1 = (P3M1 & ~0x10) | 0xa4;         // P3.4 推挽输出 P3.5 P3.6 P3.7 高阻输入

    Pulse = 0;

    EA = 1;                                 // 全局中断开

    // 模块初始化
    Ext_Init();
    ADC_INIT();
    Timer1_Init();
    PWMB_config();

    PWMB_CR1 |= 0x01;                            // 触发一次单脉冲输出

    while (1)
    {
      // ========== mode ========== //
      if(mode == 0)                     // 锁定停机模式
      {
            TR0 = 0;                        // 停止定时器0
            ET0 = 0;                        // 禁止定时器0中断
      
            TR1 = 0;                        // 停止定时器1
            ET1 = 0;                        // 禁止定时器1中断
      
            EX0 = 0;                        // 禁止外部中断0
            CMPCR1 &= ~0x80;                // 关闭比较器模块
            ADC_CONTR &= ~0x80;             // 关闭ADC电源
      
            EA = 0;                         // 关闭全局中断,系统完全锁定
      }

      if(mode == 2)                     // 工作模式
      {
            //if(CMP == 0)                  // 反馈信号低电平
            //{
            //    cmp_now = 0;
            //}

            if (/*cmp_now == 0 && */ !trigger_lock)          // 未锁定
            {
                // 输出一个脉冲信号
                PWMB_CR1 |= 0x01;          // 触发一次单脉冲输出

                trigger_lock = 1;
                cmp_now = 1;
                mode = 1;
            }
      }
      // ========== mode ========== //


      // ========== ADC-test ========== //
      if(FiveMinuteFlag == 1)
      {
            ADC_value = GET_ADC_DATA();
            voltage = (((float)ADC_value / 1023) * 5000);         // 10位ADC 参考电压5V

            if(voltage < 100)               // 低于0.1V
            {
                // 关闭设备
                mode = 0;                   // 锁定停机模式
            }
      }
      // ========== ADC-test ========== //
    }
}


// 中断服务函数
// 外部中断0服务函数
void INT0_Isr(void) interrupt 0
{
    trigger_lock = 0;
    mode = 2;                                           // 工作模式
}

// 定时器1中断函数:用于5分钟检测
void Timer1_Isr(void) interrupt 3
{
    count_20ms++;                                       // 每次中断 20ms

    if(count_20ms >= 10)                              // 200ms
    {
      count_20ms = 0;
      count_200ms++;

      if(count_200ms >= 10)                           // 2000ms = 2s
      {
            count_200ms = 0;
            count_2000ms++;

            if(count_2000ms >= 150)                     // 150 × 2s = 300s = 5分钟
            {
                count_2000ms = 0;                     // 重新计数

                TR1 = 0;                              // 停止定时器1
                ET1 = 0;                              // 禁止定时器1中断

                FiveMinuteFlag = 1;                     // 设置标志位,主循环检测
            }
      }
    }
}这个程序

用的是AI8H1K08 SOP16芯片
现在 的问题是大部分的检测已经去掉了 只剩下了 一个为了保证没次只能发送一次脉冲的一个bit锁和一个下降沿检测
结果还是出现了第一个下降沿检测到以后有一段类似要出脉冲的抖动 然后第二个下降沿来的时候发送一次脉冲了 然后又缺失一个下降沿 再下一个下降沿发出脉冲
而不是每个下降沿都会发送一次脉冲

神农鼎 发表于 2025-10-28 19:43:17





/*************功能说明    **************
本例程基于STC8H8K64U为主控芯片的实验箱9进行编写测试,STC8H系列芯片可通用参考.
高级PWM定时器实现高速PWM脉冲输出.
周期/占空比可调, 通过比较/捕获中断进行脉冲个数计数.
通过P6口演示输出,每隔10ms输出一次PWM,计数10个脉冲后停止输出.
使用单脉冲模式配合重复计数寄存器,纯硬件控制脉冲个数.
定时器每1ms调整PWM周期.
下载时, 选择时钟 24MHZ (用户可自行修改频率).
******************************************/

#include    "stc8h.h"
#include    "intrins.h"

#define   MAIN_Fosc       24000000L         //定义主时钟

typedef   unsigned char   u8;
typedef   unsigned int    u16;
typedef   unsigned long   u32;

/****************************** 用户定义宏 ***********************************/

#define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒


/*****************************************************************************/

#define PWM1_1          0x00                  //P:P1.0N:P1.1
#define PWM1_2          0x01                  //P:P2.0N:P2.1
#define PWM1_3          0x02                  //P:P6.0N:P6.1

#define PWM2_1          0x00                  //P:P1.2/P5.4N:P1.3
#define PWM2_2          0x04                  //P:P2.2N:P2.3
#define PWM2_3          0x08                  //P:P6.2N:P6.3

#define PWM3_1          0x00                  //P:P1.4N:P1.5
#define PWM3_2          0x10                  //P:P2.4N:P2.5
#define PWM3_3          0x20                  //P:P6.4N:P6.5

#define PWM4_1          0x00                  //P:P1.6N:P1.7
#define PWM4_2          0x40                  //P:P2.6N:P2.7
#define PWM4_3          0x80                  //P:P6.6N:P6.7
#define PWM4_4          0xC0                  //P:P3.4N:P3.3

#define ENO1P         0x01
#define ENO1N         0x02
#define ENO2P         0x04
#define ENO2N         0x08
#define ENO3P         0x10
#define ENO3N         0x20
#define ENO4P         0x40
#define ENO4N         0x80

/*************本地变量声明    **************/
bit B_1ms;                                    //1ms标志
bit PWM1_Flag;

u16 Period;
u8 Counter;
u8 msSecond;

void UpdatePwm(void);
void TxPulse(u8 rep);

/******************** 主函数 **************************/
void main(void)
{
    P_SW2 |= 0x80;                              //扩展寄存器(XFR)访问使能

    P0M1 = 0x00;   P0M0 = 0x00;               //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;               //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;               //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;               //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;               //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;               //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;               //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;               //设置为准双向口

    PWM1_Flag = 0;
    Counter = 0;
    Period = 0x1000;

    //Timer0初始化
    AUXR = 0x80;                              //Timer0 set as 1T, 16 bits timer auto-reload,
    TH0 = (u8)(Timer0_Reload / 256);
    TL0 = (u8)(Timer0_Reload % 256);
    ET0 = 1;                                    //Timer0 interrupt enable
    TR0 = 1;                                    //Tiner0 run

    PWMA_ENO = 0x00;
    PWMA_ENO |= ENO1P;                        //使能输出

    PWMA_CCER1 = 0x00;                        //写 CCMRx 前必须先清零 CCxE 关闭通道
    PWMA_CCMR1 = 0x68;                        //设置 PWM1 模式1 输出
//    PWMA_CCER1 = 0x01;                        //使能 CC1E 通道, 高电平有效
    PWMA_CCER1 = 0x03;                        //使能 CC1E 通道, 低电平有效

    PWMA_PS = 0x00;                           //高级 PWM 通道输出脚选择位
    PWMA_PS |= PWM1_3;                        //选择 PWM1_3 通道

    UpdatePwm();
    PWMA_BKR = 0x80;                            //使能主输出
//    PWMA_CR1 |= 0x89;                         //使能ARR预装载,单脉冲模式,开始计时

    P40 = 0;                                    //给LED供电
    EA = 1;                                     //打开总中断

    while (1)
    {
      if(B_1ms)
      {
            B_1ms = 0;
            msSecond++;
            if(msSecond >= 10)                  //10ms启动一次PWM输出
            {
                msSecond = 0;
                TxPulse(10);                  //输出10个脉冲
            }
      }
    }
}

/************* 发送脉冲函数 **************/
void TxPulse(u8 rep)
{
    if(rep == 0) return;
    rep -= 1;

    PWMA_RCR = rep;                           //重复计数寄存器,计数 rep 个脉冲后产生更新事件
    PWMA_CR1 |= 0x89;                           //使能ARR预装载,单脉冲模式,开始计时
}

/********************** Timer0 1ms中断函数 ************************/
void timer0(void) interrupt 1
{
    B_1ms = 1;
    if(PWM1_Flag)
    {
      Period++;                               //周期递增
      if(Period >= 0x1000) PWM1_Flag = 0;
    }
    else
    {
      Period--;                               //周期递减
      if(Period <= 0x0100) PWM1_Flag = 1;
    }
    UpdatePwm();                              //设置周期、占空比
}

//========================================================================
// 函数: UpdatePwm(void)
// 描述: 更新PWM周期占空比.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void UpdatePwm(void)
{
    PWMA_ARR = Period;
    PWMA_CCR1 = (Period >> 1);                  //设置占空比时间: Period/2
}




HyunYong_7782 发表于 2025-10-30 10:47:29

神农鼎 发表于 2025-10-28 19:43
/*************功能说明    **************
本例程基于STC8H8K64U为主控芯片的实验箱9进行编写测试, ...

这个只是脉冲吧

HyunYong_7782 发表于 2025-10-30 10:50:01

神农鼎 发表于 2025-10-28 19:43
/*************功能说明    **************
本例程基于STC8H8K64U为主控芯片的实验箱9进行编写测试, ...

现在是下降沿只能两个检测一个
是不是第一个下降沿的抖动 其实是下降沿检测到了 但是脉冲还没发送完来了第二个下降沿导致重置了

晓飛飛 发表于 2025-10-30 22:44:09

如果脉冲周期不是很短的话,可以结合外部中断和定时器做跟踪状态机,利用定时器来检测脉冲之间的周期,并根据预期的条件触发相应的模式。
页: [1]
查看完整版本: 检测下降沿发送脉冲