2016-03-15
Az utóbbi időben adódott egy feladat Arduino Mega panelen, amivel sajnos nem tudtam megküzdeni. Az Arduino egy baromi jó elektronikai LEGO. Leegyszerűsíti a program fejlesztést, a fordítást, a letöltést, nem kell forrasztgatni, nem kell táp (USB), és kész modulok vannak hozzá, szinte minden elektronika kezeléséhez. Mint a LEGO-nak megvannak a maga saját szabályai, ami leegyszerűsíti a munkát, de korlátozza is a lehetőségeket. Például az eszköz drágább, mintha egy csupasz uC-t venne az ember (főleg magyar kereskedőknél). Ez egy darabnál nem gond, a könnyebb kezelhetőség és a gyorsabb fejlesztés sokkal nagyobb előny. Az Arduino Uno mindkét oldalán végigfut egy foglalat sor, amire előre gyártott modulokat, "Shield"-eket lehet rádugni. Az Ardiono Uno meglévő pin-ek kiosztása szabványos, de az egyes változatoknál a többi pin változik. Amíg előre elkészített "Shield"-eket csatlakoztatunk rá, addig rendben van a dolog, sőt nagyon klassz. A láb számozási rendszer elfedi az eredeti AVR port/láb kiosztást. Nem rossz koncepció a hardware-t elfedő egységes felület definiálása, de az eredeti Arduino Uno után megjelent verzióknál kiderül, hogy ez nem sikerült teljesen. Az a benyomásom, hogy a fejlődés jelentősen túlszárnyalta az eredeti koncepciót.
Visszatérve, több érdekes tanulsága volt a project-nek. Az egyik, hogy jobban megismerkedtem az Arduino-hoz szabadon letölthető fejlesztő környezettel, az egyes eszközöket kezelő modulokkal, amit az Arduino-ban "Libraries"-nak neveznek. Az egyes modulok C++-ban megírt objektumok, amelyeknek egyszerű a felhasználásuk. Mindegyikhez melléklenek felhasználási minta programo(ka)t. Mivel GNU rendszer, sokan tesznek közzé hozzá készített objektumokat, amiknek változó a minőségük. A használatuk rendben van, de ha az ember hozzá akar nyúlni, akkor kiderül hogy a kód olvashatóságával baj van. Eddig is figyelmet fordítottam a kódom olvashatóságára, mert előfordul az ember praxisában, hogy egy régebbi programban valamit módosítani kell. És ha mondjuk nem is egy nyúlfaroknyi programról van szó... Igyekezni fogok, hogy a közzé tett program kódjaim mások számára is érthetőek legyenek. Igaz, ehhez részetekről sem ártana egy néha egy kis visszajelzés. Rádöbbentem, hogy az Arduino-ban ugyan az a GCC fordító dolgozik, mint amit eddig is használtam. Sőt a mappái között turkálva megtalálható az Arduino teljes C/C++ forráskódja is. A "Libraries" is forráskódban van. Ez egy nagy pirospont.
A C++ a C továbbfejlesztése (persze vannak akik mást mondanak), jelenleg még nem vagyok penge benne. Különben nincs egyszerű dolga a földi halandó szakinak, ha C++-t akar tanulni, mintha mindegyik Google-val turkált magyar leírást elméleti matematikus írta volna. Azt hiszem a C, meg a C++ is hardware közelében :) tudja megmutatni az erejét. Akkor lesz standard I/O, ha majd megírjuk hozzá :). Megbarátkozva azzal a gondolattal, hogy már C-ben is rosszabb és lassabb kódot írok, mintha mondjuk assembly-ben dolgoznék, azt hiszem érdemes C++-ban dolgozni. Úgy látom az az előnye, hogy egybe lehet kapcsolni, egy objektumba, az adott eszköz kezeléséhez szükséges függvényeket és adatokat. Amúgy ugyanazokat a függvényeket kell megírnunk, mint amivel C-ben oldanánk meg a feladatot. Viszont nem lesz szükség globális függvény és adat definíciókra, mert az objektum függvényei hozzáférnek az objektum adataihoz. Egy nagyobb project-nél ez fontos. Az objektumok megkönnyítik a feladat felosztását modulokra, iletve korábban megírt kód újra használatát.
Az első objetum a "morse" volt, amit az Arduino-val kapcsolatban valaki feltett oktató anyagként. Nézzük ennek hagyományos megvalósítását C-ben. A program egy LED-en a közismert SOS jelzést villogtatja Morse kóddal.
/*******************************************************************************
* Author - Kiraly Tibor
* http://www.tkiraaly.hu
* Date - 2016.03.15.
* Chip - Atmel AMega8
* Compiler - avr-gcc ( WinAVR)
*
* morse.c - demo program
*
********************************************************************************
* PonyProg Configuration and Security Bits (bepipalva):
*
* CKSEL3, CKSEL2, CKSEL1, CKSEL0
* P P - - 0011 Calibrated Internal RC Oscillator 4MHz
*
* Calibrated Internal RC Oscillator
* SUT1 SUT0
* - P Slowly rising power
*
*******************************************************************************/
#define F_CPU 4 MHZ
#include "tkiraaly_atmega8.h"
#include <util/delay.h>
#define LED_BE PB0_0
#define LED_KI PB0_1
#define LED_KIMENET PB0_OUTPUT
int main( void);
void pont( void);
void vonal( void);
void szunet( void);
int main( void)
{
LED_KI;
LED_KIMENET;
for(;;)
{
pont();
pont();
pont();
szunet();
vonal();
vonal();
vonal();
szunet();
pont();
pont();
pont();
szunet();
szunet();
}
}
void pont( void)
{
LED_BE;
szunet();
LED_KI;
szunet();
}
void vonal( void)
{
LED_BE;
szunet();
szunet();
LED_KI;
szunet();
}
void szunet( void)
{
_delay_ms( 300);
}
Így néz ki C++-ban. Olvasgattam a GCC leírását, hogyan kellene módosítani a "fordit.bat"-ot .cpp fordításához, de csak a program nevét kellett átírni benne .c-ről .cpp-re. Korábban a két .HEX között csak 10 byte méret különbség volt, később ez 50 byte-ra (10%) nőtt. Gondolom a fordító sebesség/memória optimalizálását lehetne változtatni.
/*******************************************************************************
* Author - Kiraly Tibor
* http://www.tkiraaly.hu
* Date - 2016.03.15.
* Chip - Atmel AMega8
* Compiler - avr-gcc ( WinAVR)
*
* morse - cpp demo objektum
*
********************************************************************************
* PonyProg Configuration and Security Bits (bepipalva):
*
* CKSEL3, CKSEL2, CKSEL1, CKSEL0
* P P - - 0011 Calibrated Internal RC Oscillator 4MHz
*
* Calibrated Internal RC Oscillator
* SUT1 SUT0
* - P Slowly rising power
*
*******************************************************************************/
#define F_CPU 4 MHZ
#include "tkiraaly_atmega8.h"
#include <util/delay.h>
#define LED_BE PB0_0
#define LED_KI PB0_1
#define LED_KIMENET PB0_OUTPUT
class morse
{
public:
morse();
void pont();
void vonal();
void szunet();
};
int main( void) // SOS jelzes
{
morse m= morse();
for(;;)
{
m.pont();
m.pont();
m.pont();
m.szunet();
m.vonal();
m.vonal();
m.vonal();
m.szunet();
m.pont();
m.pont();
m.pont();
m.szunet();
m.szunet();
}
}
morse::morse()
{
LED_KI;
LED_KIMENET;
}
void morse::szunet()
{
_delay_ms( 300);
}
void morse::pont()
{
LED_BE;
szunet();
LED_KI;
szunet();
}
void morse::vonal()
{
LED_BE;
szunet();
szunet();
LED_KI;
szunet();
}
Az objektum definíciója a C++-ban a "class". Ebben definiáljuk az objektum függvényeit és adatait, ezek lehetnek "privat:" meg "public:". Ebben most nincs "privat:" rész. A "privat:" változókat és függvényeket csak az objektum függvényei érhetik el. Értelem szerűen a "public:" dolgokat pedig a programunkból kívülről is elérhetjük. Attól, hogy van egy "class"-sunk, azzal még nem tud a program dolgozni, létre kell hozni belőle legalább egy példányt. Ez egy egyszerű objektum, nincsenek adatai sem, de az objektum példány létrehozásakor történik meg az adatok számára a memóriában a helyfoglalás. Az objektumnak kötelező, hogy legyen egy "construktor"-a, ez egy függvény aminek a neve megegyezik a "class" nevével, és kizárólag az objektum létrehozásakor fut le. Tehát ide lehet tenni a vátozók kezdeti értékadását... stb. A "class morse"-ban leírt objektumból a "main()"-ban "morse m= morse()" bemondással hozunk létre egy "m" objetumot. Lehet persze megszüntető "destruktor" függvényt is definiálni, AVR-ek esetében most nem látom ennek szükségességét. A "main()" után található a "morse" objektum tagfüggvényeinek definíciói. A C++-ban ha a függvénynek nincs paramétere, nem kell beírni a void kulcsszót. Soxor láttam, hogy egyetlen objektumot hoznak létre csupán, azután a tagfüggvényeit hívogatják. A minta programok alapján ne pipáljuk ki, hogy a C++ is megvolt. Számos érdekes dolgot lehet még művelni benne, sok olyat is, amit még nekem is meg kell fejtenem. Már most elárulhatom, lesz még folytatása a C++ témának :).
Itt a vége, fuss el véle, legytek az én vendégeim, innen letölthetitek a teljes programokat, miegymást, összecsomagolva.