In case anyone else is in the position of needing to move to LPC and is missing the CMT provided in some Kinetis and Coldfire parts for infrared transmission, here's the solution I came up with.
The CMT provides a timer to generate a carrier signal (typically around 36 or 38 kHz), and then provides high and low time registers to control the mark and space time for each pulse to be transmitted. I've substituted a CTIMER + SCT to implement the same functionality.
The SCT is set up in unified mode (single 32-bit timer) and has four events and two states. State 0 is the 'on' state, and two events produce a 50% duty cycle square wave at 38 kHz. State 1 is the 'off' state. The other two events monitor input 0, which is set to the MAT0 output of CTIMER1. The sense is inverted, since the CTIMER PWM mode starts out low. A falling edge on the input switches to state 0 and turns the carrier on. A rising edge switches to state 1 and turns the carrier off.
In this sample, the initial PWM period is set to 9 ms on and 4.5 ms off to generate an NEC protocol start pulse. The ir_load_next_bit() function here is a dummy rather than a real NEC data source and just loads alternating bits forever. It works by loading the next bit's mark and space times into the shadow match registers, so that at the end of the current cycle they're automatically loaded and an interrupt is generated so another bit can be loaded.
I've tried to use the MCUXpresso SDK here, but it doesn't expose all of the needed functions. This demo assumes a 150 MHz main clock.
#define NEC_TIME_UNIT 84375
#define NEC_ONE_TIME (NEC_TIME_UNIT * 3)
#define NEC_ZERO_TIME (NEC_TIME_UNIT * 2)
#define NEC_RESET_HI (NEC_TIME_UNIT * 16)
#define NEC_RESET_LO (NEC_TIME_UNIT * 8)
ctimer_callback_t ir_tx_callback_table[] = {ir_tx_callback};
uint8_t ir_tx_bit;
/**
* Dummy data source, loads alternating 1s and 0s into shadow registers
*/
static void ir_load_next_bit(void)
{
ir_tx_bit++;
if (ir_tx_bit & 1)
{
CTIMER1->MSR[kCTIMER_Match_3] = NEC_ONE_TIME;
CTIMER1->MSR[kCTIMER_Match_0] = NEC_TIME_UNIT;
}
else
{
CTIMER1->MSR[kCTIMER_Match_3] = NEC_ZERO_TIME;
CTIMER1->MSR[kCTIMER_Match_0] = NEC_TIME_UNIT;
}
}
/**
* Initialize infrared transmitter system. Uses SCT0 to generate the 38 kHz carrier, which
* is controlled by the output of CTIMER1.
*/
void ir_tx_init(void)
{
sctimer_config_t sctimerInfo;
sctimer_pwm_signal_param_t pwmParam;
uint32_t sctimerClock;
uint32_t pwm_event;
uint32_t on_event;
uint32_t off_event;
ctimer_config_t config;
sctimerClock = CLOCK_GetFreq(kCLOCK_BusClk);
SCTIMER_GetDefaultConfig(&sctimerInfo);
/* Initialize SCTimer module */
SCTIMER_Init(SCT0, &sctimerInfo);
/*
* Sets up events 0 and 1 to create a 38 kHz square wave to serve as the carrier for the
* IR emitter. Both are active in state 0. Match on 1 clears the output and match on 0
* limits the timer.
*/
pwmParam.output = kSCTIMER_Out_2;
pwmParam.level = kSCTIMER_HighTrue;
pwmParam.dutyCyclePercent = 50;
SCTIMER_SetupPwm(SCT0, &pwmParam, kSCTIMER_EdgeAlignedPwm, 38000U, sctimerClock, &pwm_event);
/*
* Selects the output of CTIMER1_MAT0 as SCT0 input 0. This is the data input that controls
* the carrier. The 'off' event is active in state 0 (carrier on) and will cause a transition
* to state 1 (carrier off) on the rising edge of input 0. The 'on' event is active in state 1
* and watches for a low level on input 0, which triggers a switch back to state 0. This
* configuration is chosen because the PWM function of the CTIMER has the low portion at the
* start of the period.
*
* The first PWM setup is configured for a 9 x 4.5 ms start pulse, and the next bit to be sent
* is loaded into the shadow registers so the values are ready at the next cycle.
*
*/
INPUTMUX_AttachSignal(INPUTMUX, 0, kINPUTMUX_Ctimer1M0ToSct0);
SCTIMER_CreateAndScheduleEvent(SCT0, kSCTIMER_InputRiseEvent, 0, kSCTIMER_Input_0, kSCTIMER_Counter_U,
&off_event);
SCTIMER_SetupNextStateActionwithLdMethod(SCT0, 1, off_event, true);
SCTIMER_SetupCounterStopAction(SCT0, kSCTIMER_Counter_U, off_event);
SCTIMER_SetupCounterLimitAction(SCT0, kSCTIMER_Counter_U, off_event);
SCTIMER_SetupOutputClearAction(SCT0, kSCTIMER_Out_2, off_event);
SCTIMER_IncreaseState(SCT0);
SCTIMER_CreateAndScheduleEvent(SCT0, kSCTIMER_InputLowEvent, 0, kSCTIMER_Input_0, kSCTIMER_Counter_U,
&on_event);
SCTIMER_SetupNextStateActionwithLdMethod(SCT0, 0, on_event, true);
SCTIMER_SetupCounterStartAction(SCT0, kSCTIMER_Counter_U, on_event);
SCTIMER_StartTimer(SCT0, kSCTIMER_Counter_U);
// Configure CTIMER1 to generate an NEC bit pattern, starting with a 9ms high/4.5ms low start pulse
CLOCK_AttachClk(kMAIN_CLK_to_CTIMER1);
CTIMER_GetDefaultConfig(&config);
CTIMER_Init(CTIMER1, &config);
CTIMER_SetupPwmPeriod(CTIMER1, kCTIMER_Match_3, kCTIMER_Match_0, NEC_RESET_HI+NEC_RESET_LO, NEC_RESET_HI, 1);
CTIMER_RegisterCallBack(CTIMER1, ir_tx_callback_table, kCTIMER_SingleCallback);
CTIMER1->MCR |= (CTIMER_MCR_MR0RL_MASK | CTIMER_MCR_MR3RL_MASK);
ir_load_next_bit();
CTIMER_StartTimer(CTIMER1);
}
/**
* Called from timer interrupt when a transmitted IR bit is done and next value needs to
* be loaded.
*/
static void ir_tx_callback(uint32_t flags)
{
ir_load_next_bit();
}
Hi,
I think it is a great idea to implement the signal modulation with carrier signal.
The CTimer generates the SPACE and MARK signal, the SCT generates the high frequency carrier signal. with the space/mark signal rising/falling edge, the SCT generates events and the events switch the SCT states, with one MARK state, the SCT generated high frequency signal can output to SCT output pad, with another state, the SCT carrier signal generating events are disabled and output low logic to the SCT output pad.
I think it can be used in irda signal generating also.
BR
XiangJun Rong
That's right. It looks like you could accomplish the same thing using the SCT in split mode where the upper half tracks the mark/space time, and with some extra states it could handle the start bit. The combination with the CTIMER was a closer match for how the CMT works, though, and I wasn't sure what the best way to trigger the SCT would be without the CTIMER.
Scott
Hi,
I suggest you use CTimer to generate the mark/space signal and the SCTimer works in 32 bits mode with UNIFY = 1. The solution is very straightforward.
BR
XiangJun Rong