Flera saker "samtidigt" på en PIC? -Struktur?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
JimmyAndersson
Inlägg: 26578
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Flera saker "samtidigt" på en PIC? -Struktur?

Inlägg av JimmyAndersson »

Funderar på en sak, om man gör såhär:

(Psuedo-kod)

Kod: Markera allt

PROCEDURE DO_STUFF
  gör saker...
  vänta 1 sekund...
END SUB

FOR value = 1 TO 1000
  tal = tal + 1
  IF tal = 500 THEN gå till do_stuff-proceduren
NEXT value
När "tal" = 500 så går programmet till do_stuff-proceduren.
Men då fortsätter väl inte FOR-loopen förrän proceduren är klar?

Om man har ett program (i MikroBasic) där det "hela tiden" ska kollas om någon knapp har trycks ner, en LCD ska skriva tecken och t.ex en DAC ska få nytt värde..
Hur ska man tänka för att få det att flyta på bäst med minimala avbrott? (Menar rent generellt...)
Användarvisningsbild
EagleSpirit
Inlägg: 1288
Blev medlem: 27 maj 2003, 23:15:48
Ort: Västerås
Kontakt:

Inlägg av EagleSpirit »

Det finns en app-note på microchips hemsida hur man sköter det där. Det hela bygger på att man har en timer som skapar ett interrupt efter en viss tid, desto viktigare "task" desto oftare skapas interruptet. De lägst prioriterade taskerna körs mer sällan. Kommer inte ihåg under vilken kategori det ligger men nånting om RTC har jag för mig.
Användarvisningsbild
JimmyAndersson
Inlägg: 26578
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Inlägg av JimmyAndersson »

Snabbt svarat! :)

Hittade RTC-sidan, men tyvärr bara lite info om interrupt rent allmänt.
Har tyvärr inte kodat med interrupt, men jag förstår hur du menar.


Kan passa på att berätta lite mer om mitt projekt och varför jag undrar.
(PIC-kretsen är en 18LF2320, så jag inte glömmer skriva det.)

Jag har ett litet projekt som består av 6st tangentbordsknappar som styr olika funktioner:
"Run" Skickar 0-4095, 4094-1 om och om igen till en DAC-krets.
"Stop" Stoppar ovanstående och låter mig styra manuellt med:
"Min" skickar "0" till DAC-kretsen.
"Max" skickar "4095" till DAC-kretsen.
"-" minskar DAC'ens värde med 1.
"+" ökar värdet med 1.

Sedan har jag en display där jag ser:
*Värdet som skickas till DAC'en.
*Mode (run/stop/min/max/+/-.
*En förenklad skala som visar värdet grafiskt (mest för att det var kul att göra..)

Jag behöver läsa av ifall någon knapp tryckts ner. Samtidigt har jag en FOR-loop som räknar DAC-värdet och skickar det till DAC'en. Jag vill inte att den loopen ska avbrytas varje gång programmet kollar om någon knapp tryckts ner eller när displayen uppdateras. (För tillfället går det minst 40ms mellan varje gång displayen uppdateras pga LCD'ns E-pinne blir hög i 20ms och sedan låg lika länge, innan nästa gång den uppdateras. Ska minska dessa tider...)

Behöver jag interrutp för att lösa det ovanstående på ett bra sätt?
Användarvisningsbild
EagleSpirit
Inlägg: 1288
Blev medlem: 27 maj 2003, 23:15:48
Ort: Västerås
Kontakt:

Inlägg av EagleSpirit »

Visst kan du lösa det med interrupt, det är ju enklare och kanske bra att lära sig för framtiden?

Enligt din lilla kod-snutt därnere så kommer den inte köra for-loopen samtidigt. Det är omöjligt eftersom PIC inte kan göra två saker samtidigt. Men, hur kollar du knapparna? Använder du varianten som du skrivit i en annan tråd? Det du kan göra är ju att läsa av ADn varje gång i FOR-loopen, dvs, istället för "IF tal = 500" så läser du av ADn och skriver "IF AD > 0". Då får du ju dessutom "direktrespons" på knappen (förutsatt att FOR-loopen alltid körs). Hur lång är FOR-loopen?

Kom ihåg att det endast tar några µs att läsa av om en knapp har tryckts in, sen kan det ta lite extra tid om en knapp väl tryckts in men då ska ju troligtvis allt ändras ändå så det gör inget. Måste FOR-loopen vara så snabb att du inte kan undvara ett par µs?

En andra variant skulle ju vara att sätta ett timer-interrupt och en tredje är att ha interrupt direkt på ingången.

Btw, hur man använder interrupt i mikrobasic står lite taskigt förklarat i mikrobasic-manualen men om du kombinerar den och PIC-manualen så kan du lära dig det efter en del försök. Det står på sidan 31 i MB-manualen och ett exempel finns på sidan 205... I PIC-manualen finns det helt kapitel har jag för mig.

