| Details |
Message |
|
Read-Only
Author Damian doles
Posted 3-May-2012 11:23 GMT
Toolset ARM
|
 LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Some Body
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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Some Body
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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
jm hamir
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
|
 RE: LPC1768 problem with timer0
Per Westermark
"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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Per Westermark
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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Per Westermark
"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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Per Westermark
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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Per Westermark
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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: LPC1768 problem with timer0
Per Westermark
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
|
 RE: LPC1768 problem with timer0
Damian doles
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
|
 RE: I have many habits from AVR
Andrew Neil
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
|
 RE: I have many habits from AVR
Alex Eramian
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
|
|