µTasker Forum
µTasker Forum => NXPTM M522XX, KINETIS and i.MX RT => Topic started by: johnr on September 25, 2007, 09:13:30 PM
-
Hi,
I started setting up the ADC and I'm using the ini code
genertaed by the Freescale init software that allows you to
plug in some params and it generates the C code to ini various
peripherals. While debugging in mixed view mode I noticed the
line
// Wait for converter A power up to complete
while (MCF_ADC_POWER & MCF_ADC_POWER_PSTS0)
would never fall through because the compile was not re reading the
MCF_ADC_POWER reg. I changed the definition to make it volitale
#define ADC_POWER *(volatile unsigned short *)(ADC_ADD + 0x52) // ADC Power Control register
but it still wouldn't work. I had to change the CodeWarrior global
optimization from 4, generate smallest code to 2 to get it to
work. I had to remap the M5223x.h files to a new file which uses the
reg naming conventions used by the FreeScale init utility ie
#define MCF_ADC_POWER ADC_POWER etc
I see where various regs in M5223x.h are using the volatile keyword
and it works with full optimization. Why do I have to lower it
to get the ADC loop to work ?
Thanks,
John
-
Hi John
Registers which can change without SW influence are declared as volatile (there may be some which are not, but should really be..) and typically the problem you have seen is experienced very quickly due to the optimiser removing the wait loop.
Personally I have never had a problem with the volatile define since it has always solved the issue. I can't explain why it doesn't (or didn't with higher levels of optimisation) work in your case. If you think that it is a compiler issue I suggest you mention this on the CW Coldifre forum on the links page - normally there are quick responses there.
The uTasker project doesn't include ADC demo code (at the moment) but the following has been used successfully. It is quite simple - configuring and reading 8 values:
// Initialisation
ADC_CTRL2 = SLOWEST_ADC_CLOCK; // Configure ADC and start first conversion
ADC_POWER &= ~(PD0 | PD1 | PD2); // Power up the ADC circuits
PANPAR = 0xff; // Enable all ports as ADC inputs
ADC_CTRL1 = (ALL_SINGLE_ENDED | ONCE_SEQUENCIAL | START0); // Start a scan of 8 inputs sequentially
... later, or on a regular basis
// Read 8 values and copy to an array
#define ADC_CHANNELS 8
static unsigned short usADC_result[ADC_CHANNELS];
while (ADC_ADSTAT & (CIP0 | CIP1)) {} // Wait for any active ADC conversion to complete
usADC_result[0] = ADC_ADRSLT0; // Save the present ADC values (note the order!!)
usADC_result[1] = ADC_ADRSLT1;
usADC_result[2] = ADC_ADRSLT2;
usADC_result[3] = ADC_ADRSLT3;
usADC_result[4] = ADC_ADRSLT7;
usADC_result[5] = ADC_ADRSLT6;
usADC_result[6] = ADC_ADRSLT5;
usADC_result[7] = ADC_ADRSLT4;
I think that the power up was done in an initialisation routine (very quickly after start up) and the rest in a task after some delay so there was (probably) no need to wait for the module to power up.
I must admit to not having used the Freescale init software, but rather prefer to get to know the registers as well as possible (the hard way...). Some times I sneek a peek at some demo driver code.
Regards
Mark
I believe that several others have used the ADC (although there is no demo in the project to build on) so possibly someone else has similar experiences and can offer a tip?
-
Mark, I posted up the question on the Freescale site and got the following reply which didn't work:
I modified it as follows, but it still didn't work even when the global optimization was lowered to 2.
#pragma opt_loop_invariants off
// Wait for converter A power up to complete
while (MCF_ADC_POWER & MCF_ADC_POWER_PSTS0)
;
0001274C: 4A80 tst.l d0
0001274E: 66FC bne.s fnInit_adc+0x44 (0x1274c); 0x0001274c
// Wait for converter B power up to complete
while (MCF_ADC_POWER & MCF_ADC_POWER_PSTS1)
#pragma opt_loop_invariants on
return;
00012750: 028100000800 andi.l #0x800,d1
00012756: 66000104 bne.w fnInit_adc+0x14a (0x1285c); 0x0001285c
Here's a copy of my ADC task that ini's the ADC and reads it every
second when the task wakes up. It works with the M52233DEMO
using the pot on AN0. It operates in the "Triggered parallel mode", scanning all 8 inputs when issued a START0 then stopping.
void fnADC(TTASKTABLE *ptrTaskTable) // ADC called regularly
{
MCF_ADC_CTRL1 = START0; // Start the scan
while (ADC_ADSTAT & (CIP0 | CIP1)) {} // Wait for any active ADC conversion to complete
usADC_result[0] = ADC_ADRSLT0 >> 3; // Save the present ADC values (note the order!!)
usADC_result[1] = ADC_ADRSLT1 >> 3;
usADC_result[2] = ADC_ADRSLT2 >> 3;
usADC_result[3] = ADC_ADRSLT3 >> 3;
usADC_result[4] = ADC_ADRSLT4 >> 3;
usADC_result[5] = ADC_ADRSLT5 >> 3;
usADC_result[6] = ADC_ADRSLT6 >> 3;
usADC_result[7] = ADC_ADRSLT7 >> 3;
fnDebugMsg("A0=");
fnDebugDec(usADC_result[0],0,0);
fnDebugMsg(" A1=");
fnDebugDec(usADC_result[1],0,0);
fnDebugMsg(" A2=");
fnDebugDec(usADC_result[2],0,0);
fnDebugMsg(" A3=");
fnDebugDec(usADC_result[3],0,0);
fnDebugMsg(" A4=");
fnDebugDec(usADC_result[4],0,0);
fnDebugMsg(" A5=");
fnDebugDec(usADC_result[5],0,0);
fnDebugMsg(" A6=");
fnDebugDec(usADC_result[6],0,0);
fnDebugMsg(" A7=");
fnDebugDec(usADC_result[7],0,0);
fnDebugMsg("\r\n");
}
/*********************************************************************
* init_adc - Analog-to-Digital Converter (ADC) *
**********************************************************************/
void fnInit_adc (void)
{
// Scan mode = Triggered parallel, converters A and B run simultaneously
// ADC clock frequency = 5.00 MHz
// Voltage reference supplied by VDDA and VSSA
// All ADC interrupts disabled
//
// Sample list for converter A:
// Sample 0 : AN0
// Low limit = $000, High limit = $FFF, Offset = $000
// Sample 1 : AN1
// Low limit = $000, High limit = $FFF, Offset = $000
// Sample 2 : AN2
// Low limit = $000, High limit = $FFF, Offset = $000
// Sample 3 : AN3
// Low limit = $000, High limit = $FFF, Offset = $000
//
// Sample list for converter B:
// Sample 4 : AN4
// Low limit = $000, High limit = $FFF, Offset = $000
// Sample 5 : AN5
// Low limit = $000, High limit = $FFF, Offset = $000
// Sample 6 : AN6
// Low limit = $000, High limit = $FFF, Offset = $000
// Sample 7 : AN7
// Low limit = $000, High limit = $FFF, Offset = $000
// Initialise LOLIM, HILIM and OFFST registers of enabled channels:
MCF_ADC_ADLLMT0 = 0;
MCF_ADC_ADHLMT0 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS0 = 0;
MCF_ADC_ADLLMT1 = 0;
MCF_ADC_ADHLMT1 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS1 = 0;
MCF_ADC_ADLLMT2 = 0;
MCF_ADC_ADHLMT2 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS2 = 0;
MCF_ADC_ADLLMT3 = 0;
MCF_ADC_ADHLMT3 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS3 = 0;
MCF_ADC_ADLLMT4 = 0;
MCF_ADC_ADHLMT4 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS4 = 0;
MCF_ADC_ADLLMT5 = 0;
MCF_ADC_ADHLMT5 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS5 = 0;
MCF_ADC_ADLLMT6 = 0;
MCF_ADC_ADHLMT6 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS6 = 0;
MCF_ADC_ADLLMT7 = 0;
MCF_ADC_ADHLMT7 = MCF_ADC_ADHLMT_HLMT(0xfff);
MCF_ADC_ADOFS7 = 0;
// Initialise ADC
MCF_ADC_ADZCC = 0;
MCF_ADC_ADLST1 = MCF_ADC_ADLST1_SAMPLE3(0x3) |
MCF_ADC_ADLST1_SAMPLE2(0x2) |
MCF_ADC_ADLST1_SAMPLE1(0x1);
MCF_ADC_ADLST2 = MCF_ADC_ADLST2_SAMPLE7(0x7) |
MCF_ADC_ADLST2_SAMPLE6(0x6) |
MCF_ADC_ADLST2_SAMPLE5(0x5) |
MCF_ADC_ADLST2_SAMPLE4(0x4);
MCF_ADC_ADSDIS = 0;
MCF_ADC_CAL = 0;
MCF_ADC_CTRL1 = MCF_ADC_CTRL1_STOP0 |
MCF_ADC_CTRL1_SMODE(0x5);
MCF_ADC_CTRL2 = MCF_ADC_CTRL2_STOP1 |
MCF_ADC_CTRL2_SIMULT |
MCF_ADC_CTRL2_DIV(0x2);
// Power up ADC converter(s) in use
MCF_ADC_POWER = MCF_ADC_POWER_PUDELAY(0xd) |
MCF_ADC_POWER_PD2;
// Wait for converter A power up to complete
while (MCF_ADC_POWER & MCF_ADC_POWER_PSTS0)
;
// Wait for converter B power up to complete
while (MCF_ADC_POWER & MCF_ADC_POWER_PSTS1)
;
PANPAR = 0xff; // Enable all ports as ADC inputs
}
-
Hi John
I think that you could start the scanning when leaving the periodic task and then the result will always be ready on the next entry (start first one in the initialisation routine). This would avoid any delay waiting for the result to become available on each task entry.
I also tried the following:
1. In M5223X. h - I extended the power controller register bit defines:
#define ADC_POWER *(volatile unsigned short *)(ADC_ADD + 0x52) // ADC Power Control register {5}
#define PD0 0x0001 // Circuit A power down
#define PD1 0x0002 // Circuit B power down
#define PD2 0x0004 // Reference circuit power down
#define ADP 0x0008 // Auto-power down mode bit
#define PSTS0 0x0400 // Converter A Power Status bit
#define PSTS1 0x0800 // Converter B Power Status bit
#define PSTS2 0x1000 // Reference circuit A Power Status bit
#define ASB 0x8000 // Auto Standby Bit
2. Then I disassembled the following code using CW6.3 build 14 (I think the same as yours).
ADC_POWER = 0;
while (ADC_POWER & PD0) {}
The result was as follows with volatile declaration of teh register. Optimisation set as max (smallest code)
; 434: ADC_POWER = 0;
0x00000000 0x7000 moveq #0,d0
0x00000002 0x33C040190052 move.w d0,0x40190052
;
; 435: while (ADC_POWER & PSTS0) {}
; 436:
;
0x00000008 0x323940190052 move.w 0x40190052,d1
0x0000000E 0x7000 moveq #0,d0
0x00000010 0x3001 move.w d1,d0
0x00000012 0x028000000400 andi.l #0x400,d0 ; '....'
0x00000018 0x66EE bne.s *-16
When I remove the volatile declaration I get:
; 434: ADC_POWER = 0;
0x00000000 0x427940190052 clr.w 0x40190052
0x00000006 0x7000 moveq #0,d0
0x00000008 0x303940190052 move.w 0x40190052,d0
0x0000000E 0x028000000400 andi.l #0x400,d0 ; '....'
;
; 435: while (ADC_POWER & PSTS0) {}
; 436:
;
0x00000014 0x4A80 tst.l d0
0x00000016 0x66FC bne.s *-2
The first one will wait correctly until the bit is clear and the second will loop endlessly if the first read shows that it is still set. This is about what I would expect.
(Note that you can select the file of interest, right click and command disassemble to get this listing).
Is your assembler code significantly different from this?
Regards
Mark
-
Mark, I changed M5223x.h also the same as yours and the compiler
doesn't re read the ADC_POWER reg in the while loop for some reason
unless I set the global optimization down to 2
#define ADC_POWER *(volatile unsigned short *)(ADC_ADD + 0x52) // ADC Power Control register
while (MCF_ADC_POWER & MCF_ADC_POWER_PSTS0)
;
0001274C: 4A80 tst.l d0
0001274E: 66FC bne.s fnInit_adc+0x44 (0x1274c); 0x0001274c
John
-
John
Since we seem to be using the same compiler I can only think of 2 explanations.
1. I am not using the same level of optimisation (it must be <= 2), but the slider in the setting is definitiely on 4.
If I test without volatile the optimisation removes the loop. If I set the slider to 2 it doesn't remove the loop (same result as you have). Therefore I don't think that this is the cause.
2. The code which you are compiling is not really using volatile (for some reason).
You could do the following to be absolutely sure.
In CW select the file in the file manager and right click. Then perform "Pre-process".
The file which is displayed shows the real input to the compiler. Search it for the routine and you will be able to find the input which is being compiled.
Here is mine:
ADC_POWER = 0;
while (ADC_POWER & PSTS0) {}
gives
*(volatile unsigned short *)(((0x40000000) + 0x190000) + 0x52) = 0;
while (*(volatile unsigned short *)(((0x40000000) + 0x190000) + 0x52) & 0x0400 ) {}
Here the defines have been entered and it is certain that the correct define is being used.
If you do find the volatile in place, it must be the compiler (slightly different version??).
If you find the volatile not to be there you may find that you are editing a header which is not used in the project or some brackets or syntax is not exactly as you expected.
There must be an explanation for this somewhere...
Regards
Mark
-
Mark, you are correct. I ran the pre-processor on my adc.c and found the volatile
keyword missing from the ADC_POWER reg in m5223x.h. When I brought it up in the
editor I noticed that instead of pointing to my working copy, it pointed to the
original source tree. It was the only file in the project that did not map to my working
tree. When I unzipped the Service pack 5 code, I kept the originals intact and copied
them to a CW project location and made my change there. When I was having a problem where m5223x.h got deactivated from CW by mistake, I deleted it and then
re-added it from the original location instead of the working one :(
It now works as expected. Thanks for the help. I am learning CW
John
-
Hi John
I am glad we could clear this one up - you always know that there must be an explanation but sometimes think that you are going mad when you can't come up with it.
The pre-compile function in CW is sometimes very useful to see what the compiler is really seeing - and it can sometimes explain otherwise quite unusual things!
Regards
Mark