EmbeddedRelated.com
Forums
Memfault Beyond the Launch

adc - simple adc sample doesn't work

Started by klemen_dovrtel November 8, 2007
I tested a simple adc conversion example, but it doesn't work. The adc
conversion never ends. It gets stuck in the while loop waiting for A/D
conversion to end. I added a simple debug message to get the regVal
value in the while loop - regVal=0x00010008. Any idea what could be
wrong?

#define ADC_OFFSET 0x10
#define ADC_INDEX 4
#define ADC_DONE 0x80000000
#define ADC_OVERRUN 0x40000000
#define ADC_ADINT 0x00010000

uint32_t ADCInit( uint32_t ADC_Clk )
{
PINSEL0=PINSEL0|BIT(28);// P0.30 -> AD0.3

AD0CR=(0x01<<0)| // SEL=1,select channel 0, 1 to 4 on ADC0
((PCLK/ADC_Clk-1 )<<8)| // CLKDIV = Fpclk / 1000000 - 1
(0<<16)| // BURST = 0, no BURST, software controlled
(0<<17)| // CLKS = 0, 11 clocks/10 bits
(1<<21)| // PDN = 1, normal operation
(0<<22)| // TEST1:0 = 00
(0<<24)| // START = 0 A/D conversion stops
(0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D conversion)

AD1CR=(0x01<<0)| // SEL=1,select channel 0, 0 to 7 on ADC1
((PCLK/ADC_Clk-1)<<8)| // CLKDIV = Fpclk / 1000000 - 1
(0<< 16 )| // BURST = 0, no BURST, software controlled
(0<<17)| // CLKS = 0, 11 clocks/10 bits
(1<<21)| // PDN = 1, normal operation
(0<<22)| // TEST1:0 = 00
(0<<24)| // START = 0 A/D conversion stops
(0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D conversion)

return (TRUE);
}

