Keil Logo Arm Logo

LPC1768 problem with timer0

Next Thread | Thread List | Previous Thread Start a Thread | Settings

Details Message
Read-Only
Author
Damian doles
Posted
3-May-2012 11:23 GMT
Toolset
ARM
New! LPC1768 problem with timer0

Hi, this is my first thread in this forum and at the beginning i want to apologize for my English OK, I've just written small program with timer0 and interrupt. I want to toggle one of the LED on board, but...It doesn't work and I really don't know what's wrong with this code. Each LED is lit and nothing more... Could you help with it ? Thank you in advance and best regards :)

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

// TODO: insert other include files here

// TODO: insert other definitions and declarations here
int main(void)
{
    LPC_GPIO2->FIODIR |= 1 << 29;
    LPC_SC->PCONP |= 1 << 1;
    LPC_SC->PCLKSEL0 |= 1 << 3;
    LPC_TIM0->TCR |= 0x00000002;
    LPC_TIM0->MCR |= 0x00000003;
    LPC_TIM0->MR0 |= 6000000;
    NVIC_EnableIRQ(TIMER0_IRQn);
    LPC_TIM0->TCR |= 0x00000001;
    while(1){}
    return 0 ;
}

void TIMER0_IRQHandler (void)
{
    if((LPC_TIM0->IR & 0x01) == 0x01)
    {
        LPC_TIM0->IR |= 1 << 0;
        LPC_GPIO2->FIOPIN ^= 1 << 29;
    }
}
Read-Only
Author
Some Body
Posted
3-May-2012 11:44 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

I have no experience of this micro but perhaps the following line should be:

LPC_TIM0->MR0 |= 0x6000000;

instead of:

LPC_TIM0->MR0 |= 6000000;
Read-Only
Author
Damian doles
Posted
3-May-2012 11:53 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

I changed this line and it still doesn't work. I supose that timer doesn't enter to interrupt...Important question, maybe timer's configuration is bad ? I don't know...
For example, when I write program on AVR microprocessors (ATmega32 and others) I must write declaration of frequency - #define F_CPU 16000000. Here I do not have to do it?

Read-Only
Author
Damian doles
Posted
3-May-2012 12:03 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

OK, It works ! Configuration was bad ;) and more specifically the line:

LPC_TIM0->TCR = 0x00000002;
must be followed by
LPC_TIM0->TCR &= 0 << 1;

As I said, timer didn't enetr to interrupt but now everything is OK.

Read-Only
Author
Some Body
Posted
3-May-2012 12:08 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Your original post does not even show

TCR = 0x00000002

and if you follow it with

TCR &= 0 << 1

then you're writing 0 to TCR so why write 2 in the first place?

Read-Only
Author
Damian doles
Posted
3-May-2012 12:13 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Generally I wrote the same program with timer and interrupt on assembler and there I had loading by LDR 0x00000002 value...I don't know why the same configuration on asm is OK and after moving to C language it dido't work...

Read-Only
Author
jm hamir
Posted
3-May-2012 12:16 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

you said@

...I don't know why the same configuration on asm is OK and after moving to C language it dido't work...

bug in compiler???????????

Read-Only
Author
Per Westermark
Posted
3-May-2012 12:20 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

"bug in compiler???????????"

Skip the many ???? - they are not needed.

The posted code was buggy - no need to suggest a bug in the compiler when the source code has errors.

Read-Only
Author
Damian doles
Posted
3-May-2012 12:23 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

I've just checked one thing - &= operator and in all it doesn't necessary. There could be common "=" and it works too.

Compiler ? Timer on asm was written on Keil, but now I'm writing on LPCXpresso ;) I don't think so about compiler...it's about the bits.

Read-Only
Author
Per Westermark
Posted
3-May-2012 12:19 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Of course you need to care about the frequencies for the 17xx chip too.

By the way - why are you so totally against using comments?