Lycka till
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Det finns många sätt att lösa detta på.

En avbrottsstyrd (interrupt) variant är antagligen enklast. Antingen med naturliga avbrott från den externa eller interna enhet som vill ha uppmärksamhet, eller via en timer med lämpligt intervall (pollning).

Men, viktigast är att du måste kvantifiera begrepp som "samtidigt", "hela tiden" o.s.v i milli- eller mikro-sekunder !! Lösningen kan variera kraftigt beroende på vad du menar med detta ! Bäst är att helt och hållet undvika den typen av luddiga uttryck...

I ditt fall får man se vilken enhet som behöver det kortaste "service intervallet" och anpassa sitt timeravbrott till det. Detta blir en "tidbas" i applikationen. Låt oss säga att det är DAC funktionen. Sedan kan man serva andra funktioner (LCD o.s.v) med intervall som är multiplar av denna tidbas genom att ha en tidbas-räknade i interrupt rutinen för varje funktion.

Men å andra sidan så kanske uppdateringen av LCDn kan ske "behovsstyrt" istället ?

Fråga mig inte hur man får ihop det i MikroBasic, men i assembler är det "a piece of cake"... :-)
Användarvisningsbild
JimmyAndersson
Inlägg: 26578
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Inlägg av JimmyAndersson »

EagleSpirit & Sodjan: Tackar! Nu har jag mer koll på det hela!
Återstår bara att fixa det i MikroBasic. Men som sagt, det känns som om assembler vore mer "rätt" för detta. :)

Kan passa på att svara på frågorna också:
Jepp, jag använder knapp-varianten jag skrev om i en annan tråd. Fungerar fint. "Några" µs kan jag undvara, inga problem alls.

"Samtidigt" och "hela tiden" är lite som frågan "hur långt är ett snöre?"...
Eftersom jag är en obotlig testa-mig-fram-person så har jag tyvärr inga specifika tidsangivelser ännu. Det enda jag kollar är så att jag inte får upprepade fördröjningar på 50ms eller mer efter varandra. 10st sådana fördröjningar på rad blir ju en halv sekund och det vore inget vidare i detta projekt. Nu är 50ms lite "extremfall", men jag ville ändå skriva lite om hur jag tänker.

MikroBasic är kanske inte det optimala för att få snabb effektiv kod, men efter att ha ägnat min "barndom" med att programmera spel och ritprogram i alla tänkbara basic-varianter, så har jag lärt mig att komma runt många begränsningar.


Men som sagt, tack för hjälpen. Ska återgå till programmeringen nu och se vad som händer. :)
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Din knapp-läsning använder väll ADC, eller hur ?
Då kan du spara tid (eller i alla fall få färre och kortare fördröjningar) genom att starta en AD omvandling, göra annat, och låta ADC interruptet ta hand om resultatet. Ingen onödigt väntande.

> så har jag tyvärr inga specifika tidsangivelser ännu.

Nja, men man måste veta *något*. Det är stor skillnad på 50 us och 50 ms.
Båda värderna kan vara helt rellevanta intervall för ett periodiskt interrupt beroende på vad man håller på med, men den faktiska realiseringen ser antagligen lite olika ut. Ett 50 us intervall ställer antagligen mycket större krav på kompakt och "smart" kodning, än om det är 50 ms.

"Samtidigt" och "hela tiden" är komplett värdelöst för den som inte vet det *du* vet om applikationen...
Kaggen
Inlägg: 432
Blev medlem: 29 januari 2005, 03:06:02

Inlägg av Kaggen »

Om man inte kör interrupt skulle jag gjort enligt följande:

Kod: Markera allt

MAIN_LOOP:
 check_button()
 count_DAC()
 update_display()
 GOTO MAIN_LOOP
Du gör om FOR/NEXT loopen för att räkna upp/ner DAC:en till en procedur som räknar upp eller ned *en* enhet för varje anrop.

Du har en flagga som används av count_DAC() och check_button() procedurerna. Om du trycker "stopp" så sätts flaggan till 0, om du trycker "start" sätts följaktligen flaggan till 1. Samma flagga kollar du i count_DAC(), om den är 0 hoppar du helt enkelt ur proceduren utan att räkna upp/ned DAC-värdet.

mvh Mats
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Under förutsättnng att alla funktioner kan köras med samma intervall samt att alla funktioner accepterar en fördröjning som är lika med summan av tiden för att köra alla övriga funktioner en gång. Ett ganska ovanligt scenario, och skillnaden i flexibilitet är ganska stor mot en interrupt styrd arkitektur.