uint32_t ADC0Read(uint8_t channelNum)
{
uint32_t regVal, ADC_Data;

if (channelNum>C_NUM) // channel number is 0 through 7
{
channelNum=0; // reset channel number to 0
}

AD0CR0CR&0xFFFFFF00;
AD0CR0CR|(1<<24)|(1<
while(1) // wait until end of A/D convert
{
regVal=*(volatile unsigned
long*)(ADC0+ADC_OFFSET+ADC_INDEX*channelNum); // read result of A/D
conversion
if(regVal&ADC_DONE) { break; }

}

AD0CR&=0xF8FFFFFF; // stop ADC now
if(regVal&ADC_OVERRUN) { return(0); } // save data when it's not
overrun. Otherwise, return zero
ADC_Data=(regVal>>6)&0x3FF;
return(ADC_Data); // return A/D conversion value
}

int main(void)
{
sysInit();
ADCInit(ADC_CLK);

uint32_t adcdata;
adcdataC0Read(3);
...

An Engineer's Guide to the LPC2100 Series

klemen_dovrtel :
>I tested a simple adc conversion example, but it doesn't work. The adc
>conversion never ends. It gets stuck in the while loop waiting for A/D
>conversion to end. I added a simple debug message to get the regVal
>value in the while loop - regVal=0x00010008. Any idea what could be
>wrong?

regVal isn't volatile?

Albert
--- In l..., al_bin@... wrote:
>
> klemen_dovrtel :
> >I tested a simple adc conversion example, but it doesn't work. The adc
> >conversion never ends. It gets stuck in the while loop waiting for A/D
> >conversion to end. I added a simple debug message to get the regVal
> >value in the while loop - regVal=0x00010008. Any idea what could be
> >wrong?
>
> regVal isn't volatile?
>
> Albert
>
>
The registers are defined like this. If i omit this volatile
attribute, this won't change much, except that if i'll change the
regVal inside the ISR, it won't work as it should. But i have no
interrupts here anyway.

#define REG_8 volatile unsigned char
#define REG16 volatile unsigned short
#define REG32 volatile unsigned long

#define ADC0 ((adcRegs_t *)0xE0034000)

#define AD0CR ADC0->cr /* Control Register */
#define AD0GDR ADC0->gdr /* Global Data Register */
#define ADGSR ADC0->gsr /* Global Start Register */
#define AD0INTEN ADC0->inten /* Interrupt Enable Register */
#define AD0DR0 ADC0->dr0 /* Channel 0 Data Register */
#define AD0DR1 ADC0->dr1 /* Channel 1 Data Register */
#define AD0DR2 ADC0->dr2 /* Channel 2 Data Register */
#define AD0DR3 ADC0->dr3 /* Channel 3 Data Register */
#define AD0DR4 ADC0->dr4 /* Channel 4 Data Register */
#define AD0DR5 ADC0->dr5 /* Channel 5 Data Register */
#define AD0DR6 ADC0->dr6 /* Channel 6 Data Register */
#define AD0DR7 ADC0->dr7 /* Channel 7 Data Register */
#define AD0STAT ADC0->stat /* A/D Status Register */
--- In l..., "klemen_dovrtel"
wrote:
>
> I tested a simple adc conversion example, but it doesn't work. The
adc
> conversion never ends. It gets stuck in the while loop waiting for
A/D
> conversion to end. I added a simple debug message to get the regVal
> value in the while loop - regVal=0x00010008. Any idea what could be
> wrong?
>
> #define ADC_OFFSET 0x10
> #define ADC_INDEX 4
> #define ADC_DONE 0x80000000
> #define ADC_OVERRUN 0x40000000
> #define ADC_ADINT 0x00010000
>
> uint32_t ADCInit( uint32_t ADC_Clk )
> {
> PINSEL0=PINSEL0|BIT(28);// P0.30 -> AD0.3
>
> AD0CR=(0x01<<0)| // SEL=1,select channel 0, 1 to 4 on ADC0
> ((PCLK/ADC_Clk-1 )<<8)| // CLKDIV = Fpclk / 1000000 - 1
> (0<<16)| // BURST = 0, no BURST, software controlled
> (0<<17)| // CLKS = 0, 11 clocks/10 bits
> (1<<21)| // PDN = 1, normal operation
> (0<<22)| // TEST1:0 = 00
> (0<<24)| // START = 0 A/D conversion stops
> (0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D conversion)
>
> AD1CR=(0x01<<0)| // SEL=1,select channel 0, 0 to 7 on ADC1
> ((PCLK/ADC_Clk-1)<<8)| // CLKDIV = Fpclk / 1000000 - 1
> (0<< 16 )| // BURST = 0, no BURST, software controlled
> (0<<17)| // CLKS = 0, 11 clocks/10 bits
> (1<<21)| // PDN = 1, normal operation
> (0<<22)| // TEST1:0 = 00
> (0<<24)| // START = 0 A/D conversion stops
> (0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D conversion)
>
> return (TRUE);
> }
>
> uint32_t ADC0Read(uint8_t channelNum)
> {
> uint32_t regVal, ADC_Data;
>
> if (channelNum>C_NUM) // channel number is 0 through 7
> {
> channelNum=0; // reset channel number to 0
> }
>
> AD0CR0CR&0xFFFFFF00;
> AD0CR0CR|(1<<24)|(1<
convert
>
> while(1) // wait until end of A/D convert
> {
> regVal=*(volatile unsigned
> long*)(ADC0+ADC_OFFSET+ADC_INDEX*channelNum); // read result of A/D
> conversion
> if(regVal&ADC_DONE) { break; }
>
> }
>
> AD0CR&=0xF8FFFFFF; // stop ADC now
> if(regVal&ADC_OVERRUN) { return(0); } // save data when it's
not
> overrun. Otherwise, return zero
> ADC_Data=(regVal>>6)&0x3FF;
> return(ADC_Data); // return A/D conversion value
> }
>
> int main(void)
> {
> sysInit();
> ADCInit(ADC_CLK);
>
> uint32_t adcdata;
> adcdataC0Read(3);
> ...
>

Try writing to the START bitfield in a separate write.
This way:
AD0CR0CR|(1< AD0CR0CR|(1<<24);

Regards
Zdravko Dimitrov
CompAC, Varna
Bulgaria
a...@vp.pl napisa:
>klemen_dovrtel :
>>I tested a simple adc conversion example, but it doesn't work. The adc
>>conversion never ends. It gets stuck in the while loop waiting for A/D
>>conversion to end. I added a simple debug message to get the regVal
>>value in the while loop - regVal=0x00010008. Any idea what could be
>>wrong?
>
>regVal isn't volatile?
>
Sorry, should be:

regVal isn't volatile value but their address.

Albert




--- In l..., "Zdravko" wrote:
>
> --- In l..., "klemen_dovrtel"
> wrote:
> >
> > I tested a simple adc conversion example, but it doesn't work. The
> adc
> > conversion never ends. It gets stuck in the while loop waiting for
> A/D
> > conversion to end. I added a simple debug message to get the regVal
> > value in the while loop - regVal=0x00010008. Any idea what could be
> > wrong?
> >
> >
> >
> > #define ADC_OFFSET 0x10
> > #define ADC_INDEX 4
> > #define ADC_DONE 0x80000000
> > #define ADC_OVERRUN 0x40000000
> > #define ADC_ADINT 0x00010000
> >
> >
> >
> > uint32_t ADCInit( uint32_t ADC_Clk )
> > {
> > PINSEL0=PINSEL0|BIT(28);// P0.30 -> AD0.3
> >
> > AD0CR=(0x01<<0)| // SEL=1,select channel 0, 1 to 4 on ADC0
> > ((PCLK/ADC_Clk-1 )<<8)| // CLKDIV = Fpclk / 1000000 - 1
> > (0<<16)| // BURST = 0, no BURST, software controlled
> > (0<<17)| // CLKS = 0, 11 clocks/10 bits
> > (1<<21)| // PDN = 1, normal operation
> > (0<<22)| // TEST1:0 = 00
> > (0<<24)| // START = 0 A/D conversion stops
> > (0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D conversion)
> >
> > AD1CR=(0x01<<0)| // SEL=1,select channel 0, 0 to 7 on ADC1
> > ((PCLK/ADC_Clk-1)<<8)| // CLKDIV = Fpclk / 1000000 - 1
> > (0<< 16 )| // BURST = 0, no BURST, software controlled
> > (0<<17)| // CLKS = 0, 11 clocks/10 bits
> > (1<<21)| // PDN = 1, normal operation
> > (0<<22)| // TEST1:0 = 00
> > (0<<24)| // START = 0 A/D conversion stops
> > (0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D conversion)
> >
> > return (TRUE);
> > }
> >
> >
> >
> > uint32_t ADC0Read(uint8_t channelNum)
> > {
> > uint32_t regVal, ADC_Data;
> >
> > if (channelNum>C_NUM) // channel number is 0 through 7
> > {
> > channelNum=0; // reset channel number to 0
> > }
> >
> > AD0CR0CR&0xFFFFFF00;
> > AD0CR0CR|(1<<24)|(1< > convert
> >
> > while(1) // wait until end of A/D convert
> > {
> > regVal=*(volatile unsigned
> > long*)(ADC0+ADC_OFFSET+ADC_INDEX*channelNum); // read result of A/D
> > conversion
> > if(regVal&ADC_DONE) { break; }
> >
> > }
> >
> > AD0CR&=0xF8FFFFFF; // stop ADC now
> > if(regVal&ADC_OVERRUN) { return(0); } // save data when it's
> not
> > overrun. Otherwise, return zero
> > ADC_Data=(regVal>>6)&0x3FF;
> > return(ADC_Data); // return A/D conversion value
> > }
> >
> > int main(void)
> > {
> > sysInit();
> > ADCInit(ADC_CLK);
> >
> > uint32_t adcdata;
> > adcdataC0Read(3);
> > ...
> > Try writing to the START bitfield in a separate write.
> This way:
> AD0CR0CR|(1< > AD0CR0CR|(1<<24);
>
> Regards
> Zdravko Dimitrov
> CompAC, Varna
> Bulgaria
>

I tried to select the channel number and start conversion in sequence
as suggested, but the result is the same. It also makes no difference
if i omit the volatile attribute.

BTW, i am using LPC2138/01
--- In l..., "klemen_dovrtel"
wrote:
>
> --- In l..., "Zdravko" wrote:
> >
> > --- In l...,
"klemen_dovrtel"
> > wrote:
> > >
> > > I tested a simple adc conversion example, but it doesn't work.
The
> > adc
> > > conversion never ends. It gets stuck in the while loop waiting
for
> > A/D
> > > conversion to end. I added a simple debug message to get the
regVal
> > > value in the while loop - regVal=0x00010008. Any idea what
could be
> > > wrong?
> > >
> > >
> > >
> > > #define ADC_OFFSET 0x10
> > > #define ADC_INDEX 4
> > > #define ADC_DONE 0x80000000
> > > #define ADC_OVERRUN 0x40000000
> > > #define ADC_ADINT 0x00010000
> > >
> > >
> > >
> > > uint32_t ADCInit( uint32_t ADC_Clk )
> > > {
> > > PINSEL0=PINSEL0|BIT(28);// P0.30 -> AD0.3
> > >
> > > AD0CR=(0x01<<0)| // SEL=1,select channel 0, 1 to 4 on ADC0
> > > ((PCLK/ADC_Clk-1 )<<8)| // CLKDIV = Fpclk / 1000000 - 1
> > > (0<<16)| // BURST = 0, no BURST, software controlled
> > > (0<<17)| // CLKS = 0, 11 clocks/10 bits
> > > (1<<21)| // PDN = 1, normal operation
> > > (0<<22)| // TEST1:0 = 00
> > > (0<<24)| // START = 0 A/D conversion stops
> > > (0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D
conversion)
> > >
> > > AD1CR=(0x01<<0)| // SEL=1,select channel 0, 0 to 7 on ADC1
> > > ((PCLK/ADC_Clk-1)<<8)| // CLKDIV = Fpclk / 1000000 - 1
> > > (0<< 16 )| // BURST = 0, no BURST, software controlled
> > > (0<<17)| // CLKS = 0, 11 clocks/10 bits
> > > (1<<21)| // PDN = 1, normal operation
> > > (0<<22)| // TEST1:0 = 00
> > > (0<<24)| // START = 0 A/D conversion stops
> > > (0<<27); // EDGE = 0 (CAP/MAT singal falling,trigger A/D
conversion)
> > >
> > > return (TRUE);
> > > }
> > >
> > >
> > >
> > > uint32_t ADC0Read(uint8_t channelNum)
> > > {
> > > uint32_t regVal, ADC_Data;
> > >
> > > if (channelNum>C_NUM) // channel number is 0 through 7
> > > {
> > > channelNum=0; // reset channel number to 0
> > > }
> > >
> > > AD0CR0CR&0xFFFFFF00;
> > > AD0CR0CR|(1<<24)|(1<
D
> > convert
> > >
> > > while(1) // wait until end of A/D convert
> > > {
> > > regVal=*(volatile unsigned
> > > long*)(ADC0+ADC_OFFSET+ADC_INDEX*channelNum); // read result of
A/D
> > > conversion
> > > if(regVal&ADC_DONE) { break; }
> > >
> > > }
> > >
> > > AD0CR&=0xF8FFFFFF; // stop ADC now
> > > if(regVal&ADC_OVERRUN) { return(0); } // save data when it's
> > not
> > > overrun. Otherwise, return zero
> > > ADC_Data=(regVal>>6)&0x3FF;
> > > return(ADC_Data); // return A/D conversion value
> > > }
> > >
> > > int main(void)
> > > {
> > > sysInit();
> > > ADCInit(ADC_CLK);
> > >
> > > uint32_t adcdata;
> > > adcdataC0Read(3);
> > > ...
> > >
> >
> >
> >
> > Try writing to the START bitfield in a separate write.
> > This way:
> > AD0CR0CR|(1< > > AD0CR0CR|(1<<24);
> >
> >
> >
> > Regards
> > Zdravko Dimitrov
> > CompAC, Varna
> > Bulgaria
> > I tried to select the channel number and start conversion in
sequence
> as suggested, but the result is the same. It also makes no
difference
> if i omit the volatile attribute.
>
> BTW, i am using LPC2138/01
>

Note that the START bitfield is not self cleared. Also it is safer to
try reading AD0DR before starting a new convertion.
In your case it is better to write:

AD0CR=(AD0CR & ~(0x07<<24)) | (1< AD0DR; //Dummy read, tries to unblock ADC
AD0CR0CR | (1<<24);

Regards
Zdravko Dimitrov
This code works for me on the LPC2148:

// initialise ADC

PINSEL1 = 0x05000000; // P0.28 and P0.29 set for AD0.1 and AD0.2
inputs
AD0CR = 0x00210004; // setup A/D AD0.1 input (pin 13) and AD0.2
input (pin 14), 11 clocks/10 bits

//--------------------- read AD0.2 --------------------//

short int read_AD0_2(void)
{
unsigned short int val;

AD0CR = 0x00210004; // setup A/D AD0.2 input (P0.29, pin 14), 11
clocks/10 bits
AD0CR |= 0x01000000; // start conversion
while (AD0GDR == 0x8000000) // wait for DONE to go high
;
val = AD0DR2; // get ADC data
val = ((val >>6) & 0x03FF); // extract result
return(val);
}

Leon
--- In l..., "Zdravko" wrote:
>
> Note that the START bitfield is not self cleared. Also it is safer to
> try reading AD0DR before starting a new convertion.
> In your case it is better to write:
>
> AD0CR=(AD0CR & ~(0x07<<24)) | (1< > AD0DR; //Dummy read, tries to unblock ADC
> AD0CR0CR | (1<<24);
>
> Regards
> Zdravko Dimitrov
>

The code I pasted already does all this:
AD0CR0CR&0xFFFFFF00;
I also tried your code, but the result is the same.
--- In l..., "Leon Heller" wrote:
>
> This code works for me on the LPC2148:
>
> // initialise ADC
>
> PINSEL1 = 0x05000000; // P0.28 and P0.29 set for AD0.1 and AD0.2
> inputs
> AD0CR = 0x00210004; // setup A/D AD0.1 input (pin 13) and AD0.2
> input (pin 14), 11 clocks/10 bits
>
> //--------------------- read AD0.2 --------------------//
>
> short int read_AD0_2(void)
> {
> unsigned short int val;
>
> AD0CR = 0x00210004; // setup A/D AD0.2 input (P0.29, pin 14), 11
> clocks/10 bits
> AD0CR |= 0x01000000; // start conversion
> while (AD0GDR == 0x8000000) // wait for DONE to go high
> ;
> val = AD0DR2; // get ADC data
> val = ((val >>6) & 0x03FF); // extract result
> return(val);
> }
>
> Leon
>

I see that this code checks if the ad conversion is done in the AD0GDR
register, but mine checks one of AD0DR registers.

I tried your code. Now the program doesn't get stuck in while loop any
more, but i get some strange AD reading. I get some strange random
data (0, 141, 283, 1023...).
>>
>> Note that the START bitfield is not self cleared. Also it is safer to
>> try reading AD0DR before starting a new convertion.
>> In your case it is better to write:
>>
>> AD0CR=(AD0CR & ~(0x07<<24)) | (1< >> AD0DR; //Dummy read, tries to unblock ADC
>> AD0CR0CR | (1<<24);
>>
>> Regards
>> Zdravko Dimitrov
>> The code I pasted already does all this:
> AD0CR0CR&0xFFFFFF00;
> I also tried your code, but the result is the same.
>
>--- In l..., "Leon Heller" wrote:
>>
>> This code works for me on the LPC2148:
>>
>> // initialise ADC
>>
>> PINSEL1 = 0x05000000; // P0.28 and P0.29 set for AD0.1 and AD0.2
>> inputs
>> AD0CR = 0x00210004; // setup A/D AD0.1 input (pin 13) and AD0.2
>> input (pin 14), 11 clocks/10 bits
>>
>> //--------------------- read AD0.2 --------------------//
>>
>> short int read_AD0_2(void)
>> {
>> unsigned short int val;
>>
>> AD0CR = 0x00210004; // setup A/D AD0.2 input (P0.29, pin 14), 11
>> clocks/10 bits
>> AD0CR |= 0x01000000; // start conversion
>> while (AD0GDR == 0x8000000) // wait for DONE to go high
>> ;
>> val = AD0DR2; // get ADC data
>> val = ((val >>6) & 0x03FF); // extract result
>> return(val);
>> }
>>
>> Leon
>> I see that this code checks if the ad conversion is done in the AD0GDR
> register, but mine checks one of AD0DR registers.
>
> I tried your code. Now the program doesn't get stuck in while loop any
> more, but i get some strange AD reading. I get some strange random
> data (0, 141, 283, 1023...).

I'll start out with some code that I know works on LPC2368 and LPC2468
boards:

unsigned int val;
AD0CR |= 1 << 1; //Choose channel
AD0CR |= 1 << 24; //Start ADC
while ( 1 )
{
val = AD0GDR;
if ( val & 0x80000000 )
{
break;
}
}
AD0CR &= 0xF8FFFFFF; //Stop the conversion and clear off the channel choice
val = (val >> 6) & 0x3FF; //Remove unused bits, now val contains the
conversion result

Hope this is remotely useful.

Best
Mikael

Memfault Beyond the Launch