LinkedIn YouTube Facebook
Szukaj

Newsletter

Proszę czekać.

Dziękujemy za zgłoszenie!

Wstecz
SoM / SBC

ADuCino360: termometr cyfrowy na mikrokontrolerze ADuCM360 (Cortex-M3 z Analog Devices)

Obsługa urządzeń peryferyjnych

Program został napisany w języku C i uruchomiony w środowisku Vision4 Keila. Darmowa wersja ewaluacyjna tego środowiska umożliwia tworzenie kodu o wielkości do 32 kB, co z dużym zapasem wystarczyło dla naszych potrzeb. Vision4 zawiera wszystkie niezbędne do pracy elementy, takie jak: edytor, kompilator, linker, debugger.

Urządzenia peryferyjne mikrokontrolera ADuCM360 mogą być obsługiwane w systemie przerwań lub odpytywań (polling). Decyzja o wyborze metody należy do konstruktora/programisty i wynika zwykle z analizy rygorów czasowych. Warto zauważyć, że w urządzeniach, w których poszczególne zadania są wykonywane sekwencyjnie, jedno po drugim, system przerwań może być wolniejszy niż praca z odpytywaniem. Nie ma bowiem uzasadnionej potrzeby, żeby przerywać program, wywoływać funkcję obsługi przerwania, tylko po to, by odczytać rejestr statusu urządzenia i ustawić odpowiednią flagę. Tę samą czynność można wykonać bezpośrednio w programie, nie ma bowiem zagrożenia, że w tym czasie nastąpi konieczność obsługi innego zadania. Programiści piszący w językach wysokiego poziomu mogą zapomnieć, że każde wywołanie funkcji obsługi przerwania wiąże się z odkładaniem na stosie choćby rejestrów procesora, a często także innych parametrów, i zajmuje określony czas. Pisząc np. w języku C operacje te nie są bowiem widoczne, gdyż są wykonywane w tle.

 

Interfejs I2C

Z takim przypadkiem mamy do czynienia przy obsłudze czujnika temperatury, który jest odczytywany z dużymi, jak dla procesora odstępami czasu. Z tego powodu przyjęto metodę odpytywań. Fragment programu odczytujący dane z układu ADT7420 przedstawiono na listingu 2. Mikrokontroler pełni funkcję Master, układ ADT7420 jest Slave’em.

 

List. 2. Obsługa czujnika temperatury ADT7420

  
       signed short int czytajADT7420(unsigned char rejestr,unsigned char liczodb)
{
 unsigned char bufodbADT7420[2]={0,0};
 signed short int odczyt;

 I2cFifoFlush(MASTER,ENABLE);
 I2cFifoFlush(MASTER,DISABLE);
 I2cTx(MASTER,rejestr);                  //wyslij pierwszy bajt
 I2cMWrCfg(adrADT7420);
 while(!(I2cSta(MASTER)&I2CMSTA_TXREQ)); //czekaj na moment, w ktorym mozna wygenerowac restart
 I2cMRdCfg(adrADT7420,liczodb,DISABLE);
 for(;liczodb!=0;liczodb--)
 {
  while(!(I2cSta(MASTER)&I2CMSTA_RXREQ));
  bufodbADT7420[liczodb-1]=I2cRx(MASTER);
 }
 odczyt=256*bufodbADT7420[1]+bufodbADT7420[0]; //formatowanie wyniku patrz nota ukladu ADT7420
 return odczyt;
}

W programie wykorzystano funkcje z biblioteki I2cLib udostępnianej w środowisku Vision w wersji dla mikrokontrolera ADuCM360. Dane odbierane z interfejsu są zapisywane w kolejce FIFO, która jest czyszczona zawsze przed odczytem danych (instrukcje I2cFifoFlush). Z obsługą interfejsu wiąże się charakterystyczny problem generowania powtórnego bitu startu po wysłaniu adresu obsługiwanego urządzenia. Algorytm opisujący kolejność operacji związanych z obsługą transmisji interfejsem I2C jest następujący:

1. Wygeneruj sekwencję START (SDA=0 -> SCL=0)

2. Wyślij adres do zapisu urządzenia (bit R/W=0), czyli 0x90

3. Wyślij adres czytanego rejestru (0x00 – rejestr temperatury)

4. Wygeneruj ponownie sekwencję START

5. Wyślij adres do odczytu urządzenia (bit R/W=1), czyli 0x91

6. Odczytaj pierwszy rejestr temperatury

7. Odczytaj drugi rejestr temperatury. Po ostatnim bicie nie generuj potwierdzenia (NACK), co dla urządzenia Slave oznacza zakończenie transmisji.

8. Wygeneruj sekwencję STOP (SCL=1 -> SDA=1)

