Strömmätare
Strömmätare
Jag har byggt en strömmätare, eller vad man ska kalla det. Det är en apparat som visar momentan ström (A), samt Ah, integrerat över tid. Den har två "kanaler" med en gemensam ledare, eller typ änna liksom... se bilden så blir det kanske förståligt
Tanken är att jag ska ha den i båten. Den gemensamma ledaren kopplas till batteriet, och ledarna märkta 1 och 2 kopplas till varsin plusskena. Då kan man mäta förbrukningen på olika förbrukare (kylskåp, belysning etc) och laddare (generator, solpanel, etc). Det är tänkt att man lätt ska kunna koppla om en förbrukare/laddare mellan de olika skenorna medelst strömbrytare, så om man tex vill se skillnaden mellan vad solpanelen ger och vad kylskåpet drar så lägger man över dem på samma skena. Vill man istället se dem individuellt lägger man dem ensamma på en skena.
Displayen visar som sagt A och Ah för varje kanal. Det finns två resetknappar för att nollställa respektive kanal.
Processor: atmega88
strömsensorer: ACS0752-SCA050 halleffektssensorer.
Displayen är en helt vanlig sodjan-display
Jag känner mig riktigt nöjd just nu. Får hoppas att den funkar lika bra IRL som på testbänken.
Då äre bara plusgrader som saknas för att man ska börja med båten... det brukar det ju vara gott om vid den här tiden men...
Tanken är att jag ska ha den i båten. Den gemensamma ledaren kopplas till batteriet, och ledarna märkta 1 och 2 kopplas till varsin plusskena. Då kan man mäta förbrukningen på olika förbrukare (kylskåp, belysning etc) och laddare (generator, solpanel, etc). Det är tänkt att man lätt ska kunna koppla om en förbrukare/laddare mellan de olika skenorna medelst strömbrytare, så om man tex vill se skillnaden mellan vad solpanelen ger och vad kylskåpet drar så lägger man över dem på samma skena. Vill man istället se dem individuellt lägger man dem ensamma på en skena.
Displayen visar som sagt A och Ah för varje kanal. Det finns två resetknappar för att nollställa respektive kanal.
Processor: atmega88
strömsensorer: ACS0752-SCA050 halleffektssensorer.
Displayen är en helt vanlig sodjan-display
Jag känner mig riktigt nöjd just nu. Får hoppas att den funkar lika bra IRL som på testbänken.
Då äre bara plusgrader som saknas för att man ska börja med båten... det brukar det ju vara gott om vid den här tiden men...
Senast redigerad av simon78 8 januari 2008, 21:00:52, redigerad totalt 1 gång.
- JimmyAndersson
- Inlägg: 26308
- Blev medlem: 6 augusti 2005, 21:23:33
- Ort: Oskarshamn (En bit utanför)
- Kontakt:
Riktigt intressant projekt inför båtsäsongen. Har länge funderat på att göra något liknande för kommersiella kostar ju runt 2000-3000 kr. Har du lust att beskriva lite mer vad du gjort? Det jag undrar över är vad noggranheten är, och då framför allt när man laddar batteriet under motorgång.
Var har du hittat kretsarna. ELFA har inte Allegros sortiment.
Var har du hittat kretsarna. ELFA har inte Allegros sortiment.
Sensorerna köpte jag av någon här på forumet. Har inte sett dem på någon annat ställe. Jag kan lägga upp lite mer info (bla källkoden) senare, men det tar nog någon dag. Nogrannhet och dyligt kan jag inte uttala mig om riktigt än. Jag vet att den drar sig lite, kanske 1Ah per dygn på den ena sensorn. Jag har inte kalibrerat för sensorernas olikheter utan bara omvandlat enligt databladet.
-
- Inlägg: 49
- Blev medlem: 7 augusti 2005, 15:01:30
-
- Inlägg: 49
- Blev medlem: 7 augusti 2005, 15:01:30
Ser bra ut.
Har liknade i vår segelbåt, Byggd av en polare,
Den är rätt smart håller koll på allt möjligt så som startström laddning/urladdning, kan se ifall jag kan fixa lite bilder på den.
En sak som den har är tid kvar till 40% kan va bra att ha koll på. Har även ingångar för nivåmätning för färskvatten/diesel.
Har liknade i vår segelbåt, Byggd av en polare,
Den är rätt smart håller koll på allt möjligt så som startström laddning/urladdning, kan se ifall jag kan fixa lite bilder på den.
En sak som den har är tid kvar till 40% kan va bra att ha koll på. Har även ingångar för nivåmätning för färskvatten/diesel.
- EagleSpirit
- Inlägg: 1288
- Blev medlem: 27 maj 2003, 23:15:48
- Ort: Västerås
- Kontakt:
Ja... jag har funderat lite på nogranheten och kalibreringen.... Som det är nu har jag bara räknat ut en omvandlingsfaktor från ADCn till A. Jag har gjort det med räta linjens ekvation ur grafen i databladet. Det blir inte helt optimalt. Jag försökte göra en funktionsanpassning. Tyckte att grafen såg ut att vara väldigt lik en sigmoid eller logistisk funktion eller vad man behagar att kalla den. Men anpassningen blev inget vidare.
Helst skulle man ju för varje givare ta ett 15-tal mätpunkter vid känd ström och använda ADC-värdena för kalibrering. Man kan ju enklast kanske köra räta linjer mellan dem, eller kanske en funktionsanpassning.
Möjligen blir det bättre med en shunt. Dessa givare fluktuerar ganska mycket (tror det är givarna iallafall). De ligger ganska bra runt medel, men det kan skilja +-0.02A per mätning. Inte mycket kanske, men det gör att den integrerade förbrukningen "drar sig".
Helst skulle man ju för varje givare ta ett 15-tal mätpunkter vid känd ström och använda ADC-värdena för kalibrering. Man kan ju enklast kanske köra räta linjer mellan dem, eller kanske en funktionsanpassning.
Möjligen blir det bättre med en shunt. Dessa givare fluktuerar ganska mycket (tror det är givarna iallafall). De ligger ganska bra runt medel, men det kan skilja +-0.02A per mätning. Inte mycket kanske, men det gör att den integrerade förbrukningen "drar sig".
-
- Inlägg: 49
- Blev medlem: 7 augusti 2005, 15:01:30
Börja inte tvivla nu när jag fått tag i kretsarna kompis! 0.02A spelar väl ingen större roll när du har ett batteri på 80Ah? Du kan mäta fel i 80/0.02=4000 timmar typ. Räcker att man "nollar" varje gång man vet att batterierna är fulla så är det ju ok.
Det jag skulle säga var ivf att jag lyckades sampla 4st! Hur går det med C-koden förresten?
Tack å hej!
Det jag skulle säga var ivf att jag lyckades sampla 4st! Hur går det med C-koden förresten?
Tack å hej!
-
- Inlägg: 49
- Blev medlem: 7 augusti 2005, 15:01:30
Hej igen! Jag funderade över dessa punkter:
1. Det här med att det hoppar mellan 0.02A, det kan ha att göra med att du måste skärma av din ATMega?! (Testa att koppla mätpinnen till Jord tex för att se om den blir stabil då...)
2. Vad använder du för referens när du använder ADC'n? Är den stabil?
3. AtMega88 klarar att mäta 0 till 5V i tio bitar. Alltså skillnaden på en bit blir ungefär 5mV. Detta motsvarar 0.125A enligt formeln nedan. Dessutom har ATMega88 +-2 LSB absolute accuracy vilket alltså blir +- 20mV => +-0.25A.
4. Om du inte skall mäta större strömmar än 50A så behöver du ju inte bry dig om hela grafen => Kollar man i grafen i databladet så är den ju linjär i mätområdet -50 till +50A. Denna formeln blir bäst: I = 25*U -62.5 Kan du inte testa det.
Kan du inte kommentera dessa pukter, tack
1. Det här med att det hoppar mellan 0.02A, det kan ha att göra med att du måste skärma av din ATMega?! (Testa att koppla mätpinnen till Jord tex för att se om den blir stabil då...)
2. Vad använder du för referens när du använder ADC'n? Är den stabil?
3. AtMega88 klarar att mäta 0 till 5V i tio bitar. Alltså skillnaden på en bit blir ungefär 5mV. Detta motsvarar 0.125A enligt formeln nedan. Dessutom har ATMega88 +-2 LSB absolute accuracy vilket alltså blir +- 20mV => +-0.25A.
4. Om du inte skall mäta större strömmar än 50A så behöver du ju inte bry dig om hela grafen => Kollar man i grafen i databladet så är den ju linjär i mätområdet -50 till +50A. Denna formeln blir bäst: I = 25*U -62.5 Kan du inte testa det.
Kan du inte kommentera dessa pukter, tack
1. JAg har inte gjort några ansträgningar för att avskärma ADCn och höja tillförlitligheten på värdet där. Jag har bara kopplat en liten konding enligt strömmätarens datablad. Jag kunde ju säkert höjt nogrannheten genom att cadda ett bättre lämpat kort. Kanske man kunde låtit uP:n gått ner i ADC-läge, eller vad det kallas. Det kan finnas lite att vinna här faktiskt.
2. Referensen är Vcc. (om jag fattat rätt)
3&4. Du verkar ha lite rätt här. Jag funderar på hur jag har räknat i koden egentligen. Jag räknar ju till I från ADC så att säga.
Jag har inte tid att kolla igenom så jättenoga just nu (mätaren är i bruk och jag ska se på film) Jag klistrar in koden så länge så kan du kanske komma vidare lite. Tackar för dina kommentarer.
2. Referensen är Vcc. (om jag fattat rätt)
3&4. Du verkar ha lite rätt här. Jag funderar på hur jag har räknat i koden egentligen. Jag räknar ju till I från ADC så att säga.
Jag har inte tid att kolla igenom så jättenoga just nu (mätaren är i bruk och jag ska se på film) Jag klistrar in koden så länge så kan du kanske komma vidare lite. Tackar för dina kommentarer.
Kod: Markera allt
Ja, jag använder sprintf... jag vet men den är praktisk..
/*
* C Implementation: main
*
* Description:
*
*
* Author: Simon Lundell <simon.lundell@his.se>, (C) 2006
*
* Copyright: See COPYING file that comes with this distribution
*
*/
#include "stdio.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include "lcd/lcd.h"
#define RESET_DELAY_TICKS 100
#define RESET_BUTTON1_DDR DDRB
#define RESET_BUTTON1_PORT PORTB
#define RESET_BUTTON1_PIN PINB
#define RESET_BUTTON1_PIN_ID PB1
#define RESET_BUTTON2_DDR DDRB
#define RESET_BUTTON2_PORT PORTB
#define RESET_BUTTON2_PIN PINB
#define RESET_BUTTON2_PIN_ID PB2
static uint8_t reset_counter1;
static uint8_t reset_counter2;
/*************************************************************************
delay loop for small accurate delays: 16-bit counter, 4 cycles/loop
*************************************************************************/
static inline void _delayFourCycles(unsigned int __count)
{
if ( __count == 0 )
__asm__ __volatile__( "rjmp 1f\n 1:" ); // 2 cycles
else
__asm__ __volatile__ (
"1: sbiw %0,1" "\n\t"
"brne 1b" // 4 cycles/loop
: "=w" (__count)
: "0" (__count)
);
}
/*************************************************************************
delay for a minimum of <us> microseconds
the number of loops is calculated at compile-time from MCU clock frequency
*************************************************************************/
#define delay(us) _delayFourCycles( ( ( 1*(F_CPU/4000) )*us)/1000 )
volatile static double centiamps_sens1;
volatile static double centiamps_sens2;
volatile static double centiamp_hours_sens1;
volatile static double centiamp_hours_sens2;
volatile static uint32_t irq_ticks_sens1;
volatile static uint32_t irq_ticks_sens2;
SIGNAL(SIG_OVERFLOW2){ //called continously
++irq_ticks_sens1;
++irq_ticks_sens2;
if (bit_is_clear(RESET_BUTTON1_PIN, RESET_BUTTON1_PIN_ID)){
if (reset_counter1++ > RESET_DELAY_TICKS){
centiamp_hours_sens1=0;
}
} else{
reset_counter1=0;
}
if (bit_is_clear(RESET_BUTTON2_PIN, RESET_BUTTON2_PIN_ID)){
if (reset_counter2++ > RESET_DELAY_TICKS){
centiamp_hours_sens2=0;
}
} else{
reset_counter2=0;
}
}
void inline init_timer2(){
ASSR |= _BV(AS2); //Use external crystal as clock
TCCR2B |= /*_BV(CS21) |*/ _BV(CS20); //prescaler 32 => 4/s (256 ticks between irqs)
TIMSK2 |= _BV(TOIE2); //Overflow interrupt enable
}
void adc_init(){
PRR &= ~_BV(PRADC);
ADCSRA |= _BV(ADEN);
ADCSRA &= ~_BV(ADATE);
ADMUX &= ~_BV(ADLAR);
ADCSRA &= ~_BV(ADIE);
ADCSRA |= _BV(ADPS0) |_BV(ADPS1) |_BV(ADPS2); //slow but accurate, highest prescaler
}
void adc_select_sens1(){
ADMUX &= ~_BV(MUX2) & ~_BV(MUX3);
ADMUX |= _BV(MUX0) | _BV(MUX1);
}
void adc_select_sens2(){
ADMUX &= ~_BV(MUX0) & ~_BV(MUX2) & ~_BV(MUX3);
ADMUX |= _BV(MUX1);
}
uint16_t adc_read(){
ADMUX |= _BV(REFS0);
ADMUX &= ~_BV(REFS1);
// ADCSRA |= _BV(ADIF);
ADCSRA |= _BV(ADSC);
while(bit_is_set(ADCSRA,ADSC));
// CAUTION: MUST READ ADCL BEFORE ADCH!!!
return (ADCL | ADCH<<8); // read ADC (full 10 bits);
}
double adc_to_centiamps(uint16_t adc){
if ((adc<102) || (adc>921))
return ((double)adc-512.0)*24.90;
else
return ((double)adc-512.0)*12.20703125;
//Taken from primari-A-to-voltage graph in datasheet
// 204,8 ADC-bits / 2500 cA = 12.20703125
}
inline void update_display(){
static char buffer1[16];
static char buffer2[16];
sprintf(buffer1,"%-+3.1fA %-+3.1fA",centiamps_sens1/100.0,centiamps_sens2/100.0);
sprintf(buffer2,"%-+3.2fAh %-+3.2fAh",centiamp_hours_sens1/100.0,centiamp_hours_sens2/100.0);
lcd_clrscr();
lcd_puts(buffer1);
lcd_gotoxy(0,1);
lcd_puts(buffer2);
}
int main(){
RESET_BUTTON1_DDR &= ~_BV(RESET_BUTTON1_PIN_ID);
RESET_BUTTON1_PORT |= _BV(RESET_BUTTON1_PIN_ID);
RESET_BUTTON2_DDR &= ~_BV(RESET_BUTTON2_PIN_ID);
RESET_BUTTON2_PORT |= _BV(RESET_BUTTON2_PIN_ID);
adc_init();
init_timer2();
sei();
lcd_init(LCD_DISP_ON);
static uint16_t adc;
adc_select_sens1();
adc = adc_read();
centiamps_sens1 = -adc_to_centiamps(adc);// - sign to correct for physical orientation of sensor
adc_select_sens2();
adc = adc_read();
centiamps_sens2 = +adc_to_centiamps(adc);// + sign to correct for physical orientation of sensor
for (;;) {
adc_select_sens1();
adc = adc_read();
centiamps_sens1 += (-adc_to_centiamps(adc)-centiamps_sens1)/30.0;// - sign to correct for physical orientation of sensor
centiamp_hours_sens1 += (-adc_to_centiamps(adc)*irq_ticks_sens1)/460800.0;// - sign to correct for physical orientation of sensor
irq_ticks_sens1=0;
adc_select_sens2();
adc = adc_read();
centiamps_sens2 += (+adc_to_centiamps(adc)-centiamps_sens2)/30.0;// + sign to correct for physical orientation of sensor
centiamp_hours_sens2 += (+adc_to_centiamps(adc)*irq_ticks_sens2)/460800.0;// + sign to correct for physical orientation of sensor
irq_ticks_sens2=0;
update_display();
}
return 0;
}