Motorvärmartimer

Berätta om dina pågående projekt.
Användarvisningsbild
PaNiC
Inlägg: 2610
Blev medlem: 15 augusti 2003, 22:16:15
Ort: Skånelandet

Motorvärmartimer

Inlägg av PaNiC »

By popular request (en person iaf) kommer här en tråd om mitt pågående projekt, en pryl som själv räknar ut hur länge motorvärmaren behöver vara på beroende på utetemperatur och som sätter igång den i rätt tid.

Tanken är att man ska schemalägga när man ska nyttja bilen (och kunna göra tillfälliga ändringar) och timern sätter igång värmaren i lagom tid före.

Bild

Displayen ser ut så pga ett problem som jag har med följande kod:

Kod: Markera allt

#define MCU_CLK 7372800
#define cbi(port, shift) (port) &= ~(1<<shift)
#define sbi(port, shift) (port) |= (1<<shift) 
#define F_OSC 7372800
#define LCD_TIGHTER_TIMING

#define relay 0
#define light 1
#define	enter 4 
#define	hoger 8
#define	ner 16 
#define vanster 32
#define	upp 64

#include "avr/io.h"
#include "lcd_44780.h"
#include "lcd_44780.c"
#include "avr/interrupt.h"
#include "inttypes.h"
#include "avr/signal.h"
#include "onewire.c"
#include "onewire.h"


volatile unsigned char boolean = 0;
volatile unsigned char secs = 32;
volatile unsigned char mins = 0;
volatile unsigned char hrs = 0;
volatile unsigned int onsecs = 0;
float temperatur;



float gettemp()
	{
	if (ow_reset())
		{
		lcd_clear();
		lcd_home();
		lcd_puts(" Sensor error!  ");
		lcd_puts("Heater disabled");
		cbi(PORTD,relay);
		for(;;);
	
		}
	return temperatur;
	}


SIGNAL(SIG_TIMER1_COMPA)
	{
	boolean = !boolean;
	if ((PIND&enter) == 0)
		{
		cli();
		}
	if (boolean)
		{
		secs++;
		lcd_putchar(secs+'0');
		if (++secs >= 60)
			{
			secs = 0;
			mins++;
			}
		if (++mins >= 60)
			{
			hrs++;
			secs = 0;
			mins = 0;
			}
		if (++hrs >= 24);
			{
			hrs = 0;
			mins = 0;
			secs = 0;
			}
		}
	lcd_putchar(secs+'0');

	}


void setup()
	{
	ms_spin(500);
	lcd_init();
	lcd_puts("Init!");
	//sätt upp 2Hz-interrupt
	TCCR1B = (1<<CS12|1<<CS10|1<<WGM12); //systemklocka/1024
	TIMSK = (1<<OCIE1A);	//Compare1A enable
	OCR1A = 3600; //interrupt två ggr/sek
	DDRD = 0b00000011;
	lcd_home();
	lcd_clear();
	lcd_set_cursor(1,0); //on,blink
	ow_parasite_disable();
	//temperatur = gettemp();
	sei();
	}



int main(void) 
	{
	setup();
  //UART_init();
  	for(;;)
		{	
		/*if ((PIND&enter) == 0)
			{
			cli();
			}*/
		}	
	}
Problemet är att secs nollställs oavsett allting.
Knappen enter känns inte heller av i interruptet. Klistrar jag däremot in precis samma rader i mainloopen så går det bra.
Senast redigerad av PaNiC 4 januari 2006, 23:01:45, redigerad totalt 1 gång.
Användarvisningsbild
oJsan
EF Sponsor
Inlägg: 1541
Blev medlem: 11 november 2005, 21:36:51
Ort: Umeå
Kontakt:

Inlägg av oJsan »

Intressant! Ser fram emot att följa projektets utveckling!