Przebiegi związane z transmisją przedstawiono na rysunku 4. Są one generowane automatycznie przez wewnętrzny blok mikrokontrolera obsługujący interfejs. Należy tylko pilnować momentu, w którym należy ponowne wygenerować sekwencję START. Następuje to po wykonaniu instrukcji I2cMRdCfg(adrADT7420,liczodb,DISABLE); zaznaczonej kolorem czerwonym na listingu 2. Ponowna sekwencja startu może być wygenerowana, gdy flaga TXREQ zostanie ustawiona. Jest to badane w pętli while(!(I2cSta(MASTER)&I2CMSTA_TXREQ)). Zbyt późne wykonanie następnej instrukcji spowoduje automatyczne wygenerowanie sekwencji STOP, podczas gdy urządzenie Slave jest już w tym momencie gotowe do odczytu (lub w ogólnym przypadku także zapisu). Odczyt następuje po ponownym przesłaniu adresu urządzenia (do odczytu). Master, czyli mikrokontroler, odczytuje dwa kolejne rejestry (00H i 01H), w których jest zakodowana zmierzona temperatura. Po odebraniu ostatniego bitu Master nie wystawia potwierdzenia sygnalizując, że nie jest zainteresowany dalszym odbieraniem danych, a więc następuje zakończenie transmisji. W programie głównym należy jeszcze odpowiednio sformatować zwracaną przez funkcję czytajADT7420 temperaturę, gdyż dana ta zapisana w systemie U2 (uzupełnień do 2) jest zakodowana na bitach 15…3 rejestru 00H i 01H.

 

Rys. 4. Przebiegi na liniach interfejsu I2C

Rys. 4. Przebiegi na liniach interfejsu I2C

 

Fragment programu realizujący interpretację wyniku oraz modyfikujący wskazanie wyświetlacza cyfrowego i uaktualniającego położenie „słupka” na termometrze analogowym przedstawiono na listingu 3.

 

