Sida 3 av 4

Postat: 26 januari 2008, 21:00:25
av chrille112
Ok, vad är snabbt i detta fallet? Snabbare än perioden mellan varje interrupt (100ms)?

Postat: 26 januari 2008, 21:53:53
av sodjan
Yes !
Annars "kuggar det över"...

Men din fråga går igentligen inte att svara på, eftersom bara du vet vad
den där funktionen gör. Att du tycker att "klockan" börjar gå fel, är ju
en anledning att börja bli lite misstänksam...

Postat: 26 januari 2008, 22:05:06
av chrille112
Ok. Jag ska flytta undan lite kod som inte behöver ligga i interrupten. Där behöver man ju egentligen bara registrera att något har hänt, och sedan hantera vad man ska göra i main-loopen.

Å andra sidan så borde man ju kunna köra en väldig massa instruktioner på 100 ms (interupttiden)...

Postat: 26 januari 2008, 22:10:08
av Icecap
Precis som sodjan skriver...

INGET som körs från en ISR får ta längre tid än ca: 50% av tiden mellan interrupten.

Vissa är så "dumma" att de gör debounce i mjukvara när de läser knappar, det gör jag ALDRIG! Men jag läser knapparna från en ISR och sparar knapptryckningar i en buffer. När man läser knapparna läser man 1 gång "per gång" och använder det värde som definition.

Exempel:

Kod: Markera allt

  // Done within timer-ISR
  static byte Buttons_Previous;
  byte        X, Work;
  // Read buttons (N-key rollover) and treat them accordingly
  Work = Button_Port & Button_Port_Mask; // Read buttons port
  // Now Work contains a '1' for each button that's active
  X = Work & ~Buttons_Previous; Mask out "old" buttons
  // Now X contains the NEW buttons
  Buttons_Previous = Work;
  switch(X)
    {
    case Button_1:
    case Button_1:
    case Button_3:
    case Button_4:
    case Button_5:
      PutKey(X); // Send into the key-buffer only if ONE new key is pressed
    break;
    }
Edit: Städade lite.

Postat: 26 januari 2008, 22:10:20
av sodjan
Visst, men då måste du också ha järnkoll på "cyklerna". I assembler är det
rellativt lätt. I C/Basic/Pascal är det mer eller mindre svårt beroende på hur
bra LST filer eller "profiling" verktyg de har...

Postat: 26 januari 2008, 22:40:53
av chrille112
icecap: Jisses vilken snygg och effektiv kod! Jag har suttit och försökt tolka den, är detta rätt?

Kod: Markera allt

  // Done within timer-ISR
  static byte Buttons_Previous;
  byte        X;

  // Read buttons and treat them accordingly
  Work = 
(~Button_PortA 
invertera Button_PortA

Kod: Markera allt

&Button_PortA_Mask) 
AND:a det med mask. På så sätt får man fram vilka bitar som har förändrats

Kod: Markera allt

| (~Button_PortB & Button_PortB_Mask);
Gör samma sak med PortB

Kod: Markera allt

  // Now Work contains a '1' for each button that's active
  X = Work & ~Buttons_Previous;
And:a knapparna som är nedtryckta nu (enligt ovan) med hur de var inverterat av hur de var nedtryckta förut. På så sätt får man fram bara de som inte var nedtryckta innan.

Kod: Markera allt

  // Now X contains the NEW buttons
  Buttons_Previous = Work;
  switch(X)
    {
    case Button_Sharp:
    case Button_Dress:
    case Button_Plus:
    case Button_Minus:
    case Button_Profile:
Hanterar du en knapp i taget? Är t.ex. Button_sharp 0b0001000 ? Vad händer om man trycker flera knappar samtidigt, då kommer väl ingen av ovanstående matcha?

Kod: Markera allt

      PutKey(X); // Send into the key-buffer only if ONE new key is pressed
Vad händer här? Tar denna funktionen hand om vad som ska göras vid respektive knapptryck?

Kod: Markera allt

    break;
    }

Postat: 26 januari 2008, 23:05:54
av Icecap
Har ändrat lite i koden, jag glömde att jag har inverterat knapper in ('1' = ej tryck, '0'= tryck). Koden i mitt exempel är nu ändrat och jag kommenterar enl. den nya version.

Att jag har inverterade knappar beror på designval, om jag skickar ut VCC till knapparna kan en oavsiktlig kortis stänga av processorn men om jag skickar ut nollan och har pull-up på knapparna kan en kortis inte skada på det vis.

Jag har bara knappar på vissa av pinnarna på porten, därav min maskning.

