使用Atmel AVR studio中的ATMega2560读取RC PWM信号

时间:2015-08-06 10:08:28

标签: input timer avr pwm

我正在尝试从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 );

2 个答案:

答案 0 :(得分: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寄存器&#34; ICR5H&lt;&lt; 8 | ICR5L&#34;保持自上次从低变为高以来已经过的预定时钟脉冲数。这个数量通常不到2000美元。我已经说过,如果计数大于2500us(5000计数),则输入无效,下一个输入应为ppm.ch [0]。

我附上了PPM图像,如我的示波器所示。

Image of 6 Channel PPM

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

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

答案 1 :(得分:0)

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

三个定时器运行,每20 ms重置一次。这可以通过在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中)。