Using ADC

Thread Starter

ivars88

Joined Feb 17, 2013
5
Hello! I am struggling with my program. With Arduino everything is easy, but with my PIC16F873 is harder. I hope some smart people will watch code and tell whats wrong. I'm using MPLAB IDE and HI-TECH compiler. Build hapens succesfully but when writing code in MC it doesnt go very well.. It doesnt read A0.

Code:
//Simple program to read analog sensor(potenciometer)
//and control 3 leds.
#include "htc.h"
__CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_OFF);
void main()
{
TRISB = 0x00; // B ports are outputs
PORTB = 0; //Beginning condition 0
TRISA = 0xFF; //A port are input
unsigned int val; //Variable for storing converted anolog signal

ADCON0 = 0b01000101; //A/D control bit register
ADCON1 = 0b10000000; //ADFM 1

while(1)
{
GO_DONE = 1; //Start conversion
while(GO_DONE) continue;
val = ADRESL >> 2; //Assign converted value to variable
if (val > 500) {PORTB = 0x01;}
else {PORTB = 0b00000110;} //Turn on 2 LED diodes
}
}

Here the schematic:
http://www.bildites.lv/viewer.php?file=q6hefcnqyezzgjkaukg7.png
 

ErnieM

Joined Apr 24, 2011
8,377
Excellent first post, a simple program defining your pboblem and a full schematic of a simple target device. All I would ask is you use the code tag button (#) so your code is more readable.

I believe you are correctly running the A2D, just misinterpreting the results. Since the result is a 10 bit number and larger then any one register, ADRES comes in hi and low sections. I don't have the HiTech compiler handy but look thru the user's guide and there should be some sort of macro to read a register pair like this.

If not...
Rich (BB code):
   val = ADRESH<<8 + ADRESL;
should always do the trick (albeit the long way).

Next, since you put just 8 bits of the result into val, then further divided it by 4 (via the >>2) it will never be 500; even without the shift it would never get above 255.

Try changing how you read into val and your code should start working.

And welcome to the forums!
 

ErnieM

Joined Apr 24, 2011
8,377
There's several things happening here that may not be apparent (and I also sometimes make silly mistakes)

A PIC holds the A2D result inside two 8 bit registers, as the result is 10 bits long. If you want to put them into one variable you need to squich em together the right way.

Rich (BB code):
val = ADRESH<<8 + ADRESL;
I don't offhand know the size (in bytes) of an unsigned int (as you chose for val) but I am assuming it is larger then an unsigned char. Unsigned char is 8 bits or 1 byte.

When C is handed some math with variables of different sizes it "promotes" those in smaller sizes to larger ones. Here when working with ADRESL and ADRESH C makes unsigned int size places to hold them.

First, ADRESH holds the top 2 bits, but in the lowest 2 bits of the register. So you need to shift them before you add in the lower part.

ADRESH<<8 means shift ADRESH 8 bits to the left (larger).
So if it started as: 00000001
It ended as:...... 0100000000

Note since it was "promoted" there is room for a larger quantity.

Then there is room in the lower 8 bits for the lower 8 bits of the result.

Hope this helps you on the way!
 

Thread Starter

ivars88

Joined Feb 17, 2013
5
Hi, thank you for response! I tried some things, changed the code to see on LEDS how big is "val" and how it changes. Everything else left the same.

val = ADRESH<<8 + ADRESL; //Assign converted value to variable
if (val < 100) {PORTB = 0b00000001;} //Turn on 1st diode
else if (val < 100 && val < 500) {PORTB = 0b00000010;} //Turn on 2nd diode
else if (val > 500) {PORTB = 0b00000100;} //Turn on 3rd diode
else {PORTB = 0b00000000;} //Turn off all diods if all F

In result 1st LED only turns on, the 3rd blinks for few ms and everything don't work as expected.

So in the conclusion:
1. Im still reading bits wrong;
2. The code isnt writted well;
3. Something wrong with chip or in schematic;

If i shift bits <<8 isn't they go out of ADRESH register and are lost?
Maybe i need to clear ADIF bit?
 

RG23

Joined Dec 6, 2010
304
while(1)
{
GO_DONE = 1; //Start conversion
while(GO_DONE) continue;
val = ADRESL >> 2; //Assign converted value to variable
if (val > 500) {PORTB = 0x01;}
else {PORTB = 0b00000110;} //Turn on 2 LED diodes
}
}
_____________________________________________________________

