<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Starter Kit</title>
	<atom:link href="http://starter-kit.nettigo.pl/feed/" rel="self" type="application/rss+xml" />
	<link>http://starter-kit.nettigo.pl</link>
	<description>Od zera do ... czyli o moich walkach z Arduino</description>
	<lastBuildDate>Wed, 04 Jan 2012 15:47:35 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>TLC5940 czyli co najmniej 16 dodatkowych pinów PWM w Arduino</title>
		<link>http://starter-kit.nettigo.pl/2012/01/tlc5940-czyli-co-najmniej-16-dodatkowych-pinow-pwm-w-arduino/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tlc5940-czyli-co-najmniej-16-dodatkowych-pinow-pwm-w-arduino</link>
		<comments>http://starter-kit.nettigo.pl/2012/01/tlc5940-czyli-co-najmniej-16-dodatkowych-pinow-pwm-w-arduino/#comments</comments>
		<pubDate>Wed, 04 Jan 2012 09:14:13 +0000</pubDate>
		<dc:creator>sprae</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[led]]></category>
		<category><![CDATA[pwm]]></category>
		<category><![CDATA[tlc5940]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=907</guid>
		<description><![CDATA[TLC5940 to układ scalony zawierający 16 wyjść PWM. Generator PWM układu ma rozdzielczość 12 bitów czyli 4096 stopni wypełnienia. Jedną z głównych zalet tego chipu jest możliwość podłączenia do jego wyjść bezpośrednio diod LED (bez rezystorów). Jego wyjścia mogą wytrzymać do 120 mA obciążenia. Wyprowadzenia układu TLC5940 OUT0..OUT15 &#8211; 16 wyjść PWM generatorów układu. Wyjścia [...]]]></description>
			<content:encoded><![CDATA[<div>
<h1 dir="ltr"></h1>
<p style="text-align: left;" dir="ltr"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/intro.jpg"><img class="aligncenter size-full wp-image-909" title="intro" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/intro.jpg" alt="" width="500" height="375" /></a></p>
<p dir="ltr"><a title="TLC5940NT na Nettigo" href="http://nettigo.pl/products/50">TLC5940</a> to układ scalony zawierający 16 wyjść PWM. Generator PWM układu ma rozdzielczość 12 bitów czyli 4096 stopni wypełnienia. Jedną z głównych zalet tego chipu jest możliwość podłączenia do jego wyjść bezpośrednio diod LED (bez rezystorów). Jego wyjścia mogą wytrzymać do 120 mA obciążenia.</p>
<h3 dir="ltr">Wyprowadzenia układu TLC5940</h3>
<p><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940.png"><img class="aligncenter size-full wp-image-914" title="tlc5940" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940.png" alt="" width="500" height="826" /></a></p>
<p><span id="more-907"></span></p>
<p dir="ltr"><strong>OUT0..OUT15</strong> &#8211; 16 wyjść PWM generatorów układu. Wyjścia są typu “otwarty kolektor” czyli przewodzą albo stan niski, albo stan nieustalony. Wyjścia można obciążać maksymalnie od 5 do 120 mA. Prąd wyjść reguluje się za pomocą wyprowadzenia IREF i/lub specjalnych rejestrów “DC” układu.</p>
<p dir="ltr"><strong>VPRG</strong> &#8211; Wejście trybu programowania układu &#8211; podłączasz do GND w Arduino.<br />
Stan niski to programowanie rejestrów wypełnienia PWM (GS), stan wysoki to programowanie rejestrów korekcji prądu wyjść (DC).</p>
<p dir="ltr"><strong>SIN</strong> &#8211; Wejście szeregowe  - podłączasz do pinu cyfrowego 11 w Arduino UNO</p>
<p dir="ltr"><strong>SCLK</strong> &#8211; Wejście szeregowe zegara zatwierdzającego bity danych &#8211; podłączasz do pinu cyfrowego 13 w Arduino UNO</p>
<p dir="ltr"><strong>XLAT</strong> &#8211; Wejście zatwierdzające wysłane dane &#8211; podłączasz do pinu cyfrowego 9 w Arduino UNO</p>
<p dir="ltr"><strong>BLANK</strong> &#8211; Wejście wyłączania wyjść i resetowania liczników generatora PWM &#8211; podłączasz do pinu cyfrowego 10 w Arduino UNO</p>
<p dir="ltr"><strong>GND</strong> &#8211; Wejście zasilania masa &#8211; podłączasz do GND w Arduino</p>
<p dir="ltr"><strong>VCC</strong> &#8211; Wejście zasilania +5 V &#8211; podłączasz do pinu zasilania 5V w Arduino</p>
<p dir="ltr"><strong>IREF</strong> &#8211; Wejście ustalania prądu na wyjściach OUT. Do wejścia podłączasz rezystor o oporze dostosowanym do prądu wyjścia, gdzie druga noga rezystora jest podłączona do GND.</p>
<p dir="ltr">Prąd wyjścia oblicza się ze wzoru:</p>
<pre dir="ltr">Imax = (Vref / Rref) * 31,5 = (1.24 / Rref) * 31,5</pre>
<p dir="ltr">Rref = Wartość oporu rezystora podłączonego między IREF i GND.</p>
<p dir="ltr">Przyjmując, że diody LED wymagają prądu 20 mA, to rezystor powinien mieć wartość 2 KOhm.</p>
<p dir="ltr"><strong>DCPRG</strong> &#8211; Wejście wybierania danych dokładnej regulacji prądu wyjścia &#8211; podłączasz do +5V</p>
<p dir="ltr"><strong>GSCLK</strong> &#8211; Wejście zegara, którego impulsy powodują zliczanie przez liczniki generatorów PWM układu &#8211; podłączasz do pinu cyfrowego 3 w Arduino UNO</p>
<p dir="ltr"><strong>SOUT</strong> &#8211; Szeregowe wyjście danych z układu. W konfiguracji wielu układów złączonych szeregowo to wyjście jest podłączone do SIN następnego układu.</p>
<p dir="ltr"><strong>XERR  </strong>- Wyjście błędów układu. Jeśli układ się przegrzeje, lub jakaś dioda LED podłączona do wyjść generatora PWM się przepali, na wyjściu pojawią się impulsy stanu niskiego. Wyjście jest typu “otwarty dren”, co powoduje, że ma albo stan niski, albo nieustalony.</p>
<h3 dir="ltr">Podłączenie układu TLC5940 do Arduino</h3>
<p><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940_arduino.png"><img class="aligncenter size-full wp-image-915" title="tlc5940_arduino" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940_arduino.png" alt="" width="565" height="565" /></a></p>
<p dir="ltr">Schemat przedstawia sposób podłączenia układu do Arduino. Połączenie zabiera 5 cyfrowych pinów Arduino. Do wyjść od OUT0 do OUT15 można podłączyć bezpośrednio diody LED, katodą do OUT, anodą do 5V (pod warunkiem zastosowania odpowiedniego rezystora IREF). W przykładzie podłączyłem diodę LED do wyjścia OUT1.</p>
<div dir="ltr">
<table>
<colgroup>
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" /></colgroup>
<tbody>
<tr>
<td>
<p style="text-align: center;" dir="ltr"><strong>Noga TLC5940</strong></p>
</td>
<td style="text-align: center;">
<p dir="ltr"><strong>Pin Arduino</strong></p>
</td>
<td style="text-align: center;">
<p dir="ltr"><strong>Nazwa sygnału</strong></p>
</td>
<td>
<p style="text-align: center;" dir="ltr"><strong>Checklista</strong></p>
</td>
</tr>
<tr>
<td>
<p dir="ltr">27</p>
</td>
<td>
<p dir="ltr">GND</p>
</td>
<td>
<p dir="ltr">VPRG</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">26</p>
</td>
<td>
<p dir="ltr">Digital 11</p>
</td>
<td>
<p dir="ltr">SIN</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">25</p>
</td>
<td>
<p dir="ltr">Digital 13</p>
</td>
<td>
<p dir="ltr">SCLK</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">24</p>
</td>
<td>
<p dir="ltr">Digital 9</p>
</td>
<td>
<p dir="ltr">XLAT</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">23</p>
</td>
<td>
<p dir="ltr">Digital 10</p>
</td>
<td>
<p dir="ltr">BLANK</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">22</p>
</td>
<td>
<p dir="ltr">GND</p>
</td>
<td>
<p dir="ltr">GND</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">21</p>
</td>
<td>
<p dir="ltr">5V</p>
</td>
<td>
<p dir="ltr">VCC</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">20</p>
</td>
<td>
<p dir="ltr">&#8211;Rezystor&#8211;GND</p>
</td>
<td>
<p dir="ltr">IREF</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">19</p>
</td>
<td>
<p dir="ltr">5V</p>
</td>
<td>
<p dir="ltr">DCPRG</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">18</p>
</td>
<td>
<p dir="ltr">Digital 3</p>
</td>
<td>
<p dir="ltr">GSCLK</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">17</p>
</td>
<td>
<p dir="ltr">do drugiego TLC5940</p>
</td>
<td>
<p dir="ltr">SOUT</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">16</p>
</td>
<td></td>
<td>
<p dir="ltr">XERR</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
</tbody>
</table>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3 dir="ltr">Podłączenie do Arduino MEGA</h3>
<p><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940mega_bb.png"><img class="aligncenter size-full wp-image-917" title="tlc5940mega_bb" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940mega_bb.png" alt="" width="526" height="691" /></a></p>
<p dir="ltr">Ponieważ Arduino MEGA ma wyjście SPI w nieco innym miejscu, podłączenie do niego TLC5940 wymaga innego schematu elektrycznego.</p>
<div dir="ltr">
<table>
<colgroup>
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" /></colgroup>
<tbody>
<tr>
<td>
<p style="text-align: center;" dir="ltr"><strong>Noga TLC5940</strong></p>
</td>
<td style="text-align: center;">
<p dir="ltr"><strong>Pin Arduino MEGA</strong></p>
</td>
<td style="text-align: center;">
<p dir="ltr"><strong>Nazwa sygnału</strong></p>
</td>
<td>
<p style="text-align: center;" dir="ltr"><strong>Checklista</strong></p>
</td>
</tr>
<tr>
<td>
<p dir="ltr">27</p>
</td>
<td>
<p dir="ltr">GND</p>
</td>
<td>
<p dir="ltr">VPRG</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">26</p>
</td>
<td>
<p dir="ltr">Digital 51</p>
</td>
<td>
<p dir="ltr">SIN</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">25</p>
</td>
<td>
<p dir="ltr">Digital 52</p>
</td>
<td>
<p dir="ltr">SCLK</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">24</p>
</td>
<td>
<p dir="ltr">Digital 11</p>
</td>
<td>
<p dir="ltr">XLAT</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">23</p>
</td>
<td>
<p dir="ltr">Digital 12</p>
</td>
<td>
<p dir="ltr">BLANK</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">22</p>
</td>
<td>
<p dir="ltr">GND</p>
</td>
<td>
<p dir="ltr">GND</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">21</p>
</td>
<td>
<p dir="ltr">5V</p>
</td>
<td>
<p dir="ltr">VCC</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">20</p>
</td>
<td>
<p dir="ltr">&#8211;Rezystor&#8211;GND</p>
</td>
<td>
<p dir="ltr">IREF</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">19</p>
</td>
<td>
<p dir="ltr">5V</p>
</td>
<td>
<p dir="ltr">DCPRG</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">18</p>
</td>
<td>
<p dir="ltr">Digital 9</p>
</td>
<td>
<p dir="ltr">GSCLK</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">17</p>
</td>
<td>
<p dir="ltr">do drugiego TLC5940</p>
</td>
<td>
<p dir="ltr">SOUT</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
<tr>
<td>
<p dir="ltr">16</p>
</td>
<td></td>
<td>
<p dir="ltr">XERR</p>
</td>
<td>
<input type="checkbox" /></td>
</tr>
</tbody>
</table>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3 dir="ltr">Programowanie układu TLC5940</h3>
<p>&nbsp;</p>
<pre>#include &lt;Tlc5940.h&gt;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();

  Tlc.set(1, 2048);

  <span style="color: #cc6600;">while</span> (Tlc.update());
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<pre></pre>
<p>&nbsp;</p>
<p dir="ltr">Społeczność Arduino napisała również dla układu TLC5940 odpowiednią bibliotekę. Przykład zapala diodę LED podłączoną do wyjścia OUT1 na połowę jasności.</p>
<p>&nbsp;</p>
<p dir="ltr">Programowanie układu rozpoczyna się od załadowania odpowiedniej biblioteki poprzez “#include &lt;Tlc5940.h&gt;”. Następnie w funkcji “setup” biblioteka musi zostać zainicjowana za pomocą metody “Tlc.init();”. Metodę “Tlc.update()” umieściłem w pętli “while”. Metoda ta odpowiada za wysłanie danych do do układu i zaktualizowanie jego rejestrów. Trzeba wywoływać ja tak długo aż zwróci wartość 0. Jeśli nie jest to krytyczne, możesz umieścić też tą metodę w funkcji “loop”.</p>
<p>&nbsp;</p>
<p dir="ltr">Za ustawianie poszczególnych wyjść PWM układu TLC5940 odpowiada metoda “Tlc.set”. Jej pierwszym parametrem jest numer wyjścia od 0 do 15, a drugim wartość wypełnienia generatora PWM od 0 do 4095.</p>
<p>&nbsp;</p>
<p dir="ltr">Bibioteka wykorzystuje do swojej pracy układy Timer1 i Timer2 oraz SPI kontrolera Arduino, dlatego warto uważać, czy nie koliduje to z inną biblioteką w twoim projekcie.</p>
<p>&nbsp;</p>
<p dir="ltr">Metody obiektu Tlc:</p>
<ul>
<li><strong>Tlc.init()</strong> Uaktywnia działanie biblioteki i inicjuje komunikacje z układem TLC5940. Ma opcjonalny argument, który jest liczbą od 0 do 4095. Argument ustawia początkową wartość wypełnienia na wszystkich wyjściach układu. Wywołanie metody bez argumentu sprawia, że wyjścia ustawiają się na 0.</li>
<li><strong>Tlc.clear()</strong> Ustawia wszystkie wyjścia na wartość 0.</li>
<li>
<p dir="ltr"><strong>Tlc.setAll(value)</strong> Ustawia wszystkie wyjścia na wartość wypełnienia podaną w argumencie (od 0 do 4095).</p>
<p>&nbsp;</li>
<li><strong>Tlc.set(out, value)</strong> Ustawia wyjście określone w argumencie “out” (od 0 do 15) na wartość określoną w argumencie “value” (od 0 do 4095).</li>
<li><strong>Tlc.get(out)</strong> Zwraca wartość wypełnienia dla wyjścia podanego w argumencie “out” (od 0 do 15).</li>
<li><strong>Tlc.update()</strong> Wysyła dane do układu. Zwraca 0 jeśli dane zostaną zatwierdzone lub 1 jeśli jeszcze nie.</li>
</ul>
<p>&nbsp;</p>
<h3 dir="ltr">Wskaźnik diodowy</h3>
<pre>#include &lt;Tlc5940.h&gt;

<span style="color: #cc6600;">void</span> tlc_progress(<span style="color: #cc6600;">word</span> value, <span style="color: #cc6600;">word</span> prefix)
{
  <span style="color: #cc6600;">byte</span> out_num = value / 4096;
  <span style="color: #cc6600;">word</span> out_brg = value % 4096;

  <span style="color: #cc6600;">for</span> (<span style="color: #cc6600;">byte</span> out=0; out&lt;16; out++)
  {
    <span style="color: #cc6600;">if</span> (out &lt; out_num)
    {
      Tlc.set(out, prefix);
    }

    <span style="color: #cc6600;">else</span> <span style="color: #cc6600;">if</span> (out == out_num)
    {
      Tlc.set(out, out_brg);
    }

    <span style="color: #cc6600;">else</span>
    {
      Tlc.set(out, 0);
    }
  }
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">int</span> value = <span style="color: #cc6600;">analogRead</span>(0);
  value = <span style="color: #cc6600;">map</span>(value, 0, 1023, 0, 65535);
  tlc_progress(value, 4095);
  Tlc.update();
}</pre>
<p dir="ltr">Jako temat przykładu użycia biblioteki wykorzystałem pomysł na wskaźnik typu “linijka diodowa”. Zakłada on podłączenie do wszystkich pinów OUT układu TLC5940 diod LED. Działanie wskaźnika można opisać w ten sposób, że im program dostanie większe napięcie na pin Arduino Analog 0 tym więcej diod na kolejnych wyjściach się będzie świeciło. Przy napięciu 5V świecą się wszystkie diody. Dodatkową zaletą wykorzystania PWM jest to, że ostatnia zapalona dioda wskaźnika zapala się stopniowo wraz ze wzrostem napięcia na wejściu.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Najważniejszą funkcją programu jest “tlc_progress”. Steruje ona zapalaniem diod podłączonych do układu. Jej pierwszy arguement to wartość od 0 do 65535. Im większa liczba tym kolejne diody wskaźnika się płynnie zapalają. Kolejnym argumentem funkcji jest argument jasności świecenia lampek poprzedzających ostatnią zapaloną. Jeśli ustawisz mu wartość 0, to będzie paliła się na raz tylko jedna dioda &#8211; imitując wskazówkę.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/single_low.jpg"><img class="alignnone size-full wp-image-913" title="single_low" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/single_low.jpg" alt="" width="250" height="188" /></a><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/single_hi.jpg"><img class="alignnone size-full wp-image-912" title="single_hi" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/single_hi.jpg" alt="" width="250" height="188" /></a></strong></strong></p>
<p>&nbsp;</p>
<p dir="ltr">Jeśli wstawisz tam wartość maksymalną, czyli 4095, to będą zapalały się kolejne diody aż do maksymalnej jasności.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/multi_lo.jpg"><img class="alignnone size-full wp-image-911" title="multi_lo" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/multi_lo.jpg" alt="" width="250" height="188" /></a><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/multi_hi.jpg"><img class="alignnone size-full wp-image-910" title="multi_hi" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/multi_hi.jpg" alt="" width="250" height="188" /></a></strong></strong></p>
<p>&nbsp;</p>
<p dir="ltr">W funkcji “loop” program odczytuje wartość z pinu Analog 0. Ponieważ funkcja “analogRead” odczytuje wartości z zakresu od 0 do 1023, to trzeba je przeskalować za pomocą funkcji “map” na wartości zakresu funkcji “tlc_progress”, czyli od 0 do 65535.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Wskaźnik diodowy ma zastosowanie tam, gdzie nie liczy się zbytnia dokładność, ale ważne jest szybkie zorientowanie się co do poziomu jakiegoś sygnału. Program możesz łatwo przerobić na fajny “analogowy” termometr, obrotomierz lub wskaźnik poziomu głośności w zestawie audio.</p>
<p><iframe src="http://www.youtube.com/embed/1BIZE6g9o_I" frameborder="0" width="500" height="369"></iframe></p>
<p dir="ltr">W nagraniu do wejścia analogowego 0 podłączyłem potencjometr.</p>
<h3 dir="ltr">Inne przykłady z biblioteki układu</h3>
<p dir="ltr">Oprócz podstawowego nagłówka “Tlc5940.h”, biblioteka posiada jeszcze kilka ciekawych nagłówków z dodatkowymi funkcjami pomagającymi szybko rozwiązać pewne pomysły.</p>
<h4 dir="ltr">Fades</h4>
<p dir="ltr">Pierwszym z nich jest “tlc_fades.h”, ułatwiający tworzenie płynnie zapalających się i gasnących efektów świetlnych.</p>
<pre>#include &lt;Tlc5940.h&gt;
#include &lt;tlc_fades.h&gt;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();

  tlc_addFade(1, 0, 4095, 5000, 10000);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  tlc_updateFades();
}</pre>
<p dir="ltr">Użycie biblioteki rozpoczynasz od dodania nagłówka “#include &lt;tlc_fades.h&gt;”. Potem należy normalnie zainicjować układ w funkcji “setup”. W funkcji “loop” powinna być funkcja “tlc_updateFades();” odpowiedzialna za synchronizowanie działania całej biblioteki.</p>
<p dir="ltr">Za przygotowanie zapalania/gaśnięcia odpowiada funkcja “tlc_addFade”. Jej pierwszym argumentem jest numer wyjścia układu TLC5940 na którym ma się odbyć animacja. Kolejnym jest wartość początkowa jasności animacji (od 0 do 4095). Trzecim argumentem jest wartość końcowa jasności animacji o parametrach jak poprzednia.</p>
<p dir="ltr">Dwa ostatnie argumenty to czas w milisekundach liczony od chwili włączenia Arduino. Przedostatni mówi o tym w jakiej milisekundzie ma się rozpocząć animacja, a kolejny w jakiej ma się skończyć.</p>
<p dir="ltr">Funkcja w przykładzie tworzy animacje zapalenia się diody na wyjściu “OUT1” od wartości 0 do wartości 4095. Rozświetlenie ma się odbyć w czasie od 5 do 10 sekundy od chwili włączenia Arduino.</p>
<p dir="ltr">Można wykonać do 24 animacji na raz (wywołań funkcji “tlc_addFade”). Potem trzeba poczekać, aż któraś animacja się skończy. Pomocną przy tym funkcją jest “tlc_isFading”. Jej argumentem jest numer wyjścia, a zwraca 1 jeśli na danym wyjściu jest przewidziana jakaś animacja lub 0 jeśli nie jest.</p>
<p dir="ltr">Jest jeszcze funkcja “tlc_removeFades”. Jej argumentem jest numer wyjścia. Usuwa ona wszystkie animacje dla danego wyjścia.</p>
<pre>#include &lt;Tlc5940.h&gt;
#include &lt;tlc_fades.h&gt;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">if</span> (!tlc_isFading(1))
  {
    <span style="color: #cc6600;">unsigned</span> <span style="color: #cc6600;">long</span> time = <span style="color: #cc6600;">millis</span>();
    tlc_addFade(1, 0, 4095, time+1000, time+1250);
    tlc_addFade(1, 4095, 0, time+1500, time+2000);
  }
  tlc_updateFades();
}</pre>
<p dir="ltr">W tym przykładzie zrealizowałem animację mrugania diody LED na wyjściu OUT1. Warunek “if” sprawdza czy jest tam aktualnie animacja i jeśli nie ma to tworzy kolejną.</p>
<p dir="ltr">Animacja powstaje tak, że pobierany jest aktualny czas do zmiennej “time”. Sekundę po tym czasie rozpoczyna się rozświetlanie trwające 250 ms. Po 1.5 s zaczyna się gaśnięcie trwające 500 ms. Daje to ładny efekt płynnego mrugania.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Funkcja “tlc_updateFades” ma opcjonalny argument, którym są aktualne milisekundy. Jeśli nie odpowiada ci w animacji czas od chwili włączenia Arduino, możesz w argumencie dostarczyć swoją aktualną wartość czasu wyrażoną w milisekundach.</p>
<p><iframe src="http://www.youtube.com/embed/w1929ihNjKQ" frameborder="0" width="500" height="339"></iframe></p>
<h4 dir="ltr">Shifts</h4>
<p dir="ltr">Biblioteka “tlc_shifts.h” odpowiada za efekt przesuwania wartości wypełnienia z jednego wyjścia na kolejne.</p>
<pre>#include &lt;Tlc5940.h&gt;
#include &lt;tlc_shifts.h&gt;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">static</span> <span style="color: #cc6600;">byte</span> counter = 0;
  <span style="color: #cc6600;">if</span> (counter % 4)
    tlc_shiftUp(0);

  <span style="color: #cc6600;">else</span>
    tlc_shiftUp(4095);

  counter++;
  Tlc.update();
  <span style="color: #cc6600;">delay</span>(100);
}</pre>
<p dir="ltr">Przykład tworzy efekt “wąż świetlny”, który polega na złudzeniu, że światło w diodach ustawionych w linię pełza.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Funkcja “tlc_shiftUp” powoduje zapisanie do wyjścia OUT0 nowej wartości wypełnienia, podanej w argumencie, podczas gdy reszta wartości jest przesuwana do kolejnych wyjść (z OUT0 do OUT1, z OUT1 do OUT2 itp.). Ostatnia wartość, która była w rejestrze wypełnienia OUT15 jest zwracana przez tą funkcję (ułatwia to zapętlanie).</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Jest też odwrotnie działająca funkcja o nazwie “tlc_shiftDown”, której argument zapisuje wartość wypełnienia do OUT15 i przesuwa wartości rejestrów w dół (z OUT15 do OUT14, z OUT14 do OUT13 itp.). Zwraca ona wartość rejestru OUT0 przed przesunięciem.</p>
<p><strong><strong><br />
</strong></strong></p>
<p><iframe src="http://www.youtube.com/embed/1u2Yg2vANYc" frameborder="0" width="500" height="369"></iframe></p>
<h4 dir="ltr">Dane z pamięci programu</h4>
<p dir="ltr">Czasem zachodzi potrzeba przeniesienia na wyjścia PWM jakiś danych z pamięci. Kontroler AVR w Arduino ma kilka pamięci z których najbardziej pojemna jest pamięć Flash, gdzie przechowywany jest program. Pamięć RAM jest kilka razy mniej pojemna, dlatego najlepiej przechowuje się w niej aktualne zmienne, a nie tablice ze stałymi danymi. Dlatego autorzy biblioteki układu TLC5940 zadbali o możliwość współpracy z pamięcią programu.</p>
<pre>#include &lt;Tlc5940.h&gt;
#include &lt;tlc_progmem_utils.h&gt;

<span style="color: #cc6600;">byte</span> pwm_data[24] PROGMEM = {
  GS_DUO(4095, 3839), GS_DUO(3583, 3327), GS_DUO(3071, 2815), GS_DUO(2559, 2303), 
  GS_DUO(2047, 1791), GS_DUO(1535, 1279), GS_DUO(1023, 767), GS_DUO(511, 255)
};

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();

  tlc_setGSfromProgmem(pwm_data);

  <span style="color: #cc6600;">while</span> (Tlc.update());
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">Biblioteka “tlc_progmem_utils.h” zawiera narzędzia do obsługi danych z pamięci programu.</p>
<p dir="ltr">Aby przygotować tablice wartości wypełnienia dla rejestrów PWM układu, trzeba ją zadeklarować w odpowiedni sposób. oprócz standardowego określenia typu “byte”, nazwy tablicy “pwm_data” i jej rozmiaru, trzeba jeszcze dodać odpowiednią dyrektywę informującą, że tablica ma być przechowywana w pamięci programu a nie w normalnej pamięci RAM. Ta dyrektywa to “PROGMEM”. Rozmiar tablicy określony jest przez ilość danych potrzebnych do zaprogramowania układu. Wynika on z tego, że układ potrzebuje szesnastu 12 bitowych wartości. W języku C nie ma typu, który by przechowywał dokładnie 12 bitowe wartości. Jest albo 16 bitowy “word”, albo 8 bitowy “byte”. Aby nie było niepotrzebnych strat danych biblioteka używa 8 bitowego “byte”. 24 elementy tablicy typu “byte” wynikają z tego, że 16&#215;12 bitów /  8 (typ byte) = 24. Tablica jest poukładana od ostatniego rejestru GS OUT15 do pierwszego GS OUT0.</p>
<p dir="ltr">Aby było łatwiej układać dane, w bibliotece jest makro o nazwie GS_DUO, które ma dwa argumenty reprezentujące kolejne 2 wyjścia PWM (OUT15, OUT14 aż do OUT1, OUT0).</p>
<p dir="ltr">Wysłaniem danych z tablicy zajmuje się funkcja “tlc_setGSfromProgmem”. Jej argumentem jest nazwa tablicy z danymi do wyświetlenia.</p>
<h4 dir="ltr">Animacje z pamięci programu</h4>
<p dir="ltr">Jeśli chciałbyś wysyłać do układu TLC5940 sekwencje danych np. w postaci animacji lub danych do wyświetlacza widmowego, możesz skorzystać z biblioteki “tlc_animations.h”, która działa podobnie do poprzedniej.</p>
<pre>#include &lt;Tlc5940.h&gt;
#include &lt;tlc_animations.h&gt;

<span style="color: #cc6600;">byte</span> pwm_data[24*2] PROGMEM = {
  GS_DUO(4095, 0), GS_DUO(4095, 0), GS_DUO(4095, 0), GS_DUO(4095, 0), 
  GS_DUO(4095, 0), GS_DUO(4095, 0), GS_DUO(4095, 0), GS_DUO(4095, 0), 

  GS_DUO(0, 4095), GS_DUO(0, 4095), GS_DUO(0, 4095), GS_DUO(0, 4095), 
  GS_DUO(0, 4095), GS_DUO(0, 4095), GS_DUO(0, 4095), GS_DUO(0, 4095)
};

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">if</span> (!tlc_onUpdateFinished)
  {
    tlc_playAnimation(pwm_data, 2, 500);
  }
}</pre>
<p dir="ltr">Tym razem wielkość tablicy “pwm_data” musi być pomnożona o liczbę klatek animacji. W tym przykładzie stworzyłem 2 klatki animacji. Pierwsza klatka to maksymalne świecenie diod wyjść nieparzystych, a druga to maksymalne świecenie diod parzystych. Klatki rozdzieliłem pustą linia.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Za uruchomienie animacji odpowiada funkcja “tlc_playAnimation”. Jej pierwszy argument to tablica z danymi animacji, drugi to liczba klatek animacji, a ostatni to czas po jakim mają się wyświetlać kolejne klatki animacji. Czas jest wyrażony w cyklach licznika PWM, a jeden cykl trwa 1,024 ms. Zatem 500 oznacza, że klatki będą pojawiać się co około pół sekundy.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Ponieważ funkcja “tlc_playAnimation” nie zapętla animacji, dlatego po wyświetleniu ostatniej klatki animacji trzeba ją uruchamiać ponownie. Zmienna “tlc_onUpdateFinished” zwraca 0 jeśli animacja się skończyła i 1 jeśli wciąż trwa. Dlatego zastosowałem ją w “if” do zapętlania animacji.</p>
<h3 dir="ltr">Korekcja prądu wyjść układu TLC5940</h3>
<p dir="ltr">Jeśli podłączasz do wyjść układu TLC5940 różnokolorowe diody LED, często ich światło nie jest jednakowo jasne. Można to ustawić tworząc skomplikowane wzory przeliczające wartość rejestru wypełnienia i ograniczając rozdzielczość kanałów PWM. Ten układ ma jednak dodatkowy sprzęt, który pomoże dokonać korekty jasność.</p>
<p dir="ltr">Rejestry DC (Dot Correct) odpowiadają za dokładniejszą regulację prądu na wyjściach PWM. Ich rozdzielczość jest 6 bitowa, czyli przyjmują wartości od 0 do 63. Dzielą one wartość maksymalnego prądu na wyjściu ustalanego przez rezystor na wejściu IREF.</p>
<p dir="ltr">Aby dobrać się do rejestrów DC należy dokonać drobnej zmiany w układzie elektrycznym i bibliotece programu.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940vprg_bb.png"><img class="aligncenter size-full wp-image-921" title="tlc5940vprg_bb" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940vprg_bb.png" alt="" width="566" height="553" /></a><br />
</strong></strong></p>
<p dir="ltr">Na schemacie dodałem kolejny przewód do cyfrowego pinu Arduino. Przewód prowadzi od wejścia VPRG (noga 27 układu TLC5940) do pinu cyfrowego 8 w Arduino. W Arduino MEGA należy podłączyć ten sygnał do cyfrowego pinu 50.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Potem w katalogu biblioteki układu znajdź plik “tlc_config.h”, który odpowiada za ustawianie parametrów biblioteki i znajdz w nim definicję “#define VPRG_ENABLED 0” i zmień na “#define VPRG_ENABLED 1”. Uaktywni to dodatkowe elementy biblioteki odpowiedzialne za rejestry DC.</p>
<pre>#include &lt;Tlc5940.h&gt;
#include &lt;tlc_progmem_utils.h&gt;

<span style="color: #cc6600;">byte</span> DC_data[12] PROGMEM = {
  DC_QUARTET(3, 3, 3, 3), DC_QUARTET(3, 3, 3, 3), 
  DC_QUARTET(14, 14, 14, 14), DC_QUARTET(14, 14, 14, 14)
};

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init(4095);

  tlc_setDCfromProgmem(DC_data);

  <span style="color: #cc6600;">while</span> (Tlc.update());
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">Rejestry najłatwiej obsługiwać za pomocą biblioteki “tlc_progmem_utils.h”.</p>
<p dir="ltr">Ustawianie rejestrów DC wygląda podobnie do opisywanej wcześniej funkcji ustawiania rejestrów wypełnienia z pamięci programu Flash. W tym przypadku potrzebna jest tablica w pamięci programu zawierająca 12 elementów typu “byte”. Do łatwego ustawiania poszczególnych rejestrów potrzebne są makra “DC_QUARTET”, które mają po 4 argumenty, reprezentujące poszczególne wyjścia w odwrotnej kolejności. Do ustawienia wszystkich rejestrów potrzeba czterech makr.</p>
<p dir="ltr">Za zapisanie danych z tablicy do układu TLC5940 odpowiada funkcja “tlc_setDCfromProgmem”, której jedynym argumentem jest nazwa tablicy z danymi rejestrów DC.</p>
<p dir="ltr">W przykładzie miałem do wyjść OUT0 &#8211; OUT7 podłączone diody zielone i do wyjść OUT8 &#8211; OUT15 diody czerwone. Diody czerwone były bardzo jaskrawe i dużo jaśniejsze od zielonych, dlatego dostały wartość 3, a diody zielone, mniej jaskrawe wartość 14.</p>
<p dir="ltr">Wszystko dlatego ma tak niskie wartości, aby nie raziło, a jednocześnie świeciło na tyle jasno by widzieć dokładnie które diody świecą.</p>
<h3 dir="ltr">Sterowanie serwomechanizmem</h3>
<p dir="ltr">Jak dowidziałeś się z poprzedniego wpisu, kąt obrotu serwomechanizmu zależy od szerokości impulsu PWM. Do układu TLC5940 możesz też podłączyć 16 serwomechanizmów.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940servo_bb.png"><img class="aligncenter size-full wp-image-920" title="tlc5940servo_bb" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940servo_bb.png" alt="" width="597" height="640" /></a><br />
</strong></strong></p>
<p dir="ltr">Wyjścia układu TLC 5940 są typu “otwarty kolektor” i przekazują tylko albo stan niski, albo nieustalony. Dlatego aby wysterować wejście sterujące sewomechanizmu, trzeba podłączyć rezystor PullUp. Rezystor ten wg. dokumentacji biblioteki powinien mieć od 2k do 5k Ohm.</p>
<pre>#include &lt;Tlc5940.h&gt;

#define SERVO_MIN_WIDTH 177
#define SERVO_MAX_WIDTH 571

#include &lt;tlc_servos.h&gt;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  tlc_initServos();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">int</span> value = <span style="color: #cc6600;">analogRead</span>(0);
  value = <span style="color: #cc6600;">map</span>(value, 0, 1023, 0, 180);

  tlc_setServo(1, value);
  Tlc.update();
}</pre>
<p dir="ltr">Do używania serwomechanizmów przez układ służy specjalna biblioteka “tlc_servos.h”. Ponieważ biblioteka ma ustawione dość bezpieczne czasy minimalnego i maksymalnego impulsu serwomechanizmu, dlatego przed załadowaniem biblioteki trzeba zdefiniować własne ich czasy w makrach “SERVO_MIN_WIDTH” i “SERVO_MAX_WIDTH”. Wartości te są wyrażone w liczbie czasu wypełnienia w rejestrze PWM czyli od 0 do 4095. Pierwsza wartość wypełnienia jest przy serwie ustawionym na 0 stopni, a druga na 180 stopni. Czas wypełnienia dla 1000 mikrosekund to 204. Moje serwomechanizmy (HTX900) mają zakres od 870 do 2800 mikrosekund, dlatego dałem im wartości wypełnienia od 177 do 571. Wartości te można obliczyć ze wzoru:</p>
<pre dir="ltr">GSvalue = czas_impulsu_us * 204 / 1000</pre>
<p dir="ltr">W funkcji “setup” biblioteka wymaga wywołania funkcji “tlc_initServos();”, której opcjonalny argument to kąt obrotu serw podłączonych do wyjść.</p>
<p dir="ltr">Serwomechanizmy ustawia się za pomocą funkcji “tlc_setServo”, której pierwszym argumentem jest numer wyjścia, do którego podłączone jest serwo, a drugim kąt obrotu jego osi.</p>
<p dir="ltr">Na końcu należy oczywiście zatwierdzić wysłanie danych do układu za pomocą metody “Tlc.update();”</p>
<p dir="ltr">Dane do serwomechanizmu można też przekazywać za pomocą metody “Tlc.set”, pamiętając by nie przekraczać minimalnego i maksymalnego impulsu. Zanim poda się parametr wypełnienia dla tej metody, trzeba go odjąć od liczby 4095 (“Tlc.set(4095-imp_width);”).</p>
<p dir="ltr">Korzystając z biblioteki serwomechanizmów, do można do wyjść podłączyć też inne elementy, pamiętając o tym, że teraz generują impulsy z częstotliwością 50 Hz.</p>
<p dir="ltr">Program w przykładzie odczytuje wartość z wejścia analogowego i po przeskalowaniu ustawia oś serwomechanizmu proporcjonalnie do napięcia na wejściu.</p>
<p dir="ltr">UWAGA:</p>
<p dir="ltr">Biblioteka wymaga drobnej poprawki, gdyż wynik zakresu impulsu moich serwomechanizmów wywołał w niej błąd. W katalogu biblioteki w pliku “tlc_servos.h” funkcja “tlc_angleToVal” powinna wyglądać tak:</p>
<pre><span style="color: #7e7e7e;">/** Converts and angle (0 - SERVO_MAX_ANGLE) to the inverted tlc channel value</span>
<span style="color: #7e7e7e;">    (4095 - 0). */</span>
uint16_t tlc_angleToVal(uint8_t angle)
{
    <span style="color: #cc6600;">return</span> 4095 - SERVO_MIN_WIDTH - (
            ((uint32_t) (angle) * (uint16_t)(SERVO_MAX_WIDTH - SERVO_MIN_WIDTH))
            / SERVO_MAX_ANGLE);
}</pre>
<h3 dir="ltr">Sterowanie silnikiem</h3>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940motor_bb.png"><img class="aligncenter size-full wp-image-918" title="tlc5940motor_bb" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940motor_bb.png" alt="" width="598" height="570" /></a><br />
</strong></strong></p>
<p dir="ltr">Silnikiem elektrycznym steruje się jak w poprzednim wpisie za pomocą tranzystora MOSFET. Pamiętając oczywiście o zapewnieniu rezystora PullUp na wyjściu układu TLC5940.</p>
<pre>#include &lt;Tlc5940.h&gt;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">int</span> value = <span style="color: #cc6600;">analogRead</span>(0);
  value = <span style="color: #cc6600;">map</span>(value, 0, 1023, 0, 4095);

  Tlc.set(1, value);
  Tlc.update();
}</pre>
<p dir="ltr">Przykład nie różni się od poprzednich, nie wymaga specjalnej biblioteki. Steruje prędkością silnika w zależności od napięcia na analogowym wejściu 0 w Arduino, skalując wartość do poziomu zakresu wartości rejestru wypełnienia GS.</p>
<h3 dir="ltr">Łączenie wielu układów TLC5940</h3>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940duo_bb.png"><img class="aligncenter size-full wp-image-916" title="tlc5940duo_bb" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/tlc5940duo_bb.png" alt="" width="601" height="552" /></a><br />
</strong></strong></p>
<p dir="ltr">Aby zyskać kolejne wyjścia PWM należy podłączyć kolejny układ TLC5940. Nie trzeba do tego wykorzystywać żadnych dodatkowych pinów Arduino. Kolejne układy podłączamy tak samo jak ten pierwszy, przekazując kolejnym te same sygnały z jednym wyjątkiem. Sygnał SIN (noga 26) następnego układu łączysz z sygnałem SOUT (noga 17) poprzedniego. W ten sposób zyskujesz kolejne 16 wyjść PWM. Układów możesz tak łączyć dowolną ilość pamiętając, że przesadne ilości wymagają więcej modyfikacji w bibliotece sterującej.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Aby program mógł skorzystać z dodatkowych układów należy otworzyć plik z katalogu biblioteki o nazwie “tlc_config.h”. Jest w nim zdefiniowane makro “#define NUM_TLCS 1”. Liczba za nazwą odpowiada liczbie podłączonych układów TLC5940. W moim przypadku będzie to “#define NUM_TLCS 2”.</p>
<pre>#include &lt;Tlc5940.h&gt;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  Tlc.init();

  Tlc.set(1, 2048);
  Tlc.set(17, 2048);

  <span style="color: #cc6600;">while</span> (Tlc.update());
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">W programie sterującym układami prawie nic się nie zmienia. Różnica polega tylko na tym, że kolejne wyjścia mają kolejne numery. Zatem czerwona dioda LED podłączona do OUT1 pierwszego układu ma numer 1, a zielona dioda LED podłączona do wyjścia OUT1 drugiego układu ma numer 17. Jest to bardzo proste rozwiązanie.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Podobnie jest z innymi elementami biblioteki układu. W każdej ilość wyjść zwiększa się o wielokrotność układów. Biblioteki korzystające z pamięci Flash muszą mieć powiększone tablice do ilości wyjść układów.</p>
<h3 dir="ltr">Zakończenie</h3>
<p dir="ltr">TLC5940 to bardzo ciekawy układ scalony. Jego możliwości w połączeniu z Arduino są bardzo duże. Dzięki niemu możesz robić kroczące roboty, na których mięśnie składa się kilkanaście serwomechanizmów, lub zaskakujące efekty świetlne na przyszłoroczną choinkę. Zdecydowanie najciekawszym efektem jaki można zrobić przy pomocy tych układów jest wyświetlacz widmowy. Nie chciałem odbierać ci tej przygody i dlatego postaraj się zrealizować go sam :-). Tylko pamiętaj o solidnym przymocowaniu LED-ów najlepiej na płytce uniwersalnej.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/end.jpg"><img class="aligncenter size-full wp-image-908" title="end" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/end.jpg" alt="" width="500" height="375" /></a><br />
</strong></strong></p>
<h3 dir="ltr">Linki</h3>
<ul>
<li><a title="TLC5940NT na Nettigo" href="http://nettigo.pl/products/50">TLC5940NT do kupienia na Nettigo</a></li>
<li><a href="http://code.google.com/p/tlc5940arduino/">Biblioteka Tlc5940.h</a></li>
<li><a href="http://alex.kathack.com/codes/tlc5940arduino/html_r014/index.html">Opis biblioteki Tlc5940.h [en]</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2012/01/tlc5940-czyli-co-najmniej-16-dodatkowych-pinow-pwm-w-arduino/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Co to jest PWM?</title>
		<link>http://starter-kit.nettigo.pl/2012/01/co-to-jest-pwm/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=co-to-jest-pwm</link>
		<comments>http://starter-kit.nettigo.pl/2012/01/co-to-jest-pwm/#comments</comments>
		<pubDate>Mon, 02 Jan 2012 20:41:55 +0000</pubDate>
		<dc:creator>sprae</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[pwm]]></category>
		<category><![CDATA[starter-kit]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=891</guid>
		<description><![CDATA[Wstęp Wielu użytkowników Arduino zapewne zauważyło, że wśród pinów z grupy DIGITAL jest kilka oznaczonych jako “PWM” lub “~”. W tym artykule postaram się wyjaśnić co to znaczy i jak dokładnie działa. Napiszę też jak można to praktycznie wykorzystać. PWM w teorii PWM to skrót od angielskich słów “Pulse Width Modulation”, co oznacza po polsku [...]]]></description>
			<content:encoded><![CDATA[<div>
<h3 dir="ltr">Wstęp</h3>
<p dir="ltr">Wielu użytkowników Arduino zapewne zauważyło, że wśród pinów z grupy DIGITAL jest kilka oznaczonych jako “PWM” lub “~”. W tym artykule postaram się wyjaśnić co to znaczy i jak dokładnie działa. Napiszę też jak można to praktycznie wykorzystać.</p>
<h3 dir="ltr">PWM w teorii</h3>
<p dir="ltr">PWM to skrót od angielskich słów “Pulse Width Modulation”, co oznacza po polsku “Modulacja Szerokości Impulsu”.</p>
<p dir="ltr">W życiu codziennym posługujesz się przełącznikami. One powodują, że włączasz jakieś urządzenie lub wyłączasz. Włączenie oznacza dostarczenie do urządzenia 100% energii elektrycznej, a wyłączenie zmniejsza tą ilość do 0%.</p>
<p dir="ltr">Jeśli masz w domu jakieś urządzenie z silnikiem to możesz zauważyć, że włączając i wyłączając je wiele razy w ciągu sekundy silnik nie zdąży się rozpędzić do maksymalnych obrotów. Wynika to z tego, że wolno się rozpędza. Zatem jeśli odetniesz mu prąd zanim osiągnie maksymalne obroty to będzie kręcił się wolniej i zwalniał do czasu, aż znowu go włączysz. W ten sposób można regulować jego prędkość.</p>
<p dir="ltr">Działanie PWM polega właśnie na tym, że im dłużej silnik jest włączony w ciągu sekundy, tym szybciej się kręci. A jeśli dłużej trwa czas wyłączenia tym wolniej. Czas włączenia to właśnie ten “Impuls” (“Pulse”) w nazwie, którego “Szerokość” (“Width”) regulujesz.</p>
<p dir="ltr">Fizycznie rzecz ujmując działanie PWM polega na dostarczeniu mniejszej ilości energii elektrycznej do urządzenia w przeciągu jakiegoś czasu. Czego skutkiem ubocznym są takie właśnie miłe efekty jak regulacja szybkości lub jasności.</p>
<p><span id="more-891"></span></p>
<h3 dir="ltr">Parametry PWM</h3>
<p dir="ltr">W rzeczywistości urządzenia PWM są dużo szybsze. Te w Arduino działają z prędkością około 400 razy na sekundę, co pozwala regulować urządzenia mające dużo szybszy czas reakcji np. jasność żarówek lub diod LED.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Parametrów pracy PWM jest kilka. Najważniejsze z nich są 3.</p>
<ul>
<li>
<p dir="ltr">Szybkość działania: To częstotliwość w jakiej mieszczą się czasy włączenia i wyłączenia. 400 Hz oznacza, że PWM włącza i wyłącza coś 400 razy na sekundę. Od tego zależy płynność pracy urządzenia.</p>
</li>
<li>
<p dir="ltr">Wypełnienie: Oznacza ile czasu na jeden okres włączenia/wyłączenia PWM jest w stanie wysokim. 50% oznacza, że pół czasu urządzenie jest włączone, a drugie pół wyłączone. 75% oznacza, że ¾ czasu urządzenie jest włączone.</p>
</li>
<li>
<p dir="ltr">Czas impulsu: Jest parametrem pochodzącym od powyższych. Oznacza to ile czasu trwa impuls włączenia. Jeśli PWM pracuje z prędkością 400 Hz, to czas całej fali PWM (włączony/wyłączony) trwa 1/400 = 0,0025s. Jeśli wypełnienie zajmuje 25% to czas impulsu wynosi 0,0025 * 25% = 0,0025 * 0,25 = 0,000625 s czyli 625 mikrosekund.</p>
</li>
</ul>
<h3 dir="ltr">PWM w praktyce</h3>
<p dir="ltr">Działanie generatora PWM w urządzeniach cyfrowych polega na tym, że w rejestrze przechowującym jakąś liczbę trzymana jest wartość wypełnienia. Od wielkości tego rejestru zależy ile jest stopni wypełnienia. 8 bitowy rejestr może mieć 256 stopni wypełnienia, a 16 bitowy 65 tysięcy. Oznacza to z jaką dokładnością będzie można regulować długość impulsu.</p>
<p><strong><strong><br />
</strong></strong></p>
<pre><code>1 / 256 * 100 = 0,39</code></pre>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Wynik ten podaje, że 8 bitowy rejestr PWM jest w stanie regulować wypełnienie z dokładnością 0,39%.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Oprócz rejestru w generatorze PWM jest też licznik. Licznik to urządzenie, które przechowuje liczbę i co jakiś czas dodaje do niej +1. Od szybkości dodawania tej wartości zależy szybkość z jaką pracuje PWM.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Ostatnim elementem generatora PWM jest układ, który porównuje wartość licznika z wartością rejestru wypełnienia. Jeśli te wartości są sobie równe to wyjście PWM zmienia się w stan niski czyli wyłącza urządzenie.</p>
<p dir="ltr">Po dojściu licznika do ostatniej wartości jaką może policzyć, stan wyjścia PWM zmieniany jest na wysoki &#8211; włączający urządzenie i licznik zaczyna liczyć od nowa.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwm10.png"><img class="aligncenter size-full wp-image-892" title="pwm10" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwm10.png" alt="" width="500" height="304" /></a><br />
</strong></strong></p>
<p dir="ltr">Wykres działania generatora PWM dla wartości 10 w 8 bitowym rejestrze wypełnienia.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwm15.png"><img class="aligncenter size-full wp-image-893" title="pwm15" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwm15.png" alt="" width="500" height="304" /></a><br />
</strong></strong></p>
<p dir="ltr">Wykres działania generatora PWM dla wartości 15 w 8 bitowym rejestrze wypełnienia.</p>
<h3 dir="ltr">Programowy PWM</h3>
<p dir="ltr">Z pewnością mogłeś mi nie uwierzyć, że to jest takie proste ;-). Dlatego, aby ci to udowodnić napiszę program do Arduino, który będzie udawał działanie generatora PWM i regulował jasność wbudowanej w Arduino diody LED.</p>
<pre><span style="color: #7e7e7e;">// numer pinu z wbudowaną diodą</span>
#define LED_PIN 13

<span style="color: #7e7e7e;">// Zmienna z wartością licznika</span>
<span style="color: #cc6600;">byte</span> pwmCounter = 0;
<span style="color: #7e7e7e;">// Zmienna z wartością rejestru wypełnienia</span>
<span style="color: #cc6600;">byte</span> pwmValue = 10;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  <span style="color: #7e7e7e;">// Ustawienie pinu diody na wyjście</span>
  <span style="color: #cc6600;">pinMode</span>(LED_PIN, <span style="color: #006699;">OUTPUT</span>);
  <span style="color: #7e7e7e;">// Ustawienie pinu diody na stan niski</span>
  <span style="color: #cc6600;">digitalWrite</span>(LED_PIN, <span style="color: #006699;">LOW</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #7e7e7e;">// Jeśli stan licznika się przekręci to ustaw pin w stan wysoki</span>
  <span style="color: #cc6600;">if</span> (pwmCounter == 0)
  {
    <span style="color: #cc6600;">digitalWrite</span>(LED_PIN, <span style="color: #006699;">HIGH</span>);
  }

  <span style="color: #7e7e7e;">// Jeśli stan licznika jest równy stanu rejestru to ustaw pin w stan niski</span>
  <span style="color: #cc6600;">if</span> (pwmCounter == pwmValue)
  {
    <span style="color: #cc6600;">digitalWrite</span>(LED_PIN, <span style="color: #006699;">LOW</span>);
  }

  <span style="color: #7e7e7e;">// Zwiększanie wartości licznika o 1</span>
  pwmCounter++;
}</pre>
<p dir="ltr">Działanie programu odpowiada temu co opisałem w zasadzie działania generatora PWM. Zmienna “pwmValue” reprezentuje rejestr regulujący szerokość impulsu czyli poziom wypełnienia. Wartość 10 odpowiada wypełnieniu 3,90%.</p>
<p><strong><strong><br />
</strong></strong></p>
<pre><code>10 / 256 * 100 = 3,90</code></pre>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Zmienna “pwmCounter” to zmienna reprezentująca licznik generatora. Zarówno ta zmienna, jak i “pwmValue” są typu “byte”, zatem reprezentują wartości 8 bitowe, czyli mogą przechowywać liczby od 0 do 255. Jeśli licznik podczas dodawania przekroczy tą liczbę, to zaczyna liczyć od nowa od 0.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Cały mechanizm działania generatora PWM znajduje się w funkcji “loop”. W pierwszym warunku “if” sprawdzane jest czy licznik nie zaczął liczyć od nowa. Jeśli tak to wyjście PWM ustawiane jest w stan wysoki.</p>
<p dir="ltr">W drugim warunku “if” program sprawdza, czy wartość licznika jest równa wartości rejestru wypełnienia. Jeśli tak to zmienia stan wyjścia PWM na niski, który będzie utrzymany aż licznik zacznie liczyć od nowa czyli od 0.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Jeśli uruchomiłeś ten program to powinieneś zauważyć w Arduino, że wbudowana dioda świeci bardzo słabo. Zmieniając wartość zmiennej “pwmValue” możesz zmieniać jasność świecenia tej diody. Wartość 255 to maksymalne świecenie, a 0 to minimalne.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Takie rozwiązanie programowe jest proste, ale ma kilka wad. Pierwszą jest to, że dodając nowe elementy do programu powodujesz, że szybkość działania generatora spada, bo musi się on dzielić czasem procesora z innymi elementami programu. Inną wadą jest to, że wyjście takiego generatora może być niestabilne, przez to, że na działanie głównego programu mogą wpływać przerwania. Jednym z nich jest np. komunikacja Serial.</p>
<h3 dir="ltr">Sprzętowy PWM</h3>
<p dir="ltr">Arduino UNO posiada w grupie pinów “DIGITAL” 6 wyjść PWM. Oznaczone są one na płytce przez napisy “PWM” lub znaczek “~”. Generowane przez te wyjścia sygnały PWM są tworzone sprzętowo przez wbudowane w procesor Arduino liczniki/timery. Oznacza to, że program procesora ani ich nie zwalania, ani nie wpływa na zakłócenia ich pracy. Domyślnie pracują one z prędkością 490 Hz i przyjmują wartości 8 bitowe.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Funkcja odpowiadająca za działanie tych pinów nosi nazwę “analogWrite”. Jej pierwszym argumentem jest numer pinu PWM, a drugim wartość wypełnienia od 0 do 255.</p>
<p dir="ltr">Aby ta funkcja zadziałała pin musi być najpierw ustawiony jako wyjście.</p>
<p><strong><strong><br />
</strong></strong></p>
<pre>#define PWM_PIN 3

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  <span style="color: #cc6600;">pinMode</span>(PWM_PIN, <span style="color: #006699;">OUTPUT</span>);

  <span style="color: #cc6600;">analogWrite</span>(PWM_PIN, 10);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Jak widzisz w funkcji “loop” nie trzeba już niczego zamieszczać. Sygnał generuje się automatycznie po wywołaniu “analogWrite”.</p>
<h3 dir="ltr">Co można podłączyć do PWM?</h3>
<p dir="ltr">Niestety nie ma sprzętowego wyjścia PWM do pinu 13 wbudowanej diody LED. Zatem aby sprawdzić działanie wyjścia możesz podłączyć do niego inną diodę połączoną szeregowo z rezystorem 220 Ohm. Jej anodę podłączasz do wyjścia PWM, a katodę do GND.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwmex_exled.png"><img class="aligncenter size-full wp-image-894" title="pwmex_exled" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwmex_exled.png" alt="" width="373" height="493" /></a><br />
</strong></strong></p>
<p dir="ltr">Można też w ten sposób podłączyć 3-kolorową diodę LED ze wspólną katodą, tak by każde wyjście PWM sterowało osobnym kolorem, co da płynną możliwość dobierania kolorów.</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwmex_rgbled.png"><img class="aligncenter size-full wp-image-896" title="pwmex_rgbled" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwmex_rgbled.png" alt="" width="326" height="511" /></a><br />
</strong></strong></p>
<p dir="ltr">Taka dioda LED składa się z 3 niezależnych diod w jednej obudowie. Barwy emitowane przez diody są podstawowymi barwami składowymi RGB &#8211; Czerwony, Zielony, Niebieski. Każda z tych barw podłączona jest do innego wyjścia PWM (pin 3 &#8211; zielony, pin 5 &#8211; niebieski, pin 6 &#8211; czerwony) przez rezystor 220 Ohm. Wspólna katoda diod podłączona jest do GND. Takie połączenie powinno dać teoretycznie 16 mln barw (256 poziomów na składową czyli 256^3 = 16777216).</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Do wyjścia PWM możesz podłączyć też silnik elektryczny i regulować prędkość jego wału. Ponieważ silnik pobiera znacznie więcej prądu niż może wygenerować cyfrowe wyjście Arduino, zalecam zastosowanie tranzystora MOSFET typu N. Tranzystory te wytrzymują znaczne prądy obciążenia i duże napięcia (w moim przypadku do 12A i 60V &#8211; tranzystor o oznaczeniu D12N06 (typu N) ze starej drukarki).</p>
<p><strong><strong><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwmex_mosfet.png"><img class="aligncenter size-full wp-image-895" title="pwmex_mosfet" src="http://starter-kit.nettigo.pl/wp-content/uploads/2012/01/pwmex_mosfet.png" alt="" width="497" height="653" /></a><br />
</strong></strong></p>
<p dir="ltr">Bramkę (Gate) tranzystora podłączyłem przez rezystor 1 KOhm do wyjścia PWM pin 3. Źródło (Source) tranzystora podłączyłem do GND. Natomiast Dren (Drain) połączyłem z silnikiem. Drugie wyprowadzenie silnika podłączyłem z jego zasilaniem. Do tego może służyć osobny zasilacz silnika lub pin Vin Arduino, jeśli silnik nie pobiera zbyt dużej mocy.</p>
<p dir="ltr">W przypadku silnika, nie każda wartość wypełnienia impulsu PWM może wpływać na jego działanie. W moim przypadku silnik zaczął się kręcić dopiero od wartości 60 (wypełnienie 23,5%).</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Serwomechanizmy są elementami zależnymi od czasu trwania impulsu PWM. Szerokość impulsu decyduje o tym jak będzie ustawiony kąt osi serwomechanizmu. Moje serwomechanizmy (HTX900) przyjmują szerokości impulsów od 580 (0 stopni) do 2800 (180 stopni) mikrosekund. Do ich obsługi stosuje się raczej bibliotekę “Servo.h”, która dobrze spełnia swoje zadanie.</p>
<p><strong><strong><br />
</strong></strong></p>
<p dir="ltr">Wyjście PWM może sterować też głośnikiem lecz wymaga generatora PWM o częstotliwości pracy w zakresie ultradzwięków (powyżej 20000 Hz). Częstotliwości niższe są słyszalne dla ucha człowieka i powodują, że zamiast sygnału jaki chciałeś wygenerować, słyszysz pisk sygnału PWM. W tym artykule nie napiszę o sposobach generowania takiego sygnału, ale pojawiło się już wiele takich projektów w społeczności Arduino i można je wyszukać.</p>
<p dir="ltr">Sterowanie głośnikiem przez PWM pozwala na wytwarzanie przez głośnik fal o dowolnych kształtach i głośności, a nawet odtwarzanie sampli. Eksperymentatorzy będą mieli zapewne wiele zabawy.</p>
<h3 dir="ltr">Zakończenie</h3>
<p dir="ltr">Mam nadzieję, że tym wpisem rozwiałem twoje wątpliwości dotyczące tego jak działają i do czego służą wyjścia PWM. Jeśli masz jeszcze jakieś pytania dotyczące PWM, możesz zamieścić je w komentarzach pod artykułem.</p>
<p dir="ltr">W nastepnym wpisie przedstawię jak można uzyskać 16 kanałów PWM o rozdzielczości 12 bitów (4096 poziomów wypełnienia).</p>
<h3 dir="ltr">Linki</h3>
<ul>
<li><a href="http://arduino.cc/en/Reference/AnalogWrite">Opis funkcji “analogWrite” [EN]</a></li>
<li><a href="http://pl.wikipedia.org/wiki/Modulacja_szeroko%C5%9Bci_impuls%C3%B3w">Opis działania PWM na Wikipedii [Paskudne akademickie teoretyzowanie ;-)]</a></li>
<li><a href="http://arduino.cc/en/Reference/Servo">Opis biblioteki “Servo.h” [EN]</a></li>
</ul>
</div>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2012/01/co-to-jest-pwm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PCF8574 czyli jak łatwo zwiększyć liczbę pinów w Arduino</title>
		<link>http://starter-kit.nettigo.pl/2011/11/pcf8574-czyli-jak-latwo-zwiekszyc-liczbe-pinow-w-arduino/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=pcf8574-czyli-jak-latwo-zwiekszyc-liczbe-pinow-w-arduino</link>
		<comments>http://starter-kit.nettigo.pl/2011/11/pcf8574-czyli-jak-latwo-zwiekszyc-liczbe-pinow-w-arduino/#comments</comments>
		<pubDate>Sun, 27 Nov 2011 23:19:20 +0000</pubDate>
		<dc:creator>sprae</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[i2c]]></category>
		<category><![CDATA[PCF8574]]></category>
		<category><![CDATA[pcf8574p]]></category>
		<category><![CDATA[podstawy]]></category>
		<category><![CDATA[starter-kit]]></category>
		<category><![CDATA[TWI]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=866</guid>
		<description><![CDATA[Wstęp Arduino z rodziny UNO posiada 14 pinów cyfrowych. Jest to wystarczająca ilość do większości prostych zastosowań z dziedziny automatyki. Zdarzają się jednak sytuacje, jak np. podłączenie wyświetlacza, które drastycznie tą liczbę mogą zmniejszyć. Co wtedy robić? Można przesiąść się na 2 razy droższe Arduino MEGA i mieć ponad 50 dodatkowych pinów. Można też użyć [...]]]></description>
			<content:encoded><![CDATA[<div>
<div>
<h3 dir="ltr">Wstęp</h3>
<p dir="ltr">Arduino z rodziny UNO posiada 14 pinów cyfrowych. Jest to wystarczająca ilość do większości prostych zastosowań z dziedziny automatyki. Zdarzają się jednak sytuacje, jak np. podłączenie wyświetlacza, które drastycznie tą liczbę mogą zmniejszyć.</p>
<p dir="ltr">Co wtedy robić? Można przesiąść się na 2 razy droższe Arduino MEGA i mieć ponad 50 dodatkowych pinów. Można też użyć rejestru przesuwnego 74HC595, który da dodatkowe 8 wyjść cyfrowych za cenę 2 zł i użycia 3 pinów cyfrowych. Można też użyć układu <a title="PCF8574" href="http://nettigo.pl/product/PCF8574P---8-portow-cyfrowych-po-I2C,pcf8574p">PCF8574</a> i mieć dodatkowe 8 pinów (lub nawet 128 łącząc więcej układów) wejścia/wyjścia o możliwościach przekraczających te w Arduino za cenę użycia 2 pinów analogowych.</p>
<h3 dir="ltr">Układ scalony PCF8574</h3>
<p style="text-align: center;"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574.png"><img class="aligncenter size-full wp-image-868" title="pcf8574" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574.png" alt="" width="500" height="245" /></a></p>
<ul>
<li>
<p dir="ltr">Vcc &#8211; Pin zasilania, podłączany w Arduino do pinu 5V.</p>
</li>
<li>
<p dir="ltr">GND &#8211; Pin masy zasilania podłączany w Arduino do GND.</p>
</li>
<li>
<p dir="ltr">P0..P7 &#8211; Cyfrowe piny do własnego wykorzystania.</p>
</li>
<li>
<p dir="ltr">SDA &#8211; Sygnał danych magistrali I2C podłączany w Arduino do Analog In 4.</p>
</li>
<li>
<p dir="ltr">SCL &#8211; Sygnał zegara magistrali I2C podłączany w Arduino do Analog In 5.</p>
</li>
<li>
<p dir="ltr">A0, A1, A2 &#8211; Wybór adresu układu, jeśli używasz jednego układu, wszystkie można podłączyć do GND.</p>
</li>
<li>
<p dir="ltr">/INT &#8211; Zanegowany sygnał przerwania. Można go używać do wykrywania zmiany stanu na jednym z cyfrowych wejść.</p>
</li>
</ul>
<h3 dir="ltr">Magistrala I2C</h3>
<p dir="ltr">Układ PCF8574 komunikuje się z Arduino za pomocą magistrali I2C. Jest to synchroniczna magistrala szeregowa wykorzystywana powszechnie w sprzęcie RTV. Szeregowa oznacza, że bity są przesyłane jednym pinem po kolei &#8211; pinem SDA. Synchroniczna oznacza, że każdy wysłany bit jest zatwierdzany sygnałem na drugim pinie &#8211; SCL. Synchroniczność przyczynia się do zwiększenia prędkości komunikacji i eliminuje błędy transmisji.</p>
<p dir="ltr">Magistrala I2C ma też własny protokół komunikacji, dzięki któremu do jej sygnałów SDA/SCL można podłączyć więcej niż jeden układ scalony. Każdy układ w magistrali ma swój adres. Adres jest to liczba, która identyfikuje układ, wybierając tą liczbę masz pewność, że dane które wysyłasz trafią do właściwego układu. Układ PCF8574 ma dodatkowo piny A0, A1, A2 za pomocą których możesz konfigurować część adresu. Pozwala to na podłączenie do magistrali więcej takich samych układów scalonych, dzięki modyfikacji stanów logicznych na tych pinach. Trzy piny adresu oznaczają, że możesz podłączyć do magistrali 8 takich układów scalonych, co da 64 dodatkowe piny cyfrowe. Jeśli i to jest za mało to istnieje też wersja układu PCF8574A, która ma zmieniony adres, co sprawia, że oprócz tamtych 8 można podłączyć jeszcze 8 układów i mieć w sumie 128 pinów.</p>
<p dir="ltr">W Arduino magistrala I2C ze względu na problemy licencyjne nazywa się TWI (TwoWires [dwa przewody] &#8211; od liczby przewodów używanych przez nią). Mikrokontroler AVR zawarty na płytce zawiera sprzętową obsługę tej magistrali. Jej wyprowadzenia znajdują się na dwóch ostatnich pinach grupy “ANALOG IN”. Sygnał danych SDA znajduje się na pinie 4, a sygnał zegarowy SCL na pinie 5.</p>
<h3 dir="ltr">Adres układu PCF8574</h3>
<p dir="ltr">Układy z rodziny PCF8574 mają 7 bitowy adres. Pierwsze 3 bity adresu nadaje mu użytkownik, przez ustawienie pinów A0, A1, A2. Kolejne 4 są nadane fabrycznie na stałe. Układ PCF8574 ma je ustawione na 0100, a układ PCF8574A ustawione na 0111.</p>
<h4 dir="ltr">Binarny adres układu PCF8574</h4>
<div dir="ltr">
<table>
<colgroup>
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" /></colgroup>
<tbody>
<tr>
<td>
<p dir="ltr">0</p>
</td>
<td>
<p dir="ltr">1</p>
</td>
<td>
<p dir="ltr">0</p>
</td>
<td>
<p dir="ltr">0</p>
</td>
<td>
<p dir="ltr">A2</p>
</td>
<td>
<p dir="ltr">A1</p>
</td>
<td>
<p dir="ltr">A0</p>
</td>
</tr>
</tbody>
</table>
</div>
<p dir="ltr">Co oznacza, że minimalny adres (dla wszystkich A = LOW) to dziesiętnie 32, szesnastkowo 0&#215;20. Maksymalny adres (dla wszystkich A = HIGH) to dziesiętnie 39, a szesnastkowo 0&#215;27.</p>
<h4 dir="ltr">Binarny adres układu PCF8574A</h4>
<div dir="ltr">
<table>
<colgroup>
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" />
<col width="*" /></colgroup>
<tbody>
<tr>
<td>
<p dir="ltr">0</p>
</td>
<td>
<p dir="ltr">1</p>
</td>
<td>
<p dir="ltr">1</p>
</td>
<td>
<p dir="ltr">1</p>
</td>
<td>
<p dir="ltr">A2</p>
</td>
<td>
<p dir="ltr">A1</p>
</td>
<td>
<p dir="ltr">A0</p>
</td>
</tr>
</tbody>
</table>
</div>
<p dir="ltr">Co oznacza, że minimalny adres (dla wszystkich A = LOW) to dziesiętnie 56, szesnastkowo 0&#215;38. Maksymalny adres (dla wszystkich A = HIGH) to dziesiętnie 63, a szesnastkowo 0x3F.</p>
<h3 dir="ltr">Podłączenie do Arduino</h3>
<p><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_bb.png"><img class="aligncenter size-full wp-image-869" title="pcf8574_bb" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_bb.png" alt="" width="454" height="472" /></a></p>
<p>Podłączenie nie sprawia trudności i wykorzystuje tylko 4 przewody. Vcc układu łącze z pinem 5V Arduino, GND układu łącze z pinem GND Arduino w sekcji zasilania. Sygnały SDA łącze z pinem 4, a SCL z pinem 5 sekcji “ANALOG IN” Arduino.</p>
<p dir="ltr">Ponieważ używam w tej konfiguracji tylko jednego układu, podłączyłem wszystkie linie adresowe w stan niski (LOW) łącząc je z GND. Jeśli w twojej konfiguracji jest więcej takich samych układów, powinieneś do linii adresowych stosować różne kombinacje stanów (LOW &#8211; GND, HIGH &#8211; 5V) innych dla każdego układu. Pozostałe piny P0 do P7 możesz wykorzystywać dowolnie jako piny cyfrowe (DIGITAL), jak to robisz w Arduino.</p>
<h3 dir="ltr">Programowanie układu PCF8574</h3>
<h4 dir="ltr">Przygotowanie</h4>
<div>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  expander.<span style="color: #cc6600;">begin</span>(0x20);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
</div>
<p dir="ltr">Jak zawsze społeczność Arduino nie zawiodła i jeden z miłośników platformy przygotował odpowiednią bibliotekę. Aby rozpocząć komunikacje z układem PCF8574 musisz najpierw podłączyć 2 biblioteki &#8211; PCF8574.h (dostarczającą funkcje obsługi układu) i Wire.h (do obsługi magistrali TWI).</p>
<p dir="ltr">Następnie trzeba zadeklarować obiekt klasy “PCF8574”, który nazwałem “expander”. Deklaruje się to jak inne zmienne czyli “PCF8574 expander;”.</p>
<p dir="ltr">Potem w funkcji “setup” należy za pomocą metody “expander.begin” ustawić adres układu, który ma reprezentować zadeklarowany obiekt. W tym przypadku ustawiłem wartość szesnastkową 0&#215;20 odpowiadającą układowi z liniami adresowymi w stanie “LOW” &#8211; jak na schemacie.</p>
<p><span id="more-866"></span></p>
<h4 dir="ltr">Ustawienie kierunku działania pinów</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  expander.<span style="color: #cc6600;">begin</span>(0x20);
  expander.<span style="color: #cc6600;">pinMode</span>(0, <span style="color: #006699;">OUTPUT</span>);
  expander.<span style="color: #cc6600;">pinMode</span>(1, <span style="color: #006699;">INPUT</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">Nazwy funkcji biblioteki obsługującej układ zostały tak dobrane, by przypominać te z biblioteki obsługującej piny Arduino. z tym, że zamiast “pinMode” jest “expander.pinMode”, gdyż dotyczy obiektu układu. Działanie funkcji jest bliźniaczo podobne do tych w Arduino. Pierwszy argument to numer pinu układu od 0 do 7, a drugi to kierunek działania pinu “INPUT” &#8211; wejście, “OUTPUT” &#8211; wyjście.</p>
<p dir="ltr">W tym przykładzie ustawiłem pin 0 na wyjście, a pin 1 na wejście.</p>
<h4 dir="ltr">Ustawienie stanu pinów</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  expander.<span style="color: #cc6600;">begin</span>(0x20);
  expander.<span style="color: #cc6600;">pinMode</span>(4, <span style="color: #006699;">OUTPUT</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  expander.<span style="color: #cc6600;">digitalWrite</span>(4, <span style="color: #006699;">LOW</span>);
  <span style="color: #cc6600;">delay</span>(1000);
  expander.<span style="color: #cc6600;">digitalWrite</span>(4, <span style="color: #006699;">HIGH</span>);
  <span style="color: #cc6600;">delay</span>(1000);
}</pre>
<p dir="ltr">Podobnie jak w Arduino, tak i tutaj do ustawienia stanu pinów służy bliźniacza funkcja “expander.digitalWrite”. Jej pierwszym argumentem jest numer pinu od 0 do 7, a kolejnym stan jaki ma być ustawiony. “LOW” to stan niski odpowiadający GND, “HIGH” to stan wysoki odpowiadający 5V. Oczywiście można ustawiać tylko stan pinów wyjściowych “OUTPUT”.</p>
<p dir="ltr">Ten przykład co sekundę zmienia stan pinu 4 układu.</p>
<p dir="ltr"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_led.png"><img class="aligncenter size-full wp-image-872" title="pcf8574_led" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_led.png" alt="" width="440" height="460" /></a></p>
<p>Jeśli teraz podłączysz do pinu 4 (noga układu 9) rezystor 220 Ohm, a drugie wyprowadzenie rezystora do katody diody LED, potem anodę diody LED do zasilania 5V, to powinna zgodnie z programem mrugać.</p>
<h4 dir="ltr">Odwracanie stanu pinów wyjściowych</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{ 
  expander.<span style="color: #cc6600;">begin</span>(0x20);
  expander.<span style="color: #cc6600;">pinMode</span>(4, <span style="color: #006699;">OUTPUT</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  expander.toggle(4);
  <span style="color: #cc6600;">delay</span>(1000);
}</pre>
<p dir="ltr">Przykład z poprzedniego rozdziału można odrobinę skrócić. Biblioteka PCF8574 zawiera dodatkową funkcję “expander.toggle”, która automagicznie odwraca stan na wyjściu układu na przeciwny. Argumentem metody jest numer pinu od 0 do 7, którego stan chcesz odwrócić.</p>
<p dir="ltr">Przykładowy program robi to samo co poprzedni z tym, że krócej.</p>
<h4 dir="ltr">Mruganie</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{ 
  expander.<span style="color: #cc6600;">begin</span>(0x20);
  expander.<span style="color: #cc6600;">pinMode</span>(4, <span style="color: #006699;">OUTPUT</span>);

  expander.<span style="color: #cc6600;">blink</span>(4, 10, 500);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">Metoda “expander.blink” odpowiada za mruganie stanem pinu. Jej pierwszy argument to numer pinu od 0 do 7, drugi argument to ilość mrugnięć, a trzeci, czas w jakim ma trwać jeden stan mrugania w milisekundach.</p>
<p dir="ltr">Przykład sprawia, że na pinie 4 (noga 9) pojawi się 10 impulsów &#8211; naprzemiennych stanów LOW &#8211; HIGH. Każdy stan będzie trwał 500 milisekund, czyli 0,5 s.</p>
<h4 dir="ltr">Odczytywanie stanu pinów</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">begin</span>(9600);

  expander.<span style="color: #cc6600;">begin</span>(0x20);
  expander.<span style="color: #cc6600;">pinMode</span>(0, <span style="color: #006699;">INPUT</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">byte</span> value = expander.<span style="color: #cc6600;">digitalRead</span>(0);

  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(value, DE);
  <span style="color: #cc6600;">delay</span>(100);
}</pre>
<p dir="ltr">Odczyt stanu pinu ustawionego na wejście “INPUT” odczytujesz za pomocą “expander.digitalRead”. Jedynym argumentem tej metody jest numer pinu od 0 do 7.</p>
<p dir="ltr">Powyższy przykład odczytuje wartość pinu 0 (noga 4) i zapisuje go do zmiennej “value”. Jeśli na pinie 0 jest stan niski, to zmienna “value” będzie miała wartość “LOW” (0), a jeśli wysoki, to zmienna będzie miała wartość “HIGH” (1). Wartość zmiennej jest wysyłana do komputera (dzięki Serial.println), dzięki czemu możesz ją podglądać w Serial Monitor z Arduino IDE.</p>
<h4 dir="ltr">Rezystory PullUp wejść układu PCF8574</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">begin</span>(9600);

  expander.<span style="color: #cc6600;">begin</span>(0x20);
  expander.<span style="color: #cc6600;">pinMode</span>(4, <span style="color: #006699;">INPUT</span>);
  expander.pullUp(4);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">byte</span> value = expander.<span style="color: #cc6600;">digitalRead</span>(4);

  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(value, <span style="color: #006699;">DEC</span>);
  <span style="color: #cc6600;">delay</span>(100);
}</pre>
<p dir="ltr">Wejścia cyfrowych układów scalonych mogą mieć 3 stany. LOW &#8211; wejście ustawione w stan niski np. przez podłączenie do GND, HIGH &#8211; wejście ustawione w stan wysoki np. podłączone do 5V i stan nieustalony. Stan nieustalony jest wtedy, kiedy w przewodzie wejścia cyfrowego nie ma żadnego konkretnego stanu wysyłanego przez inne podłączone układy. Stan nieustalony jest interpretowany przez wejście układu jako stan losowy w zależności od zakłóceń ustawiany raz na LOW, a raz na HIGH.</p>
<p dir="ltr"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_pullup.png"><img class="aligncenter size-full wp-image-874" title="pcf8574_pullup" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_pullup.png" alt="" width="438" height="487" /></a>Wiele elementów jak np. przełącznik, albo przewodzą prąd, albo nie. Jeśli podłączysz przełącznik do wejścia cyfrowego, to gdy nie będzie przewodził, będzie stan nieustalony i układ może dostawać błędne sygnały.</p>
<p dir="ltr">Rezystory pullup wymyślono po to, aby gdy wejście nie ma ustalonego stanu, same ustalały stan domyślny, dopóki na wejściu nie pojawi się konkretny stan. Pullup oznacza “podciągnij w górę”, znaczy to, że domyślnym stanem niepodłączonego wejścia będzie stan wysoki “HIGH”. Dlatego na schemacie podłączyłem przełącznik do pin 4 układu, a jego drugie wyprowadzenie do GND. Wobec czego gdy przełącznik nie przewodzi, dzieki pullup jest domyślnie stan wysoki. Gdy przełącznik przewodzi, zwiera wejście do GND przez co ustala stan na niski “LOW”.</p>
<p dir="ltr">Za ustalenie domyślnie stanu wysokiego na wejściu układu PCF8574 odpowiada funkcja “expander.pullUp”, której argumentem jest numer pinu układu od 0 do 7.</p>
<p dir="ltr">Przykład programu ustawia pullup na wejście numer 4 (noga 9) i wysyła jego stan do komputera.</p>
<h4 dir="ltr">Rezystory Pull Down układu PCF8574</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">begin</span>(9600);

  expander.<span style="color: #cc6600;">begin</span>(0x20);
  expander.<span style="color: #cc6600;">pinMode</span>(4, <span style="color: #006699;">INPUT</span>);
  expander.pullDown(4);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">byte</span> value = expander.<span style="color: #cc6600;">digitalRead</span>(4);

  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(value, <span style="color: #006699;">DEC</span>);
  <span style="color: #cc6600;">delay</span>(100);
}</pre>
<p>Układ PCF8574 jest na tyle uniwersalny, że zapewnia też rezystory pulldown. Ustalają one domyślnie wejście cyfrowe na stan niski “LOW”. Zatem teraz podłączenie wejścia do 5V ustala jego stan na wysoki “HIGH”, a brak stanu to domyślnie stan niski.</p>
<h4 dir="ltr">Równoległy dostęp do pinów</h4>
<p>Równoległy dostęp do pinów to funkcje dzięki którym za jednym wywołaniem program może obsłużyć wszystkie piny układu PCF8574 na raz.</p>
<ul>
<li>expander.write(value)<br />
Pozwala ustawić wartość binarną argumentu “value” na wszystkich pinach wyjściowych układu.</li>
<li>expander.read()<br />
Pozwala odczytać wartość binarną wszystkich pinów.</li>
<li>expander.clear()<br />
Ustawia na wszystkich wyjściowych pinach stan niski “LOW”.</li>
<li>expander.set()<br />
Ustawia na wszystkich pinach stan wysoki “HIGH”.</li>
</ul>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{ 
  expander.<span style="color: #cc6600;">begin</span>(0x20);
  <span style="color: #cc6600;">for</span> (<span style="color: #cc6600;">byte</span> i=0; i&lt;8; i++)
    expander.<span style="color: #cc6600;">pinMode</span>(i, <span style="color: #006699;">OUTPUT</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
  <span style="color: #cc6600;">byte</span> value = <span style="color: #cc6600;">random</span>(0, 255);

  expander.<span style="color: #cc6600;">write</span>(value);
  <span style="color: #cc6600;">delay</span>(100);
}</pre>
<p>Przykład ustawia wszystkie piny jako wyjścia i zmienia ich stan losowo.</p>
<h4 dir="ltr">Przerwania &#8211; układ elektryczny</h4>
<p dir="ltr">Układ PCF8574 na nodze 13 posiada sygnał /INT. Jest to sygnał przerwania, informujący Arduino, “że coś się dzieje”, bez potrzeby ciągłego sprawdzania stanu pinu układu. Sygnał ten wykorzystywany jest do tego, że gdy na pinie wejściowym pojawi się odpowiedni stan, sygnał /INT ustawia się w stan niski. Sygnał ten jest w standardzie “otwarty dren”. Oznacza to, że jego wyjście ma albo stan niski, albo nieustalony. Umożliwia to podłączenie do tego samego pinu Arduino wyjść przerwań z większej ilości układów PCF8574, gdyż nie następuje kolizja stanów logicznych.</p>
<p dir="ltr"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_int.png"><img class="aligncenter size-full wp-image-871" title="pcf8574_int" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_int.png" alt="" width="433" height="492" /></a>Sygnał przerwania /INT możesz podłączyć do dowolnego pinu cyfrowego Arduino. Ja wybrałem pin numer 2, do którego prowadzi niebieski przewód.</p>
<h4 dir="ltr">Przerwania &#8211; program podstawowy</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> onInterrupt()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(<span style="color: #006699;">"Przerwanie"</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">begin</span>(9600);

  expander.<span style="color: #cc6600;">begin</span>(0x20);

  expander.<span style="color: #cc6600;">pinMode</span>(4, <span style="color: #006699;">INPUT</span>);
  expander.pullUp(4);

  <span style="color: #cc6600;">pinMode</span>(2, <span style="color: #006699;">INPUT</span>);
  <span style="color: #cc6600;">digitalWrite</span>(2, <span style="color: #006699;">HIGH</span>);

  expander.enableInterrupt(2, onInterrupt);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">W przykładzie ustawiłem pin 4 układu PCF8574 jako wejście i włączyłem mu domyślny stan wysoki (“pullUp”).</p>
<p dir="ltr">Następnie ustawiłem pin 2 Arduino na wejście. Ponieważ wyjście /INT może mieć albo stan niski, albo nieustalony, ustawiłem wejście pin 2 Arduino też jako “pullup” za pomocą “digitalWrite”.</p>
<p dir="ltr">Za pomocą metody “expander.enableInterrupt” wyberasz pin w Arduino do którego przyłączyłeś sygnał /INT układu (pierwszy argument) i rejestrujesz funkcję, która będzie automatycznie wywoływana po nastąpieniu przerwania (drugi argument). Ja zarejestrowałem funkcje o nazwie “onInterrupt”, która wysyła do komputera napis “Przerwanie”, gdy zostanie wywołana.</p>
<p dir="ltr">Po uruchomieniu programu, zmiana stanu pinu 4 układu (noga 9) na przeciwny niż domyślny, powoduje uruchomienie zarejestrowanej funkcji. Jak widzisz bez żadnego sprawdzania stanu pinów można dzięki przerwaniu automatycznie wywoływać funkcje. To rozwiązanie ma jedną wadę. Tą samą funkcję wywołuje dowolny pin wejściowy układu.</p>
<p dir="ltr">Powiązanie przerwania z funkcją można zlikwidować za pomocą funkcji “expander.disableInterrupt”, która nie ma żadnego argumentu.</p>
<h4 dir="ltr">Przerwania &#8211; program rozszerzony</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander;

<span style="color: #cc6600;">void</span> onPin0()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(<span style="color: #006699;">"Pin 0"</span>);
}

<span style="color: #cc6600;">void</span> onPin1()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(<span style="color: #006699;">"Pin 1"</span>);
}

<span style="color: #cc6600;">void</span> onInterrupt()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(<span style="color: #006699;">"Przerwanie"</span>);
  expander.checkForInterrupt();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{
  <span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">begin</span>(9600);

  expander.<span style="color: #cc6600;">begin</span>(0x20);

  expander.<span style="color: #cc6600;">pinMode</span>(0, <span style="color: #006699;">INPUT</span>);
  expander.pullUp(0);
  expander.<span style="color: #cc6600;">pinMode</span>(1, <span style="color: #006699;">INPUT</span>);
  expander.pullUp(1);

  <span style="color: #cc6600;">pinMode</span>(2, <span style="color: #006699;">INPUT</span>);
  <span style="color: #cc6600;">digitalWrite</span>(2, <span style="color: #006699;">HIGH</span>);

  expander.enableInterrupt(2, onInterrupt);

  expander.<span style="color: #cc6600;">attachInterrupt</span>(0, onPin0, <span style="color: #006699;">FALLING</span>);
  expander.<span style="color: #cc6600;">attachInterrupt</span>(1, onPin1, <span style="color: #006699;">FALLING</span>);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">Jeśli w funkcji obsługującej przerwanie (“onInterrupt”) umieścisz wywołanie metody “expander.checkForInterrupt”, biblioteka zdobędzie nowe możliwości.</p>
<p dir="ltr">Od tej chwili możesz używać metod “expander.attachInterrupt” do rejestrowania funkcji wywoływanych osobno dla każdego pinu układu PCF8574. Ułatwia to pisanie nowych programów.</p>
<p dir="ltr">Metoda “expander.attachInterrupt” ma 3 argumenty. Pierwszym z nich jest numer pinu wejściowego układu, do którego chcesz podłączyć swoją funkcję. Kolejny argument to nazwa funkcji, która chcesz podłączyć. Ostatnim jest rodzaj zdarzenia jaki ma wywoływać funkcję. Istnieją 4 rodzaje zdarzeń.</p>
<ul>
<li>
<p dir="ltr">CHANGE &#8211; wywołuje funkcje przy każdej zmianie stanu pinu</p>
</li>
<li>
<p dir="ltr">LOW &#8211; wywołuje funkcje jeśli pin układu ma stan niski</p>
</li>
<li>
<p dir="ltr">FALLING &#8211; wywołuje funkcję przy zmianie stanu z wysokiego na niski</p>
</li>
<li>
<p dir="ltr">RISING &#8211; wywołuje funkcje przy zmianie stanu z niskiego na wysoki</p>
</li>
</ul>
<p dir="ltr">W przykładzie ustawiłem piny 0 i 1 układu na wejścia o domyślnym stanie wysokim. Potem zarejestrowałem dla nich funkcje “OnPin0” i “OnPin1” wywoływane podczas zmiany stanu z wysokiego “HIGH” na niski “LOW” (“FALLING”).</p>
<p dir="ltr">Powiązanie pinu z funkcją można zlikwidować za pomocą funkcji “expander.detachInterrupt”. Jej jednym parametrem jest numer pinu układu.</p>
<h3 dir="ltr">Podłączenie większej ilości układów PCF8574</h3>
<h4 dir="ltr">Układ elektryczny</h4>
<p><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_dbl.png"><img class="aligncenter size-full wp-image-870" title="pcf8574_dbl" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_dbl.png" alt="" width="443" height="506" /></a>Włączając w obwód dodatkowe układy PCF8574 należy połączyć ich linie sygnałowe i zasilania. SDA, łączysz z SDA pierwszego układu. SCL łączysz z SCL pierwszego układu. /INT łączysz z /INT pierwszego układu. To samo z zasilaniem. 5V łączysz z 5v, a GND z GND.</p>
<p dir="ltr">Nieco inaczej ma się sprawa z liniami adresowymi. Tu należy ustawić binarnie inny adres niż układu pierwszego. W przykładzie podłączyłem linię A0 do 5V &#8211; czyli wymusiłem na niej stan wysoki “HIGH”. A1 i A2 zostawiłem bez zmian, czyli podłączone do GND. Sprawia to, że adres drugiego układu zwiększył się o 1. Teraz pierwszy układ ma adres szesnastkowy 0&#215;20, a drugi 0&#215;21.</p>
<h4 dir="ltr">Programowanie</h4>
<pre>#include &lt;<span style="color: #cc6600;">PCF8574</span>.h&gt;
#include &lt;<span style="color: #cc6600;">Wire</span>.h&gt;

<span style="color: #cc6600;">PCF8574</span> expander1;
<span style="color: #cc6600;">PCF8574</span> expander2;

<span style="color: #cc6600;">void</span> onInterrupt()
{
  expander1.checkForInterrupt();
  expander2.checkForInterrupt();
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>setup</strong></span>()
{

  expander1.<span style="color: #cc6600;">begin</span>(0x20);
  expander2.<span style="color: #cc6600;">begin</span>(0x21);

  <span style="color: #cc6600;">pinMode</span>(2, <span style="color: #006699;">INPUT</span>);
  <span style="color: #cc6600;">digitalWrite</span>(2, <span style="color: #006699;">HIGH</span>);

  expander1.enableInterrupt(2, onInterrupt);
}

<span style="color: #cc6600;">void</span> <span style="color: #cc6600;"><strong>loop</strong></span>()
{
}</pre>
<p dir="ltr">Po dodaniu drugiego układu program też trzeba zmodyfikować. Teraz każdy układ reprezentują 2 niezależne obiekty &#8211; expander1 i expander2. Każdy z obiektów trzeba połączyć z innym układem przez adres danego układu. Zajmuje się tym metoda “begin” w funkcji “setup”. Jak ustaliłem na schemacie jeden układ ma adres 0&#215;20, a kolejny 0&#215;21. W funkcji “onInterrupt” trzeba rozdzielić metodę “checkForInterrupt” na 2 układy. Cała reszta programowania jest taka sama, z tym, że należy pamiętać który pin należy do którego układu i co za tym idzie do którego obiektu. Piny w obydwu układach liczone są od 0 do 7.</p>
<h3 dir="ltr">Inne rodzaje Arduino</h3>
<h4 dir="ltr">Arduino MEGA</h4>
<p><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_mega.png"><img class="aligncenter size-full wp-image-873" title="pcf8574_mega" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/pcf8574_mega.png" alt="" width="489" height="528" /></a></p>
<p>W Arduino MEGA magistrala TWI wyprowadzona jest w nieco innym miejscu. Dlatego jeśli jesteś użytkownikiem tej płytki to musisz nieco zmodyfikować schemat połączeń. Sygnał SDA jest tam na pinie 20, a SCL na pinie 21 z grupy “COMMUNICATION”.</p>
<h4 dir="ltr">Arduino R3</h4>
<p dir="ltr">Nadchodzące wersje Arduino z serii R3 mają powiększoną ilość pinów. Powyższe schematy nie wymagają modyfikacji, jednak dla wygody użytkowników wyprowadzono tam sygnały TWI w dodatkowe miejsce. Znajdują się one w grupie “DIGITAL” na lewo od pinu “AREF”. Pierwszy z nich licząc od lewej to SCL, a następny to SDA.</p>
<p dir="ltr"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/r3.png"><img class="aligncenter size-full wp-image-875" title="r3" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/11/r3.png" alt="" width="578" height="471" /></a></p>
<div style="text-align: center;">Zdjęcie Arduino UNO R3 należy do zespołu Arduino (<a href="http://www.arduino.cc/">www.arduino.cc</a>). Zostało pobrane z <a href="http://arduino.cc/en/Main/ArduinoBoardUno">http://arduino.cc/en/Main/ArduinoBoardUno</a> i zmodyfikowane.</div>
<h3 dir="ltr">Zakończenie</h3>
<p dir="ltr">Teraz jeśli brak ci pinów masz już o jedno rozwiązanie więcej. Jest to rozwiązanie o tyle elastyczne, że bez problemu w razie potrzeb możesz dokładać i dokładać, aż do 128 pinów.</p>
<p dir="ltr">Zatem zapraszam do lutownicy i życzę nowych oraz ekscytujących projektów. No i nie zapomnij się nimi pochwalić.</p>
<h3 dir="ltr">Linki</h3>
<ul>
<li><a href="http://nettigo.pl/product/PCF8574P---8-portow-cyfrowych-po-I2C,pcf8574p">PCF8574 do kupienia na Nettigo</a></li>
<li><a href="http://skyduino.wordpress.com/librairies/librairie-arduino-pcf8574-pcf8575/">Strona projektu biblioteki PCF8574</a> [eng/fr]</li>
<li><a href="http://nettigo.pl/pub/files/Dokumenty/software/1322435581_PCF8574.zip">Plik biblioteki PCF8574 do pobrania z Nettigo</a></li>
<li><a href="http://www.nxp.com/documents/data_sheet/PCF8574.pdf">Nota katalogowa PCF8574</a> [pdf]</li>
<li><a href="http://pl.wikipedia.org/wiki/I%C2%B2C">Opis magistrali I2C</a> [wikipedia/pl]</li>
</ul>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/11/pcf8574-czyli-jak-latwo-zwiekszyc-liczbe-pinow-w-arduino/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Sygnalizator revisited czyli przyciski i debouncing</title>
		<link>http://starter-kit.nettigo.pl/2011/09/sygnalizator-revisited-czyli-przyciski-i-debouncing/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=sygnalizator-revisited-czyli-przyciski-i-debouncing</link>
		<comments>http://starter-kit.nettigo.pl/2011/09/sygnalizator-revisited-czyli-przyciski-i-debouncing/#comments</comments>
		<pubDate>Tue, 13 Sep 2011 06:02:15 +0000</pubDate>
		<dc:creator>netmaniac</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[bounce]]></category>
		<category><![CDATA[button]]></category>
		<category><![CDATA[debounce]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[podstawy]]></category>
		<category><![CDATA[starter-kit]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=841</guid>
		<description><![CDATA[Dawno temu pisałem o tym jak można korzystać z przycisków (tactile switch, pushbutton) na przykładzie kartonowego sygnalizatora. Wróćmy do tematu, tym razem na poważniej zajmując się przyciskami. Jeśli spojrzycie na kod tamtego sygnalizatora, możecie dostrzec następującą pętlę loop: void loop() { &#160;&#160;val&#160;=&#160;digitalRead(buttonPin); &#160;&#160;if (val == HIGH &#38;&#38; prev == LOW) { &#160;&#160;&#160;&#160;next_status(); &#160;&#160;} &#160;&#160;prev&#160;=&#160;val; &#160;&#160;display_status(); [...]]]></description>
			<content:encoded><![CDATA[<p>Dawno temu pisałem o tym jak można korzystać z przycisków (tactile switch, pushbutton) na <a href="http://starter-kit.nettigo.pl/2009/06/sygnalizator-czyli-jak-wykorzystac-przycisk/">przykładzie kartonowego sygnalizatora</a>. Wróćmy do tematu, tym razem na poważniej zajmując się przyciskami.</p>
<p>Jeśli spojrzycie na kod tamtego sygnalizatora, możecie dostrzec następującą pętlę <code>loop</code>:</p>
<pre>
<span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>loop</b></span>()
{
&nbsp;&nbsp;val&nbsp;=&nbsp;<span style="color: #CC6600;">digitalRead</span>(buttonPin);
&nbsp;&nbsp;<span style="color: #CC6600;">if</span> (val == <span style="color: #006699;">HIGH</span> &amp;&amp; prev == <span style="color: #006699;">LOW</span>) {
&nbsp;&nbsp;&nbsp;&nbsp;next_status();
&nbsp;&nbsp;}
&nbsp;&nbsp;prev&nbsp;=&nbsp;val;
&nbsp;&nbsp;display_status();
&nbsp;&nbsp;<span style="color: #CC6600;">delay</span>(50);

}
</pre>
<p>Wykrywanie naciśnięcia odbywa się przez porównanie bieżącej wartości wejścia cyfrowego z poprzednią. Jeżeli aktualna wartość to HIGH a poprzednia to LOW, to wykonujemy akcję <code>next_status()</code>, która zmienia stan sygnalizatora. I wszystko działa. Ale nie ma problemów, tylko dzięki ostatniej linii kodu w <code>loop</code>: <code>delay(50);</code>. </p>
<p>Przycisk jest urządzeniem mechanicznym i włączenie lub wyłączenie nie jest jednoznaczne, jeżeli będziemy stan przycisku badać dostatecznie często. W momencie dociskania/zwalniania przycisku jest taki moment, w którym styk <em>już łapie/puszcza</em> przez co włączenie wyłączenie nie jest czystym przełączeniem między LOW a HIGH tylko migotaniem. Angielska <a href="http://en.wikipedia.org/wiki/Debouncing#Contact_bounce" title="Contact bounce">Wikipedia przy haśle</a> <em>contact bounce</em> (bo tak się zjawisko to nazywa) ma ten piękny obrazek ilustrujący ten problem:<br />
<div id="attachment_842" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/09/bounce.jpg"><img src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/09/bounce-300x225.jpg" alt="Ilustracja migotania przełącznika (CC Wikipedia)" title="Ilustracja migotania przełącznika (CC Wikipedia)" width="300" height="225" class="size-medium wp-image-842" /></a><p class="wp-caption-text">Ilustracja migotania przełącznika (CC Wikipedia)</p></div></p>
<p>Opóźnienie w <code>loop</code> załatwia nam ten problem, bowiem jeżeli zauważymy zmianę stanu klawisza odczekanie 50 ms przed następnym odczytem zapewnia margines wykluczający błędny odczyt. Gdyby nie ono, wówczas w następnym przebiegach moglibyśmy odczytać migotanie jako kolejne przyciśnięcia. Rezultat? Z punktu widzenia użytkownika &#8211; on nacisnął przycisk jeden raz, a sygnalizator przeskoczył o 1 lub więcej stanów za jednym razem.</p>
<p>Rozwiązanie z użyciem delay nie zawsze jest dopuszczalne, bo może nasze Arduino musi robić coś więcej niż tylko czekać na naciśnięcie klawisza.</p>
<h2>Jak się zabezpieczyć przed złymi odczytami?</h2>
<p><span id="more-841"></span><br />
Najlepszą metodą jest zapamiętanie czasu w którym odczytaliśmy zmianę stanu klawisza i przez następne kilkanaście, kilkadziesiąt milisekund nie reagować na zmiany stanu wejścia. Ale wówczas kod znacznie się komplikuje, a taki jest znacznie bardziej podatny na błędy. Na szczęście jest gotowe rozwiązanie w postaci <a href="http://arduino.cc/playground/Code/Bounce" title="Bounce na Arduini Playground">biblioteki Bounce</a>.</p>
<p>Instalujemy bibliotekę w <code>sketchbook/libraries</code> i teraz możemy korzystać z dobrodziejstw klasy <code>Bounce</code>. Zamiast odczytywać wartość przez <code>digitalRead</code> i samemu starać się rozszyfrować co znaczy odczyt w zależności od poprzedniej wartości i czasu który upłynął od ostatniej zmiany, definiujemy obiekt w naszym szkicu:</p>
<pre>
<span style="color: #CC6600;">int</span> buttonPin = 4;
<span style="color: #CC6600;">Bounce</span> button(buttonPin,20);
</pre>
<p>Od teraz cyfrowe wejście nr 4 będzie obsługiwane przez Bounce, z 20-sto milisekundowym marginesem. W czasie 20 milisekund od wykrycia zmiany stanu wejścia jego następne zmiany będą ignorowane. Dopiero po upływie tego czasu obiekt może zmienić swój stan.</p>
<p>Pierwszym krokiem jest uaktualnienie stanu przycisku przez funkcję <code>update()</code>. To właśnie w tym momencie jest odczytywana wartość na wejściu:</p>
<pre>
&nbsp;&nbsp;button.<span style="color: #CC6600;">update</span>();
</pre>
<p><code>update</code> zwraca wartość true lub false &#8211; w zależności czy stan przycisku uległ zmianie czy jest taki jak poprzednio.</p>
<p>Po tej operacji możemy sprawdzić czy przycisk jest wciśnięty lub nie (<code>read()</code>) albo jeżeli interesuje nas tylko czy został właśnie wciśnięty lub zwolniony informacji dostarczy nam <code>risingEdge()</code> lub <code>fallingEdge()</code>. W wypadku sygnalizatora nas interesuje kiedy został wciśnięty klawisz:</p>
<pre>
&nbsp;&nbsp;<span style="color: #CC6600;">if</span> (button.<span style="color: #CC6600;">risingEdge</span>()) {
&nbsp;&nbsp;&nbsp;&nbsp;next_status();
&nbsp;&nbsp;};
</pre>
<p><code>next_status</code> zostanie wywołany tylko kiedy zostanie wykryta zmiana ze stanu LOW na HIGH. W następnym przebiegu, jeżeli przycisk jest nadal wciśnięty, <code>risingEdge()</code> już zwróci <code>false</code>.</p>
<p>Biblioteka ma sobie jeszcze kilka przydatnych funkcji, pozwalających określić jak długo już przycisk jest w aktualnym stanie, albo przydatnych do generowania wirtualnych &#8216;przyciśnięć&#8217; (to drugie świetnie się sprawdza do powtarzania akcji gdy przycisk jest ciągle wciśnięty).</p>
<p>To jeszcze nie koniec o sygnalizatorze, bo on nadal istnieje. Oczywiście kartonowy v1 został zastąpiony przez v2 ze <a href="http://allegro.pl/listing/search.php?sg=0&#038;string=spienione+pcw" title="Spienione PCW na Allegro">spienionego PCW</a>, które jest świetnym materiałem do budowy prostych konstrukcji. Sygnalizator dorobił się dodatku w postaci zapory napędzanej przez serwomechanizm, co widać też w kodzie. </p>
<p>O tym jak korzystać z serwomechanizmów na przykładzie zapory i tego szkicu postaram się napisać już niedługo.</p>
<p>Cały kod <a href="http://static.nettigo.pl/code/sygnalizator_v2.pde" title="Sygnalizator i zapora w jednym domu stali...">sygnalizatora v2</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/09/sygnalizator-revisited-czyli-przyciski-i-debouncing/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Rezystor &#8211; co o nim trzeba wiedzieć</title>
		<link>http://starter-kit.nettigo.pl/2011/09/rezystor-co-o-nim-trzeba-wiedziec/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rezystor-co-o-nim-trzeba-wiedziec</link>
		<comments>http://starter-kit.nettigo.pl/2011/09/rezystor-co-o-nim-trzeba-wiedziec/#comments</comments>
		<pubDate>Mon, 05 Sep 2011 21:39:50 +0000</pubDate>
		<dc:creator>netmaniac</dc:creator>
				<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[prawo ohma]]></category>
		<category><![CDATA[rezystory]]></category>
		<category><![CDATA[starter-kit]]></category>
		<category><![CDATA[teoria]]></category>
		<category><![CDATA[teoria obwodów]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=822</guid>
		<description><![CDATA[Rezystory to jeden z podstawowych elementów wszystkich układów elektronicznych. Jako, że jest to element naprawdę podstawowy i powszechny, spróbujemy się o nim dowiedzieć kilku rzeczy. Teoria związana z przepływem prądów w układach RLC (R &#8211; rezystory, L &#8211; cewki, C &#8211; kondensatory) jest cała najeżona wzorami, których nie chcecie widzieć :) (a jeżeli ktoś musi [...]]]></description>
			<content:encoded><![CDATA[<p>Rezystory to jeden z podstawowych elementów wszystkich układów elektronicznych. Jako, że jest to element naprawdę podstawowy i powszechny, spróbujemy się o nim dowiedzieć kilku rzeczy.</p>
<p>Teoria związana z przepływem prądów w układach RLC (R &#8211; rezystory, L &#8211; cewki, C &#8211; kondensatory) jest cała najeżona wzorami, których nie chcecie widzieć :) (a jeżeli ktoś musi się przekonać to np <a title="Szereg Fouriera" href="http://zasoby1.open.agh.edu.pl/dydaktyka/automatyka/c_teoria_obwodow/szf.htm" target="_blank">zobaczcie to</a> i wrócicie zaraz tutaj :) ). </p>
<p>Teraz podam kilka uproszczeń, które wystarczą do zmierzenia się z użyciem rezystorów w kontekście Arduino.</p>
<p>Ważną cechą układów elektronicznych jest to, że jeżeli mamy jakieś napięcie w układzie (coś je wytwarza), to jeżeli obwód jest zamknięty (nie ma żadnej przerwy), napięcie to się odłoży na wszystkich elementach w taki sposób, że sumując napięcia na każdym elemencie z osobna otrzymamy napięcie z naszego źródła. Jest to zgodne z intuicyjnym rozumieniem &#8211; napięcie nie bierze się znikąd i nie może nigdzie znikać.</p>
<p><span id="more-822"></span></p>
<p>Druga ważna informacja, to <a title="Prawo Ohma" href="http://pl.wikipedia.org/wiki/Prawo_Ohma">prawo Ohma</a> &#8211; mówiące że napięcie (U) na danym elemencie jest proporcjonalne do prądu (I) przez niego płynącego, a jaka jest to konkretnie wartość mówi nam właśnie rezystancja (R):</p>
<div id="attachment_831" class="wp-caption aligncenter" style="width: 75px"><img class="size-full wp-image-831" title="Prawo Ohma" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/09/prawo_ohma.png" alt="Prawo Ohma" width="65" height="14" /><p class="wp-caption-text">Prawo Ohma</p></div>
<p>Oczywiście dotyczy to elementów o charakterze rezystancyjnym &#8211; właśnie rezystorów, sprawa np z diodą jest inna.</p>
<p>Może podłączmy sobie dwa rezystory do baterii:</p>
<div id="attachment_823" class="wp-caption aligncenter" style="width: 510px"><img class="size-full wp-image-823" title="Prosty układ z dwoma rezystorami" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/09/zrodlo2rezystory.jpg" alt="Prosty układ z dwoma rezystorami" width="500" height="300" /><p class="wp-caption-text">Prosty układ z dwoma rezystorami</p></div>
<p>Zgodnie z tym co właśnie napisaliśmy napięcia U<sub>1</sub> i U<sub>2</sub> zsumowane dadzą napięcie równe napięciu baterii. Jeżeli bateria daje 5V to napięcia U<sub>1</sub> i U<sub>2</sub> po dodaniu wyniosą 5V.</p>
<p><strong>Ważne</strong> &#8211; jest to połączenie szeregowe, czyli mówiąc po ludzku &#8211; rezystory są łączone po kolei. W takim wypadku rezystancja całego układu dwóch rezystorów jest sumą ich rezystancji. </p>
<p>W teorii układów RLC jest kilka twierdzeń, które w wielkim uproszczeniu można zastąpić zdaniem, że <em>z punktu widzenia baterii</em> jest obojętne czy podłączymy jeden rezystor 10k czy 2 rezystory 5k połączone szeregowo &#8211; prąd płynący w takim układzie będzie taki sam. </p>
<p>Inaczej &#8211; dowolny układ rezystorów możemy zastąpić jednym o rezystancji będącej wypadkową rezystancji składowych przeliczonych w zależności od tego jak połączone są te rezystory (jak dotąd była mowa o połączeniu szeregowym, może być jeszcze równoległe i wszelkie kombinacje tych dwóch)</p>
<p>Kolejną intuicyjnie rozumianą rzeczą jest, że prąd płynący przez elementy w połączeniu szeregowym jest taki sam w każdym elemencie.</p>
<p>Przypomnijmy sobie prawo Ohma &#8211; jeżeli przyłożymy napięcie z baterii wówczas przez oba rezystory popłynie jednakowy prąd równy napięcie baterii/sumaryczna rezystancja. Skoro prąd jest taki sam w obu rezystorach to napięcie na każdym z nich będzie wynosić rezystancja tego jednego rezystora * prąd.</p>
<p>Na przykład weźmy układ dwóch rezystorów połączonych szeregowo o sumarycznej rezystancji 10 Ohm. Podłączmy je do baterii 20V &#8211; prąd płynący przez rezystory (wyliczony z prawa Ohma) to 2A &#8211; bo 20V/10Ohm = 2A. Teraz jeżeli są to dwa rezystory po 5 Ohm (suma rezystancji wynosi 10 Ohm), prąd nadal jest 2A. Napięcie na każdym z rezystorów to 5 Ohm * 2 A = 10V. W sumie &#8211; 20V, czyli wszystko się zgadza.</p>
<p>Jeżeli jednak weźmiemy rezystor o różnych wartościach &#8211; na przykład 1 Ohm i 9 Ohm (suma nadal 10 Ohm) to mamy na pierwszym 1 Ohm * 2A = 2V a na drugim &#8211; 9 Ohm * 2A = 18V. Suma napięć &#8211; nadal 20V, czyli tyle ile daje nasza bateria. Ale w tym wypadku rozkład napięć U<sub>1</sub> U<sub>2</sub> jest zupełnie inny. Powiedzmy że na naszym schemacie górny rezystor to 1 Ohm a dolny to 9 Ohm. Napięcie na górnym rezystorze to 2V, na dolnym 18V.</p>
<p>Mierząc napięcie między minusem baterii (tutaj jest to tak zwana masa) a punktem pomiędzy rezystorami otrzymamy 18V &#8211; 90% wartości napięcia baterii. 9 Ohm stanowi 90% sumarycznej rezystancji układu. Zmieniając proporcje rezystancji, w tym punkcie otrzymamy odpowiednio zmienione napięcie. Gdy dolny rezystor będzie stanowił 30% sumarycznej rezystancji obu rezystorów, napięcie w punkcie pomiędzy rezystorami będzie wynosiło 30% napięcia baterii. Widzicie zależność? Właśnie poznaliście zasadę działania dzielnika napięcia, bo tak taki układ rezystorów się nazywa.</p>
<p>W rzeczywistym zastosowaniu trzeba pamiętać o jednym &#8211; nie możemy pobierać (dużo) prądu z punktu pomiaru pomiędzy rezystorami. Bo powyższe rozumowanie zakładało, że przez oba rezystory płynie równy prąd, prawda? Czyli nadaje się to uzyskania jakiegoś napięcia odniesienia, ale nie bardzo aby uzyskać napięcie zasilające.</p>
<h3>Do czego jeszcze może się nam przydać rezystor?</h3>
<p>Kolejnym podstawowym zastosowaniem oporników (jak też są nazywane rezystory) jest ograniczanie (czy raczej wymuszanie) wartości prądu w gałęzi układu. Jeżeli wiemy jakie napięcie zostanie odłożone na oporniku to dobierając odpowiednią wartość rezystancji możemy określić jaki prąd będzie płynął w gałęzi. Jeżeli wiemy, że na rezystorze będzie napięcie 1.7V to jeżeli użyjemy rezystora 220 Ohm, prąd płynący w gałęzi będzie wynosił 1.7/220 = 7.73 mA. Wartość tę wyliczyliśmy dzięki przekształceniu prawa Ohma &#8211; zależność prądu od napięcia i rezystancji: </p>
<div id="attachment_833" class="wp-caption aligncenter" style="width: 64px"><img src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/09/prawo_ohma2.png" alt="Inny zapis prawa Ohma" title="Inny zapis prawa Ohma" width="54" height="41" class="size-full wp-image-833" /><p class="wp-caption-text">Inny zapis prawa Ohma</p></div>
<p>Ma to zastosowanie np przy podłączaniu diod LED, ale bardziej szczegółowo o tym wkrótce.</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/09/rezystor-co-o-nim-trzeba-wiedziec/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Gameduino &#8211; czyli zróbmy fajną grę</title>
		<link>http://starter-kit.nettigo.pl/2011/08/gameduino-czyli-zrobmy-fajna-gre/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=gameduino-czyli-zrobmy-fajna-gre</link>
		<comments>http://starter-kit.nettigo.pl/2011/08/gameduino-czyli-zrobmy-fajna-gre/#comments</comments>
		<pubDate>Mon, 01 Aug 2011 22:50:15 +0000</pubDate>
		<dc:creator>netmaniac</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[projekty]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[fun]]></category>
		<category><![CDATA[gameduino]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[gry]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=800</guid>
		<description><![CDATA[W zasadzie od momentu kiedy w ofercie Nettigo pojawił się pierwszy czujnik przyspieszenia miałem ochotę zrobić pewien hack i wykorzystać go jako główny czujnik do jakiegoś urządzenia sterującego. Myślałem o grze sportowej i podłączeniu czujnika do komputera. Ale, czasu ciągle brakowało, a przynajmniej odnosiłem wrażenie, że hack będzie długi i skomplikowany. Odczyt z czujnika przetworzyć [...]]]></description>
			<content:encoded><![CDATA[<p>W zasadzie od momentu kiedy w <a href="http://nettigo.pl/product/Trojosiowy-analogowy-czujnik-przyspieszenia,accel-3-axis" title="Czujnik przyspieszenia">ofercie Nettigo pojawił się pierwszy czujnik przyspieszenia</a> miałem ochotę zrobić pewien hack i wykorzystać go jako główny czujnik do jakiegoś urządzenia sterującego. Myślałem o grze sportowej i podłączeniu czujnika do komputera. Ale, czasu ciągle brakowało, a przynajmniej odnosiłem wrażenie, że hack będzie długi i skomplikowany. Odczyt z czujnika przetworzyć na ciąg wciśnięć klawiszy w komputerze. Arduino, serial, jakaś biblioteka odczytująca serial, potem emulacja wciśnięcia klawisza. Może to wszystko okaże się prostsze niż wygląda, ale sprawiało wrażenie dużej liczby <em>ruchomych części</em>, a co za tym idzie większą szansę niepowodzenia. No cóż, po prostu nie chciało mi się :)</p>
<p>Gdy pojawiło się Arduino UNO, przez chwilę pojawiła się chęć przetestowania <a href="http://arduino.cc/forum/index.php?topic=56025.0" title="Keyboard HID">klawiaturowego firmware dla ATmega8u2</a> w ramach tego właśnie hacku, ale projekt nie wystartował. Co UNO zmieniało? Otóż z nowym firmware, podłączone do komputera widziane jest jako klawiatura więc odpada cała komplikacja z odczytem seriala i wysyłania komunikatów do gry. Po prostu Arduino może &#8216;wciskać klawisze&#8217; przez port USB i całość wydaje się znacznie prostsza.</p>
<p>Ale gdy tylko zobaczyłem <a href="http://nettigo.pl/product/Gameduino---Arduino-staje-sie-platforma-do-gier,gameduino" title="Gameduino">Gameduino</a> wiedziałem że to jest to czego potrzebowałem. Ostatnie dni deszczowe były, tak więc &#8211; do dzieła!</p>
<p><span id="more-800"></span></p>
<p>Najpierw &#8211; jedno z dem dołączonych do biblioteki to Asteroids &#8211; lecisz statkiem przez pole asteroidów i strzelasz do nich. Proste. Rozpakować bibliotekę do <code>sketchbook/libraries</code>, uruchomić Arduino IDE, menu File/Examples/Gameduino/Demos/asteroids. Kompilacja, upload i mamy. Tyle, że nie mamy żadnego kontrolera do sterowania statkiem. Ja do pierwszych testów użyłem <a href="http://nettigo.pl/product/Nettigo-Keypad---odczyt-przez-wejscie-analogowe,keypad" title="Nettigo Keypad">Nettigo Keypada</a>, bo go pod ręką miałem. </p>
<p>Wystarczy zmienić kod w <code>controller_sense</code> i zamiast domyślnego kodu przeznaczonego dla game controllera od SparkFun użyć swojego. Ale to mało pasjonujące naciskać na klawisze, byle konsola to ma :)</p>
<h3>Czas na Level 2</h3>
<p>Podłączyć czujnik przyspieszenia i zrobić jakąś platformę, na której stojąc będzie się sterować statkiem. Przechył w lewo &#8211; obrót w lewo. Przechył w prawo &#8211; obrót w prawo. Przechył do przodu &#8211; lecimy. W tył &#8211; strzelamy. Taki Segway. No może bez tego strzelania :)</p>
<p>Pierwsza rzecz jak zrobić samą platformę? Miałem pod ręką kawałek panela podłogowego. Do niego przyklejona <a href="http://nettigo.pl/product/Plytka-stykowa---mini-170,BB-170" title="Płytka stykowa 170 otworów">płytka stykowa 170</a> (dzięki swojej taśmie samoprzylepnej). Na niej czujnik przyspieszenia, kabelki zgodnie z opisem w <a href="http://starter-kit.nettigo.pl/2009/12/czujnik-przyspieszenia/" title="Dokumentacja czujnika przyspieszenia">dokumentacji czujnika</a> i możemy ustalić położenie panela. Interesują nas dwie osie &#8211; X i Y, Z na razie nam niepotrzebne :)</p>
<div id="attachment_804" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/08/plytka.jpg"><img src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/08/plytka-300x225.jpg" alt="Czujnik przyspieszenia na płytce stykowej" title="Czujnik przyspieszenia na płytce stykowej" width="300" height="225" class="size-medium wp-image-804" /></a><p class="wp-caption-text">Czujnik przyspieszenia na płytce stykowej</p></div>
<p>Pozostaje teraz rozwiązać konstrukcję modułu wychylnego. Musi to być  coś co pozwoli naszemu panelowi kiwać się na wszystkie strony, jednocześnie nie za bardzo, tak aby gracz nie spadał z niego. Po wielu testach różnych materiałów i różnych rozwiązań mocowań konsultowanych z NASA  nasze finalne rozwiązanie wygląda tak:</p>
<div id="attachment_807" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/08/calosc.jpg"><img src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/08/calosc-300x225.jpg" alt="Źródło: NASA (just joking)" title="Źródło: NASA (just joking)" width="300" height="225" class="size-medium wp-image-807" /></a><p class="wp-caption-text">Źródło: NASA (just joking)</p></div>
<p>Jak widać wybór padł na starą poduszkę :) Panel położony na niej ma dość swobody aby poruszać się w dwóch osiach na tyle aby czujnik mógł odczytać położenie.</p>
<h3>Hard za nami, teraz soft</h3>
<p>Teraz trochę kodu, czyli w zasadzie jedyna zmiana w kodzie dema, to nowa funkcja <code>controller_sense</code>:</p>
<pre>
#define&nbsp;LR_NEUTRAL&nbsp;330
#define&nbsp;UD_NEUTRAL&nbsp;345

#define&nbsp;LR_DELTA&nbsp;8
#define&nbsp;UD_DELTA&nbsp;7
<span style="color: #CC6600;">static</span> <span style="color: #CC6600;">byte</span> controller_sense(uint16_t clock)
{
<span style="color: #CC6600;">int</span> rd,a1,a2;

rd&nbsp;=&nbsp;0;
a1&nbsp;=&nbsp;<span style="color: #CC6600;">analogRead</span>(3);
a2&nbsp;=&nbsp;<span style="color: #CC6600;">analogRead</span>(4);

&nbsp;&nbsp;<span style="color: #CC6600;">if</span> (a1 &lt; LR_NEUTRAL - LR_DELTA){
<span style="color: #7E7E7E;">//&nbsp;&nbsp;&nbsp;&nbsp;Serial.println("Lewo");</span>
&nbsp;&nbsp;&nbsp;&nbsp;rd&nbsp;=&nbsp;rd&nbsp;|&nbsp;CONTROL_LEFT;
&nbsp;&nbsp;}&nbsp;&nbsp;
&nbsp;&nbsp;<span style="color: #CC6600;">else</span> <span style="color: #CC6600;">if</span> (a1 &gt; LR_NEUTRAL + LR_DELTA)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;rd&nbsp;=&nbsp;rd&nbsp;|&nbsp;CONTROL_RIGHT;
<span style="color: #7E7E7E;">//&nbsp;&nbsp;&nbsp;&nbsp;Serial.println("Prawo");</span>
&nbsp;&nbsp;}
&nbsp;&nbsp;<span style="color: #CC6600;">if</span> (a2 &lt; UD_NEUTRAL - UD_DELTA){
&nbsp;&nbsp;&nbsp;&nbsp;rd&nbsp;=&nbsp;rd&nbsp;|&nbsp;CONTROL_DOWN;
<span style="color: #7E7E7E;">//&nbsp;&nbsp;&nbsp;&nbsp;Serial.println("Jazda");</span>
&nbsp;&nbsp;}&nbsp;&nbsp;
&nbsp;&nbsp;<span style="color: #CC6600;">else</span> <span style="color: #CC6600;">if</span> (a2 &gt; UD_NEUTRAL + UD_DELTA)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;rd&nbsp;=&nbsp;rd&nbsp;|&nbsp;CONTROL_UP;
<span style="color: #7E7E7E;">//&nbsp;&nbsp;&nbsp;&nbsp;Serial.println("Ognia");</span>
&nbsp;&nbsp;}
&nbsp;&nbsp;<span style="color: #CC6600;">return</span> rd;
</pre>
<p>Teraz kilka słów wyjaśnienia. <code>LR_NEUTRAL</code> to wartość jaka jest odczytywana w osi X w pozycji neutralnej. LR jest od Left Right, bo przechył w osi X w naszym ułożeniu czujnika oznacza przechył w lewo/prawo. LR_DELTA to jest margines o jaki może się w każdą stronę zmienić odczyt od pozycji neutralnej zanim zostanie uznany za <em>wciśnięcie</em> lewo lub prawo.</p>
<p>Podobnie jest z UD (od up i down). Oznaczenia te pochodzą od oznaczeń używanych w kodzie dema, nie od kierunku ruchu panela :)</p>
<p><code>rd</code> jest na początku ustawiane na zero i jeżeli wychył od pozycji neutralnej jest dostatecznie duży odpowiednia wartość CONTROL_ jest <em>ORowana</em>. O co chodzi? Jeśli spojrzeć na definicję wartości CONTROL_ :</p>
<pre>
#define&nbsp;CONTROL_LEFT&nbsp;&nbsp;1
#define&nbsp;CONTROL_RIGHT&nbsp;2
#define&nbsp;CONTROL_UP&nbsp;&nbsp;&nbsp;&nbsp;4
#define&nbsp;CONTROL_DOWN&nbsp;&nbsp;8
</pre>
<p>są to kolejne potęgi 2. W zapisie binarnym odpowiada to kolejnym bitom. 1 to 0001, 2 to 0010, 4 to 0100, itd. Logiczne OR dwóch liczb (w C to symbol tzw pałki | ) zwraca wartość gdzie bity są ustawione na 1 wszędzie tam gdzie choć w jednej z liczb była jedynka. Na przykład:</p>
<ul>
<li>0000 OR 0001 = 0001</li>
<li>0000 OR 0010 = 0010</li>
<li>0100 OR 0010 = 0110</li>
<li>0100 OR 0100 = 0100</li>
</ul>
<p>W ten sposób wiedząc że 1 na ostatnim bicie to CONTROL_LEFT a 1 na pierwszym to CONTROL_DOWN, można w jednej liczbie całkowitej przekazać informację, że wciśnięte zostały dwa klawisze jednocześnie: 1001 czyli 9 dziesiętnie. Bardzo często stosowana metoda przekazywania stanu różnych rzeczy przez jedną liczbę całkowitą. </p>
<p>A jak potem łatwo można odczytać czy bit jest ustawiony? Ano z wykorzystaniem AND (znak &amp;):</p>
<ul>
<li>1001 AND 1000 = 1000</li>
<li>1001 AND 0100 = 0000</li>
<li>1001 AND 0010 = 0000</li>
<li>1001 AND 0001 = 0001</li>
</ul>
<p>Teraz już chyba jest jasne w jaki sposób przez <code>rd</code> jest zwracana informacja o równocześnie naciśniętych przyciskach.</p>
<p>Co do naszego czujnika &#8211; wartości _NEUTRAL oraz _DELTA zostały dobrane eksperymentalnie &#8211; obserwując odczyty z czujnika, które na ten czas wysyłane były na Serial. Po ich ustaleniu komendy <code>Serial.print</code> zostały usunięte, bo zajmują cenne takty procesora ;)</p>
<p>Jak to wychodzi w praktyce można zobaczyć na tym krótkim filmiku (nie wiem jak go odwrócić, aparat było mi wygodniej trzymać w pionie):<br />
<iframe width="500" height="405" src="http://www.youtube.com/embed/ofRrzhCCifE" frameborder="0" allowfullscreen></iframe></p>
<p>Ze względu na dynamikę całości :) niezbędne jest osłonięcie czujnika aby zabezpieczyć przed przypadkowym zgnieceniem:</p>
<div id="attachment_803" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/08/obudowa.jpg"><img src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/08/obudowa-300x225.jpg" alt="W obudowie" title="W obudowie" width="300" height="225" class="size-medium wp-image-803" /></a><p class="wp-caption-text">W obudowie</p></div>
<h3>Raport Millera &#8211; czyli co można poprawić</h3>
<p>Położenie neutralne zmienia się nieco (tzn odczyt z czujnika), w zależności od zasilania Arduino (np mój notebook zamiast 5V na USB ma 4.9, co przy niewielkich zmianach napięcia na wyjściu czujnika może mieć znaczenie &#8211; punkt odniesienia się zmienia to i odczyt zwracany przez <code>analogRead</code> się zmienia). Również z czasem czujnik może minimalnie zmienić odczyt. Wówczas zostaje ręczna zmiana wartości w kodzie, kompilacja i wgranie do Arduino.</p>
<p>Lepszym rozwiązaniem byłaby kalibracja &#8211; otóż specjalny tryb, zamiast grania wyświetli polecenie: <em>Wychyl się w lewo</em>. Potem odczytuje wartość z czujnika. Następnie <em>Wychyl się w prawo</em> i znowu. W ten sposób szkic jest sam w stanie wyznaczyć sobie wartości _NEUTRAL i _DELTA.</p>
<p>Druga uwaga &#8211; dynamika gry jest duża i chyba taki kontroler byłby lepszy dla jakiejś sportowej niż strzelanki. Ale to już lepiej byłoby jednak użyć klawiaturowego firmware i komputera, może innym razem&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/08/gameduino-czyli-zrobmy-fajna-gre/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Czytnik RFID 125 kHz</title>
		<link>http://starter-kit.nettigo.pl/2011/06/czytnik-rfid-125-khz/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=czytnik-rfid-125-khz</link>
		<comments>http://starter-kit.nettigo.pl/2011/06/czytnik-rfid-125-khz/#comments</comments>
		<pubDate>Wed, 15 Jun 2011 11:47:26 +0000</pubDate>
		<dc:creator>netmaniac</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[rfid]]></category>
		<category><![CDATA[rfid-kit]]></category>
		<category><![CDATA[rfid-reader]]></category>
		<category><![CDATA[rfid-tokens]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=696</guid>
		<description><![CDATA[Od dłuższego czasu w ofercie Nettigo jest czytnik kart RFID z interfejsem UART. Co to znaczy? System RFID w uproszczeniu składa się z dwóch elementów &#8211; czytnika i tokenów. Token to fizyczna rzecz, mająca w sobie trochę elektroniki. Każdy token ma swój unikalny numer. Gdy token zostanie zbliżony do drugiego elementu &#8211; czytnika, ten będzie [...]]]></description>
			<content:encoded><![CDATA[<p>Od dłuższego czasu w ofercie Nettigo jest <a title="Czytnik RFID" href="http://nettigo.pl/product/Kompletny-zestaw-czytnika-RFID,rfid-kit">czytnik kart RFID z interfejsem UART</a>. Co to znaczy?</p>
<p>System RFID w uproszczeniu składa się z dwóch elementów &#8211; czytnika i tokenów. Token to fizyczna rzecz, mająca w sobie trochę elektroniki. Każdy token ma swój unikalny numer. Gdy token zostanie zbliżony do drugiego elementu &#8211; czytnika, ten będzie w stanie odczytać ten numer.</p>
<p>Jakie ma to zalety? Po pierwsze &#8211; token nie musi mieć swojego zasilania (wbudowanej baterii). Może być mały, lekki i odporny na wodę. Po drugie &#8211; nie jest potrzebny fizyczny kontakt tokena i czytnika &#8211; wystarczy token zbliżyć na kilka, kilkanaście centymetrów aby go odczytać.</p>
<p>Na rynku dostępnych jest kilka standardów, jeden z pierwszych to standard działający na częstotliwości 125 kHz. Urządzenia i tokeny do niego są proste a co za tym idzie niedrogie.</p>
<p>Jak skorzystać z RFID?</p>
<p>Pokażę teraz jak wygląda odczytywanie tokenów. Zestaw RFID sprzedawany przez Nettigo składa się z czytnika oraz 5 tokenów &#8211; 2 kart oraz trzech breloczków. Przyjrzyjmy się jednemu.</p>
<div id="attachment_697" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-token.jpg"><img class="size-medium wp-image-697" title="Token RFID" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-token-300x225.jpg" alt="Token RFID" width="300" height="225" /></a><p class="wp-caption-text">Token RFID</p></div>
<p>Jest na nim numer. Ale to nie numer, który zostanie odczytany przez czytnik. Dlaczego? Gdyby numer nadrukowany na tokenie oraz ten &#8216;w środku&#8217; były takie same, łatwo byłoby sklonować token, a tak aby poznać jego numer trzeba go odczytać &#8211; nie jest to jakiś duży problem, ale zawsze utrudnienie.</p>
<p>Teraz &#8211; jak go odczytać. Przyjrzyjmy się czytnikowi. Jak napisałem ma interfejs UART, czyli można go podłączyć do Arduino korzystając z portu szeregowego, lub do komputera za pomocą konwertera USB/Serial.</p>
<p>Czytnik ma trzy złącza: 2, 3, i 5 pinów. Złącze dwu-pinowe jest przeznaczone na antenę dołączoną do zestawu. Nie ma znaczenia jak ją podłączymy. Pozostałe dwa złącza pozwoli opisać to zdjęcie:</p>
<div id="attachment_698" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-czytnik-wyprowadzenia.jpg"><img class="size-medium wp-image-698" title="Wyprowadzenia czytnika RFID" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-czytnik-wyprowadzenia-300x225.jpg" alt="Wyprowadzenia czytnika RFID" width="300" height="225" /></a><p class="wp-caption-text">Wyprowadzenia czytnika RFID</p></div>
<p>Zaznaczyłem na czerwono piny nr 1 i tak złącze trzy-pinowe:</p>
<ol>
<li>LED &#8211; podłączenie dla diody sygnalizującą pracę czytnika (świeci ciągle gaśnie na chwilę w momencie odczytania tokena)</li>
<li>+5V &#8211; wyprowadzone napięcie zasilania</li>
<li>GND &#8211; masa</li>
</ol>
<p>Jak widać rola jego jest informacyjna &#8211; można podłączyć diodę, która będzie ludziom sygnalizować że został odczytany token.</p>
<p>Złącze 5-cio pinowe to jest to co nam jest potrzebne aby uzyskać informację od czytnika:</p>
<ol>
<li>TX &#8211; czyli to co odczytuje czytnik</li>
<li>RX &#8211; dane wysyłane do czytnika (ten model nie jest konfigurowalny, więc nie ma pożytku z tego)</li>
<li>NC &#8211; nie używany</li>
<li>GND &#8211; masa</li>
<li>+5V &#8211; zasilanie</li>
</ol>
<p>Zademonstruję podłączenie czytnika do komputera z wykorzystaniem <a title="FTDI Friend" href="http://nettigo.pl/product/FTDI-Friend---wygodny-konwerter-USB%7CSerial,ftdi-friend">konwertera USB/Serial &#8211; FTDI Friend</a>. Podłączamy TX (pin 1) czytnika do RX na FTDI Friend, masę i zasilanie 5V &#8211; i gotowe:</p>
<div id="attachment_699" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-czytnik-podłączenie.jpg"><img class="size-medium wp-image-699" title="Podłączenie czytnika RFID do FTDI Friend" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-czytnik-podłączenie-300x225.jpg" alt="Podłączenie czytnika RFID do FTDI Friend" width="300" height="225" /></a><p class="wp-caption-text">Podłączenie czytnika RFID do FTDI Friend</p></div>
<p>Po wsadzeniu kabla USB do komputera i podpięciu monitora portu szeregowego (Hyperterminal na Windows, gtkterm lub minicom na Linuxie, lub monitor z Arduino IDE, parametry portu 9600,8,N,1) dostaniemy np taki ciąg:</p>
<p>0041DEF392FE</p>
<p>Najpierw &#8211; bajt o wartości 0&#215;02 &#8211; symbol startu numeru karty, potem 5 bajtów zapisanych w ASCI w kodowaniu szesnastkowym (tutaj to 00-41-DE-F3-92), dwa znaki sumy kontrolnej (XOR 5 bajtów, tutaj to FE), bajt o wartości 0&#215;03 &#8211; znak końca transmisji.</p>
<p>W dużym skrócie &#8211; to tyle. Podłączyć do Arduino można &#8216;tak samo&#8217;. Czyli podłączamy zasilanie i masę, oraz RX na Arduino (D0) z TX na czytniku i korzystając z Serial.read() można odczytywać dane.</p>
<p>Cały układ podłączony via FTDI Friend wygląda tak:</p>
<div id="attachment_702" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-calosc.jpg"><img class="size-medium wp-image-702" title="Caly uklad" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/06/rfid-calosc-300x171.jpg" alt="Caly uklad" width="300" height="171" /></a><p class="wp-caption-text">Caly uklad</p></div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/06/czytnik-rfid-125-khz/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Debouncing klawiszy w Nettigo Keypad (i nie tylko)</title>
		<link>http://starter-kit.nettigo.pl/2011/06/debouncing-klawiszy-w-nettigo-keypad-i-nie-tylko/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=debouncing-klawiszy-w-nettigo-keypad-i-nie-tylko</link>
		<comments>http://starter-kit.nettigo.pl/2011/06/debouncing-klawiszy-w-nettigo-keypad-i-nie-tylko/#comments</comments>
		<pubDate>Sat, 04 Jun 2011 09:25:32 +0000</pubDate>
		<dc:creator>netmaniac</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[keypad]]></category>
		<category><![CDATA[nettigo]]></category>
		<category><![CDATA[nettigo keypad]]></category>
		<category><![CDATA[przyklady]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=688</guid>
		<description><![CDATA[Biblioteka do Nettigo Keypada została uaktualniona o domyślne wsparcie dla debouncingu. O co chodzi? Jeżeli naciskasz klawisz, może pojawić się zjawisko migotania podczas włączania i wyłączania przycisku. Wynika to właściwości styku mikroprzełącznika &#8211; jest to mechaniczne zetknięcie/rozłączenie styków, więc może być tak że pojawi się szereg impulsów podczas jednego przełączenia. Gdy Arduino często sprawdza stan [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Biblioteka do Nettigo Keypad" href="https://github.com/netmaniac/NettigoKeypad">Biblioteka</a> do Nettigo <a title="Nettigo Keypad dla Arduino" href="http://nettigo.pl/product/Nettigo-Keypad---odczyt-przez-wejscie-analogowe,keypad">Keypada</a> została uaktualniona o domyślne  wsparcie dla debouncingu. O co chodzi? Jeżeli naciskasz klawisz, może  pojawić się zjawisko <em>migotania</em> podczas włączania i wyłączania  przycisku. Wynika to właściwości styku mikroprzełącznika &#8211; jest to  mechaniczne zetknięcie/rozłączenie styków, więc może być tak że pojawi  się szereg impulsów podczas jednego przełączenia.</p>
<p>Gdy Arduino często sprawdza stan klawisza takie migotanie może zostać odczytane jako wielokrotne naciśnięcia i zwolnienia danego przycisku, mimo że został naciśnięty tylko raz.</p>
<h3>Jak temu zapobiegać?</h3>
<p>Potrzebny jest tak zwany debouncing. Można po odczycie stanu klawisza poczekać chwilę używając delay. Proste i skuteczne.</p>
<p><em>Pseudokod</em> realizujący to wygląda tak:</p>
<pre>obecny_stan_klawisza = odczytaj_klawisz();
if (obecny_stan_klawisza != poprzedni_stan) {
  poprzedni_stan = obecny_stan_klawisza;
  zrob_cos_po_zmianie_stanu();
  delay(50);
}
</pre>
<p>Taki kod po wykryciu zmiany stanu klawisza zapisuje jego nową wartość , wykonuje co trzeba po naciśnięciu/zwolnieniu klawisza i czeka 50 milisekund, zanim podejmie dalsze wykonywanie programu. Jeżeli więcej nic specjalnie szkic nie ma do roboty to zapobiegnie to odczytaniu migotania w następnym przebiegu loop.</p>
<p>Takie rozwiązanie, choć proste ma jednak jedną niezaprzeczalną wadę &#8211; Arduino nic nie może innego robić w czasie gdy czeka korzystając z funkcji delay. Nie zawsze jest to dopuszczalne. Jeżeli nasz szkic robi coś co wymaga szybkiej pracy, lub ścisłych zależności czasowych (generowanie obrazu, programowe implementacje różnych protokołów) takie rozwiązanie jest wykluczone.</p>
<p>Co zostaje? Trzeba zignorować wszystkie zmiany stanu klawisza w krótkim czasie po zaobserwowaniu jego wciśnięcia. Zapamiętujemy czas wciśnięcia klawisza i jeżeli zmiany stanu następują w krótkim czasie po tym &#8211; są ignorowane. Znowu &#8211; pseudokod:</p>
<pre>obecny_stan_klawisza = odczytaj_klawisz();
if (obecny_stan_klawisza != poprzedni_stan &amp;&amp; millis() - czas_ostatniego_wcisniecia &gt; timeout) {
  poprzedni_stan = obecny_stan_klawisza;
  czas_ostatniego_wcisniecia = millis()
  zrob_cos_po_zmianie_stanu();
}</pre>
<p>Oczywiście, na starcie szkicu musimy ustawić wartość <code>czas_ostatniego_wcisniecia</code> i <code>timeout</code> &#8211; zostawione bez nadanej wartość mogą stwarzać problemy. Czas jaki trzeba czekać ignorując zmiany stanu zależy od przełącznika. W wypadku mikroprzełączników 50 ms wystarcza w  zupełności.</p>
<p>Dla wejść cyfrowych są gotowe biblioteki do obsługi debouncingu np <a title="Biblioteka Arduino Bounce" href="http://arduino.cc/playground/Code/Bounce">Bounce</a>, jednak dla naszego keypada ona się nie nadaje.</p>
<p>Dlatego biblioteka do <a href="https://github.com/netmaniac/NettigoKeypad">Nettigo Keypada</a> ma od wersji 0.3 domyślnie <em>debouncowane</em> klawisze. Domyślnie timeout jest ustawiony na 50 ms. Można go odczytać/zmienić przez funkcje <code>getDebounceDelay</code> i <code>setDebounceDelay</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/06/debouncing-klawiszy-w-nettigo-keypad-i-nie-tylko/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>P4A &#8211; PHP dla Arduino cz. 2</title>
		<link>http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=p4a-php-dla-arduino-cz-2</link>
		<comments>http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-2/#comments</comments>
		<pubDate>Thu, 19 May 2011 09:20:43 +0000</pubDate>
		<dc:creator>netmaniac</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[eth-sd-shield]]></category>
		<category><![CDATA[ethernet]]></category>
		<category><![CDATA[ethernet shield]]></category>
		<category><![CDATA[fat]]></category>
		<category><![CDATA[fat16]]></category>
		<category><![CDATA[fat32]]></category>
		<category><![CDATA[microsd]]></category>
		<category><![CDATA[sd]]></category>
		<category><![CDATA[sdfatlib]]></category>
		<category><![CDATA[sdhc]]></category>
		<category><![CDATA[webduino]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=656</guid>
		<description><![CDATA[W poprzednim wpisie skończyliśmy w miejscu w którym Webduino mogło nam już serwować dowolne pliki z karty SD. Teraz musimy wybrane pliki przepuścić przez nasze PHP :) i rezultat przesłać do klienta. Dla uproszczenia całego procesu, zakładamy, że każdy plik który ma zostać poddany obróbce jest nam znany. Tzn rejestrujemy każdy taki plik (URL) za [...]]]></description>
			<content:encoded><![CDATA[<p>W <a href="http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-1/">poprzednim wpisie skończyliśmy w miejscu w którym Webduino</a> mogło nam już serwować dowolne pliki z karty SD. Teraz musimy wybrane pliki przepuścić przez nasze PHP :) i rezultat przesłać do klienta.</p>
<p>Dla uproszczenia całego procesu, zakładamy, że każdy plik który ma zostać poddany obróbce jest nam znany. Tzn rejestrujemy każdy taki plik (URL) za pomocą <code>addCommand</code>.</p>
<p>Następnie jak to ma działać? Idea jest taka, że mamy swoje funkcje w kodzie szkicu i których wynik działania ma zostać wklejony w wybrane miejsca kodu HTML. Czyli chcemy mieć plik HTML z takim kawałkiem kodu:</p>
<pre>&lt;p&gt;
Wynik odczytu czujnika 1: MAGIA1&lt;br/&gt;
Wynik odczytu czujnika 2: MAGIA2&lt;br/&gt;
&lt;/p&gt;</pre>
<p>Na skutek działania naszego parsera chcemy MAGIA1 i MAGIA2 mieć zastąpione przez wynik działania funkcji w szkicu.</p>
<h2>Najlepiej zacząć od magii!</h2>
<p>Czyli jak zapisać w HTMLu że ma nasz parser wsadzić w to miejsce inny tekst. Dla uproszczenia przyjmujemy następującą formułę: <code>#{X}</code> zostanie zastąpione przez wywołanie odpowiedniej funkcji. <code>X</code> to jest jednoliterowy mnemonik określający którą funkcję chcemy wywołać.</p>
<p>Funkcje muszą mieć określoną definicję i nie mogą przyjmować argumentów. Dlaczego? Przyjmowanie argumentów komplikuje parser i na tym etapie nie jest chyba potrzebne.</p>
<p>Wybraliśmy sposób zapisu w HTML. Teraz, jak nasza funkcja ma przekazać wynik działania? Otóż zakładamy że przykładowa funkcja ma mieć następującą definicję:</p>
<pre><span style="color: #cc6600;">void</span> timeReport(<span style="color: #cc6600;">char</span> *buf) { itoa(<span style="color: #cc6600;">millis</span>()/1000, buf, 10); };</pre>
<p>Funkcja ma nie zwracać danych (<code>void</code>) a przyjmować wskaźnik na bufor tekstowy. W tym buforze ma umieścić wynik swojego działania, który następnie zostanie wstawiony w odpowiednie miejsce HTML. Funkcja sama musi pilnować, żeby nie przepełnić tego bufora. Jego rozmiar jest definiowany przez <code>P4A_MAX_FUNCTION_RESPONSE_LENGTH</code>.</p>
<p>Jak widać powyższa funkcja zwraca liczbę pełnych sekund które minęły od uruchomienia lub resetu Arduino.</p>
<p>Jak parsować plik? Dzięki prostemu znacznikowi naszej magii jest to względnie proste. Czytamy plik znak po znaku. Jeżeli natkniemy się na # wówczas czytamy następny znak i sprawdzamy czy jest to klamra { tworzące w sumie sekwencję otwierającą naszej magii. Póki nie natkniemy się na # znaki nas nieinteresujące wysyłamy do bufora, który zostanie w końcu wysłany do przeglądarki.</p>
<p>Jeżeli następny znak po # nie jest klamrą wysyłamy do przeglądarki oba znaki # i następny &#8211; w tym miejscu nie mamy nic do robienia, czekamy na następny #.</p>
<p>Gdy jednak drugi znak to była klamra, wówczas czytamy kolejny znak &#8211; jest to nasz mnemonik! Wywołujemy odpowiednią funkcję w zależności od mnemonika, wysyłamy wynik do przeglądarki.</p>
<p>Następnie czytamy plik aż do zamykającej klamry i skanujemy dalej plik szukając kolejnego #.</p>
<p>Pozostaje nam kwestia przypisania funkcji do mnemoników. Posłuży nam do tego</p>
<h2>Tablica wskaźników do funkcji</h2>
<p>Pomówimy teraz o trochę bardziej zaawansowanym temacie, czyli o wskaźnikach do funkcji. Otóż jak się dobrze zastanowić, funkcja po skompilowaniu jest adresem pod którym znajduje się kod ją realizujący oraz pewien kontrakt określający jak przekazywane są dane do funkcji i jak z niej są zwracane.</p>
<p>Jeżeli kontrakt jest taki sam w wypadku wielu funkcji (czyli lista typów argumentów oraz zwracana wartość), funkcje takie można zapisać w postaci samego wskaźnika i trzymać w tablicy. Wówczas możemy wywołać taką funkcję używając zapisanego wskaźnika, nie musimy znać jej nazwy w kodzie.</p>
<p>To właśnie nam posłuży jako mechanizm tłumaczący mnemoniki na wywoływane funkcje. Teraz wiemy czemu wszystkie nasze funkcje muszą mieć taki sam interfejs/kontrakt (jak ustaliliśmy będzie do <code>void</code> jeżeli chodzi o zwracane dane oraz <code>char *</code> jako argument) &#8211; dzięki temu możemy je trzymać w jednej tablicy, której indeks będzie literą. Ale po kolei:</p>
<pre><span style="color: #cc6600;">void</span> (*_fcts[<span style="color: #006699;">'z'</span>-<span style="color: #006699;">'a'</span>])(<span style="color: #cc6600;">char</span> *);</pre>
<p>Definiujemy tablicę wskaźników na funkcje. void z przodu określa typ zwracany przez funkcję, to co w nawiasie na końcu oznacza jakich argumentów spodziewa się funkcja. W środku znajduje się deklaracja tablicy. Jej rozmiar <code>'z'-'a'</code> może wydawać się dziwny, ale w takim kontekście znaki przez kompilator traktowane są jako bajty. Czyli od kodu &#8216;z&#8217; odejmujemy kod &#8216;a&#8217; &#8211; różnica to liczba liter. Dzięki temu mamy tablicę mogącą pomieścić tyle wskaźników na funkcje ile jest małych liter w alfabecie łacińskim (lub raczej w standardzie ASCII).</p>
<p>Jeśli będziemy mieli jakiś kod litery wystarczy od niej odjąć kod litery a a dostaniemy indeks z tablicy. Zresztą zobaczmy:</p>
<pre>				<span style="color: #cc6600;">if</span> (_fcts[c[0]-<span style="color: #006699;">'a'</span>] == NULL) {
					bufferedSend(server,<span style="color: #006699;">"n/a"</span>);
					<span style="color: #cc6600;">continue</span>;
				} <span style="color: #cc6600;">else</span> {
					<span style="color: #7e7e7e;">//Call function from table</span>
					_fcts[ c[0]-<span style="color: #006699;">'a'</span>](buf);
					<span style="color: #7e7e7e;">//Write response to client</span>
					bufferedSend( server, buf );
				}</pre>
<p><code>c[0]</code> zawiera znak naszego mnemonika. Jeżeli tablica nie ma wartości (tzn ma wartość NULL) pod indeksem <code>c[0] - 'a'</code> wówczas w HTML jest wstawiane <code>n/a</code> &#8211; nasz sposób na sygnalizowanie złych mnemoników.</p>
<p><code>'a'</code> w ASCII ma kod 97. Jeżeli nasz mnemonik to też <code>'a'</code> 97-97 = 0, czyli pierwszy element tablicy. Jeżeli mnemonik to <code>'b'</code> to <code>'b'-'a'</code> = 98 &#8211; 97 = 1 czyli drugi element tablicy, itd. Przydałoby się sprawdzać czy mnemonik jest we właściwym zakresie, bo inaczej możemy próbować wywołać funkcję z losowym adresem (jeżeli indeks tablicy jest poza zakresem, wówczas pamięć RAM spoza tablicy zostanie odczytana i procesor spróbuje zinterpretować wartość spod tego adresu jako adres funkcji do wywołania &#8211; gwarantowane zawieszenie się programu).</p>
<p>W powyższym kodzie widać też, jak należy wywoływać funkcje w tablicy:</p>
<pre>_fcts[ c[0]-<span style="color: #006699;">'a'</span>](buf);</pre>
<p>Do analizy pliku wykorzystujemy zmienną <code>status</code>, dzięki której wiem w jakim stanie nasz parser się znajduje. Może to być:</p>
<ul>
<li><code>P4A_PARSE_REGULAR</code> &#8211; stan w którym szukamy znaku #</li>
<li><code>P4A_PARSE_HASH</code> &#8211; stan w którym czekamy na klamrę otwierającą</li>
<li><code>P4A_PARSE_COMMAND</code> &#8211; stan w którym szukamy mnemonika</li>
</ul>
<p>Dzięki tym stanom łatwiej kontrolować co robi nasz parser. W zależności od bieżącego stanu oraz kolejnego znaku można podejmować decyzje co dalej. Wysyłać dane do przeglądarki czy wywoływać funkcję na podstawie mnemonika.</p>
<p>Dla zainteresowanych całość funkcji <code>parseP4A</code>:</p>
<pre><span style="color: #7e7e7e;">//Reads HTML file, parses looking for our macro and sends back to client</span>
<span style="color: #cc6600;">int</span> parseP4A( <span style="color: #cc6600;">char</span> * filename, WebServer &amp;server ) {
	<span style="color: #7e7e7e;">//simple status</span>
	<span style="color: #cc6600;">short</span> <span style="color: #cc6600;">int</span> STATUS = P4A_PARSE_REGULAR;
	<span style="color: #cc6600;">char</span> c[2];
	c[1] = 0;

	<span style="color: #7e7e7e;">//buffer to hold response from functions - there is no boundary checking, so</span>
	<span style="color: #7e7e7e;">//function has to not overwrite data</span>
  <span style="color: #cc6600;">char</span> buf[P4A_MAX_FUNCTION_RESPONSE_LENGTH];

  <span style="color: #cc6600;">if</span> (! file.<span style="color: #cc6600;">open</span>(&amp;root, filename, O_READ)) {
		<span style="color: #cc6600;">return</span> -1;
  }
  <span style="color: #cc6600;">while</span> ((file.<span style="color: #cc6600;">read</span>(c,1) ) &gt; 0) {
		<span style="color: #cc6600;">if</span> (STATUS == P4A_PARSE_REGULAR &amp;&amp; c[0] == <span style="color: #006699;">'#'</span>)
		{
			<span style="color: #7e7e7e;">//hash was found we need to inspect next character</span>
			STATUS = P4A_PARSE_HASH;
			<span style="color: #cc6600;">continue</span>;
		}

		<span style="color: #cc6600;">if</span> (STATUS == P4A_PARSE_HASH) {
			<span style="color: #cc6600;">if</span> (c[0] == <span style="color: #006699;">'{'</span>) {
				<span style="color: #7e7e7e;">//go to command mode</span>
				STATUS = P4A_PARSE_COMMAND;
				<span style="color: #cc6600;">continue</span>;
			} <span style="color: #cc6600;">else</span> {
				<span style="color: #7e7e7e;">//fallback to regular mode, but first send pending hash</span>
				STATUS = P4A_PARSE_REGULAR;
				bufferedSend(server, <span style="color: #006699;">"#"</span>);
			}

		}

		<span style="color: #cc6600;">if</span> (STATUS == P4A_PARSE_COMMAND) {
			<span style="color: #cc6600;">if</span>(c[0] == <span style="color: #006699;">'}'</span>) {
				STATUS = P4A_PARSE_REGULAR;
				<span style="color: #cc6600;">continue</span>;
			};
		  <span style="color: #cc6600;">if</span> (c[0] &gt;= <span style="color: #006699;">'a'</span> &amp;&amp; c[0] &lt;=<span style="color: #006699;">'z'</span>) {
				<span style="color: #7e7e7e;">//Command found</span>
				<span style="color: #cc6600;">if</span> (_fcts[c[0]-<span style="color: #006699;">'a'</span>] == NULL) {
					bufferedSend(server,<span style="color: #006699;">"n/a"</span>);
					<span style="color: #cc6600;">continue</span>;
				} <span style="color: #cc6600;">else</span> {
					<span style="color: #7e7e7e;">//Call function from table</span>
					_fcts[ c[0]-<span style="color: #006699;">'a'</span>](buf);
					<span style="color: #7e7e7e;">//Write response to client</span>
					bufferedSend( server, buf );
				}
			}

		}

		<span style="color: #cc6600;">if</span> (STATUS == P4A_PARSE_REGULAR)
			bufferedSend(server, c);
  }

  <span style="color: #7e7e7e;">//force buffer flushing</span>
  flushBuffer(server);
	file.<span style="color: #cc6600;">close</span>();
  <span style="color: #cc6600;">return</span> 0;
}</pre>
<h2>P4A czyli PHP for Arduino w akcji</h2>
<p>Załóżmy, że chcemy zrobić ładny wirtualny barometr, ale pokazujący prawdziwe ciśnienie. Skorzystamy z <a title="Adapter do BMP085" href="http://nettigo.pl/product/Czujnik-cisnienia-atmosferycznego-BMP085,SEN-09694">BMP085</a> &#8211; jest to poręczny adapter (breakout board) czujnika ciśnienia i temperatury  produkowany przez SparkFun. Rezultat ma być następujący:</p>
<div id="attachment_663" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/05/baro.png"><img class="size-medium wp-image-663" title="Wygląd wirtualnego barometru" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/05/baro-300x297.png" alt="Wygląd wirtualnego barometru" width="300" height="297" /></a><p class="wp-caption-text">Wygląd wirtualnego barometru</p></div>
<p>Wskazówka ma pokazywać wartość odczytaną z czujnika, a symbol <em>prognozowanej pogody</em> ma się zmieniać w zależności od wartości ciśnienia.</p>
<p>Jak sobie poradzimy z tym,  że jak pisaliśmy w poprzednim odcinku, serwer WWW na Arduino, nie jest najlepszym rozwiązaniem, gdy trzeba serwować wiele plików jednocześnie (obrazki!)? Ano, skoro całość i tak ma być dostępna z sieci, czemu nie udostępnić statycznych zasobów z serwera sieci? Na swoje potrzeby mam taki serwer i wszystkie potrzebne elementy graficzne są na nim umieszczone. Czyli tarcza barometru, obraz wskazówki i symbole pogody.</p>
<p>Na Arduino znajduje się sam plik HTML, na karcie SD. W szkicu umieszczamy funkcję wywołującą parser dla tego pliku:</p>
<pre><span style="color: #cc6600;">void</span> index(WebServer &amp;server, WebServer::ConnectionType type, <span style="color: #cc6600;">char</span> *, bool){
  server.httpSuccess();
  <span style="color: #cc6600;">if</span> (!parseP4A(<span style="color: #006699;">"BARO.HTM"</span>, server)) {
		<span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span>(<span style="color: #006699;">"P4A: -1"</span>);
	}
};</pre>
<p><code>parseP4A</code> to funkcja, która parsuje podany plik i wysyła rezultat korzystając z obiektu serwera Webduino. Pozostaje zarejestrować naszą funkcję jako domyślną komendę:</p>
<pre>webserver.setDefaultCommand(&amp;index);</pre>
<p>Sam HTML wykorzystuję JaveScript i obiekt <code>canvas</code> do narysowania samej tarczy. Robi to funkcja draw, która jako argument przyjmuje ciśnienie w hektopaskalach. Gdy strona jest gotowa do wyświetlenia (tzn załadowała się) przez atrybut <code>onload</code> wywołujemy funkcję draw, ciśnienie jest wstawiane przez nasze P4A:</p>
<pre>&lt;body onload="draw(#{p});"</pre>
<p>Mnemonik p trzeba skojarzyć z właściwą funkcją. W szkicu, w <code>setup</code> ustawiamy funkcję dla <code>p</code>:</p>
<pre>  _fcts[<span style="color: #006699;">'p'</span>-<span style="color: #006699;">'a'</span>] = pressureReport;</pre>
<p>A samo <code>pressureReport</code> ma wygląd:</p>
<pre><span style="color: #cc6600;">void</span> pressureReport(<span style="color: #cc6600;">char</span> *buf) {
	bmp085_read_temperature_and_pressure (&amp;temperature, &amp;pressure);
	itoa(pressure/100.0, buf, 10);
	<span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">print</span> (<span style="color: #006699;">"temp &amp; press: "</span>);
	<span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">print</span> (temperature/10.0);
	<span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">print</span>(<span style="color: #006699;">" "</span>);
	<span style="color: #cc6600;"><strong>Serial</strong></span>.<span style="color: #cc6600;">println</span> (pressure/100.0);

};</pre>
<p>Serial jest używany do sprawdzania czy wartości są jak się spodziewamy i nie ma wpływu na działanie barometru. <code>bmp085_read_temperature_and_pressure</code> to funkcja z kodu obsługi BMP085 zaczerpnięta z <a href="http://mitat.tuu.fi/?p=78">fińskiego bloga</a>.</p>
<p>Całość kodu do ściągnięcia <a href="http://static.nettigo.pl/p4a/php4arduino.zip">tutaj</a>. Jest to szkic napędzający nasz serwer, plus plik HTML i grafiki. Tarcza barometru, grafika i kod HTML/JS <a href="http://sprae.jogger.pl/">autorstwa Sprae</a>.</p>
<h3>Instalacja</h3>
<p>Ściągnąć, rozpakować w <code>sketchbook</code>. Zawartość podkatalogu <code>html</code> wrzucić w główny katalog na karcie SD, wsadzić ją w shielda. Szkic poprawić podając właściwy MAC i IP adres. Po otwarciu strony głównej powinniśmy zobaczyć barometr, o ile posiadamy <a title="Czujnik ciśnienia BMP085" href="http://nettigo.pl/product/Czujnik-cisnienia-atmosferycznego-BMP085,SEN-09694">BMP085</a> ;)</p>
<h3>Kilka uwag na koniec</h3>
<p>Kod jest wersją beta :) tzn &#8211; działa na tyle ile moje testy to potwierdzają, może być (i na pewno jest) kilka błędów o których nie mam pojęcia :)</p>
<p>Kod należałoby uporządkować &#8211; np funkcje związane z buforowanym wyjściem powinny zostać przeniesione do kodu Wbeduino. Planuję to zrobić i wszystkie zmiany jakie w Webduino zostały wprowadzone wysłać do developerów Webduino &#8211; może coś z tego znajdzie się bezpośrednio w bibliotece.</p>
<p>Jestem ciekaw waszych opinii. Czy coś takiego jak P4A ma w ogóle sens i może być przydatne?</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-2/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>P4A &#8211; PHP dla Arduino cz. 1</title>
		<link>http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=p4a-php-dla-arduino-cz-1</link>
		<comments>http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-1/#comments</comments>
		<pubDate>Wed, 04 May 2011 23:45:35 +0000</pubDate>
		<dc:creator>netmaniac</dc:creator>
				<category><![CDATA[arduino]]></category>
		<category><![CDATA[dla początkujących]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[eth-sd-shield]]></category>
		<category><![CDATA[ethernet]]></category>
		<category><![CDATA[ethernet shield]]></category>
		<category><![CDATA[fat]]></category>
		<category><![CDATA[fat16]]></category>
		<category><![CDATA[fat32]]></category>
		<category><![CDATA[microsd]]></category>
		<category><![CDATA[sd]]></category>
		<category><![CDATA[sdfatlib]]></category>
		<category><![CDATA[sdhc]]></category>
		<category><![CDATA[webduino]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://starter-kit.nettigo.pl/?p=642</guid>
		<description><![CDATA[Gdy poznamy już trochę Arduino w głowie każdego prędzej czy później pojawi się pomysł na projekt, który wymaga aby Arduino mogło połączyć się z siecią. Czy to będzie automatyka domowa dostępna przez sieć, czy zestaw czujników raportujący odczyty do bazy danych &#8211; trzeba jakoś połączyć Arduino do Internetu. I tu z pomocą przychodzi nam Ethernet [...]]]></description>
			<content:encoded><![CDATA[<p>Gdy poznamy już trochę Arduino w głowie każdego prędzej czy później pojawi się pomysł na projekt, który wymaga aby Arduino mogło połączyć się z siecią. Czy to będzie automatyka domowa dostępna przez sieć, czy zestaw czujników raportujący odczyty do bazy danych &#8211; trzeba jakoś połączyć Arduino do Internetu. I tu z pomocą przychodzi nam Ethernet Shield.</p>
<p>Najpierw trochę historii. Ethernet Shield był początkowo kompatybilny z małym Arduino. Dlaczego nie z Mega? Otóż do komunikacji z układem W5100 będącym sercem shielda wykorzystywany jest protokół SPI &#8211; na cyfrowych wejściach nr 10, 11, 12 i 13. W Arduino Mega SPI jest na innych wejściach. Można to obejść wyginając nóżki shielda i podłączając je do właściwych wejść cyfrowych kabelkiem.</p>
<p>Ale to dotyczy starszych shieldów. Obecnie <a title="Ethernet Shield z czytnikiem kart microSD" href="http://nettigo.pl/product/Modul-Ethernet-z-czytnikiem-kart-SD,eth-sd-shield">sprzedawane przez Nettigo Ethernet Shieldy</a> są kompatybilne zarówno z <em>małymi</em> Arduino (UNO, Duemilanove) jaki i Mega (te z procesorami ATmega1280 i <a title="Arduino Mega 2560" href="http://nettigo.pl/product/Arduino-Mega-2560,ard-uno-mega">ATmega2560</a>) . Po czym poznać takiego shielda? Jeżeli jest na nim gniazdo kart microSD &#8211; znaczy to, że to jest nowsza wersja.</p>
<div id="attachment_653" class="wp-caption aligncenter" style="width: 310px"><a href="http://starter-kit.nettigo.pl/wp-content/uploads/2011/05/ethernet-sd-poe-2.jpg"><img class="size-medium wp-image-653" title="Ethernet Shield z gniazdem kart microSD" src="http://starter-kit.nettigo.pl/wp-content/uploads/2011/05/ethernet-sd-poe-2-300x225.jpg" alt="Ethernet Shield z gniazdem kart microSD" width="300" height="225" /></a><p class="wp-caption-text">Ethernet Shield z gniazdem kart microSD</p></div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Wspomniany już został W5100 &#8211; układ scalony koreańskiej firmy WIZnet, <em>napędzający</em> Ethernet Shielda. Różni się tym od wielu innych kontrolerów Ethernet, że stos TCP/IP jest zaimplementowany bezpośrednio w układzie scalonym.  <strong>Co to znaczy dla użytkownika</strong>? Że biblioteka, którą musisz wykorzystać aby komunikować się ze światem <strong>potrzebuje mniej pamięci RAM i zajmuje mniej pamięci flash</strong> w porównaniu z układami nie mającymi na sobie stosu TCP/IP.</p>
<h2>Jak korzystać z Ethernet Shielda?</h2>
<p>Jest wiele przykładów w sieci jak tworzyć strony WWW wyświetlające dane z Arduino. Jednak, jeżeli masz już większe  doświadczenie z programowaniem Arduino to pewnie wiesz, że wszystkie łańcuchy znakowe, nawet te zdefiniowane w kodzie zajmują RAM, którego w ATmedze zawsze mało.</p>
<p>Weźmy <em>oficjalny</em> przykład z <a href="http://arduino.cc/en/Tutorial/WebServer">Arduino Tutoriala</a>. Kod:</p>
<pre> client.<span style="color: #cc6600;">println</span>(<span style="color: #006699;">"HTTP/1.1 200 OK"</span>);
 client.<span style="color: #cc6600;">println</span>(<span style="color: #006699;">"Content-Type: text/html"</span>);
 client.<span style="color: #cc6600;">println</span>();</pre>
<p>Zajmie nam 40 bajtów RAMu (15 znaków w <em>HTTP&#8230;</em> + kończące zero i 23 w <em>Conte&#8230;</em> + kończące zero). Łatwo sobie wyobrazić co to znaczy gdy mamy 2kB do dyspozycji w ogóle. Strona HTML nie może być zbyt rozbudowana.</p>
<p>Istnieje pewne rozwiązanie, które może nam pomóc &#8211; czyli przechowywanie stringów w pamięci Flash. Pozwala to zmniejszyć użycie pamięci RAM, ale często kosztem dodatkowego kodu. Dostęp do stringów tak definiowanych wymaga użycia specjalnego makra i kompilator nie pozwoli nam korzystać z tego makra w wywołaniu funkcji oczekującej <code>char *</code> jako argumentu. Na dodatek &#8211; każda zmiana w kodzie HTML, który chcemy wysłać oznacza, że trzeba zmodyfikować szkic i wgrać go w Arduino.</p>
<p>Zaraz, przecież Ethernet Shield od dwóch wydań ma na sobie gniazda na karty microSD &#8211; nie można jakoś wykorzystać przestrzeni jaką dają karty SD? Można, ale trzeba się trochę postarać.</p>
<p>Najpierw &#8211;  Ethernet Shield musi zostać skonfigurowany do pracy w sieci &#8211; oznacza to ustawienie adresu MAC oraz IP. Można to zrobić tak:</p>
<pre><span style="color: #cc6600;">byte</span> mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
<span style="color: #cc6600;">byte</span> ip[] = { 192,168,1, 177 };

<span style="color: #cc6600;">Ethernet</span>.<span style="color: #cc6600;">begin</span>(mac, ip);
</pre>
<p>Adres MAC najlepiej, zamiast wymyślać samemu odczytać z naklejki jaka znajduje się na spodzie shielda. Adres IP zależy od konfiguracji sieci. Powyższa sekwencja <code>Ethernet.begin</code> będzie działać tylko w sieci lokalnej &#8211; tzn gdy  wszystkie adresy IP znajdują się w jednej podsieci. Jeżeli shield ma się łączyć z hostami w innych sieciach (zarówno jako klient lub gdy ma służyć jako serwer) musicie podać jeszcze jeden argument &#8211; tablice z 4 liczbami &#8211; adres IP domyślnej bramki (default gateway).  Więcej w <a href="http://www.arduino.cc/en/Reference/EthernetBegin">opisie biblioteki Ethernet</a>.</p>
<p>Większość klocków mamy prawie gotowych. Biblioteka Ethernet, wchodząca w skład Arduino IDE, nie jest tym co nam najbardziej będzie pasować. Ułatwia ona tworzenie m.in. serwera TCP, ale do serwera HTTP jeszcze trzeba kawałek. Dlatego przyda się nam <a title="Webduino" href="http://code.google.com/p/webduino/">Webduino</a> &#8211; na bazie biblioteki Ethernet, ktoś za nas wykonał spory kawałek pracy budując serwer HTTP.</p>
<p>Normalnie ściągnęlibyśmy ze strony <a title="Webduino do ściągnięcia" href="http://code.google.com/p/webduino/downloads/list"><em>Downloads</em></a> bibliotekę i rozpakowalibyśmy w <code>sketchbook/libraries</code>. Na nasze potrzeby będziemy modyfikować trochę Webduino,  będzie do ściągnięcia na końcu tego mini-tutoriala, więc na razie nic nie trzeba instalować.</p>
<h3>Webduino &#8211; jak zaczać?</h3>
<p>Na początek musimy wiedzieć, że za pomocą</p>
<pre><span style="color: #cc6600;">void</span> addCommand(const <span style="color: #cc6600;">char</span> *verb, Command *cmd);</pre>
<p>możemy zarejestrować dowolną funkcję wywoływaną gdy URL będzie pasował do <code>verb</code>. Przykład:</p>
<pre>webserver.addCommand(<span style="color: #006699;">"blob.htm"</span>, &amp;blob);</pre>
<p>Jeżeli URL będzie się zaczynał (tzn porcja po adresie Arduino) od <em>blob.htm</em>, wówczas wywołana zostanie funkcja <code>blob</code>, która musi przyjmować argumenty zgodnie ze zdefiniowanym typem <code>Command</code>:</p>
<pre>typedef <span style="color: #cc6600;">void</span> Command(WebServer &amp;server, ConnectionType type,

                       <span style="color: #cc6600;">char</span> *url_tail, bool tail_complete);</pre>
<p><code>server</code> to obiekt WebServer, dla którego nastąpiło wywołanie metody, <code>type</code> to rodzaj połaczenia (<code>INVALID, GET, HEAD, POST</code>), <code>url_tail</code> to jest to co zostało w URLu po dopasowanym <em>blob.htm</em>. Jeżeli URL został obcięty ze względu na niewielki bufor używany przez Webduino (ehh ta pamięć), to ostatni parametr <code>tail_complete</code> bedzie miał wartość <code>false</code>.</p>
<p>OK, ale to mało wygodne musieć rejestrować każdą funkcję, zwłaszcza, że chcemy serwować dane z karty SD, której zawartości nie znamy. Przyda się nam teraz <code>setFailureCommand</code>, która pozwoli zarejestrować funkcję w naszym kodzie wywoływaną, gdy nie nastąpiło żadne inne dopasowanie do zarejestrowanych funkcji przez <code>addCommand</code>.</p>
<p>Czyli jeżeli URL nie pasuje do żadnej zgłoszonej wcześniej funkcji, wówczas zostanie wywołana funkcja podana do <code>setFailureCommand</code>.</p>
<p>Teraz wystarczy w niej sprawdzić czy <code>url_tail</code> jest nazwą pliku na karcie SD (ponieważ nie nastąpiło żadne dopasowanie, <code>url_tail</code> zawiera pełny URL włącznie z pierwszym znakiem / za adresem Arduino). Gdy pliku nie ma &#8211; wyświetlamy HTTP 404, jeżeli jest &#8211; wystarczy go wysłać do klienta.</p>
<h3>Jak odczytać plik z karty SD?</h3>
<p>Podobnie jak z serwerem HTTP nie musimy robić wszystkiego sami. Dobrą pomocą do stworzenia tego szkicu był tutorial przygotowany przez <a href="http://www.ladyada.net/learn/arduino/ethfiles.html">Limor Fried czyli &#8211; LadyAda</a>. Korzysta on z biblioteki <a href="http://code.google.com/p/sdfatlib/downloads/list">SdFatLib</a>, która ma wsparcie dla systemów plików FAT16 i FAT32 (czyli tego co zwykle na kartach SD i SDHC jest).</p>
<p>Kod inicjalizacji i obsługi plików w zasadzie został wzięty z tego tutoriala. Omówię tutaj funkcję <code>fetchSD</code>, która została zarejestrowana przez <code>setFailureCommand</code>. Jej zadaniem jest znaleźć plik na karcie i wysłać go do przeglądarki:</p>
<pre>P(CT_PNG) = <span style="color: #006699;">"image/png\n"</span>;
P(CT_JPG) = <span style="color: #006699;">"image/jpeg\n"</span>;
P(CT_HTML) = <span style="color: #006699;">"text/html\n"</span>;
P(CT_CSS) = <span style="color: #006699;">"text/css\n"</span>;
P(CT_PLAIN) = <span style="color: #006699;">"text/plain\n"</span>;
P(CT) = <span style="color: #006699;">"Content-type: "</span>;
P(OK) = <span style="color: #006699;">"HTTP/1.0 200 OK\n"</span>;
<span style="color: #cc6600;">void</span> fetchSD(WebServer &amp;server, WebServer::ConnectionType type, <span style="color: #cc6600;">char</span> *urltail, bool){
	<span style="color: #cc6600;">char</span> buf[32];
	int16_t  readed;

	++urltail;
	<span style="color: #cc6600;">char</span> *dot_index; <span style="color: #7e7e7e;">//Where dot is located</span>
	<span style="color: #cc6600;">if</span> (! file.<span style="color: #cc6600;">open</span>(&amp;root, urltail, O_READ)) {
		<span style="color: #7e7e7e;">//Real 404</span>
		webserver.httpNotFound();
  } <span style="color: #cc6600;">else</span> {
	<span style="color: #cc6600;">if</span> (dot_index = strstr(urltail, <span style="color: #006699;">"."</span>)) {
		++dot_index;
		server.printP(OK);
		server.printP(CT);
		<span style="color: #cc6600;">if</span> (!strcmp(dot_index, <span style="color: #006699;">"htm"</span>)) {
				server.printP(CT_HTML);

		} <span style="color: #cc6600;">else</span> <span style="color: #cc6600;">if</span> (!strcmp(dot_index, <span style="color: #006699;">"css"</span>)) {
				server.printP(CT_CSS);

		} <span style="color: #cc6600;">else</span> <span style="color: #cc6600;">if</span> (!strcmp(dot_index, <span style="color: #006699;">"jpg"</span>)) {
				server.printP(CT_JPG);

		} <span style="color: #cc6600;">else</span> {
				server.printP(CT_PLAIN);
		}
		server.<span style="color: #cc6600;">print</span>(CRLF);
	}
	readed = file.<span style="color: #cc6600;">read</span>(buf,30);
	<span style="color: #cc6600;">while</span>( readed &gt; 0) {
		buf[readed] = 0;
		bufferedSend(server,buf,readed);
		readed = file.<span style="color: #cc6600;">read</span>(buf,30);
	}
	flushBuffer(server);
	file.<span style="color: #cc6600;">close</span>();
	}
}</pre>
<p>Na samym początku mamy zarejestrowanych kilka stałych znakowych przechowywanych w pamięci flash.</p>
<pre>P(CT_PNG) = <span style="color: #006699;">"image/png\n"</span>;</pre>
<p>Makro <code>P</code> jest częścią Webduino, które służy do zapisywania stringów na pamięci flash a nie w RAM. A stałe te to są nazwy różnych formatów danych, tzw MIME Type, jakie chcemy obsługiwać. O co chodzi? Przeglądarka nie wie jakie dane zostaną wysłane przez serwer. Czy to będzie HTML czy obrazek dowiaduje się ona właśnie z nagłówka <em>Content-Type</em>, o którym za chwilę.</p>
<p>Następnie &#8216;pozbywamy&#8217; się wiodącego ukośnika: <code>++urltail;</code>, potem próbujemy otworzyć plik na karcie SD &#8211; jeżeli nie udaje się &#8211; to wyświetlamy błąd HTTP 404 (Not Found):</p>
<pre>	<span style="color: #cc6600;">if</span> (! file.<span style="color: #cc6600;">open</span>(&amp;root, urltail, O_READ)) {
		<span style="color: #7e7e7e;">//Real 404</span>
		webserver.httpNotFound();
  } <span style="color: #cc6600;">else</span> {</pre>
<p>jeżeli się udało otworzyć plik, to w <code>else</code> spróbujemy go odczytać i wysłać do klienta.</p>
<p>Teraz kilka uwag. Po pierwsze &#8211; SdFatLib obsługuje tylko krótkie nazwy w formacie 8.3. Jeżeli spróbujesz użyć dłuższych nazw (które FAT32 dopuszcza) to pamiętaj, że nazwa widziana przez SdFatLib będzie inna od tej, którą zobaczysz po podmonotwaniu karty w twoim komputerze. I jeżeli zrobisz plik <em>index.html</em> (cztery znaki w rozszerzeniu), wówczas nazwa będzie dla SdFatLib <em>ind~1.htm</em>. Cóż, nawet jeżeli teraz w komputerze zmienisz nazwę na <em>index.htm</em>, wpis w katalog będzie w rozszerzonej formie. Musisz skasować plik i utworzyć go na nowo z nazwą w formacie 8.3.</p>
<p>Druga uwaga jest taka &#8211; z oczywistych względów (FISI :) ) nie będziemy się przejmować katalogami i zakładamy że wszystkie pliki są w głównym katalogu. Może w późniejszych wersjach kodu dodam obsługę trochę bardziej skomplikowanych struktur.</p>
<p>OK, wracamy do kodu <code>fetchSD</code>. Skoro udało się nam otworzyć plik, to szukamy kropki w nazwie pliku i jeżeli ją znajdziemy to sprawdzamy czy pozostała część (w domyśle &#8211; rozszerzenie) będzie pasować do znanych nam rozszerzeń. Bo nie wystarczy nam wysłać danych do klienta HTTP &#8211; musimy wysłać nagłówek z informacją o właściwym <em>Content-Type</em> (mówiliśmy o tym już wcześniej), inaczej dane mogą zostać błędnie zinterpretowane przez przeglądarkę.</p>
<p>Słowo o tym jak wygląda odpowiedź serwera WWW. Podzielona ona jest na dwie części. Pierwsza to tak zwane nagłówki. Przeglądarka jako nagłówek traktuje wszystko to co na początku, aż natrafi na pustą linię tekstu (linie są oddzielane znakami CR LF). Reszta to właściwa odpowiedź. Jak co ona zostanie zinterpretowana, będzie zależało od nagłówków. Serwer może pomóc przeglądarce przez ustawienie nagłówka określającego typ danych:</p>
<pre>Content-type: text/html</pre>
<p>Do pierwszego dwukropka jest nazwa nagłówka (tutaj <em>Content-Type</em>) potem wartość nagłówka. Tutaj używane są tak zwane typy <a title="Typy MIME" href="http://pl.wikipedia.org/wiki/MIME">MIME</a>. I tak może to być np <code>image/png</code> dla obrazka PNG, <code>image/jpg</code> dla JPG czy <code>text/html</code> dla pliku HTML.</p>
<p>Obowiązkowym nagłówkiem jest status &#8211; czyli czy żądanie klienta zostało obsłużone, czy wystąpił błąd a może przekierowanie. <code>HTTP/1.0 200 OK</code> znaczy &#8211; jest dobrze, będzie żądna treść. Najpierw jest protokół i jego wersja (<code>HTTP</code> w wersji 1.0) a następnie sam kod 200 &#8211; w świecie HTTP znaczy to że jest dobrze. Inne częste kody to 404 &#8211; nie znaleziono zasobu (sławne <em>Not Found</em>), 301 i 302 &#8211; przekierowania.</p>
<p>Wiedząc to, staramy się rozpoznać rozszerzenie pliku i wysłać odpowiedni nagłówek:</p>
<pre>	<span style="color: #cc6600;">if</span> (dot_index = strstr(urltail, <span style="color: #006699;">"."</span>)) {
		++dot_index;
		server.printP(OK);
		server.printP(CT);
		<span style="color: #cc6600;">if</span> (!strcmp(dot_index, <span style="color: #006699;">"htm"</span>)) {
				server.printP(CT_HTML);

		} <span style="color: #cc6600;">else</span> <span style="color: #cc6600;">if</span> (!strcmp(dot_index, <span style="color: #006699;">"css"</span>)) {
				server.printP(CT_CSS);

		} <span style="color: #cc6600;">else</span> <span style="color: #cc6600;">if</span> (!strcmp(dot_index, <span style="color: #006699;">"jpg"</span>)) {
				server.printP(CT_JPG);

		} <span style="color: #cc6600;">else</span> {
				server.printP(CT_PLAIN);
		}
		server.<span style="color: #cc6600;">print</span>(CRLF);</pre>
<p>Funkcje z dużym P na końcu oczekują nie <code>char *</code> ale <code>const prog_uchar *</code>.</p>
<p>Mamy już wysłany nagłówek HTTP (zakończony pustą linią <code>server.print(CRLF)</code>), więc wyślemy same dane:</p>
<pre>	readed = file.<span style="color: #cc6600;">read</span>(buf,30);
	<span style="color: #cc6600;">while</span>( readed &gt; 0) {
		buf[readed] = 0;
		bufferedSend(server,buf,readed);
		readed = file.<span style="color: #cc6600;">read</span>(buf,30);
	}
	flushBuffer(server);
	file.<span style="color: #cc6600;">close</span>();</pre>
<p>Czytamy po 30 bajtów, wysyłamy do klienta przez funkcję buforującą wysyłane dane. Po co? Otóż jeżeli użyjemy najprostszego rozwiązania i będziemy wysyłać dane znak po znaku, wówczas każdy znak będzie w odrębnym pakiecie TCP. Bardzo (naprawdę, uwierz, naprawdę) nieefektywne rozwiązanie. Po prostu <code>server.write</code> wysyła od razu dane.</p>
<p>Dlatego napisałem funkcję <code>bufferedSend</code>, która jako argumenty bierze obiekt serwera WWW, wskaźnik na bufor z danymi i rozmiar bufora. Czemu nie korzystamy z funkcji określających rozmiar bufora znakowego takich jak <code>strlen</code>? Bo działać to może tylko gdy dane są tekstowe. Jeżeli dane są binarne (obrazki) to znacznik końca łańcucha może pojawić się w <em>legalnym</em> strumieniu danych.</p>
<p>W C i C++ znakiem końca łańcucha jest znak 0 (nie cyfra, tylko bajt o wartości 0). Jeżeli w naszym strumieniu danych mogą pojawić się zera, wszystkie funkcje związane z łańcuchami znakowymi, a oferowane przez standardową bibliotekę nie przydadzą się nam</p>
<p>Z tego powodu musimy wprost określić ilość danych wysyłanych do bufora.</p>
<p>I w zasadzie to tyle. Mamy na Arduino serwer WWW wysyłający dane z karty SD.</p>
<h2>Czy to ma sens?</h2>
<p>Wystarczy kilka testów z bardziej złożoną stroną WWW (nie jeden plik HTML ale do tego jakieś CSS i obrazki), żeby przekonać się, że rozwiązanie to ma swoje ograniczenia. Arduino jest jednowątkowe, więc każdy element z naszego serwera WWW jest ściągany po kolei. Oznacza to, że z punktu widzenia użytkownika <em>strona się wolno ładuje</em>.</p>
<p>Więc po co to? Arduino może prezentować dane zbierane z czujników w przyjaźniejszej formie jeżeli nie będzie ograniczeniem ilość pamięci RAM potrzebnej bardziej rozbudowanej stronie WWW. Trzymając kod HTML na karcie SD pozbywamy się tego ograniczenia. Ale jak w HTMLu trzymanym na karcie SD umieścić dane zebrane z czujników przez Arduino?</p>
<p>Potrzebujemy czegoś, co pozwoli nam wstrzyknąć dane do HTMLa pomiędzy &#8216;odczytem&#8217; a &#8216;wysłaniem&#8217;. Czyli coś jak:</p>
<h1>PHP dla Arduino</h1>
<p>OK, to jest na wyrost :) potrzebujemy czegoś co bardziej przypomina szablony niż <em>pełne</em> PHP, ale na początku PHP też nie powalało funkcjonalnością :)</p>
<p>O tym jak zrobić taki parser (i pełny kod szkicu) &#8211; <a href="http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-2/">w następnym odcinku</a>. Stay tuned.</p>
]]></content:encoded>
			<wfw:commentRss>http://starter-kit.nettigo.pl/2011/05/p4a-php-dla-arduino-cz-1/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

