wtorek, 10 maja 2016

Nowy projekt w Atmel Studio

Jakieś 2 miesiące temu pomyślałem, że obejdzie się bez sięgania do podstaw. Dzisiaj pomyślałem - a co, jeśli ktoś przeczyta moje wpisy, zapała nieograniczoną chęcią stworzenia własnego małego urządzenia i cały Jego zapał pryśnie, gdy w Atmel Studio po utworzeniu projektu dostanie w twarz takim oto okienkiem:


By uniknąć zaprzepaszczonych szans - dziś specjalnie dla Ciebie, początkujący czytelniku kilka słów na temat konfiguracji programatora AVR Dragon w Atmel Studio.

Zatem od początku. Naszym celem jest zaprogramowanie mikrokontrolera Atmega 162 skompilowanym kodem pochodzącym z pliku main.c. Zakładam, że Dragon jest podpięty przewodem USB do komputera i taśmą z wyprowadzeniami JTAG do mikrokontrolera.

Pierwszy krok - tworzymy projekt. Dla pierwszego w solucji:


Dla każdego następnego prawy przycisk myszy na solucji i:


Po wybraniu opcji (New) Project pojawi nam się okno:


(gdzie dla pierwszego projektu będziemy mieli dodatkowo możliwość nazwania naszej nowej solucji).
W otwartym okienku wybieramy typ projektu - Executable project - możemy wybrać język c lub c++, podajemy nazwę i klikamy OK. Następnie zostaniemy zapytani o docelowy mikrokontroler:


Możemy użyć szukajki z prawego górnego rogu i dzięki niej znaleźć interesujący nas model mikrokontrolera. Zaznaczamy i OK. Atmel Studio utworzy plik main.c (jeżeli wybraliśmy język C) i dorzuci tam kawałek boiler plate'a z informacją o autorze, a w funkcji main umieści nieskończoną pętlę. Zestaw w sam raz na dobry początek. Wgrajmy ten skomplikowany kodzik do mikrokontrolera! Na początek - upewnijmy się, że wybrany przez nas projekt jest domyślnym (startowym) w solucji. Prawy klawisz na nazwę projektu i do dzieła:


Domyślny projekt jest zaznaczony pogrubioną czcionką. Następnie klikając w:


Uruchamiamy procedurę budująco-programującą. I tu zonk:


Konfigurując projekt nie wybraliśmy domyślnego programatora. Możemy to zrobić teraz. Continue. Szczęśliwie AS zostawia nas w miejscu, gdzie powinniśmy skonfigurować programator. Z tajemniczego dropdowna wybieramy naszego Dragona z interfacem JTAG:


Naszym oczom ukaże się multum opcji do wyboru. Ustawiamy jeszcze JTAG clock. Częstotliwość nie powinna być większa niż 1/5 częstotliwości taktowania mikrokontrolera. W naszym przypadku 8 MHz, zatem nie przekraczamy 1.6 MHzm czyli 1 MHz jest jak najbardziej poprawną wartością. Zapisujemy zmiany (Ctrl+S albo dyskietka z menu) i próbujemy jeszcze raz.


Nasz skompilowany kodzik trafia wprost do naszej Atmegi, o czym zostaniemy poinformowani w okienku Output dla Build:



Z ciekawych rzeczy - dowiemy się ile pamięci programu i ile pamięci danych zajął nasz kodzik w mikrokontrolerze (zaznaczony fragment). Możemy zrobić pierwszego commita i naduszać dalej z kodzikiem:)

piątek, 6 maja 2016

Fun with Fan part 4

Teoria już za nami - czas na kodzik!

Na dobry początek rozważmy rozwiązanie wykorzystujące mechanizm przerwań i timer. Sygnał tacho podepniemy pod zewnętrzne przerwanie INT2 (PD3 w mojej Atmedze), a timer skonfigurujemy tak, by generował przerwanie co 1 sekundę. W obsłudze przerwania INT2 będziemy zliczać impulsy, a co sekundę, dzięki timerowi policzymy, ile obrotów wykonał nasz wentylator. Wiemy, że na pełen obrót składają się 2 impulsy, dlatego sumę impulsów podzielimy przez 2.

Żeby mieć jako takie pojęcie o tym, co się dzieje na pokładzie mikrokontrolera, wyślemy ilość obrotów na minutę USARTem przez bluetooth'a wprost do aplikacji terminala odpalonej na telefonie z Androidem. Informację o prędkości będziemy wysyłać również co sekundę. Skąd będziemy wiedzieć kiedy dokładnie? Ustawimy flagę send w obsłudze przerwania!

Do dzieła!


