Lessons Learned from Using Embedded Artists Baseboard with LPC17xx (for reference)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Lessons Learned from Using Embedded Artists Baseboard with LPC17xx (for reference)

915 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by IntStarFoo on Mon Oct 22 10:02:27 MST 2012
Hi All,

I have been working with the EA Baseboard and the LPC1769.  I downloaded the example projects from http://www.embeddedartists.com/support/xpr/base and started playing around.  I decided to make my own project using the LIB_MCU, Lib_Ea_Baseboard and CMSIS v2.  My goal was to create an output signal for sending to an external system and I needed to have a 1us timer with interrupts to do so.

So, I'm new to embedded programming and this was my first time using timers and interrupts.  I watched a few YouTube videos that were very helpful in understanding the basics.  I read through the LPC17xx user manual (UM10360) and was more confused than before.  I figured the Library code would be helpful because the naming and structure defines helped to disambiguate all of the TLAs involved and the sample code seemed elegant.   The examples include using lpc17xx_timer.c from LIB_MCU and I followed the examples to configure my timer.  TIM_TIMERCFG_Type, TIM_MATCHCFG_Type and TIM_Init seemed to be exactly what I was looking for.

I put everything together and wrote my ~first interrupt based application.  It was simply not working as expected.  I barely knew enough to copy and paste the code and adapt it to my situation...

// Initialize timer 0, prescale count time of 1uS
TIM_ConfigStruct.PrescaleOption = TIM_PRESCALE_USVAL;
TIM_ConfigStruct.PrescaleValue= 1;
// use channel 0, MR0
TIM_MatchConfigStruct.MatchChannel = 0;
// Enable interrupt when MR0 matches the value in TC register
TIM_MatchConfigStruct.IntOnMatch   = TRUE;
//Enable reset on MR0: TIMER will reset if MR0 matches it
TIM_MatchConfigStruct.ResetOnMatch = FALSE;
//Stop on MR0 if MR0 matches it
TIM_MatchConfigStruct.StopOnMatch  = FALSE;
//Let me take care of MR0.0 pin if MR0 matches it
TIM_MatchConfigStruct.ExtMatchOutputType = TIM_EXTMATCH_NOTHING;
// Set Match value to 1 for 1us interuupts
TIM_MatchConfigStruct.MatchValue   = 1;

// Set configuration for Tim_config and Tim_MatchConfig
TIM_Init(LPC_TIM0, TIM_TIMER_MODE,&TIM_ConfigStruct);
TIM_ConfigMatch(LPC_TIM0,&TIM_MatchConfigStruct);

/* preemption = 1, sub-priority = 1 */
NVIC_SetPriority(TIMER0_IRQn, ((0x01<<3)|0x01));
/* Enable interrupt for timer 0 */
NVIC_EnableIRQ(TIMER0_IRQn);


It seemed perfect to me, yet it wasn't working.  I searched around for a week trying to figure out what the hell I was doing wrong.  After reading message boards, documentation and chatting with a veteran EE, I finally figured it out.  There is a bug in lpc17xx_timer.c!  The library was incorrectly configuring the timer.  It took me a long time to figure it out because I decided to skip over the basic understanding of what the helper functions were doing.  The lesson I learned (again) last weekend:  If you're going to use a library of helper functions, you need to have a basic understanding of what they are doing.  Learn the basics first!

So, to review... If you're going to use lpc17xx_timer.c from NXP's LIB_MCU, you should make a few fixes to the code.  The fixes are outlined by [I]'Ned Konz'[/I] here.

//THIS DOESN'T WORK IN THE CURRENT VERSION OF LIB_MCU's lpc17xx_timer.c!!!
TIM_MatchConfigStruct.ResetOnMatch = FALSE;



Embedded Artists Baseboard
http://www.embeddedartists.com/support/xpr/base

The LPC176x sample projects...
lpc176x_xpr_bb_120314.zip

Thanks [I]'Ned Konz'[/I] for describing the fix for the bug!
http://forums.nxp.com/viewtopic.php?t=5696

Here are the two fixed functions in lpc17xx_timer.c.  Mistakes are commented out with "LIB_MCU_BUG" and replaced just below the comment.
/*********************************************************************//**
 * @brief Initial Timer/Counter device
 *  Set Clock frequency for Timer
 * Set initial configuration for Timer
 * @param[in]TIMx  Timer selection, should be:
 * - LPC_TIM0: TIMER0 peripheral
 * - LPC_TIM1: TIMER1 peripheral
 * - LPC_TIM2: TIMER2 peripheral
 * - LPC_TIM3: TIMER3 peripheral
 * @param[in]TimerCounterMode Timer counter mode, should be:
 * - TIM_TIMER_MODE: Timer mode
 * - TIM_COUNTER_RISING_MODE: Counter rising mode
 * - TIM_COUNTER_FALLING_MODE: Counter falling mode
 * - TIM_COUNTER_ANY_MODE:Counter on both edges
 * @param[in]TIM_ConfigStruct pointer to TIM_TIMERCFG_Type
 * that contains the configuration information for the
 *                    specified Timer peripheral.
 * @return None
 **********************************************************************/
