ADC Program For LPC2138
ADC Program For LPC2138
One of the interesting features of ARM microcontrollers is that each pin can perform up to 4
different 'functions' ... though only one at a time! If you're not sure how this works, or what the
advantages of this are, this can be done by PINSEL (a set of registers that we can use to indicate
exactly which would like each pin to perform). In the case of AD0.3 (located on pin 15 of the
lpc2138), the pin can be configure to perform any of the following functions (see page 71 of the
LPC21XX User's Manual):
It's always good practice to make sure that a peripheral is powered on (using the PCONP
register) before trying to use it. This can be accomplished for ADC0 with the following line of
code:
To configure the A/D Converter, we need to pass a specific 32-bit value to the appropriate
ADCR, or "Analog/Digital Control Register" (see p.270-272 of the LPC21XX User Manual
for more details). This 'control register' manages the configuration of our A/D converter, and
determines a variety of things, including:
Unfortunately, that may not make very much sense to you, but without going into every little
detail of every possible configuration option (see p.270-272 for the User's Manual for full
details), we'll try to explain the register values that are being used in the example at the
beginning of this tutorial: SEL, CLKDIV, CLKS and PDN.
SEL
This set of 8 bits corresponds to the 8 different 'channels' available on either A/D converter. You
can indicate which channel you wish to use by setting it's appropriate bit to '1'. For example,
since we are using AD0.3 in this case (channel 3), we would pass the following value to
AD0CR:
CLKDIV
The A/D Converters on the LPC2138 are able to run at a maximum speed of 4.5MHz. The
conversion speed is selectable by the user, but the only catch is that to arrive at a number equal to
or less than 4.5MHz, we need to 'divide' our PCLK (the speed at which our microprocessor is
running) by a fixed number, which we provide (in binary format) using the 8 CLKDIV bits.
The default PCLK for your microcontroller is 12MHz (since the Olimex LPC-P2138 has a
12.0MHz crystal installed on it), but this can be 'multiplied' up to 60MHz, so you need to know
what 'speed' you have set your mcu to before providing a value in CLKDIV.
We are going to assume that we are running at 12.0MHz (since we haven't covered how to adjust
the mcu speed yet!). In order to stay below the ADC's maximum speed of 4.5MHz, we would
need to divide our PCLK (12MHz) by 3, which will give us 4MHz (the closest value we can
have with a 12MHz clock since we need to divide by a whole number). In order to avoid a
'divide by 0' error, though, the A/D control register will add one to whatever value you
supply. This means that if we want to divide the PCLK by 3, we actually need to provide '2',
which will be adjusted up one to '3' by the control register. (This is one of the little details that
can cause your software to malfunction if you don't read the user manual and pay close attention
to how to configure your peripherals.)
This means that if we were running at 12.0MHz, we could achieve a 4.0MHz A/D conversion
speed by setting the following CLKDIV bits in AD0_CR ("00000010" being the binary
equivalent of 2):
And what if we were running at 48.0MHz, for example? We would be able to achieve a
maximum conversion speed of 4.36MHz by dividing 48.0MHz by 11, so we would provide '10'
to the CLKDIV (10 + 1 = 11), as follows ('00001010' being the binary equivalent of 10):
CLKS
These three bits are used to indicate the range of values used when converting analog data. You
can set the 'precision' of the results from 3-bits (values from 0-7) up to 10-bits (values from 0-
1023), depending on your requirements. You simply need to provide one of the following values
to indicate how many 'bits' to use for the conversion:
For example, to get the maximum 10-bit 'range', we would provide the following value to the
CLKS field:
PDN
PDN (short for 'Power-Down') indicates whether the ADC should be in 'Power-Down' mode (0),
or actively converting data (1). Since we want to convert data right away, we could tell the ADC
to go out of power-down mode (it's default value) by setting this bit to 1 as follows:
This bit is set to 1 when an A/D conversion is complete. For accurate results, you need to wait
until this value is 1 before reading the RESULT bits. (Please note that this value is cleared when
you read this register.)
While not relevant to the examples used in this tutorial, this value with be 1 if the results of one
or more conversions were lost when converting in BURST mode. See the User's Manual for
further details. (As with DONE, this bit will be cleared when you read this register.)
Before we can read the results from the A/D Data Register, we first need to perform the
following steps (to start the actual conversion process by re-configuring ADCR):
This can be accomplished by modifying the A/D Control Register (see ADCR above) as follows:
What you are doing here is setting the START bits in ADCR to 000 (stopping any conversions)
and disabling any channels that may have been previously selected. This essentially 'resets' our
ADC to a blank, inactive state.
Since we have previously deselected all channels, we need to re-enable the specific channel that
we wish to use in this conversion. In this particular case, we can enable channel 3 with the
following line of code:
AD0CR |=chn;
AD0CR |=adc0_start;
The ADC can be configured to start in two ways: Manually (as we will do here), or when some
sort of internal hardware event occurs such as an external interrupt (for example when a button
connected to an EINT pin is pressed), or some other form of interrupt. (For details on starting the
conversion when an interrupt occurs, please see chapter 17 of the LPC21XX User's Manual.
Interrupts will also be covered in a later tutorial.)
In our case, the easiest solution is to simply tell the ADC to start converting manually, which is
as simple as setting the appropriate START bits (26..24) in the ADC Control Register (see
ADCR above). To start converting now, we need to send 001 to the three-bit START block in
ADCR, which can be done with the following line of code .
AD0CR |=adc0_start;
This will cause the code to endlessly loop until the conversion is complete, and then move on to
the next line once the conversion is finished.
The last step (now that we know the conversion is complete) is simply to read the 10-bit value
stored in the appropriate ADDR register, and do whatever we need to do with it. This last step
can be accomplished by reading bits 6 through 15 (15..6) of ADDR3 (since we are using Channel
3 in this example), and then 'shifting' the results to the right 6 places to give a normal 10-bit
value:
i=((AD0DR)&(0x0000FFC0))>>6 ;
That's all that's involved in manually starting an A/D conversion and reading the results. If you
wish to continually read the results of the ADC, you can simply place the appropriate code in a
method/function and continually call it, as we are doing in the example.
Since all the values are distributed in the range of 0 – 1023 and with reference to ‘Vref’(which is
3.3V), the following self explanatory code will demonstrate how its done.
And you can use our lcd program to display the result from here : http://bit.ly/dowbTw
CODE:
#include<lpc213x.h>
#define adc_pin 1<<30
#define pdn_bit 1<<21
#define adcr_start_mask 7<<24
#define adcr_sel_mask 0x000000FF
#define adc0_start 1<<24
void init_adc0(unsigned char,unsigned char);
unsigned char chn;
unsigned int read_adc0();
void delay(int);
unsigned char Dis_Value[5];
main()
{
unsigned int val;
init_adc0(3,3); /*pass channel number and clkdiv*/
Dis_Value[1]=46; /*ascii value for '.' */
while(1)
{
val=read_adc0();
lcd_init();
lcd_clear();
lcd_gotoxy(0,0);
val=(float) (val*1000/0x3ff)*3.3; /*Vref = 3.3V*/
Dis_Value[0]=(unsigned char)(val/1000) + 0x30;
Dis_Value[2]=(unsigned char)(val/100%10) + 0x30;
Dis_Value[3]=(unsigned char)(val/10%10) + 0x30;
Dis_Value[4]=(unsigned char)(val%10) + 0x30;
lcd_print(Dis_Value); /*display the result on LCD*/
delay(1000); /*display the result for some amount of time*/
}
}