Author Topic: volatile regs  (Read 12899 times)

Offline johnr

  • Jr. Member
  • **
  • Posts: 91
    • View Profile
volatile regs
« 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



Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: volatile regs
« Reply #1 on: September 25, 2007, 11:29:16 PM »
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:

Code: [Select]
// 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?

Offline johnr

  • Jr. Member
  • **
  • Posts: 91
    • View Profile
Re: volatile regs
« Reply #2 on: September 26, 2007, 07:13:50 PM »
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


}


Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: volatile regs
« Reply #3 on: September 26, 2007, 08:03:17 PM »
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:

Code: [Select]
#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)

Code: [Select]
;  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:

Code: [Select]
;  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



Offline johnr

  • Jr. Member
  • **
  • Posts: 91
    • View Profile
Re: volatile regs
« Reply #4 on: September 26, 2007, 09:57:09 PM »
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

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: volatile regs
« Reply #5 on: September 26, 2007, 10:59:03 PM »
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



Offline johnr

  • Jr. Member
  • **
  • Posts: 91
    • View Profile
Re: volatile regs
« Reply #6 on: September 27, 2007, 03:17:26 PM »
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



Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: volatile regs
« Reply #7 on: September 27, 2007, 04:52:14 PM »
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