0

我正在尝试从 RC 接收器读取几个 PWM 信号到 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 );
4

2 回答 2

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<<8|ICR5L”保存自上次从低电平变为高电平以来经过的预分频时钟脉冲数。这个计数通常小于 2000 us。我说过如果计数大于 2500us(5000 个计数)那么输入无效,下一个输入应该是 ppm.ch[0]。

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

6 通道 PPM 的图像

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

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

于 2016-03-04T23:58:06.867 回答
0

假设您要执行以下操作(我并不是说这可以让您准确测量 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 为单位)。

于 2015-08-06T12:22:34.340 回答