List. 3. Interpretacja odczytu temperatury i wizualizacja danych na wskaźniku cyfrowym i analogowym

 pomiar=czytajADT7420(TEMP_H,2);            //czytaj temperature
 if(pomiar<0)
 {
  pomiar=pomiar>>3;                         //po pomiarze wazne sa bity 15..4 
  pomiar&=0x1fff;                           //ale liczbe trzeba dosunac do pozycji LSB
  temperatura=(float)(pomiar-8192)/16;      //obliczenie temperatury wg. algorytmu z pdf-a
 }
 else
 {
  pomiar=pomiar>>3;                         //po pomiarze wazne sa bity 15..4 
  pomiar&=0x1fff;                           //ale liczbe trzeba dosunac do pozycji LSB
  temperatura=(float)pomiar/16;
 }
 poziom=temperatura/rozdzielczosc[zakres]+offset_skali[zakres];  //poziom paska na termometrze
                                           //rozdzielczosc termometru = 1.25 lub 0.3125 st/dz
 sprintf(wynik,"%f",temperatura);          //konwersja float -> int 
 wynik[5]=0;                               //ograniczenie stringu do 5 znakow (0 - koniec stringu)
 LcdProstokat(62,49,84,130,FILL,WHITE);
 LcdString(wynik,62,49,DIGIT7,RED,YELLOW); //wyswietl aktualna temperature na LCD
 
 for(wiersz=25;wiersz<127;wiersz++)        //wyswietl slupek termometru analogowego
 {
  if(wiersz>poziom)
  {
   LcdLinia(wiersz,9,wiersz,21,WHITE);
  }
  else  
  {
   if(wiersz>104)
   {
    LcdLinia(wiersz,9,wiersz,21,RED);
   }
   else
   {
    if(wiersz>81)
    {
     LcdLinia(wiersz,9,wiersz,21,ORANGE);
    }
    else
    {
     if(wiersz>61)
     {
      LcdLinia(wiersz,9,wiersz,21,YELLOW);
     }
     else
     {
      if(wiersz>41)
      {
       LcdLinia(wiersz,9,wiersz,21,GREEN);
       

 

W programie zastosowano wyrażenie obliczeniowe podane w nocie katalogowej układu ADT7420 dla 13-bitowej rozdzielczości pomiaru. Temperatura jest zapamiętywana jako liczba zmiennoprzecinkowa w zmiennej temperatura . Następnie jest obliczany odpowiadający jej poziom „słupka” w termometrze analogowym (zmienna poziom ). Następuje też konwersja liczby zmiennoprzecinkowej na stałoprzecinkową, co jest wymagane do wyświetlenia wyniku na emulowanym wyświetlaczu 7-segmentowym. Konwersję z float na int realizuje instrukcja sprintf . Pętla for umieszczona w dalszej części programu modyfikuje wskazanie termometru analogowego.

W programie przewidziano dwa zakresy pracy termometru analogowego: od ok. 0?C do około 30?C oraz od ok. –20?C do ok. 100?C. Zmiana zakresu następuje po naciśnięciu przycisku P2 znajdującego się na płytce ewaluacyjnej. Jego stan jest sprawdzany po każdym cyklu pomiarowym, czyli co ok. 200 ms, należy więc odpowiednio długo go przytrzymać.

 

Port SPI

Można odnieść wrażenie, że konstruktorzy mikrokontrolera ADuCM360 trochę przedobrzyli kwestie związane z obsługą interfejsów. Uwzględniono kolejkowanie danych z interfejsów komunikacyjnych, przewidziano szereg zdarzeń generujących przerwania oraz różne tryby pracy poszczególnych portów. Opanowanie wszystkich możliwości może być jednak dla programisty dość trudne, tym bardziej, że w dużej części zastosowań obsługę peryferiów można by było zrealizować dużo prościej, czego dowodem są starsze generacje mikrokontrolerów. Tymczasem zupełnie pominięto możliwość pracy interfejsów z ramkami innymi niż 8-bitowe, a jak się okazuje liczba urządzeń wymagających takiej konfiguracji interfejsu nie jest taka mała, jak mogłoby się wydawać. Przykładem jest choćby zastosowany moduł wyświetlacza KAmodTFT2, którego sterownik wymaga ramki 9-bitowej. Niestety, mimo wielu prób nie udało się skonfigurować interfejsu SPI mikrokontrolera do pracy z taką ramką, konieczna więc była programowa emulacja. Odbiło się to jednak znacznie na szybkości pracy, co w przypadku wyświetlacza ma dość duże znaczenie.

Funkcje obsługi interfejsu SPI zapisano w pliku spi.c . Zawierają one makra zmieniające stany poszczególnych linii interfejsu, wysyłające dane i komendy do wyświetlacza. Z kolei funkcje związane z obsługą wyświetlacza zapisano w pliku lcd.c. Realizują one m.in. inicjowanie sterownika wyświetlacza, rysują figury geometryczne, wyświetlają napisy (pojedyncze znaki i łańcuchy tekstowe). Część funkcji obsługujących wyświetlacz powstała na bazie zaczerpniętych z Internetu gotowych programów autorstwa Jamesa P. Lyncha (http://www.elecfreaks.com/store/download/datasheet/shield/6100_Display_Driver.pdf ). Wykorzystano z nich na przykład generator znaków FONT6x8 wraz z jego obsługą. Do programu dodano natomiast generator znaków emulujący wyświetlacz 7-segmentowy (FONT_7DIGIT). Poszczególne segmenty są rysowane przez funkcje rysujące wypełnione prostokąty LcdProstokat .

 

Uwagi i spostrzeżenia

Zestaw ewaluacyjny z definicji ma służyć do przedstawienia określonego produktu i możliwości wykonania na nim różnych prób i eksperymentów. W opisywanym przykładzie mamy do czynienia z płytką przeznaczoną do prób z mikrokontrolerem ADuCM360. Aby było to możliwe bez angażowania dużej liczby elementów zewnętrznych przewidziano na niej montaż czujnika temperatury i 3-osiowego akcelerometru. W opisywanym przykładzie ograniczono się do pomiaru temperatury za pomocą czujnika ADT7420. Jak już wiemy jest to układ charakteryzujący się bardzo dużą rozdzielczością, umożliwiający pomiar z dużą dokładnością. Nie bez znaczenia jest jednak spora bezwładność termiczna układu płytka-czujnik. Wyraźnie zauważalny jest czas reakcji czujnika po skokowej zmianie warunków temperaturowych.

Program uruchomiono z zastosowaniem debugera/programatora sprzętowego J-LINK lite. Na płytce ewaluacyjnej jest umieszczone gniazdo SWD przeznaczone do tego celu. Rozwiązanie takie bardzo ułatwia pracę przy usuwaniu błędów programu, i jest godne polecenia, tym bardziej, że cena narzędzia jest akceptowalna.

Jarosław Doliński jest absolwentem Wydziału Elektroniki na Politechnice Warszawskiej. Pracował w Przemysłowymi Instytucie Telekomunikacji oraz Instytucie Fizyki Plazmy i Laserowej Mikrosyntezy, gdzie zajmował się konstruowaniem urządzeń transmisji danych. Współpracował z Zakładem Urządzeń Teatralnych m.in. w zakresie konstrukcji interkomów teatralnych i urządzeń dla inspicjentów. Brał także udział w pracach projektowych rejestratorów urządzeń wiertniczych i elektroniki montowanej na żurawiach mobilnych. Obecnie prowadzi firmę zajmująca się konstruowaniem i produkcją urządzeń elektronicznych dla rehabilitacji i wspomagania treningu sportowego. Jest autorem czterech książek poświęconych elektronice i mikrokontrolerom, współpracuje ponadto z miesięcznikami „Elektronika Praktyczna”, „Elektronik” oraz „Świat Radio”.