[技术支持] HC18M121B1单片机编程问题

[复制链接]
2039|17
poxiao0822 发表于 2025-10-21 22:28 | 显示全部楼层 |阅读模式
C语言编程,在主程序下方写了两个中断程序,INT0和Term1,用IDE软件编译报错,说是在同一优先级,请问什么原因,怎么解决 9278568f79877e33df.png
sunjd 发表于 2025-10-22 11:03 | 显示全部楼层
HC18M121B1单片机只有一个中断向量地址(0008H),这意味着所有中断都共享同一个入口点,不能像某些单片机那样为每个中断单独编写中断服务函数,从数据手册第11页可以看到:
复位向量是0000H
中断向量是0008H(只有一个!)
这意味着所有中断(INT0、INT1、Timer0、Timer1、Timer2、ADC等)都共用同一个中断入口地址。
你需要将两个中断服务程序合并为一个统一的中断服务函数,然后在其中判断具体是哪个中断源触发的。
为了更好地管理多个中断,建议采用以下结构
void interrupt ISR(void)
{
    // 按优先级检查各个中断标志
    if(INT1F) {
        INT1F = 0;
        INT1_Handler();
    }
    else if(T1IF) {
        T1IF = 0;
        Timer1_Handler();
    }
    else if(INT0F) {
        INT0F = 0;
        INT0_Handler();
    }
    // ... 其他中断
}

// 各个中断的具体处理函数
void INT1_Handler(void)
{
    intpwmcount++;
    // INT1具体处理代码
}

void Timer1_Handler(void)
{
    count++;
    // Timer1具体处理代码
}

评论

解释的很到位。  发表于 2025-10-22 13:25
 楼主| poxiao0822 发表于 2025-10-22 22:46 | 显示全部楼层
感谢回复,问题已解决
再请教下,我在做PWM信号检测,方法是外部中断设置边沿触发,通过计数到一定数量,判断有PWM信号输入,但是检测的PWM信号周期为1us,占空比可能很大,也可能很小,致使IO口无法检测到边沿触发,这个单片机FCU为4MHz,想问下需要怎么解决,或者有没更好的PWM检测方法
sunjd 发表于 2025-10-23 17:31 | 显示全部楼层
试试定时器输入捕捉功能
根据数据手册看,Timer1和Timer2都有外部时钟输入功能
// 使用Timer1作为PWM周期测量
void PWM_Detection_Init(void)
{
    // 配置Timer1为外部计数器模式
    T1CON = 0b00011000;  // T1CK[1:0]=10, T1CKI上升沿计数
    T1EN = 1;            // 使能Timer1
   
    // 配置Timer0作为时间基准
    OPTION = 0b00000111; // 预分频给Timer0,1:256
    T0IE = 1;            // 使能Timer0中断
}

// Timer0中断用于定期读取Timer1计数值
void interrupt ISR(void)
{
    if(T0IF)
    {
        T0IF = 0;
        static unsigned int last_count = 0;
        unsigned int current_count = T1;
        
        // 计算频率 = (current_count - last_count) / 定时时间
        unsigned int pulse_count = current_count - last_count;
        last_count = current_count;
        
        // 根据脉冲计数计算PWM频率
        if(pulse_count > 0) {
            // 检测到有效PWM信号
        }
    }
}
 楼主| poxiao0822 发表于 2025-10-25 13:40 | 显示全部楼层
是否可以用T0做外部计数器模式,看数据手册,这个计数器不需要EN吗
sunjd 发表于 2025-10-27 09:12 | 显示全部楼层
HC18M121B1 的 Timer0 可以用作外部计数器,并且它不需要一个独立的“ENABLE”位。通过将 OPTION寄存器的 T0CS 位设置为 1(选择外部 T0CKI 引脚作为时钟源),就自动启动并“使能”了计数器功能。
 楼主| poxiao0822 发表于 2025-10-27 21:52 | 显示全部楼层
已使用T0做好了PWM边沿计数,我现在要检测上图周期间断的PWM波形,请教如何编程将这种波形识别出来
1.png
 楼主| poxiao0822 发表于 2025-10-27 21:59 | 显示全部楼层