int main(void)
{
    LPC_GPIO2->FIODIR |= 1 << 29;  <=== what magic is connected to P0.29 that requires pin as
                                        output?
    LPC_SC->PCONP |= 1 << 1;       <=== what device is powered on by this? why not write the code
                                        so it's possible to know without access to the user manual?
    LPC_SC->PCLKSEL0 |= 1 << 3;    <=== why not tell that you play with peripherial clock for
                                        timer0?
                                        Why not tell what PCLK you set compared to CCLK?
    LPC_TIM0->TCR |= 0x00000002;   <=== why not tell that you reset the counter?
    LPC_TIM0->MCR |= 0x00000003;   <=== why not tell you want interrupt + reset?
    LPC_TIM0->MR0 |= 6000000;      <=== why no formula to tell exactly what interrupt frequency
                                        this represents?
    NVIC_EnableIRQ(TIMER0_IRQn);
    LPC_TIM0->TCR |= 0x00000001;   <=== does this line do what you think it does?
    while(1){}
    return 0 ;
}


Another thing - what is your view about the sentence "The
counters remain reset until TCR[1] is returned to zero."?

Read-Only
Author
Damian doles
Posted
3-May-2012 22:50 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Usually I write comments at the end of writing some code. I have other problem / idea. I though that maybe try to learn and use ADC converter and...I have some idea. I want to use a potentiometer which is connect to AD0.2 pin on my board and try to change frequency of toggling. If I write other value to the MR0 register I change frequency, for example value 12000000 = toggling with 1Hz frequency, and I can change as I want. I can do it by potentiometer...It was easy when I wrote the same on AVR, but there...I don't know how can i do it. I've just written a code but of course it doesn't work. Sometimes when I change position of potentiometer, the LED suddenly toggle several times and stop... I understand that ADDRx register store conversion result ? On AVR, there is stright and "direct" acess to register with data conversion...

/*
===============================================================================
 Name        : main.c
 Author      :
 Version     :
 Copyright   : Copyright (C)
 Description : main definition
===============================================================================
*/

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

// TODO: insert other include files here

// TODO: insert other definitions and declarations here
int timer_config(void);
int adc_config(void);
float i;
int main(void)
{
        LPC_GPIO2->FIODIR = 0x000000FF;
        timer_config();
        adc_config();
        while(1)
        {
                i = LPC_ADC->ADDR0;
                LPC_TIM0->MR0 = i;
        }
        return 0 ;
}

void TIMER0_IRQHandler(void)
{
        LPC_GPIO2->FIOPIN ^= 0x000000FF;
    LPC_TIM0->IR |= 1 << 0;
    return;
}

int timer_config(void)
{
        LPC_SC->PCONP |= 1 << 1;
        LPC_TIM0->TCR = 0 << 1;
        LPC_TIM0->MCR = 0x00000003;
        //LPC_TIM0->MR0 = 12000000; //frequency of toggling
        NVIC_EnableIRQ(TIMER0_IRQn); //enable interrupts
        LPC_TIM0->TCR |= 0x00000001; //start timer0
    return 0;
}

int adc_config(void)
{
        LPC_SC->PCONP |= 1 << 12; // power for adc
        LPC_PINCON->PINSEL1 |= (0 << 19) | (1 << 18); //activate adc function on pin P0.25
        //LPC_SC->PCLKSEL0 |= (0 << 25) | (0 << 24); //clock for adc
        LPC_ADC->ADCR |= (1 << 2) | (1 << 16) | (1 << 21);
        return 0;
}

Read-Only
Author
Per Westermark
Posted
4-May-2012 00:00 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

"Usually I write comments at the end of writing some code."
Usually, that is a very bad idea. That means that when you try to get your code working, you don't have the advantage of your comments - so every single line will look very hostile. Especially since you like to use magic numbers, instead of creating enum constants with suitable names.

Think what it would mean if you instead did:

enum {
    PCONP_TIM0 = 1,
    PCONP_ADC = 12,
};

LPC_SC->PCONP |= 1 << PCONP_TIM0;
...


LPC_SC->PCONP |= 1 << PCONP_ADC;


Suddenly, you don't need a comment to know that the assign turns on the timer0 peripherial. Compare that to your current code:

LPC_SC->PCONP |= 1 << 1;

Maybe you just like your code to be hard to read? Or you find that with good comments, you get the code to work too quickly?

"It was easy when I wrote the same on AVR, [...]"
Note that the match registers for this processor are 32 bits long. I bet your AVR chip didn't have 32-bit match registers?

So what happens if the current timer value is 1 million, and the match register is at 1.2 millions and you decide to rewrite the value 0.8 million to the match register?

The timer match register checks for an exact match - so if you change the value from above current value to below current value, you miss that exact match. It can then be up to four billion (2^32) ticks until the timer turns around and once more reaches the match register value.

