【问题标题】:Read RC PWM signal using ATMega2560 in Atmel AVR studio在 Atmel AVR studio 中使用 ATMega2560 读取 RC PWM 信号
【发布时间】:2015-10-29 10:24:29
【问题描述】:

我正在尝试将几个 PWM 信号从 RC 接收器读取到 ATMega 2560。我无法理解 ICRn 引脚的功能,因为它似乎用于所有三个比较寄存器。

RC PWM 信号的周期为 20ms,其中 2ms 的 HIGH 脉冲为有效上限值,1ms 为有效下限值。所以该值将从 1000us 扫描到 2000us。该周期应从脉冲的上升沿开始。

我已将 16MHz 时钟预分频 8 以拥有一个 2MHz 定时器,因此应该能够以 0.5us 的精度测量信号,这足以满足我的要求。

请注意,我对 PWM 输出没有任何问题,这个问题专门针对 PWM 输入。

到目前为止,我的代码附在下面。我知道我必须使用 ICR3 和 ISR 来测量 PWM 值,但我不确定执行此操作的最佳程序。我也不知道如何检查测量的值是来自 PE3、PE4 还是 PE5。这段代码是否正确?如何获得我正在寻找的价值?

任何帮助将不胜感激。

// Set pins as inputs
DDRE |= ( 0 << PE3 ) | ( 0 << PE4 ) | ( 0 << PE5 );

// Configure Timers for CTC mode
TCCR3A |=   ( 1 << WGM31 ) | ( 1 << WGM30 ); // Set on compare match
TCCR3B |=   ( 1 << WGM33 ) | ( 1 << WGM32 ) | ( 1 << CS31); // Set on compare match, prescale_clk/8

TCCR3B |=   ( 1 << ICES5 ) // Use rising edge as trigger

// 16 bit register - set TOP value
OCR3A = 40000 - 1;
OCR3B = 40000 - 1;
OCR3C = 40000 - 1;

TIMSK3 |= ( 1 << ICIE3 );

【问题讨论】:

    标签: input timer avr pwm


    【解决方案1】:

    几个月前我忘记发布我的解决方案,所以在这里......

    我最后使用了一个 PPM 接收器,因此可以轻松编辑此代码以读取简单的 PWM。

    在我的头文件中,我为我的项目使用的 6 通道接收器创建了一个结构。对于具有更多或更少频道的接收器,这可以根据需要进行更改。

    #ifndef _PPM_H_
    #define _PPM_H_
    
    // Libraries included
    #include <stdint.h>
    #include <avr/interrupt.h>
    
    struct orangeRX_ppm {
        uint16_t ch[6];
        };
    volatile unsigned char ch_index;
    struct orangeRX_ppm ppm;
    
    /* Functions */
    void ppm_input_init(void); // Initialise the PPM Input to CTC mode
    ISR( TIMER5_CAPT_vect ); // Use ISR to handle CTC interrupt and decode PPM
    
    #endif /* _PPM_H_ */
    

    然后我的 .c 文件中有以下内容。

    // Libraries included
    #include <avr/io.h>
    #include <stdint.h>
    #include "ppm.h"
    
    /*      PPM INPUT
     *      ---
     *      ICP5    Pin48 on Arduino Mega
     */
    void ppm_input_init(void)
    {
        DDRL |= ( 0 << PL1 ); // set ICP5 as an input
    
        TCCR5A = 0x00; // none
        TCCR5B = ( 1 << ICES5 ) | ( 1 << CS51); // use rising edge as trigger, prescale_clk/8
        TIMSK5 = ( 1 << ICIE5 ); // allow input capture interrupts
    
        // Clear timer 5
        TCNT5H = 0x00;
        TCNT5L = 0x00;
    }
    
    // Interrupt service routine for reading PPM values from the radio receiver.
    ISR( TIMER5_CAPT_vect )
    {
        // Count duration of the high pulse
        uint16_t high_cnt; 
        high_cnt = (unsigned int)ICR5L;
        high_cnt += (unsigned int)ICR5H * 256;
    
        /* If the duration is greater than 5000 counts then this is the end of the PPM signal 
         * and the next signal being addressed will be Ch0
         */
        if ( high_cnt < 5000 )
        {
            // Added for security of the array
            if ( ch_index > 5 )
            {
                ch_index = 5;
            }
    
            ppm.ch[ch_index] = high_cnt; // Write channel value to array
    
            ch_index++; // increment channel index
        }
        else
        {
            ch_index = 0; // reset channel index
        }
    
        // Reset counter
        TCNT5H = 0;
        TCNT5L = 0;
    
        TIFR5 = ( 1 << ICF5 ); // clear input capture flag
    }
    

    每次 ICP5 从低到高时,此代码都会使用触发 ISR。在此 ISR 中,16 位 ICR5 寄存器“ICR5H

    我附上了在我的示波器上看到的 PPM 图像。

    这种读取 PPM 的方法非常有效,因为我们不需要保持轮询引脚来检查它们的逻辑电平。

    不要忘记使用 sei() 命令启用中断。否则 ISR 将永远不会运行。

    【讨论】:

      【解决方案2】:

      假设您想要执行以下操作(我并不是说这可以让您准确测量 PWM 信号,但它可以作为如何设置寄存器的示例)

      三个计时器正在运行,每 20 毫秒重置一次。这可以通过将它们设置为 OCRnA 的 CTC 模式来完成:wgm3..0 = 0b0100。

      //timer 1
      TCCR4A = 0;
      TCCR1B = (1<<CS11) | (1<<WGM12);
      OCR1A = 40000 - 1;
      //timer 3 (there's no ICP2)
      TCCR3A = 0;
      TCCR3B = (1<<CS31) | (1<<WGM32);
      OCR3A = 40000 - 1;
      //timer 4
      TCCR4A = 0;
      TCCR4B = (1<<CS41) | (1<<WGM42);
      OCR4A = 40000 - 1;
      

      现在将三个 pwm 信号中的每一个连接到它们自己的 ICPn 引脚(其中 n = 定时器)。检查数据表中不同 ICPn 引脚的位置(我很确定它不是 PE3、4、5)

      假设 pwm 信号在 t=0 时开始为高电平,并在其高电平时间后在该周期的剩余时间内变为低电平。您想测量高电平时间,因此我们会在 ICPn 引脚上出现下降沿时触发一个中断。

      TCCRnB 寄存器中的位 ICESn 设置为 0 将选择下降沿(这在前面的代码块中已经完成)。

      要触发中断,请设置相应的中断使能位:

      TIMSK1 |= (1<<ICIE1);
      TIMSK3 |= (1<<ICIE3);
      TIMSK4 |= (1<<ICIE4);
      sei();
      

      现在,每次为 ICn 触发中断时,您都可以获取 ICRn 寄存器以查看下降沿发生的时间(以时钟周期/8 为单位)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多