View previous topic :: View next topic |
Author |
Message |
aroman
Joined: 06 Oct 2003 Posts: 12
|
Question of A/D converter of PIC16F876 |
Posted: Sun Nov 16, 2003 11:58 pm |
|
|
The A/D converter of the PIC16F876 has 10 bits of resolution. Do you know if there is a way to put it to work with just 8 bits of resolution, instead of 10? |
|
![](templates/subSilver/images/spacer.gif) |
Aztlan Guest
|
Re: Question of A/D converter of PIC16F876 |
Posted: Mon Nov 17, 2003 12:02 am |
|
|
aroman wrote: | The A/D converter of the PIC16F876 has 10 bits of resolution. Do you know if there is a way to put it to work with just 8 bits of resolution, instead of 10? |
Yes use
#device adc=8
Aztlan |
|
![](templates/subSilver/images/spacer.gif) |
aroman
Joined: 06 Oct 2003 Posts: 12
|
|
Posted: Tue Nov 18, 2003 8:28 am |
|
|
Thanks to Aztlan for the suggestion, but unfortunately I tried it and the PIC still uses 10 bits of resolution for the A/D conversion. This is the code that I am using on the PIC:
Code: |
#include <16F876.h>
#device adc=8
#use delay(clock=76800)
#fuses LP,NOWDT,PUT,NOPROTECT,NOBROWNOUT,NOLVP
#byte adcon1 = 0x9f //Direccion del registro donde se configuran los pines de los puertos del
//convertidor A/D
#byte adcon0 = 0x1f //Direccion del registro donde se configuran los pines de los puertos del
//convertidor A/D
#byte adresh = 0x1e //Direccion del registro que contiene el resultado de la parte alta de
//la conversión A/D
#byte adresl = 0x9e //Direccion del registro que contiene el resultado de la parte alta de
//la conversión A/D
void main() { //aquí inicia el programa principal
int valor_sensor; //es el valor digital del voltaje analógico del sensor
int direccion; //es la dirección en memoria EEPROM interna del PIC
valor_sensor = 0;
direccion = 0;
disable_interrupts(global); //se deshabilitan las interrupciones pues la recepción se hace
//por "polling" y no por interrupción
setup_adc_ports(RA0_ANALOG); //se pone únicamente el pin RA0 como entrada analógica (todos
//los demás pines del puerto A se dejan como entrada/salida
//digital) y con voltaje de referencia Vdd
setup_adc(ADC_CLOCK_INTERNAL); //Aquí utiliza como reloj para el ADC el reloj interno RC
//(el periodo varía entrer 2-6 us y es típicamente 4 us)
set_adc_channel(0); //Escoge el canal analógico a digital 0 (pin AN0)
adcon1 = 0x8e; //Pone el resultado de la conversión A/D justificado a la derecha (por
//defecto viene justificado a la izquierda)
setup_spi(FALSE);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_ccp1(CCP_OFF);
setup_ccp2(CCP_OFF);
write_eeprom(255, adcon0);
write_eeprom(254, adcon1);
while (direccion<100){
valor_sensor = read_adc();
write_eeprom(direccion, valor_sensor);
direccion++;
write_eeprom(direccion, adresh);//Escribe la parte alta del resultado de la conversión
//A/D en memoria EEPROM interna
direccion++;
write_eeprom(direccion, adresl);//Escribe la parte baja del resultado de la
//conversión A/D en memoria EEPROM interna
direccion++;
}//del while (direccion<100)
}//fin de main
|
When I read the adresh register, the result is non-zero, as it should be if the conversion would be with just 8 bits of resolution (instead of 10). When I read the adcon0 register, the result is 0xC1, which means that the A/D clock is set to the internal RC oscillator, the analog channel selected is channel 0, at that time the A/D was not in progress (yet) and that the A/D converter module is operating. When I read the adcon1 register, the result is 0x8E, which means that the A/D result format is right justified (most significant bits of ADRESH are read as 0) and that pin RA0 is configured as analog input with Vref+=Vdd and Vref-=Vss and the rest of the pins of port A are configured as digital I/O.
Does anyone knows if there is a way to tell the PIC16F876 to use 8 bits of resolution instead of 10 for the A/D module? |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Nov 18, 2003 8:49 am |
|
|
Technically you can't. You can however left or right justify the result. Left justified will place the 8 bit result in adresh. You are using a mixture of the CCS functions and also setting of the registers yourself. The statement
is the problem. Bit 7 is set when it should be cleared. You shouldn't need this statement at all. The CCS setup_adc functions will configure this register for you. The 8 bit result will be placed in adresh and can be retreived by accessing that register or by using the read_adc() function. |
|
![](templates/subSilver/images/spacer.gif) |
aroman
Joined: 06 Oct 2003 Posts: 12
|
|
Posted: Tue Nov 18, 2003 9:51 am |
|
|
Thanks to Mark. On the data sheet, I did not find how to change the resolution for the A/D, but I wanted to ask if it was possible.
I want the result of the A/D conversion right-justified, so I can read the low part of the result in register adresl and the 2 higher bits in register adresh. That's why I used the
statement, by putting bit 7 of adcon1 register in 1.
I didn't find how to tell the PIC to right-justified the result of the A/D conversion with the CCS setup_adc functions, so I modified the adcon1 register directly. Does anyone knows how to do it with the setup_adc functions?
Another question:
Is the result of the read_adc function an int (8 bits-wide) or a long int (16 bits-wide)? Because it seems like that no matter if I define a variable of either int or long ing to assign the result of the read_adc function, the compiler does not give a "type mismatch" error by using either of those 2 types. |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Nov 18, 2003 10:03 am |
|
|
If you want 8 bit adc, the result is left justified and the result in placed in adresh
If you want 10 bit adc, the result is right justified and the 2 MSB are placed in adresh and the 8 LSB are placed in adresl
The CCS functions handle the setting up of the adc registers.
ADC=8 causes bit 7 to be cleared. Without the statement, the bit is set.
The size of the return value of the read_adc() function depends on the adc=8 statement. The compiler will cast the 8 bit result and allow it to be placed in a 16 bit variable (or larger).
It sounds like you are a bit confused about wether or not you want 8 bit adc or how it is supposed to function. Why don't you explain a little more as to what you are trying to do with the adc result and how many bits you are trying to use. |
|
![](templates/subSilver/images/spacer.gif) |
aroman
Joined: 06 Oct 2003 Posts: 12
|
|
Posted: Tue Nov 18, 2003 10:11 am |
|
|
I would like to have 8 bits resolution in the A/D conversion (not the 10 bits resolution that is by default), and store the result in an 8 bit-wide variable. What do I need to modify in my code to acheive that? |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Nov 18, 2003 10:17 am |
|
|
Remove this line:
Also, is you crystal speed really 76.8KHz? |
|
![](templates/subSilver/images/spacer.gif) |
aroman
Joined: 06 Oct 2003 Posts: 12
|
|
Posted: Tue Nov 18, 2003 10:22 am |
|
|
Yes, my crystal is 76.8 kHz (PIC is in low power mode). Is there a problem with this? That's why I used the internal RC for the clock of the A/D converter.
If I only remove the line that you told me, the result of the 8-bit resolution A/D conversion will be stored in the valor_sensor variable? |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Nov 18, 2003 10:25 am |
|
|
No problem with that. I was just making sure that it was a typo. I like to run them as fast as I can. I haven't used the internal RC for the ADC but I think you have everything set correctly. It should read the adc result properly. Try it and let us know. |
|
![](templates/subSilver/images/spacer.gif) |
aroman
Joined: 06 Oct 2003 Posts: 12
|
|
Posted: Tue Nov 18, 2003 10:29 am |
|
|
OK Mark, thank you very much for your help. I will try it and I will let you know how it goes |
|
![](templates/subSilver/images/spacer.gif) |
aroman
Joined: 06 Oct 2003 Posts: 12
|
|
Posted: Tue Nov 18, 2003 8:50 pm |
|
|
Bad news: I removed the line that Mark recommended me but I am still getting a result of the A/D conversion that is wider than 8 bits-wide (or at least, that is how I am interpreting it).
If I use left-justification of the result (bit 7 of adcon1 register -ADFM- is 0, as the default), and the result is stored in adresh, if I read the value of adresl, it should always be 0x00, right? Am I correct? However, when I read the value of adresl, I get different non-zero values like 80, C0 and 40 (just the 2 MSBs are sometimes changing to 1).
On figure 11-4, on page 116 of the PIC16F876 data sheet, it says that if the result is left-justified the MSB of the A/D conversion is bit 7 of adresh, and the LSB is bit 6 of adresl. If this is correct, if I set the A/D conversion with 8 bits resolution, bits 6 and 7 of adresh should always be 00, right? Am I correct? However, when I read the value of adresh, I get different non-zero values on those bits, because the values of the whole adresh register are 55, 5A, 53..., in general 5X, and since the high nibble of adresh is a 5 (0101 in binary), bit 6 is 1 (and not zero, as it should be)
Please tell me if I am right, or if I am missinterpreting the results. I think that it is not possible to tell the PIC to use 8 bits of resolution in the A/D conversion instead of 10. Or is my problem somewhere else? |
|
![](templates/subSilver/images/spacer.gif) |
astus
Joined: 17 Nov 2003 Posts: 12 Location: Australia
|
|
Posted: Tue Nov 18, 2003 10:07 pm |
|
|
Why not try clamping the analog input with a zener diode and a resistor so that only voltages <8-bits are avaliable for digitization?
Adam |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Nov 18, 2003 11:02 pm |
|
|
Before you get led totally off the track:
1. The 16F877 hardware always generates a 10-bit result.
2. The #device adc=8 statement tells the compiler to
take the 10-bit result and return the upper 8 bits of it,
when using the read_adc() function.
Quote: | I would like to have 8 bits resolution in the A/D conversion (not the 10 bits resolution that is by default), and store the result in an 8 bit-wide variable. What do I need to modify in my code to acheive that? |
The following code should work. But I'm not at the company
right now, so I can't test it. (ie., with respect to using a very
low frequency crystal and LP mode).
#include <16F877.H>
#device adc=8 // Tell compiler to return 8 bits from read_adc()
#use delay(clock=76800)
#fuses LP,NOWDT,PUT,NOPROTECT,NOBROWNOUT,NOLVP
main()
{
char result;
setup_adc_ports(RA0_ANALOG);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(0);
result = read_adc();
while(1);
} |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Nov 18, 2003 11:24 pm |
|
|
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
The result is always 10 bits
Just use the result in the adresh register. That is effectively your 8 bit adc value. Just ignore the 2 bits in the adresl register. They are the least significant bits. They represent the very small changes in the analog signal which you do not care about since you want only 8 bits. |
|
![](templates/subSilver/images/spacer.gif) |
|