void TIM_Init(LPC_TIM_TypeDef *TIMx, TIM_MODE_OPT TimerCounterMode, void *TIM_ConfigStruct)
{
TIM_TIMERCFG_Type *pTimeCfg;
TIM_COUNTERCFG_Type *pCounterCfg;

CHECK_PARAM(PARAM_TIMx(TIMx));
CHECK_PARAM(PARAM_TIM_MODE_OPT(TimerCounterMode));

//set power

if (TIMx== LPC_TIM0)
{
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCTIM0, ENABLE);
//PCLK_Timer0 = CCLK/4
CLKPWR_SetPCLKDiv (CLKPWR_PCLKSEL_TIMER0, CLKPWR_PCLKSEL_CCLK_DIV_4);
}
else if (TIMx== LPC_TIM1)
{
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCTIM1, ENABLE);
//PCLK_Timer1 = CCLK/4
CLKPWR_SetPCLKDiv (CLKPWR_PCLKSEL_TIMER1, CLKPWR_PCLKSEL_CCLK_DIV_4);
}

else if (TIMx== LPC_TIM2)
{
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCTIM2, ENABLE);
//PCLK_Timer2= CCLK/4
CLKPWR_SetPCLKDiv (CLKPWR_PCLKSEL_TIMER2, CLKPWR_PCLKSEL_CCLK_DIV_4);
}
else if (TIMx== LPC_TIM3)
{
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCTIM3, ENABLE);
//PCLK_Timer3= CCLK/4
CLKPWR_SetPCLKDiv (CLKPWR_PCLKSEL_TIMER3, CLKPWR_PCLKSEL_CCLK_DIV_4);
}

//LIB_MCU_BUG TIMx->CCR &= ~TIM_CTCR_MODE_MASK;
//LIB_MCU_BUG TIMx->CCR |= TIM_TIMER_MODE;

TIMx->CTCR &= ~TIM_CTCR_MODE_MASK;
TIMx->CTCR |= TimerCounterMode;

TIMx->TC =0;
TIMx->PC =0;
TIMx->PR =0;
TIMx->TCR |= (1<<1); //Reset Counter
TIMx->TCR &= ~(1<<1); //release reset

if (TimerCounterMode == TIM_TIMER_MODE )
{
pTimeCfg = (TIM_TIMERCFG_Type *)TIM_ConfigStruct;
if (pTimeCfg->PrescaleOption  == TIM_PRESCALE_TICKVAL)
{
TIMx->PR   = pTimeCfg->PrescaleValue -1  ;
}
else
{
TIMx->PR   = converUSecToVal (converPtrToTimeNum(TIMx),pTimeCfg->PrescaleValue)-1;
}
}
else
{

pCounterCfg = (TIM_COUNTERCFG_Type *)TIM_ConfigStruct;
//LIB_MCU_BUG TIMx->CCR  &= ~TIM_CTCR_INPUT_MASK;
TIMx->CTCR &= ~TIM_CTCR_INPUT_MASK;
if (pCounterCfg->CountInputSelect == TIM_COUNTER_INCAP1)
{
//LIB_MCU_BUG TIMx->CCR |= _BIT(2);
TIMx->CTCR |= _BIT(2);
}
}

// Clear interrupt pending
TIMx->IR = 0xFFFFFFFF;

}


/*********************************************************************//**
 * @brief Convert a timer register pointer to a timer number
 * @param[in]TIMx Pointer to LPC_TIM_TypeDef, should be:
 * - LPC_TIM0: TIMER0 peripheral
 * - LPC_TIM1: TIMER1 peripheral
 * - LPC_TIM2: TIMER2 peripheral
 * - LPC_TIM3: TIMER3 peripheral
 * @return The timer number (0 to 3) or -1 if register pointer is bad
 **********************************************************************/
uint32_t converPtrToTimeNum (LPC_TIM_TypeDef *TIMx)
{
//LIB_MCU_BUG uint32_t tnum = -1;
uint32_t tnum = (uint32_t)-1;

if (TIMx == LPC_TIM0)
{
tnum = 0;
}
else if (TIMx == LPC_TIM1)
{
tnum = 1;
}
else if (TIMx == LPC_TIM2)
{
tnum = 2;
}
else if (TIMx == LPC_TIM3)
{
tnum = 3;
}

return tnum;
}
0 Kudos
Reply
2 Replies

740 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by frame on Mon Oct 22 22:50:12 MST 2012
Apart from the timer bug in the library, a 1us interrupt is a rather heavy load for the uC.
These are about 120 instructions (at max. speed), for servicing the interrupt AND doing anything else.
It might work if there is no other performance load, but don't be surprised if the code misbehaves
when adding modest functionality.
0 Kudos
Reply

740 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by daniel.widyanto on Mon Oct 22 22:07:03 MST 2012
Thanks, filling the CCR, instead of CTCR to set the Timer mode is definitely a mistake.

I will update the developer for the issue.
0 Kudos
Reply