This document shows the implementation of the infrared on the UART0 using the FRDM-KE02Z platform.
The FRDM-KE02Z platform is a developing platform for rapid prototyping. The board has a MKE02Z64VQH2 MCU a Kinetis E series MCU which is the first 5-Volt MCU built on the ARM Cortex-M0+ core. You can check the evaluation board in the Freescale’s webpage (FRDM-KE02Z: Kinetis E Series Freedom Development Platform)
The Freedom Board has a lot of great features and one of this is an IrDA transmitter and receiver on it. Check this out!
One of the features of the MCU is that the UART0 module can implement Infrared functions just following some tricks (MCU-magic tricks). According to the Reference Manual (Document Number: MKE02Z64M20SF0RM) this tricks are:
UART0_TX modulation:
UART0_RX Tag:
For this example we are going to use the ACMP0 module to implement the UART0_RX functionality.
Note1: The Core is configured to run at the maximum frequency: 20 Mhz
Note2: Refer to the reference manual document for more information about the registers.
The next lines show the configuration of the FTM0; the module is configured with a Frequency of 38 KHz which is the ideal frequency for an infrared led. The FTM0_CH0 is in Edge_Aligned PWM mode (EPWM).
#define IR_FREQUENCY 38000 //hz
#define FTM0_CLOCK BUS_CLK_HZ
#define FTM0_MOD_VALUE FTM0_CLOCK/IR_FREQUENCY
#define FTM0_C0V_VALUE FTM0_MOD_VALUE/2
void FTM0CH0_Init( void )
{
SIM_SCGC |= SIM_SCGC_FTM0_MASK;
// Init FTM0 to PWM output,frequency is 38khz
FTM0_MOD= FTM0_MOD_VALUE;
FTM0_C0SC = 0x28;
FTM0_C0V = FTM0_C0V_VALUE;
FTM0_SC = 0x08; // bus clock divide by 2
}
With this we accomplish the UART0_TX modulation through a PWM on the FTM0_CH0.
The configuration of the ACMP0 is using a DAC and allowing the ACMP0 can be driven by an analog input.
void ACMP_Init ( void )
{
SIM_SCGC |= SIM_SCGC_ACMP0_MASK;
ACMP0_C1 |= ACMP_C1_DACEN_MASK |
ACMP_C1_DACREF_MASK|
ACMP_C1_DACVAL(21); // enable DAC
ACMP0_C0 |= ACMP_C0_ACPSEL(0x03)|
ACMP_C0_ACNSEL(0x01);
ACMP0_C2 |= ACMP_C2_ACIPE(0x02); // enable ACMP1 connect to PIN
ACMP0_CS |= ACMP_CS_ACE_MASK; // enable ACMP
}
With this we have now implemented the UART0_RX.
Now the important thing is to initialize the UART0 to work together with these tricks and implement the irDA functions.
Basically we initialize the UART0 like when we use normal serial communication (this is not the topic of this post, refer to the project to see the UART_init function) and we write to the most important registers:
SIM_SOPT |= SIM_SOPT_RXDFE_MASK;
SIM_SOPT |= SIM_SOPT_TXDME_MASK;
The configuration is as follows:
void IrDA_Init( void )
{
// initialize UART0, 2400 baudrate
UART_init(UART0_BASE_PTR,BUS_CLK_HZ/1000,2400);
// clear RDRF flag
UART0_S1 |= UART_S1_RDRF_MASK;
// initialize FTM0CH1 as 38k PWM output
FTM0CH0_Init();
// enable ACMP
ACMP_Init();
SIM_SOPT |= SIM_SOPT_RXDFE_MASK; //UART0_RX input signal is filtered by ACMP, then injected to UART0.
UART0_S2 &= ~UART_S2_RXINV_MASK; //inverse data input
SIM_SOPT |= SIM_SOPT_TXDME_MASK; //UART0_TX output is modulated by FTM0 channel 0 before mapped to pinout.
}
With the irDA initialization we got the infrared features on the UART0.
In the attachments of this post you can find the example which shows the use of these functions in a basic application; the project was compiled in CodeWarrior 10.6 and the philosophy is:
I hope that the information presented on this document could be useful for you. Thank you!
Best Regards!
Hello Sanchez,
A good post indeed. But, I was wondering what might be the communication pattern? Is it a standard 3/16th bit width communication or the modulated Tx waveform looks different altogether? Couldn't capture on oscilloscope as the FTM is running continuously. Is the modulated waveform similar to the one attached?
I am trying this with the TWR-K60N512 KIT, I can receive interrupts on UART0_Rx through Comparator from a TV remote signal, but not from the Tx pin although checking with a smartphone camera, the Tx IR signal is going on. The code is as follows:
#include "types.h"
#include "infrared.h"
#define IR_FREQUENCY 38000
#define FTM1_CLOCK 750000
#define FTM1_MOD_VALUE FTM1_CLOCK/IR_FREQUENCY
#define FTM1_C0V_VALUE FTM1_MOD_VALUE/2
void init_infrared(void){
//initialize uart0 as infrared port through CMP0
init_uart0();
//initialize Comparator
init_cmp0();
//initialize the Flex timer
init_ftm();
}
void init_uart(UART_MemMapPtr uartch, int sysclk, int baud){
uint16_t ubd, temp, brfa;
//disable Tx and Rx during setup
UART_C2_REG(uartch) &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK );
/* Configure the UART for 8-bit mode, no parity */
/* We need all default settings, so entire register is cleared */
UART_C1_REG(uartch) = 0;
/* Calculate baud settings */
ubd = (uint16_t)((sysclk*1000)/(baud * 16));
/* Save off the current value of the UARTx_BDH except for the SBR */
temp = UART_BDH_REG(uartch) & ~(UART_BDH_SBR(0x1F));
UART_BDH_REG(uartch) = temp | UART_BDH_SBR(((ubd & 0x1F00) >> 8));
UART_BDL_REG(uartch) = (uint8_t)(ubd & UART_BDL_SBR_MASK);
/* Determine if a fractional divider is needed to get closer to the baud rate */
brfa = (((sysclk*32000)/(baud * 16)) - (ubd * 32));
/* Save off the current value of the UARTx_C4 register except for the BRFA */
temp = UART_C4_REG(uartch) & ~(UART_C4_BRFA(0x1F));
UART_C4_REG(uartch) = temp | UART_C4_BRFA(brfa);
// enable the reciever interrupts
UART_C2_REG(uartch) |= UART_C2_RIE_MASK;
}
void init_uart0(void){
//Gate clock to uart0
SIM_SCGC4 |= SIM_SCGC4_UART0_MASK;
init_uart(UART0_BASE_PTR,96000,2400);//core clock is 96Mhz=96000Khz
//enable IR encoding and decoding
UART0_IR |= UART_IR_IREN_MASK;
UART0_IR |= UART_IR_TNP(0x0);//narrow pulse 3/16 of baudrate
//System integration to route UART0_RX to CMP0
SIM_SOPT5 |= SIM_SOPT5_UART0RXSRC(01);//CMP0 as source of UART0_RX
SIM_SOPT5 |= SIM_SOPT5_UART0TXSRC(01);//Tx pin modulated with FTM1 channel0 output
SIM_SOPT2 |= SIM_SOPT2_CMTUARTPAD_MASK;//select dual pad drive strength for UART0_TX
//Enable receiver and transmitter
UART0_C2 |= UART_C2_RE_MASK;
UART0_C2 |= UART_C2_TE_MASK;
// configure Nested Vector Interrupt Controller: clear pending and set enable
NVICICPR1 |= interrupt_mask(1,INT_UART0_RX_TX);
NVICISER1 |= interrupt_mask(1,INT_UART0_RX_TX);
}
//UART0 brief interrupt handler
void UART0_RX_TX_IRQHandler(){
if((UART0_S1 & UART_S1_RDRF_MASK)==UART_S1_RDRF_MASK){
byte2 = (uint8_t)UART0_D;
data_available2=1;
}
}
//FTM1 initialization, used to modulate UART0_TX
void init_ftm(void){
SIM_SCGC6 |= SIM_SCGC6_FTM1_MASK;//gate the clock
FTM1_SC |= FTM_SC_CLKS(01);//system core clock as clock source
FTM1_SC |= FTM_SC_PS(7);//divide system clock by 128 = 750Khz
FTM1_MODE |= FTM_MODE_WPDIS_MASK;//disable write protect
FTM1_MOD |= FTM_MOD_MOD(FTM1_MOD_VALUE);//modulo value
FTM1_C0SC = 0x28;//edge-aligned PWM
FTM1_C0V = FTM1_C0V_VALUE;
}
void init_cmp0(void){
//gate the clock to CMP
SIM_SCGC4 |= SIM_SCGC4_CMP_MASK;
//disable comparator while configuring
CMP0_CR1 &= ~(CMP_CR1_OPE_MASK | CMP_CR1_EN_MASK);
//Configure and enable the Comparator's DAC for reference voltage
CMP0_DACCR |= CMP_DACCR_VRSEL_MASK;//supply voltage select is 1 for Vin2
CMP0_DACCR |= CMP_DACCR_VOSEL(0x0F);//Output voltage select is (Vin/64)*VOSEL+1 = Vin * 0.5 for a high(1)
CMP0_DACCR |= CMP_DACCR_DACEN_MASK;
//enable PMUX and MMUX
CMP0_MUXCR |= CMP_MUXCR_PEN_MASK | CMP_MUXCR_MEN_MASK;
//select IN0 for PMUX(positive side) and DACOUT i.e channel 7 as MMUX(minus side)
// |\
// IN0---|+\
// | \______CMP_OUT
// | /
// DACout---|-/
// |/
// COMPARATOR
//
CMP0_MUXCR |= CMP_MUXCR_PSEL(0);
CMP0_MUXCR |= CMP_MUXCR_MSEL(7);
//set 0 samples per measurement
CMP0_CR0 |= CMP_CR0_FILTER_CNT(0x0);//disable filter
CMP0_CR1 |= CMP_CR1_COS_MASK;//unfiltered output
CMP0_CR1 |= CMP_CR1_INV_MASK;//invert output
//enable comparator and the output pin
CMP0_CR1 |= (CMP_CR1_OPE_MASK | CMP_CR1_EN_MASK);
}
I think I am missing some information on how the modulation actually works. Could you please explain a little further on the actual protocol, how the timing diagrams look like, how do we demodulate, where are the UART settings for demodulation, i can see how we modulate with the FTM, but not how we are actually demodulating the received signal. Is it by sampling the CMP input?
BlackNight you have any ideas?
I got it to work without FTM modulation, just with the 3/16 narrow pulse. UART0 configured with 2400 baudrate and inverted RX, CMP DAC Output voltage select is (Vin/64)*31+1 = VDD(3.3v) * 0.5 = 1.65V for a high(1), supply voltage select is 1 for Vin2 which is VDD (3.3v). The trick is to configure the Comparator properly with a good reference voltage on the inverting input. I did not configure any sampling. Next I will try with FTM modulation, only I dont know how exactly to demodulate on the comparator end using the sampling feature.
I figured out what was the problem. The TWR-K60 has a different IR receiver filter with R=1KOhm and C=0.1uF, which passes frequencies less than 1.6KHz compared to the FRDM-KE02Z which has R=1KOhm and C=1000PF, which will passes frequencies less than 160KHz, hence 38KHz is OK for that board. 38KHz wont work with TWR-K60N512 because it will be blocked by the filter. I set the UART0 baud lower to 1400 and the FTM1 frequency to 1.5KHz and it works like a charm! I compared the two user manuals :-)
Formula:
fc = 1/(2*pi*Τ) = 1/(2pi*RC); where R=1000Ohms, C=0.1*10^-6Farads
fc = 1.6KHz