Då jag pga. en del annat använder en 16-bytes cirkulär tangentbuffer sparar PutKey() den nya knapptryckning i den buffer. Om du inte behöver detta (sällan man gör) vill jag föreslå att du gör som följer:

Kod: Markera allt

// Global variable:
volatile byte Key; // Keypress

Timer_ISR:
  {
  // Done within timer-ISR
  static byte Buttons_Previous;
  byte        X, Work;
  if(!Key)
    {
    // Read buttons (N-key rollover) and treat them accordingly
    Work = Button_Port & Button_Port_Mask; // Read buttons port active bits
    // Now Work contains a '1' for each button that's active
    X = Work & ~Buttons_Previous; Mask out "old" buttons
    // Now X contains the NEW buttons
    Buttons_Previous = Work;
#if false
    // This switch(...) is optional.
    switch(X)
      {
      case Button_1:
      case Button_1:
      case Button_3:
      case Button_4:
      case Button_5:
        Key = X; // Send into the key-store only if ONE new key is pressed
      break;
      }
#else
      Key = X; // Send into the key-store
#endif
    }
  }
När du har läst senaste "Key" nollar du den, detta gör att den kan läsa in nästa knapptryckning, på detta vis kan du i din mainrutin avkänna OM det är en knapptryckning:

Kod: Markera allt

  if(Key)
    { // Some key to execute
    // Do the Key-thingy bla-ti-bla
    // Done with all Key-work, then do next line!
    Key = 0; // Mark Key as used and enable search for next
    }
Med denna rutin (heter "n-key rollover") och maskning kan man trycka på en knapp (den blir detekterat 1 gg) och hålla den inne (om man vill), trycka på nästa (som blir detekterat 1 gg) osv. inget debounce-delay eller läsa igen eller skit, bara snabbt och effektivt igenom.

Det finns också en "2-key lockout" som kräver att man ska släppa en knapp innan nästa kan accepteras, den har jag testat och den fungerar inte till mig, inte användarvänlig nog.

Edit: n-key rollover patent

Postat: 27 januari 2008, 00:41:56
av chrille112
Precis som väntat fungerar din kod alldeles utmärkt. Tack för att du tog dig tid. Jag gjorde en väldigt förenklad variant där jag inte har stöd för flera knappar samtidigt:

Kod: Markera allt

  Work = PORTA & ButtonMask; 
  X = Work & ~Buttons_Previous;
  Buttons_Previous = Work;
  switch(X)
  {
           case 0b00000001: // menyknappen
                timeMINUTE++;
                break;
           case 0b00000010: // plusknappen
                timeHOUR++;
                break;

  }
Just nu ökar jag sekundrarna och minuterna för att se att knapparna svarar. Tanken är att jag ska sätta variabler här, och sedan ta hand om resten i main.

Jag har två knappar, meny och plus. Eventuellt kommer det en minusknapp sen också. Dessa knappar har ju lite olika funktion beroende på vilket läge man är i. Finns det något snyggt sätt att lösa detta på också, eller är det if-satser som gäller?

Min tanke är:
Klockan visas när kretsen startas. Trycker man plus händer inget, trycker man meny så kommer man till ställ klocka. Med plus ställer man timme, med meny hoppar man till minut (och ställer den med plus). Trycker man meny en gång till kommer man tillbaka till klockan, och den ställs enligt det man valt.
Detta har jag gjort med if-satser. En väldigt ful lösning som jag ogärna visar... :shock:

Postat: 27 januari 2008, 08:37:00
av Icecap
Jag har jobbat en del med menyer och för mig fungerar det bäst med switch. Om vi anser att knapptryckninger sparas enl. det sätt jag beskrev senast:

Kod: Markera allt

enum {Menu_Start, Menu_Set_Hour, Menu_Set_Minute, Menu_Something_Else,[...] Menu_Highest};

if(Key)
  {
  if(Key == Key_Menu)
    {
    if(++Menu >= Menu_Highest) Menu = 0; // Increment in a circular manner
    switch(Menu)
      {
      case Menu_Start:
      break;
      case Menu_Set_Hour:
        switch(Key)
          {
          case Key_Plus:
            if(++Hour >= 24) Hour = 0;
          break;
          case Key_Minus:
            if(Hour) Hour--;
            else Hour = 23;
          break;
          }
      break;
      case Menu_Set_Minute:
        switch(Key)
          {
          case Key_Plus:
            if(++Minute >= 60) Minute = 0;
          break;
          case Key_Minus:
            if(Minute) Minute--;
            else Minute = 59;
          break;
          }
      break;
      case Menu_Something_Else:
      break;
      [more cases if applicable]
      }
    }
  Key = 0; // Accept next keypress
  }
