![]() | ![]() | ![]() |
| |||||||
| Micro Controllers Discuss all aspects of micro controllers - building them, coding them, etc. All controllers are welcome - PIC, BASIC, Z8 Encore!, etc. |
![]() |
| | Tools |
| | #1 |
|
Hi Guys, This part of my project involves measuring solar panel voltage and current flow into a battery. I've done all the coding and used a voltage divider (120k and 22k resistors) to get the correct ADC input levels, and for the ammeter part am simply measuring the voltage drop over a 1 ohm resistor in the system. My problem arises from the fact the panel voltage is a sinusoidal but around a constant voltage (at that insolation level). A regular multimeter in DC V mode says e.g. "13.7v", but an oscilloscope shows it varies by 400mv, centered on that value. I want my PIC voltmeter to read 13.7v and stay there, but instead it (accurately) varies from 13.5 -> 13.9v in time with the panel I dont want this. Why does my multimeter read it as a stable voltage, and how can I adapt my PIC system to do the same? I have the same problem when using it as an ammeter. The multimeter gives a constant 10mA, my PIC varies (but more substantially than before). Thanks. | |
| |
| | #2 |
|
You have another thread dealing with the multimeter side of the post so lets keep that side of it there. For the voltage display using a PIC I would average several reading over time to give you a stable display. The time and sample rate depends on the frequency. There may be other methods. The problem is mostly the same for the ammeter. Did you know that maxim/dallas (and others) make battery management chips. They directly read the voltage and can keep track of current/charge. For the $3 or $5 I would let one of them do the work. EDIT: I am far from an expert on this.
__________________ Please post questions to the forums. PM's are for personal communication. BCHS/3v0's Tutorials Junebug USB PIC programmer kit., USB Bit Whacker, The 15 Minute Printed Circuit Board! (+drill time) Last edited by 3v0; 22nd February 2009 at 12:56 AM. | |
| |
| | #3 |
|
16 or 32 bit accumulator 16 bit value called filter 8 bit value called shift accumulator -= filter; accumulator += adc_reading; filter = accumulator>>shift; The higher "shift" is the higher the time constant of the filter. The first time you take a reading you should set accumulator to that value after shifting it left "shift" places
__________________ A rectangular bear is just a polar bear after a coordinate transform. -- I dunno who. A recent study shows that research causes cancer in rats. -- I dunno who said that one either. | |
| |
| | #4 |
|
thanks for the responses. noggin, i tried using your code, but get an incomplatible type error after declaring them as int (tried other declarations too). have set the three types as int arrays of the lengths you specified the last line gives an invalid operands to binary >> error please help! | |
| |
| | #5 |
|
much easier to help if you post your code and put [ code] and [ /code] tags around them (without the spaces)
__________________ A rectangular bear is just a polar bear after a coordinate transform. -- I dunno who. A recent study shows that research causes cancer in rats. -- I dunno who said that one either. Last edited by Noggin; 22nd February 2009 at 02:13 PM. | |
| |
| | #6 |
|
Ive just adapted microchip's voltmeter tutorial program to the adc channel i need: Code: #include "system.h"
/*****************************************************************************
* Module Constants Definitions
*****************************************************************************/
//ADC channels numbers
#define ADC_TEMPERATURE ADC_TEMP_CHAN
//#define ADC_VOLTAGE ADC_VOLT_CHAN
// Delay after input switching
#define ADC_SWITCH_DELAY 400
// Temperature scale switching period
#define ADC_CELSIUS_DELAY 100
// Number position
#define ADC_POS_NUMBER 10
// Temperature scale sign position
#define ADC_POS_SCALE 15
// Memory sign position
#define ADC_POS_MEM 13
// Reference voltage, mV
#define AVCC 3300
/*****************************************************************************
* Arrays: _voltage_str and _temperature_str
*
* Overview: These arrays contains voltage and temperature strings.
*
*****************************************************************************/
char _voltage_str[16] = "Voltage = V";
char _temperature_str[16] = "Temp = C";
unsigned char _uADCState;
unsigned int _uADCWait;
// Period to switch temperature scale
unsigned int _uADCCelsiusWait;
// Current temperature scale
unsigned char _uADCCelsius;
// Buffer to filter temperature sensor value
unsigned int _uADCTemperature;
// Switch to get temperature data from EEPROM rather than from ADC
unsigned char _uADCFromMemory;
/*****************************************************************************
* Function: ADCInit
*
* Preconditions: None.
*
* Overview: This function initiates ADC and state mashine.
*
* Input: None.
*
* Output: None.
*
******************************************************************************/
void ADCInit(){
AD1CON1 = 0x80E4; //Turn on, auto sample start, auto-convert
AD1CON2 = 0; //AVdd, AVss, int every conversion, MUXA only
AD1CON3 = 0x1F05; //31 Tad auto-sample, Tad = 5*Tcy
AD1CHS = 0x0002;
TRISBbits.TRISB2 = 1;
TRISBbits.TRISB3 = 1;
AN_VOLT_PIN = 0; //Disable digital input on AN5
AN_TEMP_PIN = 0; //Disable digital input on AN4
AD1CSSL = 0; //No scanned inputs
_uADCWait = ADC_SWITCH_DELAY;
_uADCCelsiusWait = ADC_CELSIUS_DELAY;
_uADCState = 1;
_uADCCelsius = 0; //Show celsius scale
_uADCFromMemory = 0; //Show current temperature
}
/*****************************************************************************
* Function: ADCProcessEvents
*
* Preconditions: ADCInit must be called before.
*
* Overview: This is a state mashine to grab analog data from potentiometer
* and temperature sensor.
*
* Input: None.
*
* Output: None.
*
******************************************************************************/
void ADCProcessEvents(){
unsigned long Result;
unsigned long rlt;
switch(_uADCState){
case 1: // Convert result for potentiometer
// Wait for conversion to complete
while(!AD1CON1bits.DONE);
Result = (long) ADC1BUF0;
Result = (Result*AVCC)/(1024/0.64);
// new part not working
int accumulator [16];
int filter [16];
int shift [8];
accumulator = accumulator-filter;
accumulator = accumulator+AD1CHS;
filter = accumulator>>shift;
ADCShortToString((int)Result, 2, _voltage_str+ADC_POS_NUMBER);
// Sweep least significant digit
_voltage_str[ADC_POS_NUMBER+4] = ' ';
AD1CHS = ADC_TEMPERATURE;
_uADCState++;
break;
case 3: // Convert result for temperature
// Wait for conversion to complete
while(!AD1CON1bits.DONE);
Result = (long) ADC1BUF0;
// filter temperature Value
// _uADCTemperature = 15/16*_uADCTemperature + 1/16*New Sample
_uADCTemperature -= _uADCTemperature>>4;
_uADCTemperature += Result;
Result = _uADCTemperature>>4;
// Read temperature stored into EEPROM
_temperature_str[ADC_POS_MEM] = ' ';
if(ADCIsFromMemory()){
Result = ADCLoadTemperature();
_temperature_str[ADC_POS_MEM] = 'M';
}
if(0 == _uADCCelsiusWait--){
_uADCCelsiusWait = ADC_CELSIUS_DELAY;
_uADCCelsius ^= 1;
if(_uADCCelsius)
_temperature_str[ADC_POS_SCALE]='C';
else
_temperature_str[ADC_POS_SCALE]='F';
}
if(_uADCCelsius)
ADCShortToString((int)Result,3,_temperature_str+ADC_POS_NUMBER);
else{
// Convert Celsius temperatures into Fahrenheit
// Begin by multiplying the Celsius temperature by 9.
// Divide the answer by 5.
// Now add 32.
Result *= 9; Result /= 5; Result += 320;
ADCShortToString((int)Result, 3,_temperature_str+ADC_POS_NUMBER);
}
// Switch input to potentiometer
AD1CHS = 0x0002;
_uADCState++; break;
case 2:
case 4:
// Delay slot between channel switching
if(0 == _uADCWait--){
_uADCWait = ADC_SWITCH_DELAY;
_uADCState++;
}
break;
break;
default:
_uADCState = 1;
}
}
/*****************************************************************************
* Function: ADCShortToString
*
* Preconditions : None.
*
* Overview: The function converts integer into string.
*
* Input: Value - value to be converted; DotPos - dot position ( can be
* between 0 and 3, DOTPOS_NONE = 4, if equals DOTPOS_TRAIL_ZEROS = -1 will not
* put a dot and insert leading zeros); Buffer - receives the result string
*
* Output: None.
*
*****************************************************************************/
void ADCShortToString(int Value, char DotPos, char* Buffer){
char Result;
char Pos;
// Clean Buffer (4 digits + Dot)
for(Pos = 0; Pos < 5; Pos++) Buffer[Pos] = ' ';
Pos = 0;
if(Pos == DotPos){ *Buffer++ = '0';*Buffer++ = '.';}
Pos++;
Result = Value/1000;
Value -= 1000*Result;
if(Result) *Buffer++ = Result + '0';
else if(Pos >= DotPos) *Buffer++ = '0';
if(Pos == DotPos) *Buffer++ = '.';
Pos++;
Result = Value/100;
Value -= 100*Result;
if(Result) *Buffer++ = Result + '0';
else if(Pos >= DotPos) *Buffer++ = '0';
if(Pos == DotPos) *Buffer++ = '.';
Pos++;
Result = Value/10;
Value -= 10*Result;
if(Result) *Buffer++ = Result + '0';
else if(Pos >= DotPos) *Buffer++ = '0';
if(Pos == DotPos) *Buffer++ = '.';
Pos++;
//Result = Value/10;
//Value -= 10*Result;
if(Value) *Buffer++ = Value + '0';
else if(Pos >= DotPos) *Buffer++ = '0';
}
/*****************************************************************************
* Function: ADCStoreTemperature()
*
* Preconditions: SPIMPolInit and EEPROMInit must be called before.
*
* Overview: The function stores the current temperature into EEPROM.
*
* Input: None.
*
* Output: None.
*
*****************************************************************************/
void ADCStoreTemperature(){
unsigned Temp;
// Get temperature
Temp = _uADCTemperature>>4;
// Write MS byte into EEPROM address = 0
EEPROMWriteByte(Hi(Temp),0);
// Write LS byte into EEPROM address = 1
EEPROMWriteByte(Lo(Temp),1);
}
/*****************************************************************************
* Function: ADCLoadTemperature()
*
* Preconditions: SPIMPolInit and EEPROMInit must be called before.
*
* Overview: The function returns temperature stored by ADCStoreTemperature
* into EEPROM.
*
* Input: None.
*
* Output: Temperature value read from EEPROM.
*
*****************************************************************************/
unsigned ADCLoadTemperature(){
unsigned Temp;
// Read MS byte from EEPROM address = 0
Temp = EEPROMReadByte(0);
Temp = (Temp<<8)&0xff00;
// Read LS byte from EEPROM address = 1
Temp |= (EEPROMReadByte(1)&0x00ff);
// If there's not valid value replace it with 25.0 C
if(Temp > 990)
Temp = 250;
return Temp;
}
/*****************************************************************************
* Function: ADCSetFromMemory
*
* Preconditions: None.
*
* Overview: The function toggles switch to display temperature stored into EEPROM
* rather than current one.
*
* Input: None.
*
* Output: None.
*
*****************************************************************************/
void ADCSetFromMemory(){
_uADCFromMemory ^=1; // Toggle switch
}
/*****************************************************************************
* Function: ADCIsFromMemory
*
* Preconditions: None.
*
* Overview: The function returns mode of EEPROM displaying.
*
* Input: None.
*
* Output: It returns zero if the current temperature is displyed.
*
*****************************************************************************/
unsigned char ADCIsFromMemory(){
return _uADCFromMemory;
}
/*****************************************************************************
* EOF
*****************************************************************************/
Last edited by albad; 22nd February 2009 at 02:18 PM. | |
| |
| | #7 |
|
int accumulator [16]; int filter [16]; int shift [8]; These are not 16 or 8 bit values, these are arrays of integers.
__________________ A rectangular bear is just a polar bear after a coordinate transform. -- I dunno who. A recent study shows that research causes cancer in rats. -- I dunno who said that one either. | |
| |
| | #8 |
|
i have identified a mistake, and changed AD1CHS to AD1BUF0, and now I get a varying value on my screen, using accumulator as my "result". still have the same problem as before though, where my value wont stabilise like I want it to, any ideas? *i changed my arrays into "long" for the 16's and int for the 8, thanks Last edited by albad; 22nd February 2009 at 06:05 PM. | |
| |
| | #9 |
|
The accumulator is not the result, filter is. Accumulator will vary just as much as the adc reading does, however, it is much larger so its variation, percentage wise, is smaller. When you shift the value right by using the >> operator, you basically divide the variation by 2^shift
__________________ A rectangular bear is just a polar bear after a coordinate transform. -- I dunno who. A recent study shows that research causes cancer in rats. -- I dunno who said that one either. | |
| |
| | #10 |
|
Right back were you started. ![]() Go back to the start of the thread and read the posts again. The problem is the same as is the solution. This is a bit of code I use to illustrate how to calculate an average value. The code noggin provided works too and is much shorter. This may be easier to understand. Code: // format humidity
{
static unsigned char hum[10]; // array of read values
static unsigned char humPt=0; // index into above array
unsigned char idx;
/// take reading here and place in humidity
humindity = ........ ;
// place reading in array
hum[humPt++]=humidity;
humPt = humPt%10; // cycle from 0 to 9
humidity=0; //recalc humidity as ave of last 10 readings
for(idx=0;idx<10;idx++) // sum the readings
{
humidity +=hum[idx];
}
humidity=humidity/10;
fprintf(_H_USER, "%02i%%H ", humidity );
}
__________________ Please post questions to the forums. PM's are for personal communication. BCHS/3v0's Tutorials Junebug USB PIC programmer kit., USB Bit Whacker, The 15 Minute Printed Circuit Board! (+drill time) | |
| |
| | #11 | |
|
its still varying by the same amount as before, even using filter as my result to change into the char string to be outputted. it what you mentioned here in the code already or does it need to be added (i dont really understand it) Quote:
| ||
| |
| | #12 |
|
thanks 3v0 i'll have a look
| |
| |
| | #13 |
|
nope, seems to work fine initially, varying by .1 instead of .5 then when i turn the panel off and the voltage goes below 10 then if comes back up again! i really cant be arsed anymore i realise ive got my other thread running about the voltmeter, so can it please be deleted so i can have an answer in here about whether its possible to stabilise the reading with simple hardware? i know it would be simpler to do with a pre-made chip, but the idea of this project is to show that i can do this with the hardware given | |
| |
| | #14 |
|
YAZOO! it works, changing the ststic unsigned into a long i love you both, noggin and 3v0, (in a manly, man hug way) | |
| |
| | #15 |
|
I looked at this post and thought "volt/ammeter for solar system" was going to be something to measure current flowing between Jupiter and the Sun, something with solar wind and magnetic fields... or something...
| |
| |
|
| Tags |
| fluctuating, pic24, readings, solar, system, voltmeter or ammeter |
| Thread Tools | |
| Display Modes | |
| |
Similar | ||||
| Title | Starter | Forum | Replies | Latest |
| Solar tracking System for solar cells | bogdanfirst | Electronic Projects Design/Ideas/Reviews | 17 | 7th March 2009 10:51 AM |
| Solar Tracking system | timex83 | Electronic Projects Design/Ideas/Reviews | 21 | 12th June 2008 11:40 AM |
| Capturing serial data using PIC24 | nick2412 | Micro Controllers | 3 | 12th March 2007 10:32 PM |
| Building a Voltmeter/Ammeter for an Electric Bicycle | AusEbiker | Electronic Projects Design/Ideas/Reviews | 8 | 24th November 2006 09:51 AM |
| Charging System of Solar Car | devonsc | General Electronics Chat | 3 | 21st December 2004 05:43 AM |