Hämta data från USART med funktion
Det löser väll fortfarande inte mitt problem hur jag ska kunna från main anropa en funktion som lyssnar på RF-modulen under en viss tid om någon data kommer för att sedan returnera till main det datat funktionen har mottagit.
Att ligga och kolla om det finns något data på porten för att sedan plocka datat och lägga i en array är inga problem. Problemet är att returnera datat till anroparen av funktionen i och med att det inte är en vanlig sträng med 0x00 som avslut.,
Att ligga och kolla om det finns något data på porten för att sedan plocka datat och lägga i en array är inga problem. Problemet är att returnera datat till anroparen av funktionen i och med att det inte är en vanlig sträng med 0x00 som avslut.,
en char[256] kan innehålla alla siffror mellan 0-255
däremot så kan du inte hantera denna som en textsträng.
du kan deklarera en egen datatyp och använda dessa i dina funktioner
Lite Pseudo-c kod
däremot så kan du inte hantera denna som en textsträng.
du kan deklarera en egen datatyp och använda dessa i dina funktioner
Lite Pseudo-c kod
Kod: Markera allt
typedef struct mydata{
char *dataptr; //pekare till datan
int datalen; //datalength
int maxlen;
}
volatile mydata d;
int main(void){
char data[256];
d.dataptr = data;
d.datalen = 0;
d.maxlen = sizeof(data);
getmydata(&d);
putmydata(&d);
}
isr(vad nu du vill hämta datan){
if(d->datelen < maxlen)
d->dataptr[d->datelen++] = getc();
}
void putmydata(mydata *d){
int n, *p;
n = d->datalen;
p = d->dataptr;
while(n--){
putchar(*p++);
}
}
Du har övergripande problem med applikationsarkitekturen !
> hur jag ska kunna från main anropa en funktion som lyssnar
> på RF-modulen
Frågan är om du ska göra det alls. Att anropa en rutin som sedan ligger
och "väntar" är sällan speciellt bra. Generellt ska man undvika att
processorn väntar på någon händelse alls. Låt de olika händelserna
avbryta (interrupt) processorn istället.
Det låter som något liknande detta skulle fungera (och det motsvarar
nog i princip det som Icecap har beskrivit...) :
(1) Main() anropar en funktion som (som du skrev) "skickar ut ett kommando
att få datat från en klient". Denna funktion returnerar direkt efter att
data är skickat, men först sätter den en variabel/flagga som indikerar
*vilken* klient som har "anropats".
Status ("state") för applikationen är nu att "kommando är skickat,
väntar på svar".
*All* mottaganing från RF modulen sker via en interruptrutin. Denna
rutin tar alltså bara mot *ett* tecken i tagen. Status för mottagningen
håller man reda på via flaggor/variabler mellan de olika interrupten.
Denna rutin sätter lämpliga flaggor/variabler som indikerar att/när ett
helt "paket" är mottaget.
Main() (som hela tiden ligger i en loop i förgrunden) kollar kontinuerligt
de olika flaggorna/variablerna, och när RF-mottagningen har signalerat
att det finns ett komplett paket, så anropar main() en funktion som
hanterar/analyserar/agerar på det mottagna datat. När det är klart,
anropar main() funmtionen i punkt (1) ovan igen men för nästa kient.
En annan funktion som main() håller redan på är timeout för
status "väntar på klient". Och så får man hantera det när det inträffar.
Å så snurrar det hela på...
> Problemet är att returnera datat till anroparen av funktionen...
Gör inte det då !
Skriv datat till en global array samt till en "length" variablel.
Finns ingen anledning att returnera något som returvärde från
funktionen. Om man ska returnera något alls, så skulle det vara en
"fail/success" kod, eller liknande, inte en hel "sträng".
> inte är en vanlig sträng med 0x00 som avslut
Det där är bara C-tjafs, skit i det och hantera ditt data på annat sätt
som är mer lämligt för en microcontroller...
> hur jag ska kunna från main anropa en funktion som lyssnar
> på RF-modulen
Frågan är om du ska göra det alls. Att anropa en rutin som sedan ligger
och "väntar" är sällan speciellt bra. Generellt ska man undvika att
processorn väntar på någon händelse alls. Låt de olika händelserna
avbryta (interrupt) processorn istället.
Det låter som något liknande detta skulle fungera (och det motsvarar
nog i princip det som Icecap har beskrivit...) :
(1) Main() anropar en funktion som (som du skrev) "skickar ut ett kommando
att få datat från en klient". Denna funktion returnerar direkt efter att
data är skickat, men först sätter den en variabel/flagga som indikerar
*vilken* klient som har "anropats".
Status ("state") för applikationen är nu att "kommando är skickat,
väntar på svar".
*All* mottaganing från RF modulen sker via en interruptrutin. Denna
rutin tar alltså bara mot *ett* tecken i tagen. Status för mottagningen
håller man reda på via flaggor/variabler mellan de olika interrupten.
Denna rutin sätter lämpliga flaggor/variabler som indikerar att/när ett
helt "paket" är mottaget.
Main() (som hela tiden ligger i en loop i förgrunden) kollar kontinuerligt
de olika flaggorna/variablerna, och när RF-mottagningen har signalerat
att det finns ett komplett paket, så anropar main() en funktion som
hanterar/analyserar/agerar på det mottagna datat. När det är klart,
anropar main() funmtionen i punkt (1) ovan igen men för nästa kient.
En annan funktion som main() håller redan på är timeout för
status "väntar på klient". Och så får man hantera det när det inträffar.
Å så snurrar det hela på...
> Problemet är att returnera datat till anroparen av funktionen...
Gör inte det då !
Skriv datat till en global array samt till en "length" variablel.
Finns ingen anledning att returnera något som returvärde från
funktionen. Om man ska returnera något alls, så skulle det vara en
"fail/success" kod, eller liknande, inte en hel "sträng".
> inte är en vanlig sträng med 0x00 som avslut
Det där är bara C-tjafs, skit i det och hantera ditt data på annat sätt
som är mer lämligt för en microcontroller...
Micke_s: "char[256]" är en array av char med plats för 256 element. Om man lägger in en 0x00 på lämplig plats är det en sträng i C då en sträng i C definierat som ett array av char som termineras med 0x00.
ankan: sodjan har helt rätt! Du ska göra om din signalbehandling helt för att få det att fungera. Om du vill ha allt i en separat fil är det OK, du kan fint göra blockar som ISR kallar.
För att detektera blockar ville jag antingen avsätta en timer eller skicka HEX data. Med timern är det relativt enkelt: för varje char som tas emot omstartas timern och timerinterrupt enablas. När timern ger interrupt har UART'en inte tagit emot data på kanske 3 bytes tid, detta använder man till att markera "block mottagit" och stänga av timern sedan. Detta ger att det kommer 1 timer interrupt per mottagna block.
Alternativet med att skicka HEX data kan då lösas enkelt också:
STX (0x02) HEX-DATA bla bla ETX (0x03)
Sedan filtrerar man vid mottagningen:
Är det en STX? Om ja, nollställ input-index.
Är det en ETX? Om ja, markera att block har kommit.
Att sedan omvandla HEX till bytes igen är ju snabbt, det kan göras i samband med att man flyttar datan på plats.
Detta tar förvisso inte bort behovet av en timer men det minskar hur tight den behöver vara. Varje inkommande byte nollar en räknare som man räknar upp med timer-interruptentill ett visst max. värde, kommer det inte en ETX innan detta max-värde uppnås är blocken inte komplett och ska kastas.
Så du får nog tänka om ordentligt från det gamla BASIC-tänkande med programflöde och börja låta interrupten göra saker för dig.
ankan: sodjan har helt rätt! Du ska göra om din signalbehandling helt för att få det att fungera. Om du vill ha allt i en separat fil är det OK, du kan fint göra blockar som ISR kallar.
För att detektera blockar ville jag antingen avsätta en timer eller skicka HEX data. Med timern är det relativt enkelt: för varje char som tas emot omstartas timern och timerinterrupt enablas. När timern ger interrupt har UART'en inte tagit emot data på kanske 3 bytes tid, detta använder man till att markera "block mottagit" och stänga av timern sedan. Detta ger att det kommer 1 timer interrupt per mottagna block.
Alternativet med att skicka HEX data kan då lösas enkelt också:
STX (0x02) HEX-DATA bla bla ETX (0x03)
Sedan filtrerar man vid mottagningen:
Är det en STX? Om ja, nollställ input-index.
Är det en ETX? Om ja, markera att block har kommit.
Att sedan omvandla HEX till bytes igen är ju snabbt, det kan göras i samband med att man flyttar datan på plats.
Detta tar förvisso inte bort behovet av en timer men det minskar hur tight den behöver vara. Varje inkommande byte nollar en räknare som man räknar upp med timer-interruptentill ett visst max. värde, kommer det inte en ETX innan detta max-värde uppnås är blocken inte komplett och ska kastas.
Så du får nog tänka om ordentligt från det gamla BASIC-tänkande med programflöde och börja låta interrupten göra saker för dig.
Jag har nog svårt för det här med strukturell C programmering på mikroprocessorer. När jag lärde mig C så gällde det att slänga ut allt från main och bara anropa olika funktioner som sedan skickar tillbaka sina resultat.
Sedan har jag lärt mig att det är bra om man kan återanvända olika funktioner till andra projekt också. Därför tänkte jag ha en funktion tex som lyssnar efter inkommande data en viss tid för att sedan returnera strängen till anroparen.
Jag har förstått att man ska hålla sig från delay_ms() när man programmerar PICar om man kan men hur ska man tänka mer för att strukturera upp ett program på PICen?
I skolan lärde vi oss C på vanliga PC-datorer och hur PICen fungerar i ASM. Sedan presenterade de bara att de har kompilatorer för PICar i C. Men de berättar inget att man ska tänka annorlunda vid upplägget av programmet då. Kanske finns någon bra sida som beskriver just detta.
Annars får ni gärna dra huvudprinciperna för mig hur man ska lägga upp ett program i en PIC.
Sedan har jag lärt mig att det är bra om man kan återanvända olika funktioner till andra projekt också. Därför tänkte jag ha en funktion tex som lyssnar efter inkommande data en viss tid för att sedan returnera strängen till anroparen.
Jag har förstått att man ska hålla sig från delay_ms() när man programmerar PICar om man kan men hur ska man tänka mer för att strukturera upp ett program på PICen?
I skolan lärde vi oss C på vanliga PC-datorer och hur PICen fungerar i ASM. Sedan presenterade de bara att de har kompilatorer för PICar i C. Men de berättar inget att man ska tänka annorlunda vid upplägget av programmet då. Kanske finns någon bra sida som beskriver just detta.
Annars får ni gärna dra huvudprinciperna för mig hur man ska lägga upp ett program i en PIC.
> hur man ska lägga upp ett program i en PIC.
Det kan vara svårt att generallisera, men så som Icecap och jag har
beskrivet det tycker jag borde fungera för den aktuella applikationen.
> skolan lärde vi oss C på vanliga PC-datorer
Hm, men där är det inte oika vanligt att man jobbar interruptstyrt.
Och OS'et (Windows) hjälper ju till en del också. Här får man göra
en del av det som Widnows gjorde på PCn.
Det kan vara svårt att generallisera, men så som Icecap och jag har
beskrivet det tycker jag borde fungera för den aktuella applikationen.
> skolan lärde vi oss C på vanliga PC-datorer
Hm, men där är det inte oika vanligt att man jobbar interruptstyrt.
Och OS'et (Windows) hjälper ju till en del också. Här får man göra
en del av det som Widnows gjorde på PCn.
Vi körde förståss C för DOS och hade inte med Windows att göra. Detta för att bara lära oss grunderna. Sedan körde vi object orienterat med Java i stället av någon dum anledning.
Varför jag har svårt med interruptet när det gäller meddelanden från RF-modulen är för att kompilatorn jag kör (MikroC) har färdiga funktioner för att ta emot tecken. Man kan kolla om det har kommit in en byte och sedan får man läsa av den byten.
Antar att jag kanske måste skriva funktionen själv i stället så jag kan utnyttja interruptet för att en byte har mottagits på USART. På så vis kan jag göra som du säger att sätta PICen i lyssna läge och då ligga i main och loopa till ett avbrott av USART dyker upp och då hoppa till läget att spara ner datat i en array för att sedan jobba vidare.
Jag kanske ska glömma det där med att göra funktioner som kan användas till flera saker i andra projekt och i stället göra allt anpassat för projektet även om jag kanske delar upp funktionerna i olika filer för att få lite bättre ordning.
Jag tror jag skulle ha kommit mycket längre om jag bara skulle ha kört på men jag vill att koden ska bli strukturerad och lättförstårlig för ev vidareutveckling i framtiden. Det är nog där skon klämmer just nu och jag behöver mest hjälp med.
Hur har ni andra lärt ur hur koden ska struktureras upp? Har ni bara lärt er med tiden vad som är smartast eller kan man läsa sig till det någon stans?
Varför jag har svårt med interruptet när det gäller meddelanden från RF-modulen är för att kompilatorn jag kör (MikroC) har färdiga funktioner för att ta emot tecken. Man kan kolla om det har kommit in en byte och sedan får man läsa av den byten.
Antar att jag kanske måste skriva funktionen själv i stället så jag kan utnyttja interruptet för att en byte har mottagits på USART. På så vis kan jag göra som du säger att sätta PICen i lyssna läge och då ligga i main och loopa till ett avbrott av USART dyker upp och då hoppa till läget att spara ner datat i en array för att sedan jobba vidare.
Jag kanske ska glömma det där med att göra funktioner som kan användas till flera saker i andra projekt och i stället göra allt anpassat för projektet även om jag kanske delar upp funktionerna i olika filer för att få lite bättre ordning.
Jag tror jag skulle ha kommit mycket längre om jag bara skulle ha kört på men jag vill att koden ska bli strukturerad och lättförstårlig för ev vidareutveckling i framtiden. Det är nog där skon klämmer just nu och jag behöver mest hjälp med.
Hur har ni andra lärt ur hur koden ska struktureras upp? Har ni bara lärt er med tiden vad som är smartast eller kan man läsa sig till det någon stans?
Att strukturera koden från början är bra! Det viktigaste man kan göra är att skapa en översikt från början och sedan fylla i de funktioner som behövs.
Mitt första kommersiella program var en styrning som skulle övervaka 3 elektriska ugnar.
Jag satta mig ner och skrev rutin efter rutin men BARA deklarationen för varje och sedan lite kommentarer om vad rutinen skulle göra, flaggor osv.
SEDAN började jag fylla i de olika rutiner, t-bordrutin, interruptrutin osv osv. Då alla rutiner var ifyllda hade jag redan gjort main-loop och då fungerade hela programmet i första skott. Det körde i övrigt oändrad i 12 år till fabriken stängdes ner av lönsamhetsskäl.
Så se interruptdelen som en rutin i sig som gör klar datan till resten.
Vad du alltså måste göra först är att klura ut HUR du ska detektera att det har kommit en block, hur du ska signalera ATT den har kommit osv.
Mitt första kommersiella program var en styrning som skulle övervaka 3 elektriska ugnar.
Jag satta mig ner och skrev rutin efter rutin men BARA deklarationen för varje och sedan lite kommentarer om vad rutinen skulle göra, flaggor osv.
SEDAN började jag fylla i de olika rutiner, t-bordrutin, interruptrutin osv osv. Då alla rutiner var ifyllda hade jag redan gjort main-loop och då fungerade hela programmet i första skott. Det körde i övrigt oändrad i 12 år till fabriken stängdes ner av lönsamhetsskäl.
Så se interruptdelen som en rutin i sig som gör klar datan till resten.
Vad du alltså måste göra först är att klura ut HUR du ska detektera att det har kommit en block, hur du ska signalera ATT den har kommit osv.
Tack det där var rätt tydligt beskrivet på ett som jag tycker vara ett bra tillvägagångssätt. Någon annan som vill flika in med något annat?
Några småfrågor bara. Vad är t-bordrutin?
Interruptrutinen ska väll inte göra mer än att kolla vad det var för interrupt och sedan sätta någon flagga för att så snabbt som möjligt komma ur interruptet för att släppa fram andra interrupt
Sedan får man tolka flaggan i main.
Började med att sätta upp ett tillståndsdiagram men det var med bara de tillstånd jag tyckte att noden skulle kunna hamna i. Dvs
START - Startar här
LEARN - Byta identitet med master så noden vet vem den tillhör
NORMAL - Lyssna på dataförfrågan och skicka data
SLEEP - Sov när det inte är ens tidslucka
LOST - Om den inte blir tillfrågad när tidsluckan uppstår för att vänta på förfrågan efter tappade.
Men jag kanske borde ha flera tillstånd i så fall. En för att vänta på indata, en för att skicka data osv.
Som jag har gjort nu sätter jag vilket tillstånd noden är i, i interruptet för att sedan köra det som ska köras i det tillståndet i main där det inte finns mycket mer än en switch case sats som kollar tillstånd och sedan kör lite olika funktioner beroende på tillståndet.
Några småfrågor bara. Vad är t-bordrutin?
Interruptrutinen ska väll inte göra mer än att kolla vad det var för interrupt och sedan sätta någon flagga för att så snabbt som möjligt komma ur interruptet för att släppa fram andra interrupt
Sedan får man tolka flaggan i main.
Började med att sätta upp ett tillståndsdiagram men det var med bara de tillstånd jag tyckte att noden skulle kunna hamna i. Dvs
START - Startar här
LEARN - Byta identitet med master så noden vet vem den tillhör
NORMAL - Lyssna på dataförfrågan och skicka data
SLEEP - Sov när det inte är ens tidslucka
LOST - Om den inte blir tillfrågad när tidsluckan uppstår för att vänta på förfrågan efter tappade.
Men jag kanske borde ha flera tillstånd i så fall. En för att vänta på indata, en för att skicka data osv.
Som jag har gjort nu sätter jag vilket tillstånd noden är i, i interruptet för att sedan köra det som ska köras i det tillståndet i main där det inte finns mycket mer än en switch case sats som kollar tillstånd och sedan kör lite olika funktioner beroende på tillståndet.
t-bord = tangentbord.... det var det projekt jag gjorde då.
UART-Interrupten ska tömma UART'en och lägga den inkommande byte i det array den ska i, uppdatera pekare/index osv. Om du använder en timer ska den också starta (om) timern och allt som ska göras för att den ska fånga pausen snabbt.
Att definiera tillstånder är en bit på vägen men det säger inget om vilka rutiner som behövs. Du behöver en rutin som tar hand om den inkommande datablock och sparar den på rätt sätt, du behöver en rutin för att skicka data i det valda formatet osv.
Det "gamla" tänkande med "nu ska den göra detta och här står vi till det är klart" är förlegat, det kan fungera till viss mån när man inte har realtidsdata men det du ska göra är att skapa ett sorts OS som tar hand om realtidsdata, petar i buffrar, signalerar i flaggor osv varefter din main-loop kan köra det gamla viset, beta av allt eftersom.
Du måste ställa upp en definition av vilka funktioner som ska utföras i de olika tillstånder, en del av dom lär vara mycket lika och enda skillnad kan vara en flagga men det blir en del tänkande innan du kan programmera klart.
Det viktigaste är att vara säker på hur ditt dataformat ser ut, sedan ska du se till att kunna hämta in datan i en buffer och signalera hel block.
Därnäst kan du göra en rutin som behandlar de inkommande data enl. de behov som finns.
Du måste alltså FÖRST av allt skriva ner:
START - vad ska ske här, synkronisera tid? Meddela närvaru? osv.
LEARN - vad ska den göra här? osv osv.
UART-Interrupten ska tömma UART'en och lägga den inkommande byte i det array den ska i, uppdatera pekare/index osv. Om du använder en timer ska den också starta (om) timern och allt som ska göras för att den ska fånga pausen snabbt.
Att definiera tillstånder är en bit på vägen men det säger inget om vilka rutiner som behövs. Du behöver en rutin som tar hand om den inkommande datablock och sparar den på rätt sätt, du behöver en rutin för att skicka data i det valda formatet osv.
Det "gamla" tänkande med "nu ska den göra detta och här står vi till det är klart" är förlegat, det kan fungera till viss mån när man inte har realtidsdata men det du ska göra är att skapa ett sorts OS som tar hand om realtidsdata, petar i buffrar, signalerar i flaggor osv varefter din main-loop kan köra det gamla viset, beta av allt eftersom.
Du måste ställa upp en definition av vilka funktioner som ska utföras i de olika tillstånder, en del av dom lär vara mycket lika och enda skillnad kan vara en flagga men det blir en del tänkande innan du kan programmera klart.
Det viktigaste är att vara säker på hur ditt dataformat ser ut, sedan ska du se till att kunna hämta in datan i en buffer och signalera hel block.
Därnäst kan du göra en rutin som behandlar de inkommande data enl. de behov som finns.
Du måste alltså FÖRST av allt skriva ner:
START - vad ska ske här, synkronisera tid? Meddela närvaru? osv.
LEARN - vad ska den göra här? osv osv.
Tack, den här diskutionen gillar jag. Något som oftast inte brukar tas upp är hur man bör strukturera upp programmeringsarbetet.
Ang interrupten så ska man alltså lägga lite kod i själva interruptrutinen. Först för att kolla vilket interrupt som såg till att det blev avbrott och sedan om det tex var ett UART-interrupt så hämtar man den inkommande byten och sedan återställer flaggorna för att kunna ta emot nästa avbrott.
Fuktionerna i kompilatorn är tänkt för att underlätta programmerandet men ibland gör de lite för mycket verkar det som så att den inte använder interupt utan sitter och väntar i stället tex.
Ang tillstånden har jag definierat till viss del vad som ska hända i de olika tillstånden men jag borde nog utöka till fler tillstånd för att få med allt misstänker jag.
Kan förresten rekommendera UMLpad för att göra tillståndsdiagram. Mycket litet och smidigt freeware för windows.
Ang interrupten så ska man alltså lägga lite kod i själva interruptrutinen. Först för att kolla vilket interrupt som såg till att det blev avbrott och sedan om det tex var ett UART-interrupt så hämtar man den inkommande byten och sedan återställer flaggorna för att kunna ta emot nästa avbrott.
Fuktionerna i kompilatorn är tänkt för att underlätta programmerandet men ibland gör de lite för mycket verkar det som så att den inte använder interupt utan sitter och väntar i stället tex.
Ang tillstånden har jag definierat till viss del vad som ska hända i de olika tillstånden men jag borde nog utöka till fler tillstånd för att få med allt misstänker jag.
Kan förresten rekommendera UMLpad för att göra tillståndsdiagram. Mycket litet och smidigt freeware för windows.
Ni som är vanare PIC programmerare än mig. Brukar ni inte försöka göra rutiner som kan användas även i framtiden för andra applikationer eller gör ni allt för den applikationen ni håller på med för tillfället och sedan om ni har gjort någon funktion i tidigare app som skulle kunna fungera med vissa modifikationer så plockar ni dem, eller?
Känns som jag kanske borde glömma att göra rutiner som ska kunna fungera universiellt för framtiden också och i stället koncentrera mig på det jag håller på med och se till att rutinera fungerar för den applikationen i första hand.
Det jag menar är att de rutiner jag gör nu så stoppar jag in pekare och variabler mm i anropet istället för att låta funktionen ändra direkt på globala variabler.
Känns som jag kanske borde glömma att göra rutiner som ska kunna fungera universiellt för framtiden också och i stället koncentrera mig på det jag håller på med och se till att rutinera fungerar för den applikationen i första hand.
Det jag menar är att de rutiner jag gör nu så stoppar jag in pekare och variabler mm i anropet istället för att låta funktionen ändra direkt på globala variabler.
Jag har vissa rutiner som jag kopierar in när jag behöver vissa funktioner men när det gäller ISR håller jag hårt på att jag vill optimera vilket gör varje projekt ganska unikt fastän det ofta är mycket lika i vissa delar.
Att man ska bygga upp ett bibliotek kan vara bra men ISR är för mig en sak som ska klaras snabbast möjligt vilket ofta betyder att man kopierar och klistrar från andra projekt och sedan finputsar.
Universella rutiner kan vara bra, jag har sådana till I²C, LCD-moduler, min RTC, SPI EEPROM, BIN<->HEX och dylikt men man ska välja vad man vill ha generellt noga, det löner sig definitivt inte att ha ALLT generellt.
Att man ska bygga upp ett bibliotek kan vara bra men ISR är för mig en sak som ska klaras snabbast möjligt vilket ofta betyder att man kopierar och klistrar från andra projekt och sedan finputsar.
Universella rutiner kan vara bra, jag har sådana till I²C, LCD-moduler, min RTC, SPI EEPROM, BIN<->HEX och dylikt men man ska välja vad man vill ha generellt noga, det löner sig definitivt inte att ha ALLT generellt.