Så snart men kommer lite över komplexiteten i ett "blink-a-LED" program, så kommer man att upptäcka att allt blir renare och snyggare med en interruptstyrd arkitektur på applikationen. Det blir så mycket enklare att lägga till ("plugga in") nya funktioner eller att ändra i de befintliga, eftersom de alla körs mer självständigt än när alla ligger i samma main-loop.
Användarvisningsbild
JimmyAndersson
Inlägg: 26578
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Inlägg av JimmyAndersson »

En av mina idéer var att lösa det ungefär som Kaggens exempel. Men jag skulle fortfarande få kolla om någon knapp trycks ner om och om igen. Men eftersom mitt program har snart 300 rader så skulle det blivit lite jobbigt.


sodjan:

Har kollat upp tiderna lite mer nu. Hoppas detta underlättar:

Man hinner med att trycka på en knapp ca 7 gånger på en sekund. Alltså 7Hz eller ca 140ms. Två av knapparna ("+" och "-") ska förändra värdet som skickas till DAC-kretsen. När "Run-knappen" är tryckt så ska DAC'en få nya värden med en hastighet som jag måste testa mig fram till. Det ligger iallfall mellan 10-40kHz. (Jag vet att det är stor skillnad på 10kHz och 40kHz, men det är så det är.) Den övre gränsen beror en hel del på min kod och vad MikroBasic gör med den innan den skickas till PIC-kretsen...

Däremot: När jag trycker "+" eller "-" så ska DAC'en få nya värden så snabbt som jag kan trycka, dvs 140ms som snabbast.

Sedan blir det ju tidskillnader från det att jag trycker ner en knapp, tills DAC'en får nytt värde och LCD'n får nya tecken. Eftersom "+" och "-" läses av på samma sätt som övriga knappar så behöver tidskillnaden inte vara mindre än 140ms. Maxgränsen ligger vid ca 250ms. 500ms vore inte kul.

Jag tror jag fick med allt där. :humm:


ADC-interruptet skulle underlätta hel del!
LCD-ritningen och DAC-uppdateringen ska rulla på som om inget hade hänt om inget har hänt. :)
Men om någon knapp trycks så ska DAC'en "stanna" och programmet gör det som knapptryckningen motsvarade, så där passar ett interrupt perfekt.
Användarvisningsbild
Jeppsson
EF Sponsor
Inlägg: 810
Blev medlem: 3 oktober 2005, 18:00:43
Ort: Karlskrona

Inlägg av Jeppsson »

Jag hade för mig att jag hade läst om något programmeringgänssnitt för µprocessor som hade multitasking och ROTS (Real-Time Operating System)...

Här kommer länken Salvo

Det skall bland annat klara av och köra andra programsnuttar undertiden man väntar på något...

Från Salvo's hemsida
"Salvo is highly configurable and scalable, with a full set of run-time features including priority-based, cooperative multitasking, event services, real-time delays and elapsed-time services.

Hoppas att det är något som kan hjälpa... :)

Edit: Glömde att skriva: Självklart fungerar det för de flesta µprocessorerna bland annat PIC... :)
Kaggen
Inlägg: 432
Blev medlem: 29 januari 2005, 03:06:02

Inlägg av Kaggen »

sodjan:
Jo visst, man lär försöka bryta ner tiden i varje function så långt det går. Även försöka bryta ner FOR/NEXT loopar till enskillda anrop som jag beskrev ovan. Det är ingen "snygg" lösning, men det funkar för enklare projekt, iomed att Jimmy sa att den största tidsåtgången låg på 40 ms vid skrivandet till displayen så borde det funka.

Jimmy:
> Men jag skulle fortfarande få kolla om någon knapp trycks ner om och
> om igen. Men eftersom mitt program har snart 300 rader så skulle det
> blivit lite jobbigt.

Jag vet inte vad du menar med ovanstående. I mitt exempel är det *en* funktion du använder för att kolla tangentnertryckning. Anropet till funktionen är *en* rad. Varför skulle det bli så jobbigt eller generera mycket kod? Jag tror inte du "sliter" på AD:n för att du pollar den många gånger.

Alternativet är att du använder 2 interrupt. Ett för AD:n som kör en funktion för tangentbordsavkänning, och ett timerinterrupt som kör en funktion som med viss intervall ökar/minskar värdet till DAC:en (var 25:e us för 40kHz eller var 100:e us för 10kHz).

Kan bli nätt dock kanske att hinna sätta, generera och hantera ett nytt interrupt var 25us iaf om du kör med 4MHz klocka. Beror lite på hur många klockcycler din DAC funktion tar och hur bra MicroBasic optimerar kod.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

För ett nytt värde på DAC'en skall två bytes överföras på SPI bussen.
Med 400Khz busshastighet, blir det svårt att köra 40,000 uppdat/sek.
Vilken hastighet körs SPI med ?

Exakt hur DACen uppdateras beror också på vilket "jitter" som kan accepteras i uppdateringen av DACen...
Skriv svar