Maker Pro
Maker Pro

Getting value of ADRESH out of a pic μcu?

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Can I ask for some assistance in how to get the value of ADRESH out of a pic12f675? It is a single port 8 pin chip and I would like to read the contents into a variable so that I can do operations on/with it. I am using AN1 as my analog input from a potentiometer.

Thanks in advance.
 

Arouse1973

Adam
Dec 18, 2013
5,178
Joined
Dec 18, 2013
Messages
5,178
Don't know if this is of any use John. Found an example of a volt meter, you might be able to pick the bits out you want.
Adam

Code:
1 // Voltage.c, DW Smith, 1st December 2011
2 #include < p18f1220. h >
3 #pragma config WDT = OFF, OSC = INTIO2, PWRT = ON, LVP = OFF, MCLRE = OFF
4 #include < delays.h >
5
6 int READING;
7 float VOLTAGE; // Voltage is a decimal number not an integer.
8
9 void main (void)
10 {
11 // SET UP
12 // OSCCON defaults to 31 kHz. So no need to alter it.
13 ADCON1 = 0x7E; // AN0 is analogue or 0b01111110 in binary
14 TRISA = 0b11111111; // sets PORTA as all inputs, bit0 is AN0
15 PORTA = 0b00000000; // turns off PORTA outputs, not required, no outputs
16 TRISB = 0b00000000; // sets PORTB as all outputs
17 PORTB = 0b00000000; // turns off PORTB outputs,
good start position
18 ADCON0bits.ADON = 1; // turn on A/ D
19 ADCON2 = 0b10000000; // right justified, acquisition times are ok at 0 with 31 kHz
20
21 while (1)
22 {
23 ADCON0bits.GO_DONE = 1; // do A/ D measurement
24 while (ADCON0bits.GO_DONE = = 1); // wait until bit = 0 measurement completed
25 READING = ADRESL +( ADRESH * 256);
26 VOLTAGE = READING/ 204.6;
27
28 if (VOLTAGE > 0 && VOLTAGE < = 1) PORTB = 0b00000000;
29 if (VOLTAGE > 1 && VOLTAGE < = 2) PORTB = 0b00000001;
30 if (VOLTAGE > 2 && VOLTAGE < = 3) PORTB = 0b00000011;
31 if (VOLTAGE > 3 && VOLTAGE < = 4) PORTB = 0b00000111;
32 if (VOLTAGE > 4 && VOLTAGE < =5) PORTB = 0b00001111;
33 }
34 }
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Don't know if this is of any use
Brilliant Adam - its of much use thank you!!! It is clearly commented and not obfuscated with many function calls - very low level and simple, easy to follow along. I will see if I can put it to use later on today, thanks again for finding that :D
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Found this to be cute:

upload_2016-6-4_17-10-18.png

Meanwhile, it's together in the same register (ADCON0) so when you initialize the values to turn on the A/D converter and set the channel, you have to set this bit in a separate instruction code. Wonder why that is... Reasons why we RTM ;)
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Here is what I have so far:

Code:
/*
* File:   devboard.c
*
*
* Created on June 2, 2015, 9:33 PM
*/
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // GP3/MCLR pin function select (GP3/MCLR pin function is MCLR)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>


#define _XTAL_FREQ 4000000



int AD_IN; //variable to hold value of A/D input from potentiometer.

void main (void)
{
  // SET UP
  ADCON0 = 0b0000101; // AN1 is analogue, turning on analog channel and starting converter
  TRISIO = 0b001010;          //Only AN1 and AN3/MCLR are inputs all else outputs.
//good start position
ADCON0bits.ADON = 1; // turn on A/D
ANSEL =  0b0110010;         //AN1 set to Analog, other 3 pins are digital, 011 on right side indicates FRC for timing


while (1)
{
    ADCON0bits.GO_DONE = 1;           // do A/ D measurement
    while (ADCON0bits.GO_DONE == 1); // wait until bit = 0 measurement completed
    {AD_IN = ADRESH;          //ADRESL +( ADRESH * 256);
    AD_IN = AD_IN/256; //

    if ((AD_IN > 0) && (AD_IN <=50))            //compiler ok with this if conditional the next two get flagged with various syntax issues??
        {
        GPIO = 0b000100;
        }   
  
            if ((AD_IN > 75) && (AD_IN < = 150))
                {   
                GPIO = 0b010000;
                }
  
                    if ((AD_IN > 175) && (AD_IN < =255))
                     {
                        GPIO = 0b100000;
                        }
}
}

}

