<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pl-pl">
<link rel="self" type="application/atom+xml" href="https://forum.atnel.pl/feed.php?f=23&amp;t=2527&amp;mode" />

<title>ATNEL tech-forum</title>
<link href="https://forum.atnel.pl/index.php" />
<updated>2013-03-09T21:23:50+01:00</updated>

<author><name><![CDATA[ATNEL tech-forum]]></name></author>
<id>https://forum.atnel.pl/feed.php?f=23&amp;t=2527&amp;mode</id>
<entry>
<author><name><![CDATA[barney]]></name></author>
<updated>2013-03-09T21:23:50+01:00</updated>
<published>2013-03-09T21:23:50+01:00</published>
<id>https://forum.atnel.pl/viewtopic.php?t=2527&amp;p=30202#p30202</id>
<link href="https://forum.atnel.pl/viewtopic.php?t=2527&amp;p=30202#p30202"/>
<title type="html"><![CDATA[bLEDd13 - latarkowy driver programowalny na ATtiny13]]></title>

<content type="html" xml:base="https://forum.atnel.pl/viewtopic.php?t=2527&amp;p=30202#p30202"><![CDATA[
Po brutalnym wyciągnięciu do tablicy nie mam wyboru - muszę to cholerstwo opisać <img src="https://forum.atnel.pl/images/smilies/icon_lol.gif" alt=":lol:" title="Śmieje się" /> .<br /><br />Projekt ten to oprogramowanie do latarki PowerLED (kilka ładnych W w diodę, ogniwa przemysłowe 18650, czasami na rowerku mylą mnie ze skuterem/samochodem <img src="https://forum.atnel.pl/images/smilies/icon_twisted.gif" alt=":twisted:" title="Szalony" /> ).<br />Aby móc zapanować nad tą jasnością potrzebne jest jakieś sterowanie, bo po poświeceniu taką latareczką ustawioną na 100% w zegarek przez kilka minut widzielibyśmy tylko jasną plamę <img src="https://forum.atnel.pl/images/smilies/icon_cool.gif" alt="8-)" title="Cool" /> .<br />Stosuję się w tym celu zazwyczaj stabilizatory liniowe AMC7135 (do ustalania prądu maksymalnego). Dalsza regulacja jasności odbywa się poprzez odpowiedni PWM (jeżeli jest wystarczająco szybki to oko nie zauważy migotania diody).<br />Typowy chiński driver: <!-- m --><a class="postlink" href="http://www.swiatelka.pl/upload_img/obrazki/IMG_4b42557f0f85d6012.jpg" >http://www.swiatelka.pl/upload_img/obra ... 5d6012.jpg</a><!-- m --><br /><br />Jak widać jest (prawie) wszystko co potrzebne do szczęścia - element(y) wykonawczy i dzielnik do kontroli napięcia na ogniwie (chemia Li.... czyli najmniej ~2.5V pod obciążeniem co w porównaniu z napięciem &quot;diodki&quot; - 3V wygląda OK, ale potrzebujemy jeszcze przecież wcześniejszego ostrzegania o kończącym się ogniwie).<br /><br />Dlaczego prawie? Niektórzy pewnie oburzą się, gdzie tutaj jest podłączony przycisk do podawania sygnałów na procka (zmiana trybów, itp.). Otóż nie ma go <img src="https://forum.atnel.pl/images/smilies/icon_e_wink.gif" alt=";)" title="Puszcza oko" /> , sygnały podajemy faktycznie poprzez zwykły przycisk, ale odłączający zasilania od całego drivera (odcinający GND konkretnie).<br />Wymusza to oczywiście specyficzne podejście do odczytywania gestów/klików - korzystanie z EEPROMu tutaj to po prostu konieczność.<br />Żeby nie było za łatwo, możemy mierzyć tylko czas świecenia, a nie czas przerwy, więc robi się już ciekawie <img src="https://forum.atnel.pl/images/smilies/icon_e_smile.gif" alt=":)" title="Szczęśliwy" /><br /><br />No to mamy pierwsze założenia, problemy i schematy. O konkretnych opcjach/możliwościach tego projektu przeczytacie tutaj: <!-- m --><a class="postlink" href="http://www.swiatelka.pl/viewtopic.php?p=108868#108868" >http://www.swiatelka.pl/viewtopic.php?p=108868#108868</a><!-- m --> (nie będę kopiował, bo post będzie znacznie za długi). Zajmiemy się tym, co tam nie zostało zamieszczone - czyli przeanalizujemy main.c (link do pobrania w tamtym temacie, naprawdę polecam dokładnie przeanalizować opis programowania i opcji w setup.h, bo nie będziecie wiedzieć co się dzieje) <img src="https://forum.atnel.pl/images/smilies/icon_e_smile.gif" alt=":)" title="Szczęśliwy" /> <br />[syntax=c]<br />#include &lt;avr/io.h&gt;<br />#include &lt;util/delay.h&gt;<br />#include &lt;avr/eeprom.h&gt;<br />#include &lt;avr/interrupt.h&gt;<br />#include &quot;setup.h&quot;<br />#define AMC  (1&lt;&lt;PB1)<br />#define PWM  OCR0B<br />#define TRYB (cfg_ram.ktory_tryb&gt;&gt;1)<br />#define STP_SLT(SLT)(((SLOT##SLT##_STRB)&lt;&lt;3)+(SLOT##SLT##_LVL)-1)[/syntax]<br />Typowa &quot;gra wstępna&quot; <img src="https://forum.atnel.pl/images/smilies/icon_mrgreen.gif" alt=":mrgreen:" title="Pan Zielony" /> biblioteki do przerwań, delayów, i obsługi EEPROMu. Do tego 2 makra do rejestrów i portów. Mamy również 1 makro typu &quot;funkcja inline&quot; - to konkretnie wydobywa numer aktywnego slotu.<br />Ostatnie makro służy do wygodnego rozpisywania do macierzy wartości &quot;startowych&quot; ustawień slotów. (patrz Bluebook-4.6.4) Tutaj konkretnie wykorzystane do tworzenia nazw makr z których wartości dopiero będą pobierane.<br />[syntax=c]typedef uint8_t u08;<br />typedef struct<br />{<br />u08 jasnosci    [8];<br />u08 strb_time1  [3];<br />u08 strb_factor1[3];<br />u08 strb_time2     [3];<br />u08 strb_factor2   [3];<br />} TSET;<br />typedef struct<br />{<br />u08 ktory_tryb;<br />u08 ile_trybow;<br />u08 klikniecia;<br />u08 rower;<br />u08 sloty          [8];<br />u08 menu;<br />u08 menu_lvl[3];<br />} TCFG;<br />TSET set_epr EEMEM =<br />{<br />{LVL1, LVL2, LVL3, LVL4, LVL5, LVL6, LVL7, LVL8},<br />{STRB1_TIME1,      STRB2_TIME1,      STRB3_TIME1},<br />{STRB1_FACTOR1,    STRB2_FACTOR1,    STRB3_FACTOR1},<br />{STRB1_TIME2,      STRB2_TIME2,      STRB3_TIME2},<br />{STRB1_FACTOR2,    STRB2_FACTOR2,    STRB3_FACTOR2}<br />};<br />TCFG cfg_epr EEMEM =<br />{<br />(AKTUALNY_SLOT - 1)&lt;&lt;1,<br />SLOTY_WLACZONE - 1,<br />0,<br />TRYB_ROWEROWY,<br />{STP_SLT(1), STP_SLT(2), STP_SLT(3), STP_SLT(4), STP_SLT(5), STP_SLT(6), STP_SLT(7), STP_SLT(8)},<br />0,<br />{0, 0, 0}<br />};<br />TCFG cfg_ram;<br />TSET set_ram;<br />u08 czas, licznik, jasnosc_obecna, numer_stroboskopu;<br />volatile u08 wdt_epr=0;<br />[/syntax]<br />Naszej &quot;gry wstępnej&quot; ciąg dalszy. Tutaj jednak już coś konkretnego - tworzymy cały szkielet do przechowywania danych. Struktury typu TSET są praktycznie stałymi - nie zmieniają się nigdy w czasie &quot;życia&quot; programu - tylko podczas przeprogramowania. Dlaczego nie wpiszę ich bez użycia EEPROMu? Nie mam po prostu na to miejsca w kodzie <img src="https://forum.atnel.pl/images/smilies/icon_e_smile.gif" alt=":)" title="Szczęśliwy" /> Zużycie Flasha wynosi 1002/1024<br />W TSET mieszczą się:<br />- dostępne jasności<br />- ustawienia stroboskopów (czasy i dzielniki)<br />Teraz przechodzimy do do struktur typy TCFG - tutaj już mamy te wartości, które zmieniają się w trakcie korzystania z latarki.<br />Odpowiednio:<br />- który slot jest teraz włączony<br />- ile slotów jest aktywnych<br />- licznik kliknięć (do wchodzenia w programowanie i wychodzenia z blokady)<br />- stan blokady<br />- ustawienia slotów (3 najmłodsze bity zawierają przypisaną jasność, 2 następne stroboskopy)<br />- i 4 zmienne potrzebne do programowania (odpowiednio poziom menu w którym teraz jesteśmy i co wybraliśmy na danym poziomie).<br />Dlaczego 2 razy definiuje struktury tego samego typu? Bo jedna instancja jest w EEPROMie, a druga w RAMie <img src="https://forum.atnel.pl/images/smilies/icon_e_wink.gif" alt=";)" title="Puszcza oko" /> <br />Jak widać struktury ładowane do EEPROMu pobierają masę danych z Setup.h - dlatego ważne jest, abyście przeanalizowali opis na swiatelkach).<br />2 ostatnie linijki to już definicja kilku zmiennych pomocniczych oraz zmiennej do przekazywania wartości z przerwania do funkcji main (stąd to volatile).<br />[syntax=c]inline void odczytaj_epr (void)<br />{<br />eeprom_read_block(&amp;cfg_ram, &amp;cfg_epr, sizeof(cfg_ram));<br />eeprom_read_block(&amp;set_ram, &amp;set_epr, sizeof(set_epr));<br />cfg_ram.ile_trybow&amp;=0b00000111;// Proste poprawianie struktury na wypedek blednego zapisu EEPROMU<br />}<br />inline void zapisz_epr (void) {eeprom_write_block(&amp;cfg_ram, &amp;cfg_epr, sizeof(cfg_ram));}<br />inline void wyzeruj_menu (void)<br />{<br />cfg_ram.menu=0;// Wylaczenie programowania<br />cfg_ram.menu_lvl[1]=0;<br />cfg_ram.klikniecia=0;// Wyzerowanie licznika klikniec<br />}<br />inline void ignoruj_klik (void)<br />{<br />cfg_ram.ktory_tryb&amp;=0b11111110;<br />cfg_ram.klikniecia=0;<br />}[/syntax]<br />Teraz potrzebujemy kilku funkcji (wyrzucić trochę kodu, który nie odpowiada za logikę drivera), które zwiększą przejrzystość programu głównego (dlatego wszystkie są inline).<br />Mamy odpowiednio:<br />1. Sczytanie wartości zapisanych w EEPROMie (potrzebna co odcięcie zasilania od uP, a w ciągu dnia reset odbywa się z 50-200 razy u mnie  <img src="https://forum.atnel.pl/images/smilies/icon_e_wink.gif" alt=";)" title="Puszcza oko" /> <br />Jak widać zawiera również &quot;Proste poprawianie struktury na wypedek blednego zapisu EEPROMU&quot;. Wiadomo, można zrobić to lepiej - trzymać 2 instancje w EEPROMie, liczyć proste CRC, itd.. Ale nie ma na to po prostu miejsca w kodzie. Dlatego jest proste poprawiania, które powinno pozwolić na ręczne przywrócenie wszystkich ustawień bez wyciągania programatora oraz pobożne życzenie, aby EEPROM dobrze się zapisywał <img src="https://forum.atnel.pl/images/smilies/icon_e_biggrin.gif" alt=":D" title="Bardzo szczęśliwy" /> (jak na razie naklikałem się jak głupi podczas pisania tego drivera i w żadnym momencie wartości mi się nie posypały.<br />2. Zapis EEPROMu (tylko tej struktury, która się zmienia).<br />3. Wyłączenie zmiennych uruchamiających programowanie oraz wyzerowanie licznika kliknięć<br />4. Funkcję do obsługi &quot;zapamiętania trybu&quot;<br />[syntax=c]int main (void)<br />{<br />DDRB   |= AMC;                      // kierunek pinu AMC wyjsciowy<br />    TCCR0A |= (1&lt;&lt;WGM01) | (1&lt;&lt;WGM00) | (1&lt;&lt;COM0B1);// fast PWM, Clear OC0B on Compare Match<br />    TCCR0B |= (1&lt;&lt;CS00);         // prescaler 1<br /><br />odczytaj_epr();                            // Odczytanie zawartosci EEPROM[/syntax]<br />Jeżeli powyższy kod nie jest oczywisty, to tego poniżej nawet nie macie co czytać <img src="https://forum.atnel.pl/images/smilies/icon_twisted.gif" alt=":twisted:" title="Szalony" /> <br />Uwaga! Zaczynamy cuda i dziwy <img src="https://forum.atnel.pl/images/smilies/icon_mrgreen.gif" alt=":mrgreen:" title="Pan Zielony" /> <br />[syntax=c]/*A1*/if (!cfg_ram.rower)// Jezeli driver nie jest zablokowany<br />{<br />/*B1*/if ((cfg_ram.menu==3) || (cfg_ram.menu_lvl[1]&gt;3))// Rozpiska z programowania<br />{// Programowanie slotow<br />if (cfg_ram.menu_lvl[1]&lt;4) cfg_ram.sloty[cfg_ram.menu_lvl[0]]=(cfg_ram.menu_lvl[1]&lt;&lt;3) | cfg_ram.menu_lvl[2];<br />if (cfg_ram.menu_lvl[1]==4)// Programowanie slotu rowerowego<br />{<br />cfg_ram.rower=1;<br />cfg_ram.ktory_tryb=(cfg_ram.menu_lvl[0]&lt;&lt;1);<br />}<br />else<br />/*B2*/cfg_ram.ktory_tryb=0;// Ustawianie slotu pierwszego jako aktywny<br />if (cfg_ram.menu_lvl[1]==5) cfg_ram.ile_trybow=cfg_ram.menu_lvl[0];<br />wyzeruj_menu();// Programowanie ilosci aktywnych slotow<br />}<br />/*B3*/else<br />{// Obsluga klikniec<br />if (cfg_ram.ktory_tryb &amp; 0b00000001) cfg_ram.ktory_tryb+=2; else cfg_ram.ktory_tryb++;<br />if (TRYB&gt;cfg_ram.ile_trybow) cfg_ram.ktory_tryb=1;<br />}<br />if (cfg_ram.klikniecia==MENU_IN)// Programowanie<br />{<br />for (licznik=0; licznik&lt;MENU_SIG_RPT; licznik++)<br />{<br />PWM=MENU_LVL1;  _delay_ms (MENU_TIME1);<br />PWM=MENU_LVL2;_delay_ms (MENU_TIME2);<br />}<br />cfg_ram.menu++;<br />for (licznik=0; licznik&lt;MENU_CHOOSE_RPT; licznik++)<br />for (czas=0; czas&lt;8; czas++)<br />{<br />cfg_ram.menu_lvl[cfg_ram.menu-1]=czas;<br />zapisz_epr();<br />PWM=set_ram.jasnosci[czas];  _delay_ms(MENU_CHOOSE_TIME);<br />}<br />wyzeruj_menu();// Jezeli nic nie wybrano to wychodzimy<br />}<br />}<br />else<br />/*A2*/if (cfg_ram.klikniecia==ROWER_STOP)// Wyjscie z trybu rowerowego<br />{<br />cfg_ram.rower=0;<br />cfg_ram.klikniecia=0;<br />}<br />/*A3*/cfg_ram.klikniecia++;<br />zapisz_epr();// Zapis EEPROM[/syntax]<br />Duży fragment kodu, co? Jest to praktycznie cała logika drivera, tego nie da się praktycznie tłumaczyć fragmentami...<br />Dodatkowym utrudnieniem jest fakt, że zazwyczaj operujecie na zupełnie innym sterowaniu - wy odczytujecie stany przycisków i w oparciu o to coś robicie. Tutaj mamy tylko informację o włączeniach uP <img src="https://forum.atnel.pl/images/smilies/icon_rolleyes.gif" alt=":roll:" title="Udaje, że to nie on" /><br />Jak już napisałem - tego nie da się tłumaczyć fragmentami. Ale nie jesteśmy tutaj po to, żeby robić rzeczy wykonalne <img src="https://forum.atnel.pl/images/smilies/icon_lol.gif" alt=":lol:" title="Śmieje się" /> <br />Do kodu dodałem znaczniki /*.....*/ oznaczają one miejsce w kodzie, które omawiam.<br />A1 - Jeżeli włączona jest blokada drivera to praktycznie cała logika jest wyłączona/nie potrzebna.<br />A2 - Czekamy więc na ilość kliknięć (zwiększaną dzięki A3), która wyłączy tryb blokady<br />B1 - Rozpisujemy programowanie - fragment cfg_ram.menu_lvl[1]&gt;3 odpowiada za przedwczesne wyjście z programowania (kiedy w II etapie wybierzemy opcje wyższą niż 4 i wybieranie jasności (etap III) jest niepotrzebne.<br />B2 - jeżeli nie ustawiamy teraz blokady trybów, to po wyjściu z menu latarka zaświeci na pierwszym slocie<br />B3 - gdyby te 2 if-y nie były pod else-m to nie moglibyśmy ustawić trybu blokady dla trybu nie należącego do wybranego zakresu<br />Numeracje tutaj zaczynamy od 0, a nie jak w Setup.h (z powodów oczywistych <img src="https://forum.atnel.pl/images/smilies/icon_e_wink.gif" alt=";)" title="Puszcza oko" /> ).<br />[syntax=c]<br />ADMUX  |= (1&lt;&lt;REFS0)| (1&lt;&lt;MUX0)  | (1&lt;&lt;ADLAR);  // 1.1V, PB2, 8bit<br />ADCSRA |= (1&lt;&lt;ADEN) | (1&lt;&lt;ADPS2) | (1&lt;&lt;ADPS0)|(1&lt;&lt;ADSC);// Wlaczenie ADC, prescaler 32, wykonanie pomiaru<br />WDTCR  |= (1&lt;&lt;WDTIE)| (1&lt;&lt;WDP1)  | (1&lt;&lt;WDP0);// Watchdog - przerwanie, prescaler 8 -&gt; przerwanie co ~128ms<br />sei();// Odblokowanie przerwan[/syntax]<br />Obsłużyliśmy już logikę, teraz możemy rozpocząć <img src="https://forum.atnel.pl/images/smilies/icon_e_smile.gif" alt=":)" title="Szczęśliwy" /><br />Konfigurujemy więc sobie ADCka oraz Watchdoga (ATtiny13 ma tylko jeden timer, który musi być szybki i obsługiwać diodę, a my potrzebujemy jeszcze drugiego, wolnego do obsługi pewnych zdarzeń.<br />[syntax=c]licznik=0;// Ustalanie jasnosci i obecnosci stroboskopu<br />jasnosc_obecna=set_ram.jasnosci[cfg_ram.sloty[TRYB] &amp; 0b00000111];<br />numer_stroboskopu=cfg_ram.sloty[TRYB]&gt;&gt;3;<br />if (numer_stroboskopu)<br />{<br />numer_stroboskopu--;<br />licznik=1;<br />}[/syntax]<br />Na samym końcu wydobywamy to, po co ten sterownik został stworzony <img src="https://forum.atnel.pl/images/smilies/icon_mrgreen.gif" alt=":mrgreen:" title="Pan Zielony" /> , czyli jasności i stroboskopy.<br />Schemat jest następujący:<br />1. Najmłodszy bit cfg_ram.ktory_tryb odpowiada za ignorowanie kliknięcia, 3 następne przechowują właściwą informację o tym, który slot jest wybrany.<br />2. Komórka z informacjami o slocie zawiera 3bitową informację o jasności (3 najmłodsze bity, które wydobywamy ANDowaniem bitowym). (2 następne to informacja o stroboskopie).<br />3. Skoro mamy już numer poziomu jasności to możemy pobrać dla niego odpowiednią jasność.<br />[syntax=c]while (1)// Swiecenie<br />{<br />/*A*/if (wdt_epr)<br />{<br />wdt_epr=0;<br />ignoruj_klik();<br />zapisz_epr();<br />PWM=jasnosc_obecna/IGN_CLICK_FACTOR;  _delay_ms(IGN_CLICK_SIG);<br />}<br />/*B*/if (licznik)<br />{<br />for (czas=0; (czas&lt;set_ram.strb_time1[numer_stroboskopu]) &amp;&amp; (!wdt_epr); czas++) {PWM=jasnosc_obecna/set_ram.strb_factor1[numer_stroboskopu];  _delay_ms (STRB_TIME1);}<br />for (czas=0; (czas&lt;set_ram.strb_time2[numer_stroboskopu]) &amp;&amp; (!wdt_epr); czas++) {PWM=jasnosc_obecna/set_ram.strb_factor2[numer_stroboskopu];  _delay_ms (STRB_TIME2);}<br />}<br />else<br />PWM=jasnosc_obecna;<br />}[/syntax]<br />A - ten fragment odpowiada za ignorowanie kliknięcia/zapamiętanie trybu<br />B - jeżeli na slocie jest stroboskop to musimy wydobyć jeszcze jego czasy<br />[syntax=c]ISR (WDT_vect)<br />{<br />static u08 batt=0, ign=0;// Ignorowanie klikniecia<br />if (ign&lt;IGN_CLICK_TIME)if (++ign==IGN_CLICK_TIME) wdt_epr=1;<br />if ((++batt&gt;=BATT_PAUSE) &amp; (!wdt_epr))// Sprawdzanie napiecia baterii<br />{<br />batt=0;<br />if (ADCH&lt;BATT_LEVEL) {PWM=jasnosc_obecna/BATT_FACTOR;  _delay_ms(BATT_TIME);}<br />ADCSRA |= (1&lt;&lt;ADSC);// Podanie startu ADC<br />}<br />}[/syntax]<br />A w przerwaniu realizujemy sobie czasu do ignorowania kliknięcia oraz sygnalizację niskiego napięcia <img src="https://forum.atnel.pl/images/smilies/icon_e_smile.gif" alt=":)" title="Szczęśliwy" /> <br /><br />I dzięki tym wszystkim syzyfowym operacjom możemy zamrugać diodką trochę ciekawiej, niż w &quot;Bluebookowy&quot; sposób <img src="https://forum.atnel.pl/images/smilies/icon_lol.gif" alt=":lol:" title="Śmieje się" /><p>Statystyki: Napisane przez <a href="https://forum.atnel.pl/memberlist.php?mode=viewprofile&amp;u=973">barney</a> — 9 mar 2013, o 21:23</p><hr />
]]></content>
</entry>
</feed>