volatile uint8_t command;
volatile uint8_t fanSpeed;
volatile uint8_t send = 0;
volatile uint16_t roundsCounter = 0;
volatile uint16_t roundsPerSecond = 0;
volatile uint16_t roundsPerMinute = 0;
int main(void)
{
uint8_t maxFanSpeed = 255;
uint8_t minFanSpeed = 80;
fanSpeed = minFanSpeed;
double minTemperature = 23.5;
double maxTemperature = 24.6875;
DDRB |= (1 << PB1);
// set pd3 (INT1) as input
DDRD &= ~(1 << PD3);
// pull-up for pd2
PORTD |= (1 << PD3);
// Fast PWM mode
TCCR0 |= (1 << WGM01) | (1 << WGM00);
// OC0 enabled, clear on match
TCCR0 |= (1 << COM01) | (1 << COM00);
// timer0 clock source prescaler
TCCR0 |= (1 << CS00);
// pin OC0 as output
DDRB |= (1 << PB0);
STRONG_PULL_UP_DDR |= (1 << STRONG_PULL_UP_PIN);
// enable INT1 interrupt
GICR |= (1 << INT1);
TCCR1B |= (1 << WGM12) | (1 << CS12);
OCR1A = 32500;
TIMSK|=(1<<OCIE1A); //Output compare 1A interrupt enable
USART0Init();
// initialize interrupts
sei();
while(1)
{
PORTC &= ~(1 << 1);
ReadTemperature();
if (temperature < minTemperature && fanSpeed > minFanSpeed)
{
fanSpeed -= 1;
}
else if (temperature > maxTemperature && fanSpeed < maxFanSpeed - 20)
{
fanSpeed += 1;
}
OCR0 = 255 - fanSpeed;
if (send)
{
dtostrf(roundsCounter, 0,0, str);
UsartWrite(str);
UsartWrite("\r\n");
send = 0;
}
}
return 0;
}
ISR(INT1_vect)
{
roundsCounter++;
}
ISR(TIMER1_COMPA_vect)
{
DDRB ^= (1 << PB1);
roundsPerSecond = roundsCounter / 2;
roundsPerMinute = roundsPerSecond * 60;
roundsCounter = 0;
send = 1;
}
// onUsart
ISR(USART0_RXC_vect)
{
command = UDR0;
}
view raw rpm-interrupt.c hosted with ❤ by GitHub

środa, 4 maja 2016

Fun with Fan part 3

Gospodarowanie dostępnymi zasobami

Trzeba dążyć do tego, by wykorzystać jak najwięcej z tego, co mają nam do zaoferowania producenci sprzętu, który wpadnie nam w ręce. Maglowany przeze mnie wiatraczek a.k.a. wentylator oprócz dwóch linii zasilających (czerwonej + i czarnej -) posiada jeszcze linię oznaczoną kolorem żółtym. Szybki rzut oka w Internety wystarcza, by dowiedzieć się, że jest to wyjście tacho. Linia ta jest częścią sprzężenia zwrotnego dla naszego proca - zwiększając napięcie zasilania spodziewa się, że wentylator będzie kręcił się coraz szybciej. W myśl zasady - kontrola najwyższą formą zaufania - za pomocą żółtej linii jest w stanie określić prędkość, z jaką obraca się wirnik wentylatora i w zależności od sytuacji odpalać takie albo inne eventy. Np. gdy wie, że prędkość jest niska, a temperatura wzrosła - może podnieść nieco napięcie zasilania - spodziewając się wzrostu prędkości obrotowej. Gdy ten wzrost nie nastąpi - nasz procesor może zacząć bić na alarm - bo coś złego dzieje się z tak ważnym elementem każdego komputera - chłodzeniem (PC zaczyna piszczeć tym swoim głośniczkiem...).

Pomysł na mikro-projekt

Co jako było nie było programista Atmegi mogę zrobić z tym fantem? Ano podpiąć dodatkową linię do mikrokontrolera i wykorzystać jakoś te informacje. Co mnie interesuje? Interesuje mnie stan tej linii w czasie. Pierwsza myśl - podłączę tacho do dowolnej nóżki mikrokontrolera i będę sobie sprawdzał jej stan. Dobre, ale są lepsze rozwiązania. Przerwanie! Przecież mogę podpiąć ten sygnał pod przerwanie - główna pętla programu już nie będzie musiała sprawdzać stanu nóżki. OK - do tego jeszcze potrzebny nam jest jakiś pomiar czasu. Z żółtej linii odczytamy ilość wykonanych przez wirnik obrotów - do zmierzenia prędkości potrzebny będzie też czas. Zapniemy jakiś zegarek? Stoper? Blisko - wykorzystamy Timer! Lepiej! Wykorzystamy mechanizm przechwytywania sygnału wejściowego (Input Capture Unit).

Zejdźmy na poziom sygnałów

Przechwytywany sygnał musimy podpiąć do jednego z dwóch (w przypadku Atmegi 162) wejść ICP. Dla niepoznaki są to ICP1 na pinie PE0 i ICP3 na pinie PD3. Pin PD3 to także wejście zewnętrznego przerwania INT1 - warto o tym pamiętać wybierając miejsce podłączenia sygnału tacho - chcąc wypróbować podejście z przerwaniem warto skorzystać z pinu PD3 - nie trzeba będzie zmieniać konfiguracji połączenia przy zmianie podejścia programowego - chcąc zaoszczędzić dobrą linię przerwania - warto wybrać port E.
Nie warto ryzykować - wentylator działa z napięciem 12V - Atmega 5V - sięgamy po transoptor!

Input Capture Unit

Jak zwykle w przypadku obcowania z zewnętrznymi sygnałami, musimy określić, na jaki stan nasza potężna jednostka ICU ma reagować. W momencie, gdy na wejściu ICPn pojawi się oczekiwany przez nas stan, nastąpi przechwycenie! W momencie przechwycenia do rejestru ICRn jest wpisywana aktualna wartość rejestru TCNTn wykorzystywanego licznika (krótko mówiąc timestamp). Warto tak skonfigurować mikrokontroler, by w tym momencie wystąpiło przerwanie - przecież musimy na bieżąco reagować! Mało tego - musimy reagować szybko, bo przy następnym oczekiwanym stanie logicznym interesujące nas rejestry zostaną nadpisane, a cenne dane utracone. W tym miejscu warto jeszcze wspomnieć o istnieniu mechanizmu redukowania szumów (Noise Canceler), który może usprawnić nasze pomiary, gdy na linii występują zakłócenia.

I z tą dozą teorii zostawiam Cię Czytelniku, w niedalekiej przyszłości pojawi się adekwatny kodzik!