ADC Acquisition Requirements for pic microcontroller - Page 1
Forum section: electronics / general

author: jerryk
author_rank: Regular Contributor
timestamp: February 09, 2022, 07:44:51 pm
content: I am using a pic16f18875 to read the battery voltages of a 4s lithium pack. I'm using RA0-RA3 as the inputs. The inputs use a voltage divider where R1 = 4.7K and R2 = 1K. It works fine, however I would like to use R1 = 470K and R2 = 100K. If I do then it fails to read correctly.I have looked at the ADC Acquisition Requirements and it states in Note 3 "The maximum recommended impedance for analog sources is 10 k. This is required to meet the pin leakage specification."Yet I have a BMS that uses pic16lf1829 which succesfully reads the cell voltages in the same manner as my attempt with a voltage divider where R1 = 976K and R2 = 276K.I have tried adding 10ms of delay for acquisition but I must be doing something plenty wrong here. Any direction would be great.Here is the code that works at the lower resistor values but fails using the higher values.Code:void main(void){//Initialize functions - as if you could not figure that outSYSTEM_Initialize();Lcd4_Init();ADCC_Initialize();float resistorOne = 4700;float resistorTwo = 1000;float convertedValue = 0;float resistorRatio = 0;float convertedValue1 = 0;float convertedValue2 = 0;float convertedValue3 = 0;//float convertedValue4 = 0;float result1 = 0;float result2 = 0;float result3 = 0;float result4 = 0;//ADCC_Initialize(); move this to mainresistorRatio = (resistorTwo / (resistorOne + resistorTwo));Lcd4_Clear();Lcd4_Set_Cursor(1, 1);Lcd4_Write_String("Battery");Lcd4_Set_Cursor(2, 1);Lcd4_Write_String("Simulation");__delay_ms(1500);Lcd4_Clear();while (1){ADCC_StartConversion(channel_ANA0);while (!ADCC_IsConversionDone());__delay_ms(10);convertedValue = ADCC_GetConversionResult();voltage0 = ((convertedValue) * 5 / 1024) / resistorRatio;ADCC_StopConversion();ADCC_StartConversion(channel_ANA1);__delay_ms(10);while (!ADCC_IsConversionDone());convertedValue1 = ADCC_GetConversionResult();voltage1 = ((convertedValue1) * 5 / 1024) / resistorRatio;ADCC_StopConversion();//__delay_ms(10);ADCC_StartConversion(channel_ANA2);__delay_ms(10);while (!ADCC_IsConversionDone());convertedValue2 = ADCC_GetConversionResult();voltage2 = ((convertedValue2) * 5 / 1024) / resistorRatio;ADCC_StopConversion();//__delay_ms(10);ADCC_StartConversion(channel_ANA3);__delay_ms(10);while (!ADCC_IsConversionDone());convertedValue3 = ADCC_GetConversionResult();voltage3 = ((convertedValue3) * 5 / 1024) / resistorRatio;ADCC_StopConversion();//__delay_ms(10);result1 = voltage0 - voltage1;result2 = voltage1 - voltage2;result3 = voltage2 - voltage3;result4 = voltage3;Lcd4_Set_Cursor(1, 0);sprintf(Buffer, "%.2f", result1);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");Lcd4_Set_Cursor(1, 8);sprintf(Buffer, "%.2f", result2);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");Lcd4_Set_Cursor(2, 0);sprintf(Buffer, "%.2f", result3);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");Lcd4_Set_Cursor(2, 8);sprintf(Buffer, "%.2f", result4);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");}}Jerry

author: woofy
author_rank: Frequent Contributor
timestamp: February 09, 2022, 08:34:36 pm
content: Its the energy needed to charge the acquisition capacitor. Microchip normally say a max. impedance of 10k to charge it within an acceptable time.Just put a 100n capacitor across the 100k and it should be fine.

author: Ian.M
author_rank: Super Contributor
timestamp: February 09, 2022, 09:03:46 pm
content: Connect min. 22nF between the ADC input pin and AVSS.This provides a low (AC) impedance during sampling so CHOLDcan charge properly. The minimum capacitance required is:CEXT>2(N+1)CHOLDand for this PIC, its a 10 bit ADC (N=10) with a 10pF CHOLD, hence the above suggested value for no more than LSB/2 error due to charge redistribution during sampling. Of course Woofy's suggestion of 100nF is equally valid, if you don't mind >60ms settling time for an input voltage change (assuming worst case a step change from 0 to full scale).N.B. This doesn't help with DC offsets caused by input leakage current flowing through the Thevenin equivalent resistance of your potential divider. The leakage current is specced (Param D341) as +/-1uA worst case, and for your divider, fortunately the offset error will be much smaller than the resolution. That would not be the case if you increased the potential divider resistors by an order of magnitude to reduce the quiescent drain.

author: jerryk
author_rank: Regular Contributor
timestamp: February 09, 2022, 09:22:11 pm
content: The end goal is not just to read the cell voltages but to reverse engineer the code that runs the microcontroller in that schematic below. The inputs they use are high impedance voltage dividers with respect to the datasheet. The pic used in the schematic has the same max 10K impedance recommendation. The input labeled pic12 uses 976K for R1 with no external capacitance to make up for the internal capacitor charge state. I assume the reasoning is that this BMS is for a battery and they are trying to minimize current consumption for the sake of battery life.I don't think acquisition time matters much here and it seems like I must not be using the delay times correctly.Jerry

