打印
[UNO]

Arduino红外发射的调频定时器设置

[复制链接]
7930|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
straka|  楼主 | 2020-1-30 17:44 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
Arduino红外发射的调频定时器设置
原博客格式更友好:http://www.straka.cn/blog/arduino-uno-infrared-emission-timer-setup/
网上了解了下ARDUINO的定时器、中断、PWM、舵机控制,红外收发等相关知识。尤其是仔细阅读了AVR atmega328p,也就是ARDUINO UNO的芯片手册的定时器部分,其中有两点:
1. AT mega328p的定时器有3个,对应Arduino UNO板子,
A. Timer0 对应 56引脚pwm, 8bit
B. Timer1 对应 910引脚pwm, 16bit
C. Timer2 对应 113引脚pwm, 8bit
2. 舵机的pwm频率为50Hz / 20ms, 但是控制舵机需要的占空比比较小,为20ms中的0.5 ~ 2.5ms
先讲讲红外发射的调频原理吧
那么根据1,由于红外发射需要38khz左右的载波频率,不过不用特别精确的38khz,所以红外收发的库里,
#define TIMER_RESET
#define TIMER_ENABLE_PWM    (TCCR2A |= _BV(COM2B1))
#define TIMER_DISABLE_PWM   (TCCR2A &= ~(_BV(COM2B1)))
#define TIMER_ENABLE_INTR   (TIMSK2 = _BV(OCIE2A))
#define TIMER_DISABLE_INTR  (TIMSK2 = 0)
#define TIMER_INTR_NAME     TIMER2_COMPA_vect
#define TIMER_CONFIG_KHZ(val) ({ \
const uint8_t pwmval = SYSCLOCK / 2000 / (val); \
TCCR2A               = _BV(WGM20); \
TCCR2B               = _BV(WGM22) | _BV(CS20); \
OCR2A                = pwmval; \
OCR2B                = pwmval / 3; \
})
#define TIMER_COUNT_TOP  (SYSCLOCK * USECPERTICK / 1000000)
假若是16M的晶振,根据上述代码和手册,WGM20WGM221,所以Mode5
那么就是pwmphase correct模式,
主要意思就是,每周期在ocrA处达到计数顶部,触发中断,然后从0开始计数,由于不是到达顶部255溢出才从0开始重新计数,所以能实现相位的调整,而不再是fclk / prescale / 256的频率。因为CS20置位,所以预除数是1,即没有预除数prescale
那么频率就是fout= fclk / ocrA / 2,除2是因为两个中断才完成电平翻转,形成载波的一个周期。
那我们很容易得到,如果我们想要38khz的频率,用16M晶振获取,需要设置OCRA
16M/38k/2=210,那么实际频率 16m / 210 / 2 = 38095.238hz
中断向量TIMER2_COMPA_vect的处理函数
中要做的就是根据要收发的消息,对载波进行调制,根据需要调制上载波或者去掉载波。
此处见代码,不详述。
//+=============================================================================
// Interrupt Service Routine - Fires every 50uS
// TIMER2 interrupt code to collect raw data.
// Widths of alternating SPACE, MARK are recorded in rawbuf.
// Recorded in ticks of 50uS [microseconds, 0.000050 seconds]
// 'rawlen' counts the number of entries recorded so far.
// First entry is the SPACE between transmissions.
// As soon as a the first [SPACE] entry gets long:
//   Ready is set; State switches to IDLE; Timing of SPACE continues.
// As soon as first MARK arrives:
//   Gap width is recorded; Ready is cleared; New logging starts
//
ISR (TIMER_INTR_NAME)
{
TIMER_RESET;
// Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on]
// digitalRead() is very slow. Optimisation is possible, but makes the code unportable
uint8_t  irdata = (uint8_t)digitalRead(irparams.recvpin);
irparams.timer++;  // One more 50uS tick
if (irparams.rawlen >= RAWBUF)  irparams.rcvstate = STATE_OVERFLOW ;  // Buffer overflow
switch(irparams.rcvstate) {
  //......................................................................
  case STATE_IDLE: // In the middle of a gap
   if (irdata == MARK) {
    if (irparams.timer < GAP_TICKS)  {  // Not big enough to be a gap.
     irparams.timer = 0;
    } else {
     // Gap just ended; Record duration; Start recording transmission
     irparams.overflow                  = false;
     irparams.rawlen                    = 0;
     irparams.rawbuf[irparams.rawlen++] = irparams.timer;
     irparams.timer                     = 0;
     irparams.rcvstate                  = STATE_MARK;
    }
   }
   break;
  //......................................................................
  case STATE_MARK:  // Timing Mark
   if (irdata == SPACE) {   // Mark ended; Record time
    irparams.rawbuf[irparams.rawlen++] = irparams.timer;
    irparams.timer                     = 0;
    irparams.rcvstate                  = STATE_SPACE;
   }
   break;
  //......................................................................
  case STATE_SPACE:  // Timing Space
   if (irdata == MARK) {  // Space just ended; Record time
    irparams.rawbuf[irparams.rawlen++] = irparams.timer;
    irparams.timer                     = 0;
    irparams.rcvstate                  = STATE_MARK;
   } else if (irparams.timer > GAP_TICKS) {  // Space
     // A long Space, indicates gap between codes
     // Flag the current code as ready for processing
     // Switch to STOP
     // Don't reset timer; keep counting Space width
     irparams.rcvstate = STATE_STOP;
   }
   break;
  //......................................................................
  case STATE_STOP:  // Waiting; Measuring Gap
    if (irdata == MARK)  irparams.timer = 0 ;  // Reset gap timer
    break;
  //......................................................................
  case STATE_OVERFLOW:  // Flag up a read overflow; Stop the State Machine
   irparams.overflow = true;
   irparams.rcvstate = STATE_STOP;
    break;
}
#ifdef BLINKLED
// If requested, flash LED while receiving IR data
if (irparams.blinkflag) {
  if (irdata == MARK)
   if (irparams.blinkpin) digitalWrite(irparams.blinkpin, HIGH); // Turn user defined pin LED on
    else BLINKLED_ON() ;   // if no user defined LED pin, turn default LED pin for the hardware on
  else if (irparams.blinkpin) digitalWrite(irparams.blinkpin, LOW); // Turn user defined pin LED on
    else BLINKLED_OFF() ;   // if no user defined LED pin, turn default LED pin for the hardware on
}
#endif // BLINKLED
}
原博客格式更友好:http://www.straka.cn/blog/arduino-uno-infrared-emission-timer-setup/
参考资料:
Arduino红外传感器-IR Receiver Module红外接收模块
https://www.ncnynl.com/archives/201606/85.html

使用特权

评论回复

相关帖子

沙发
qudonghli123| | 2020-7-29 18:02 | 只看该作者
谢谢

使用特权

评论回复
板凳
sivalley2| | 2021-3-2 22:27 | 只看该作者
谢谢

使用特权

评论回复
地板
xu@xupt| | 2022-12-13 21:38 | 只看该作者
学习啦

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:一枚软件狗,欢迎访问我的博客: www.straka.cn

17

主题

46

帖子

2

粉丝