在 Atmel AVR studio 中使用 ATMega2560 读取 RC PWM 信号
Posted
技术标签:
【中文标题】在 Atmel AVR studio 中使用 ATMega2560 读取 RC PWM 信号【英文标题】:Read RC PWM signal using ATMega2560 in Atmel AVR studio 【发布时间】: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 );
【问题讨论】:
【参考方案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 为单位)。
【讨论】:
以上是关于在 Atmel AVR studio 中使用 ATMega2560 读取 RC PWM 信号的主要内容,如果未能解决你的问题,请参考以下文章
将 Arduino 库添加到 Atmel Studio 7 AVR C++ 项目 - 缺少 Arduino.h