author: Ian.M
author_rank: Super Contributor
timestamp: February 09, 2022, 09:57:48 pm
content: They are probably waiting something like 1ms after changing channels, discarding the first reading then averaging several, taken at approx 1ms intervals. They may also be using FRCfor the ADC clock and converting in sleep to minimize disturbance due to on-chip noise sources. Self-calibration after firmware load in a jig that simulates a battery at a known state of charge may be required to get acceptable results. It still doesn't make it good practice to Muntz your hardware design like that then bodge it in software.Note that if you want to minimize loading on the battery, especially unbalanced inter-cell loading, you've got two more options - disconnect the potential dividers when not actually taking readings with high-side MOSFETs, which then require a level-shifting driver (which can probably be shared for all of them) or use sub-nanoamp bias current OPAMPs as buffers which lets you use dividers with resistors of tens of megaohms, while keeping the ADC's source resistance low.

author: errorprone
author_rank: Contributor
timestamp: February 10, 2022, 12:14:38 am
content: It seems like you are using MCC generated code and from what I remember the acquisition time is set in ADCC_Initialize(). ADCC_StartConversion() changes the input multiplexer and applies the acquisition time and waits for the conversion to complete so I think the 10ms delay might be applied in the wrong spot.

author: jerryk
author_rank: Regular Contributor
timestamp: February 10, 2022, 01:26:54 am
content: I am using MCC but I went ahead and changed it to be more readable code I hope. I did change the acquisition time in ADCC_Initialize() to the max 255 (ADACQ = 0xFF;) and that actually worked for the 470K/100K divider. I then changed the divider to 1M/220K to approximate the the potential divider at pic12 in the schematic and now the voltage reads around 0.15V high on the display. That's too much error when you are talking setting voltage cut off limits in lithium cells.I did move the delay time code to just after the adc channel slelction and set it to 20ms to see if that would help.JerryNew code as follows:Code:#include "mcc_generated_files/mcc.h"#include "lcd_jerry.h"#include <stdio.h>void Delay_Seconds(unsigned char z){unsigned char x,y;for (y = 0; y < z; y++){for (x = 0; x < 100; x++) __delay_ms(10);}}//*************************Declare Global Variables*******************float voltage;float voltage0;float voltage1;float voltage2;float voltage3;float voltage4;char Buffer;/*Main application*/void main(void){//Initialize functions - as if you could not figure that outSYSTEM_Initialize();Lcd4_Init();ADCC_Initialize();float resistorOne = 4700;float resistorTwo = 1000;float resistorRatio = 0;float resistorOneTest = 1010;float resistorTwoTest = 222;float resistorRatioTest = 0;float convertedValue = 0;float convertedValue1 = 0;float convertedValue2 = 0;float convertedValue3 = 0;//float convertedValue4 = 0;float result1 = 0;float result2 = 0;float result3 = 0;float result4 = 0;//ADCC_Initialize(); move this to mainresistorRatio = (resistorTwo / (resistorOne + resistorTwo));resistorRatioTest = (resistorTwoTest / (resistorOneTest + resistorTwoTest));Lcd4_Clear();Lcd4_Set_Cursor(1, 1);Lcd4_Write_String("Battery");Lcd4_Set_Cursor(2, 1);Lcd4_Write_String("Simulation");__delay_ms(1500);Lcd4_Clear();// Turn on the ADC moduleADCON0bits.ADON = 1;while (1){// select the A/D channelADPCH = 0x0;__delay_ms(20);// Start the conversionADCON0bits.ADGO = 1;while (ADCON0bits.ADGO);convertedValue = ((adc_result_t) ((ADRESH << 8) + ADRESL));voltage0 = ((convertedValue) * 5 / 1024) / resistorRatioTest;ADCON0bits.ADGO = 0;// select the A/D channelADPCH = 0x1;__delay_ms(20);// Start the conversionADCON0bits.ADGO = 1;while (ADCON0bits.ADGO);convertedValue = ((adc_result_t) ((ADRESH << 8) + ADRESL));voltage1 = ((convertedValue) * 5 / 1024) / resistorRatio;ADCON0bits.ADGO = 0;// select the A/D channelADPCH = 0x2;__delay_ms(20);// Start the conversionADCON0bits.ADGO = 1;while (ADCON0bits.ADGO);convertedValue = ((adc_result_t) ((ADRESH << 8) + ADRESL));voltage2 = ((convertedValue) * 5 / 1024) / resistorRatio;ADCON0bits.ADGO = 0;// select the A/D channelADPCH = 0x3;__delay_ms(20);// Start the conversionADCON0bits.ADGO = 1;while (ADCON0bits.ADGO);convertedValue = ((adc_result_t) ((ADRESH << 8) + ADRESL));voltage3 = ((convertedValue) * 5 / 1024) / resistorRatio;ADCON0bits.ADGO = 0;//Converts to individual cell voltageresult1 = voltage0 - voltage1;result2 = voltage1 - voltage2;result3 = voltage2 - voltage3;result4 = voltage3;//Display the resultsLcd4_Set_Cursor(1, 0);sprintf(Buffer, "%.2f", result1);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");Lcd4_Set_Cursor(1, 8);sprintf(Buffer, "%.2f", result2);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");Lcd4_Set_Cursor(2, 0);sprintf(Buffer, "%.2f", result3);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");Lcd4_Set_Cursor(2, 8);sprintf(Buffer, "%.2f", result4);Lcd4_Write_String(Buffer);Lcd4_Write_String(" V");}}/**End of File*/