At 12 MHz (which you haven't explicitly written but can be implied from your use of hard-coded magic values) it can then take up to 2^32 / 12E6 seconds (about 358 seconds) until you get the next match.

So what do we learn from this? Don't just change the MR0 register while the timer is running. Either restart the timer, or do something more advanced. Like setting the new value to MR1 and activate both MR0 and MR1 matches. Then on next match interrupt, switch back to using a single match register again. With two match registers enabled, you know that the timer will not step over both - it will hit either the old or the new match register (depending on if you increased or decreased the intended period) once after a change.

Or change MR0 and then manually sample the current timer and see if it have missed MR0 - in that case perform a manual reset of the timer.

Read-Only
Author
Damian doles
Posted
4-May-2012 13:31 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

I see, that it would be better for me when I learn ADC first without timer. But it's difficult for me. I have many habits from AVR...for example, there I can read direct ADC register or ADCH and ADCL. There I see that, can't do it the same way. Tell me, converter has 12 bits ? But registers which hold converted data ? Max value on this registers could be 4096 ? Of course for channels registers - ADDR0 for example. I don't know how can i read data. Could you tell me, I understand the burst mode it's the same as free running mode on AVR ?

Read-Only
Author
Per Westermark
Posted
4-May-2012 14:30 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Of course you can read directly from the ADC.

But you have to either start a single conversion, and then check the ADC status to find out when the conversion is done, before you pick up the result. Or you can use interrupts and have continuous conversions.

But the main issue I mentioned was that you can't just modify the match register of the timer while the timer is running - if you decrease the value of the match register to a value lower than the current timer counter value, the timer will have to count all the way to 2^32 and restart at zero before that match register can give a match. It doesn't matter if you involve an ADC or not - the timer don't care what reason you have to change the match register. All it cares about is that it ticks the timer and on every tick performs an exact compare with the match register.

Which register holds the 12 bits of converted data? That is extremely well described in the user manual for the processor. If you are stuck at so simple a task as figuring out where the conversion result is, you do really need to read that ADC chapter in the manual. Not many pages. And very well written.

12 bits represents 4096 steps. But that would normally be the values 0 to 4095 - a maximum value of 4096 would require the ADC to span more than 12 bits.

The data sheet very clearly explains the BURST bit. The SEL field controls which of the ADC inputs that should be processed before the ADC restarts next set of conversions.

Read-Only
Author
Damian doles
Posted
4-May-2012 15:53 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Now using timer and adc is unreal, so at first I want to learn how to use adc and read his registers...So I'm writing code for switching diodes. Depending on value of the data register I lit the LED, but of course it doesn't work :) What a surprise... LEDs don't react to the position of potentiometer. Only one LED is on. I want to make continuous conversion with adc interrupt, as the manual advises.

/*
===============================================================================
 Name        : main.c
 Author      :
 Version     :
 Copyright   : Copyright (C)
 Description : main definition
===============================================================================
*/

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

// TODO: insert other include files here

// TODO: insert other definitions and declarations here
int adc_config(void);
volatile int i;
int main(void) {

        LPC_GPIO2->FIODIR = 0x000000FF;
        LPC_GPIO2->FIOCLR = 0x000000FF;
        adc_config();

        while(1)
        {
                if((i >= 0) & (i < 512))                      LPC_GPIO2->FIOSET = 0x00000001; //1 led
                else if((i > 512) & (i < 1024))       LPC_GPIO2->FIOSET = 0x00000003; //2 leds
                else if((i > 1024) & (i < 1536))      LPC_GPIO2->FIOSET = 0x00000007; //3 leds
                else if((i > 1536) & (i < 2048))      LPC_GPIO2->FIOSET = 0x0000000F; //4 leds
                else if((i > 2048) & (i < 2560))      LPC_GPIO2->FIOSET = 0x0000001F; //5 leds
                else if((i > 2560) & (i < 3072))      LPC_GPIO2->FIOSET = 0x0000003F; //6 leds
                else if((i > 3072) & (i < 3584))      LPC_GPIO2->FIOSET = 0x0000007F; //7 leds
                else if((i > 3584) & (i <= 4095))     LPC_GPIO2->FIOSET = 0x000000FF; //8 leds
        }
        return 0 ;
}

