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 对应 5、6引脚pwm, 8bit B. Timer1 对应 9、10引脚pwm, 16bit C. Timer2 对应 11、3引脚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的晶振,根据上述代码和手册,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的处理函数 中要做的就是根据要收发的消息,对载波进行调制,根据需要调制上载波或者去掉载波。 此处见代码,不详述。 //+============================================================================= // 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
|