请教大佬分享该单片机C代码
sunjd 发表于 2025-10-28 09:26 | 显示全部楼层
试试这个统计分析法,根据实际的PWM参数调整相关阈值和时间参数。
超时阈值:根据正常PWM周期的2-3倍设置
2. 滤波参数:适当增加确认次数避免误判
3. 缓冲区大小:根据检测灵敏度要求调整
4. 统计窗口:通常8-16个周期比较合适
统计分析方法
typedef struct {
    uint32_t period_buffer[10];  // 周期缓冲区
    uint8_t buffer_index;
    uint32_t avg_period;         // 平均周期
    uint32_t max_period;         // 最大周期
    uint8_t is_interrupted;      // 间断标志
} pwm_analyzer_t;

void analyze_pwm_pattern(pwm_analyzer_t *analyzer, uint32_t new_period)
{
    // 更新缓冲区
    analyzer->period_buffer[analyzer->buffer_index] = new_period;
    analyzer->buffer_index = (analyzer->buffer_index + 1) % 10;
   
    // 计算统计信息
    uint32_t sum = 0;
    analyzer->max_period = 0;
   
    for(int i = 0; i < 10; i++) {
        sum += analyzer->period_buffer[i];
        if(analyzer->period_buffer[i] > analyzer->max_period) {
            analyzer->max_period = analyzer->period_buffer[i];
        }
    }
    analyzer->avg_period = sum / 10;
   
    // 检测间断
    if(analyzer->max_period > (analyzer->avg_period * 3)) {
        if(!analyzer->is_interrupted) {
            analyzer->is_interrupted = 1;
            printf("PWM间断检测到!\n");
        }
    } else {
        if(analyzer->is_interrupted) {
            analyzer->is_interrupted = 0;
            printf("PWM恢复正常!\n");
        }
    }
}
 楼主| poxiao0822 发表于 2025-10-28 22:05 | 显示全部楼层
请问还有其他较为简单的实现方法和代码吗,我处于入门水平。
sunjd 发表于 2025-10-30 08:51 | 显示全部楼层
本帖最后由 sunjd 于 2025-10-30 08:57 编辑

那试试这个Timer0计数 + Timer1定时采样

; 使用Timer1产生固定采样周期(如10ms)
INIT_TIMER1_SAMPLING:
    ; 配置Timer1为定时器模式,10ms中断
    MOVLW   B'00000000'      ; 预分频1:2, Timer1振荡器关闭
    MOVWF   T1CON
   
    ; 设置Timer1重载值(根据系统时钟计算)
    MOVLW   0xXX             ; 高字节
    MOVWF   TMR1H
    MOVLW   0xXX             ; 低字节  
    MOVWF   TMR1L
   
    ; 使能Timer1中断
    BSF     T1CR_AUXR, T1IE
    BSF     INTCON, GIE
    RETURN

; Timer1中断服务程序
T1_INTERRUPT_SERVICE:
    ; 保护现场
    MOVWF   W_TEMP
    SWAPF   STATUS, W
    MOVWF   STATUS_TEMP
   
    ; 调用PWM检测函数
    CALL    CHECK_PWM_SIGNAL
   
    ; 清除Timer1中断标志
    BCF     T1CR_AUXR, T1IF
   
    ; 重载Timer1(如果需要)
    MOVLW   0xXX
    MOVWF   TMR1H
    MOVLW   0xXX  
    MOVWF   TMR1L
   
    ; 恢复现场
    SWAPF   STATUS_TEMP, W
    MOVWF   STATUS
    SWAPF   W_TEMP, F
    SWAPF   W_TEMP, W
    RETFIE

sunjd 发表于 2025-10-30 08:59 | 显示全部楼层
防抖动时间:NO_SIGNAL_CNT的阈值(建议3-5次)
采样周期:Timer1中断周期(10-50ms为宜)
PWM存在阈值:最小边沿计数阈值
测量时间:频率测量时的窗口时间
 楼主| poxiao0822 发表于 2025-10-30 22:58 | 显示全部楼层
这个是汇编语言吗,C语言怎么编程
 楼主| poxiao0822 发表于 2025-11-3 22:24 | 显示全部楼层
再请教个问题,这款芯片有一个电平变化检测中断功能,我发现0--->5V变化,如果速度很慢的话,单片机会认为发生了好多次中断,这个是为什么,难道电平变化检测的是短时间的模拟电压变化吗
 楼主| poxiao0822 发表于 2025-11-3 22:30 | 显示全部楼层
如下是我写的初始化函数和中断函数
void EXTLC_Init()
{
        IOCB0=1;
        PBIF=0;
        PBIE=1;
        GIE=1;
}
void interrupt ISR(void)
         if(PBIF)                                //等待溢出
        {
                PBIF=0;                        //清除中断标志
                PBIE=1;               
                 intpwmcount++;
                              
         }