int adc_config(void)
{
        LPC_GPIO1->FIODIR = 0 << 31;
        LPC_SC->PCONP |= 1 << 12; // power for adc
        LPC_PINCON->PINSEL1 |= (1 << 30) | (1 << 31); //activate adc function on pin P1.31
        LPC_SC->PCLKSEL0 |= 3; //clock for ADC - adcclk = pclk/4 = 3Mhz
        LPC_ADC->ADCR |= (1 << 5) | (1 << 21); // bit for ADC0.5 input and PDN mode is operational
        LPC_ADC->ADINTEN = 1 << 0; //enable interrupts for channel 0
        NVIC_EnableIRQ(ADC_IRQn);
        return 0;
}

void ADC_IRQHandler(void)
{
        LPC_ADC->ADCR |= 1 << 24; //start conversion
        while(!(LPC_ADC->ADGDR & (1 << 31))); // wait for completion of coversion
        i = (LPC_ADC->ADGDR >> 4) & 0x0FFF;

}

Read-Only
Author
Per Westermark
Posted
4-May-2012 16:21 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Exactly how do you think your code should work?

When you get an interrupt, you start the conversion. But how can you get an interrupt if you haven't started a conversion?

And you never ever want the ADC interrupt to wait for a full conversion - at that time, you have the processor 100% locked up in a busy loop.

Haven't you already realized that you do not (!) need any ADC interrupt to handle the ADC? It's enough to manually start a conversion, and then in the main loop poll the state of the ADC until the conversion is done. Then, you can read out the converted value. Then you can start a new conversion.

Right now, you are busy with a trial-and-error methodology. It is well known that such a strategy have a very bad convergence speed. It is many times better to first think, and then write code. Then test the code and compare what happens with what you expected to happen. Then figure out potential reasons for the difference between idea and real worl, and apply fixes.

By the way - can you explain to us how you thought a global variable named "i" would be good for storing the result of the latest ADC conversion? You think "i" sends out a message to the reader making it obvious that it represents the current ADC sample?


                if((i >= 0) & (i < 512))                      LPC_GPIO2->FIOSET = 0x00000001; //1 led
                else if((i > 512) & (i < 1024))       LPC_GPIO2->FIOSET = 0x00000003; //2 leds
                else if((i > 1024) & (i < 1536))      LPC_GPIO2->FIOSET = 0x00000007; //3 leds
                else if((i > 1536) & (i < 2048))      LPC_GPIO2->FIOSET = 0x0000000F; //4 leds
                else if((i > 2048) & (i < 2560))      LPC_GPIO2->FIOSET = 0x0000001F; //5 leds
                else if((i > 2560) & (i < 3072))      LPC_GPIO2->FIOSET = 0x0000003F; //6 leds
                else if((i > 3072) & (i < 3584))      LPC_GPIO2->FIOSET = 0x0000007F; //7 leds
                else if((i > 3584) & (i <= 4095))     LPC_GPIO2->FIOSET = 0x000000FF; //8 leds


For some reason, I feel I have seen the above code before. Is it yours, or a cut and paste?

Can you tell me what your code should do for the value 1024? You have one test that only catches values below 1024 and then the rest of the tests looks for values larger than 1024. But what about 1024?

What about 1536? Or 2048? Or 2560? Or 3072? or 3548?. And how often will you have a value larger than 4095 since your last test checks if i is below or equal to 4095?

Don't you think that it is enough with one (1) comparison for every range?

if (i < 512) LPC_GPIO2->FIOSET = 1;
else if (i < 1024) LPC_GPIO2->FIOSET = 3;
else if (i < 1536) LPC_GPIO2->FIOSET = 7;
...


You think that makes the code too simple?

Next thing - what about dividing i with 512 like:

switch ((i >> 9) & 7) {
    case 0: LPC_GPIO2->FIOSET = 1; break;
    case 1: LPC_GPIO2->FIOSET = 3; break;
    case 2: LPC_GPIO2->FIOSET = 7; break;
    ...
}


or even add a lookup table:

const unsigned set_patterns[] = {
    0x01, 0x03, 0x07, 0x0f,
    0x1f, 0x3f, 0x7f, 0xff
};
LPC_GPIO2->FIOSET = set_patterns[(i >> 9) & 7];

