freetech

kinetis的红外收发例程——基于UART串口进行38kHz载波通信

0
阅读(4864)

前面已经有了串口、FTM、模拟比较器等例程,这次综合用起来实现一个基于UART串口进行38kHz载波红外通信的例子。kinetis为红外载波通信做了优化,需要很少的外围器件即可实现。另一篇文章总结了实现38kHz调制的6种电路http://blog.chinaaet.com/detail/28795.html,其中最简单的至少还要占用2个MCU引脚,kinetis则只需一个MCU引脚即可。另外还有红外信号的接收,一般是采用3个脚的一体化红外接收头,像HS0038、IPM3638等,或用红外光敏二极管+比较器。kinetis内置了比较器,可直接外接红外光敏二极管来实现。下面是参考电路:

image

从上图可以看出,发送电路和一般的点亮发光二极管的电路没有差别,然而它却可以38kHz载波调制。接收电路就是将电阻与红外光敏二极管的分压红电容滤除38kHz载波成份后连到内部比较器的一个引脚,MCU内部可以实现比较器的输出作为UART输入。

下面看下kinetis的哪些特异功能实现了上面的通信:

1、系统集成模块(SIM):SIM_SOPT5可设置UART0和UART1的接收和发送源。可以把TXD经FTM调制后经TX引脚输出,把模拟比较器的输出作为UART的输入。

image

2、UART模块引脚电平可反转:UART0_C3的第4位置1后,TX输出的电平是反着的。笔者测试发现,当UART发送源设成FTM调制模式时,是TX为1时输出38kHz,而红外发射是希望发送0时输出38kHz,这就需要将TX电平反转。下图是TX引脚的波形:

image

3、端口驱动强度控制:PORTx_PCRn和第6位DSE可选择驱动强度,为1时为高强度输出。红外发射需要是驱动电流较大,在低驱动强度时通信距离会很近。

有了上述几项功能,就可以用上面很简单的电路实现38kHz载波通信了。

下面是接收引脚波形,只要将比较器阀值设在波形的高、低电平之间就可以正确接收了。

image

下面是完整代码:

/*
* main implementation: use this 'C' sample to create your own application
*
*/

#define GPIO_PIN_MASK            0x3C000000
#define GPIO_PIN(x)                ((1<<x)&GPIO_PIN_MASK)
#include <stdio.h>

#include "derivative.h" /* include peripheral declarations */

void MCG_Init()
{
  SIM_SCGC6 |= 0x20000000; //SIM_SCGC6: RTC=1                     
  if ((RTC_CR & RTC_CR_OSCE_MASK) == 0u)//Only if the OSCILLATOR is not already enabled
  {
    RTC_CR &= ~0x3C00; //RTC_CR: SC2P=0,SC4P=0,SC8P=0,SC16P=0                    
    RTC_CR |= 0x0100;  //RTC_CR: OSCE=1                    
    RTC_CR &= ~0x0200; //RTC_CR: CLKO=0                    
  }
  //System clock initialization
  SIM_CLKDIV1 = 0x01130000; //SIM_CLKDIV1: OUTDIV1=0,OUTDIV2=1,OUTDIV3=1,OUTDIV4=3 Update system prescalers
  SIM_SOPT2 &= ~0x00010000; //SIM_SOPT2: PLLFLLSEL=0 Select FLL as a clock source for various peripherals
  SIM_SOPT1 &= ~0x00080000; //SIM_SOPT1: OSC32KSEL=0 System oscillator drives 32 kHz clock for various peripherals
  // Switch to FEE Mode
  SIM_SOPT2 |= 0x01;// SIM_SOPT2: MCGCLKSEL=1 0-System oscillator (OSCCLK), 1-32 kHz RTC oscillator
  MCG_C2 = 0x00; // MCG_C2: ??=0,??=0,RANGE=0,HGO=0,EREFS=0,LP=0,IRCS=0                           
  MCG_C1 = 0x02; // MCG_C1: CLKS=0,FRDIV=0,IREFS=0,IRCLKEN=1,IREFSTEN=0                           
  MCG_C4 |= 0xE0; //MCG_C4: DMX32=1,DRST_DRS=3                          
  MCG_C5 = 0x00; // MCG_C5: ??=0,PLLCLKEN=0,PLLSTEN=0,PRDIV=0                           
  MCG_C6 = 0x00;// MCG_C6: LOLIE=0,PLLS=0,CME=0,VDIV=0                            
  while((MCG_S & MCG_S_IREFST_MASK) != 0x00U) //Check that the source of the FLL reference clock is the external reference clock.
  {
  }
  while((MCG_S & 0x0CU) != 0x00U) // Wait until output of the FLL is selected
  {   
  }   
}
void UART_Init()
{
      SIM_SCGC4 |= 1 << 10;// SIM_SCGC1: UART0=1
      SIM_SCGC5 |= 1 << 9;// SIM_SCGC5: PORTA=1
      PORTA_PCR15 = (3<<8) | (1<<6) ;// PORTA_PCR15: ISF=0,MUX=3 做UART
      PORTA_PCR14 = (3<<8) | (1<<6); // PORTA_PCR14: ISF=0,MUX=3 做UART                         
      SIM_SOPT5 = (1<<2)|1;    //UART0TX来自CMP0,UART0TX 由FTM1CH0调制
      UART0_C4 = 0x14;    //波特率微调
      UART0_BDH = (4992>>8) & 0x1F;//设波特率1200bps
      UART0_BDL = 4992&0xFF;
      UART0_C2 |= 1<<2;//允许接收
      UART0_C3 |= 1<<4;//发送引脚反转
}

void FTM1_Init()
{
    SIM_SCGC6 |= 1 << 25;
    FTM1_MODE = 0x04;
    FTM1_SC = 0x00;   
    //FTM1_C0SC = 0x14;也可以用输出比较模式,但周期差一半,需调整C0V和MOD为PWM模式的2倍   
    FTM1_C0SC = 0x28;//PWM模式,匹配时产生中断
    FTM1_C0V = 632;
    FTM1_CNTIN = 0;//初值值0
    FTM1_CNT = 0;//计数值0
    FTM1_MOD = 1263;//模
    FTM1_SC = (1<<3) | 0;//系统时钟,1分频

}
void ACMP_Init()
{
      SIM_SCGC4 |= SIM_SCGC4_CMP_MASK; // 打开CMP时钟 
      CMP0_DACCR = (1<<7)| (1<<6) | 47;//6位DAC输出
      SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;//打开PORTC时钟
      PORTC_PCR7 &= ~0x01000700; // PORTC_PCR6: ISF=0,MUX=0
      PORTC_PCR5 = ((PORTC_PCR5 & ~0x01000100) | 0x0600);
      CMP0_MUXCR = 0xC7;// CMP0_MUXCR: PEN=1,MEN=1,PSEL=0,MSEL=7
      CMP0_CR1 = 0x03;      
}
int main(void)
{
    int idle,counter = 0;
    char rcv_buf[10];
    MCG_Init();
    UART_Init();
    FTM1_Init();
    ACMP_Init();
    printf("Hello (Kinetis) World in 'C' from MK60DX256Z derivative! \n\r");
    for(;;) {
        //发送
        if( (UART0_S1&(1<<7)) != 0)
        {
            UART0_D = 0x5F;
            UART0_C2 |= 1<<3;
        }
        //接收
        if( (UART0_S1&(1<<5)) != 0)
        {
            rcv_buf[counter] = UART0_D;
            counter++;
            if(counter >= 10)
            {
                counter = 0;
            }
        }
    }
    return 0;
}