přerušení vyvolané usartem
Moderátor: Moderátoři
přerušení vyvolané usartem
Ovládám přes seriovou linku a tiny2313 krokový motor. Řízení probíhá tak že odešlu řetězec a atmel ho vyhodnotí a otáčí motorem bud doleva/doprava určený čas. Ale ted bych potřeboval aby se motor točil do té doby něž přijde nový příkaz, a podle vyhodnocení se bud točil dál nebo stál. K tomu je potřeba vyvolat přerušení , pokud se nepletu..
Ale nedaří se mi přijít na to jak správně nastavit přerušení na příchozí data, ty přijmout a pak přes switch zpracovat.
V příloze mám aktualní kod bez přerušení
- Přílohy
-
test-1.txt- (2.78 KiB) Staženo 132 x
- bohumilfulin
- Příspěvky: 111
- Registrován: 12 led 2010, 00:00
Vychazejme z verze STARTBYTE + COMMANDBYTE. Prvni znak co prijde testujes zda je to STARTBYTE pokud ano, spustis casove omezeni prijmu a cekas na dalsi prijem znaku (preruseni od seriove linky). Kdyz prijde otestujes zda tomuto bytu predchazel Startbyte a zda je v casovem intervalu. Pokud ano vezmes jej a vyhodnotis (az do teto doby je platny predchozi prikaz nebo stav po resetu). Po vyhodnoceni zastavis casove omezeni a vse znovu. To casove omezeni je tam dulezite aby se ti linka nezustala viset treba kvuli ruseni co se nachomitne na linku.
Staci takto?
Kód: Vybrat vše
uint8_t mot_pause = 60; // pausa mezi kroky motoru
char rec_byte; // bajt přijatý z UART
uint8_t j;
while(1)
{
rec_byte = 0;
if(bit_is_set(UCSRA, RXC)) // přišel bajt?
{
rec_byte = UDR;
if(rec_byte == '+')
for(j=0;j<5;j++) krok_plus(); // 5 kroků směrem +
if(rec_byte == '-')
for(j=0;j<5;j++) krok_minus();
if(rec_byte == '1') mot_pause = 30; // motor rychle
if(rec_byte == '0') mot_pause = 60; // motor pomalu
if(rec_byte == 'L' || rec_byte == 'l')
while(2) // motor se točí doleva
{
krok_minus();
if(bit_is_set(UCSRA, RXC)) break; // vystup z while(2)
}
if(rec_byte == 'R' || rec_byte == 'r')
while(3) // motor se točí doprava
{
krok_plus();
if(bit_is_set(UCSRA, RXC)) break; // vystup z while(3)
}
}//if(bit_is_set
}//while(1)
}---
minimum pro Mega64
Kód: Vybrat vše
.cseg
.org $0 RJMP INIT
.org $024 RJMP USART_REC
.org $034 RETI
USART_REC:
push r21
in r21,sreg
// tady rutina pro prijem
out sreg,r21
pop r21
RETI
INIT:
; config StackPointer
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,high(RAMEND)
out SPH,r16
; config USART
ldi r16,25 ; Set BR
ldi r17,0
sts UBRR0H, r17
out UBRR0L, r16
ldi r16, 0b10001110 ;URSEL UMSEL UPM1 UPM0 USBS UCSZ1 UCSZ0 UCPOL
sts UCSR0C,r16
ldi r16, 0b10011000 ;RXCIE TXCIE UDRIE RXEN TXEN UCSZ2 RXB8 TXB8
out UCSR0B,r16
; IntEn
sei
LOOP:
//tady main prog
RJMP LOOP
- ZdenekHQ
- Významný člen
- Příspěvky: 25519
- Registrován: 21 črc 2006, 00:00
- Bydliště: skoro Brno
- Kontaktovat uživatele:
Správně navržené zapojení je jako recept na dobré jídlo.
Můžete vynechat půlku ingrediencí, nebo přidat jiné,
ale jste si jistí, že vám to bude chutnat[?]
Bavíme se o zadání z původního příspěvku.
Tam to jde bez přerušení i kdyby příkaz byl vícebajtový.
Ale nevidím žádnou výhodu v použití příkazů ":1" , ":2" proti "1" , "2".
Proč označovat začátek jednobajtového příkazu?
Říkám jenom, že to v tomto jednoduchém případě jde, ne že je to nejlepší řešení..
- datový rámec přenosového protokolu by měl někde na začátku v hlavičce obsahovat délku dat (případně i verzi protokolu). Vyhnete se tak na vrstvě obslužné rutiny přerušení od příjmu dat analýze jestli je už přijato všechno. Rutina jen bude skládat data do bufferu a po každém znaku nastaví událost příjmu znaku. Tuto událost pak zpracovává centrální automat a až teprve ten rozhoduje, jestli je už přijato všechno nebo ne a co dál s přijatým povelem.
- vyhněte se jakýmkoliv čekacím smyčkám v přerušovacích rutinách a nejlépe úplně všude.
- pokud chcete zpracovávat nějaký časový interval, třeba hlídat dobu příjmu rámce protokolu, nebo jen počkat nějakou dobu, tak si udělejte obsluhu přerušení od časovače, která dekrementuje nějaký čítač, nebo i více čítačů. Můžete zpracovávat i mnoho různých časových intervalů současně. Časovač se nahodí nastavením čítače na nějakou hodnotu a od tohoto okamžiku se začne odměřovat čas. Pokud se některý čítač dostane v přerušovací rutině od HW časovače na nulu, došlo k vypršení daného času a nastavte příznak vzniku příslušné události.
- hlavní smyčka programu bude testovat výskyt událostí, které mohou třeba reprezentovány nastavováním nějakých bitů. Jakmile se detekuje, že někdo nastavil událost, třeba rutina příjmu znaku nastavila událost, že přišel znak, zavolá se centrální automat, který v sobě spustí příslušnou rutinu zpracování této události, ale, pozor, pro svůj aktuální stav. Zpracování stejné události se bude obecně lišit pro různé stavy. Dokonce, mnoho kombinací stavů a událostí bude vyloučených, například událost vypršení časovače, když nebyl nastaven a takto se dají detekovat různé bugy v kódu. Nakonec se nuluje příznak události.
- v centrálním automatu vznikne matice rutin, kde například řádek je dán aktuálním stavem a sloupec událostí. Většinou jsou tyto matice hodně řídké a jen několik rutin je třeba skutečně naprogramovat. Ostatní buď nic nedělají (return), nebo jejich vyvolání je chyba.
- dobře si rozmyslete stavy a události automatu. To je dáno konkrétní úlohou, kterou děláte.
Ač se to celé zdá jako velký orloj, tak s mírnou nadsázkou je to naprogramovat jednodušší než popsat. Při dodržení těchto zásad není pak problém systém rozšiřovat o další zpracovávané události z okolního světa a to pořád stejným způsobem. Hranicí je pak jen výkonnost procesoru. Takový způsobem je možno například simultánně třeba generovat řídící impulsy pro motor, přijímat data ze sériové linky a obsluhovat klávesnici.
Protokol kterým se to bude řídit je pevně daný , je to LX200 protokol pro řízení zařízení od Meade (astronomické dalekohledy), Já z toho budu používat pouze sadu pro ostření což jsou příkazy :F+#; :F-# ; :FF#; :FS#; :FQ#.
Nejraději bych v rámci přerušení přijmul celý řetězec do pole, a s tím bych pak dál pracoval. Např ve smyčce kdy se motor točí dopředu , bych ještě znova testoval zdali se nezměnil příkaz, vlivem přerušení, pokud ano, tak ukončit a vybrat přes case odpovídající ukon.
Nejsem si jist, že jsem dobře porozuměl, ale pokud chcete mít speciální smyčku pro pohyb motoru dopředu a v té něco testovat, aby se to nepropáslo, tak to bych udělal jinak. Smyčka detekce událostí má být jen jedna pro všechno a to, že se motor točí dopředu by mělo být poznamenáno stavem. Uvedu příklad pro běžný ss motor. Pokud například přijde příkaz k zastavení motoru a stav je "TOCENI_VPRED" nebo vzad, provede se sekvence:hajs píše:Např ve smyčce kdy se motor točí dopředu , bych ještě znova testoval zdali se nezměnil příkaz, ...
- vypnutí napětí do motoru
- nahození časovače na skutečné zastavení
- přepnutí do stavu "CEKANI_NA_ZASTAVENI"
Ve stavu čekání není možno zpracovávat další příkaz pro motor. Pokud by v tomto stavu byl zkompletován nový příkaz, tak příslušná rutina reakce na událost zkompletování tohoto příkazu jej jen uloží. Naopak, ve stavu "ZASTAVENO" by příkaz ihned vykonala.
Po vypršení časovače se provede:
- kontrola, že jsme ve stavu čekání, protože vypršení časovače zastavování v jiném stavu je bugem programu. (Tato kontrola obvykle vznikne automaticky voláním různých rutin na událost podle stavu.)
- přepnutí do stavu "ZASTAVENO"
- kontrola, jestli po dobu čekání nepřišel další příkaz a jeho případné spuštění s nastavením patřičného stavu a případně i čekáním na rozběh podobně, jako při zastavování.
Byl-li příkaz k reverzaci motoru tak se pokračuje spuštěním v opačném směru. Časovač se může pro malé motorky vynechat, ale jeho použití způsobí šetrnější ovládání motoru. Pro krokový motor se časovače vynechají, namísto toho bude v činnosti časovač odměřující krokování motoru. Naznačeným způsobem je třeba správně reagovat na povely v daných stavech a také pomocí přepínání stavu udržovat povědomí programu o tom, v jaké fázi ovládání motoru právě jsme.
http://cs.wikipedia.org/wiki/Mealyho_automat
- ZdenekHQ
- Významný člen
- Příspěvky: 25519
- Registrován: 21 črc 2006, 00:00
- Bydliště: skoro Brno
- Kontaktovat uživatele:
Spoustu podmínek lze ošetřit jen tím, že doba mezi příkazy bude vždy delší než potřebný čas na vykonání nejdelšího příkazu, t.j v tomto případě reverzace.
K té komunikaci - obecně v rámci alespoň nejjednoduššího zabezpečení proti rušení bych doporučil posílat přímou a následně negovanou hodnotu příkazu, v lepší případě i nějaký kontrolní součet. Nutný je naopak nějaký time-out časovač komunikace, jinak je to nepoužitelný.
Správně navržené zapojení je jako recept na dobré jídlo.
Můžete vynechat půlku ingrediencí, nebo přidat jiné,
ale jste si jistí, že vám to bude chutnat[?]
- ZdenekHQ
- Významný člen
- Příspěvky: 25519
- Registrován: 21 črc 2006, 00:00
- Bydliště: skoro Brno
- Kontaktovat uživatele:
Pokud jde o reverzaci, pokud přichází protichůdně požadavky v rámci brždění, hlavní program prostě jen brzdí a čeká, to je věc jednoho stavového bitu.
Vážně se to hůř popisuje než programuje.
Správně navržené zapojení je jako recept na dobré jídlo.
Můžete vynechat půlku ingrediencí, nebo přidat jiné,
ale jste si jistí, že vám to bude chutnat[?]
- Panda38
- Příspěvky: 725
- Registrován: 21 lis 2012, 00:00
- Bydliště: Most, Praha, Lanžhot
- Kontaktovat uživatele:
Kód: Vybrat vše
char Cmd;
bool CmdOK = False;
char PktLen = 0;
...v přerušení (mimo jiné):
char znak = UDR;
switch (PktLen)
{
case 0:
if (znak == ':') PktLen = 1;
break;
case 1:
PktLen = (znak == 'F') ? 2 : 0;
break;
case 2:
Cmd = znak;
PktLen = 3;
break;
default:
PktLen = 0;
CmdOK = (znak == '#');
}
...v hlavní smyčce:
if (CmdOK)
{
CmdOK = False;
switch (Cmd)
{
case ....