But another thing - you have code that sets GPIO pins high. Where do you have any code that clears the other GPIO pins? Don't you feel that your code must handle both turning on and turning off of the diodes? Didn't you consider this when you did sit and sketched down the algorithm to use?

Read-Only
Author
Damian doles
Posted
4-May-2012 20:16 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

OK, I've just corrected my code and now works better. I know how to read ADC register, I make it in main loop but the logic of LEDs is still bad. It works only one way - you talked about it. The LEDs don't turn off but turning on works very well. Any snippet ? And small prompt ? Thank you for all answers and advices... :)

My code:

/*
===============================================================================
 Name        : main.c
 Author      :
 Version     :
 Copyright   : Copyright (C)
 Description : main definition
===============================================================================
*/

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

// TODO: insert other include files here
#include <math.h>
#include <stdio.h>
// TODO: insert other definitions and declarations here
int adc_config(void);
uint16_t prevVal,curVal,errCnt;
uint16_t readADC();

int main(void)
{
        int i;
        errCnt = 0;
        const unsigned int led_state[] = {
                                    0x01, 0x03, 0x07, 0x0f,
                                    0x1f, 0x3f, 0x7f, 0xff
                                };
        LPC_GPIO2->FIODIR = 0x000000FF;
        adc_config();

        while(1)
        {
                i = readADC();
                //printf("i = %d",i);
                LPC_GPIO2->FIOSET = led_state[(i >> 9) & 7];
        }
        return 0 ;
}

int adc_config(void)
{
        LPC_SC->PCONP |= 1 << 12; // power for adc
        LPC_ADC->ADCR |= (1 << 5) | (1 << 21); // bit for ADC0.5 input and PDN mode is operational
        LPC_SC->PCLKSEL0 |= 0; //clock for ADC - adcclk = pclk/4 = 3Mhz
        LPC_PINCON->PINSEL3 |= (1 << 30) | (1 << 31); //activate adc function on pin P1.31
        //LPC_ADC->ADINTEN = 1 << 8; //enable interrupts for channel 0
        //NVIC_EnableIRQ(ADC_IRQn);
}

/*void ADC_IRQHandler(void)
{

}*/

uint16_t readADC()
{
        LPC_ADC->ADCR |= 1 << 24;// start conversion.
        while(!(LPC_ADC->ADGDR & (1 << 31))); // wait for completion of coversion
        curVal = (LPC_ADC->ADGDR >> 4) & 0x0fff;

        if(errCnt >= 3)
        {
                prevVal = curVal;
        }

        if(abs((curVal - prevVal) > 20))
        {
                curVal = prevVal;
                errCnt++;
        }
        else
        {
                errCnt = 0;
        }

        prevVal = curVal;
        return curVal;
}


Read-Only
Author
Per Westermark
Posted
4-May-2012 21:07 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

Not sure if you actually used the processor user manual, or if you just looked at any blinky application.

But when you did find the FIOSET register - didn't you then also find any FIOCLR register?

By the way - I see you wrote a hysterese function to reduce problems with noise from the potentiometer. But why did you select a variable name errCnt? Where is the error?

Another thing - why is curVal a global variable? You give it a value the first thing you do in the readADC() function - and then you return the value of curVal - so no need to retain the value of it until next call...

Read-Only
Author
Damian doles
Posted
4-May-2012 21:32 GMT
Toolset
ARM
New! RE: LPC1768 problem with timer0

OK, I did it. Now it works good :) LEDs turns off too :) I only added one line:

LPC_GPIO2->FIOCLR =~(led_state[(i >> 9) & 7]);

When I negate table's value and send it to FIOCLR register LEDs turns off.
I knew about this register, but as I said I have many habits from AVR...I forgot that, if I want to turn off LEDs I must write "1" to the bits of FIOCLR.

Read-Only
Author
Andrew Neil
Posted
4-May-2012 22:24 GMT
Toolset
ARM
New! RE: I have many habits from AVR

But AVRs also have SET and CLEAR registers for their port bits - don't they?!

Read-Only
Author
Alex Eramian
Posted
8-May-2012 22:56 GMT
Toolset
ARM
New! RE: I have many habits from AVR

In a previous post he mentioned ATmega32 so no, there is no set/clr.
There is only a direction , port and input read register (DDR, PORT,PIN)

Alex

Next Thread | Thread List | Previous Thread Start a Thread | Settings

Keil logo

Arm logo
Important information

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies.

Change Settings