500 is 0x1F4
After performing the A to D conversion,
put the value of ADRESL in val2 and value of ADRESH in val1

In the loop where you are using val>500,make use of val1 and val2

check if val1 is 0x01 and val2 is > F4 at the same time

Also try
ADCON0 = 0x41
ADCON1 = 0x00
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,377
How do you program your PIC? If you have a PICkit (or several other tools) you can step theu the code as it is running on your device, and read out registers and variables to see what is actually happening in as much (or little) detail you want.
 

djsfantasi

Joined Apr 11, 2010
9,163
Hi, thank you for response! I tried some things, changed the code to see on LEDS how big is "val" and how it changes. Everything else left the same.

val = ADRESH<<8 + ADRESL; //Assign converted value to variable
if (val < 100) {PORTB = 0b00000001;} //Turn on 1st diode
else if (val < 100 && val < 500) {PORTB = 0b00000010;} //Turn on 2nd diode
else if (val > 500) {PORTB = 0b00000100;} //Turn on 3rd diode
else {PORTB = 0b00000000;} //Turn off all diods if all F

In result 1st LED only turns on, the 3rd blinks for few ms and everything don't work as expected.

So in the conclusion:
1. Im still reading bits wrong;
2. The code isnt writted well;
3. Something wrong with chip or in schematic;

If i shift bits <<8 isn't they go out of ADRESH register and are lost?
Maybe i need to clear ADIF bit?
In this code, the second LED will never be lit. If val<100, the first if statement will be executed and none of the others. In the first else if clause, val must be both <100 and <500. But the program will only get here if val>=100 and the first if trapped that condition. So your second condition will never be satisfied. Try removing "Val<100 &&" from the first else if clause.

Note this isn't perfect, but hopefully you will see how the program operates and figure out the improvements needed.
 

Thread Starter

ivars88

Joined Feb 17, 2013
5
Note this isn't perfect, but hopefully you will see how the program operates and figure out the improvements needed.
Thanks for mentioning that ;)

@ErnieM Im using PicKit2 and dont really know how to do that..

But proper readings i still cant get, even when i tried mentioned things.. :/
 

ErnieM

Joined Apr 24, 2011
8,377
debugging with a PICkit II under MPLAB is actually really simple. (I've yet to use MPLAB X so someone else will have to tell you that way).

You just leave the PICkit connected to your PIC. In MPLAB, on the menu pick DEBUGGER and hunt out the PICkit II. If you have a pull down that says "Release" pull it and change to "Debug." Rebuild your code and BAM, you are ready to in circuit debug.

Check the Debugger menu for useful things to do like single step. You can just hover over variables (I believe) and see their values but under the view menu you can open windows to see (and change!) all or selected registers and variables.
 
I think the code for pic 16f877a will work for you..

Rich (BB code):
#include<htc.h>
#include<pic.h>

#define _XTAL_FREQ 8000000

void ADC_Init()
{
  ADCON0 = 0x41; //ADC Module Turned ON and Clock is selected
  ADCON1 = 0xC0; //All pins as Analog Input
                 //With reference voltages VDD and VSS
}

unsigned int ADC_Read(unsigned char channel)
{
  if(channel > 7) //If Invalid channel selected 
    return 0;     //Return 0

  ADCON0 &= 0xC5; //Clearing the Channel Selection Bits
  ADCON0 |= channel<<3; //Setting the required Bits
  __delay_ms(2); //Acquisition time to charge hold capacitor
  GO_nDONE = 1; //Initializes A/D Conversion
  while(GO_nDONE); //Wait for A/D Conversion to complete
  return ((ADRESH<<8)+ADRESL); //Returns Result
}

void main()
{
  unsigned int a;
  TRISB = 0x00; //PORTB as output
  TRISC = 0x00; //PORTC as output
  TRISA = 0xFF; //PORTA as input
  ADC_Init(); //Initializes ADC Module

  do
  {
    a = ADC_Read(4); //Reading Analog Channel 0
    PORTB = a; //Lower 8 bits to PORTB
    PORTC = a>>8; //Higher 2 bits to PORTC
    __delay_ms(100); //Delay
  }while(1); //Infinite Loop
}

Try this link :
Using ADC of PIC Microcontroller - Hi Tech C
 
Top