när det gäller koden så vet jag inte vad jag ska tro.. inte helt lätt att se vad koden gör när man inte ser alla andra funktioner. Tänkbara fel är ju att timeravbrottet körs för ofta?! Dessutom så är jag lite tveksam på vad cli(); gör inuti en avbrottsfunktion?! Är det bara för att stoppa klockan eller? Har du kommit ihåg att deklarera ev. globala variabler som volatile?
Ett fel ser jag i alla fall, nämligen utskriften av sekunder. I vanliga fall så tar en putchar-funktion ett ascii-tecken men i din kod så skickar du ett heltal. Du måste dela upp sekunderna i ental och tiotal och sedan skicka till displayen med:

Kod: Markera allt

lcd_putchar(secs_tiotal+'0');
lcd_putchar(secs_ental+'0');
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Ytterligare tycker jag att det är dumt att enbart kolla "likamed".
Jag gör alltid såhär:

Kod: Markera allt

if(++Second >= 60)
  {
  Second = 0;
  if(++Minute >= 60)
    {
    Minute = 0;
    if(++Hour >= 24) Hour = 0;
    }
  }
Det fångar alla fel och sånt. if(++x > 10) betyder "räkna upp x och kolla sen om x > 10, if(x++ > 10) betyder kolla on X > 10 och räkna upp den sen.
Användarvisningsbild
PaNiC
Inlägg: 2610
Blev medlem: 15 augusti 2003, 22:16:15
Ort: Skånelandet

Inlägg av PaNiC »

oJsan: Tack, det löste vissa bekymmer.
cli(); har jag där för att testa om knappen läses av. Om knappen är intryckt ska interrupts stängas av och således kommer det inte att hända något mer på displayen.
Avbrottet körs två gånger per sekund, tycker att det skulle ge gott om tid att hinna med allt då klockan är på 7,3728MHz.
Varför ska globala variabler vara volatile och vad betydet det?

Icecap: Good thinking! Har ändrat det nu.

Problemen kvarstår dock. Knappen läses inte av och secs nollställs oavsett allt. Lägger upp hela koden.

Tack så långt!
Användarvisningsbild
oJsan
EF Sponsor
Inlägg: 1541
Blev medlem: 11 november 2005, 21:36:51
Ort: Umeå
Kontakt:

Inlägg av oJsan »

Kod: Markera allt

if (++hrs >= 24);
Prova att ta bort semikolonet på den raden så funkar det säkert mycket bättre! =)
Användarvisningsbild
PaNiC
Inlägg: 2610
Blev medlem: 15 augusti 2003, 22:16:15
Ort: Skånelandet

Inlägg av PaNiC »

Oj... :oops:

Attans, det jag inte gillar med C är att både ett semikolon för mycket och ett för lite kan ge så konstiga fel. Svåra att upptäcka är de också :D.

Tack!
Användarvisningsbild
oJsan
EF Sponsor
Inlägg: 1541
Blev medlem: 11 november 2005, 21:36:51
Ort: Umeå
Kontakt:

Inlägg av oJsan »

Ja sånt där blir man "blind" på när man själv felsöker... det krävs nästan att någon annan kollar koden för att man ska hitta felet. Eller så går man och lägger sig och kollar dagen efter istället! :wink:
Användarvisningsbild
PaNiC
Inlägg: 2610
Blev medlem: 15 augusti 2003, 22:16:15
Ort: Skånelandet

Inlägg av PaNiC »

Det här är dagen efter tyvärr :D.
Användarvisningsbild
oJsan
EF Sponsor
Inlägg: 1541
Blev medlem: 11 november 2005, 21:36:51
Ort: Umeå
Kontakt:

Inlägg av oJsan »