sunjd 发表于 2025-11-4 12:10 | 显示全部楼层
poxiao0822 发表于 2025-11-3 22:30
如下是我写的初始化函数和中断函数
void EXTLC_Init()
{

改进的中断服务程序
unsigned int intpwmcount = 0;
unsigned char last_pb_state = 0;
unsigned char debounce_counter = 0;

void interrupt ISR(void)
{
    if(PBIF)
    {
        PBIF = 0;           // 清除中断标志
        
        // 读取当前PORTB0的实际状态
        unsigned char current_state = (PORTB & 0x01);
        
        // 简单的延时去抖动 - 等待信号稳定
        __delay_ms(1);      // 需要包含相应的延时函数
        
        // 再次读取确认
        unsigned char confirmed_state = (PORTB & 0x01);
        
        // 只有状态确实发生变化才计数
        if(current_state == confirmed_state && current_state != last_pb_state)
        {
            intpwmcount++;
            last_pb_state = current_state;
        }
        
        PBIE = 1;           // 重新使能中断
    }
}

或是改用定时器采样法
unsigned int intpwmcount = 0;
unsigned char last_sampled_state = 0;

// 使用Timer0或Timer1定期采样,而不是用电平变化中断
void TIMER1_Init(void)
{
    // 配置Timer1每1ms中断一次
    // ... Timer1初始化代码
}

void interrupt ISR(void)
{
    // Timer1中断 - 定期采样
    if(T1IF)  
    {
        T1IF = 0;           // 清除Timer1中断标志
        
        // 采样PORTB0状态
        unsigned char current_state = (PORTB & 0x01);
        
        // 检测上升沿
        if(current_state == 1 && last_sampled_state == 0)
        {
            intpwmcount++;
        }
        
        last_sampled_state = current_state;
        
        // 重载Timer1...
    }
   
    // 保留原来的电平变化中断处理,但添加更强的去抖动
    if(PBIF)
    {
        PBIF = 0;
        // 可以在这里做简单处理,或者完全禁用这个中断
    }
}
sunjd 发表于 2025-11-4 13:20 | 显示全部楼层
poxiao0822 发表于 2025-10-30 22:58
这个是汇编语言吗,C语言怎么编程

#include <xc.h>

// 根据系统时钟计算10ms定时器的重载值
// 假设系统时钟为Fosc,预分频1:2,则定时器时钟 = Fosc/4/2
// 重载值 = 65536 - (10ms / (2 * 4 / Fosc))
#define TIMER1_RELOAD_HIGH  0xXX    // 高字节重载值
#define TIMER1_RELOAD_LOW   0xXX    // 低字节重载值

// 现场保护变量(编译器可能会自动处理,这里显式声明)
volatile unsigned char w_temp;
volatile unsigned char status_temp;

/**
* @brief 初始化Timer1用于10ms采样周期
*/
void init_timer1_sampling(void)
{
    // 配置Timer1为定时器模式,预分频1:2,关闭Timer1振荡器
    T1CON = 0x00;
   
    // 设置Timer1重载值
    TMR1H = TIMER1_RELOAD_HIGH;
    TMR1L = TIMER1_RELOAD_LOW;
   
    // 使能Timer1中断
    T1CR_AUXRbits.T1IE = 1;  // 使能Timer1中断
    INTCONbits.GIE = 1;      // 使能全局中断
   
    // 启动Timer1
    T1CONbits.TMR1ON = 1;
}

/**
* @brief Timer1中断服务程序
* @NOTE 在PIC编译器中,中断服务程序通常使用特定修饰符
*/
void __interrupt() timer1_interrupt_service(void)
{
    // 检查是否是Timer1中断
    if (T1CR_AUXRbits.T1IF)
    {
        // 保护现场(编译器通常会自动处理)
        
        // 调用PWM检测函数
        check_pwm_signal();
        
        // 清除Timer1中断标志
        T1CR_AUXRbits.T1IF = 0;
        
        // 重载Timer1计数值
        TMR1H = TIMER1_RELOAD_HIGH;
        TMR1L = TIMER1_RELOAD_LOW;
        
        // 恢复现场(编译器通常会自动处理)
    }
}

/**
* @brief PWM信号检测函数(需要根据实际需求实现)
*/
void check_pwm_signal(void)
{
    // 这里实现PWM信号的检测逻辑
    // 例如:读取PWM输入引脚,计算占空比等
}
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

13

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部