Detta kan sedan byggas ut i "evighet", fördelen är att man i varje meny-del har en definition på vad man ska göra med varje knapptryckning och när en meny blir av en viss storlek kan det bli mycket besvärligt att få allt till på rätt sätt om man inte håller en "hård" struktur.

På detta vis har jag gjort en meny som håller runt 100 punkter delad i 2 menyer: användaremeny & servicemeny, produkten är ställbar till 5 olika språk och menyerna är tydliga och enkla att leta reda på i källkoden och jag kan kasta om på deras inbördes följd via en enkel ändring i ENUM-satsen.

Postat: 27 januari 2008, 10:34:34
av chrille112
Ser bra ut! Det är samma stuktur som med mina if-satser, men switch kanske är mer lämpligt.

Fungerar denna koden? I början utesluer du ju alla knappar förutom Key_Menu (if(Key == Key_Menu)), men senare i koden kollar du om Key kan vara andra värden (case Key_Plus)?

Om man avslutar den if-satsen efter if(++Menu >= Menu_Highest) Menu = 0; så tror jag att det ska funka dock. Glömde du det eller är jag ute och cyklar? :)

Vad är det för typ av produkter du utvecklar? Skriver du i C till dem produkterna också?

Postat: 27 januari 2008, 12:32:56
av Icecap
"if(++Menu >= Menu_Highest) Menu = 0;" är alldeles rätt och ska inte avslutas mer än den redan är!

Den betyder:
Steg 1: Menu++;
Steg 2: if(Menu >= Menu_Highest) Menu = 0;

Tänk dig 2 variabler, X & Y. X är 5.

Y = X++; medför att Y blir 5 och X blir 6.
Y = ++X; medför att X inkrementeras FÖRST och sedan kopieras till Y = X = 6.

Så "X++" betyder "använd värdet och inkrementera sedan" medan
"++X" betyder "inkrementera först, använd värdet sedan".

Produkterna....

* Janfire NH pelletsbrännare
* ProSkate skridskorslipare
* Safe Traffic äldre skyltar
* Andra jag inte vill komma in på...

Postat: 27 januari 2008, 16:44:18
av chrille112
Jo, det är jag med på. Det jag menade var att du börjar med:

if(Key == Key_Menu)

Om det ej är sant så blir Key=0 och inget mer händer.

Innanför if-satsen (Key == Key_Menu) kollar du sedan om Key har andra värden ( switch(Key) ).
Förstår du vad jag menar?

Spännande produkter! Är det helt egna, eller jobbar du som konsult typ?

Postat: 27 januari 2008, 17:23:54
av Icecap
Ah... :oops:

Du såg det! ÖÖhhhh.... bra, jag skulle bara testa dig lite... :vissla:

Jag jobbade som konsult till en början men sedan hade de så mycket att göra att jag numera är tillvidareanställd.

Postat: 27 januari 2008, 17:31:27
av chrille112
Hehe :lol:

Tackar så mycket för all hjälp, nu ska jag sätta mig och koda menyer. Sedan ska jag ansluta drivkretsar till glödlamporna så har jag snart min wakeup-light :)

Postat: 27 januari 2008, 22:15:28
av chrille112
Har suttit och planerat om menystrukturen, och tänkte använda mig av undermenyer istället. Det blir 4 huvudmenyer, som man kan "gå in i" genom att klicka på meny-knappen.

Kod: Markera allt

enum MainMenu { MainMenuClock, MainMenuSettime, MainMenuSetalarm, MainMenuTurnoffAlarm, MainMenuHighest };
Började koda, men stötte på problem med undermenyerna. Jag har gjort en enum för varje huvudmeny:

Kod: Markera allt

enum SubmenuSettime { SubmenuSettimeSethour, SubmenuSettimeSetminute, SubmenuSettimeHighest };
enum SubmenuSetalarm { SubmenuSetalarmSethour, SubmenuSetalarmSetminute, SubmenuSetalarmHighest };
Men nu märkte jag att det inte var ett så smart val, eftersom jag då måste "switcha" varje undermeny för att kolla om man är i det läget när en knapp är aktiv.
Hur brukar du lösa detta?

Man skulle ju kunna dela upp det i en huvudmeny-enum och en undermeny-enum. Man skulle då kunna köra Menu++ som tidigare för huvudmenyn, men man måste sköta undermeny-valen helt manuellt och stega fram nivåer