The example code posted by Adam looked really promising, but the XC8 compiler had a fit with it. It specifcially did not care for the if conditionals being joined on one line - like this:
Code:
if (VOLTAGE > 0 && VOLTAGE < = 1)
I separated it and the compiler accepted the first instance shown above, but the copies after that all got flagged for syntax errors. I had it fixed when I tab spaced out the code, but for some reason it flagged again and I have not been able to get it to work since!! Touchy compiler..
Any help here - I don't want to write a case/switch statement. I even started a new file, new name, etc. :mad:
 
Last edited:

Arouse1973

Adam
Dec 18, 2013
5,178
Joined
Dec 18, 2013
Messages
5,178
I didn't spot that, it appears that when pasted into word it automatically puts a space in. I'll have to watch that in future.
Adam
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
I didn't spot that, it appears that when pasted into word it automatically puts a space in. I'll have to watch that in future.
Adam
No worries mate, it was a good challenge to learn to spot things in code for me too!! The code works and now my dev. board responds appropriately, in this case it compares the range of the potentiometer and when the input is between:
0.00-0.97v (000-050 in 0b) GP2 lights up
1.46-2.87v (075-150 in 0b) GP4 lights up
3.37-4.89v (175-255 in 0b) GP5 lights up

I left 25 or 0.47v between ranges for clean on/off switching.
Thanks again for that code!!

Code:
/*
* File:   devboard.c

*
* Created on June 2, 2015, 9:33 PM
*/
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // GP3/MCLR pin function select (GP3/MCLR pin function is MCLR)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>


#define _XTAL_FREQ 4000000



int AD_IN; //variable to hold value of A/D input from potentiometer.

void main (void)
{
  // SET UP
  ADCON0 = 0b0000101; // AN1 is analogue, turning on analog channel and starting converter
  TRISIO = 0b001010;          //Only AN1 and AN3/MCLR are inputs all else outputs.


while (1)
{
    //good start position
    //ADCON0bits.ADON = 1; // turn on A/D
    ANSEL =  0b0110010;         //AN1 set to Analog, other 3 pins are digital, 011 on right side indicates FRC for timing
    ADCON0bits.GO_DONE = 1;           // do A/ D measurement
  
        while (ADCON0bits.GO_DONE == 1) // wait until bit = 0 measurement completed
        {
         AD_IN = ADRESH;          //ADRESL +( ADRESH * 256); //changed value of 256 to 1024 since this chip is 10 bit
                      
            if ((AD_IN) > 0 & (AD_IN <=50))
            {
             GPIO = 0b000100;
            }
          
  
                    if ((AD_IN > 75) && (AD_IN <= 150))
                    {
                     GPIO = 0b010000;
                    }
  
                        if ((AD_IN > 175) && (AD_IN <= 255))
                        {
                             GPIO = 0b100000;
                        }
        }
}
  

}
 

hevans1944

Hop - AC8NS
Jun 21, 2012
4,886
Joined
Jun 21, 2012
Messages
4,886
If you right-justify the A/D converter results by setting bit 7=1 in ADCON0, you can read all ten bits with this expression:
AD_IN = ADRESL + (ADRESH * 256).
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
If you right-justify the A/D converter results by setting bit 7=1 in ADCON0, you can read all ten bits with this expression:
AD_IN = ADRESL + (ADRESH * 256).

