Arduino紅外發射的調頻定時器設置

Arduino紅外發射的調頻定時器設置

原博客格式更友好:http://www.straka.cn/blog/arduino-uno-infrared-emission-timer-setup/

網上了解了下ARDUINO的定時器、中斷、PWM、舵機控制,紅外收發等相關知識。尤其是仔細閱讀了AVR atmega328p,也就是ARDUINO UNO的芯片手冊的定時器部分,其中有兩點:

  1. AT mega328p的定時器有3個,對應Arduino UNO板子,
  2. Timer0 對應 5、6引腳pwm, 8bit
  3. Timer1 對應 9、10引腳pwm, 16bit
  4. Timer2 對應 11、3引腳pwm, 8bit
  5. 舵機的pwm頻率爲50Hz / 20ms, 但是控制舵機需要的佔空比比較小,爲20ms中的5 ~ 2.5ms。

先講講紅外發射的調頻原理吧

那麼根據1,由於紅外發射需要38khz左右的載波頻率,不過不用特別精確的38khz,所以紅外收發的庫裏,

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#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的晶振,根據上述代碼和手冊,WGM20和WGM22置1,所以Mode爲5

 

那麼就是pwm,phase 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的處理函數

中要做的就是根據要收發的消息,對載波進行調製,根據需要調製上載波或者去掉載波。

此處見代碼,不詳述。

C++

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

//+=============================================================================

// 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

}

參考資料:

Arduino紅外傳感器-IR Receiver Module紅外接收模塊

https://www.ncnynl.com/archives/201606/85.html

發佈了34 篇原創文章 · 獲贊 25 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章