SIGNAL([hanterare]) är ett makro som "översätts" vid kompileringen.. En annan variant är INTERUPT([hanterare]). Skillnaden mellan SIGNAL() och INTERUPT() är att SIGNAL() disablar alla övriga avbrott under tiden den körs. Använder man INTERUPT() så är andra avbrott fortfarande tillåtna (avbrott i avbrotten alltså).
Problemet är alltså att kompilatorn(pga makrot) petar in ett cli() i början och ett sei() i slutet av din avbrott, så trots att du kör cli() så kommer globala avbrott ändå enablas i slutet av avbrottsrutinen!
Lösningen på problemet borde vara att istället bara stänga av avbrottet för timern genom TIMSK &= ~(1<<OCIE1A); eller att stoppa själva räknaren genom att återställa TCCR1B.
Användarvisningsbild
PaNiC
Inlägg: 2610
Blev medlem: 15 augusti 2003, 22:16:15
Ort: Skånelandet

Inlägg av PaNiC »

Jahaa! Man lär sig något nytt varje dag. Denna dagen flera saker.
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Kod: Markera allt

      secs++;
      lcd_putchar(secs+'0');
      if (++secs >= 60)
         {
         secs = 0;
         mins++; // VILL du verkligen räkna dubbelt?
         } // Öhhh...varför avslutas if()-satsen här?
      if (++mins >= 60)
         {
         hrs++; // Samma här, VILL du räkna dubbelt?
         secs = 0; // Varför? Den är ju redan nollad
         mins = 0;
         } // Denna if()-sats ska väl inte avslutas här heller
      if (++hrs >= 24);
         {
         hrs = 0;
         mins = 0; // Är ju redan nollad
         secs = 0; // Dito
         }
Nä, med de fel kommer det att bli HELT fel! Seså, klippa bort den översta delen i ditt program och klistra in den understa här, då kommer det att fungera.
if(++secs>= 60)
  {
  secs = 0;
  if(++mins >= 60)
    {
    mins = 0;
    if(++hrs >= 24) hrs = 0;
    }
  } 
// Skriv ut tio-sekunderna med "leading zero blanking"
if(secs/10) lcd_putchar(secs/10+'0'); // Om det är mer än 0 ska det skrivas ett tal
else  lcd_putchar(' '); // Annars ett mellanslag
// Skriv ut 1-sekunderna
lcd_putchar(secs%10+'0');
Användarvisningsbild
oJsan
EF Sponsor
Inlägg: 1541
Blev medlem: 11 november 2005, 21:36:51
Ort: Umeå
Kontakt:

Inlägg av oJsan »

Icecap: Som koden såg ut i början så skulle det ha funkat, men efter ditt tips att använda pre-incremental så glömde PaNiC bort att ta bort raderna med secs/mins/hrs++;
Om man skulle skippa preincremental och bara jämföra i stil med if(secs >= 60) så skulle de tre ifsatserna funka, men det är ju klart effektivare att "nästla" if-satserna på det vis som Icecap har gjort.
Användarvisningsbild
PaNiC
Inlägg: 2610
Blev medlem: 15 augusti 2003, 22:16:15
Ort: Skånelandet

Inlägg av PaNiC »

Till slut fattade jag vad ni menar, så nu är det ändrat :).

Trodde ju inte att if (++secs >= 60) verkligen ökade variabeln utan bara jämförde med secs+1.
Hur gör jag för att ensekunderna ska visas rätt? De fortsätter i ascii-tabellen medan tiosekunderna visas som de ska.
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Om du har skrivit: lcd_putchar((secs % 10)+'0'); SKA den göra rätt, obs. att jag har lagt in en extra parantes ifall din kompiler har lite CP o inte kan få prioriterna rätt.

"secs % 10" betyder "restvärdet från division med 10" och kommer således alltid att vara mellan 0 och 9, adderar man sedan på '0' blir det '0' - '9'.
Användarvisningsbild
PaNiC
Inlägg: 2610
Blev medlem: 15 augusti 2003, 22:16:15
Ort: Skånelandet

Inlägg av PaNiC »

Ahh, missade/glömde bort en del av det du skrev i förra inlägget.
Skriv svar