Thanks Hop, I am having some trouble following this logically. Please assist:

  • Changing ADCON register to 1 gives right justified result in ADRESL - ok :)
  • ADRESL contents from 0 to 7 are LSB - ok :)
  • Adding the MSB back to our variable with ADRESL contents - ok :)
  • Why are we multiplying ADRESH by 256?:confused:

Shouldn't we be adding 256 to bump the MSB 8 bits to the left so that it is where it belongs as highest value bits? But by adding, are we not turning each bit to a value of 1 from bit 0-7? :eek: Now that would be a real mess!
Sorry, my binary is not always that great :oops:

I noticed that my word count was fairly high with little code in C for the pic12f675, so I believe that ASM will be a necessity again for the dust collector code. I will still toy with C to get the concept down, but the bit shifting operator will certainly be very helpful in saving ops and code space later!!

BTW you're missing one hell of a shower down here :rolleyes:
 

hevans1944

Hop - AC8NS
Jun 21, 2012
4,886
Joined
Jun 21, 2012
Messages
4,886
BTW you're missing one hell of a shower down here :rolleyes:
I watched it on TV. You guys made the national news. Are you still dry?
The PCB arrived in the afternoon mail! Yes, the regulator holes need to be enlarged. Will work on getting parts mounted in the coming week. The board looks great! Wish I could still do this kind of work. <sigh>

Why are we multiplying ADRESH by 256?:confused:
The two bits stored in bit positions 0 and 1 of the 8-bit register ADRESH must represent the two most significant bits of a 10-bit result stored in a 16-bit unsigned integer variable, AD_IN. You need to get those two bits to transfer over to bit positions 8 and 9 in AD_IN.

There are (at least) two ways to do this: (1) multiply the 8-bit register ADRESH by 256 (weight of bit 8) and store the result in unsigned 16-bit variable AD_IN or (2) copy ADRESH into AD_IN and then left-shit AD_IN by 8, thereby moving bit 1 of ADRESH into bit 9 position of AD_IN and moving bit 0 of ADRESH into bit 8 position of AD_IN. After you have the two bits of ADRESH in the correct bit positions of AD_IN you can either add ADRESL to the AD_IN or logically OR the contents of ADRESL with the contents of AD_IN.

Why does multiplying by 256 result in a left-shift of ADRESH? Well, multiplying by 1 doesn't do anything. Multiplying by 2 is the same as a left-shift of all the bits by one bit position. Multiply by 4, left shift everything two bit positions. Multiple by 8, left shift three bit positions. X16 --> shift left 4 bit positions. X32 --> shift left 5 bit positions. X64 --> shift left 6 bit positions. X128 --> shift left 7 bit positions. X256 --> shift left 8 bit positions. If you haven't extended the 8-bit register to 16 bits, then shifting left by 8 or multiplying by 256 will yield a zero result, assuming zeroes are shifted in from the right and arithmetic overflow during multiplication isn't flagged as a run-time error.

So bit 1 in ADRESH shifts over to bit 9 in AD_IN and bit 0 in ADRESH shifts over to bit 8 in AD_IN. After adding or ORing ADRESL with AD_IN, bits 0 through 9 of AD_IN contain the 10-bit result of the A/D conversion.

I don't remember all the rules associated with mixed byte and word arithmetic in C, but if you just copy ADRESH into AD_IN first, before doing the multiplication or left-shifting, it should work.

Multiplying an 8-bit byte by 256, or left-shifting that byte by 8 bit positions, without extending the byte to a 16-bit result will result in either zero results or a run-time error. Shifting is usually a faster operation than multiplying. So maybe a construct like this will work:

AD_IN = ADRESL + (ADRESH << 8)

or maybe (to make sure ASRESH is treated as a 16-bit variable):

AD_IN = ADRESH
AD_IN = (AD_IN << 8) + ADRESL
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Are you still dry?
Yep! Lots of rain, but inside all is well :)

