几个月前我忘记发布我的解决方案,所以在这里......
最后我使用了 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 图像。
这种读取 PPM 的方法非常有效,因为我们不需要保持轮询引脚来检查它们的逻辑电平。
不要忘记使用 sei() 命令启用中断。否则 ISR 将永远不会运行。