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; } }
I have no experience of this micro but perhaps the following line should be:
LPC_TIM0->MR0 |= 0x6000000;
instead of:
LPC_TIM0->MR0 |= 6000000;
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?
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.
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?
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...
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???????????
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."?
"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.
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.
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; }
"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.
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 ?
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.
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; }
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?