The PCB arrived in the afternoon mail! Yes, the regulator holes need to be enlarged. Will work on getting parts mounted in the coming week. The board looks great! Wish I could still do this kind of work. <sigh>
Cool!! Glad it finally got there. Yes, test fit your parts, you never know since I sized this for the parts I had on hand. I am glad I have a working version, its a lot better than breadboarding in the sense that you know that you have a working baseline. Thanks for the compliment on the board, I think it came out pretty spiffy for a home made job, despite the failed solder mask (again) LOL. I am sure once you are settled you will be able to crank out some of your own boards. Especially since I found you that great deal on Amazon for ferric chloride LOL.

Multiplying by 2 is the same as a left-shift of all the bits by one bit position.
I find this strange since when I think of a multiplication operation, that its a decimal event, meaning 3x2=6. In our example, lets say we have 0000 0001 and we multiply by 256 then we have 1000 0000! But its not ten million but the binary representation of 256 which indeed is what 1x256 is. And its even stranger that it's simply a shift to the left like that. I get it, but it takes awhile for the info to take hold, thanks for explaining it and yes, shifting is the way to go!!!

AD_IN = ADRESL + (ADRESH << 8)
I believe that is what the code Adam posted did, IIRC.

or maybe (to make sure ASRESH is treated as a 16-bit variable):

AD_IN = ADRESH
AD_IN = (AD_IN << 8) + ADRESL
makes sense, you're in a way, typecasting, since we set AD_IN as an unsigned integer. That is a good pickup Hop!!

Thanks again, this is a small step, but I find it very exciting since now I have access to interface analog components which opens up a world of ideas. Next on the list of learning is interrupts and sleep and of course porting this to ASM.

BTW, even though the field notes you linked me indicated that a schottky diode was ok to separate the MCLR circuitry from the ICSP, this is what a forum member at microchip noted:
Code:
Re: ICSP failure to program while in circuit? Yesterday
<<
From me:
I don't see how the Schottky diode idea ever worked.  Many PICs have an internal pullup for /MCLR, and if the diode is oriented to block current from the /MCLR pin to the reset circuit, it is impossible for the reset circuit to pull /MCLR low against the pullup. 
 >>
 
It was my understanding that the MCLR pin has to be held high and when it goes low, then the reset occurs. The schottky in my accompanying schematic does work (physically tested on a working board, resets fine via a tactile switch with a r/c debounce). The diode allows current to pass towards the chip from R2 which is fed by Vdd. 
 
In my example, I am not using the WPUA register.

[IMG]http://www.microchip.com/forums/app_themes/MCHP/image/noavatar.gif[/IMG]
Ian.M

Super Member
Re: ICSP failure to program while in circuit? Yesterday
The /MCLR pin is a somewhat modified CMOS input that also has a circuit to detect if its above Vdd enough for VPP to be present.  The max /MCLR leakage current at input voltages between the supply rails is +/-5uA and +/-0.1uA is quoted as typical.  (parameter D061)
 
Probably either the diode's reverse leakage current and/or the junction capacitance of the diode was enough to pull /MCLR low momentarily.  *NOT* something I would trust to work with *ANY* combination of PIC and small signal Schottky diode.

So if I see any issues, I will recall to take out the schottky and work in a 10k resistor. I have cycled the MCLR several times with various LED's illuminated with no issues though....

Off to bed, tomorrow is going to be slammed at work!
 

hevans1944

Hop - AC8NS
Jun 21, 2012
4,886
Joined
Jun 21, 2012
Messages
4,886
we have 0000 0001 and we multiply by 256 then we have 1000 0000!
Close, but no cigar! 0000 0001 multiplied by 256 is 1 0000 0000. Or regrouping the digits for base eight, 100 000 000 which I suppose you could read aloud as one hundred million. But nobody I know does that. And base eight notation died a deserved death in the twentieth century.

You have to consider the "weight" of each bit at each bit position. The weight of bit 0 is 1 if the bit is set, zero if it is cleared. The weight of bit 1 is either 2 or 0 depending on whether it is set or cleared. And so on in powers of 2 for bits 2 through 7 which have weights of 4, 8, 16, 32, 64 and 128 if set to one or zero if cleared. Bit 8 does not exist in an 8-bit register, but if it did exist (in a longer register) it would have a weight of 256. So, 1 0000 0000 = 256 and 0000 0001 x 256 = 1 0000 0000. If all the bits are set in an 8-bit register, the sum of their weights is 255: 1111 1111 = 255. Adding one to this results in all the bits clearing to zero and a carry out of the most significant bit: 1 (CY) + 0000 0000.

