přerušení vyvolané usartem

Diskuze a poradna o programátorech a programování různých obvodů

Moderátor: Moderátoři

Zpráva
Autor
Uživatelský avatar
hajs
Příspěvky: 31
Registrován: 28 kvě 2007, 02:00

přerušení vyvolané usartem

#1 Příspěvek od hajs »

Ahoj, potřeboval bych prosím poradit, nebo aspon nasměrovat správným směrem.
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 84 x

Uživatelský avatar
bohumilfulin
Příspěvky: 109
Registrován: 12 led 2010, 01:00

#2 Příspěvek od bohumilfulin »

Musis si vytvorit komunikacni protokol. Uroven slozitosti necham na tobe. U primitivnich veci muze postacovat STARTBYTE + Commandbyte nebo jenom jednobytovy prikaz. U slozitejsich to muzes vysperkovat o CRC atd, atd.

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?

Jenda_KL
Příspěvky: 1173
Registrován: 10 zář 2008, 02:00
Bydliště: Kadaň
Kontaktovat uživatele:

#3 Příspěvek od Jenda_KL »

V tom Cčku to je fakt šílenost.
Musí se nastavit UCSRB.RXCIE , SREG.I a rutina pro obsluhu se dá na specifickou adresu, DS ví víc a přesnějc.

Uživatelský avatar
AB1
Příspěvky: 312
Registrován: 23 lis 2009, 01:00

#4 Příspěvek od AB1 »

Jde to i bez přerušení, pro jednobajtové povely třeba takto (vytvoř si funkce pro krok)

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)  
}

Jenda_KL
Příspěvky: 1173
Registrován: 10 zář 2008, 02:00
Bydliště: Kadaň
Kontaktovat uživatele:

#5 Příspěvek od Jenda_KL »

Je požadováno zpracovat vícebajtový řetězec a to se při běžícím jiném programu dá jen dalším procesorem nebo přerušením.
---
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


Uživatelský avatar
ZdenekHQ
Administrátor
Administrátor
Příspěvky: 25593
Registrován: 21 črc 2006, 02:00
Bydliště: skoro Brno
Kontaktovat uživatele:

#6 Příspěvek od ZdenekHQ »

Taková komunikace se dělá celá v přerušení - po příjmu znaku se uloží data do nějakého bufferu s tím, že se vyhodnocuje čas do příjmu dalšího znaku (překročení se bere jako povel pro vyhodnocení), počet znaků, crc atd. a pokud vše sedí, teprve potom si to převezme hlavní program, buffer se smaže, vynulují čítače času atd.
Pro moje oslovení klidně použijte jméno Zdeněk
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[?
]

Uživatelský avatar
AB1
Příspěvky: 312
Registrován: 23 lis 2009, 01:00

#7 Příspěvek od AB1 »

Při jakém "jiném běžícím programu"?
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í..

Uživatelský avatar
mtajovsky
Příspěvky: 3694
Registrován: 19 zář 2007, 02:00
Bydliště: Praha

#8 Příspěvek od mtajovsky »

Základní vám popsal bohumilfulin. Pokud budete muset reagovat na vícero reálných asynchronních událostí, tak zde je několik zásad asynchronního programování:

- 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.

Jenda_KL
Příspěvky: 1173
Registrován: 10 zář 2008, 02:00
Bydliště: Kadaň
Kontaktovat uživatele:

#9 Příspěvek od Jenda_KL »

AB1 píše:Při jakém "jiném běžícím programu"?
hlavním běžícím programu "obsluha krokového motoru".
Tu by bylo dobré dát do INT od časovače a zbytek propojit v mainu, jak popsal mtajovsky.

Uživatelský avatar
hajs
Příspěvky: 31
Registrován: 28 kvě 2007, 02:00

#10 Příspěvek od hajs »

diky za pomoc ale jsem naprostý začátečník takže bych spíš potřeboval, příklad kodu v C jak nastavit správně přerušení a přijmout celý řetězec.
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.

Uživatelský avatar
mtajovsky
Příspěvky: 3694
Registrován: 19 zář 2007, 02:00
Bydliště: Praha

#11 Příspěvek od mtajovsky »

hajs píše:Např ve smyčce kdy se motor točí dopředu , bych ještě znova testoval zdali se nezměnil příkaz, ...
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:
- 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

Uživatelský avatar
ZdenekHQ
Administrátor
Administrátor
Příspěvky: 25593
Registrován: 21 črc 2006, 02:00
Bydliště: skoro Brno
Kontaktovat uživatele:

#12 Příspěvek od ZdenekHQ »

Obávám se, že teorie někdy příliš složitě popisuje praxi. Dobrá věc na doktorát, ale příliš složitá pro praxi.

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ý.
Pro moje oslovení klidně použijte jméno Zdeněk
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[?
]

Uživatelský avatar
hajs
Příspěvky: 31
Registrován: 28 kvě 2007, 02:00

#13 Příspěvek od hajs »

4 řídící příkazy mám pevně dané (:F+#; :F-# ; :FF#; :FS#; :FQ#) s tím nemohu nic měnit, takže pokud znam predem 3znaky ze 4 tak je ošetřena částečně i chybovost. Potřebuju jen v rámci přerušení přijmout celý řetězec, ukončit přerušení, a vrátit se. A pak přijatý řetězec porovnat s aktualnim stavem. Nic víc :) Je to vůbec možné?

Uživatelský avatar
ZdenekHQ
Administrátor
Administrátor
Příspěvky: 25593
Registrován: 21 črc 2006, 02:00
Bydliště: skoro Brno
Kontaktovat uživatele:

#14 Příspěvek od ZdenekHQ »

V rámci přerušení se jen pasivně přijímají znaky do bufferu, hlavní program dělá zbytek. Dokud není v rámci určitého časového rámce přijat celý smysluplný příkaz, hlavní program provádí poslední příkaz a buffer se maže.

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.
Pro moje oslovení klidně použijte jméno Zdeněk
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[?
]

Uživatelský avatar
Panda38
Příspěvky: 713
Registrován: 21 lis 2012, 01:00
Bydliště: Most, Praha, Lanžhot
Kontaktovat uživatele:

#15 Příspěvek od Panda38 »

Pokud je takhle přesně daný rámec paketů a liší se jen commandem, tak pak je lepší jen počítat bajty a vyhodnocovat strukturu paketu už v přerušení (v hlavní smyčce rozlišovat jen command):

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 ....

Odpovědět

Zpět na „Programování PIC, ATMEL, EEPROM a dalších obvodů“