VYSOKÁ ŠKOLA POLYTECHNICKÁ JIHLAVA Katedra elektrotechniky a informatiky Obor Počítačové Systémy
Systém monitorování teploty pomocí sběrnice RS-485 Bakalářská práce
Autor: Miroslav Stehlík Vedoucí práce: Ing. Bc. David Matoušek Jihlava 2013
II
Anotace Tato práce se zabývá kompletním návrhem systému pro vícebodové měření teplot v rozsáhlých prostorách, jako jsou například sklady, kancelářské prostory či výrobní haly. Monitorování probíhá pomocí měřicích jednotek osazených mikroprocesory AVR, komunikujících i na velké vzdálenosti pomocí sběrnicí RS-485. Samotné měření teploty je realizováno inteligentním čidlem Dallas DS18B20 propojeným s měřicí jednotkou pomocí sběrnice 1-Wire. Údaje z jednotlivých měřicích jednotek jsou shromažďovány v mikroprocesorových serverech, které je dále zprostředkovávají nadřazenému počítačovému serveru. Počítačový server je připojen pomocí galvanicky odděleného převodníku z USB na RS-485.
Klíčová slova Převodník z USB na RS-485, FT232R, galvanické oddělení, sběrnice RS-485, návrh komunikačního protokolu, asynchronní přenos dat, Dallas 1-Wire, teplotní čidlo DS18B20, procesory AVR, mikroprocesorový server, jazyk C, C++.
Abstract This thesis deals with an overall design of a system for multipoint temperature measurement in large spaces, such as storehouses, office buildings or factory buildings. Monitoring is performed by measuring units with embedded AVR microprocessors, communicating even over long distances via the RS-485 bus. The temperature measurement itself is implemented by Dallas DS18B20 smart sensor, connecting to the measuring unit with 1-Wire bus. Data from the separate measuring units are gathered on microprocessor servers, which mediate the data to a superior computer server. The computer server is connected with a galvanic isolated USB to RS-485 converter.
Keywords USB to RS-485 converter, FT232R, galvanic isolation, RS-485 bus, communication protocol design, asynchronous data transfer, Dallas 1-Wire, DS18B20 temperature sensor, AVR processors, microprocessor server, C, C++.
III
Bibliografická citace práce: STEHLÍK, M. Systém monitorování teploty pomocí sběrnice RS-485: bakalářská práce. Jihlava: Vysoká škola polytechnická Jihlava, katedra elektrotechniky a informatiky, 2013. 187s
Poděkování Tímto bych chtěl poděkovat vedoucímu mé bakalářské práce Ing. Davidu Matouškovi za jeho praktické rady potřebné k realizaci této práce. Dále bych také rád poděkoval firmě Tescan, a.s. za poskytnutí podpory potřebné ke zhotovení a testování praktické části této práce.
IV
Prohlášení Prohlašuji, že předložená bakalářská práce je původní a zpracoval/a jsem ji samostatně. Prohlašuji, že citace použitých pramenů je úplná, že jsem v práci neporušil/a autorská práva (ve smyslu zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů, v platném znění, dále též „AZ“). Souhlasím s umístěním bakalářské práce v knihovně VŠPJ a s jejím užitím k výuce nebo k vlastní vnitřní potřebě VŠPJ . Byl/a jsem seznámen/a s tím, že na mou bakalářskou práci se plně vztahuje AZ, zejména § 60 (školní dílo). Beru na vědomí, že VŠPJ má právo na uzavření licenční smlouvy o užití mé bakalářské práce a prohlašuji, že s o u h l a s í m s případným užitím mé bakalářské práce výhradně nekomerčním způsobem, za účelem výuky nebo k vlastní vnitřní potřebě školy. Jsem si vědom/a toho, že užít své bakalářské práce či poskytnout licenci k jejímu využití mohu, jen není-li to v rozporu s oprávněnými zájmy školy. V případě komerčního užití má škola nárok ode mne požadovat přiměřený příspěvek na úhradu nákladů, vynaložených vysokou školou na vytvoření díla (až do jejich skutečné výše), z výdělku dosaženého v souvislosti s užitím díla či poskytnutím licence.
V Jevišovicích dne 1. 5. 2013
...................................................... Podpis
V
Obsah 1
Úvod a cíl práce ......................................................................................................... 8
2
Návrh řešení............................................................................................................... 9
3
2.1
Převodník z USB na RS-485 .............................................................................. 9
2.2
Hardware měřicí jednotky ................................................................................ 10
2.3
Komunikační protokol ..................................................................................... 10
Sběrnice RS-485 ...................................................................................................... 14 3.1
4
USB ......................................................................................................................... 18 4.1
5
6
7
8
Výpočet rozdílů mezi zemními potenciály ...................................................... 15
Integrovaný obvod FT232R ............................................................................. 18
Převodník z USB na RS-485 ................................................................................... 19 5.1
Výběr součástek ............................................................................................... 19
5.2
Popis zapojení .................................................................................................. 20
5.3
Návrh desky plošných spojů ............................................................................ 22
5.4
Naprogramování FT232R ................................................................................ 24
Komunikační protokoly ........................................................................................... 25 6.1
Protokoly ASCII............................................................................................... 25
6.2
Protokoly ASCII s hexadecimálním řetězcem ................................................. 26
6.3
Protokoly binární .............................................................................................. 27
6.4
Návrh vlastního komunikačního protokolu ...................................................... 27
6.4.1
Startovací sekvence a synchronizace paketu ............................................ 28
6.4.2
Struktura paketu ........................................................................................ 29
Mikroprocesory řady ATmega ................................................................................ 33 7.1
Vývojové prostředí ........................................................................................... 33
7.2
Programovací jazyky........................................................................................ 33
7.3
Programování procesoru .................................................................................. 33
Sběrnice Dallas 1-Wire............................................................................................ 34 8.1
Teplotní čidlo DS18B20 .................................................................................. 35
8.1.1 9
Simulace sběrnice 1-Wire ......................................................................... 36
Měřicí jednotka ........................................................................................................ 38 9.1
Výběr součástek ............................................................................................... 38 6
9.2
Popis zapojení .................................................................................................. 39
9.3
Návrh desky plošných spojů: ........................................................................... 41
9.4
Firmware .......................................................................................................... 42
9.4.1
Main .......................................................................................................... 43
9.4.2
Modul rs485s ............................................................................................ 44
9.4.3
Modul ds18b20 ......................................................................................... 45
9.4.4
Modul eprom............................................................................................. 47
9.4.5
Modul wd_flo ........................................................................................... 48
9.4.6
Modul led .................................................................................................. 48
9.4.7
Pomocné soubory ...................................................................................... 48
9.5
Hardwarová konfigurace .................................................................................. 48
10 Mikroprocesorový server......................................................................................... 49 10.1
Výběr součástek ............................................................................................... 49
10.2
Popis zapojení .................................................................................................. 50
10.3
Návrh plošného spoje ....................................................................................... 52
10.4
Firmware .......................................................................................................... 54
10.4.1 10.5
Modul rs485m ........................................................................................... 54
Hardwarová konfigurace .................................................................................. 55
11 Realizace a testování ............................................................................................... 56 11.1
Proudový odběr ................................................................................................ 57
11.2
Naměřené průběhy ........................................................................................... 59
11.2.1
Převodník USB – RS-485, sběrnice RS-485 ............................................ 59
11.2.2
MCU serveru a Měřicí jednotky, sběrnice RS-485................................... 62
11.2.3
Měřicí jednotka, 1-Wire ............................................................................ 64
11.3
Sběr dat............................................................................................................. 70
12 Závěr ........................................................................................................................ 72 13 Citovaná literatura ................................................................................................... 74 14 Seznam tabulek ........................................................................................................ 76 15 Seznam obrázků....................................................................................................... 77 16 Seznam vzorců......................................................................................................... 79 17 Seznam příloh .......................................................................................................... 80 18 Přílohy ..................................................................................................................... 81 7
1 Úvod a cíl práce Předložená práce se zabývá vícebodovým měřením teploty pomocí digitálních čidel DS18b20 od firmy Dallas. V této práci popsaný měřicí systém je určen pro monitorování rozlehlých prostor, jako jsou například výrobní haly, kancelářské prostory, sklady, mrazicí boxy či stáje pro chov hospodářských zvířat. V těchto uvažovaných prostředích se mohou vyskytovat různé druhy rušení. Pro spojení jednotlivých měřicích jednotek bude použita průmyslová sběrnice RS-485. Tato sběrnice zajišťuje spolehlivý přenos dat na velké vzdálenosti i v zarušeném prostředí. Vlastní měření teploty pomocí digitálních čidel Dallas zjednodušuje potřebný hardware, neboť se vypustí analogově – digitální převod teploty, a tím umožní minimalizovat jednotlivé měřicí jednotky. Celý tento systém bude následně napojen na počítač, který bude shromažďovat a uchovávat záznamy o teplotách. K vlastnímu propojení počítače s měřicími jednotkami bude použit převodník z USB na RS-485. Cílem bude v první řadě navrhnout a zkonstruovat převodník z USB na RS-485, který bude následně využit pro ladění měřicích jednotek. Tento převodník musí být zkonstruovaný tak, aby byl schopen odolávat případným přepěťovým špičkám, které se mohou indukovat do vedení. V potaz je nutno vzít i možnost rozdílných potenciálů, které by se eventuálně mohly vyskytnout vlivem například zemních smyček. V druhé řadě bude potřeba navrhnout hardware měřicí jednotky. Také tato jednotka musí být odolná vůči eventuálním přepěťovým špičkám, a to zejména ze strany sběrnice RS-485. Důraz bude kladen i na spotřebu, jelikož budou jednotky napájeny poměrně dlouhými vodiči. Důvodem je, že proudový odběr nesmí být příliš velký, aby úbytky mezi zemními potenciály jednotek nepřekročily funkční úroveň budičů a přijímačů sběrnice RS-485. Dále bude potřeba navrhnout komunikační protokol, pomocí kterého se budou přenášet naměřená data z jednotek do počítače. Protokol bude třeba optimalizovat, aby jeho zpracovávání příliš nezatěžovalo procesory měřicích jednotek. Protokol také musí obsahovat nějaký minimální stupeň zabezpečení, aby bylo možné chyby vzniklé například vlivem rušení detekovat. V opačném případě by mohlo dojít k nesprávnému načítání dat z měřicích jednotek, aniž by to měřicí systém zaznamenal a přijatá data by interpretovala chybné hodnoty. 8
2 Návrh řešení Návrh řešení rozčleníme na tři dílčí části. První část se bude zabývat návrhem převodníku z USB na sběrnici RS-485. Druhá část návrhu bude věnována měřicím jednotkám. Část třetí pak problematice komunikačních protokolů.
2.1 Převodník z USB na RS-485 Existuje několik způsobů, jak převést USB rozhraní na sběrnici RS-485 [1] [2]. Můžeme například použít mikroprocesor s integrovaným USB řadičem. V tomto případě bychom ale museli napsat program nejen pro řízení periferie USB ze strany mikroprocesoru, ale i patřičný ovladač pro operační systém. Jelikož je USB periferie poměrně složité zařízení, které je schopno pracovat v několika různých režimech, byl by tento postup velice obtížný. Další a přijatelnější možností jsou předprogramované mikroprocesory například od firmy Cypress a nebo FTDI Chip. Tyto obvody již mají nahraný program, který z jedné strany ovládá komunikaci s USB a ze strany druhé UART. Do operačního systému se pak nahrají ovladače, které mohou vytvořit například virtuální sériový port a uživatel jej pak ovládá jako standardní zařízení. V této práci se zaměříme na obvody FTDI a to především pro jejich rozšířenost, cenovou dostupnost a podporu v operačních systémech.
Obr. 1: Blokové schéma převodníku USB – RS-485
Námi navrhovaný převodník bude tedy obsahovat obvod FT232R [1] [3], který zajistí převod dat z USB rozhraní na UART. Dále bude následovat diferenciální budič sběrnice RS-485, který bude galvanicky oddělen a chráněn přepěťovou ochranou. Blokové schéma převodníku znázorňuje Obr. 1. Hardware převodníku a ostatních zařízení bude
9
navrhnut pomocí systému Eagle Layout Editor, a to především z důvodu dlouholeté pozitivní zkušenosti s užíváním tohoto software.
2.2 Hardware měřicí jednotky Měřicí jednotka má za úkol, s využitím sběrnice Dallas 1-Wire [4] [5], přečíst teplotu z připojeného teplotního čidla a předat tuto hodnotu pomocí sběrnice RS-485 do počítače. K těmto účelům musí jednotka obsahovat mikroprocesor, hardwarové rozhraní Dallas 1-Wire a diferenciální budiče sběrnice RS-485. Proti případnému přepětí musí být všechny periferie chráněny přepěťovou ochranou. Obsluhu měřicí jednotky zajistí procesor s redukovaným instrukčním souborem řady AVR od firmy Atmel. V úvahu připadají především ATmega 8 či ATmega 16, které jsou popsány v prameni [6]. Tyto procesory se vyznačují moderním designem, poměrně vysokým výkonem a nízkou spotřebou. Především nízká spotřeba bude klíčová pro konstrukci této jednotky. Blokové schéma měřicí jednotky je znázorněno na Obr. 2.
Obr. 2: Blokové schéma měřicí jednotky
2.3 Komunikační protokol Při vytváření komunikačního protokolu je nutné nejdříve stanovit topologii sítě. V našem případě se bude jednat o komunikaci typu master – slave [7] [8]. Počítačový server pošle dotaz měřicí jednotce a ta mu odešle naměřená data. Základní topologie je znázorněna na Obr. 3. Jisté omezení ale přináší skutečnost, že v základu je ke sběrnici RS-485 připojitelných 32 zařízení. Jedno ze zařízení je master, takže měřicích jednotek by mohlo být maximálně 31. Použitím speciálních diferenciálních budičů lze tento počet rozšířit až na 128 zařízení.
10
Obr. 3: Základní topologie sběrnice
Pokud bychom potřebovali ještě více měřicích jednotek, tak by bylo třeba rozšířit topologii o další zařízení, které by sdružovalo měřicí jednotky v podsítě. Každá podsíť by mohla v základu obsahovat 31 měřicích jednotek a podsítí by mohlo být opět 31. Toto rozšíření umožní připojit až 961 měřicích jednotek. V případě užití dokonalejších diferenciálních budičů by celkový počet měřicích jednotek dosáhl 16129. Zařízení, které sdružuje prvky do podsítě, budeme nazývat mikroprocesorovým serverem (dále jen MCU server). Aby příslušný komunikační protokol nebyl příliš složitý, bude tento server cyklicky načítat celou svoji podsíť a ukládat si naměřená data do vyrovnávací paměti.
11
Obr. 4: Blokové schéma serveru
K této paměti pak bude možno přistupovat stejně, jako by se jednalo o samostatnou měřicí jednotku, jen bude obsahovat více naměřených dat. Na Obr. 4 je znázorněno blokové schéma mikroprocesorového serveru. Obr. 5 zobrazuje rozšířenou topologii. Za povšimnutí stojí, že každý mikroprocesorový server má vlastní napájecí jednotku. Důvod je prostý: předpokládaný proudový odběr jedné měřicí jednotky činí okolo 20−30 mA. V případě připojení 961 jednotek by proudový odběr činil přibližně 20−30 A. Takovýto proud by mezi jednotkami způsobil příliš velké úbytky napětí a diferenciální budiče sběrnice by tak nebyly schopné pracovat.
12
Obr. 5: Rozšířená topologie sběrnice
13
3 Sběrnice RS-485 Sběrnice RS-485 definuje fyzickou a linkovou vrstvu komunikačního rozhraní. Je určena pro asynchronní přenos dat s možností připojení až 32 účastníků. Základní princip sběrnice RS-485 spočívá v diferenciálním páru kroucených vodičů. Logická hodnota na sběrnici je definována jako rozdíl potenciálů mezi jednotlivými vodiči. Tato skutečnost činí sběrnici velmi odolnou vůči rušení. Vyskytne-li se totiž v místě vedení zdroj rušení, dochází k naindukování tohoto rušení do obou vodičů současně. Vzájemný rozdíl potenciálů však zůstává nezměněn. Díky této odolnosti se sběrnice dočkala velkého úspěchu i v náročné oblasti průmyslové techniky [7].
Obr. 6: Schéma propojení přijímače a vysílače diferenciálním párem
Na Obr. 6 je znázorněn základní princip propojení diferenciálním párem. Napětí Udif udává rozdílové napětí sběrnice a Ucm pak rozdíl zemních potenciálů. Na Obr. 7 je zobrazen diagram povolených napětí jednotlivých vodičů sběrnice RS-485. Svislá osa znázorňuje napětí neinvertujícího vodiče, vodorovná osa znázorňuje napětí vodiče invertujícího. Stav Mark reprezentuje úroveň log. „1“ a stav Space pak log. „0“. Komunikační vodiče bývají často označovány písmeny A a B. I když norma EIA definuje přiřazení těchto vodičů, v praxi velmi často dochází k jejich zaměňování.
Obr. 7: Diagram přípustných napětí vodičů sběrnice RS-485
14
Další důležitou součástí sběrnice jsou zakončovací rezistory. Tyto rezistory zamezují odrazům na vedení [9] a můžou napomáhat k definování klidového stavu sběrnice. Na Obr. 8 je znázorněno zakončení pomocí rezistoru R = 120 Ω. Druhá varianta zakončení je zobrazena na Obr. 9. Tato varianta zakončuje sběrnici kombinací rezistorů R1, R2 a R3 a zároveň definuje její klidový stav. Tato metoda je velmi výhodná pro binárně orientované protokoly, jelikož umožňuje použít mezeru ve vysílání k synchronizaci přenášeného paketu. Hodnoty rezistorů se volí především dle stupně přítomného rušení a proudových možností budičů sběrnice. Jejich paralelní kombinace by měla činit 120 Ω. Další informace o sběrnici uvádějí prameny [7] a [10].
Obr. 8: Schéma zakončení sběrnice rezistorem
Obr. 9: Schéma zakončení sběrnice rezistory
3.1 Výpočet rozdílů mezi zemními potenciály Na Obr. 10 jsou znázorněny úbytky napětí na napájecí větvi měřicích jednotek. Maximální povolené napětí mezi zeměmi jednotlivých budičů a přijímačů sběrnice RS-485 je -7 V až +12 V [11]. Z obrázku je patrné, že největší rozdíl potenciálů bude mezi zemí MCU serveru a poslední měřicí jednotkou.
Obr. 10: Schéma úbytků napětí na napájecí větvi měřicích jednotek
Pro odvození vzorce tohoto úbytku označeného jako U Gn budeme vycházet z předpokladu, že délka propojovacích kabelů je mezi měřicími jednotkami stejná. 15
Vzorec (1) odvozuje úbytek napětí mezi zemí MCU serveru a poslední měřicí jednotkou.
(1)
UGn – úbytek napětí mezi zemí MCU serveru a poslední měřicí jednotkou ΔUGi – úbytek napětí mezi zemí sousedních jednotek RG – celkový zemní odpor napájecí větve I – proud odebíraný jednou měřicí jednotkou n – celkový počet měřicích jednotek
Úpravou vzorce (1) dostaneme vzorec (2) pro výpočet maximálního přípustného odporu zemní napájecí větve RGMax. (2) UGMax – maximální přípustný úbytek napětí zemní napájecí větve RGMax – maximální přípustný odpor zemní napájecí větve
Dále musíme zohlednit úbytek napájecího napětí měřicích jednotek, který vzniká jak na zemní, tak i na kladné napájecí větvi. Z Obr. 10 je patrné, že nejmenší napájecí napětí zbyde na poslední měřicí jednotku. Vzorec (3) odvozuje vztah pro výpočet napájecího napětí poslední měřicí jednotky.
16
(3)
USup – napájecí napětí UVccn – napájecí napětí poslední měřicí jednotky
Úpravou vzorce (3) získáme vztah (4) pro výpočet maximálního přípustného odporu v napájecí větvi. (4) UVccn – minimální napájecí napětí poslední měřicí jednotky RVccMax – maximální přípustný odpor kladné napájecí větve
Pro účely propojení měřicích jednotek byl vybrán stíněný sdělovací kabel LiYCY 2x2x0,75. Tento kabel má dle pramene [12] odpor 25,3 mΩm-1. Zemní napájecí větev měřicích jednotek je tvořena paralelní kombinací stínění a jednoho vodiče. Z této skutečnosti vyplývá, že výsledný odpor zemní napájecí větve činí pouhých 12,73 mΩm-1.
Vezmeme-li v úvahu předpokládaný odběr měřicí jednotky 20 mA
a maximální povolené rozdílové napětí zemních potenciálů sběrnice RS-485 7 V a dosadíme jej do vzorce (2), vyjde nám maximální hodnota odporu pro 31 jednotek 21,8 Ω. Tato hodnota odporu odpovídá kabelu o přibližné délce 1,7 km. V této délce ale není přinejmenším zahrnut pokles vodivosti kabelů s oteplením, přechodový odpor svorkovnic a další možné nežádoucí vlivy. Rozdíl zemních potenciálů ale není jediný omezující faktor z hlediska napájení. Zbývá nám ještě stanovit nejmenší možné přípustné napájení. Minimální napájecí napětí měřicí jednotky osazené stabilizátorem L7805 bude přibližně 7 V. Vezmeme-li v úvahu napájecí napětí MCU serveru 12 V a dosadíme-li tyto napětí do vzorce (4), získáme maximální přípustný odpor v napájecí větvi přibližně 15,6 Ω. Tento odpor se rozdělí na 2/3 do kladné napájecí větve a na 1/3 do napájecí větve zemní. Délka kabelu by v tomto případě odpovídala 408 m. Na tomto příkladu je patrné, že napájecí napětí je za těchto podmínek limitující. Kdyby bylo napájecí napětí o pouhé dva volty větší, prodloužila by se vzdálenost na 570 m. 17
4 USB Sběrnice USB (Universal Serial Bus) vznikla za účelem vytvoření univerzálního rozhraní pro připojování externích periferií k PC. V současné době existují tři generační verze. USB 1.1, USB 2.0 a USB 3.0. Pro účely měřicího systému však vystačíme s dosud nejrozšířenější verzí USB 2.0. Fyzická vrstva tohoto rozhraní využívá k propojení čtyř vodičů. Dva z nich slouží k napájení připojeného zařízení a zbylé dva pro přenos dat pomocí diferenciálního páru. USB 2.0 podporuje také tři přenosové rychlosti: -
Low-Speed 1,5 Mb/s
-
Full-Speed 12 Mb/s
-
Hi-Speed 480 Mb/s.
Účastníci této sběrnice komunikují metodou master – slave. Počet zařízení připojených pomocí hubu činí až 127. Maximální délka propojovacího kabelu je omezena na 5m [1] [2].
4.1 Integrovaný obvod FT232R Integrovaný obvod FT232R od firmy FTDI Chip slouží k převodu sběrnice USB na sériový asynchronní kanál s úrovní TTL 3,3 V nebo 5 V. Typické zapojení tohoto obvodu zobrazuje Obr. 11. Přesný popis tohoto obvodu obsahují prameny [1] [3].
Obr. 11: Typické zapojení obvodu FT232R [3]
18
5 Převodník z USB na RS-485 Základem převodníku je integrovaný obvod FT232R [3]. Tento obvod z jedné strany komunikuje po sběrnici USB s počítačem a ze strany druhé vytváří sériový asynchronní kanál s TTL úrovněmi [1]. Tyto TTL signály se následně přivedou do diferenciálního budiče sběrnice RS-485. Jelikož můžou být jednotlivé mikroprocesorové servery napájeny z různých zdrojů napětí, je třeba výstupní diferenciální linku galvanicky oddělit od zemního potenciálu sběrnice USB. Toto oddělení také zvýší odolnost převodníku před přepěťovými špičkami. Pro napájení převodníku bude využito napájecí napětí sběrnice USB. Galvanické oddělení zajistíme pomocí rychlých optočlenů. Galvanicky oddělená část pak bude napájena přes DC/DC měnič. Celkový proud odebíraný z USB sběrnice nesmí překročit 500 mA [1].
5.1 Výběr součástek Integrovaný obvod FT232R se vyrábí v pouzdrech 28-LD SSOP a QFN32. Z důvodu snadné montáže byla vybrána varianta 28-LD SSOP. Obvod v tomto pouzdře se odlišuje postfixem „L“. FT232R obsahuje dokonce i vlastní oscilátor, takže k němu již nemusíme připojovat krystal. Jelikož bude sběrnice RS-485 pracovat na poměrně nízkých přenosových rychlostech, nejsou na její budič kladeny žádné zvláštní nároky. Často používaný integrovaný obvod SN75176 [11] je schopen pracovat na přenosových rychlostech v řádu megabitů za sekundu, což mnohonásobně převyšuje naše požadavky. Pro účely galvanického oddělení musíme vybrat vhodný DC/DC měnič 5 V/5 V. Přibližný proudový odběr galvanicky oddělené části by se měl s rezervou vejít do 200 mA. V úvahu připadá například měnič SIM1-0505S, nebo AM1S-0505SZ. Měniče řady SIM1 mají elektrickou pevnost do 3 kV. Oproti tomu řada AM1S má elektrickou pevnost pouze do 1 kV, ale umožňuje výstupní kapacitní zátěž až do 200 µF. Jelikož je třeba galvanicky oddělené napájení precizně vyhladit, aby případné proudové špičky diferenciálního budiče sběrnice nepronikaly do ostatních obvodů, vybereme raději měnič AM1S-0505SZ [13]. Pro galvanické oddělení signálů sériové linky použijeme optočleny. Celková šířka přenášeného pásma optočlenů by měla být přinejmenším trojnásobkem maximální 19
komunikační
rychlosti.
Pokud
bychom
předpokládali
komunikační
rychlost
115200 kb/s, potřebovali bychom celkovou šířku pásma až do 3,5 MHz. Taková frekvence je pro běžné optočleny dioda – tranzistor již příliš vysoká. Zvolíme tedy rychlé optočleny dioda – dioda s integrovaným zesilovačem. Vhodný je například optočlen HCPL-2601 [14], který je konstruovaný na přenosovou rychlost až 10 Mb/s.
5.2 Popis zapojení Na Obr. 12 je znázorněno schéma převodníku USB – RS-485. Konektor X3 přivádí z počítače pomocí USB – B koncovky napájecí napětí a datovou linku. Napájecí napětí je filtrováno pomocí LC článku. Tento článek tvoří kombinace keramických kondenzátorů C5, C10, C11, C17, tantalový kondenzátor C12 a axiální tlumivka Tl. Kondenzátor C10 zabraňuje především pronikání rušení z převodníku směrem do počítače, kondenzátory C5, C11 a C17 filtrují rušení z počítače do napájecí větve převodníku a zároveň spolu s kondenzátorem C12 pak blokují napájecí napětí obvodu IC3. Keramický kondenzátor C8 slouží k filtrování interního stabilizátoru obvodu IC3. Led diody TX_LED, RX_LED, ACK_LED připojené ke sběrnici CBUS indikují provozní stavy převodníku. Jelikož má integrovaný obvod FT232R interní kalibrovaný RC oscilátor, zůstanou vývody OSCI a OSCO nezapojeny. Rezistor R13 slouží jako pull-up pro interní resetovací obvody. Napájení galvanicky oddělené části zajišťuje DC/DC měnič DC1. Kondenzátory C3, C9 a C13 potlačují rušení generované DC měničem. Obdobně tak C16 filtruje výstup tohoto měniče a zároveň spolu s kondenzátory C2, C4, C6, a C14 blokuje napájení pro integrovaný obvod IC1 a optočleny RXD, DE, TXD. Dioda LED4 indikuje přítomnost napájecího napětí galvanicky oddělené části.
20
Obr. 12: Schéma převodníku USB – RS-485
Výstup TX z UARTU integrovaného obvodu IC3 vede přes rezistor R8 do rychlého optočlenu TXD. Rezistor R8 je zvolen s ohledem na minimální požadovaný vstupní proud 7,5 mA [14], který je potřeba k úplnému otevření výstupního tranzistoru optočlenu TXD. Jelikož je anoda diody optočlenu připojena ke kladnému napájecímu napětí, je výstupní tranzistor optočlenu sepnut při nízké výstupní úrovni vstupního signálu. Tím je zajištěno, že se vysílací signál TX přenáší ve správné fázi. Rezistor R3 slouží jako pull-up k výstupnímu tranzistoru v optočlenu TXD. Takto oddělený signál je následně přiveden na vstup D integrovaného obvodu IC1, který slouží jako diferenciální budič sběrnice RS-485. Obdobně jsou zapojeny i optočleny RXD a DE. Rezistory R16 a R18 slouží pouze jako propojky. Data přijatá ze sběrnice RS-485se z výstupu Ro integrovaného obvodu IC1 galvanicky oddělí pomocí optočlenu RXD a přivedou na vstup UARTU v obvodu IC3. Příjem a vysílání se řídí pomocí signálu CBUS0 z obvodu
IC3.
Diferenciální
signál
z budiče
sběrnice
RS-485
je
přiveden
na usměrňovací můstek, který pomocí transilů D5 a D6 omezuje případné přepěťové špičky. Spínač S1 slouží k připojení zakončovacích rezistorů R6, R15, R21, které zabraňují odrazům na sběrnici a zároveň udržují její klidový stav v definované úrovni. Rezistory R19 a R20 mohou být pro případ potřeby nahrazeny EMI filtry, které napomáhají potlačovat případné odrazy strmých hran signálu. Rezistory R9 a R10 pak slouží k vybíjení případného elektrostatického náboje z vedení a kondenzátory C1 a C15 svádí naindukované rušivé napětí přes svorkovnici X1-GND do země. Bleskojistka X omezuje maximální rozdíl potenciálů na 90 V. Tento rozdíl by měl být pro většinu aplikací postačující. Svorky X1-A, X1-B slouží k připojení datových vodičů. Na svorku X1-Shield je připojeno stínění. Seznam všech použitých součástek obsahuje Příloha 20.
5.3 Návrh desky plošných spojů Na Obr. 13 je znázorněna deska plošného spoje převodníku USB – RS-485. Plošný spoj se již na první pohled skládá ze dvou galvanicky oddělených částí. Na levé straně spoje se nachází část s převodníkem USB – USART TTL. Na této části desky je kritické především spojení obvodu IC3 s USB konektorem X3. Z důvodu vysokých přenosových rychlostí musí být datové vodiče USBDP a USBDM vedeny k obvodu co možná nejkratší cestou a bez zbytečných ohybů.
22
Obr. 13: Plošný spoj převodníku USB – RS-485, BOTTOM
Blokovací kondenzátory C5, C8 a C11 umístíme co nejblíže integrovaného obvodu IC3. Kondenzátory C3, C9 a C13 blokují rušivé proudy měniče a proto je musíme umístit poblíž DC konvertoru. Na pravé straně spoje se nachází galvanicky oddělený budič sběrnice RS-485. Tato část není nikterak kritická na vysoké frekvence, pouze musí být dodržena jistá minimální vzdálenost od levé strany. Pod optočleny je po obou vnitřních stranách natažená zemní cesta, která definuje kapacitní vazbu jednotlivých součástek každé z galvanicky oddělených částí. Blokovací kondenzátory C6 a C16 jsou umístěny poblíž DC měniče, aby zabránily pronikání rušivých proudů do ostatních obvodů. Jelikož integrovaný obvod IC1 budí poměrně velkou kapacitní zátěž, musí být tantalový kondenzátor C2 připojen nejkratší možnou cestou k integrovanému obvodu IC1.
Obr. 14: Osazovací plán převodníku USB – RS-485, TOP
23
Obr. 15: Osazovací plán převodníku USB – RS-485, BOTTOM
Obr. 14 zobrazuje osazovací plán součástek ze strany top. Zde jsou umístěny především součástky THT. Na Obr. 15 je osazovací plán ze strany Bottom. Tato strana je osazena plně SMD součástkami. Příloha 1 zobrazuje zhotovený převodník USB – RS-485.
5.4 Naprogramování FT232R Převodník USB – RS-458 připojíme k počítači a spustíme FT Prog. V záložce USB Config Descriptor zvolíme: -
Bus Powered
-
Max Bus Power 450 mA
V záložce USB String Descriptors můžeme zapsat vlastní popis zařízení. Dále je důležité v záložce Hardware Specific zaškrtnout High Current I/O’s. V záložce IO control nastavíme následující význam bitů CBUS: -
C0 - TXDEN
-
C2 - TXLED#
-
C3 - RXLED#
-
C4 - PWRON#
Celé nastavení nakonec nahrajeme do integrovaného obvodu FT232R. Nyní je převodník USB – RS-485 plně funkční.
24
6 Komunikační protokoly V současné době existuje celá řada komunikačních protokolů [7] [15], které využívají nejrůznější specializovaný hardware na fyzické a linkové vrstvě [8]. Tento specializovaný hardware umožňuje například přijímat a vysílat větší objem dat bez asistence procesoru, průběžnou synchronizaci asynchronního přenosu velkého počtu datových bitů bez potřeby vřazení start/stop sekvence, automaticky kontrolovat adresu zařízení a v opačném případě paket ignorovat, nebo také vysílaná data za běhu kódovat. Tato práce se ale bude zabývat především protokoly, vhodné pro přenos pomocí asynchronní sériové linky, integrované v procesorech AVR. Základem každého protokolu je synchronizační mechanizmus, pomocí kterého je přijímaná strana schopna z toku dat detekovat začátek a konec tak, aby bylo možné přijmout a rozeznat jednotlivé pakety. Každý protokol také musí obsahovat nějaký stupeň zabezpečení, který nám pomůže rozeznat případné chyby [16] [17], vzniklé například elektromagnetickým rušením. Pro účely měřicích jednotek připadají v úvahu protokoly založené na ASCII kódování, anebo protokoly binární.
6.1 Protokoly ASCII Princip ASCII protokolů spočívá v převodu přenášených dat na tisknutelné znaky. Například čísla se přenáší jako jednotlivé cifry zakódované podle ASCII tabulky [18]. Čísla je ale nutno nějakým způsobem převést na textový řetězec. To vyžaduje jistý výpočetní čas CPU, což je značně nevýhodné. V Tab. 1 jsou naznačeny příklady kódování. Na první pohled vidíme, že ASCII kódovaní přináší jistý nárůst objemu přenášených dat. K přenosu vlastní informace se totiž používá jen část ze všech kombinací bitového oktetu. Tento nárůst je ale vykoupen tím, že zbylé kombinace lze použít jako řídicí znaky a pomocí nich například ohraničit začátek a konec paketu. Tab. 1: Příklad kódování ASCII protokolu
Přenášené číslo 12345 binární kódování 00110000 00111001 ASCII kódování dekadické 49 50 51 52 53
25
Jednoduchý ASCII protokol je znázorněn v Tab. 2. Jako startovací znak je zvolena kombinace oktetů 0x02 a pro ukončení paketu pak 0x03. Tyto kombinace oktetů se již nikde jinde v paketu neobjevují, a proto je vždy možné bezpečně rozeznat začátek a konec telegramu. Pro základní zabezpečení ASCII protokolu postačuje například paritní bit, který je přenášen za posledním bitem datovým. Tab. 2: Jednoduchý ASCII protokol
start addr. 12345(data) stop hexa 0x02 0x31 0x31 0x32 0x33 0x34 0x35 0x03 ASCII STX '1' '1' '2' '3' '4' '5' ETX
Shrnutí:
+
Snadné rozpoznání začátku a konce paketu
-
Čísla je nutné převést na textový řetězec a zase zpět
-
Velký objem výsledných dat
6.2 Protokoly ASCII s hexadecimálním řetězcem Existuje ještě jedna zajímavá varianta ASCII kódování, která obchází složitou konverzi čísel na textový řetězec. V této variantě se čísla komplikovaně nepřevádí do dekadické soustavy. Každé číslo je rozděleno na čtveřici bitů, která se následně převede na ASCII znak, který reprezentuje číslo v hexadecimální soustavě. Tato konverze je poměrně jednoduchá a dá se provést pouze za pomocí jednoduchého součtu a jedné podmínky. Tab. 3: Jednoduchý ASCII protokol s hexadecimálním řetězcem
start addr. 12346(data) stop hexa 0x02 0x31 0x33 0x30 0x33 0x41 0x03 ASCII STX '1' '3' '0' '3' 'A' ETX
Díky této skutečnosti je tato varianta protokolu méně náročná na výpočetní výkon CPU. Za povšimnutí stojí i to, že daný způsob kódování nepřináší takový nárůst přenášených znaků, jako předchozí varianta jednoduchého ASCII protokolu. Tab. 3 zobrazuje jednoduchý ASCII protokol s hexadecimálním řetězcem.
26
Shrnutí:
+
Snadné rozpoznání začátku a konce paketu
-
Velký objem výsledných dat
6.3 Protokoly binární Základní myšlenka binárního protokolu je přenášet data přímo, bez žádného zbytečného kódování. To ale přináší potíže s rozpoznáním začátků a konců paketů. Data totiž mohou obsahovat libovolnou kombinaci oktetů, tudíž již není možné použít startovací a zakončovací znak, či lépe řečeno binární sekvenci. Pokud bychom zvolili startovací sekvenci například 0b10011010 (154d) a tato kombinace se vyskytla někde uprostřed přenášených dat, přijímací strana by pak nevěděla, jestli se jedná o pouhá data, nebo jestli nedošlo ke ztrátě paketu a tato sekvence uvozuje začátek paketu nového. Procesory AVR mají UART, který je schopen pracovat v 9bitovém režimu. Pokud bychom devátý bit využili například jako návěstí adresy paketu, vznikla by tím speciální sekvence, která by jednoznačně identifikovala začátek paketu. Konec paketu by pak bylo možné ošetřit například zařazením délky paketu za adresu. Problém pak ale nastává
ze
strany
počítače,
kde
není
standardně
implementovaná
9bitová
komunikace [10]. Paketovou synchronizaci tedy musíme zařídit jiným způsobem. V úvahu připadá použití časového rozestupu mezi jednotlivými pakety. Protože komunikace mezi měřicími jednotkami a serverem je typu master – slave, můžeme za určitých podmínek mezeru generovanou zařízením slave vypustit. Binární protokoly je také vhodné zabezpečit nějakým cyklickým redundantním kódem [16] [17]. Shrnutí:
+
Data jsou přenášena bez velké režie a konverzí
-
Složitější synchronizační mechanismus
6.4 Návrh vlastního komunikačního protokolu Při návrhu vlastního protokolu je třeba zvážit, jaký typ dat se bude převážně posílat po komunikační sběrnici. V případě měřicích jednotek bude po sběrnici přenášena převážně naměřená teplota, kterou reprezentuje 16bitové číslo se znaménkem. V takovém případě by bylo vhodné zvolit protokol s ASCII kódováním. Pokud ale 27
vezmeme v úvahu použití MCU serverů, bude zapotřebí pro zrychlení komunikace přenášet větší bloky dat s naměřenými teplotami. Následně by ale velikost paketů značně narostla. Abychom nemuseli vymýšlet dva různé protokoly, pro účely měřicích jednotek a MCU serveru zvolíme raději protokol binární.
6.4.1 Startovací sekvence a synchronizace paketu Na základě výše uvedených informací a vlastních zkušeností bude vhodné jako startovací sekvenci zvolit kombinaci alespoň dvou oktetů. Pro snadnější rozpoznání paketů od zařízení master nebo od zařízení slave zvolíme dvě odlišné startovací sekvence. Pro startovací sekvenci je vhodné vybrat takovou kombinaci bitů, která neobsahuje pokud možno žádné opakování.
Obr. 16: Nevhodná synchronizační sekvence
Na Obr. 16 je znázorněna startovací sekvence 0x5555, která je k synchronizaci naprosto nevhodná. Takto periodický signál může být snadno zaměněn například s rušením od nějakého spínaného zdroje.
Obr. 17: Zvolená synchronizační sekvence master a slave
28
Pro účely naší komunikační sběrnice zvolíme raději synchronizační sekvenci, která je zobrazena na Obr. 17. První startovací byte je stejný jak pro master zařízení, tak i pro slave. Druhý se liší pouze nepatrně. Díky těmto dvěma kombinacím, bude na první pohled jasné, jestli jsou pakety určeny pro měřicí jednotky, nebo pro server. Jako další synchronizační mechanizmus je použito vysílání jednoho oktetu ještě před začátkem samotného paketu. Pro tyto účely je vhodné zvolit oktet, který nebude obsahovat žádnou sestupnou hranu, která by mohla v případě zarušené linky způsobit nesprávné spuštění sériového asynchronního kanálu. Jedinou možnou kombinací je tedy číslo 0xFF. V zarušeném prostředí nemusí přijímač vlivem falešného spuštění tuto sekvenci vůbec zachytit, proto není na přijímací straně nijak kontrolována.
6.4.2 Struktura paketu Při volbě struktury paketu je třeba nejprve zvážit, co všechno budeme po našem komunikačním protokolu požadovat. Pokud bychom například chtěli pouze načítat z měřicí jednotky jednu naměřenou hodnotu a nic víc, stačily by nám dva druhy paketů. Tab. 4: Pakety pro jednoduchou master – slave komunikaci Požadavek na čtení (server)
Seq A 0x0A
Seq B 0x55
Addr xx
CRC xx
Odpověď (měřicí jednotka)
Seq A 0x0A
Seq B 0x53
Addr xx
Data xx xx
CRC xx
Prvním typem paketu by server požadoval po měřicí jednotce naměřená data, druhým typem paketu by pak měřicí jednotka odeslala naměřená data zpět serveru. Schéma celé komunikace znázorňuje Tab. 4. V případě užití MCU serverů bude zapotřebí načítat z jednoho MCU serveru více dat a to by výše zmíněné pakety neumožňovaly. Musíme tedy zavést nějaký způsob adresování v rámci jednoho serveru, či jednotky. Jelikož bude MCU server obsahovat pole dat s naměřenými teplotami, bude nejjednodušší variantou přenášet v požadavku na čtení i číslo prvku tohoto pole. Toto číslo budeme nazývat element. Pokud bychom po komunikačním protokolu dále chtěli například i do jednotek zapisovat, bude zapotřebí zavést do paketu položku, která bude určovat, zda se jedná o požadavek čtení, či zapisování dat. Příkaz budeme označovat zkratkou Cmd. V případě zápisu nebo čtení se tedy můžeme odkazovat na jednotlivé paměťové elementy. Pokud bychom ale chtěli například do jednotek zapisovat nějaké nastavení 29
a následně i toto nastavení z jednotek načítat, museli bychom rozlišit, zda se jedná o čtení naměřených hodnot, anebo čtení nastavení jednotky. Tento problém by šel vyřešit pomocí například kombinací příkazů. Měli bychom tedy příkazy pro čtení naměřených hodnot, čtení nastavení a zapisování nastavení. Další možností, jak tento problém vyřešit je zavedením adresovatelných celků v podobě bloků. Každá jednotka by obsahovala různé paměťové bloky. Například blok naměřených hodnot, blok nastavení, identifikační blok s popisem zařízení atd. Pro účely našeho měřicího systému zvolíme raději metodu bloků. Tato metoda je více variabilní a umožnuje mnoho dalších rozšíření. Tab. 5: Navrhovaný paket Seq A Seq B Addr 0x0A 0x55 xx
Cmd xx
Seq C Blok 0x71 xx
Elem xx
Size xx
Data xx
xx
CRC xx
Tab. 5 zobrazuje navrhovaný paket. Seq slouží k účelům synchronizace, Addr udává adresu jednotky, Cmd obsahuje přenášený příkaz, Blok určuje logickou skupinu dat, Elem určuje konkrétní prvek ze skupiny. Size udává počet přenášených dat a CRC je výsledek cyklického zabezpečovacího součtu. Výpočet CRC je možné provádět několika způsoby, jak uvádí například prameny [16] [17]. V našem případě potřebujeme z důvodu úspory výpočetního výkonu algoritmus pokud možno co nejjednodušší. Jako přijatelná varianta připadá v úvahu například bitový XOR anebo součet bajtů s následnou bitovou negací. Bitový XOR se provádí pouze mezi jednotlivými bity stejného řádu, na rozdíl od součtu bajtů, který sčítané bity přenáší i mezi řády. Zvolíme tedy raději bajtový součet s negací.
Tab. 6: Přehled příkazů
CMD Read Write Response ACK
Hodnota 0x81 0x82 0x84 0x88
Tab. 7: Adresace bloků
BLOK 0x01 0xFF
Paměť RAM Nastavení
Možné příkazy komunikačního protokolu jsou shrnuty v Tab. 6. Rozložení zmíněných logických bloků je zobrazeno v Tab. 7. Celkový přehled paketů zobrazuje Tab. 8. 30
Za povšimnutí stojí potvrzovací paket ACK, který vrací adresu zapisovaného bloku a elementu. Položka SIZE udává počet zapsaných slov. V případě neúspěchu je rovna nule.
Tab. 8: Přehled základních paketů navrhovaného protokolu
Server Read
Server Write
Server Read 0xFF
Unit Response 0xFF
Server Write 0xFF
Unit ACK 0xFF
0x0A 0x55 ADR 0x81 0x71 BLOK ELEM SIZE CRC
0x0A 0x53 ADR 0x84 0x71 BLOK ELEM SIZE FBYTE SBYTE CRC … … … … CRC
0x0A 0x55 ADR 0x82 0x71 BLOK ELEM SIZE FBYTE SBYTE CRC
0x0A 0x53 ADR 0x88 0x71 BLOK ELEM
… … … CRC
SIZE/NoACK
CRC
Byte 0 (Sync0) 1 (Sync1) 2(Sync Ma/Sl) 3 4 (Cmd) 5 (Sync3) 6 7 8 9 10 11 … … … … n
V Tab. 9 je zobrazeno rozložení paměti s nastavením měřicí jednotky či MCU serveru. Položka 0x00 reprezentuje příkaz, který má jednotka vykonat. Po vykonání příkazu se položka automaticky vynuluje. Této hodnotě odpovídá příkaz NOP (no operation). Seznam příkazů je znázorněn v Tab. 10. Význam jednotlivých paměťových položek a příkazů je podrobněji popsán v Příloha 18. Nastavení přenosových rychlostí je zapsáno v Tab. 12.
31
Tab. 9: Rozložení paměti s nastavením
Nastavení Příkaz Master Bitrate Master TX Pause Master RX Timeout Master ADR read max Master err read n Slave ADDRESS Slave Bitrate Slave TX Pause [ms] Slave RX Timeout Mes. Pause Temp. Offset[0] Temp. Offset[1]
Element 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C
Tab. 10: Přehled příkazů jednotky
Příkaz NOP Reset RAM to ROM ROM to RAM RAM to Module Load Default
Hodnota 0x00 0x01 0x02 0x03 0x04 0x05
Jelikož MCU server načítá data z měřicích jednotek a ukládá je do svojí mezipaměti, musíme zavést mechanismus, který nás bude informovat o chybových stavech měřicích jednotek. Aby nebyl tento mechanizmus příliš složitý, vymezíme rozsah hodnot, který bude reprezentovat chybové kódy. Načte-li nadřazený systém hodnotu z tohoto rozsahu, nahradí ji kódem z Tab. 11.
Tab. 11: Chybové kódy jednotek
Hodnota Chybové kódy Čidlo je odpojeno 0x8000 Chyba CRC čidla 0x8001 Chyba délky měření 0x8002 Čtení neexistující položky 0x8003 Jednotka není připojena 0x8004 Jednotka x * neodpověděla 0x800A
Tab. 12: Komunikační rychlosti
Bitrate 1200 2400 4800 9600 19200 38400
Hodnota 0x0000 0x0001 0x0002 0x0003 0x0004 0x0005
32
7 Mikroprocesory řady ATmega Mikroprocesory řady ATmega vychází z rodiny procesorů AVR. Tyto mikroprocesory jsou založeny na harvardské architektuře, a mají tedy oddělenou paměť programu a dat. Instrukční soubor obsahuje až 135 instrukcí. Procesory mají v základu 32 osmibitových registrů. Implementováno je také velké množství periferií jako jsou například čítače/časovače s PWM kanály, AD převodník, analogový komparátor, USART, SPI, TWI, JTAG a spoustu dalších. Pro účely měřicí jednotky také využijeme hardwarový watchdog v režimu Safety Level 2 a Brown-out detektor. Typická hodinová frekvence mikroprocesorů se pohybuje od 0 do 16 MHz. Jelikož mikroprocesory používají dvoufázový pipelining, jsou instrukce vykonávány za jednu až dvě periody taktovacího kmitočtu. Podrobný popis mikroprocesoru obsahují prameny [6] [19] [20].
7.1 Vývojové prostředí V současné době existuje několik vývojových prostředí pro mikroprocesory AVR. Nejrozšířenější jsou například AVR Studio, Keil, IAR a další. Pro účely měřicích jednotek budeme používat AVR Studio 4, a to především z důvodu dlouholeté zkušenosti
s tímto
vývojovým
prostředím
a
kompatibilitě
s programátorem
a debuggerem JTAG ICE.
7.2 Programovací jazyky Mikroprocesory AVR se dají programovat především v asembleru, anebo v jazyce C. Program měřicích jednotek bude poměrně obsáhlý, a proto bude lepší zvolit programování v jazyce C. K AVR Studiu tedy doinstalujeme GCC kompilátor. Popis práce s mikroprocesory AVR v jazyce C uvádějí prameny [6] [21].
7.3 Programování procesoru Mikroprocesory je možné programovat například paralelním programátorem, sériovým SPI programátorem anebo pomocí JTAG rozhraní. Pro účely měřicího systému bude vhodné použít rozhraní SPI nebo JTAG a to převážně z důvodu možnosti programování procesoru bez nutnosti vyjmutí z desky plošných spojů.
33
8 Sběrnice Dallas 1-Wire Sběrnice 1-Wire byla navržená firmou Dallas Semiconductor pro komunikaci zařízení typu iButton. Základní myšlenka této sběrnice spočívá ve dvouvodičovém propojení mezi jednotlivými účastníky. Připojená zařízení po těchto vodičích nejen komunikují, ale zároveň z nich mohou být i napájená. Komunikace jednotlivých účastníků sběrnice je typu master-slave. Počet zařízení připojených ke sběrnici je limitován proudovým odběrem účastníků s parazitním napájením a součtem zatěžovacích kapacit, které by neměly překročit 2,5nF [5]. Na Obr. 18 je znázorněno typické propojení jednoho účastníka s mikroprocesorem.
Obr. 18: Typické propojení jednoho účastníka sběrnice 1-Wire s mikroprocesorem [22]
Přesto, že je ke sběrnici přistupováno jednobitově, je většina dat přenášena po bajtech. Jednotlivé bity se na sběrnici zapisují v takzvaných timeslotech. Začátek komunikace je uvozen resetovacím pulsem a následným čtením pulsu prezenčního. Časový diagram inicializace je znázorněn na Obr. 19.
Obr. 19: Inicializace sběrnice 1-Wire [22]
34
Zápis jednotlivých bitů zobrazuje Obr. 20. Při zápisu log. „0“ stáhne master na několik mikrosekund komunikační vodič k zemi a následně jej uvolní. Aby byl takto zapsaný bit vyhodnocen jako log. „1“, musí napětí na sběrnici překročit horní komparační mez 3 V do 15 µs. Zápis log. „0“ probíhá stejným způsobem, jen se prodlouží doba, kterou master drží komunikační vodič na zemi. Minimální doba mezi zápisem dvou bitů činí 60 µs.
Obr. 20: Časový diagram sběrnice 1-Wire [22]
8.1 Teplotní čidlo DS18B20 Na Obr. 21 je znázorněno blokové schéma teplotního čidla DS18B20. Z obrázku je patrné, že teplotní čidlo má, kromě vodičů komunikačních, možnost připojit i externí napájení. Toho lze využít především ke zjednodušení měření a následného vyčítání naměřené teploty. Každá komunikace musí být započata inicializací, při které dojde ke stažení komunikačního vodiče k zemi na dobu minimálně 480 µs. Za tuto dobu dojde k vybití interního napájecího kondenzátoru Cpp (Obr. 21). Aby bylo možné naměřenou teplotu z čidla vyčíst, je nutno použít buď interní paměť eeprom k uložení hodnoty 35
před resetem čidla, anebo externí napájecí napětí, které zamezí ztrátě dat. Různé způsoby realizace zařízení typu master popisují prameny [4] [5]. Podrobněji je komunikace s čidlem popsána v pramenech [5] [22]
Obr. 21: Blokové schéma teplotního čidla DS18B20 [22]
8.1.1 Simulace sběrnice 1-Wire Na Obr. 22 je znázorněno zjednodušené simulační schéma sběrnice 1-Wire propojující měřicí jednotku a čidlo DS18B20. Indukčnost vedení nemá v tomto případě tak velký vliv a proto byla zanedbána. Cílem této simulace je navrhnout a zoptimalizovat rezistory R1 až R3. Rezistor R1 slouží jako protizkratová ochrana výstupních tranzistorů procesoru ATmega8. Jeho hodnota je stanovena z maximálního přípustného proudového zatížení těchto tranzistorů [20]. Rezistory R2 a R3 tvoří pull-up rezistor sběrnice 1-Wire. Rozdělení těchto rezistorů značně snižuje nežádoucí úbytky na ochranném rezistoru R1. Výsledky této simulace zobrazuje Obr. 23. V čase t = 100 µs, zapisuje na sběrnici měřicí jednotka hodnotu log. „1“. V tomto případě je třeba hlídat napětí na čidle, aby jeho úroveň klesla pod dolní komparační mez 0,8 V [22], potřebnou pro započetí nového timeslotu. O 10 µs později napětí překročí horní komparační mez a tím je na sběrnici zapsána log. „1“. Maximální přípustná délka nízké úrovně potřebná pro zapsání log. „1“ musí být kratší než 15 µs [22]. V čase t = 160 µs, zapisuje čidlo DS18B20 na sběrnici log „1“. V tomto případě musíme hlídat napětí na vývodu z procesoru, aby jeho úroveň klesla pod dolní komparační mez 0,8 V. K překročení horní komparační meze dojde také přibližně za 10 µs. Simulace ověřila správnost návrhu jednotlivých součástek.
36
Obr. 22: Simulační schéma sběrnice 1-Wire
Obr. 23: Časový diagram přechodových dějů na sběrnici 1-Wire
37
9 Měřicí jednotka Měřicí jednotka je řízena mikroprocesorem od firmy Atmel. Procesor zde plní dvě základní úlohy. V prvé řadě procesor komunikuje pomocí obvodu DS485 po sériové lince RS485 a v druhé řadě obsluhuje sběrnici Dallas 1-Wire, kde jako master načítá data z digitálního čidla DS18B20. Oba tyto procesy jsou na sobě nezávislé a tvoří tak dvě asynchronní programové smyčky. Programová smyčka sběrnice 1-Wire pak předává údaje o naměřené teplotě smyčce zprostředkovávající data dále do nadřazeného systému. K programování procesoru použijeme rozhraní ISP a programátor PonyProg od firmy Lancos.
9.1 Výběr součástek Pro řízení měřicí jednotky byl vybrán procesor ATmega8 [20] a to především kvůli jeho nízké spotřebě. Procesor se vyrábí v několika v několika pouzdrech. Pro účely měřicí jednotky vyhovují pouzdra PDIP28 a TQFP32. Nejvhodnější bude použít pouzdro PDIP28 a to především z důvodu jeho snadného použití při vlastnoruční výrobě plošných spojů. Další klíčovou součástí měřicí jednotky je stabilizátor napětí. Plánovaný proudový odběr jednotek je v řádu desítek miliampér. Spínané stabilizátory dokáží měnit napětí s vysokou účinností, ale jsou také o něco dražší a složitější než stabilizátory lineární. Při malých proudových odběrech účinnost spínaných stabilizátorů vlivem jejich vlastní spotřeby značně klesá. Pro tyto nevýhody zvolíme raději stabilizátor lineární. Jako nejvhodnější varianta se jeví stabilizátor L7805 [23] od firmy STMicroelectronics v pouzdře DPAK. Maximální hodnota svodového proudu je dle katalogového listu 6 mA. Dále potřebujeme vhodný budič diferenciální linky. Běžně používaný obvod SN75176 má příliš velký proudový odběr. Dle pramene [11] je proudový odběr v režimu příjem až 40 mA a v režimu vysílání bez zátěže až 50 mA. Takovýto odběr by působil velké úbytky na vedení a to by značně omezovalo jeho maximální možnou délku.
38
Vhodnější obvod je například DS485 [24] od firmy Texas Instruments a nebo ST485 [25] od firmy STMicroelectronics. Proudový odběr těchto obvodů v režimu příjem činí necelých 900 µA.
9.2 Popis zapojení Obr. 24 znázorňuje schéma zapojení měřicí jednotky. Svorkovnice X2 slouží k připojení napájecího napětí 12 VDC, komunikačních vodičů a stínění. Napájecí napětí pokračuje přes filtrační kondenzátory C3, C4, C5 a oddělovací diodu D5 do integrovaného stabilizátoru IC2 – L7805. Oddělovací dioda zabraňuje, v případě náhlého poklesu napájecího napětí, nebo zkratu na vedení, proražení stabilizátoru záporným úbytkem napětí na jeho svorkách. Výstupní napětí stabilizátoru je následně filtrováno blokačními kondenzátory C10 a C13. Takto stabilizované napětí napájí procesor i budič sběrnice RS-485. Průměrný proudový odběr předpokládáme přibližně okolo 30 mA. Sběrnice RS-485 je chráněna proti přepětí kombinací diod D1 až D4, transily D8 a D9. Jelikož by se budič sběrnice po zapnutí napájecího napětí do doby rozběhnutí procesoru mohl vlivem nedefinované vstupní úrovně přepnout jako výstupní a tím by zablokoval celou komunikační sběrnici, je připojen na jeho ovládací vstup pull-down rezistor R8, který spolehlivě zaručí nízkou úroveň na vstupu DE. Kondenzátor C1 filtruje proudové rázy budiče
sběrnice,
aby
nepronikaly
do
napájecího
napětí
ostatních
obvodů.
K mikroprocesoru IC1 – Atmega8 je připojen krystal 4 MHz, který za pomoci interní elektroniky generuje stabilní hodinový kmitočet. Proudový odběr procesoru v aktivním módu činí přibližně 7 mA při taktu 4 MHz [20]. Dip přepínačem S2 se konfiguruje komunikační rychlost. Přepínačem S1 se volí komunikační adresa. Rezistory R11 a R12 slouží jako zkratová ochrana výstupního portu procesoru. Jelikož je teplotní čidlo galvanicky izolováno od prostředí, nepředpokládá se výskyt cizího napětí na vodičích sběrnice 1-Wire. Dále je třeba brát v úvahu, že deska je minimalizovaná tak, aby se vešla do elektroinstalační krabice acidur, a proto je zde zvolen jednoduchý způsob ochrany, který chrání vodiče především proti zkratu mezi sebou. Diody D12 a D13 slouží jako EDS ochrana procesoru. Dioda LED1 signalizuje zapnutí napájecího napětí pro teplotní čidlo DS18B20.
39
Obr. 24: Schéma měřicí jednotky
Jelikož je sběrnice 1-Wire zakončena pouze pull-up rezistorem, musí být teplotní čidlo připojeno stíněným kabelem, aby se omezilo případné rušení elektromagnetickou indukcí z okolního prostředí. Potřebný pull-up rezistor rozdělíme na rezistory R4 a R5. Zmenší se tak nežádoucí úbytek na ochranném rezistoru R10. Celková kapacita komunikačního vodiče oproti společnému vodiči musí být maximálně 2,5nF [5]. Při použití kabelu LiYCY 3x0,14 [26] by přípustná délka kabelu k teplotnímu čidlu činila až 20 metrů. Pro účel naší měřicí jednotky postačuje délka okolo 5m. Seznam všech použitých součástek obsahuje Příloha 21.
9.3 Návrh desky plošných spojů: Na Obr. 25 je zobrazena deska plošného spoje měřicí jednotky. Při návrhu desky bylo hlavním kritériem, aby přípojné svorkovnice směřovaly jedním směrem a aby nebyla zbytečně velká vzdálenost připojení komunikačních vodičů A, B sběrnice RS-485. S výhodou je pak přepěťová ochrana umístěna mezi svorkovnici a chráněný obvod jakoby průchodově, což zlepšuje vlastnosti ochrany v případě externího přepětí. Uspořádání svorkovnice X2 je zvoleno tak, aby bylo v případě ovlhnutí plošného spoje minimalizováno riziko přepětí v napájecích obvodech na sběrnici RS-485.
Obr. 25: Plošný spoj měřicí jednotky, BOTTOM
Plošný spoj jednotky je z důvodu snadné vlastnoruční výroby koncipován pouze jako jednostranný. Celý spoj je optimalizován tak, že obsahuje pouze jednu propojku v napájecí větvi procesoru. Na Obr. 26 je znázorněn osazovací plán ze strany Top. Budič diferenciální sběrnice IC3 je z důvodu snadné výměny vložen do patice. Obr. 27 41
zobrazuje osazení SMD součástek ze strany Bottom. Příloha 2 zobrazuje zhotovenou měřicí jednotku.
Obr. 26: Osazovací plán měřicí jednotky,
Obr. 27: Osazovací plán měřicí jednotky,
TOP
BOTTOM
9.4 Firmware Zdrojový kód měřicí jednotky je rozdělen do několika modulů. Každý z modulů má za úkol obsluhovat jednu ze základních funkcí měřicí jednotky. Jelikož musí jednotlivé moduly včasně reagovat na různé události, je třeba alespoň do jisté míry zavést nějakou formu multitaskingu. Použití real-time operačních systémů (dále jen RTOS) nepřipadá vzhledem k výpočetnímu výkonu procesoru oproti potřebné časové odezvě v úvahu. Mimo jiné je také potřeba, aby procesor měřicí jednotky zbytečně nezatěžoval napájecí větev prováděním velkého množství instrukcí potřebných na samotný chod RTOS. Jako přijatelné se jeví rozčlenit jednotlivé úkoly na krátké úseky, které budou cyklicky spouštěny stavovým automatem v daném pořadí. Pokud budou úseky dostatečně krátké, výsledný čas potřebný na reakci jednotlivých modulů se tímto způsobem minimalizuje. Aby se celý program choval determinističtěji, je zapotřebí definovat četnost spuštění jednotlivých modulů za určitý časový úsek. K tomuto účelu byla převzata zjednodušená verze algoritmu, který byl původně vyvinut a používán pro účely řízení výpočtů vlastních regulátorů užívaných v oblasti průmyslové automatizace.
42
9.4.1 Main Obr. 28 zobrazuje vývojový diagram hlavního modulu main. Tento modul se stará o pomyslné přidělování procesoru jednotlivým modulům. Základ modulu činí přerušovací vektor časovače Timer2, který zajišťuje průběžnou rovnoměrnost spouštění ostatních modulů. Po naběhnutí napájení provede procesor nejdříve inicializaci proměnných a struktur. Dále následuje nastavení hardwaru, načtení paměti eeprom s nastavením do struktur jednotlivých modulů. Následně proběhne inicializace modulů. Tímto jsou moduly připraveny k cyklickému spouštění. Timer2 inkrementuje každou milisekundu proměnnou Time_1ms. Tato proměnná je v případě nenulové velikosti následně dekrementovaná v hlavní nekonečné smyčce programu. Po každém dekrementování jsou zavolány všechny moduly (pravá část diagramu). V případě že je proměnná Time_1ms nulová, je procesor převeden do režimu spánku a to až do dalšího přerušení. Zdrojový kód modulu main obsahuje Příloha 4.
Obr. 28: Vývojový diagram modulu main
43
9.4.2 Modul rs485s Obr. 29 zobrazuje zjednodušený vývojový diagram modulu rs485s. Tento modul obstarává sériovou komunikaci po sběrnici RS-485. Nejdříve modul čeká na přijetí paketu (Recieve). V dalším kroku je vytvořena odpověď na přijatý paket (Rx Compleat). Dále je generována prodleva mezi vysíláním (Tx Wait). Následně je spuštěno vysílání odpovědi (Tx Start). Po odvysílání paketu se modul opět převede do stavu přijímání (Recieve). Za povšimnutí stojí proměnná t1ms, která je použita k veškerému časování tohoto modulu. Vývojový diagram je zjednodušen o vektory přerušení sériového kanálu, a to především z důvodu jeho přehlednosti. Za zmínku stojí fakt, že samotná reakční doba takto řízeného systému by nestačila k přijímání či vysílání jednotlivých bajtů po sériovém kanálu. K tomuto účelu slouží fronta realizovaná cyklickým rotačním polem, do které jsou ukládány přijaté či posílané bajty. Funkce této fronty jsou z důvodu časové kritičnosti rozepsány přímo do vektorů přerušení. Zdrojový kód modulu rs485s obsahuje Příloha 5.
44
Obr. 29: Vývojový diagram modulu Rs485s
9.4.3 Modul ds18b20 Obr. 30 zobrazuje vývojový diagram modulu ds18b20. Tento modul se stará o cyklické načítání teploty po sběrnici 1-Wire a následné distribuce naměřené hodnoty modulu rs485s. V prvním kroku je čidlu přerušeno napájení na dobu Down Time, aby došlo k vybití interního napájecího kondenzátoru (Reset). Následně je provedena kontrola připojení čidla (Presence). Poté je poslán příkaz k započetí měření teploty (Send Cmd). Po uplynutí měřící doby (Mes. Wait) se z čidla přečte naměřená teplota (Read Data). Následně je generována prodleva mezi měřením (Mes. Pause). V případě jakékoliv chyby začíná celý algoritmus od začátku (Reset). Zdrojový kód modulu ds18b20 obsahuje Příloha 6. 45
Obr. 30: Vývojový diagram modulu Ds18b20
46
9.4.4 Modul eprom Obr. 31 zobrazuje vývojový diagram modulu eprom. Tento modul má na starosti ukládání a načítání dat s nastavením měřicí jednotky a vykonávání příkazů přijatých modulem rs485s. V prvním kroku se zkontroluje, zda nebyl modulem rs485s přijat nějaký příkaz (Ready). V kladném případě je spuštěno vykonání příkazu. V případě příkazu čtení či zápis eeprom se přechází do kroku Load nebo Update. Z důvodu časové náročnosti je přístup k eeprom realizován po jednotlivých bajtech. Zdrojový kód modulu eprom obsahuje Příloha 7.
Obr. 31: Vývojový diagram modulu eprom
47
9.4.5 Modul wd_flo Tento modul slouží k obsluze hardwarového watchdogu. Kontroluje průchody programu jednotlivými moduly a v případě překročení nastavené doby zakáže vykonávání instrukce wdr a tím způsobí reset procesoru. Zdrojový kód modulu wd_flo obsahuje Příloha 8.
9.4.6 Modul led Modul led obstarává obsluhu svítivých diod, které slouží k signalizaci stavů měřicí jednotky. Základem modulu je pole, které je sdíleno s ostatními moduly. Při každém zavolání modulu je každý z prvků pole dekrementován o jedničku. Pokud je hodnota prvku nenulová, příslušná dioda svítí. V okamžiku dosažení nuly dojde ke zhasnutí této diody. Velikost zapsaného čísla do pole tedy odpovídá délce svitu diody. Zdrojový kód modulu led obsahuje Příloha 9.
9.4.7 Pomocné soubory Pomocné soubory obsahují makra pro ovládání portů a definici základních datových typů. Zdrojové kódy pomocných souborů obsahuje Příloha 10.
9.5 Hardwarová konfigurace Před nahráním programu do procesoru měřicí jednotky je třeba nejprve nahrát hardwarovou konfiguraci procesoru. Pojistky je nutno naprogramovat dle následující Tab. 13. Pozor na nastavení bitů RSTDISBL a SPIEN. V případě špatného nastavení bychom se již nemohli pomocí SPI programátoru k procesoru připojit! Význam jednotlivých bitů popisuje pramen [20]. Tab. 13: Hardwarová konfigurace procesoru ATmega8
RSTDISBL WDTON SPIE CKOPT EESAVE BOTSZ1
No Yes Yes Yes Yes Yes
BOTSZ0 BOOTRST BODLEVEL BODEN STU1 STU0
Yes No Yes Yes Yes No
CKSEL3 CKSEL2 CKSEL1 CKSEL0
No No No No
48
10 Mikroprocesorový server Mikroprocesorový server slouží k cyklickému načítání dat z měřicích jednotek a následné distribuci těchto dat nadřazenému počítačovému serveru. MCU server musí tedy obsahovat dvě rozhraní sběrnice RS-485. Protože jsou měřicí jednotky napájeny z tohoto serveru, není důvod tuto sběrnici galvanicky oddělovat. Jelikož ale budou MCU servery napájeny z lokálních zdrojů, mohly by se mezi jejich zeměmi vyskytnout rozdílné potenciály. Proto bude sběrnice směrem k nadřazenému serveru galvanicky oddělená. K programování MCU serveru použijeme programovací a ladící rozhraní JTAG, které je přímo podporováno ve vývojovém prostředí Avr Studio 4.
10.1 Výběr součástek Pro účely MCU serveru byl vybrán mikroprocesor ATmega162. Procesor ATmega162 je modifikací procesoru ATmega16 a je rozšířen o jeden USART. Procesor se vyrábí v několika variantách pouzder. Z důvodu snadného návrhu a výroby plošného spoje zvolíme pouzdro PDIP40. Díky rozměrům tohoto pouzdra bude možné některé SMD součástky umístit přímo pod procesor, což zjednoduší celý návrh. Další důležitou součástí MCU serveru je stabilizátor napětí. Předpokládaný odběr zařízení by měl činit něco okolo 90 mA. Při rozdílovém napětí 7 V způsobí tento proud výkonovou ztrátu na stabilizátoru přibližně 630 mW. Tato ztráta je pro lineární stabilizátor stále ještě přijatelná. Zvolíme tedy klasický lineární stabilizátor L7805 [23] od firmy STMicroelectronics v pouzdře TO-220. Tepelný odpor RthJA tohoto pouzdra činí přibližně 50 °C/W. Celkový přírůstek teploty by činil 31,5°C. Vezmeme-li v úvahu vyšší teplotu okolí, ve které se může MCU server nacházet, bude vhodné stabilizátor opatřit malým chladičem. Pro tyto účely bude postačovat chladič DO1A prodávaný například v GM Electronic. Tento chladič sníží tepelný odpor na 21°C/W, což i při extrémní teplotě okolí například 60°C způsobí oteplení přibližně na 73°C. Tato teplota je stále ještě přijatelná. Zbytek zapojení vychází ze stejných součástek jako převodník z USB na Rs-485 a měřicí jednotka.
49
10.2 Popis zapojení Na Obr. 32 je znázorněno schéma MCU serveru. Ke svorkovnici X3 se přivádí napájecí napětí 12 VDC pro MCU server a připojené měřicí jednotky. Svorkovnice X4 slouží k připojení měřicích jednotek. Obsahuje svorky pro připojení napájení, komunikačních vodičů a stínění. Svorkovnice X1 je určena pro připojení komunikačních vodičů z nadřazeného serveru. Mimo svorek určených pro připojení komunikačních vodičů a stínění obsahuje svorky GND, které jsou určeny pro lokální přizemnění. Celý MCU server se skládá ze čtyř základních částí. První část tvoří obvody pro stabilizaci a filtraci napájecího napětí. Dioda D3 chrání obvod před případným prohozením polarity napájecího zdroje. Dále následuje filtrace pomocí kondenzátorů C12, C14, C26 a C21. Tyto kondenzátory zajišťují nejen filtraci případného rušení z napájecího zdroje, ale i vykrývají možné krátkodobé poklesy napájení. Druhá část je tvořena procesorem IO3 ATmega162. Napájecí větev procesoru je blokována kondenzátory C18, C19 a C20. K procesoru je připojen krystal s frekvencí 16 MHz. Proudový odběr procesoru v aktivním módu činí přibližně 28 mA při taktovacím kmitočtu 16 MHz. Idle mód redukuje odběr při zachování kmitočtu taktování na pouhých 14 mA [19]. Dip přepínače SW2 slouží k nastavení adresy MCU serveru. Přepínače SW3 nastavují komunikační rychlost. Svítivá dioda RUN slouží k indikaci chodu programu. Svítivé diody ERR, TX a RX indikují příslušné stavy komunikačních sběrnic. Zbylé dvě části jsou tvořeny z jednoho galvanicky odděleného a druhého galvanicky neodděleného budiče sběrnice RS-485. Obě tyto varianty budičů jsou popsány v předchozích kapitolách. Za zmínku stojí pouze drobná změna ve svorkovnici X4, kterou se mimo jiné přivádí napájecí napětí pro měřicí jednotky. Toto napájecí napětí je jištěno pojistkou F1, aby v případě zkratu došlo k odpojení měřicích jednotek a MCU server zůstal pod napětím. Seznam všech použitých součástek obsahuje Příloha 22.
50
Obr. 32: Schéma MCU serveru
10.3 Návrh plošného spoje Na Obr. 33 je zobrazena deska plošného spoje MCU serveru. Návrh této desky vychází z měřicí jednotky a převodníku USB – RS-485.
Obr. 33 Plošný spoj MCU serveru, BOTTOM
Jedinou větší změnou je část s procesorem IO3. Při návrhu musíme opět zohlednit impedance napájecí větve a blokovací kondenzátory umístit co nejblíže pinu VCC. Integrovaný stabilizátor IC1 umístíme naležato, aby se dal i s chladičem přišroubovat k desce plošného spoje. Toto upevnění volíme především z důvodu lepší mechanické odolnosti. Obr. 34 zobrazuje osazovací plán součástek ze strany Top. Zde jsou osazeny především THT součástky. Obr. 35 zobrazuje osazovací plán ze strany Bottom. Tato strana je plně osazen SMD součástkami. Příloha 3 zobrazuje fotografii zhotoveného MCU serveru.
52
Obr. 34: Osazovací plán MCU serveru, TOP
Obr. 35: Osazovací plán MCU serveru, BOTTOM
53
10.4 Firmware Firmware MCU serveru vychází ze stejných softwarových požadavků jako měřicí jednotka a využívá její moduly. Některé moduly obsahují drobné změny oproti modulům původním. Navíc se objeví pouze modul rs485m, který bude popsán v následující kapitole. Kompletní výpis programu obsahují Příloha 11 až Příloha 16.
10.4.1 Modul rs485m Obr. 36 znázorňuje vývojový diagram modulu rs485m zjednodušený o vektory přerušení sériového kanálu. Tyto vektory využívají frontu z cyklického rotačního pole, stejně jako měřicí jednotky. Tento modul má za úkol cyklicky načítat naměřené teploty z měřicích jednotek a následně je distribuovat modulu rs485s.
Obr. 36: Vývojový diagram modulu Rs485m
54
V prvním kroku je vytvořen dotaz na čtení teploty z měřicí jednotky (Tx Init). Dále je generována prodleva mezi vysíláním (Tx Wait). Následně je spuštěno vysílání dotazu (Tx Start). Po odvysílání paketu (Tx end) se modul převede do stavu přijímání (Recieve). Pokud je přijata odpověď od měřicí jednotky, uloží se změřená teplota v mezipaměti MCU serveru. V opačném případě je generována chyba a místo naměřené teploty je uložen kód chyby. Zdrojový kód modulu rs485m obsahuje Příloha 12.
10.5 Hardwarová konfigurace Po připojení programovacího a ladícího rozhraní JTAG ICE nejprve nastavíme hardwarovou konfiguraci procesoru ATmega162. Pojistky je nutno naprogramovat dle následující Tab. 14. Při nastavování jednotlivých bitů musíme dbát zvýšené opatrnosti, abychom si omylem nezakázali programovací rozhraní JTAG. Následně bychom se již k procesoru nemohli pomocí JTAG ICE připojit! Význam jednotlivých bitů popisuje pramen [19]. Tab. 14: Hardwarová konfigurace procesoru ATmega162 res res res res
M161C BOD2LEVEL BOD1LEVEL BOD0LEVEL res
No No No No No Yes No No No
OCDEN JTAGEN SPIEN WDTON EESAVE BOOTSZ1 BOOTSZ2 BOOTSZ0 BOOTRST
Yes Yes Yes Yes Yes Yes Yes Yes No
CKDIV8 CKOUT STU1 STU0 CKSEL3 CKSEL2 CKSEL1 CKSEL0 res
No Yes Yes No No No No No No
55
11 Realizace a testování Varování: Zařízení obsahuje živé části přístupné přímému dotyku a slouží výhradně pro účely testování v laboratorních podmínkách. Smí jej provozovat pouze osoba znalá s vyšší kvalifikací ve smyslu vyhlášky 50/78sb, která si je tímto vědoma veškerých následků plynoucích z testování nehomologovaných elektrických zařízení a bere tak na sebe zodpovědnost za možné škody takto vzniklé. Na Obr. 37 je zobrazeno blokové schéma zhotoveného měřícího zařízení a na Obr. 38 je jeho fotografie. Zařízení obsahuje dva MCU servery a celkem osm měřicích jednotek. Jedna z měřicích jednotek je připojena přímo k nadřazené sběrnici a tedy je možné tuto jednotku i oba MCU servery ovládat pomocí USB. K tomuto účelu slouží program TempMonitor. Tento program byl napsán jako provizorní řešení, za účelem testování měřicího systému a není primárně určen ke sběru dat. Slouží pouze k demonstrativním účelům. Stručný popis programu s přiloženými screenshoty obsahuje Příloha 17. Zdrojové kódy obsahuje Příloha 19.
Obr. 37: Blokové schéma zhotoveného měřicího zařízení
56
Obr. 38: Fotografie zhotoveného měřicího zařízení
11.1 Proudový odběr K měření proudového odběru byl použit digitální multimetr Metex M3860D. Průměrný proudový odběr aktivní měřicí jednotky osazené budičem sběrnice St485 činí přibližně 13 mA. V případě osazení měřicí jednotky obvodem SN75176 se proudový odběr zvýší přibližně na 28 mA. Dosazením proudového odběru, napájecího napětí MCU serveru a minimálního napájecího napětí měřicí jednotky do vzorce (2) a (4) získáme maximální odpor napájecí větve:
Z toho vypočítaná délka zmíněného kabelu LiYCY 2x2x0,75 činí při proudovém odběru měřicí jednotky 13 mA asi 620 m (podrobněji viz kapitola 3.1). Vzhledem k okolnostem a rezervě v minimálním napájecím napětí měřicí jednotky by se dala odhadnout maximální délka kabelu na 400 m. Tento odhad je třeba ještě v praxi ověřit.
57
Průměrný proudový odběr MCU serveru osazeného budiči ST485 v aktivním stavu činí přibližně 80 mA. V případě osazení budiči SN75176 proudový odběr vzroste na 115 mA. Naměřený odběr MCU serveru odpovídá předpokládané hodnotě. Ztrátový výkon lineárního stabilizátoru je tedy i v tom horším případě stále ještě přijatelný.
58
11.2 Naměřené průběhy K měření průběhů byl použit bateriový digitální osciloskop Agilent Technologies U1620A, zapůjčený firmou Tescan, a. s.
11.2.1 Převodník USB – RS-485, sběrnice RS-485 Na Obr. 39 je záznam začátku vysílání převodníku USB – RS-485. Komunikační rychlost převodníku je 9600 b/s. Kanál jedna zobrazuje komunikační vodič „A“ a kanál dvě komunikační vodič „B“. V prvním dílku vlevo je možné odečíst napěťovou hodnotu klidového stavu linky, která je definována zakončovacími rezistory. Rozdíl těchto hodnot lze názorněji odečíst z Obr. 40. Klidový stav diferenciálního páru je 2 V. Přibližně toto napětí by musel překonat naindukovaný zdroj rušení, aby došlo ke spuštění asynchronního sériového kanálu v některém z připojených zařízení. Tato hodnota je pro účely měřícího systému postačující.
Obr. 39: Průběh začátku vysílání na sběrnici RS-485, budič SN75176, (Ch1=A,Ch2=B)
59
Obr. 40: Průběh začátku vysílání na sběrnici RS-485, budič SN75176, (Ch1=A-B)
Na Obr. 40 je zobrazen průběh napětí diferenciálního páru při začátku vysílání. Mezi čtvrtým a šestým dílkem na časové ose vidíme, jakou dobu od zapnutí výstupního budiče (čtvrtý dílek) dojde k odvysílání start bitu prvního bajtu (sedmý až osmý dílek časové osy). Z obrázku je také patrné, že celý signál je posunut do kladného směru přibližně o 1,2 V. Toto posunutí je způsobeno zakončovacími rezistory. V horní polovině Obr. 41 můžeme vidět průběh napětí při odvysílání celého paketu. Dolní polovina obrázku zobrazuje zvětšenou oblast náběžné hrany diferenciálního páru. Délka přechodu činí o něco málo více než 60 ns. Obr. 42 zobrazuje hranu sestupnou. Délka přechodu je obdobná jako u hrany náběžné. Vzhledem k těmto dobám lze konstatovat, že daný převodník by byl schopen pracovat na rychlostech v řádu jednotek megabajtů za sekundu. Pro účely měřicího systému však postačují přenosové rychlosti do 38,4 kb/s.
60
Obr. 41: Průběh náběžné hrany na sběrnici RS-485, budič SN75176, ( Ch1=A-B )
Obr. 42: Průběh sestupné hrany na sběrnici RS-485, budič SN75176, ( Ch1=A-B )
61
11.2.2 MCU serveru a Měřicí jednotky, sběrnice RS-485 Horní polovina Obr. 43 zobrazuje začátek vysílání paketu MCU serveru. Klidový stav linky je posunut zakončovacími rezistory obdobně jako u převodníku USB – RS-485. Délka přeběhu je však nyní o něco větší. Přibližně odečteno činí 180 ns. Obr. 44 zobrazuje sestupnou hranu téhož signálu. Délka přeběhu je téměř stejná jako u náběžné hrany. Nárůst této délky je způsobeno budičem ST485, který je konstruován na nižší přenosové rychlosti. V úvahu připadají přenosové rychlosti v řádu do jednoho megabajtu za sekundu. Tato přenosová rychlost převyšuje požadavky měřicího systému.
Obr. 43: Průběh náběžné hrany na sběrnici RS-485, budič ST485, ( Ch1=A-B )
62
Obr. 44: Průběh sestupné hrany na sběrnici RS-485, budič ST485, ( Ch1=A-B )
63
11.2.3 Měřicí jednotka, 1-Wire Na Obr. 45 je zobrazen průběh měřicího cyklu teplotního čidla DS18B20. Kanál jedna zobrazuje průběh napětí na napájecím vodiči čidla. Kanál dvě zobrazuje průběh napětí na datovém vodiči. Z obrázku je patrné, že měřicí cyklus trvá přibližně 950 ms. I v následujících obrázcích, ve kterých se používají oba kanály, budou kanály připojeny stejným způsobem.
Obr. 45: Průběh měřicí sekvence čidla DS18B20, ( Ch1=napájení, Ch2=data )
Na Obr. 46 je zobrazen začátek měřicího cyklu teplotního čidla. V druhém a třetím dílku časové osy udržuje mikroprocesor zapnuté napájecí napětí na obou vodičích, aby se nabil napájecí kondenzátor čidla. Čtvrtý a pátý dílek časové osy zobrazuje reset sběrnice a presence puls, za nímž následuje příkaz „Skip Rom“ a „Convert T“ [22]. Poté čidlo začne měřit teplotu.
64
Obr. 46: Průběh příkazu „Convert T“ pro čidlo DS18B20, ( Ch1=napájení, Ch2=data)
Na Obr. 47 je znázorněn průběh zápisu log. „0“ mikroprocesorem na sběrnici. Na obrázku je vidět patrný rozdíl mezi sestupnou a náběžnou hranou datového vodiče sběrnice. Sestupná hrana je způsobena sepnutím výstupního tranzistoru portu mikroprocesoru a náběžná hrana nabíjením kapacit portu, ochranných diod a kabeláže. Jelikož je čidlo připojeno přibližně čtyřmi metry kabelu LiYCY 3x075, který má celkovou kapacitu 640 pF, má kabeláž na náběžnou hranu klíčový vliv. Za povšimnutí také stojí, že napětí ve stavu log. „0“ je přibližně 0,3 V. To je způsobeno výstupním ochranným rezistorem. Katalogový list udává, že maximální přípustné napětí pro stav log. „0“ je 0,8 V [22]. Napětí v log „0“ je tedy v toleranci. Obr. 48 znázorňuje průběh zápisu log. „1“ mikroprocesorem na sběrnici.
65
Obr. 47: Průběh zápisu log. „0“ mikroprocesorem na sběrnici, ( Ch1=data )
Obr. 48: Průběh zápisu log. „1“ mikroprocesorem na sběrnici, ( Ch1=data )
66
Obr. 49 zobrazuje konec měřícího cyklu čidla a následné přečtení naměřené teploty. Ve druhém dílku časové osy je na datovém vodiči vidět patrný pokles napětí přibližně o 0,5 V. To je způsobeno přechodem sběrnice z napájecího režimu do režimu přenosu dat. Ve třetím dílku časové osy probíhá reset a presence puls čidla. Následuje příkaz „Read Scratchpad“ [22] a následné načítání změřené teploty.
Obr. 49: Průběh čtení změřené teploty z čidla DS18B20, ( Ch1=napájení, Ch2=data)
Obr. 50 zobrazuje detail resetu a následného presence pulsu. Mikroprocesor stáhne sběrnici na 480 µs do stavu log. „0“ (druhý až pátý dílek časové osy), následně sběrnici uvolní a čeká na presence puls od teplotního čidla (sedmý dílek časové osy).
67
Obr. 50: Průběh čtení presence pulsu čidla DS18B20, ( Ch1=data )
Na Obr. 51 a Obr. 52 jsou znázorněny průběhy zápisu log. „0“ a log. „1“ teplotním čidlem na sběrnici. Za povšimnutí stojí, že při zápisu log. „0“ je doba stažení sběrnice čidlem k zemi necelých 30 µs, i když celkový timeslot je přinejmenším 60 µs. Tyto relativně krátké časy jsou generovány čidlem z důvodu malé kapacity napájecího kondenzátoru. Čidlo tedy uvolní sběrnici co nejdříve, aby v případě dvouvodičového připojení došlo k co možná nejkratšímu výpadku napájení a vnitřní kondenzátor se nestačil vybít pod kritickou mez.
68
Obr. 51: Průběh zápisu log. „0“ čidlem DS18B20 na sběrnici, ( Ch1=data )
Obr. 52: Průběh zápisu log. „1“ čidlem DS18B20 na sběrnici, ( Ch1=data )
69
11.3 Sběr dat Za účelem testovacího sběru dat byl vybrán záznam pracovního cyklu lednice kombinované s mrazicím boxem. Tento záznam byl pořízen pomocí programu TempMonitor a je zobrazen na Obr. 53. Jednotlivé vzorky byly zaznamenávány každých 10 s. Za povšimnutí stojí náhlý nárůst teploty mrazicího boxu přibližně v 19:35. Tento nárůst není způsoben chybným měřením, nýbrž krátkodobým otevřením mrazicího boxu. Další zajímavostí je průběh teploty chladiče mezi 21:00 a 21:40, kde dochází k postupnému poklesu a opětovnému nárůstu teploty. Tento jev by mohl být způsoben krátkodobým vypnutím a následným zapnutím kompresoru. Poslední zajímavostí je záporný rozdíl teploty chladiče a okolí v době mezi 22:10 a 23:05. Zde se opět nejedná o chybu měření. Záporný rozdíl teploty způsobilo ohřátí čidla od nedalekého radiátoru. Naměřená data, která reprezentují průběh pracovního cyklu lednice, by se tedy měla blížit reálným hodnotám.
70
Pracovní cyklus lednice kombinované s mrazicím boxem 40
30
Teplota [°C]
20
10
Chladič Okolí
0
Lednice Mrazicí box
krátké otevření boxu
-10
-20
-30 19:00:00
19:30:00
20:00:00
20:30:00
21:00:00
21:30:00
22:00:00
22:30:00
Čas [h:m:s]
Obr. 53: Průběh zaznamenaných teplot pracovního cyklu lednice
23:00:00
12 Závěr Cílem této práce bylo navrhnout a zkonstruovat konkrétní řešení USB – RS-485 převodníku, měřicí jednotky a vhodného komunikačního protokolu pro vícebodové měření teploty. Převodník USB – RS-485 byl navrhnut za pomocí integrovaného obvodu FT232. Při návrhu byla zohledněna různá úskalí, jako například výskyt přepěťových špiček či zemních potenciálů. Převodník byl také zkonstruován a odzkoušen. Jednotlivá měření ukázala, že převodník svými parametry převyšuje požadavky měřicího systému, a to zejména svojí datovou propustností. Měřicí jednotka byla navržena s ohledem na nízkou spotřebu, která je klíčová v systémech napájených po sběrnici. Zkonstruovaná jednotka vykazovala dokonce o 35 % menší spotřebu, než bylo původně odhadováno. Skutečný proud odebíraný jednotkou činí 13 mA. Tato skutečnost předčila veškerá očekávání a umožnila tak při zachování průřezů napájecích vodičů prodloužit celkovou délku mezi měřicími jednotkami. V této práci byla také zmíněna možnost použití MCU serverů, které by rozšiřovaly sběrnici RS-485 o další podsítě. Tento server byl také navržen a zkonstruován. Pro komunikaci měřicích jednotek se serverem byl navržen protokol kódovaný binárně. Tento protokol umožňuje adresovat nejen měřicí jednotky jako celek, ale i vnitřní paměťové bloky či jiné logické segmenty uvnitř jednotek. Tato skutečnost nám umožňuje například vytvářet podsítě, které se budou tvářit jako jedna jediná jednotka anebo adresovat bajty s nastavením jednotky. Navrhovaný komunikační protokol se v této podobě osvědčil a je pro účely měřicího systému plně dostačující. Pro účely komplexního testování bylo vytvořeno měřicí zařízení, které obsahuje osm měřicích jednotek, dva MCU servery a jeden převodník USB – RS-485. Dále byl také napsán program TempMonitor, který slouží k testovacím účelům, jako například nastavování jednotek a cyklické načítání naměřených dat do počítače. Pomocí tohoto zařízení byl zmonitorován pracovní cyklus lednice kombinované s mrazicím boxem. Výsledná data potvrdila komplexní funkčnost tohoto měřicího systému.
72
Možné vylepšení měřicího systému shledávám v miniaturizaci měřicích jednotek za pomoci profesionálně vyráběných vícevrstvých desek plošných spojů a oboustranné SMD montáži.
73
13 Citovaná literatura 1. MATOUŠEK, D. USB prakticky s obvody FTDI - 1. díl. Praha: BEN, 2003. ISBN 80-7300-103-9. 2. KAINKA, B. Měření, řízení a regulace pomocí sběrnice USB. Praha: BEN, 2003. ISBN 80-7300-073-3. 3. FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED. FT232R USB UART IC Datasheet [pdf]. Glasgow: 2010. 4. ATMEL CORPORATION. AVR318: Dallas 1-Wire® master [pdf]. San Jose: 2004. 5. HRBÁČEK, J. Komunikace mikrokontroléru s okolím 2. Praha: BEN, 2002. ISBN 80-86056-73-2. 6. MATOUŠEK, D. Práce s mikrokontroléry ATMEL AVR - ATmega16. Praha: BEN, 2006. ISBN 80-7300-174-8. 7. ZEZULKA, F., P. FIEDLER a Z. BRADÁČ. Prostředky průmyslové automatizace. Brno: VUTIUM, 2004. ISBN 80-214-2610-1. 8. HERMAN, I. Komunikační technologie [pdf]. Brno. 9. FILKA, M. Přenosová média. Brno: VUTIUM, 2002 10. VLACH, J. a V. VLACHOVÁ. Počítačová rozhraní. Praha (Czech Republic): BEN, 2000. ISBN 80-7300-010-5. 11. TEXAS INSTRUMENTS. SN75176A Differential Bus Transceiver [pdf]. Dallas: 1995. 12. KABLETRONIC. kabletronic.de: Steuerleitungen, paarig verseilt (twisted pairs), 2-LiYCY TP, geschirmt [online]. 20. 12. 2012 [cit. 2013-1-4]. Dostupné z: http://www.kabeltronik.de/elektronik-industrie/steuerleitungen-paarig-verseilt%28twisted-pairs%29/2-liycy-tp-steuerleitungen-geschirmt-paarig-verseilt-twistedpairs/
74
13. AIMTEC. Series AM1S-Z, 1 Watt DC-DC Converter [pdf]. Montreal. 14. AGILENT TECHNOLOGIES. HCPL-2601, HCPL-2611, HCPL-2630, HCPL2631, HCPL-4661 High CMR, High Speed TTL Compatible Optocouplers [pdf]. 2004. 15. WINDER, S. a M. TOOLEY. Newnes Data Communications Pocket Book. 4th edition. Oxford (Great Britain): Newns, 2002. ISBN 0-7506-52977. 16. HLAVIČKA, J., et al. Číslicové systémy odolné proti poruchám. Praha (Czech Republic): ČVUT, 1992. ISBN 80-01-00852-5. 17. NĚMEC, K. Datová Komunikace. Brno: VUTIUM, 2000. ISBN 80-214-1652-1. 18. HOROWITZ, P. a W. HILL. The Art Of Electronics. 2th edition. Cambridge (Great Britain): Cambridge University Press, 2008. ISBN 978-0-521-37095-0. 19. ATMEL CORPORATION. ATmega162 - datasheet [pdf]. San Jose: 2009. 20. ATMEL CORPORATION. ATmega8 - datasheet [pdf]. San Jose: 2011. 21. MANN, B. C pro mikrokontroléry. Praha: BEN, 2003. ISBN 80-7300-077-6. 22. MAXIM INTEGRATED. DS18B20 1-Wire Digital Thermometer [pdf]. San Jose: 2008. 23. STMICROELECTRONICS. L78xx - Positive voltage regulator ICs [pdf]. 2012. 24. TEXAS INSTRUMENTS. DS485 Low Power RS-485/RS-422 Multipoint Transceiver [pdf]. Dallas: 2004. 25. STMICROELECTRONICS. ST485 Low power RS-485/RS-422 transceiver [pdf]. 2009. 26. KABLETRONIC. Kabletronic: Steuerleitungen LiYCY, PVC, geschirmt [online]. 20. 12. 2012 [cit. 2013-1-4]. Dostupné z: http://www.kabeltronik.de/elektronikindustrie/steuerleitungen/liycy-steuerleitungen-geschirmt/
75
14 Seznam tabulek TAB. 1: PŘÍKLAD KÓDOVÁNÍ ASCII PROTOKOLU .................................................................................... 25 TAB. 2: JEDNODUCHÝ ASCII PROTOKOL .............................................................................................. 26 TAB. 3: JEDNODUCHÝ ASCII PROTOKOL S HEXADECIMÁLNÍM ŘETĚZCEM ................................................... 26 TAB. 4: PAKETY PRO JEDNODUCHOU MASTER – SLAVE KOMUNIKACI ......................................................... 29 TAB. 5: NAVRHOVANÝ PAKET............................................................................................................. 30 TAB. 6: PŘEHLED PŘÍKAZŮ ................................................................................................................. 30 TAB. 7: ADRESACE BLOKŮ ................................................................................................................. 30 TAB. 8: PŘEHLED ZÁKLADNÍCH PAKETŮ NAVRHOVANÉHO PROTOKOLU ...................................................... 31 TAB. 9: ROZLOŽENÍ PAMĚTI S NASTAVENÍM.......................................................................................... 32 TAB. 10: PŘEHLED PŘÍKAZŮ JEDNOTKY ................................................................................................ 32 TAB. 11: CHYBOVÉ KÓDY JEDNOTEK.................................................................................................... 32 TAB. 12: KOMUNIKAČNÍ RYCHLOSTI .................................................................................................... 32 TAB. 13: HARDWAROVÁ KONFIGURACE PROCESORU ATMEGA8 .............................................................. 48 TAB. 14: HARDWAROVÁ KONFIGURACE PROCESORU ATMEGA162 .......................................................... 55
76
15 Seznam obrázků OBR. 1: BLOKOVÉ SCHÉMA PŘEVODNÍKU USB – RS-485 ........................................................................ 9 OBR. 2: BLOKOVÉ SCHÉMA MĚŘICÍ JEDNOTKY ....................................................................................... 10 OBR. 3: ZÁKLADNÍ TOPOLOGIE SBĚRNICE ............................................................................................. 11 OBR. 4: BLOKOVÉ SCHÉMA SERVERU................................................................................................... 12 OBR. 5: ROZŠÍŘENÁ TOPOLOGIE SBĚRNICE ........................................................................................... 13 OBR. 6: SCHÉMA PROPOJENÍ PŘIJÍMAČE A VYSÍLAČE DIFERENCIÁLNÍM PÁREM ............................................ 14 OBR. 7: DIAGRAM PŘÍPUSTNÝCH NAPĚTÍ VODIČŮ SBĚRNICE RS-485 ........................................................ 14 OBR. 8: SCHÉMA ZAKONČENÍ SBĚRNICE REZISTOREM ............................................................................. 15 OBR. 9: SCHÉMA ZAKONČENÍ SBĚRNICE REZISTORY ................................................................................ 15 OBR. 10: SCHÉMA ÚBYTKŮ NAPĚTÍ NA NAPÁJECÍ VĚTVI MĚŘICÍCH JEDNOTEK .............................................. 15 OBR. 11: TYPICKÉ ZAPOJENÍ OBVODU FT232R [3] ............................................................................... 18 OBR. 12: SCHÉMA PŘEVODNÍKU USB – RS-485 .................................................................................. 21 OBR. 13: PLOŠNÝ SPOJ PŘEVODNÍKU USB – RS-485, BOTTOM ............................................................ 23 OBR. 14: OSAZOVACÍ PLÁN PŘEVODNÍKU USB – RS-485, TOP .............................................................. 23 OBR. 15: OSAZOVACÍ PLÁN PŘEVODNÍKU USB – RS-485, BOTTOM ...................................................... 24 OBR. 16: NEVHODNÁ SYNCHRONIZAČNÍ SEKVENCE................................................................................ 28 OBR. 17: ZVOLENÁ SYNCHRONIZAČNÍ SEKVENCE MASTER A SLAVE ........................................................... 28 OBR. 18: TYPICKÉ PROPOJENÍ JEDNOHO ÚČASTNÍKA SBĚRNICE 1-WIRE S MIKROPROCESOREM [22]............... 34 OBR. 19: INICIALIZACE SBĚRNICE 1-WIRE [22] ..................................................................................... 34 OBR. 20: ČASOVÝ DIAGRAM SBĚRNICE 1-WIRE [22] ............................................................................. 35 OBR. 21: BLOKOVÉ SCHÉMA TEPLOTNÍHO ČIDLA DS18B20 [22] ............................................................. 36 OBR. 22: SIMULAČNÍ SCHÉMA SBĚRNICE 1-WIRE .................................................................................. 37 OBR. 23: ČASOVÝ DIAGRAM PŘECHODOVÝCH DĚJŮ NA SBĚRNICI 1-WIRE .................................................. 37 OBR. 24: SCHÉMA MĚŘICÍ JEDNOTKY .................................................................................................. 40 OBR. 25: PLOŠNÝ SPOJ MĚŘICÍ JEDNOTKY, BOTTOM............................................................................ 41 OBR. 26: OSAZOVACÍ PLÁN MĚŘICÍ JEDNOTKY, TOP .............................................................................. 42 OBR. 27: OSAZOVACÍ PLÁN MĚŘICÍ JEDNOTKY, BOTTOM ...................................................................... 42 OBR. 28: VÝVOJOVÝ DIAGRAM MODULU MAIN ..................................................................................... 43 OBR. 29: VÝVOJOVÝ DIAGRAM MODULU RS485S ................................................................................. 45 OBR. 30: VÝVOJOVÝ DIAGRAM MODULU DS18B20............................................................................... 46 OBR. 31: VÝVOJOVÝ DIAGRAM MODULU EPROM .................................................................................. 47 OBR. 32: SCHÉMA MCU SERVERU ..................................................................................................... 51
77
OBR. 33 PLOŠNÝ SPOJ MCU SERVERU, BOTTOM ................................................................................ 52 OBR. 34: OSAZOVACÍ PLÁN MCU SERVERU, TOP ................................................................................. 53 OBR. 35: OSAZOVACÍ PLÁN MCU SERVERU, BOTTOM ......................................................................... 53 OBR. 36: VÝVOJOVÝ DIAGRAM MODULU RS485M ................................................................................ 54 OBR. 37: BLOKOVÉ SCHÉMA ZHOTOVENÉHO MĚŘICÍHO ZAŘÍZENÍ ............................................................. 56 OBR. 38: FOTOGRAFIE ZHOTOVENÉHO MĚŘICÍHO ZAŘÍZENÍ..................................................................... 57 OBR. 39: PRŮBĚH ZAČÁTKU VYSÍLÁNÍ NA SBĚRNICI RS-485, BUDIČ SN75176, (CH1=A,CH2=B) ................. 59 OBR. 40: PRŮBĚH ZAČÁTKU VYSÍLÁNÍ NA SBĚRNICI RS-485, BUDIČ SN75176, (CH1=A-B)......................... 60 OBR. 41: PRŮBĚH NÁBĚŽNÉ HRANY NA SBĚRNICI RS-485, BUDIČ SN75176, ( CH1=A-B ).......................... 61 OBR. 42: PRŮBĚH SESTUPNÉ HRANY NA SBĚRNICI RS-485, BUDIČ SN75176, ( CH1=A-B ) ........................ 61 OBR. 43: PRŮBĚH NÁBĚŽNÉ HRANY NA SBĚRNICI RS-485, BUDIČ ST485, ( CH1=A-B ) .............................. 62 OBR. 44: PRŮBĚH SESTUPNÉ HRANY NA SBĚRNICI RS-485, BUDIČ ST485, ( CH1=A-B ) ............................. 63 OBR. 45: PRŮBĚH MĚŘICÍ SEKVENCE ČIDLA DS18B20, ( CH1=NAPÁJENÍ, CH2=DATA ) ............................... 64 OBR. 46: PRŮBĚH PŘÍKAZU „CONVERT T“ PRO ČIDLO DS18B20, ( CH1=NAPÁJENÍ, CH2=DATA) ................. 65 OBR. 47: PRŮBĚH ZÁPISU LOG. „0“ MIKROPROCESOREM NA SBĚRNICI, ( CH1=DATA ) ................................ 66 OBR. 48: PRŮBĚH ZÁPISU LOG. „1“ MIKROPROCESOREM NA SBĚRNICI, ( CH1=DATA ) ................................ 66 OBR. 49: PRŮBĚH ČTENÍ ZMĚŘENÉ TEPLOTY Z ČIDLA DS18B20, ( CH1=NAPÁJENÍ, CH2=DATA) ................... 67 OBR. 50: PRŮBĚH ČTENÍ PRESENCE PULSU ČIDLA DS18B20, ( CH1=DATA ) .............................................. 68 OBR. 51: PRŮBĚH ZÁPISU LOG. „0“ ČIDLEM DS18B20 NA SBĚRNICI, ( CH1=DATA ) ................................... 69 OBR. 52: PRŮBĚH ZÁPISU LOG. „1“ ČIDLEM DS18B20 NA SBĚRNICI, ( CH1=DATA ) ................................... 69 OBR. 53: PRŮBĚH ZAZNAMENANÝCH TEPLOT PRACOVNÍHO CYKLU LEDNICE ............................................... 71
78
16 Seznam vzorců VZOREC (1) VÝPOČET ZEMNÍCH NAPÁJECÍCH ÚBYTKŮ………..…………………………………….…………………………16 VZOREC (2) VÝPOČET MAXIMÁLNÍHO PŘÍPUSTNÉHO ZEMNÍHO ODPORU…………………………………………………16 VZOREC (3) VÝPOČET ÚBYTKU NAPÁJENÍ……..………………………………………………………………………..………..17 VZOREC (4) VÝPOČET MAXIMÁLNÍHO PŘÍPUSTNÉHO ODPORU NAPÁJECÍ VĚTVE…………………..……………………17
79
17 Seznam příloh PŘÍLOHA 1: FOTOGRAFIE PŘEVODNÍKU USB – RS-485.......................................................................... 81 PŘÍLOHA 2: FOTOGRAFIE MĚŘICÍ JEDNOTKY .......................................................................................... 82 PŘÍLOHA 3: FOTOGRAFIE MCU SERVERU ............................................................................................. 82 PŘÍLOHA 4: ZDROJOVÝ KÓD MĚŘICÍ JEDNOTKY, MODUL MAIN ................................................................. 83 PŘÍLOHA 5: ZDROJOVÝ KÓD MĚŘICÍ JEDNOTKY, MODUL RS485S ............................................................. 87 PŘÍLOHA 6: ZDROJOVÝ KÓD MĚŘICÍ JEDNOTKY, MODUL DS18B20 ......................................................... 100 PŘÍLOHA 7 : ZDROJOVÝ KÓD MĚŘICÍ JEDNOTKY, MODUL EPROM ............................................................ 108 PŘÍLOHA 8: ZDROJOVÝ KÓD MĚŘICÍ JEDNOTKY, MODUL WD_FLO........................................................... 112 PŘÍLOHA 9: ZDROJOVÝ KÓD MĚŘICÍ JEDNOTKY, MODUL LED.................................................................. 113 PŘÍLOHA 10: ZDROJOVÝ KÓD MĚŘICÍ JEDNOTKY, POMOCNÉ SOUBORY .................................................... 114 PŘÍLOHA 11: ZDROJOVÝ KÓD MCU SERVERU, MODUL MAIN................................................................. 116 PŘÍLOHA 12: ZDROJOVÝ KÓD MCU SERVERU, MODUL RS485M ............................................................ 120 PŘÍLOHA 13: ZDROJOVÝ KÓD MCU SERVERU, MODUL RS485S ............................................................. 133 PŘÍLOHA 14: ZDROJOVÝ KÓD MCU SERVERU, MODUL EPROM .............................................................. 146 PŘÍLOHA 15: ZDROJOVÝ KÓD MCU SERVERU, MODUL WD_FLO ............................................................ 151 PŘÍLOHA 16: ZDROJOVÝ KÓD MCU SERVERU, MODUL LED ................................................................... 152 PŘÍLOHA 17: POPIS PROGRAMU TEMPMONITOR ................................................................................ 154 PŘÍLOHA 18: PŘÍKAZY A NASTAVENÍ JEDNOTEK ................................................................................... 156 PŘÍLOHA 19: ZDROJOVÝ KÓD PROGRAMU TEMPMONITOR ................................................................... 157 PŘÍLOHA 20: SEZNAM SOUČÁSTEK PŘEVODNÍKU USB – RS-485 ........................................................... 183 PŘÍLOHA 21: SEZNAM SOUČÁSTEK MĚŘICÍ JEDNOTKY ........................................................................... 184 PŘÍLOHA 22: SEZNAM SOUČÁSTEK MCU SERVERU .............................................................................. 185
80
18 Přílohy Příloha 1: Fotografie převodníku USB – RS-485
81
Příloha 2: Fotografie měřicí jednotky
Příloha 3: Fotografie MCU serveru
82
Příloha 4: Zdrojový kód Měřicí jednotky, modul main //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------//*** //*** //*** //*** Procesor : ATmega8 //*** Frekvence : 4 MHz //*** Autor : Miroslav Stehlik //*** Datum : 1.3.2013 //*** //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------#include
//názvy registrů #include //práce s přerušením #include #include #include #include #include "include/eprom/eprom.h" #include "include/sys/wd_flo.h"
83
#include "include/sys/typedefine.h" #include "include/sys/portY_def.h" #include "include/led/led.h" #include "include/rs485s/rs485s.h" #include "include/ds18b20/ds18b20.h" //Fuses WDTON | SPIEN |CKOPT | EESAVE | BOOTSZ1 | BOOTSZ0 // BODLEVEL | BODEN | STU1 //Ext. Cryst. Osc. 8.0- MHz, Start-up time 16k CK + 0ms // Brown-out 4.3V //#define DEBUGSTEP //reset po posledni zmene nastaveni x10ms #define CHANGE_RESET_DELAY 250 //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------ISR(BADISR_vect){ // user code here } //-----------------------------------------------------------------------------// Přerušení při pororovnání u čítače 2 //-----------------------------------------------------------------------------static u8vt time_1ms = 0; ISR(TIMER2_COMP_vect){ if(time_1ms < U8TOP)time_1ms++; rs485s_timer(); } //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------//hlida zmenu nastaveni void switchChangeCtr(void){ static u8t adr, bitrate; static u16t ResetDelay = 0; //Address //PD5 PD6 PD7 PB0 PB4 adr = 0; if(!(PIND&_BV(PD5)))adr|=0b1; if(!(PIND&_BV(PD6)))adr|=0b10; if(!(PIND&_BV(PD7)))adr|=0b100; if(!(PINB&_BV(PB0)))adr|=0b1000; if(!(PINB&_BV(PB4)))adr|=0b10000; //Bitrate //PD3 PD4 bitrate = 0; if(!(PIND&_BV(PD3)))bitrate|=0b1; if(!(PIND&_BV(PD4)))bitrate|=0b10;
if( adr != Rs485ss.adrSw){ Rs485ss.adrSw = adr; ResetDelay = 1; } if( bitrate != Rs485ss.bitrateSw ){ Rs485ss.bitrateSw = bitrate; ResetDelay = 1;
84
} if(ResetDelay){ led_i[0] = 255; led_i[1] = 255; ResetDelay++; } if(ResetDelay > CHANGE_RESET_DELAY){ wd_reset_cpu = 1; } } //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Hlavní program //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------int main(void){ #ifndef DEBUGSTEP wdt_enable(WDTO_120MS); wdt_reset(); #endif //-----------------------------------------------------------------------------// Nastavení čítače 2 //-----------------------------------------------------------------------------//AS2 - přepne zdroj signálu přímo na krystal //sbi(ASSR,AS2) //WGM2x - režim čítače //COM2x - generování signálu OCR //CS2x - vstupní frekvence čítače TCCR2=((0<<WGM20)|(0<
85
if(!(PIND&_BV(PD3)))Rs485ss.bitrateSw|=0b1; if(!(PIND&_BV(PD4)))Rs485ss.bitrateSw|=0b10; eprom_toModule(); #ifndef DEBUGSTEP for(int i=0; i < Rs485ss.address + 4; i++){ LED0(1); LED1(1); for(int ii=0; ii <50 ;ii++){ wdt_reset(); _delay_ms(1); } LED0(0); LED1(0); for(int ii=0; ii < 50;ii++){ wdt_reset(); _delay_ms(1); } } #endif //-----------------------------------------------------------------------------ds18b20_init(); rs485s_init(); sei(); //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Hlavní smyčka programu //-----------------------------------------------------------------------------u8t time_10ms = 0; while(1){ while(time_1ms > 0){ time_10ms++; rs485s();
ds18b20_1ms(&Ds18b20[0]); ds18b20(&Ds18b20[0]); eprom_1ms(); wd_flo(); if(time_10ms == 10){ led_10ms(); switchChangeCtr(); time_10ms = 0; } time_1ms--; } //Spanek set_sleep_mode(SLEEP_MODE_IDLE); ATOMIC_BLOCK(ATOMIC_FORCEON){ sleep_enable(); } sleep_cpu(); sleep_disable(); } }
86
Příloha 5: Zdrojový kód Měřicí jednotky, modul Rs485s //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Rs485s.h //-----------------------------------------------------------------------------#ifndef RS485S #define RS485S #include "../sys/typedefine.h" #define RS485S_READ_NO_EXIST 0x8003 #define RS485SS_OUTPORT DDRC |= (1<
//prijem dat x10ms #define LEDS_RX led_i[0] = 5 //posilani dat x10ms #define LEDS_TX led_i[1] = 10 //Errf x10ms #define LEDS_ERR ; //led_i[2] = 15 //timeout pro 1200b/s #define RS485SS_BASIC_TIMEOUT 200
#define RS485S_SIZE_MAX 8 //staticka deklarace velikosti pole //2-1,4-1,8-1,16-1,32-1,64-1,128-1 #define RS485S_CFIFO_MASK (32-1) #define RS485S_SYNC0 0xff #define RS485S_SYNC1 0x0a #define RS485S_SYNC2_MA 0x55 #define RS485S_SYNC2_Sl 0x53 #define RS485S_SYNC3 0x71 #define RS485S_CMD_READ 0x81 #define RS485S_CMD_WRITE 0x82 #define RS485S_CMD_RESPONSE 0x84 #define RS485S_CMD_ACK 0x88 //-----------------------------------------------------------------------------// Polozka typedef struct{ union{ struct{
87
u8t LO; u8t HI; }; u16t U16; }; }Trs485sValue; //-----------------------------------------------------------------------------typedef struct{ u8t Sync0; u8t Sync1; u8t Sync2; u8t Adr; u8t Cmd; //u8t Sync3; u8t Blok; u8t Elem; u8t Siz; u8t Crc; union{ Trs485sValue Value[RS485S_SIZE_MAX]; u8t ValueU8[RS485S_SIZE_MAX*2]; }; }Trs485sPaket; //-----------------------------------------------------------------------------typedef enum{ SBITRATE_1200 = 0, SBITRATE_2400, SBITRATE_4800, SBITRATE_9600, SBITRATE_19200, SBITRATE_38400, SBITRATE_57600, SBITRATE_115200 }TBitrate; //-----------------------------------------------------------------------------typedef enum{ //find start byte RS485S_START_B, //switch header RS485S_HSW, //Qest - Read RS485S_HQ, //Writte RS485S_HW, //Respons RS485S_HR, //ACK RS485S_HA,
}Trs485sRxSw; //-----------------------------------------------------------------------------typedef enum { //odvysilej bufer RS485S_RETTX, //pokracuj v prijmu RS485S_RETNOP
88
}Trs485sRxStatus; //-----------------------------------------------------------------------------//RX-TX typedef enum{ //prijimej RS485S_RXTX_RECV, //prijmuto RS485S_RXTX_RECV_COMPLET, //prodleva RS485S_RXTX_TX_WAIT, //priprav vysilani RS485S_RXTX_TX_START, //vysilej RS485S_TX_TX } Trs485sStatus; //-----------------------------------------------------------------------------typedef struct{ u8vt r_i; u8vt w_i; u8vt used; u8vt ovf; u8vt buf[RS485S_CFIFO_MASK+1]; }Trs485sCFifo; //-----------------------------------------------------------------------------typedef volatile struct { u8t address; //adresa jednotky u8t adrSw; //adresa na prepinacich TBitrate bitrate; //bitrate TBitrate bitrateSw; //bitrate na prepinacich u16t tx_pauza; //hodnota casovace u16t rx_timeout; //hodnota casovace u16vt timer_1ms; //caasovac po 1ms u8t rx_i_while; //povoluje zpracovani buferu podle zaplneni Trs485sPaket Paket; Trs485sRxSw RxSw; Trs485sRxStatus RxStatus;//rx_enum Trs485sStatus Status; volatile Trs485sCFifo CFifo; }TRs485ss; extern TRs485ss Rs485ss; //-----------------------------------------------------------------------------void rs485s_init(void); //-----------------------------------------------------------------------------void rs485s(void); //-----------------------------------------------------------------------------void rs485s_timer(void); //-----------------------------------------------------------------------------#endif
//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Rs485s.c //-----------------------------------------------------------------------------#include //názvy registrů #include //práce s přerušením #include
89
#include "../ds18b20/ds18b20.h" #include "../eprom/eprom.h" #include "../sys/wd_flo.h" #include "../led/led.h" #include "rs485s.h" //-----------------------------------------------------------------------------TRs485ss Rs485ss = { 0, 0, SBITRATE_9600, SBITRATE_9600, 5, 100, 0, 0, {}, RS485S_START_B, RS485S_RETTX, RS485S_RXTX_RECV, {}, }; //------------------------------------------------------------------------------
//-----------------------------------------------------------------------------static void rs485s_rx(void); //-----------------------------------------------------------------------------static void rs485s_rx_r(void); //-----------------------------------------------------------------------------//dotaz prijmut static void rs485s_rx_q(void); //-----------------------------------------------------------------------------//Prijmut Ack static void rs485s_rx_a(void); //-----------------------------------------------------------------------------//zapis prijmut static void rs485s_rx_w(void); //-----------------------------------------------------------------------------//CFIFO //-----------------------------------------------------------------------------static inline void cfifo_write(u8t data); //-----------------------------------------------------------------------------static inline u8t cfifo_read(void); //-----------------------------------------------------------------------------static inline u8t cfifo_read_i(u8t i); //-----------------------------------------------------------------------------static inline void cfifo_free(void); //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------//prijmut dotaz static void rs485s_rx_q(void){ u8t temp = 0; u8t pi; Rs485ss.CFifo.buf[0] = RS485S_SYNC0; Rs485ss.CFifo.buf[1] = RS485S_SYNC1; Rs485ss.CFifo.buf[2] = RS485S_SYNC2_Sl;
90
Rs485ss.CFifo.buf[3] = Rs485ss.address; Rs485ss.CFifo.buf[4] = RS485S_CMD_RESPONSE; Rs485ss.CFifo.buf[5] = RS485S_SYNC3; Rs485ss.CFifo.buf[6] = Rs485ss.Paket.Blok; Rs485ss.CFifo.buf[7] = Rs485ss.Paket.Elem; Rs485ss.CFifo.buf[8] = Rs485ss.Paket.Siz; pi = 9; //jdeli o cteni z ram if( (Rs485ss.Paket.Blok == 1) && (Rs485ss.Paket.Elem == 0) && (Rs485ss.Paket.Siz == 1)){ if(Rs485ss.Paket.Elem == 0){ Rs485ss.CFifo.buf[pi++] = Ds18b20[0].Mes.Temp&0xff; Rs485ss.CFifo.buf[pi++] = Ds18b20[0].Mes.Temp>>8; temp = 1; } } //jde o cteni z eprom if( (Rs485ss.Paket.Blok == 255) && ( (Rs485ss.Paket.Elem + Rs485ss.Paket.Siz) <= (sizeof(EpromSetRam)/sizeof(u16t))) ){ for(u8t i=0; i < Rs485ss.Paket.Siz; i++){ Rs485ss.CFifo.buf[pi++] = EpromSetRam.Value[Rs485ss.Paket.Elem + i].LO; Rs485ss.CFifo.buf[pi++] = EpromSetRam.Value[Rs485ss.Paket.Elem + i].HI; } temp = 1; } //neznama polozka if(temp == 0){ Rs485ss.CFifo.buf[pi++] =RS485S_READ_NO_EXIST&0xff; Rs485ss.CFifo.buf[pi++] =RS485S_READ_NO_EXIST>>8; } //temp crc temp = 0; for(u8t i=1; i < pi; i++){ temp += Rs485ss.CFifo.buf[i]; } Rs485ss.CFifo.buf[pi++] = ~temp; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.used = pi; } //-----------------------------------------------------------------------------//Prijat Write static void rs485s_rx_w(void){ u8t temp = 0; Rs485ss.CFifo.buf[0] = RS485S_SYNC0; Rs485ss.CFifo.buf[1] = RS485S_SYNC1; Rs485ss.CFifo.buf[2] = RS485S_SYNC2_Sl; Rs485ss.CFifo.buf[3] = Rs485ss.address; Rs485ss.CFifo.buf[4] = RS485S_CMD_ACK; Rs485ss.CFifo.buf[5] = RS485S_SYNC3; Rs485ss.CFifo.buf[6] = Rs485ss.Paket.Blok; Rs485ss.CFifo.buf[7] = Rs485ss.Paket.Elem; Rs485ss.CFifo.buf[8] = Rs485ss.Paket.Siz; //jde o zapis do eprom if( (Rs485ss.Paket.Blok == 0xff) && ( (Rs485ss.Paket.Elem + Rs485ss.Paket.Siz) <= (sizeof(EpromSetRam)/sizeof(u16t)))){
91
for(u8t i=0; i < Rs485ss.Paket.Siz; i++){ EpromSetRam.U16[Rs485ss.Paket.Elem +i] = Rs485ss.Paket.Value[i].U16; } }else{ // no exist Rs485ss.CFifo.buf[8] = 0; }
//temp crc temp = 0; for(u8t i=1; i < 9; i++){ temp += Rs485ss.CFifo.buf[i]; } Rs485ss.CFifo.buf[9] = ~temp; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.used = 10; } //-----------------------------------------------------------------------------//prijmuta odpoved static void rs485s_rx_r(void){ cfifo_free(); } //-----------------------------------------------------------------------------//Prijat Ack static void rs485s_rx_a(void){ cfifo_free(); } //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------static void rs485s_rx(void){ u8t tmp = 0; WD_R(0); //timeout if(Rs485ss.timer_1ms > Rs485ss.rx_timeout){ Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; } //Zpracovavej prijata data while(Rs485ss.CFifo.used > Rs485ss.rx_i_while){ LEDS_RX; //ovf if(Rs485ss.CFifo.ovf){ Rs485ss.CFifo.ovf = 0; LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; } //timeout if(Rs485ss.timer_1ms > Rs485ss.rx_timeout){ Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; } switch(Rs485ss.RxSw){ //start byte case RS485S_START_B :
92
if(cfifo_read() == RS485S_SYNC1){ Rs485ss.timer_1ms=0; Rs485ss.rx_i_while = 6; Rs485ss.RxSw++; } break; //****************************** //check header case RS485S_HSW : //kontrola staticke hlavicky if(cfifo_read_i(3) == RS485S_SYNC3){ Rs485ss.Paket.Sync2 = cfifo_read_i(0); Rs485ss.Paket.Cmd = cfifo_read_i(2); Rs485ss.Paket.Siz = cfifo_read_i(6); //Pokud je prekrocena maximalni delka, tak paket ignoruj if(Rs485ss.Paket.Siz > RS485S_SIZE_MAX){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } if(Rs485ss.Paket.Sync2 == RS485S_SYNC2_MA){ //Q questino - read if(Rs485ss.Paket.Cmd == RS485S_CMD_READ){ Rs485ss.RxSw = RS485S_HQ; Rs485ss.rx_i_while = 7; tmp = 1; } //W write if(Rs485ss.Paket.Cmd == RS485S_CMD_WRITE){ Rs485ss.RxSw = RS485S_HW; Rs485ss.rx_i_while = 7 + (2*Rs485ss.Paket.Siz); tmp = 1; } } if(Rs485ss.Paket.Sync2 == RS485S_SYNC2_Sl){ //R response if(Rs485ss.Paket.Cmd == RS485S_CMD_RESPONSE){ Rs485ss.RxSw = RS485S_HR; Rs485ss.rx_i_while = 7 + (2*Rs485ss.Paket.Siz); tmp = 1; } // ACK if(Rs485ss.Paket.Cmd == RS485S_CMD_ACK){ Rs485ss.RxSw = RS485S_HA; Rs485ss.rx_i_while = 7; tmp = 1; } } } //Chceck CMD and Siz if(tmp){ Rs485ss.Paket.Adr = cfifo_read_i(1); Rs485ss.Paket.Blok = cfifo_read_i(4); Rs485ss.Paket.Elem = cfifo_read_i(5); Rs485ss.Paket.Crc = RS485S_SYNC1 + Rs485ss.Paket.Sync2 + Rs485ss.Paket.Adr + Rs485ss.Paket.Cmd + RS485S_SYNC3 + Rs485ss.Paket.Blok + Rs485ss.Paket.Elem + Rs485ss.Paket.Siz; }else{
93
LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; } break; //****************************** //-----------------------------------------------------------------------------//****************************** //check paket Question - read case RS485S_HQ : //check crc Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(7) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //check adress if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } RS485S_RXD; rs485s_rx_q(); Rs485ss.RxStatus = RS485S_RETTX; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //quest end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket Write case RS485S_HW : //value tmp = 7; for(u8t i=0; i< (Rs485ss.Paket.Siz*2); i++){ Rs485ss.Paket.ValueU8[i] = cfifo_read_i(tmp++); Rs485ss.Paket.Crc += Rs485ss.Paket.ValueU8[i]; } //check crc Write Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(tmp) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //adressa if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } RS485S_RXD; rs485s_rx_w();
94
Rs485ss.RxStatus = RS485S_RETTX; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //Write end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket RESPONS case RS485S_HR : //value tmp = 7; for(u8t i=0; i< (Rs485ss.Paket.Siz*2); i++){ Rs485ss.Paket.ValueU8[i] = cfifo_read_i(tmp++); Rs485ss.Paket.Crc += Rs485ss.Paket.ValueU8[i]; } //check crc response Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(tmp) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //check adress if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //RS485S_RXD; rs485s_rx_r(); Rs485ss.RxStatus = RS485S_RETNOP; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //respons end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket ACK case RS485S_HA : //check crc response Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(7) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //check adress if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //RS485S_RXD;
95
rs485s_rx_a(); Rs485ss.RxStatus = RS485S_RETNOP; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //ACK end //****************************** //-----------------------------------------------------------------------------default : Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; wd_reset_cpu = 1; break; } } Rs485ss.RxStatus = RS485S_RETNOP; return; } //********** end rs485s_rx ********** //-----------------------------------------------------------------------------void rs485s(void){ switch (Rs485ss.Status){ //prijimej dokud nedostanes question/ACK case RS485S_RXTX_RECV : rs485s_rx(); if(Rs485ss.RxStatus == RS485S_RETTX) Rs485ss.Status++; else break; //zakaz prijem, timeout TX case RS485S_RXTX_RECV_COMPLET : //RS485S_RXD; Rs485ss.timer_1ms=0; Rs485ss.Status++; //pockej a pak zacni vysilat case RS485S_RXTX_TX_WAIT : if(Rs485ss.timer_1ms>Rs485ss.tx_pauza) Rs485ss.Status++; else break; //nastartuj vysilani case RS485S_RXTX_TX_START : RS485S_TXEA; RS485S_TXEB; LEDS_TX; Rs485ss.Status++; //pockej na odvysilani case RS485S_TX_TX : break; //-----------------------------------------------------------------------------default : RS485S_RXE; Rs485ss.Status = RS485S_RXTX_RECV; wd_reset_cpu = 1; break;
96
} } //-----------------------------------------------------------------------------void rs485s_timer(void){ if(Rs485ss.timer_1ms < U16TOP)Rs485ss.timer_1ms++; } //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Nastavení sériové linky //-----------------------------------------------------------------------------static void uart_1200(void){ #define BAUD 1200 #include UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1 << U2X); #else UCSRA &= ~(1 << U2X); #endif } //-----------------------------------------------------------------------------static void uart_2400(void){ #undef BAUD // avoid compiler warning #define BAUD 2400 #include UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1 << U2X); #else UCSRA &= ~(1 << U2X); #endif } //-----------------------------------------------------------------------------static void uart_4800(void){ #undef BAUD // avoid compiler warning #define BAUD 4800 #include UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1 << U2X); #else UCSRA &= ~(1 << U2X); #endif } //-----------------------------------------------------------------------------static void uart_9600(void){ #undef BAUD // avoid compiler warning #define BAUD 9600 #include UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1 << U2X); #else UCSRA &= ~(1 << U2X); #endif
97
} //-----------------------------------------------------------------------------static void uart_19200(void){ #undef BAUD // avoid compiler warning #define BAUD 19200 #include UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1 << U2X); #else UCSRA &= ~(1 << U2X); #endif } //-----------------------------------------------------------------------------static void uart_38400(void){ #undef BAUD // avoid compiler warning #define BAUD 38400 #include UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1 << U2X); #else UCSRA &= ~(1 << U2X); #endif } //-----------------------------------------------------------------------------// Přerušení od příjmu znaku //-----------------------------------------------------------------------------ISR(USART_RXC_vect ){ u8t temp = 0; temp |= (UCSRA & ((1<= Rs485ss.CFifo.used) RS485S_TXEND; } //-----------------------------------------------------------------------------// Přerušení od odeslání znaku //-----------------------------------------------------------------------------ISR(USART_TXC_vect){
98
u8vt dummy; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.w_i = 0; Rs485ss.CFifo.used = 0; Rs485ss.CFifo.ovf = 0; while ( UCSRA & _BV(RXC) ){ dummy = UDR; } RS485S_RXE; Rs485ss.Status = RS485S_RXTX_RECV; } //-----------------------------------------------------------------------------//CFIFO //-----------------------------------------------------------------------------static inline void cfifo_write(u8t data){ ATOMIC_BLOCK(ATOMIC_FORCEON){ Rs485ss.CFifo.buf[Rs485ss.CFifo.w_i++] = data; Rs485ss.CFifo.w_i &= RS485S_CFIFO_MASK; Rs485ss.CFifo.used = RS485S_CFIFO_MASK & (Rs485ss.CFifo.w_i - Rs485ss.CFifo.r_i); if(Rs485ss.CFifo.used == RS485S_CFIFO_MASK) { //preteceni Rs485ss.CFifo.ovf = 1; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.w_i = 0; Rs485ss.CFifo.used = 0; } } } //-----------------------------------------------------------------------------static inline u8t cfifo_read(void){ u8t temp; ATOMIC_BLOCK(ATOMIC_FORCEON){ temp = Rs485ss.CFifo.buf[Rs485ss.CFifo.r_i++]; Rs485ss.CFifo.r_i &= RS485S_CFIFO_MASK; Rs485ss.CFifo.used = RS485S_CFIFO_MASK & (Rs485ss.CFifo.w_i - Rs485ss.CFifo.r_i); } return temp; } //-----------------------------------------------------------------------------static inline u8t cfifo_read_i(u8t i){ u8t temp; ATOMIC_BLOCK(ATOMIC_FORCEON){ temp = Rs485ss.CFifo.buf[RS485S_CFIFO_MASK & (Rs485ss.CFifo.r_i + i)]; } return temp; } //-----------------------------------------------------------------------------static inline void cfifo_free(void){ ATOMIC_BLOCK(ATOMIC_FORCEON){ Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.w_i = 0; Rs485ss.CFifo.used = 0; Rs485ss.CFifo.ovf = 0;
99
} } //-----------------------------------------------------------------------------void rs485s_init(void){ UCSRB = ((0<
Příloha 6: Zdrojový kód Měřicí jednotky, modul ds18b20 //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Ds18b20.h //-----------------------------------------------------------------------------#ifndef DS18B20_H #define DS18B20_H #include "../sys/typedefine.h" //-----------------------------------------------------------------------------//!Definuje pocet cidel! //nezapomen zavolat //ds18b20_setPort(&PORTA, &DDRA, _BV(PA0), &PORTA, &DDRA, &PINA, _BV(PA1), &Ds18b20[0]);... #define DS18B20_N 1 //pauza pro mereni 1ms/1 OW_MES_PAUSE>OW_MES_TIM #define DS18B20_MES_PAUSE 950 //hlidani minimalni doby mereni 1mS/1 OW_MES_TIM
100
//hlasi pri chybe crc #define DS18B20_ERR_CRC (0x8001) //hlaseni pri spatne delce mereni #define DS18B20_ERR_MES_TIM (0x8002) //po obnoveni napajeni #define DS18B20_RESETING (0x8004) //max pocet chyb do vyhodnoceni crc chyby #define DS18B20_ERR_CRC_MAX 5 //max pocet chyb do vyhodnoceni odpojeni cidla #define DS18B20_ERR_DISCONN_MAX 5 //max pocet chyb do vyhodnoceni spatne delky mereni #define DS18b20_ERR_MES_TIM_MAX 5 typedef enum{ DS18B20_POW_RES_B, //vypni cidlo,aby se rezl procak - begin DS18B20_POW_RES_E, //vypni cidlo,aby se rezl procak - end DS18B20_RESET0, //reset zbernice DS18B20_MEASURE, //nastartuj mereni DS18B20_MES_PAUSE_B,//cekej dokud se nedomeri DS18B20_MES_PAUSE_E,//cekej dokud se nedomeri DS18B20_RESET1, //reset sbernice DS18B20_READ_TEMP, //precti teplotu DS18B20_POW_DOW //vypni napajeni }TDs18b20_swE; //-----------------------------------------------------------------------------typedef struct{ u16t MesPause; s16t TempOfs; }TDs18b20_Set; //-----------------------------------------------------------------------------typedef struct{ s16t Temp; }TDs18b20_Mes; //-----------------------------------------------------------------------------typedef struct{ volatile u16t t1ms; TDs18b20_swE sw; }TDs18b20_Data; //-----------------------------------------------------------------------------typedef struct{ u8t presentErrI; u8t crcErrI; }TDs18b20_Diag; //-----------------------------------------------------------------------------typedef struct{ u8vt* SupPORT; u8vt* SupDDR; u8t SupMask; u8vt* DataPORT; u8vt* DataDDR; u8vt*DataPIN; u8t DataMask; }TDs18b20_Hw; //-----------------------------------------------------------------------------typedef struct{
101
TDs18b20_Set Set; TDs18b20_Mes Mes; TDs18b20_Diag Diag; TDs18b20_Data Data; TDs18b20_Hw Hw; }TDs18b20; //-----------------------------------------------------------------------------extern TDs18b20 Ds18b20[DS18B20_N]; //-----------------------------------------------------------------------------void ds18b20_setPort(u8vt* SupPORT, u8vt* SupDDR, u8t SupMask, u8vt* DataPORT, u8vt* DataDDR, u8vt* DataPIN, u8t DataMask, TDs18b20* sp); //-----------------------------------------------------------------------------void ds18b20_init(void); //-----------------------------------------------------------------------------inline void ds18b20_1ms(TDs18b20* sp){ if(sp->Data.t1ms < 0xffff)sp->Data.t1ms++; } //-----------------------------------------------------------------------------// 0 - Merim // 1 - chyba // 2 - domereno u8t ds18b20(TDs18b20* sp); //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Ds18b20.c //-----------------------------------------------------------------------------#include //názvy registrů #include //práce s přerušením #include #include #include #include "../sys/wd_flo.h" #include "ds18b20.h" TDs18b20 Ds18b20[DS18B20_N] = {}; //-----------------------------------------------------------------------------void ds18b20_setPort(u8vt* SupPORT, u8vt* SupDDR, u8t SupMask, u8vt* DataPORT, u8vt* DataDDR, u8vt*DataPIN, u8t DataMask, TDs18b20* sp); //-----------------------------------------------------------------------------inline void ds18b20_1ms(TDs18b20* sp); //-----------------------------------------------------------------------------// 0 - Merim // 1 - chyba // 2 - domereno u8t ds18b20(TDs18b20* sp); //-----------------------------------------------------------------------------//konstantni znaky v mem. cidla #define DS18B20_SCRATCHPAD5 0xFF #define DS18B20_SCRATCHPAD6 0x0C #define DS18B20_SCRATCHPAD7 0x10 //-----------------------------------------------------------------------------//tabulka pro prevod teploty static const s8t
102
ds18b20_temp_tab[16]={0, 6, 13, 19, 25 ,31, 38, 44, 50, 56, 63, 69, 75, 81, 88, 94}; //-----------------------------------------------------------------------------//napajeni senzoru // 0 - tot8ln2 vypni // 1 - zapni vcc, vypni data port // 2 - zapni vcc i data port static void ds18b20_suply(u8t in, TDs18b20* sp); //-----------------------------------------------------------------------------static u8t ds18b20_reset(TDs18b20* sp); //-----------------------------------------------------------------------------//pred volanim musi byt ds18b20_suply(1)!!! static void ds18b20_writeByte(u8t in, TDs18b20* sp); //-----------------------------------------------------------------------------static u8t da18b20_readByte(TDs18b20* sp); //-----------------------------------------------------------------------------static void ds18b20_measure(TDs18b20* sp); //-----------------------------------------------------------------------------static u8t ds18b20_readTemp(TDs18b20* sp); //-----------------------------------------------------------------------------//napajeni senzoru // 0 - totalne vypni // 1 - komunikace; zapni vcc, vypni data port // 2 - mereni; zapni vcc i data port static void ds18b20_suply(u8t in, TDs18b20* sp){ if(in == 0){ *sp->Hw.SupDDR |= sp->Hw.SupMask; *sp->Hw.SupPORT &= ~sp->Hw.SupMask; *sp->Hw.DataDDR |= sp->Hw.DataMask; *sp->Hw.DataPORT &= ~sp->Hw.DataMask; return; } if(in == 1){ *sp->Hw.DataDDR &= ~sp->Hw.DataMask; *sp->Hw.DataPORT &= ~sp->Hw.DataMask; *sp->Hw.SupDDR |= sp->Hw.SupMask; *sp->Hw.SupPORT |= sp->Hw.SupMask; return; } if(in == 2){ *sp->Hw.DataPORT |= sp->Hw.DataMask; *sp->Hw.DataDDR |= sp->Hw.DataMask; *sp->Hw.SupDDR |= sp->Hw.SupMask; *sp->Hw.SupPORT |= sp->Hw.SupMask; return; } } //-----------------------------------------------------------------------------static u8t ds18b20_reset(TDs18b20* sp){ u8vt* DDR = sp->Hw.DataDDR; u8vt* PIN = sp->Hw.DataPIN; u8t Mask = sp->Hw.DataMask; u8t temp = 0; *DDR |= Mask; //stahni trazistor _delay_ms(0.480); //ceka minimalne 480uS ATOMIC_BLOCK(ATOMIC_FORCEON){ *DDR &= ~Mask; //uvolni trazistor na PULL UP _delay_us(70); if(*PIN & Mask){ temp = 1;//not connect
103
} } _delay_ms(0.410); return temp; } //-----------------------------------------------------------------------------//pred volanim musi byt ds18b20_suply(1)!!! static void ds18b20_writeByte(u8t in, TDs18b20* sp){ u8vt* DDR = sp->Hw.DataDDR; u8t Mask = sp->Hw.DataMask; for(u8t i=0; i < 8; i++){ if(in&0b01){ ATOMIC_BLOCK(ATOMIC_FORCEON){ *DDR |= Mask; //stahni trazistor _delay_us(5); *DDR &= ~Mask; //uvolni trazistor na PULL UP _delay_us(65); } }else{ ATOMIC_BLOCK(ATOMIC_FORCEON){ *DDR |= Mask; //stahni trazistor _delay_us(60); *DDR &= ~Mask; //uvolni trazistor na PULL UP _delay_us(10); } } in>>=1; } } //-----------------------------------------------------------------------------static u8t da18b20_readByte(TDs18b20* sp){ u8vt* DDR = sp->Hw.DataDDR; u8vt* PIN = sp->Hw.DataPIN; u8t Mask = sp->Hw.DataMask; u8t temp = 0; for(u8t c = 0; c<8; c++){ temp >>= 1; ATOMIC_BLOCK(ATOMIC_FORCEON){ *DDR |= Mask; //stahni trazistor _delay_us(5); *DDR &= ~Mask; //uvolni trazistor na PULL UP _delay_us(15); if(*PIN & Mask){ temp|=0b10000000; } } _delay_us(56); } return temp; } //-----------------------------------------------------------------------------static void ds18b20_measure(TDs18b20* sp){ ds18b20_writeByte(0xcc, sp); //skip comand rom ds18b20_writeByte(0x44, sp); //convert T ds18b20_suply(2, sp); } //------------------------------------------------------------------------------
104
static u8t ds18b20_readTemp(TDs18b20* sp){ u8t scratchpad[9]; u8t crc; s16t tempS16; ds18b20_writeByte(0xCC, sp); //skip comand rom ds18b20_writeByte(0xBE, sp); //read SCRATCHPAD crc = 0; //cti cidlo a kontroluj crc for( u8t i=0; i<8; i++){ scratchpad[i] = da18b20_readByte(sp); crc = _crc_ibutton_update(crc, scratchpad[i]); } scratchpad[8] = da18b20_readByte(sp); //je crc vporadku? if ((scratchpad[8] == crc)&&(scratchpad[5] == DS18B20_SCRATCHPAD5)//&& /*(scratchpad[6] == DS18B20__SCRATCHPAD6)&&(scratchpad[7] == DS18B20__SCRATCHPAD7)*/){ tempS16 = (((s16t)scratchpad[1])<<8 )|scratchpad[0]; tempS16 >>= 4; tempS16 *= 100; tempS16 = tempS16 + ds18b20_temp_tab[scratchpad[0]&0x0f]; ATOMIC_BLOCK(ATOMIC_FORCEON){ sp->Mes.Temp = tempS16 + sp->Set.TempOfs; } return 0; } return 1; } //-----------------------------------------------------------------------------void ds18b20_setPort(u8vt* SupPORT, u8vt* SupDDR, u8t SupMask, u8vt* DataPORT, u8vt* DataDDR, u8vt*DataPIN, u8t DataMask, TDs18b20* sp){ sp->Hw.SupPORT = SupPORT; sp->Hw.SupDDR = SupDDR; sp->Hw.SupMask = SupMask; sp->Hw.DataPORT = DataPORT; sp->Hw.DataDDR = DataDDR; sp->Hw.DataPIN = DataPIN; sp->Hw.DataMask = DataMask; } //-----------------------------------------------------------------------------// 0 - Merim // 1 - chyba // 2 - domereno u8t ds18b20(TDs18b20* sp){ u8t temp = 0; WD_R(1); switch(sp->Data.sw){ //***************************************** //odpoj napajeni,aby se reznul procak case DS18B20_POW_RES_B: ds18b20_suply(0, sp); //vynuluj realtimer sp->Data.t1ms = 0; sp->Data.sw++; break; //***************************************** //cekam dokud nedojde cidlu proud
105
case DS18B20_POW_RES_E: if( sp->Data.t1ms > DS18B20_POW_RES_PAUSE){ //zapni proud cidla ds18b20_suply(1, sp); sp->Data.sw++; } break; //***************************************** //reset0 zbernice case DS18B20_RESET0: //je zarizeni pripojeno if( ds18b20_reset(sp) == 0 ){ sp->Diag.presentErrI = 0; sp->Data.sw++; }else{ sp->Diag.presentErrI++; //pocitadlo chyb if( sp->Diag.presentErrI > DS18B20_ERR_DISCONN_MAX){ sp->Diag.presentErrI = DS18B20_ERR_DISCONN_MAX; sp->Mes.Temp = DS18B20_ERR_DISCONN; ds18b20_suply(0, sp); temp = 1; //zacni znovu sp->Data.sw = DS18B20_POW_RES_B; } } break; //***************************************** //nastartuj mereni case DS18B20_MEASURE: //nastartuj mereni ds18b20_measure(sp); sp->Data.t1ms = 0; sp->Data.sw++; break; //***************************************** //pauza pro mereni-hlidam min dobu mereni 100mS case DS18B20_MES_PAUSE_B: if( sp->Data.t1ms > DS18B20_MES_TIM ) {/* if(OW_DATA){ //chyba - zacni znovu,mereni bylo podezrele kratke ow_err_mes_tim_p++; if(ow_err_mes_tim_p>DS18b20_ERR_MES_TIM_MAX){ ow_err_mes_tim_p=DS18b20_ERR_MES_TIM_MAX; ATOMIC_BLOCK(ATOMIC_FORCEON){ ow_temperature=DS18b20_ERR_MES_TIM_MAX; } } ows_status=DS18B20_POW_RES_B; }else*/{//ok,pokracuj //ows_status=DS18B20_MES_PAUSE_E; sp->Data.sw++; } } break; //***************************************** //pauza pro mereni - konec 900mS case DS18B20_MES_PAUSE_E: if( sp->Data.t1ms > DS18B20_MES_PAUSE - DS18B20_MES_TIM ){ ds18b20_suply(1, sp);
106
sp->Data.sw++; } break; //***************************************** //reset1 zbernice case DS18B20_RESET1: //je zarizeni pripojeno if(ds18b20_reset(sp) == 0){ sp->Data.sw++; }else{ //zacni znovu sp->Diag.presentErrI++; sp->Data.sw = DS18B20_POW_RES_B; } break; //***************************************** //precti teplotu case DS18B20_READ_TEMP: if(ds18b20_readTemp(sp)==0){ //podarilo se sp->Diag.crcErrI=0; //vynuluj casovac sp->Data.t1ms = 0; //vypni proud cidla ds18b20_suply(0, sp); sp->Data.sw++; } else{ //nezdarilo se sp->Diag.crcErrI++; if( sp->Diag.crcErrI > DS18B20_ERR_CRC_MAX){ sp->Diag.crcErrI = DS18B20_ERR_CRC_MAX; sp->Mes.Temp = DS18B20_ERR_CRC; ds18b20_suply(0, sp); temp = 1; } //zacni znovu sp->Data.sw = DS18B20_POW_RES_B; } break; //***************************************** //pauza 10s case DS18B20_POW_DOW: if( sp->Data.t1ms > sp->Set.MesPause ){ temp = 2; sp->Data.sw = DS18B20_POW_RES_B; } break; //***************************************** default :sp->Data.sw = DS18B20_POW_RES_B; break; } return temp; } //-----------------------------------------------------------------------------void ds18b20_init(void){ //Pripojene cidla // (*SupPORT, *SupDDR, SupMask, *DataPORT, *DataDDR, *DataPIN, ataMask, *sp) ds18b20_setPort(&PORTB, &DDRB, _BV(PB1), &PORTB, &DDRB, &PINB, _BV(PB2), &Ds18b20[0]);
107
//ds18b20_setPort(&PORTB, &DDRB, _BV(PB1), &PORTB, &DDRB, &PINB, _BV(PB2), &Ds18b20[1]); Ds18b20[0].Mes.Temp = DS18B20_RESETING; }
Příloha 7 : Zdrojový kód Měřicí jednotky, modul eprom //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// eprom.h //-----------------------------------------------------------------------------#ifndef EPROM_H #define EPROM_H #include "../sys/porty_def.h" #include "../sys/typedefine.h" #include "../ds18b20/ds18b20.h" //-----------------------------------------------------------------------------// Polozka typedef struct{ union{ struct{ u8t LO; u8t HI; }; u16t U16; }; }TEpromValue; //-----------------------------------------------------------------------------//Nastaveni typedef union{ struct{ u16t CMD; u16t MaBitrate; u16t MaTxPause; u16t MaRxTimeout; u16t MaAdrReadMax; u16t MaErrReadMax; u16t SlAddr; u16t SlBitrate; u16t SlTxPause; u16t SlRxTimeout; u16t MesPause; s16t TempOffset[1]; }; u8t U8[1]; u16t U16[1]; TEpromValue Value[1]; }TEpromSet; //-----------------------------------------------------------------------------extern TEpromSet EpromSetRom; extern TEpromSet EpromSetRam; //-----------------------------------------------------------------------------void eprom_1ms(void); //-----------------------------------------------------------------------------void eprom_load(void);
108
//-----------------------------------------------------------------------------void eprom_toModule(void); //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// eprom.c //-----------------------------------------------------------------------------#include #include #include "../sys/wd_flo.h" #include "../rs485s/rs485s.h" #include "eprom.h" //-----------------------------------------------------------------------------//Cyklus eprom updateru typedef enum{ EPROM_REDY, EPROM_UPD, EPROM_UPD_BYTE, EPROM_LD, EPROM_LD_WORD }TEpromSw; //-----------------------------------------------------------------------------typedef enum{ EPROM_NOP = 0, EPROM_RESET, EPROM_RAMTOROM, EPROM_ROMTORAM, EPROM_RAMTOMODULE, EPROM_LOADDEFAULT, }TEpromCMD; //-----------------------------------------------------------------------------typedef struct{ u16t ActB_i; //Aktualni byte TEpromSw sw; }TEprom; //-----------------------------------------------------------------------------TEprom Eprom = { 0, EPROM_REDY }; //-----------------------------------------------------------------------------//Struktura nastaveni v EEPROM TEpromSet EpromSetRom EEMEM = { { 0, //CMD - Neni ukladan; (u16t)SBITRATE_9600, //MaBitrate; 100, //MaTxPause; 100, //MaRxTimeout; 31, //MaAdrReadMax; 3, //MaErrReadMax; 1, //SlAddr; (u16t)SBITRATE_9600, //SlBitrate; 5, //SlTxPause;
109
100,
//SlRxTimeout;
1000, {0}
//MesPause; //TempOffset[DS18B20_N];
} }; //-----------------------------------------------------------------------------TEpromSet EpromSetRam = { { 0, //CMD - Neni ukladan; (u16t)SBITRATE_9600, //MaBitrate; 100, //MaTxPause; 100, //MaRxTimeout; 31, //MaAdrReadMax; 3, //MaErrReadMax; 1, //SlAddr; (u16t)SBITRATE_9600, //SlBitrate; 5, //SlTxPause; 100, //SlRxTimeout; 1000, {0}
//MesPause; //TempOffset[DS18B20_N];
} }; //-----------------------------------------------------------------------------void eprom_update_b(u16t adr, u8t dat){ if(dat != eeprom_read_byte( (u8t *)adr) ){ eeprom_write_byte((u8t *)adr, (u8t)dat); } } //-----------------------------------------------------------------------------void eprom_load(void){ for(u16t i=0; i < sizeof(EpromSetRam); i++){ EpromSetRam.U8[i] = eeprom_read_byte((u8t*)i); } } //-----------------------------------------------------------------------------void eprom_1ms(void){ switch(Eprom.sw){ //------------------case EPROM_REDY: WD_R(2); if(EpromSetRam.CMD == EPROM_RESET){ wdt_enable(WDTO_15MS); while(1); } if(EpromSetRam.CMD == EPROM_RAMTOROM) Eprom.sw = EPROM_UPD; if(EpromSetRam.CMD == EPROM_ROMTORAM) Eprom.sw = EPROM_LD; if(EpromSetRam.CMD == EPROM_RAMTOMODULE){ eprom_toModule(); EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY; } if(EpromSetRam.CMD == EPROM_LOADDEFAULT){ // EpromSetRam.MaBitrate = SBITRATE_9600; // EpromSetRam.MaTxPause = 100; // EpromSetRam.MaRxTimeout = RS485SS_BASIC_TIMEOUT;
110
// //
EpromSetRam.MaAdrReadMax = 31; EpromSetRam.MaErrReadMax = 3; EpromSetRam.SlAddr = 1; EpromSetRam.SlBitrate = SBITRATE_9600; EpromSetRam.SlTxPause = 5; EpromSetRam.SlRxTimeout = RS485SS_BASIC_TIMEOUT; EpromSetRam.MesPause = 1000; EpromSetRam.TempOffset[0] = 0; eprom_toModule(); EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY; } break; //------------------case EPROM_UPD: Eprom.ActB_i = 0; Eprom.sw++; break; //------------------case EPROM_UPD_BYTE: if( !eeprom_is_ready() )return; eprom_update_b(Eprom.ActB_i, EpromSetRam.U8[Eprom.ActB_i]); Eprom.ActB_i++; if(Eprom.ActB_i == sizeof(EpromSetRam)){ EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY; } break; //------------------case EPROM_LD: Eprom.ActB_i = 0; Eprom.sw++; break; //------------------case EPROM_LD_WORD: if( !eeprom_is_ready() )return; EpromSetRam.U8[Eprom.ActB_i] = eeprom_read_byte((u8t*)(Eprom.ActB_i)); Eprom.ActB_i++; if(Eprom.ActB_i == sizeof(EpromSetRam)){ EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY; } break; //------------------default: Eprom.sw = EPROM_REDY;
} } //-----------------------------------------------------------------------------void eprom_toModule(void){ // // // // // // // // // //
Rs485ms.bitrate = EpromSetRam.MaBitrate; Rs485ms.tx_pauza = EpromSetRam.MaTxPause; Rs485ms.rx_timeout = EpromSetRam.MaRxTimeout; if(EpromSetRam.MaAdrReadMax > RS485M_SIZE_RAM){ Rs485ms.adrReadMax = RS485M_SIZE_RAM; }else{ Rs485ms.adrReadMax = EpromSetRam.MaAdrReadMax; } if(EpromSetRam.MaErrReadMax > U8TOP) Rs485ms.readErrmax = U8TOP;
111
// else{ // Rs485ms.readErrmax = EpromSetRam.MaErrReadMax; // }
if(!Rs485ss.adrSw){ Rs485ss.address = EpromSetRam.SlAddr; Rs485ss.bitrate = EpromSetRam.SlBitrate; }else{ Rs485ss.address = Rs485ss.adrSw; Rs485ss.bitrate = Rs485ss.bitrateSw; } Rs485ss.tx_pauza = EpromSetRam.SlTxPause; if(EpromSetRam.SlRxTimeout < RS485SS_BASIC_TIMEOUT/(Rs485ss.bitrate+1)){ Rs485ss.rx_timeout = (RS485SS_BASIC_TIMEOUT/(Rs485ss.bitrate+1)) + 20; }else{ Rs485ss.rx_timeout = EpromSetRam.SlRxTimeout; } Ds18b20[0].Set.MesPause = EpromSetRam.MesPause; Ds18b20[0].Set.TempOfs = EpromSetRam.TempOffset[0]; }
Příloha 8: Zdrojový kód Měřicí jednotky, modul wd_flo //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// wd_flo.h //-----------------------------------------------------------------------------#ifndef WD_FLO #define WD_FLO #include "../sys/typedefine.h" //pocet wd hlidacu #define WD_N 3 //reset hlidace #define WD_R(i) wd_i[i] = 0 //-----------------------------------------------------------------------------void wd_flo(void); //-----------------------------------------------------------------------------//pocitadlo , 10mS extern u8vt wd_i[WD_N]; //top extern u8ct wd_im[WD_N]; //-----------------------------------------------------------------------------extern u8vt wd_reset_cpu; //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------// wd_flo.c //-----------------------------------------------------------------------------#include #include "wd_flo.h" //-----------------------------------------------------------------------------void wd_flo(void); //-----------------------------------------------------------------------------//registry pro poc
112
u8vt wd_i[WD_N] = {0}; //strop pro vyhodnoceni wdr v 10x mS 255- vypnuto u8ct wd_im[WD_N] ={ 250, //rs485s 250, //ds18b20 250, //eprom }; //-----------------------------------------------------------------------------u8vt wd_reset_cpu = 0; //-----------------------------------------------------------------------------void wd_flo(void){ static unsigned char div =0; if(wd_reset_cpu){ wdt_enable(WDTO_15MS); while(1); return; } if(div == 5){ for(register unsigned char i = 0; i< WD_N; i++){ if(wd_i[i] < 255)wd_i[i]++; } } if(div > 9){ div = 0; for(register unsigned char i = 0; i< WD_N; i++){ if(wd_i[i] > wd_im[i]){ wdt_enable(WDTO_15MS); while(1); return; } } wdt_reset(); } div++; }
Příloha 9: Zdrojový kód Měřicí jednotky, modul led //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// led.h //-----------------------------------------------------------------------------#ifndef LED_H #define LED_H #include "../sys/typedefine.h" #include "../sys/porty_def.h" #define NUMOFLED 2 #define LED0(outx) OUTPORT_PORT_DDR((outx),(1),PORTC,DDRC,PINC,PC5) #define LED1(outx) OUTPORT_PORT_DDR((outx),(1),PORTD,DDRD,PIND,PD2) //#define LED2(outx) OUTPORT_PORT_DDR((outx),(1),PORTC,DDRC,PINC,PC6) //#define LED3(outx) OUTPORT_PORT_DDR((outx),(1),PORTC,DDRC,PINC,PC7) //#define LED4(outx) OUTPORT_PORT_DDR((outx),(1),PORTE,DDRE,PINE,PE2)
113
//-----------------------------------------------------------------------------extern volatile u8t led_i[NUMOFLED]; //-----------------------------------------------------------------------------void led_10ms(void); //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// led.c //-----------------------------------------------------------------------------#include //názvy registrů #include "led.h" volatile u8t led_i[NUMOFLED]={0}; //-----------------------------------------------------------------------------void led_10ms(void){ if(led_i[0] > 0){led_i[0]--; LED0(1);} else{LED0(0);} if(led_i[1] > 0){led_i[1]--;LED1(1);} else{LED1(0);} }
Příloha 10: Zdrojový kód Měřicí jednotky, pomocné soubory //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// porty_def.h //-----------------------------------------------------------------------------#ifndef PORTY_DEF #define PORTY_DEF 1 //-----------------------------------------------------------------------------//warning Portchar only A-Z //port a ddr musi byt vzdy jen 1 nebo 0 //konstantni doba vyhodnoceni v pripade vstupni konstanty provede jen CBI SBI //V pripade promene 7 cyklu //-----------------------------------------------------------------------------#define XPINOUT_PD(port, ddr, portchar,bit) {(DDR##portchar)=((DDR##portchar)&(~_BV(bit)))|((!!(ddr))<<(bit));(PORT##portchar)=((PORT##po rtchar)&(~_BV(bit)))|((!!(port))<<(bit));} #define XPINOUT_DP(ddr, port, portchar,bit) {(PORT##portchar)=((PORT##portchar)&(~_BV(bit)))|((!!(port))<<(bit));(DDR##portchar)=((DDR##po rtchar)&(~_BV(bit)))|((!!(ddr))<<(bit));} #define XPINOUT_P(port, portchar, bit) (PORT##portchar)=((PORT##portchar)&(~_BV(bit)))|((!!(port))<<(bit)) #define XPINOUT_D(ddr, portchar, bit) (DDR##portchar)=((DDR##portchar)&(~_BV(bit)))|((!!(ddr))<<(bit)) #define XPININ(portchar, bit) (!!((PIN##portchar)&_BV(bit))) //-----------------------------------------------------------------------------//warning Portchar only A-Z //port a ddr je logicky vstup //v pripade konstanty provede jen CBI SBI //v pripade promene 7 nebo 8 cyklu podle hodnoty //!dela problemy v pripade if bez {}! //------------------------------------------------------------------------------
114
#define PINOUT_PD(port, ddr, portchar,bit) {if((port))PORT##portchar|=_BV(bit);else PORT##portchar&=~_BV(bit);if((ddr))DDR##portchar|=_BV(bit);else DDR##portchar&=~_BV(bit);} #define PINOUT_DP(ddr, port, portchar,bit) {if((ddr))DDR##portchar|=_BV(bit);else DDR##portchar&=~_BV(bit);if((port))PORT##portchar|=_BV(bit);else PORT##portchar&=~_BV(bit);} #define PINOUT_P(port, portchar, bit) {if((port))PORT##portchar|=_BV(bit);else PORT##portchar&=~_BV(bit);} #define PINOUT_D(ddr, portchar, bit) {if((ddr))DDR##portchar|=_BV(bit);else DDR##portchar&=~_BV(bit);} #define PININ(portchar, bit) (!!((PIN##portchar)&_BV(bit))) //-----------------------------------------------------------------------------/* Priklad #define VYSTUPXX(port,ddr) PINOUT_PD((port),(ddr), A,PA1) #define VYSTUPXX(port,ddr) PINOUT_DP((ddr),(port), A,PA1) #define VYSTUPX(port) PINOUT_port((port), A, PA1) #define VYSTUPDR(ddr) PINOUT_ddr((ddr), A, PA1) #define VSTUP PININ(A, PA1) */ //-----------------------------------------------------------------------------// Stara metoda //-----------------------------------------------------------------------------#define OUTPORT_PORT_DDR(outx,impx,port,dr,pin,bit) {(dr)=((dr)&(~(1<<(bit))))|((impx)<<(bit));(port)=((port)&(~(1<<(bit))))|((outx)<<(bit));} #define OUTPORT_PORT(outx,port,dr,pin,bit) (port)=((port)&(~(1<<(bit))))|((outx)<<(bit)) #define OUTPORT_DDR(impx,port,dr,pin,bit) (dr)=((dr)&(~(1<<(bit))))|((impx)<<(bit)) #define INPORT(port,dr,pin,bit) ((pin)&(1<
115
#define U8TOP ((u8t)(~0)) #define U16TOP ((u16t)(~0)) #define U32TOP ((u32t)(~0)) #define S8TOP ((s8t)(((u8t)U8TOP) >>1)) #define S8BUTT (S8TOP+1) #define S16TOP ((s16t)(((u16t)U16TOP) >>1)) #define S16BUTT (S16TOP+1) #define S32TOP ((s32t)(((u32t)U32TOP) >>1)) #define S32BUTT (S32TOP+1) #endif
Příloha 11: Zdrojový kód MCU serveru, modul main //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------//*** //*** //*** //*** Procesor : ATmega162 //*** Frekvence : 16 MHz //*** Autor : Miroslav Stehlik //*** Datum : 5.3.2013 //*** //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------#include //názvy registrů #include //práce s přerušením #include #include #include #include #include "include/eprom/eprom.h" #include "include/sys/wd_flo.h" #include "include/sys/typedefine.h" #include "include/sys/porty_def.h" #include "include/led/led.h" #include "include/rs485s/rs485s.h" #include "include/rs485m/rs485m.h" // Brown-out 4.3V //Fuses OCDEN | JTAGEN | SPIEN | WDTON | EESAVE | CKOUT //Ext. Cryst. Osc. 8.0- MHz, Start-up time 16k CK + 0ms //reset po posledni zmene nastaveni x10ms #define CHANGE_RESET_DELAY 250 //#define DEBUGSTEP //-----------------------------------------------------------------------------ISR(BADISR_vect) { // user code here }
116
//-----------------------------------------------------------------------------// Přerušení při pororovnání u čítače 2 //-----------------------------------------------------------------------------static u8vt time_1ms = 0; ISR(TIMER2_COMP_vect){ if(time_1ms < U8TOP)time_1ms++; rs485s_timer(); rs485m_timer(); } //-----------------------------------------------------------------------------//hlida zmenu nastaveni void switchChangeCtr(void){ static u8t adr, bitrate; static u16t ResetDelay = 0; //Address //PA4 PA3 PA2 PA1 PA0 adr = 0; if(!(PINA&_BV(PA4)))adr|=0b1; if(!(PINA&_BV(PA3)))adr|=0b10; if(!(PINA&_BV(PA2)))adr|=0b100; if(!(PINA&_BV(PA1)))adr|=0b1000; if(!(PINA&_BV(PA0)))adr|=0b10000; //Bitrate //PE0 PA7 bitrate = 0; if(!(PINE&_BV(PE0)))bitrate|=0b1; if(!(PINA&_BV(PA7)))bitrate|=0b10; if( adr != Rs485ss.adrSw ){ Rs485ss.adrSw = adr; ResetDelay = 1; } if( bitrate != Rs485ss.bitrateSw ){ Rs485ss.bitrateSw = bitrate; ResetDelay = 1; } if(ResetDelay){ led_i[1] = 255; led_i[2] = 255; led_i[3] = 255; ResetDelay++; } if(ResetDelay > CHANGE_RESET_DELAY){ wd_reset_cpu = 1; } } //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Hlavní program //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------int main(void){ #ifndef DEBUGSTEP wdt_enable(WDTO_120MS); wdt_reset();
117
#endif //-----------------------------------------------------------------------------// Nastavení čítače 2 //-----------------------------------------------------------------------------//AS2 - přepne zdroj signálu přímo na krystal //sbi(ASSR,AS2) //WGM2x - režim čítače //COM2x - generování signálu OCR //CS2x - vstupní frekvence čítače TCCR2=((0<<WGM20)|(0<
#ifndef DEBUGSTEP for(int i=0; i < Rs485ss.address + 4; i++){ LED1(1); LED2(1); LED3(1); LED4(1); LED5(1); LED6(1); for(int ii=0; ii <50 ;ii++){ wdt_reset(); _delay_ms(1); } LED1(0);
118
LED2(0); LED3(0); LED4(0); LED5(0); LED6(0); for(int ii=0; ii < 50;ii++){ wdt_reset(); _delay_ms(1); } } #endif //-----------------------------------------------------------------------------rs485s_init(); rs485m_init(); sei(); //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Hlavní smyčka programu //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------u8t time_10ms = 0; while(1){ while(time_1ms > 0){ time_10ms++; rs485s(); rs485m(); eprom_1ms(); wd_flo(); if(time_10ms == 10){ led_10ms(); switchChangeCtr(); time_10ms = 0; } time_1ms--; } //Spanek set_sleep_mode(SLEEP_MODE_IDLE); ATOMIC_BLOCK(ATOMIC_FORCEON){ sleep_enable(); } sleep_cpu(); sleep_disable(); } }
119
Příloha 12: Zdrojový kód MCU serveru, modul Rs485m //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Rs485m.h //-----------------------------------------------------------------------------#ifndef RS485M #define RS485M #include "../sys/typedefine.h" //Nastavy porty jao vystupni #define RS485MS_OUTPORT DDRB |= (1<
120
#define RS485M_CMD_ACK 0x88 //-----------------------------------------------------------------------------// Polozka typedef struct{ union{ struct{ u8t LO; u8t HI; }; u16t U16; }; }Trs485mValue; //-----------------------------------------------------------------------------typedef struct{ u8t Sync0; u8t Sync1; u8t Sync2; u8t Adr; u8t Cmd; //u8t Sync3; u8t Blok; u8t Elem; u8t Siz; u8t Crc; union{ Trs485mValue Value[RS485M_SIZE_MAX]; u8t ValueU8[RS485M_SIZE_MAX*2]; }; }Trs485mPaket; //-----------------------------------------------------------------------------typedef enum{ MBITRATE_1200 = 0, MBITRATE_2400, MBITRATE_4800, MBITRATE_9600, MBITRATE_19200, MBITRATE_38400, MBITRATE_57600, MBITRATE_115200 }TMBitrate; //-----------------------------------------------------------------------------typedef enum{ //find start byte RS485M_START_B, //switch header RS485M_HSW, //Qest - Read RS485M_HQ, //Writte RS485M_HW, //Respons RS485M_HR, //ACK RS485M_HA,
}Trs485mRxSw;
121
//-----------------------------------------------------------------------------typedef enum { //Uloz prijate RS485M_RXOK, //pokracuj v prijmu RS485M_RETNOP }Trs485mRxStatus; //-----------------------------------------------------------------------------//RX-TX typedef enum{ RS485M_RXTX_TX_INIT, RS485M_RXTX_TX_WAIT, RS485M_RXTX_TX, RS485M_RXTX_RX_INIT, RS485M_RXTX_RX_WAIT, } Trs485mStatus; //-----------------------------------------------------------------------------typedef struct{ u8vt r_i; u8vt w_i; u8vt used; u8vt ovf; u8vt buf[RS485M_CFIFO_MASK+1]; }Trs485mCFifo; //-----------------------------------------------------------------------------// Nactene teploty typedef struct{ union{ struct{ u8t LO; u8t HI; }; u16t U16; }; u8t readErrI; }TRs485ms_Value; //-----------------------------------------------------------------------------typedef volatile struct { u8t address; //adresa jednotky u8t adrSw; //adresa na prepinacich u8t adrReadMax; //pocet ctenych adres od 1 TMBitrate bitrate; //bitrate TMBitrate bitrateSw; //bitrate na prepinacich u8t readErrmax; //pocet cteni do vyhlaseni poruchy u16t tx_pauza; //hodnota casovace u16t rx_timeout; //hodnota casovace u16vt timer_1ms; //caasovac po 1ms u8t rx_i_while; //povoluje zpracovani buferu podle zaplneni Trs485mPaket Paket; Trs485mRxSw RxSw; Trs485mRxStatus RxStatus;//rx_enum Trs485mStatus Status; volatile Trs485mCFifo CFifo; }TRs485ms; //-----------------------------------------------------------------------------extern TRs485ms Rs485ms; extern TRs485ms_Value Rs485Ram[RS485M_SIZE_RAM]; //Nacitana data //------------------------------------------------------------------------------
122
void rs485m_init(void); //-----------------------------------------------------------------------------void rs485m(void); //-----------------------------------------------------------------------------void rs485m_timer(void); //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Rs485m.c //-----------------------------------------------------------------------------#include //názvy registrů #include //práce s přerušením #include #include "../eprom/eprom.h" #include "../sys/wd_flo.h" #include "../led/led.h" #include "rs485m.h" #include "../rs485s/rs485s.h" //-----------------------------------------------------------------------------TRs485ms_Value Rs485Ram[RS485M_SIZE_RAM] = {}; //Nacitana data //-----------------------------------------------------------------------------TRs485ms Rs485ms = { 1, 0, 31, MBITRATE_9600, MBITRATE_9600, RS485M_READ_ERR_M, 20, 100, 0, 0, {}, RS485M_START_B, RS485M_RETNOP, RS485M_RXTX_TX_INIT, {}, }; //-----------------------------------------------------------------------------static void rs485m_tx_q(void); //-----------------------------------------------------------------------------static void rs485m_rx(void); //-----------------------------------------------------------------------------static void rs485m_rx_r(void); //-----------------------------------------------------------------------------//dotaz prijmut static void rs485m_rx_q(void); //-----------------------------------------------------------------------------//Prijmut Ack static void rs485m_rx_a(void); //-----------------------------------------------------------------------------//zapis prijmut static void rs485m_rx_w(void); //-----------------------------------------------------------------------------//CFIFO //-----------------------------------------------------------------------------static inline void cfifo_write(u8t data);
123
//-----------------------------------------------------------------------------static inline u8t cfifo_read(void); //-----------------------------------------------------------------------------static inline u8t cfifo_read_i(u8t i); //-----------------------------------------------------------------------------static inline void cfifo_free(void); //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------//Vysli dotaz static void rs485m_tx_q(void){ u8t temp = 0; Rs485ms.CFifo.buf[0] = RS485M_SYNC0; Rs485ms.CFifo.buf[1] = RS485M_SYNC1; Rs485ms.CFifo.buf[2] = RS485M_SYNC2_MA; Rs485ms.CFifo.buf[3] = Rs485ms.address; Rs485ms.CFifo.buf[4] = RS485M_CMD_READ; Rs485ms.CFifo.buf[5] = RS485M_SYNC3; Rs485ms.CFifo.buf[6] = 1; Rs485ms.CFifo.buf[7] = 0; Rs485ms.CFifo.buf[8] = 1; //temp crc temp = 0; for(u8t i=1; i < 9; i++){ temp += Rs485ms.CFifo.buf[i]; } Rs485ms.CFifo.buf[9] = ~temp; Rs485ms.CFifo.r_i = 0; Rs485ms.CFifo.used = 10; } //-----------------------------------------------------------------------------//prijmut dotaz static void rs485m_rx_q(void){ cfifo_free(); } //-----------------------------------------------------------------------------//Prijat Write static void rs485m_rx_w(void){ cfifo_free(); } //-----------------------------------------------------------------------------//prijmuta odpoved static void rs485m_rx_r(void){ Rs485Ram[Rs485ms.Paket.Adr].U16 = Rs485ms.Paket.Value[0].U16; cfifo_free(); } //-----------------------------------------------------------------------------//Prijat Ack static void rs485m_rx_a(void){ cfifo_free(); } //-----------------------------------------------------------------------------static void rs485m_rx(void){ u8t tmp = 0; //timeout if(Rs485ms.timer_1ms > Rs485ms.rx_timeout){ Rs485ms.rx_i_while = 0;
124
Rs485ms.RxSw = RS485M_START_B; } //Zpracovavej prijata data while(Rs485ms.CFifo.used > Rs485ms.rx_i_while){ LEDM_RX; //ovf if(Rs485ms.CFifo.ovf){ Rs485ms.CFifo.ovf = 0; LEDM_ERR; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; } //timeout if(Rs485ms.timer_1ms > Rs485ms.rx_timeout){ Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; } switch(Rs485ms.RxSw){ //start byte case RS485M_START_B : if(cfifo_read() == RS485M_SYNC1){ Rs485ms.timer_1ms=0; Rs485ms.rx_i_while = 6; Rs485ms.RxSw++; } break; //****************************** //check header case RS485M_HSW : //kontrola staticke hlavicky if(cfifo_read_i(3) == RS485M_SYNC3){ Rs485ms.Paket.Sync2 = cfifo_read_i(0); Rs485ms.Paket.Cmd = cfifo_read_i(2); Rs485ms.Paket.Siz = cfifo_read_i(6); //Pokud je prekrocena maximalni delka, tak paket ignoruj if(Rs485ms.Paket.Siz > RS485M_SIZE_MAX){ LEDM_ERR; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } if(Rs485ms.Paket.Sync2 == RS485M_SYNC2_MA){ //Q questino - read if(Rs485ms.Paket.Cmd == RS485M_CMD_READ){ Rs485ms.RxSw = RS485M_HQ; Rs485ms.rx_i_while = 7; tmp = 1; } //W write if(Rs485ms.Paket.Cmd == RS485M_CMD_WRITE){ Rs485ms.RxSw = RS485M_HW; Rs485ms.rx_i_while = 7 + (2*Rs485ms.Paket.Siz); tmp = 1; } } if(Rs485ms.Paket.Sync2 == RS485M_SYNC2_Sl){
125
//R response if(Rs485ms.Paket.Cmd == RS485M_CMD_RESPONSE){ Rs485ms.RxSw = RS485M_HR; Rs485ms.rx_i_while = 7 + (2*Rs485ms.Paket.Siz); tmp = 1; } // ACK if(Rs485ms.Paket.Cmd == RS485M_CMD_ACK){ Rs485ms.RxSw = RS485M_HA; Rs485ms.rx_i_while = 7; tmp = 1; } } } //Chceck CMD and Siz if(tmp){ Rs485ms.Paket.Adr = cfifo_read_i(1); Rs485ms.Paket.Blok = cfifo_read_i(4); Rs485ms.Paket.Elem = cfifo_read_i(5); Rs485ms.Paket.Crc = RS485M_SYNC1 + Rs485ms.Paket.Sync2 + Rs485ms.Paket.Adr + Rs485ms.Paket.Cmd + RS485M_SYNC3 + Rs485ms.Paket.Blok + Rs485ms.Paket.Elem + Rs485ms.Paket.Siz; }else{ LEDM_ERR; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; } break; //****************************** //-----------------------------------------------------------------------------//****************************** //check paket Question - read case RS485M_HQ : //check crc Rs485ms.Paket.Crc = ~Rs485ms.Paket.Crc; if(cfifo_read_i(7) != Rs485ms.Paket.Crc){ LEDM_ERR; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } //check adress if(Rs485ms.Paket.Adr != Rs485ms.address){ cfifo_free(); Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } RS485M_RXD; rs485m_rx_q(); Rs485ms.RxStatus = RS485M_RETNOP; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; return; //quest end //****************************** //-----------------------------------------------------------------------------//******************************
126
//check paket Write case RS485M_HW : //value tmp = 7; for(u8t i=0; i< (Rs485ms.Paket.Siz*2); i++){ Rs485ms.Paket.ValueU8[i] = cfifo_read_i(tmp++); Rs485ms.Paket.Crc += Rs485ms.Paket.ValueU8[i]; } //check crc Write Rs485ms.Paket.Crc = ~Rs485ms.Paket.Crc; if(cfifo_read_i(tmp) != Rs485ms.Paket.Crc){ LEDM_ERR; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } //adressa if(Rs485ms.Paket.Adr != Rs485ms.address){ cfifo_free(); Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } RS485M_RXD; rs485m_rx_w(); Rs485ms.RxStatus = RS485M_RETNOP; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; return; //Write end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket RESPONS case RS485M_HR : //value tmp = 7; for(u8t i=0; i< (Rs485ms.Paket.Siz*2); i++){ Rs485ms.Paket.ValueU8[i] = cfifo_read_i(tmp++); Rs485ms.Paket.Crc += Rs485ms.Paket.ValueU8[i]; } //check crc response Rs485ms.Paket.Crc = ~Rs485ms.Paket.Crc; if(cfifo_read_i(tmp) != Rs485ms.Paket.Crc){ LEDM_ERR; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } //check adress if(Rs485ms.Paket.Adr != Rs485ms.address){ cfifo_free(); Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } //RS485M_RXD; rs485m_rx_r(); Rs485ms.RxStatus = RS485M_RXOK; Rs485ms.rx_i_while = 0;
127
Rs485ms.RxSw = RS485M_START_B; return; //respons end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket ACK case RS485M_HA : //check crc response Rs485ms.Paket.Crc = ~Rs485ms.Paket.Crc; if(cfifo_read_i(7) != Rs485ms.Paket.Crc){ LEDM_ERR; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } //check adress if(Rs485ms.Paket.Adr != Rs485ms.address){ cfifo_free(); Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; break; } //RS485M_RXD; rs485m_rx_a(); Rs485ms.RxStatus = RS485M_RETNOP; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; return; //ACK end //****************************** //-----------------------------------------------------------------------------default : Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; wd_reset_cpu = 1; break; } } Rs485ms.RxStatus = RS485M_RETNOP; return; } //********** end rs485m_rx ********** //-----------------------------------------------------------------------------void rs485m(void){ switch (Rs485ms.Status){ //inicializuj vysilac case RS485M_RXTX_TX_INIT : WD_R(1); rs485m_tx_q(); Rs485ms.timer_1ms = 0; Rs485ms.Status++; break; //cekej case RS485M_RXTX_TX_WAIT : if( Rs485ms.timer_1ms < Rs485ms.tx_pauza) break; RS485M_TXEA; RS485M_TXEB;
128
LEDM_TX; Rs485ms.Status++; break; //vysilej case RS485M_RXTX_TX : break; //zapni prijem case RS485M_RXTX_RX_INIT : Rs485ms.timer_1ms = 0; Rs485ms.rx_i_while = 0; Rs485ms.RxSw = RS485M_START_B; Rs485ms.Status++; break; //cekej na prijatou odpoved, nebo na vyprseni timeoutu case RS485M_RXTX_RX_WAIT : rs485m_rx(); if(Rs485ms.RxStatus == RS485M_RXOK){ //vynuluj citadlo chyb Rs485Ram[Rs485ms.address].readErrI = 0; Rs485ms.address++; if( Rs485ms.address > Rs485ms.adrReadMax ) Rs485ms.address = 1; Rs485ms.Status = RS485M_RXTX_TX_INIT; break; } //timeout if( Rs485ms.timer_1ms > Rs485ms.rx_timeout ){ RS485M_RXD; LEDM_ERR; //inkrementuj pocitadlo chyb if( Rs485Ram[Rs485ms.address].readErrI < Rs485ms.readErrmax ){ Rs485Ram[Rs485ms.address].readErrI++; }else{ Rs485Ram[Rs485ms.address].U16 = RS485M_READ_ERR_VAL; } Rs485ms.address++; if( Rs485ms.address > Rs485ms.adrReadMax ) Rs485ms.address = 1; Rs485ms.Status = RS485M_RXTX_TX_INIT; break; } break; //-----------------------------------------------------------------------------default : Rs485ms.address = 0; Rs485ms.Status = RS485M_RXTX_TX_INIT; wd_reset_cpu = 1; break; //-----------------------------------------------------------------------------} } //-----------------------------------------------------------------------------void rs485m_timer(void){ if(Rs485ms.timer_1ms < U16TOP)Rs485ms.timer_1ms++; } //-----------------------------------------------------------------------------// Nastavení sériové linky //------------------------------------------------------------------------------
129
static void uart_1200(void){ #define BAUD 1200 #include UBRR1H = UBRRH_VALUE; UBRR1L = UBRRL_VALUE; #if USE_2X UCSR1A |= (1 << U2X0); #else UCSR1A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_2400(void){ #undef BAUD // avoid compiler warning #define BAUD 2400 #include UBRR1H = UBRRH_VALUE; UBRR1L = UBRRL_VALUE; #if USE_2X UCSR1A |= (1 << U2X0); #else UCSR1A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_4800(void){ #undef BAUD // avoid compiler warning #define BAUD 4800 #include UBRR1H = UBRRH_VALUE; UBRR1L = UBRRL_VALUE; #if USE_2X UCSR1A |= (1 << U2X0); #else UCSR1A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_9600(void){ #undef BAUD // avoid compiler warning #define BAUD 9600 #include UBRR1H = UBRRH_VALUE; UBRR1L = UBRRL_VALUE; #if USE_2X UCSR1A |= (1 << U2X0); #else UCSR1A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_19200(void){ #undef BAUD // avoid compiler warning #define BAUD 19200 #include UBRR1H = UBRRH_VALUE; UBRR1L = UBRRL_VALUE; #if USE_2X UCSR1A |= (1 << U2X0); #else
130
UCSR1A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_38400(void){ #undef BAUD // avoid compiler warning #define BAUD 38400 #include UBRR1H = UBRRH_VALUE; UBRR1L = UBRRL_VALUE; #if USE_2X UCSR1A |= (1 << U2X0); #else UCSR1A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------// Přerušení od příjmu znaku //-----------------------------------------------------------------------------ISR(USART1_RXC_vect ){ u8t temp = 0; temp |= (UCSR1A & ((1<= Rs485ms.CFifo.used) RS485M_TXEND; } //-----------------------------------------------------------------------------// Přerušení od odeslání znaku //-----------------------------------------------------------------------------ISR(USART1_TXC_vect){ u8vt dummy; Rs485ms.CFifo.r_i = 0; Rs485ms.CFifo.w_i = 0; Rs485ms.CFifo.used = 0; Rs485ms.CFifo.ovf = 0; while ( UCSR1A & _BV(RXC1) ){ dummy = UDR1; } RS485M_RXE; Rs485ms.Status = RS485M_RXTX_RX_INIT;
131
} //-----------------------------------------------------------------------------//CFIFO //-----------------------------------------------------------------------------static inline void cfifo_write(u8t data){ ATOMIC_BLOCK(ATOMIC_FORCEON){ Rs485ms.CFifo.buf[Rs485ms.CFifo.w_i++] = data; Rs485ms.CFifo.w_i &= RS485M_CFIFO_MASK; Rs485ms.CFifo.used = RS485M_CFIFO_MASK & (Rs485ms.CFifo.w_i - Rs485ms.CFifo.r_i); if(Rs485ms.CFifo.used == RS485M_CFIFO_MASK) { //preteceni Rs485ms.CFifo.ovf = 1; Rs485ms.CFifo.r_i = 0; Rs485ms.CFifo.w_i = 0; Rs485ms.CFifo.used = 0; } } } //-----------------------------------------------------------------------------static inline u8t cfifo_read(void){ u8t temp; ATOMIC_BLOCK(ATOMIC_FORCEON){ temp = Rs485ms.CFifo.buf[Rs485ms.CFifo.r_i++]; Rs485ms.CFifo.r_i &= RS485M_CFIFO_MASK; Rs485ms.CFifo.used = RS485M_CFIFO_MASK & (Rs485ms.CFifo.w_i - Rs485ms.CFifo.r_i); } return temp; } //-----------------------------------------------------------------------------static inline u8t cfifo_read_i(u8t i){ u8t temp; ATOMIC_BLOCK(ATOMIC_FORCEON){ temp = Rs485ms.CFifo.buf[RS485M_CFIFO_MASK & (Rs485ms.CFifo.r_i + i)]; } return temp; } //-----------------------------------------------------------------------------static inline void cfifo_free(void){ ATOMIC_BLOCK(ATOMIC_FORCEON){ Rs485ms.CFifo.r_i = 0; Rs485ms.CFifo.w_i = 0; Rs485ms.CFifo.used = 0; Rs485ms.CFifo.ovf = 0; } } //-----------------------------------------------------------------------------void rs485m_init(void){ UCSR1B = ((0<
132
uart_4800(); if(Rs485ms.bitrate == MBITRATE_9600) uart_9600(); if(Rs485ms.bitrate == MBITRATE_19200) uart_19200(); if(Rs485ms.bitrate == MBITRATE_38400) uart_38400(); for(u8t i = 0; i
Příloha 13: Zdrojový kód MCU serveru, modul Rs485s //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Rs485s.h //-----------------------------------------------------------------------------#ifndef RS485S #define RS485S #include "../sys/typedefine.h" #define RS485S_READ_NO_EXIST 0x8003 #define RS485SS_OUTPORT DDRD |= (1<
133
#define RS485S_SYNC0 0xff #define RS485S_SYNC1 0x0a #define RS485S_SYNC2_MA 0x55 #define RS485S_SYNC2_Sl 0x53 #define RS485S_SYNC3 0x71 #define RS485S_CMD_READ 0x81 #define RS485S_CMD_WRITE 0x82 #define RS485S_CMD_RESPONSE 0x84 #define RS485S_CMD_ACK 0x88 //-----------------------------------------------------------------------------// Polozka typedef struct{ union{ struct{ u8t LO; u8t HI; }; u16t U16; }; }Trs485sValue; //-----------------------------------------------------------------------------typedef struct{ u8t Sync0; u8t Sync1; u8t Sync2; u8t Adr; u8t Cmd; //u8t Sync3; u8t Blok; u8t Elem; u8t Siz; u8t Crc; union{ Trs485sValue Value[RS485S_SIZE_MAX]; u8t ValueU8[RS485S_SIZE_MAX*2]; }; }Trs485sPaket; //-----------------------------------------------------------------------------typedef enum{ SBITRATE_1200 = 0, SBITRATE_2400, SBITRATE_4800, SBITRATE_9600, SBITRATE_19200, SBITRATE_38400, SBITRATE_57600, SBITRATE_115200 }TBitrate; //-----------------------------------------------------------------------------typedef enum{ //find start byte RS485S_START_B, //switch header RS485S_HSW, //Qest - Read RS485S_HQ,
134
//Writte RS485S_HW, //Respons RS485S_HR, //ACK RS485S_HA, }Trs485sRxSw; //-----------------------------------------------------------------------------typedef enum { //odvysilej bufer RS485S_RETTX, //pokracuj v prijmu RS485S_RETNOP }Trs485sRxStatus; //-----------------------------------------------------------------------------//RX-TX typedef enum{ //prijimej RS485S_RXTX_RECV, //prijmuto RS485S_RXTX_RECV_COMPLET, //prodleva RS485S_RXTX_TX_WAIT, //priprav vysilani RS485S_RXTX_TX_START, //vysilej RS485S_TX_TX } Trs485sStatus; //-----------------------------------------------------------------------------typedef struct{ u8vt r_i; u8vt w_i; u8vt used; u8vt ovf; u8vt buf[RS485S_CFIFO_MASK+1]; }Trs485sCFifo; //-----------------------------------------------------------------------------typedef volatile struct { u8t address; //adresa jednotky u8t adrSw; //adresa na prepinacich TBitrate bitrate; //bitrate TBitrate bitrateSw; //bitrate na prepinacich u16t tx_pauza; //hodnota casovace u16t rx_timeout; //hodnota casovace u16vt timer_1ms; //caasovac po 1ms u8t rx_i_while; //povoluje zpracovani buferu podle zaplneni Trs485sPaket Paket; Trs485sRxSw RxSw; Trs485sRxStatus RxStatus;//rx_enum Trs485sStatus Status; volatile Trs485sCFifo CFifo; }TRs485ss; extern TRs485ss Rs485ss; //-----------------------------------------------------------------------------void rs485s_init(void);
135
//-----------------------------------------------------------------------------void rs485s(void); //-----------------------------------------------------------------------------void rs485s_timer(void); //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Rs485s.c //-----------------------------------------------------------------------------#include //názvy registrů #include //práce s přerušením #include #include #include #include "../eprom/eprom.h" #include "../sys/wd_flo.h" #include "../led/led.h" #include "rs485s.h" #include "../rs485m/rs485m.h" TRs485ss Rs485ss = { 0, 0, SBITRATE_9600, SBITRATE_9600, 5, 100, 0, 0, {}, RS485S_START_B, RS485S_RETTX, RS485S_RXTX_RECV, {}, }; //-----------------------------------------------------------------------------static void rs485s_rx(void); //-----------------------------------------------------------------------------static void rs485s_rx_r(void); //-----------------------------------------------------------------------------//dotaz prijmut static void rs485s_rx_q(void); //-----------------------------------------------------------------------------//Prijmut Ack static void rs485s_rx_a(void); //-----------------------------------------------------------------------------//zapis prijmut static void rs485s_rx_w(void); //-----------------------------------------------------------------------------//CFIFO //-----------------------------------------------------------------------------static inline void cfifo_write(u8t data); //-----------------------------------------------------------------------------static inline u8t cfifo_read(void); //-----------------------------------------------------------------------------static inline u8t cfifo_read_i(u8t i); //------------------------------------------------------------------------------
136
static inline void cfifo_free(void); //-----------------------------------------------------------------------------//prijmut dotaz static void rs485s_rx_q(void){ u8t temp = 0; u8t pi; Rs485ss.CFifo.buf[0] = RS485S_SYNC0; Rs485ss.CFifo.buf[1] = RS485S_SYNC1; Rs485ss.CFifo.buf[2] = RS485S_SYNC2_Sl; Rs485ss.CFifo.buf[3] = Rs485ss.address; Rs485ss.CFifo.buf[4] = RS485S_CMD_RESPONSE; Rs485ss.CFifo.buf[5] = RS485S_SYNC3; Rs485ss.CFifo.buf[6] = Rs485ss.Paket.Blok; Rs485ss.CFifo.buf[7] = Rs485ss.Paket.Elem; Rs485ss.CFifo.buf[8] = Rs485ss.Paket.Siz; pi = 9;
//jdeli o cteni z ram if( (Rs485ss.Paket.Blok == 1) && ( (Rs485ss.Paket.Elem + Rs485ss.Paket.Siz) <= (sizeof(Rs485Ram)/sizeof(u16t))) && (Rs485ss.Paket.Elem <= Rs485ms.adrReadMax)) { for(u8t i=0; i< Rs485ss.Paket.Siz; i++){ Rs485ss.CFifo.buf[pi++] = Rs485Ram[Rs485ss.Paket.Elem + i].LO; Rs485ss.CFifo.buf[pi++] = Rs485Ram[Rs485ss.Paket.Elem + i].HI; } temp = 1; } //jde o cteni z eprom if( (Rs485ss.Paket.Blok == 255) && ( (Rs485ss.Paket.Elem + Rs485ss.Paket.Siz) <= (sizeof(EpromSetRam)/sizeof(u16t))) ){ for(u8t i=0; i < Rs485ss.Paket.Siz; i++){ Rs485ss.CFifo.buf[pi++] = EpromSetRam.Value[Rs485ss.Paket.Elem + i].LO; Rs485ss.CFifo.buf[pi++] = EpromSetRam.Value[Rs485ss.Paket.Elem + i].HI; } temp = 1; } //neznama polozka if(temp == 0){ Rs485ss.CFifo.buf[pi++] =RS485S_READ_NO_EXIST&0xff; Rs485ss.CFifo.buf[pi++] =RS485S_READ_NO_EXIST>>8; } //temp crc temp = 0; for(u8t i=1; i < pi; i++){ temp += Rs485ss.CFifo.buf[i]; } Rs485ss.CFifo.buf[pi++] = ~temp; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.used = pi; } //-----------------------------------------------------------------------------//Prijat Write static void rs485s_rx_w(void){ u8t temp = 0;
137
Rs485ss.CFifo.buf[0] = RS485S_SYNC0; Rs485ss.CFifo.buf[1] = RS485S_SYNC1; Rs485ss.CFifo.buf[2] = RS485S_SYNC2_Sl; Rs485ss.CFifo.buf[3] = Rs485ss.address; Rs485ss.CFifo.buf[4] = RS485S_CMD_ACK; Rs485ss.CFifo.buf[5] = RS485S_SYNC3; Rs485ss.CFifo.buf[6] = Rs485ss.Paket.Blok; Rs485ss.CFifo.buf[7] = Rs485ss.Paket.Elem; Rs485ss.CFifo.buf[8] = Rs485ss.Paket.Siz; //jde o zapis do eprom if( (Rs485ss.Paket.Blok == 0xff) && ( (Rs485ss.Paket.Elem + Rs485ss.Paket.Siz) <= (sizeof(EpromSetRam)/sizeof(u16t)))){ for(u8t i=0; i < Rs485ss.Paket.Siz; i++){ EpromSetRam.U16[Rs485ss.Paket.Elem +i] = Rs485ss.Paket.Value[i].U16; } }else{ // no exist Rs485ss.CFifo.buf[8] = 0; }
//temp crc temp = 0; for(u8t i=1; i < 9; i++){ temp += Rs485ss.CFifo.buf[i]; } Rs485ss.CFifo.buf[9] = ~temp; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.used = 10; } //-----------------------------------------------------------------------------//prijmuta odpoved static void rs485s_rx_r(void){ cfifo_free(); } //-----------------------------------------------------------------------------//Prijat Ack static void rs485s_rx_a(void){ cfifo_free(); } //-----------------------------------------------------------------------------static void rs485s_rx(void){ u8t tmp = 0; WD_R(0); //timeout if(Rs485ss.timer_1ms > Rs485ss.rx_timeout){ Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; } //Zpracovavej prijata data while(Rs485ss.CFifo.used > Rs485ss.rx_i_while){ LEDS_RX; //ovf if(Rs485ss.CFifo.ovf){ Rs485ss.CFifo.ovf = 0; LEDS_ERR; Rs485ss.rx_i_while = 0;
138
Rs485ss.RxSw = RS485S_START_B; } //timeout if(Rs485ss.timer_1ms > Rs485ss.rx_timeout){ Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; } switch(Rs485ss.RxSw){ //start byte case RS485S_START_B : if(cfifo_read() == RS485S_SYNC1){ Rs485ss.timer_1ms=0; Rs485ss.rx_i_while = 6; Rs485ss.RxSw++; } break; //****************************** //check header case RS485S_HSW : //kontrola staticke hlavicky if(cfifo_read_i(3) == RS485S_SYNC3){ Rs485ss.Paket.Sync2 = cfifo_read_i(0); Rs485ss.Paket.Cmd = cfifo_read_i(2); Rs485ss.Paket.Siz = cfifo_read_i(6); //Pokud je prekrocena maximalni delka, tak paket ignoruj if(Rs485ss.Paket.Siz > RS485S_SIZE_MAX){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } if(Rs485ss.Paket.Sync2 == RS485S_SYNC2_MA){ //Q questino - read if(Rs485ss.Paket.Cmd == RS485S_CMD_READ){ Rs485ss.RxSw = RS485S_HQ; Rs485ss.rx_i_while = 7; tmp = 1; } //W write if(Rs485ss.Paket.Cmd == RS485S_CMD_WRITE){ Rs485ss.RxSw = RS485S_HW; Rs485ss.rx_i_while = 7 + (2*Rs485ss.Paket.Siz); tmp = 1; } } if(Rs485ss.Paket.Sync2 == RS485S_SYNC2_Sl){ //R response if(Rs485ss.Paket.Cmd == RS485S_CMD_RESPONSE){ Rs485ss.RxSw = RS485S_HR; Rs485ss.rx_i_while = 7 + (2*Rs485ss.Paket.Siz); tmp = 1; } // ACK if(Rs485ss.Paket.Cmd == RS485S_CMD_ACK){ Rs485ss.RxSw = RS485S_HA; Rs485ss.rx_i_while = 7; tmp = 1; } }
139
} //Chceck CMD and Siz if(tmp){ Rs485ss.Paket.Adr = cfifo_read_i(1); Rs485ss.Paket.Blok = cfifo_read_i(4); Rs485ss.Paket.Elem = cfifo_read_i(5); Rs485ss.Paket.Crc = RS485S_SYNC1 + Rs485ss.Paket.Sync2 + Rs485ss.Paket.Adr + Rs485ss.Paket.Cmd + RS485S_SYNC3 + Rs485ss.Paket.Blok + Rs485ss.Paket.Elem + Rs485ss.Paket.Siz; }else{ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; } break; //****************************** //-----------------------------------------------------------------------------//****************************** //check paket Question - read case RS485S_HQ : //check crc Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(7) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //check adress if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } RS485S_RXD; rs485s_rx_q(); Rs485ss.RxStatus = RS485S_RETTX; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //quest end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket Write case RS485S_HW : //value tmp = 7; for(u8t i=0; i< (Rs485ss.Paket.Siz*2); i++){ Rs485ss.Paket.ValueU8[i] = cfifo_read_i(tmp++); Rs485ss.Paket.Crc += Rs485ss.Paket.ValueU8[i]; } //check crc Write Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(tmp) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break;
140
} //adressa if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } RS485S_RXD; rs485s_rx_w(); Rs485ss.RxStatus = RS485S_RETTX; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //Write end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket RESPONS case RS485S_HR : //value tmp = 7; for(u8t i=0; i< (Rs485ss.Paket.Siz*2); i++){ Rs485ss.Paket.ValueU8[i] = cfifo_read_i(tmp++); Rs485ss.Paket.Crc += Rs485ss.Paket.ValueU8[i]; } //check crc response Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(tmp) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //check adress if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //RS485S_RXD; rs485s_rx_r(); Rs485ss.RxStatus = RS485S_RETNOP; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //respons end //****************************** //-----------------------------------------------------------------------------//****************************** //check paket ACK case RS485S_HA : //check crc response Rs485ss.Paket.Crc = ~Rs485ss.Paket.Crc; if(cfifo_read_i(7) != Rs485ss.Paket.Crc){ LEDS_ERR; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break;
141
} //check adress if(Rs485ss.Paket.Adr != Rs485ss.address){ cfifo_free(); Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; break; } //RS485S_RXD; rs485s_rx_a(); Rs485ss.RxStatus = RS485S_RETNOP; Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; return; //ACK end //****************************** //-----------------------------------------------------------------------------default : Rs485ss.rx_i_while = 0; Rs485ss.RxSw = RS485S_START_B; wd_reset_cpu = 1; break; } } Rs485ss.RxStatus = RS485S_RETNOP; return; } //********** end rs485s_rx ********** //-----------------------------------------------------------------------------void rs485s(void){ switch (Rs485ss.Status){ //prijimej dokud nedostanes question/ACK case RS485S_RXTX_RECV : rs485s_rx(); if(Rs485ss.RxStatus == RS485S_RETTX) Rs485ss.Status++; else break; //zakaz prijem, timeout TX case RS485S_RXTX_RECV_COMPLET : //RS485S_RXD; Rs485ss.timer_1ms=0; Rs485ss.Status++; //pockej a pak zacni vysilat case RS485S_RXTX_TX_WAIT : if(Rs485ss.timer_1ms>Rs485ss.tx_pauza) Rs485ss.Status++; else break; //nastartuj vysilani case RS485S_RXTX_TX_START : RS485S_TXEA; RS485S_TXEB; LEDS_TX; Rs485ss.Status++; //pockej na odvysilani
142
case RS485S_TX_TX : break; //-----------------------------------------------------------------------------default : RS485S_RXE; Rs485ss.Status = RS485S_RXTX_RECV; wd_reset_cpu = 1; break; } } //-----------------------------------------------------------------------------void rs485s_timer(void){ if(Rs485ss.timer_1ms < U16TOP)Rs485ss.timer_1ms++; } //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Nastavení sériové linky //-----------------------------------------------------------------------------static void uart_1200(void){ #define BAUD 1200 #include UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_2400(void){ #undef BAUD // avoid compiler warning #define BAUD 2400 #include UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_4800(void){ #undef BAUD // avoid compiler warning #define BAUD 4800 #include UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_9600(void){ #undef BAUD // avoid compiler warning
143
#define BAUD 9600 #include UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_19200(void){ #undef BAUD // avoid compiler warning #define BAUD 19200 #include UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------static void uart_38400(void){ #undef BAUD // avoid compiler warning #define BAUD 38400 #include UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif } //-----------------------------------------------------------------------------// Přerušení od příjmu znaku //-----------------------------------------------------------------------------ISR(USART0_RXC_vect ){ u8t temp = 0; temp |= (UCSR0A & ((1<
144
ISR(USART0_UDRE_vect){ UDR0 = Rs485ss.CFifo.buf[Rs485ss.CFifo.r_i++]; if(Rs485ss.CFifo.r_i >= Rs485ss.CFifo.used) RS485S_TXEND; } //-----------------------------------------------------------------------------// Přerušení od odeslání znaku //-----------------------------------------------------------------------------ISR(USART0_TXC_vect){ u8vt dummy; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.w_i = 0; Rs485ss.CFifo.used = 0; Rs485ss.CFifo.ovf = 0; while ( UCSR0A & _BV(RXC0) ){ dummy = UDR0; } RS485S_RXE; Rs485ss.Status = RS485S_RXTX_RECV; } //-----------------------------------------------------------------------------//CFIFO //-----------------------------------------------------------------------------static inline void cfifo_write(u8t data){ ATOMIC_BLOCK(ATOMIC_FORCEON){ Rs485ss.CFifo.buf[Rs485ss.CFifo.w_i++] = data; Rs485ss.CFifo.w_i &= RS485S_CFIFO_MASK; Rs485ss.CFifo.used = RS485S_CFIFO_MASK & (Rs485ss.CFifo.w_i - Rs485ss.CFifo.r_i); if(Rs485ss.CFifo.used == RS485S_CFIFO_MASK) { //preteceni Rs485ss.CFifo.ovf = 1; Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.w_i = 0; Rs485ss.CFifo.used = 0; } } } //-----------------------------------------------------------------------------static inline u8t cfifo_read(void){ u8t temp; ATOMIC_BLOCK(ATOMIC_FORCEON){ temp = Rs485ss.CFifo.buf[Rs485ss.CFifo.r_i++]; Rs485ss.CFifo.r_i &= RS485S_CFIFO_MASK; Rs485ss.CFifo.used = RS485S_CFIFO_MASK & (Rs485ss.CFifo.w_i - Rs485ss.CFifo.r_i); } return temp; } //-----------------------------------------------------------------------------static inline u8t cfifo_read_i(u8t i){ u8t temp; ATOMIC_BLOCK(ATOMIC_FORCEON){ temp = Rs485ss.CFifo.buf[RS485S_CFIFO_MASK & (Rs485ss.CFifo.r_i + i)]; } return temp;
145
} //-----------------------------------------------------------------------------static inline void cfifo_free(void){ ATOMIC_BLOCK(ATOMIC_FORCEON){ Rs485ss.CFifo.r_i = 0; Rs485ss.CFifo.w_i = 0; Rs485ss.CFifo.used = 0; Rs485ss.CFifo.ovf = 0; } } //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------void rs485s_init(void){ UCSR0B = ((0<
Příloha 14: Zdrojový kód MCU serveru, modul eprom //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// eprom.h //-----------------------------------------------------------------------------#ifndef EPROM_H #define EPROM_H #include "../sys/porty_def.h" #include "../sys/typedefine.h" //-----------------------------------------------------------------------------// Polozka typedef struct{ union{ struct{ u8t LO; u8t HI; }; u16t U16; }; }TEpromValue; //------------------------------------------------------------------------------
146
//Nastaveni typedef union{ struct{ u16t CMD; u16t MaBitrate; u16t MaTxPause; u16t MaRxTimeout; u16t MaAdrReadMax; u16t MaErrReadMax; u16t SlAddr; u16t SlBitrate; u16t SlTxPause; u16t SlRxTimeout; u16t MesPause; s16t TempOffset[1]; }; u8t U8[1]; u16t U16[1]; TEpromValue Value[1]; }TEpromSet; //-----------------------------------------------------------------------------extern TEpromSet EpromSetRom; extern TEpromSet EpromSetRam; //-----------------------------------------------------------------------------void eprom_1ms(void); //-----------------------------------------------------------------------------void eprom_load(void); //-----------------------------------------------------------------------------void eprom_toModule(void); //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// eprom.c //-----------------------------------------------------------------------------#include #include #include "../sys/wd_flo.h" #include "../rs485s/rs485s.h" #include "../rs485m/rs485m.h" #include "eprom.h" //-----------------------------------------------------------------------------//Cyklus eprom updateru typedef enum{ EPROM_REDY, EPROM_UPD, EPROM_UPD_BYTE, EPROM_LD, EPROM_LD_WORD }TEpromSw; //------------------------------------------------------------------------------
147
typedef enum{ EPROM_NOP = 0, EPROM_RESET, EPROM_RAMTOROM, EPROM_ROMTORAM, EPROM_RAMTOMODULE, EPROM_LOADDEFAULT, }TEpromCMD; //-----------------------------------------------------------------------------typedef struct{ u16t ActB_i; //Aktualni byte TEpromSw sw; }TEprom; //-----------------------------------------------------------------------------TEprom Eprom = { 0, EPROM_REDY }; //-----------------------------------------------------------------------------//Struktura nastaveni v EEPROM TEpromSet EpromSetRom EEMEM = { { 0, //CMD - Neni ukladan; (u16t)SBITRATE_9600, //MaBitrate; 100, //MaTxPause; 100, //MaRxTimeout; 31, //MaAdrReadMax; 3, //MaErrReadMax; 1, //SlAddr; (u16t)SBITRATE_9600, //SlBitrate; 5, //SlTxPause; 100, //SlRxTimeout; 1000, {0}
//MesPause; //TempOffset[DS18B20_N];
} }; //-----------------------------------------------------------------------------TEpromSet EpromSetRam = { { 0, //CMD - Neni ukladan; (u16t)SBITRATE_9600, //MaBitrate; 100, //MaTxPause; 100, //MaRxTimeout; 31, //MaAdrReadMax; 3, //MaErrReadMax; 1, //SlAddr; (u16t)SBITRATE_9600, //SlBitrate; 5, //SlTxPause; 100, //SlRxTimeout; 1000, {0}
//MesPause; //TempOffset[DS18B20_N];
} }; //-----------------------------------------------------------------------------void eprom_update_b(u16t adr, u8t dat){
148
if(dat != eeprom_read_byte( (u8t *)adr) ){ eeprom_write_byte((u8t *)adr, (u8t)dat); } } //-----------------------------------------------------------------------------void eprom_load(void){ for(u16t i=0; i < sizeof(EpromSetRam); i++){ EpromSetRam.U8[i] = eeprom_read_byte((u8t*)i); } } //-----------------------------------------------------------------------------void eprom_1ms(void){ switch(Eprom.sw){ //------------------case EPROM_REDY: WD_R(2); if(EpromSetRam.CMD == EPROM_RESET){ wdt_enable(WDTO_15MS); while(1); } if(EpromSetRam.CMD == EPROM_RAMTOROM) Eprom.sw = EPROM_UPD; if(EpromSetRam.CMD == EPROM_ROMTORAM) Eprom.sw = EPROM_LD; if(EpromSetRam.CMD == EPROM_RAMTOMODULE){ eprom_toModule(); EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY; } if(EpromSetRam.CMD == EPROM_LOADDEFAULT){ EpromSetRam.MaBitrate = SBITRATE_9600; EpromSetRam.MaTxPause = 100; EpromSetRam.MaRxTimeout = RS485SS_BASIC_TIMEOUT; EpromSetRam.MaAdrReadMax = 31; EpromSetRam.MaErrReadMax = 3; EpromSetRam.SlAddr = 1; EpromSetRam.SlBitrate = SBITRATE_9600; EpromSetRam.SlTxPause = 5; EpromSetRam.SlRxTimeout = RS485SS_BASIC_TIMEOUT; EpromSetRam.MesPause = 1000; EpromSetRam.TempOffset[0] = 0; eprom_toModule(); EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY; } break; //------------------case EPROM_UPD: Eprom.ActB_i = 0; Eprom.sw++; break; //------------------case EPROM_UPD_BYTE: if( !eeprom_is_ready() )return; eprom_update_b(Eprom.ActB_i, EpromSetRam.U8[Eprom.ActB_i]); Eprom.ActB_i++; if(Eprom.ActB_i == sizeof(EpromSetRam)){ EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY;
149
} break; //------------------case EPROM_LD: Eprom.ActB_i = 0; Eprom.sw++; break; //------------------case EPROM_LD_WORD: if( !eeprom_is_ready() )return; EpromSetRam.U8[Eprom.ActB_i] = eeprom_read_byte((u8t*)(Eprom.ActB_i)); Eprom.ActB_i++; if(Eprom.ActB_i == sizeof(EpromSetRam)){ EpromSetRam.CMD = EPROM_NOP; Eprom.sw = EPROM_REDY; } break; //------------------default: Eprom.sw = EPROM_REDY; } } //-----------------------------------------------------------------------------void eprom_toModule(void){ Rs485ms.bitrate = EpromSetRam.MaBitrate; Rs485ms.tx_pauza = EpromSetRam.MaTxPause; Rs485ms.rx_timeout = EpromSetRam.MaRxTimeout; if(EpromSetRam.MaAdrReadMax > RS485M_SIZE_RAM){ Rs485ms.adrReadMax = RS485M_SIZE_RAM; }else{ Rs485ms.adrReadMax = EpromSetRam.MaAdrReadMax; } if(EpromSetRam.MaErrReadMax > U8TOP) Rs485ms.readErrmax = U8TOP; else{ Rs485ms.readErrmax = EpromSetRam.MaErrReadMax; } if(!Rs485ss.adrSw){ Rs485ss.address = EpromSetRam.SlAddr; Rs485ss.bitrate = EpromSetRam.SlBitrate; }else{ Rs485ss.address = Rs485ss.adrSw; Rs485ss.bitrate = Rs485ss.bitrateSw; } Rs485ss.tx_pauza = EpromSetRam.SlTxPause; if(EpromSetRam.SlRxTimeout < RS485SS_BASIC_TIMEOUT/(Rs485ss.bitrate+1)){ Rs485ss.rx_timeout = (RS485SS_BASIC_TIMEOUT/(Rs485ss.bitrate+1)) + 20; }else{ Rs485ss.rx_timeout = EpromSetRam.SlRxTimeout; } // Ds18b20[0].Set.MesPause = EpromSetRam.MesPause; // Ds18b20[0].Set.TempOfs = EpromSetRam.TempOffset[0]; }
150
Příloha 15: Zdrojový kód MCU serveru, modul wd_flo //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// wd_flo.h //-----------------------------------------------------------------------------#ifndef WD_FLO #define WD_FLO #include "../sys/typedefine.h" //pocet wd hlidacu #define WD_N 3 //reset hlidace #define WD_R(i) wd_i[i] = 0 //-----------------------------------------------------------------------------void wd_flo(void); //-----------------------------------------------------------------------------//pocitadlo , 10mS extern u8vt wd_i[WD_N]; //top extern u8ct wd_im[WD_N]; //-----------------------------------------------------------------------------extern u8vt wd_reset_cpu; //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// wd_flo.c //-----------------------------------------------------------------------------#include #include "wd_flo.h" void wd_flo(void); //registry pro poc u8vt wd_i[WD_N] = {0}; //strop pro vyhodnoceni wdr v 10x mS 255- vypnuto u8ct wd_im[WD_N] ={ 250, //rs485s 250, //rs485m 250, //eprom }; //-----------------------------------------------------------------------------u8vt wd_reset_cpu = 0; //-----------------------------------------------------------------------------void wd_flo(void){ static unsigned char div =0; if(wd_reset_cpu){ wdt_enable(WDTO_15MS); while(1); return; } if(div == 5){ for(register unsigned char i = 0; i< WD_N; i++){ if(wd_i[i] < 255)wd_i[i]++; }
151
} if(div > 9){ div = 0; for(register unsigned char i = 0; i< WD_N; i++){ if(wd_i[i] > wd_im[i]){ wdt_enable(WDTO_15MS); while(1); return; } } wdt_reset(); } div++; }
Příloha 16: Zdrojový kód MCU serveru, modul led //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// led.h //-----------------------------------------------------------------------------#ifndef LED_H #define LED_H #include "../sys/typedefine.h" #include "../sys/porty_def.h" #define NUMOFLED 7 //Run #define LED0(outx) OUTPORT_PORT_DDR((outx),(1),PORTC,DDRC,PINC,PC0) //Rx0 #define LED1(outx) OUTPORT_PORT_DDR((outx),(1),PORTD,DDRD,PIND,PD5) //Tx0 #define LED2(outx) OUTPORT_PORT_DDR((outx),(1),PORTD,DDRD,PIND,PD3) //Err0 #define LED3(outx) OUTPORT_PORT_DDR((outx),(1),PORTD,DDRD,PIND,PD4)
//Rx1 #define LED4(outx) OUTPORT_PORT_DDR((outx),(1),PORTB,DDRB,PINB,PB7) //Tx1 #define LED5(outx) OUTPORT_PORT_DDR((outx),(1),PORTB,DDRB,PINB,PB5) //Err1 #define LED6(outx) OUTPORT_PORT_DDR((outx),(1),PORTB,DDRB,PINB,PB6) //-----------------------------------------------------------------------------extern volatile u8t led_i[NUMOFLED]; //-----------------------------------------------------------------------------void led_10ms(void); //-----------------------------------------------------------------------------#endif //-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// led.c //-----------------------------------------------------------------------------#include //názvy registrů
152
#include "led.h" volatile u8t led_i[NUMOFLED]={0}; //-----------------------------------------------------------------------------void led_10ms(void){ static unsigned char i=0; i++; if( i == 50){LED0(1);} if(i > 100){LED0(0); i=0;} if(led_i[1] > 0){led_i[1]--;LED1(1);} else{LED1(0);} if(led_i[2] > 0){led_i[2]--;LED2(1);} else{LED2(0);} if(led_i[3] > 0){led_i[3]--;LED3(1);} else{LED3(0);} if(led_i[4] > 0){led_i[4]--;LED4(1);} else{LED4(0);} if(led_i[5] > 0){led_i[5]--;LED5(1);} else{LED5(0);} if(led_i[6] > 0){led_i[6]--;LED6(1);} else{LED6(0);} }
153
Příloha 17: Popis programu TempMonitor Program TempMonitor slouží k nastavování jednotek a MCU serverů. Obsahuje tři záložky. První záložka, Mes Unit, slouží k ovládání měřicích jednotek. Na následujícím obrázku je její screenshot.
Po spuštění programu nejdříve vybereme port, na kterém je měřicí systém připojen. Pomocí tlačítka Open port otevřeme. V případě výběru neexistujícího portu GroupBox zčervená. Dále vybereme adresu jednotky, se kterou chceme komunikovat. Pomocí tlačítek Read, Write, OK provádíme čtení, zápis a příkazy s vybranou jednotkou. V případě správného načtení příslušný EditBox zezelená, v opačném případě zčervená. Úspěšný zápis indikuje světle modrá barva. Neúspěch je indikován opět červenou barvou.
154
Předchozí obrázek zobrazuje screenshot záložky MCU Serveru. Ovládání je totožné jako u záložky Mes Unit. Následující obrázek zobrazuje screenshot poslední záložky Network. Tato záložka slouží k načítání naměřených teplot z měřicích jednotek a MCU serverů. Cyklické načítání se aktivuje CheckBoxem Start. ComboBox Delay slouží k nastavení prodlevy mezi načítáním. Tlačítko Copy log okopíruje naměřená data do schránky. Pomocí tlačítka Clear log se MemoBox vymaže.
155
Příloha 18: Příkazy a nastavení jednotek -
Příkazy: o NOP – jednotka nevykonává žádný příkaz. o Reset – provede reset jednotky. o RAM to ROM – uloží nastavení jednotky do paměti eeprom. o ROM to RAM – nahraje nastavení jednotky z paměti eeprom. o RAM to Module – předá nastavení jednotky modulům. o Load Default – nahraje základní nastavení jednotky a provede příkaz RAM to Module.
-
Nastavení: o Master Bitrate – komunikační rychlost masteru [0-5].
156
o Master TX Pause – prodleva mezi přijetím odpovědi a vysíláním nového požadavku masteru [ms]. o Master RX Timeout – doba, za kterou musí být přijata odpověď [ms]. o Master ADR read max – adresa poslední jednotky načítané masterem [0-127]. o Master err read n – počet povolených chyb čtení masterem, po překročení tohoto čísla uloží master do paměti chybový kód [0-254]. o Slave ADDRESS – pokud je dip přepínač adres nastaven na nulu, obsahuje položka adresu jednotky, v opačném případě je hodnota ignorována. o Slave Bitrate – pokud je dip přepínač adres nastaven na nulu, obsahuje položka bitrate jednotky, v opačném případě je hodnota ignorována a bitrate je načtena z dip přepínačů. o Slave TX Pause – prodleva mezi přijetím požadavku a odvysíláním odpovědi slavem [ms]. o Slave RX Timeout – doba, za kterou musí být přijat celý paket [ms]. o Mes. Pause – prodleva mezi jednotlivými měřeními teploty [ms]. o Temp. Offset – ofset teplotního čidla [-9,99 – 9,99°C].
Příloha 19: Zdrojový kód programu TempMonitor //--------------------------------------------------------------------------//--------------------------------------------------------------------------// Unit1.h //--------------------------------------------------------------------------#ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------#include #include #include <StdCtrls.hpp> #include #include <ExtCtrls.hpp> #include #include #include #include #include #include #include #include <Mask.hpp> #include #include #include #include
157
//--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components TPageControl *PageControl1; TTabSheet *TabSheet1; TTabSheet *TabSheet2; TGroupBox *GroupBox1; TLabel *Label5; TLabel *Label6; TButton *Button7; TGroupBox *GroupBox2; TLabel *Label2; TLabel *Label3; TLabel *Label4; TComboBox *ComboBox2; TLabel *Label7; TLabel *Label8; TButton *Button1; TButton *Button3; TButton *Button5; TButton *Button8; TButton *Button9; TButton *Button2; TButton *Button4; TButton *Button6; TButton *Button10; TButton *Button11; TMaskEdit *MaskEdit1; TMaskEdit *MaskEdit2; TMaskEdit *MaskEdit3; TMaskEdit *MaskEdit4; TGroupBox *GroupBox3; TMaskEdit *MaskEdit5; TLabel *Label9; TMaskEdit *MaskEdit6; TLabel *Label10; TButton *Button12; TButton *Button13; TGroupBox *GroupBox4; TLabel *Label11; TComboBox *ComboBox3; TGroupBox *GroupBox5; TLabel *Label1; TComboBox *ComboBox1; TGroupBox *GroupBox6; TLabel *Label13; TLabel *Label14; TLabel *Label15; TLabel *Label16; TLabel *Label17; TComboBox *ComboBox4; TButton *Button14; TButton *Button15; TButton *Button16; TButton *Button17; TButton *Button18; TButton *Button19; TButton *Button20; TButton *Button21;
158
TButton *Button22; TButton *Button23; TMaskEdit *MaskEdit7; TMaskEdit *MaskEdit8; TMaskEdit *MaskEdit9; TMaskEdit *MaskEdit10; TGroupBox *GroupBox7; TLabel *Label18; TLabel *Label19; TButton *Button24; TGroupBox *GroupBox8; TLabel *Label20; TLabel *Label21; TMaskEdit *MaskEdit11; TMaskEdit *MaskEdit12; TButton *Button25; TButton *Button26; TComboBox *ComboBox5; TLabel *Label22; TTabSheet *TabSheet3; TGroupBox *GroupBox9; TGroupBox *GroupBox10; TLabel *Label24; TGroupBox *GroupBox11; TLabel *Label26; TGroupBox *GroupBox12; TLabel *Label28; TGroupBox *GroupBox13; TLabel *Label30; TGroupBox *GroupBox14; TLabel *Label32; TGroupBox *GroupBox15; TLabel *Label34; TGroupBox *GroupBox16; TLabel *Label36; TGroupBox *GroupBox17; TGroupBox *GroupBox18; TLabel *Label38; TTimer *Timer1; TGroupBox *GroupBox19; TCheckBox *CheckBox1; TPanel *Panel1; TButton *Button27; TRichEdit *RichEdit1; TLabel *Label12; TComboBox *ComboBox6; TLabel *Label41; TLabel *Label42; TComboBox *ComboBox7; TButton *Button28; TLabel *Label44; TButton *Button30; TButton *Button31; TComboBox *ComboBox9; TLabel *Label40; TComboBox *ComboBox8; TButton *Button29; TLabel *Label43; TComboBox *ComboBox10; TButton *Button32;
159
TButton *Button33; TLabel *Label45; TComboBox *ComboBox11; TButton *Button34; TButton *Button35; TLabel *Label46; TMaskEdit *MaskEdit13; TButton *Button36; TButton *Button37; TLabel *Label47; TMaskEdit *MaskEdit14; TButton *Button38; TButton *Button39; TPanel *Panel2; TPanel *Panel3; TMemo *Memo1; TComboBox *ComboBox12; TLabel *Label23; TButton *Button40; TButton *Button41; void __fastcall FormCreate(TObject *Sender); void __fastcall FormDestroy(TObject *Sender); void __fastcall Button1Click(TObject *Sender); void __fastcall Button12Click(TObject *Sender); void __fastcall Button13Click(TObject *Sender); void __fastcall MaskEdit6Change(TObject *Sender); void __fastcall Button3Click(TObject *Sender); void __fastcall Button5Click(TObject *Sender); void __fastcall Button8Click(TObject *Sender); void __fastcall Button9Click(TObject *Sender); void __fastcall Button2Click(TObject *Sender); void __fastcall Button4Click(TObject *Sender); void __fastcall Button6Click(TObject *Sender); void __fastcall Button10Click(TObject *Sender); void __fastcall Button11Click(TObject *Sender); void __fastcall MaskEdit1Change(TObject *Sender); void __fastcall MaskEdit2Change(TObject *Sender); void __fastcall MaskEdit3Change(TObject *Sender); void __fastcall MaskEdit4Change(TObject *Sender); void __fastcall ComboBox2Change(TObject *Sender); void __fastcall Button7Click(TObject *Sender); void __fastcall Button24Click(TObject *Sender); void __fastcall Button26Click(TObject *Sender); void __fastcall Button14Click(TObject *Sender); void __fastcall Button15Click(TObject *Sender); void __fastcall Button16Click(TObject *Sender); void __fastcall Button17Click(TObject *Sender); void __fastcall Button18Click(TObject *Sender); void __fastcall Button25Click(TObject *Sender); void __fastcall Button19Click(TObject *Sender); void __fastcall Button20Click(TObject *Sender); void __fastcall Button21Click(TObject *Sender); void __fastcall Button22Click(TObject *Sender); void __fastcall Button23Click(TObject *Sender); void __fastcall MaskEdit12Change(TObject *Sender); void __fastcall ComboBox4Change(TObject *Sender); void __fastcall MaskEdit7Change(TObject *Sender); void __fastcall MaskEdit8Change(TObject *Sender); void __fastcall MaskEdit9Change(TObject *Sender); void __fastcall MaskEdit10Change(TObject *Sender);
160
void __fastcall TabSheet3Show(TObject *Sender); void __fastcall TabSheet2Show(TObject *Sender); void __fastcall TabSheet1Show(TObject *Sender); void __fastcall Timer1Timer(TObject *Sender); void __fastcall Button27Click(TObject *Sender); void __fastcall Button28Click(TObject *Sender); void __fastcall Button30Click(TObject *Sender); void __fastcall Button31Click(TObject *Sender); void __fastcall ComboBox9Change(TObject *Sender); void __fastcall ComboBox7Change(TObject *Sender); void __fastcall Button29Click(TObject *Sender); void __fastcall ComboBox8Change(TObject *Sender); void __fastcall Button32Click(TObject *Sender); void __fastcall Button33Click(TObject *Sender); void __fastcall ComboBox10Change(TObject *Sender); void __fastcall ComboBox11Change(TObject *Sender); void __fastcall Button34Click(TObject *Sender); void __fastcall Button35Click(TObject *Sender); void __fastcall MaskEdit13Change(TObject *Sender); void __fastcall Button36Click(TObject *Sender); void __fastcall Button37Click(TObject *Sender); void __fastcall MaskEdit14Change(TObject *Sender); void __fastcall Button38Click(TObject *Sender); void __fastcall Button39Click(TObject *Sender); void __fastcall MaskEdit5Change(TObject *Sender); void __fastcall MaskEdit11Change(TObject *Sender); void __fastcall Button40Click(TObject *Sender); void __fastcall Button41Click(TObject *Sender); private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); //--------------------------------------------------------------------------// Bus read & write //--------------------------------------------------------------------------int quest(char adr, char blok, char elem, char siz); int questWord(char adr, char blok, char elem); int questField(char adr, char blok, char elem, char siz); int write(char adr, char blok, char elem, char siz, short int *data); int writeWord(char adr, char blok, char elem, short int data); //--------------------------------------------------------------------------// Input/output Text & log //--------------------------------------------------------------------------void logToFile(int *p, int siz); void charToHex(char *in, char *out, int siz); void RichTimeLineAdd(AnsiString &str, TColor c, bool cr); void intToStringLabel(int in, TLabel *Lab); void intToStringMaskEdit(int in, int format, TMaskEdit *Edit); unsigned short readMaskEdit(int format, TMaskEdit *Edit); void intToItemComboBox(int in, TComboBox *Combo); void writeComboBox(int in, TComboBox *Combo); bool MaskEditFiltr(int min, int max, int format, TMaskEdit *Mask); void writeEditFiltr(char adr, char blok, char elem, int min, int max, int format, TMaskEdit *Mask); HANDLE FHandle; DCB dcb; COMMTIMEOUTS ct;
161
char strBuf[1000]; char rxBuf[1000]; char txBuf[1000]; int valueBuf[500]; char sw; }; //--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------#endif //--------------------------------------------------------------------------//--------------------------------------------------------------------------// Unit1.c //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { FHandle = NULL; sw=0; } //--------------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender) { CloseHandle(FHandle); } //--------------------------------------------------------------------------// Open/Close port //--------------------------------------------------------------------------void __fastcall TForm1::Button27Click(TObject *Sender) { CheckBox1->Checked = false; if(Button27->Caption == "Close"){ CloseHandle(FHandle); FHandle = NULL; Button27->Caption = "Open"; GroupBox1->Color = clBtnShadow; GroupBox2->Color = clBtnShadow; GroupBox5->Color = clBtnShadow; GroupBox6->Color = clBtnShadow; GroupBox7->Color = clBtnShadow; GroupBox1->Enabled = false; GroupBox2->Enabled = false; GroupBox5->Enabled = false;
162
GroupBox6->Enabled = false; GroupBox7->Enabled = false; CheckBox1->Enabled = false; ComboBox3->Enabled = true; ComboBox6->Enabled = true; return; } if(Button27->Caption == "Open"){ FHandle = CreateFile(ComboBox3->Text.t_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,0,NULL); if(FHandle == INVALID_HANDLE_VALUE){ Button24->Caption = "Open"; GroupBox4->Color =clRed; GroupBox1->Color = clBtnShadow; GroupBox2->Color = clBtnShadow; GroupBox5->Color = clBtnShadow; GroupBox6->Color = clBtnShadow; GroupBox7->Color = clBtnShadow; GroupBox1->Enabled = false; GroupBox2->Enabled = false; GroupBox5->Enabled = false; GroupBox6->Enabled = false; GroupBox7->Enabled = false; CheckBox1->Enabled = false; return; }else{ Button27->Caption = "Close"; ComboBox3->Enabled = false; ComboBox6->Enabled = false; GroupBox4->Color = clBtnFace; GroupBox1->Color = clBtnFace; GroupBox2->Color = clBtnFace; GroupBox5->Color = clBtnFace; GroupBox6->Color = clBtnFace; GroupBox7->Color = clBtnFace; GroupBox1->Enabled = true; GroupBox2->Enabled = true; GroupBox5->Enabled = true; GroupBox6->Enabled = true; GroupBox7->Enabled = true; CheckBox1->Enabled = true; } //nastaveni seriove linky dcb.DCBlength = sizeof(DCB); GetCommState(FHandle,&dcb); dcb.BaudRate=ComboBox6->Text.ToInt(); dcb.ByteSize=8; dcb.Parity=NOPARITY; dcb.StopBits=ONESTOPBIT; SetCommState(FHandle,&dcb); EscapeCommFunction(FHandle, CLRRTS); EscapeCommFunction(FHandle, SETDTR); ct.ReadIntervalTimeout=50; ct.ReadTotalTimeoutMultiplier=11; ct.ReadTotalTimeoutConstant=200; SetCommTimeouts(FHandle,&ct); }
163
} //--------------------------------------------------------------------------void __fastcall TForm1::TabSheet3Show(TObject *Sender) { GroupBox5->Visible = false; //Timer1->Enabled = true; } //--------------------------------------------------------------------------void __fastcall TForm1::TabSheet2Show(TObject *Sender) { GroupBox5->Visible = true; //Timer1->Enabled = false; } //--------------------------------------------------------------------------void __fastcall TForm1::TabSheet1Show(TObject *Sender) { GroupBox5->Visible = true; //Timer1->Enabled = false; } //--------------------------------------------------------------------------// Bus read & write //--------------------------------------------------------------------------int TForm1::quest(char adr, char blok, char elem, char siz) { int len=0; char tempCRC=0; txBuf[len++] = 0xff; txBuf[len++] = 0x0a; txBuf[len++] = 0x55; txBuf[len++] = adr; txBuf[len++] = 0x81; txBuf[len++] = 0x71; txBuf[len++] = blok; txBuf[len++] = elem; txBuf[len++] = siz; for(int i=1; i< len; i++){ tempCRC+=txBuf[i]; } txBuf[len++] = ~tempCRC; charToHex(txBuf, strBuf, len); return len; } //--------------------------------------------------------------------------int TForm1::write(char adr, char blok, char elem, char siz, short int *data) { int len=0; char tempCRC=0; txBuf[len++] = 0xff; txBuf[len++] = 0x0a; txBuf[len++] = 0x55; txBuf[len++] = adr; txBuf[len++] = 0x82; txBuf[len++] = 0x71; txBuf[len++] = blok; txBuf[len++] = elem; txBuf[len++] = siz; for(int i=0; i< siz; i++){ txBuf[len++] = (char)data[i];
164
txBuf[len++] = (char)(data[i]>>8); } for(int i=1; i< len; i++){ tempCRC+=txBuf[i]; } txBuf[len++] = ~tempCRC; charToHex(txBuf, strBuf, len); return len; } //--------------------------------------------------------------------------int TForm1::writeWord(char adr, char blok, char elem, short int data) { DWORD d=1000; int len; signed short * p; int temp=0; AnsiString str; len = write(adr, blok, elem, 1, &data); PurgeComm(FHandle, PURGE_RXCLEAR); WriteFile(FHandle,txBuf,len,&d,NULL); str = " Tx>>WriteWord>> "; str += strBuf; RichTimeLineAdd(str, clBlue, false); ReadFile(FHandle,rxBuf,10,&d,NULL); if(d==0){ str = " Rx<< No response"; RichTimeLineAdd(str, clRed, true); return -1; }else{ charToHex(rxBuf, strBuf, d); str = " Rx<<WriteACK<< "; if((d == 10) && (rxBuf[0] == (char)0xff) && (rxBuf[1] == (char)0x0a) && (rxBuf[2] == (char)0x53)){ temp = rxBuf[8]; }else{ str += "Write fail "; temp = -1; } } str += strBuf; RichTimeLineAdd(str, clRed, true); return temp; } //--------------------------------------------------------------------------int TForm1::questWord(char adr, char blok, char elem) { DWORD d=1000; int len; unsigned short * p; int temp=0; AnsiString str; len = quest(adr, blok, elem, 1); PurgeComm(FHandle, PURGE_RXCLEAR); WriteFile(FHandle,txBuf,len,&d,NULL);
165
str = " Tx>>QuestWord>> "; str += strBuf; RichTimeLineAdd(str, clBlue, false); ReadFile(FHandle,rxBuf,12,&d,NULL); if(d==0){ str = " Rx<< No response"; RichTimeLineAdd(str, clRed, true); return -1; }else{ charToHex(rxBuf, strBuf, d); str = " Rx<
int TForm1::questField(char adr, char blok, char elem, char siz) { DWORD d=1000; int len; int temp=0; unsigned short * p = (unsigned short*)&rxBuf[9]; AnsiString str;
len = quest(adr, blok, elem, siz); PurgeComm(FHandle, PURGE_RXCLEAR); WriteFile(FHandle,txBuf,len,&d,NULL); str = " Tx>>QuestField>> "; str += strBuf; RichTimeLineAdd(str, clBlue, false); ReadFile(FHandle,rxBuf,10+(2*siz),&d,NULL); if(d==0){ str = " Rx<< No response"; RichTimeLineAdd(str, clRed, true); for(int i=0; i< siz; i++){ valueBuf[i] = -1; } return -1; }else{ charToHex(rxBuf, strBuf, d); str = "Rx<
166
}else{ str += "Rx fail "; for(int i=0; i< siz; i++){ valueBuf[i] = -1; } temp = -1; } } str += strBuf; RichTimeLineAdd(str, clRed, true); return temp; } //--------------------------------------------------------------------------// Input/output Text & log //--------------------------------------------------------------------------void TForm1::charToHex(char *in, char *out, int siz) { for(int i=0; i<siz; i++){ *out = (in[i]&0xf0)>>4; if(*out<10)*out+= '0'; else*out+= 'A'-10; out++; *out = (in[i]&0x0f); if(*out<10)*out+= '0'; else*out+= 'A'-10; out++; *out++=' '; } *out=0; } //--------------------------------------------------------------------------void TForm1::RichTimeLineAdd(AnsiString &str, TColor c, bool cr) { AnsiString stri; TDateTime dt; unsigned short h,m,s,ms; dt=TDateTime::CurrentTime(); dt.DecodeTime(&h,&m,&s,&ms); stri.sprintf("%02d:%02d:%02d,%03d",h,m,s,ms); stri += str; RichEdit1->SelLength=0; RichEdit1->SelAttributes->Color = c; RichEdit1->Lines->Add(stri); if(cr){ stri = ""; RichEdit1->Lines->Add(stri); } PostMessage(RichEdit1->Handle,WM_VSCROLL, SB_PAGEDOWN, 0); } //--------------------------------------------------------------------------void TForm1::writeEditFiltr(char adr, char blok, char elem, int min, int max, int format, TMaskEdit *Mask){ if(MaskEditFiltr(min, max, format, Mask)){ Mask->Color = clRed; return; } unsigned short U16T = readMaskEdit(format,Mask); int temp = writeWord( adr , blok, elem, U16T );
167
if((temp == -1) || (temp==0) )Mask->Color = clRed; else Mask->Color = clAqua; } //--------------------------------------------------------------------------//format 0 - u16t //format 1 - s16t //format 2 - float/100 bool TForm1::MaskEditFiltr(int min, int max, int format, TMaskEdit *Mask){ bool empty = false; char buf[20] = {0}; int i=0; char *p; int S32T; unsigned int U32T; double d; AnsiString str = Mask->Text; p = str.c_str(); //format 0 - u16t if(format == 0){ while((*p)&&(i<8)){ if((*p<'0')||(*p>'9')){ p++; }else{ if(buf[0]=='0' && i==1 && *p=='0'){ p++; }else{ buf[i++] = *p++; } } } str.sprintf("%s",buf); U32T = str.ToIntDef(999999999); if(U32T != 999999999){ if(U32T > (unsigned int)max)str = max; if(U32T < (unsigned int)min)str = min; }else{ empty = true; } } //format 1 - s16t if(format == 1){ while((*p)&&(i<8)){ if((*p<'0')||(*p>'9')){ if( (i == 0) && (*p == '-') ){ buf[i++] = *p; } p++; }else{ if(buf[0]=='0' && i==1 && *p=='0'){ p++; }else{ buf[i++] = *p++; } } } str.sprintf("%s",buf); S32T = str.ToIntDef(999999999); if(S32T != 999999999){ if(S32T > max)str = max;
168
if(S32T < min)str = min; }else{ empty = true; } } //format 2 - float/100 if(format == 2){ while((*p)&&(i<8)){ if(*p == '.')*p = ','; if((*p<'0')||(*p>'9')){ if( (i == 0) && (*p == '-') ){ buf[i++] = *p; } if( ( (i == 1) && (*p == ',')&&(buf[0]!='-') )|| ( (i == 2) && (*p == ',') && (buf[0] == '-') ) ){ buf[i++] = *p; } p++; }else{ if(buf[0]=='0' && i==1 && *p=='0'){ p++; }else{ buf[i++] = *p++; } } } str.sprintf("%s",buf); if( !((i==0) || (i==1) && (buf[0]=='-')) ){ S32T = (signed int)(str.ToDouble()*100); if(S32T > max){ d = max; d /=100; str = d; } if(S32T < min){ d = max; d /=100; str = d; } }else{ empty = true; } } Mask->Text = str; Mask->Color = clWindow; return empty; } //--------------------------------------------------------------------------//format 0 - u16t //format 1 - s16t //format 2 - float/100 unsigned short TForm1::readMaskEdit(int format, TMaskEdit *Edit){ double d; int temp; unsigned short *U16Tp = (unsigned short*)&temp; unsigned short U16T = 0;
169
if( (format == 0) || (format == 1) ){ temp = Edit->Text.ToInt(); U16T = *U16Tp; } if(format == 2){ d= Edit->Text.ToDouble(); d*=100; temp = d; U16T = *U16Tp; } return U16T; } //--------------------------------------------------------------------------void TForm1::writeComboBox(int in, TComboBox *Combo){ if((in == -1) || (in==0) ){ Combo->Color = clRed; }else{ Combo->Color = clAqua; } } //--------------------------------------------------------------------------void TForm1::intToItemComboBox(int in, TComboBox *Combo){ bool fail = false; unsigned short *U16Tp,U16T; AnsiString str; if(in == -1){ Combo->Text = "NoResp"; Combo->Color = clRed; fail = true; } if(!fail){ U16Tp = (unsigned short*)∈ U16T = *U16Tp; Combo->ItemIndex = U16T; Combo->Color = clLime; } } //--------------------------------------------------------------------------//format 0 - u16t //format 1 - s16t //format 2 - float/100 void TForm1::intToStringMaskEdit(int in, int format, TMaskEdit *Edit){ bool fail = false; double d; unsigned short *U16Tp,U16T; signed short *S16Tp,S16T; AnsiString str; if(in == -1){ Edit->Text = "NoResp"; Edit->Color = clRed; fail = true; }
170
if(!fail&&(format==0)){ U16Tp = (unsigned short*)∈ U16T = *U16Tp; str.sprintf("%d", U16T); Edit->Text = str; Edit->Color = clLime; } if(!fail&&(format==1)){ S16Tp = (signed short*)∈ S16T = *S16Tp; str.sprintf("%d", S16T); Edit->Text = str; Edit->Color = clLime; } if(!fail&&(format==2)){ d = ((float)((short int)in))/100; str.sprintf("%0.2f", d); Edit->Text = str; Edit->Color = clLime; } } //--------------------------------------------------------------------------void TForm1::intToStringLabel(int in, TLabel *Lab){ bool fail = false; double d; AnsiString str; if(in == -1){ Lab->Caption = "NoResp"; Lab->Color = clRed; fail = true; } if(in == 0x8000){ Lab->Caption = "SnDisc"; Lab->Color = clRed; fail = true; } if(in == 0x8001){ Lab->Caption = "SnCRCErr"; Lab->Color = clRed; fail = true; } if(in == 0x8002){ Lab->Caption = "MesFail"; Lab->Color = clRed; fail = true; } if(in == 0x8003){ Lab->Caption = "ElNoExis"; Lab->Color = clRed; fail = true; } if(in == 0x8004){ Lab->Caption = "NotReady"; Lab->Color = clRed;
171
fail = true; } if(in == 0x800A){ Lab->Caption = "ReadErr"; Lab->Color = clRed; fail = true; } if(!fail){ d = ((float)((short int)in))/100; str.sprintf("%0.2f°C", d); Lab->Caption = str; Lab->Color = clLime; } } //--------------------------------------------------------------------------void TForm1::logToFile(int *p, int siz){ static unsigned int n=0; bool fail = false, minus = false; signed short int cela, desetina,temp; AnsiString str, strf, stra; TDateTime dt; unsigned short h,m,s,ms; dt=TDateTime::CurrentTime(); dt.DecodeTime(&h,&m,&s,&ms); str.sprintf("%02d:%02d:%02d", h, m, s); strf.sprintf("%d\t%02d:%02d:%02d,%03d", n++, h, m, s, ms);
for(int i=0; i < siz; i++){ str +="\t"; fail = false; minus = false; if(p[i] == -1){ str += "NoResp"; fail = true; } if(p[i] == 0x8000){ str += "SnDisc"; fail = true; } if(p[i] == 0x8001){ str += "SnCRCErr"; fail = true; } if(p[i] == 0x8002){ str += "MesFail"; fail = true; } if(p[i] == 0x8003){ str += "ElNoExis"; fail = true; } if(p[i] == 0x8004){ str += "NotReady"; fail = true;
172
} if(p[i] == 0x800A){ str += "ReadErr"; fail = true; } if(!fail){ temp = *((signed short int *)&p[i]); if (temp < 0 ){ temp = -temp; minus = true; } cela = temp /100; desetina = temp-(cela*100); if(minus){ stra.sprintf("-%d,%02d", cela, desetina); }else{ stra.sprintf("%d,%02d", cela, desetina); } str += stra; } } Memo1->Lines->Add(str); } //--------------------------------------------------------------------------// Network & Log //--------------------------------------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender) { static unsigned int div=0,divtop=0;; int temp; AnsiString str; static int TempLog[8]={0}; double d; if ((!CheckBox1->Checked) || (FHandle == NULL) || (FHandle == INVALID_HANDLE_VALUE) ){ Panel1->Color = clBtnFace; Panel2->Color = clBtnFace; Label24->Color = clBtnFace; Label26->Color = clBtnFace; Label28->Color = clBtnFace; Label30->Color = clBtnFace; Panel3->Color = clBtnFace; Label32->Color = clBtnFace; Label34->Color = clBtnFace; Label36->Color = clBtnFace; Label38->Color = clBtnFace; div=0; sw = 0; return; } divtop = 0; if(ComboBox12->ItemIndex==0)divtop = 0; //0 if(ComboBox12->ItemIndex==1)divtop = 3; //1s if(ComboBox12->ItemIndex==2)divtop = 16;//5s if(ComboBox12->ItemIndex==3)divtop = 33;//10s if(ComboBox12->ItemIndex==4)divtop = 100;//30s if(ComboBox12->ItemIndex==5)divtop = 200;//60s
173
Timer1->Enabled = false; switch(sw){ //Server1 case 0: temp = questField(1, 1, 1, 3); intToStringLabel(valueBuf[0], Label24); intToStringLabel(valueBuf[1], Label26); intToStringLabel(valueBuf[2], Label28); TempLog[0] = valueBuf[0]; TempLog[1] = valueBuf[1]; TempLog[2] = valueBuf[2]; if(temp == -1 ){ Panel1->Color = clRed; Panel2->Color = clRed; }else{ Panel1->Color = clGreen; Panel2->Color = clGreen; } sw++; break; //zhasni case 1: Panel1->Color = clBtnFace; Panel2->Color = clBtnFace; Label24->Color = clBtnFace; Label26->Color = clBtnFace; Label28->Color = clBtnFace; sw++; break; //MesUnit 3 case 2: temp = questWord(3, 1, 0); intToStringLabel(temp, Label30); TempLog[3] = temp; if(temp == -1 ){ Panel1->Color = clRed; }else{ Panel1->Color = clGreen; } sw++; break; //zhasni case 3: Panel1->Color = clBtnFace; Label30->Color = clBtnFace; sw++; break; //Server2 case 4: temp = questField(2, 1, 1, 4); intToStringLabel(valueBuf[0], Label32); intToStringLabel(valueBuf[1], Label34); intToStringLabel(valueBuf[2], Label36); intToStringLabel(valueBuf[3], Label38); TempLog[4] = valueBuf[0]; TempLog[5] = valueBuf[1]; TempLog[6] = valueBuf[2]; TempLog[7] = valueBuf[3]; if(temp == -1 ){
174
Panel1->Color = clRed; Panel3->Color = clRed; }else{ Panel1->Color = clGreen; Panel3->Color = clGreen; } sw++; break; //zhasni case 5: Panel1->Color = clBtnFace; Panel3->Color = clBtnFace; Label32->Color = clBtnFace; Label34->Color = clBtnFace; Label36->Color = clBtnFace; Label38->Color = clBtnFace; logToFile(TempLog,8); sw++; break; case 6: if(div <= divtop){ div++; }else{ div=0; sw=0; } break; //def default : sw = 0; } Timer1->Enabled = true; } //--------------------------------------------------------------------------// Label Read //--------------------------------------------------------------------------void __fastcall TForm1::Button7Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 1, 0); intToStringLabel(temp, Label6); } //--------------------------------------------------------------------------void __fastcall TForm1::Button24Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 1, ComboBox5->Text.ToInt()); intToStringLabel(temp, Label19); } //--------------------------------------------------------------------------// ComboBox Read //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 7); intToItemComboBox(temp, ComboBox2); } //---------------------------------------------------------------------------
175
void __fastcall TForm1::Button34Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 7); intToItemComboBox(temp, ComboBox11); } //--------------------------------------------------------------------------void __fastcall TForm1::Button32Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 6); intToItemComboBox(temp, ComboBox10); } //--------------------------------------------------------------------------void __fastcall TForm1::Button30Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 6); intToItemComboBox(temp, ComboBox9); } //--------------------------------------------------------------------------void __fastcall TForm1::Button14Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 1); intToItemComboBox(temp, ComboBox4); } //--------------------------------------------------------------------------// Maskedit Read //--------------------------------------------------------------------------void __fastcall TForm1::Button13Click(TObject *Sender) { if(MaskEditFiltr(0, 255, 0, MaskEdit5)){ MaskEdit5->Color = clRed; MaskEdit6->Text = ""; return; } int temp = questWord(ComboBox1->Text.ToInt(), 1, MaskEdit5->Text.ToInt()); intToStringMaskEdit(temp, 0, MaskEdit6); } //--------------------------------------------------------------------------void __fastcall TForm1::Button26Click(TObject *Sender) { if(MaskEditFiltr(0, 255, 0, MaskEdit11)){ MaskEdit11->Color = clRed; MaskEdit12->Text = ""; return; } int temp = questWord(ComboBox1->Text.ToInt(), 1, MaskEdit11->Text.ToInt()); intToStringMaskEdit(temp, 0, MaskEdit12); } //--------------------------------------------------------------------------void __fastcall TForm1::Button38Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 9); intToStringMaskEdit(temp, 0, MaskEdit14); } //---------------------------------------------------------------------------
176
void __fastcall TForm1::Button36Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(),255, 8); intToStringMaskEdit(temp, 0, MaskEdit13); } //--------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(),255, 8); intToStringMaskEdit(temp, 0, MaskEdit1); } //--------------------------------------------------------------------------void __fastcall TForm1::Button5Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 9); intToStringMaskEdit(temp, 0, MaskEdit2); } //--------------------------------------------------------------------------void __fastcall TForm1::Button8Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(), 255, 11); intToStringMaskEdit(temp, 2, MaskEdit3); } //--------------------------------------------------------------------------void __fastcall TForm1::Button9Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(),255, 10); intToStringMaskEdit(temp, 0, MaskEdit4); } //--------------------------------------------------------------------------void __fastcall TForm1::Button15Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(),255, 2); intToStringMaskEdit(temp, 0, MaskEdit7); } //--------------------------------------------------------------------------void __fastcall TForm1::Button16Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(),255, 3); intToStringMaskEdit(temp, 0, MaskEdit8); } //--------------------------------------------------------------------------void __fastcall TForm1::Button17Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(),255, 4); intToStringMaskEdit(temp, 0, MaskEdit9); } //--------------------------------------------------------------------------void __fastcall TForm1::Button18Click(TObject *Sender) { int temp = questWord(ComboBox1->Text.ToInt(),255, 5); intToStringMaskEdit(temp, 0, MaskEdit10);
177
} //--------------------------------------------------------------------------// ComboBox write //--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender) { int temp = writeWord( ComboBox1->Text.ToInt() , 255, 7, ComboBox2->ItemIndex ); writeComboBox(temp, ComboBox2); } //---------------------------------------------------------------------------
void __fastcall TForm1::Button19Click(TObject *Sender) { int temp = writeWord( ComboBox1->Text.ToInt() , 255, 1, ComboBox4->ItemIndex ); writeComboBox(temp, ComboBox4); } //--------------------------------------------------------------------------void __fastcall TForm1::Button28Click(TObject *Sender) { int temp = writeWord( ComboBox1->Text.ToInt() , 255, 0, ComboBox7->ItemIndex ); writeComboBox(temp, ComboBox7); } //--------------------------------------------------------------------------void __fastcall TForm1::Button31Click(TObject *Sender) { int temp = writeWord( ComboBox1->Text.ToInt() , 255, 6, ComboBox9->ItemIndex ); writeComboBox(temp, ComboBox9); } //--------------------------------------------------------------------------void __fastcall TForm1::Button29Click(TObject *Sender) { int temp = writeWord( ComboBox1->Text.ToInt() , 255, 0, ComboBox8->ItemIndex ); writeComboBox(temp, ComboBox8); } //--------------------------------------------------------------------------void __fastcall TForm1::Button33Click(TObject *Sender) { int temp = writeWord( ComboBox1->Text.ToInt() , 255, 6, ComboBox10->ItemIndex ); writeComboBox(temp, ComboBox10); } //--------------------------------------------------------------------------void __fastcall TForm1::Button35Click(TObject *Sender) { int temp = writeWord( ComboBox1->Text.ToInt() , 255, 7, ComboBox11->ItemIndex ); writeComboBox(temp, ComboBox11); } //--------------------------------------------------------------------------//--------------------------------------------------------------------------// Maskedit write //--------------------------------------------------------------------------void __fastcall TForm1::Button25Click(TObject *Sender)
178
{ if(MaskEditFiltr(0, 65535, 0, MaskEdit11)){ MaskEdit11->Color = clRed; MaskEdit12->Color = clWindow; return; } writeEditFiltr(ComboBox1->Text.ToInt() , 1, MaskEdit11->Text.ToInt(), 0, 65535, 0, MaskEdit12); } //--------------------------------------------------------------------------void __fastcall TForm1::Button12Click(TObject *Sender) { if(MaskEditFiltr(0, 65535, 0, MaskEdit5)){ MaskEdit5->Color = clRed; MaskEdit6->Color = clWindow; return; } writeEditFiltr(ComboBox1->Text.ToInt() , 1, MaskEdit5->Text.ToInt(), 0, 65535, 0, MaskEdit6); } //--------------------------------------------------------------------------void __fastcall TForm1::Button4Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 8, 0, 65535, 0, MaskEdit1); } //--------------------------------------------------------------------------void __fastcall TForm1::Button6Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 9, 0, 65535, 0, MaskEdit2); } //--------------------------------------------------------------------------void __fastcall TForm1::Button10Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 11, -1000, 1000, 2, MaskEdit3); } //--------------------------------------------------------------------------void __fastcall TForm1::Button11Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 10, 0, 65535, 0, MaskEdit4); } //--------------------------------------------------------------------------void __fastcall TForm1::Button37Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 8, 0, 65535, 0, MaskEdit13); } //--------------------------------------------------------------------------void __fastcall TForm1::Button39Click(TObject *Sender)
179
{ writeEditFiltr(ComboBox1->Text.ToInt() , 255, 9, 0, 65535, 0, MaskEdit14); } void __fastcall TForm1::Button20Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 2, 0, 65535, 0, MaskEdit7); } //--------------------------------------------------------------------------void __fastcall TForm1::Button21Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 3, 0, 65535, 0, MaskEdit8); } //--------------------------------------------------------------------------void __fastcall TForm1::Button22Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 4, 0, 65535, 0, MaskEdit9); } //--------------------------------------------------------------------------void __fastcall TForm1::Button23Click(TObject *Sender) { writeEditFiltr(ComboBox1->Text.ToInt() , 255, 5, 0, 65535, 0, MaskEdit10); } //--------------------------------------------------------------------------// MaskEdit Filter //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit13Change(TObject *Sender) { MaskEditFiltr(0, 65535, 0, MaskEdit13); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit14Change(TObject *Sender) { MaskEditFiltr(0, 65535, 0, MaskEdit14); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit5Change(TObject *Sender) { MaskEditFiltr(0, 255, 0, MaskEdit5); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit11Change(TObject *Sender) { MaskEditFiltr(0, 255, 0, MaskEdit11); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit12Change(TObject *Sender) { MaskEditFiltr(0, 65535, 0, MaskEdit12);
180
} //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit7Change(TObject *Sender) { MaskEditFiltr(0, 65535, 0, MaskEdit7); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit8Change(TObject *Sender) { MaskEditFiltr(0, 65535, 0, MaskEdit8); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit9Change(TObject *Sender) { MaskEditFiltr(1, 31, 0, MaskEdit9); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit10Change(TObject *Sender) { MaskEditFiltr(0, 255, 0, MaskEdit10); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit6Change(TObject *Sender) { MaskEditFiltr(0, 65535, 0, MaskEdit6); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit1Change(TObject *Sender) { MaskEditFiltr(0, 65000, 0, MaskEdit1); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit2Change(TObject *Sender) { MaskEditFiltr(0, 65000, 0, MaskEdit2); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit3Change(TObject *Sender) { MaskEditFiltr(-999, 999, 2, MaskEdit3); } //--------------------------------------------------------------------------void __fastcall TForm1::MaskEdit4Change(TObject *Sender) { MaskEditFiltr(0, 65000, 0, MaskEdit4); } //--------------------------------------------------------------------------//--------------------------------------------------------------------------// ComboBox Change color //---------------------------------------------------------------------------
181
void __fastcall TForm1::ComboBox2Change(TObject *Sender) { ComboBox2->Color = clWindow; } //--------------------------------------------------------------------------void __fastcall TForm1::ComboBox4Change(TObject *Sender) { ComboBox4->Color = clWindow; } //--------------------------------------------------------------------------void __fastcall TForm1::ComboBox9Change(TObject *Sender) { ComboBox9->Color = clWindow; } //--------------------------------------------------------------------------void __fastcall TForm1::ComboBox7Change(TObject *Sender) { ComboBox7->Color = clWindow; } //--------------------------------------------------------------------------void __fastcall TForm1::ComboBox8Change(TObject *Sender) { ComboBox8->Color = clWindow; } //--------------------------------------------------------------------------void __fastcall TForm1::ComboBox10Change(TObject *Sender) { ComboBox10->Color = clWindow; } //--------------------------------------------------------------------------void __fastcall TForm1::ComboBox11Change(TObject *Sender) { ComboBox11->Color = clWindow; } //---------------------------------------------------------------------------
void __fastcall TForm1::Button40Click(TObject *Sender) { Memo1->Clear(); } //---------------------------------------------------------------------------
void __fastcall TForm1::Button41Click(TObject *Sender) { Memo1->SelectAll(); Memo1->CopyToClipboard(); } //---------------------------------------------------------------------------
182
Příloha 20: Seznam součástek převodníku USB – RS-485 Part
Value
ACK_LED B1 B380C1000 C1 10n C2 22uF/35V C3 100nF C4 100nF C5 100nF C6 100nF C7 100nF C8 100nF C9 100nF C10 100nF C11 100nF C12 22uF C13 22uF C14 100nF C15 10n C16 100uF/25V C17 100nF D5 SMAJ11CA D6 SMAJ11CA DC1 AM1S-0505SZ DE HCPL2601 IC3 FT232RL IO1 SP485 LED4 R1 420R R2 1k R3 1k R4 420R R5 420R R6 360R R7 420R R8 420R R9 100k R10 100k R11 1k R12 2k2 R13 11k R15 360R R16 0R R17 4k7 R18 0R R19 0R
Device
Package
Library
LED3MM DBDB107G C-EUC1206 CPOL-EUD/7343-31R C-EUC0805 C-EUC1206 C-EUC0805 C-EUC1206 C-EUC1206 C-EUC0805 C-EUC0805 C-EUC0805 C-EUC0805 CPOL-EUCT7343 CPOL-EUCT7343 C-EUC1206 C-EUC1206 CPOL-EUD/7343-31R C-EUC0805 SUPPRESSOR-SMBJ SUPPRESSOR-SMBJ NME HCPL2601 FT232RFOTO 75176 LED3MM R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R0805 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206 R-EU_R1206
LED3MM DB C1206 D/7343-31R C0805 C1206 C0805 C1206 C1206 C0805 C0805 C0805 C0805 CT7343 CT7343 C1206 C1206 7343-31R C0805 SMBJ SMBJ NME DIL08 SSOP28F DIL08 LED3MM R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R0805 R1206 R1206 R1206 R1206 R1206
led rectifier rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl diode diode dc-dc-converter optocoupler ftdichip IO led rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl
183
R20 R21 RXD RX_LED S1 TL TXD TX_LED X X1 X3
0R 360R HCPL2601
10uH HCPL2601 90V PN61729-S
R-EU_R1206 R-EU_R1206 HCPL2601 LED3MM DIP02YL L-EU_0207 HCPL2601 LED3MM CG90 AK500/5 PN61729-S
R1206 R1206 DIL08 LED3MM DIP02YL 207 DIL08 LED3MM BLESKOJIST AK500/5 PN61729-S
rcl rcl optocoupler led switch-dil rlc optocoupler led SPECIAL con-ptr500 con-berg
Příloha 21: Seznam součástek měřicí jednotky Part
Value
C1 C3
330uF/6V 47n/50V
C4 C5 C7 C9 C10 C13 C15 D1 D2 D3 D4 D5 D8 D9 D12 D13 IC1 IC2 IC3 LED1 LED2 LED3 Q1 R1 R2
Device
CPOLEUCT7343 C-EUC1206 CPOL22uF/35V EUCT7343 CPOL22uF/35V EUCT7343 22p C-EUC0805 22p C-EUC0805 2u2 C-EUC1206 100n C-EUC1206 47n C-EUC1206 STPS30 DIODE-SMB STPS30 DIODE-SMB STPS30 DIODE-SMB STPS30 DIODE-SMB STPS30 DIODE-SMB P6KE10 P6KEXX P6KE10 P6KEXX BZX85C5V6 BZX85/7 BZX85C5V6 BZX85/7 ATmega8-16PU MEGA8-P 7805DT 7805DT SP485 MAX3468CPA Red LED3MM Yellow LED3MM Green LED3MM 4Mhz CRYTALHC49S 4k7 R-EU_R1206 4k7 R-EU_R1206
Package
Library
CT7343 C1206
rcl rcl
CT7343
rcl
CT7343 C0805 C0805 C1206 C1206 C1206 SMB SMB SMB SMB SMB CB1417-12 CB1417-12 D2_7 D2_7 DIL28-3 TO252 DIL08 LED3MM LED3MM LED3MM HC49/S R1206 R1206
rcl rcl rcl rcl rcl rcl diode diode diode diode diode diode diode diode diode atmel linear maxim led led led crystal rcl rcl
184
R3 R4 R5 R8 R10 R11 R14 S1 S2 SV2 X1 X2
4k7 5k1 5k1 47k 120R 120R 2k2
R-EU_R1206 R-EU_M1206 R-EU_M1206 R-EU_M1206 R-EU_M1206 R-EU_M1206 R-EU_M1206 DIP05YL DIP02YL MA05-2 AK500/4 AK500/5
R1206 M1206 M1206 M1206 M1206 M1206 M1206 DIP05YL DIP02YL MA05-2 AK500/4 AK500/5
rcl rcl rcl rcl rcl rcl rcl switch-dil switch-dil con-lstb con-ptr500 con-ptr500
Příloha 22: Seznam součástek MCU serveru Part
Value
Device
Package
Library
B1 B2 BUSPWR0 BUSPWR1 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13 C14 C15 C16 C17 C18 C19 C20 C21 C22 C23 C24 C25 C26 C27
DBDB107G DBDB107G
DBDB107G DBDB107G LED3MM LED3MM C-EUC1206 CPOL-EUD/7343-31R C-EUC1206 C-EUC1206 C-EUC1206 C-EUC1206 C-EUC1206 C-EUC1206 C-EUC1206 CPOL-EUD/7343-31R C-EUC1206 CPOL-EUD/7343-31R CPOL-EUCT7343 C-EUC1206 C-EUC1206 C-EUC1206 C-EUC1206 CPOL-EUCT7343 C-EUC1206 C-EUC1206 CPOL-EUE5-10.5 CPOL-EUD/7343-31R C-EUC1206 C-EUC1206 CPOL-EUD/7343-31R CPOL-EUE5-10.5 C-EUC1206
DB DB LED3MM LED3MM C1206 7343-31R C1206 C1206 C1206 C1206 C1206 C1206 C1206 7343-31R C1206 7343-31R CT7343 C1206 C1206 C1206 C1206 CT7343 C1206 C1206 E5-10,5 7343-31R C1206 C1206 7343-31R E5-10,5 C1206
rectifier rectifier led led rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl
10n 22uF/35V 100nF 100nF 100nF 100nF 100nF 100nF 100nF 22uF/35V 100nF 22uF/35V 22u/35V 100nF 10n 18p 18p 330u/6.3V 100n 100n 220u/25V 22uF/35V 100nF 100nF 100uF/25V 220u/25V 100nF
185
D1 D2 D3 D5 D6 DC1 DE0 ERR0LED ERR1LED F1 IC1 IO1 IO2 IO3 JTAG Q1 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 R30 R31 RUN
SMAJ11CA SMAJ11CA STPS3 SMAJ11CA SMAJ11CA AM1S-0505SZ HCPL2601
SUPPRESSOR-SMBJ SUPPRESSOR-SMBJ DIODE-SMB SUPPRESSOR-SMBJ SUPPRESSOR-SMBJ NME HCPL2601 LED3MM LED3MM T2,5A SH22,5A L7805 78XXL SP485 SP485 SP485 SP485 MEGA162-1 JTAGICE_ML10 JTAGICE_ML10 16MHz CRYSTALHC49S 420R R-EU_R1206 1k R-EU_R1206 1k R-EU_R1206 2k2 R-EU_R0805 2k2 R-EU_R1206 360R R-EU_R1206 420R R-EU_R1206 420R R-EU_R1206 100k R-EU_R1206 100k R-EU_R1206 1k R-EU_R1206 2k2 R-EU_R1206 1k R-EU_R1206 0R R-EU_R1206 360R R-EU_R1206 2k2 R-EU_R1206 2k2 R-EU_R1206 100R R-EU_R1206 0R R-EU_R1206 0R R-EU_R1206 360R R-EU_R1206 0R R-EU_R1206 0R R-EU_R1206 2k2 R-EU_R1206 2k2 R-EU_R1206 360R R-EU_R1206 360R R-EU_R1206 360R R-EU_R1206 2k2 R-EU_R1206 2k2 R-EU_R1206 6k8 R-EU_R1206 LED3MM
SMBJ SMBJ SMB SMBJ SMBJ NME DIL08 LED3MM LED3MM SH22,5A 78XXL DIL08 DIL08 DIL40 ML10_ISP HC49/S R1206 R1206 R1206 R0805 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 R1206 LED3MM
diode diode diode diode diode dc-dc-converter optocoupler led led fuse v-reg IO IO ATMEL SPECIAL crystal rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl rcl led
186
RX0 RX0LED RX1LED S1 S2 S3 T1 TX0 TX0LED TX1LED X X1 X3 X4
HCPL2601
2N7002 HCPL2601
90V
HCPL2601 LED3MM LED3MM DIP02YL DIP05YL DIP02YL 2N7002 HCPL2601 LED3MM LED3MM CG90 AK500/5 AK500/3 AK500/5
DIL08 LED3MM LED3MM DIP02YL DIP05YL DIP02YL SOT23 DIL08 LED3MM LED3MM BLESKOJISTKA AK500/5 AK500/3 AK500/5
optocoupler led led switch-dil switch-dil switch-dil Transistor optocoupler led led SPECIAL con-ptr500 con-ptr500 con-ptr500
187