Friday, July 10, 2009

Using new ADC libraries

Since several weeks or months, quite a lot of effort has been put into the development and update of ADC libraries. Why ? Because when Guru Stef Mientki first developed this library, he designed it for quite a few PICs (but most used ones at this time). Since jallib is to support all (or as much as) possible PICs, it was time to have a look on this huge piece of code...


Many different cases...

As usual, Microchip PICs offers a wide choice configuring ADC:
  • Not all PICs have ADC module (...)
  • Analog pins are dispatched differently amongst PICs, still for user's sake, they have to be automatically configured as input. We thus need to know, for each PIC, where analog pins are...
  • Some PICs have their analog pins dependent from each other, and some are independent (more on this later)
  • Clock configuration can be different
  • Some PICs have 8-bits low resolution ADC module, some have 10-bits high resolution ADC module (and some have 12-bits, those aren't currently handled, that's a restriction...)
  • Some PICs can have two voltage references (VRef+ and VRef-), only one voltage reference (Vref+) and some can't handle voltage references at all
  • (and probably other differences I can't remember :)...
Luckily most of these differences are transparent to users (hey, that was the goal of this refactoring/rewriting...).


Dependent and independent analog pins

OK, let's write some code ! But before this, you have to understand one very important point: some PICs have their analog pins dependent from each other, some PICs have their analog pins independent from each other. "What is this suppose to mean ?" I can hear... Let's consider two famous PICs: 16F877 and 16F88. 16F877 datasheet explains how to configure the number of analog pins, and vref, setting PCFG bits:


Want 6 analog pins, no Vref ? Then PCFG bits must be set to 0b1001. What will then be the analog pins ? RA0, RA1, RA2, RA3, RA5 and RE0. "What if I want 7 analog pins, no Vref ?" You can't because you'll get a Vref pin, no choice. "What if I want 2 analog pins being RE1 and RE2 ?" You can't, because there's no such combination. So, for this PIC, analog pins are dependent from each other, driven by a combination. In this case, you'll have to specify:
  • the number of ADC channels you want,
  • and amongst them, the number of Vref channels
Note: this is the behavior of the original Stef's lib adc_hardware.jal

Now, let's consider 16F88. In this case, there's no such table:


Mmmh... OK, there are ANS bits, one for each analog pins. Setting an ANS bit to 1 sets the corresponding pin to analog. This means I can set whatever pin I want to be analog. "I can have 3 analog pins, configured on RA0, RA4 and RB6. Freedom !"

Analog pins are
independent from each other in this case, you can do what you want. As a consequence, since it's not driven by a combination, you won't be able to specify the number of ADC channels here. Instead, you'll use set_analog_pin() procedure, and if needed, the reverse set_digital_pin() procedure. These procedures takes a analog pin number as argument. Say analog pin AN5 is on pin RB6. To turn this pin as analog, you just have to write set_analog_pin(5), because this is about analog pin AN5, and not RB6.

Note: As a consequence, these procedures don't exist when analog pins are dependent as in our first case.

Careful:
it's not because there are PCFG bits that PICs have dependent analog pins. Some have PCFG bits which act exactly the same as ANS bits (like some of recent 18F)

"Stop it, show me some code !"

Basically, all the logic accessing ADC module has been kept. Most of the work was about configuring the ADC module to handle all PICs. You'll find adc_read() and adc_read_low_res() functions, as usual. Changes only occur while configuring the ADC module.

16F877 : dependent analog pins

The following examples briefly explains how to setup ADC module when analog pins are dependent from each other, using PIC 16F877.

The following diagram is here to help knowing where analog pins are and where Vref pins are:



Example 1: 16F877, with only one analog pin, no voltage reference

-- beginning is about configuring the chip
-- this is the same for all examples for about 18F877
include 16f877
-- setup clock running @20MHz
pragma target OSC HS
pragma target clock 20_000_000
-- no watchdog
pragma target WDT disabled
pragma target LVP disabled
enable_digital_io()
include delay

-- ok, now setup serial, we'll use this
-- to get ADC measures
const serial_hw_baudrate = 19_200
include serial_hardware
serial_hw_init()


-- ok, now let's configure ADC
-- we want to measure using low resolution
-- (that's our choice, we could use high resolution as well)
const bit ADC_HIGH_RESOLUTION = false
-- we said we want 1 analog channel...
const byte ADC_NCHANNEL = 1
-- and no voltage reference
const byte ADC_NVREF = 0
-- now we can include the library
-- note it's now named "adc", not "adc_hardware" anymore
include adc
-- and run the initialization step
adc_init()


-- will periodically send those chars
var byte measure
forever loop
-- get ADC result, on channel 0
-- this means we're currently reading on pin RA0/AN0 !
measure = adc_read_low_res(0)
-- send it back through serial
serial_hw_write(measure)

-- and sleep a litte to prevent flooding serial...
delay_1ms(200)
end loop

Example 2: 16F877, with 5 analog pins, 1 voltage reference, that is, Vref+

This is almost the same as before, except we now want 5 (analog pins) + 1 (Vref) = 6 ADC channels (yes, I consider Vref+ pin as an ADC channel).

The beginning is the same, here's just the part about ADC configuration and readings:

const bit ADC_HIGH_RESOLUTION = false
-- our 6 ADC channel
const byte ADC_NCHANNEL = 6
-- and one Vref pin
const byte ADC_NVREF = 1
-- the two parameters could be read as:
-- "I want 6 ADC channels, amongst which 1 will be
-- reserved for Vref, and the 5 remaining ones will be
-- analog pins"
include adc
adc_init()

-- will periodically send those chars
var byte measure
forever loop
-- get ADC result, on channel 0
-- this means we're currently reading on pin RA0/AN0 !
measure = adc_read_low_res(0)
-- send it back through serial
serial_hw_write(measure)

-- same for pin RA1/AN1
measure = adc_read_low_res(1)
serial_hw_write(measure)

-- same for pin RA2/AN2
measure = adc_read_low_res(2)
serial_hw_write(measure)

-- pin RA3/AN3 can't be read, since it's Vref+

-- same for pin RA5/AN4
-- 4 is from from "AN4" !
measure = adc_read_low_res(4)
serial_hw_write(measure)

-- same for pin RE10/AN5
measure = adc_read_low_res(5)
serial_hw_write(measure)

-- and sleep a litte to prevent flooding serial...
delay_1ms(200)
end loop


16F88 : independent analog pins

The following example is about setting up ADC module with PIC 16F88, where analog pins are independent from each other.

The following diagram is here to help knowing where analog pins are and where Vref pins are:


Example 1: 16F88, analog pins on RA0/AN0, RA4/AN4 and RB6/AN5. No voltage reference.

-- beginning is about configuring the chip
include 16f88
-- We'll use internal oscillator. It work @ 8MHz
pragma target CLOCK 8_000_000
pragma target OSC INTOSC_NOCLKOUT
OSCCON_IRCF = 0b_111
pragma target WDT disabled
enable_digital_io()

-- ok, now setup serial, we'll use this
-- to get ADC measures
const serial_hw_baudrate = 19_200
include serial_hardware
serial_hw_init()

-- now configure ADC
const bit ADC_HIGH_RESOLUTION = false
const byte ADC_NVREF = 0
-- we can't specify a number of ADC channel here,
-- or we'll get an error !
include adc
adc_init()
-- now we declare the pin we want as analog !
set_analog_pin(0) -- RA0/AN0
set_analog_pin(4) -- RA4/AN4
set_analog_pin(5) -- RB6/AN5

-- reading is then the same
var byte measure
forever loop

measure = adc_read_low_res(0)
serial_hw_write(measure)

measure = adc_read_low_res(4)
serial_hw_write(measure)

measure = adc_read_low_res(5)
serial_hw_write(measure)

end loop



Whether you would want to turn RB6/AN5 into a digital pin again, you'd just call:

set_digital_pin(5)



OK, enough for now. Next time, we'll see how it works for real, with a "Step by Step" tutorial. We'll probably use a 16F88 for this, using serial.

As of July 19th, 2009, this ADC libraries aren't release yet (but expected to be soon). If you want to have fun with them, you'll have to get them from jallib SVN repository. See instructions here:

http://code.google.com/p/jallib/source/checkout


You can also get files one by one from SVN:

Anyway, browse SVN as needed :)



Sébastien Lelong

10 comments:

  1. "Note: As a consequence, these procedures don't exist when analog pins are dependent as in our first case.
    "

    Well, because of that note, I suspect you have defined a constant on each pic library, telling that ADC pins are independent or not.

    So, we, as users, can test the value of that constant to know what to use from the ADC library ?

    ReplyDelete
  2. Oops! Not on each library, because in that case, ADC library must include all pic libraries... So, it is inside ADC library...

    ReplyDelete
  3. I've hesitated to put this, as this constant should only be useful for the lib's developpement itself. Reading the datasheet should give you the answer. Look for PCFG bits and a table with all combinations: analog pins are dependent.

    But, if you're lazy like me, you can also try to compile your code with:

    const byte ADC_NCHANNEL = 1

    Does it compile ? Yes ? So it's dependent. Else, you'll get an error like: "You can't specify the number of ADC channel with ADC_NCHANNEL, you have to use set_analog_pin(...)". Quite explicit... :)

    To test, try to compile given example for 16f88, putting this line.

    Cheers,
    Seb

    ReplyDelete
  4. And, or the other side, if you try to use "set_analog_pin()" with a PIC where pins are dependent (hence not possible), you'll get an undefined procedure error from the compiler itself.

    Seb

    ReplyDelete
  5. Thank you! I can say I like the pics with independent ADC pins...

    ReplyDelete
  6. Well, the second procedure (downloading only adc related files and pic related files) is not working.

    The first error at compilation is:

    "
    adc_channels.jal:112: "pic_18f66j93" not defined
    "

    And it seems the new file "chipdef_jallib.jal" can't be found browsing the svn.

    ReplyDelete
  7. Oops, I forgot this one... It's in SVN, in "/include/device", but because there are many, many device files, you have to go at the end, using "Next" link.

    Anyway, here it is:
    http://code.google.com/p/jallib/source/browse/trunk/include/device/chipdef_jallib.jal


    Seb

    ReplyDelete
  8. I look forward to the publication of your 'universal' ADC library. Should make programming a lot simpler for us!

    cheers

    Colin

    ReplyDelete
  9. good tutorial Seb!

    What is missing here:
    1. how to measure negative voltages with the PIC ADC and this library without using any suplementary hardware
    2. how to measure positive and negative voltage with the PIC ADC without using any suplementary hardware


    BTW, I'm still confident that can't be written any "universal" ADC library, just because of the "analog" character of the problem...However it can exist an "almost universal" one.

    Vasile (Wesley)

    ReplyDelete