There are assembler instructions that allow you to add arbitrarily wide binary numbers by using the carry bit to extend byte or word additions to arbitrary lengths. Well, you have to define what you are doing in the code, but there is no "built in" limit to how far you can take the process. Back in the 1980s I wrote a program that would allow arithmetic on very large decimal numbers, just to prove I understood the process. Satisfied that I did, I never used it again. That's what mainframes are for: crunching large numbers. I just wanted to "prove" my "toy" computer could play with the big boys, albeit slower.

On how to interface /MCLR input and still be compatible with ICSP, read this thread and use the schematic below, No Schottky diode necessary.

download.axd


Minimum circuit for /MCLR is a 10 kΩ pull-up to +5 V or Vdd. And I never worry about contact bounce on reset. Why care how many times it resets as long as it does reset? I am sure someone, somewhere, can provide a counter-example where one and only one reset must result from a single button-press, but I haven't encountered that yet. Heck, sometimes I just pull off the wire providing power to effect a "reset" condition.
 

Arouse1973

Adam
Dec 18, 2013
5,178
Joined
Dec 18, 2013
Messages
5,178
Hi Guys
The capacitor that's part of the reset RC circuitry is for slow rising Vdd and slows down the rise of MCLR to create a POR. The Vdd must be stable before MCLR. If you look on the data sheet you should see some timings which show this.
Thanks
Adam
 

hevans1944

Hop - AC8NS
Jun 21, 2012
4,886
Joined
Jun 21, 2012
Messages
4,886
Some more observations on "shifting" to obtain a multiplication result:

In the decimal or base 10 number system, we "shift" the decimal point to the right to multiply a number by powers of 10. So 1234. becomes 12340. Similarly, "shift" the decimal point to the left to divide a number by powers of 10. So 1234. become 123.4.

A similar effect occurs with binary or base 2 number systems. If we "shift" the binary point to the right by one bit position the effect is to multiply the binary number by 2: 1010 become 10100. Check: 1010 is decimal 10 and 10100 is decimal 20. So far so good. But what we have really done in moving the binary point to the right (and filling in with zeroes) is to left-shift all the bits by one bit position, filling in the blanks with zeroes. Note that this type of multiplication (moving the binary point or the decimal point) is valid only for multiplication by integer powers of the number base. So you can move the decimal point for multiplication by powers of ten: 10, 100, 1000, etc. And you can move the binary point for multiplication by powers of two: 2, 4, 8, 16, etc.

Where you can get into trouble is thinking you can shift in the other direction for division operations. Well, you can, but the results may not be to your expectations. 1010 binary divided by 2 becomes 0101 (shifting zeroes in from the left) which makes sense. But what does it mean if we divide 1010 binary by 4? The answer is 0010 plus a remainder of .1 to deal with. We know 1010 is the binary representation of the decimal number 10, so 10 / 2 = 5 or 0101 binary. But decimal 10 / 4 = 2.5 decimal, or binary 0010.1 with that .1 appended to the end having a "weight" of 1/2 or two to the minus one power. What do we do with that?

[BEGIN RANT] It's a can of worms working with negative powers of two when everything else is signed or un-signed binary integers! Best to do divisions using floating-point math functions. And multiplications, too, if multiplying by something that is not an integral power of two. In general, math operations on small microprocessors are a PITA that should be avoided. Floating-point math takes up a lot of program space for even the most trivial functions and operations. If at all possible, avoid having to use floating-point arithmetic. Sure, it's easy to use FP variables and constants in C, but take a look at the assembly code it produces. Caveat: some microprocessors have floating-point processors built in. That doesn't mean you should liberally sprinkle your code with floating-point operations![/END RANT]
 
Top