Powrót

Mikroprocesorowa ośla łączka, część 27

Dziedziczenie to jeden z najbardziej fundamentalnych elementów, jakie wnosi język C++. Jest to potężny mechanizm, który umożliwia tworzenie nowych klas (nazywanych klasami pochodnymi) na bazie klas już istniejących (nazywanych klasami bazowymi).

Mechanizm dziedziczenia pozwala programiście używać raz napisanego kodu w nowych zastosowaniach. Ułatwia to zarządzanie kodem programu oraz tworzenie przejrzystych, hierarchicznych struktur odzwierciedlających rzeczywiste zależności między obiektami. W ogólnym przypadku to bardzo szeroki mechanizm, który został stworzony do wspierania opracowywania oprogramowania dla komputerów. W świecie mikrokontrolerów, ze względu na znacząco ograniczone zasoby (przede wszystkim pamięć operacyjną), nie ma sensu zagłębiać się we wszystkie szczegóły. Niemniej pewne możliwości są przydatne w tworzeniu kodu dla mikrokontrolerów. Przykładem może być rozwiązanie sterowników, umożliwiające obsługę określonych podzespołów w systemach mikroprocesorowych. Ilustracją tej koncepcji będzie obsługa modułu 7-segmentowego wyświetlacza LED, bazującego na układzie scalonym MAX7219, opisanym w Mikroprocesorowej oślej łączce, cz. 9 (ZE 02/2025). Zawarte tam informacje są wystarczające, by szczegółowo poznać możliwości i obsługę modułu wyświetlacza z tym układem scalonym. Ten wybór jest podyktowany dosyć prostą obsługą sprzętową oraz dużymi możliwościami funkcjonalnymi (bez konieczności ponownego ich opisu, skupiając się na nowej koncepcji rozwiązania tego typu tematów, opierającej się na dziedziczeniu).

Komponent do obsługi wyświetlacza

Do obsługi wspomnianego modułu zostało utworzone kilka funkcji, których zadaniem jest realizacja odpowiednich operacji dotyczących wyświetlacza. Zadaniem jednych jest wyświetlanie danych (DisplayNumber, ClearDisplay), inne są przeznaczone do konfigurowania określonych cech (SetIntensity). Wszystkie te funkcje w jakiś sposób komunikują się ze sprzętem (realizują fizyczne manipulowanie pinami portów, gdzie przyłączony jest wyświetlacz). Możliwą definicję klasy pokazuje listing 1, gdzie między innymi występuje metoda SendControlToMAX7219:

typedef enum { BCDDecodeMode ,
               SegmentsMode  } MAX7219DecodeModeT ;
 
class MAX7219Disp
{
  public:
    MAX7219Disp ( ) ;
    void SetDispMode ( MAX7219DecodeModeT Mode ,
                       uint8_t            Digits ,
                       uint8_t            DutyCycle ) ;
    uint8_t NumberToSegments ( uint8_t Digit ) ;
    void DisplayNumber ( int16_t Number ,
                         uint8_t EmptyChar ,
                         uint8_t NumberWidth ) ;
    void WriteCharOnDisplay ( uint8_t Code ) ;
    void RefreshDisplay ( ) ;
    void SetPosOnDisplay ( uint8_t Column ) ;
    void WriteSpace ( ) ;
    void ClearDisplay ( ) ;
    void SetIntensity ( uint8_t DutyCycle ) ;
    void SetScanLimit ( uint8_t ScanLimit ) ;
    void SetDecodeMode ( MAX7219DecodeModeT Mode ) ;
    void SendControlToMAX7219 ( uint8_t RegAdr ,
                                uint8_t Data ) ;
  protected:
    uint8_t GetBlankChar ( ) ;
    void DisplaySegmentsNumber ( int16_t Number ,
                                 uint8_t EmptyChar ,
                                 uint8_t NumberWidth ) ;
    void DisplayBCDNumber ( int16_t Number ,
                            uint8_t EmptyChar ,
                            uint8_t NumberWidth ) ;
  private:
    uint8_t MAX7219DisplayBuffer [ 8 ] ;
    MAX7219DecodeModeT MAX7219DecodeMode ;
    uint8_t MAX7219DisplayBufferIndex ;
} /* class */ ;

Jej zadaniem jest rzeczywiste wysłanie określonych sterowań do układu MAX7219, wszystkie inne metody wykorzystują tę funkcję (cała rzeczywista komunikacja programu z modułem wyświetlacza ogranicza się do jednej funkcji). Realizację przykładowych metod pokazuje listing 2:

MAX7219Disp::MAX7219Disp ( )
{
  DisplayBusConfigPort |= ( 1 << MAX7219_ClkPin ) | ( 1 << MAX7219_DInPin ) |
                          ( 1 << MAX7219_LoadPin ) ;
  SetMAX7219_LoadPin ;
  ClearMAX7219_ClkPin ;
  SetMAX7219_DInPin ;
  SendControlToMAX7219 ( DisplayTest_Register , 0x00 ) ;
  SendControlToMAX7219 ( Shutdown_Register , 0x01 ) ;
  SetIntensity ( DutyCycle ) ;
  SetScanLimit ( ScanLimit ) ;
  SetDecodeMode ( Mode ) ;
} /* MAX7219Disp::MAX7219Disp */
 
void MAX7219Disp::SendControlToMAX7219 ( uint8_t RegAdr ,
                                         uint8_t Data )
{
  uint8_t Loop ;
  uint16_t ControlWord ;
  ControlWord = ( RegAdr << 8 ) | Data ;
  ClearMAX7219_LoadPin ;
  for ( Loop = 0 ; Loop < 16 ; Loop ++ )
  {
    nop ( ) ;  nop ( ) ;
    if ( ControlWord & 0x8000 )
      SetMAX7219_DInPin ;
    else
      ClearMAX7219_DInPin ;
    ControlWord = ControlWord << 1 ;
    nop ( ) ;  nop ( ) ;
    SetMAX7219_ClkPin ;
    nop ( ) ;  nop ( ) ;
    ClearMAX7219_ClkPin ;
  } /* for */ ;
  nop ( ) ;  nop ( ) ;
  SetMAX7219_LoadPin ;
} /* MAX7219Disp::SendControlToMAX7219 */
 
void MAX7219Disp::SetIntensity ( uint8_t DutyCycle )
{
  SendControlToMAX7219 ( Intensity_Register , DutyCycle ) ;
} /* MAX7219Disp::SetIntensity */

Ta prosta koncepcja ma jedną wadę. Do obsługi układu MAX7219, oprócz zasilania (VCC, GND) wykorzystuje się trzy sygnały (CLK, DIN oraz CS). Mogą one być przyłączone do mikrokontrolera w dowolnym miejscu (jako określenie portu i numeru bitu w jego obrębie). Aby zachować dużą elastyczność programu można to określić jako #define i użyć tych identyfikatorów w implementacji metod klasy. To rozwiązanie będzie wymagało każdorazowej kompilacji, gdyż szczegóły określone przez #define wpływają na generowany kod. Konstruktor klasy (MAX7219Disp::MAX7219Disp – patrz listing 2) może zawierać odpowiednie konfiguracje pinów portów mikrokontrolera do obsługi wyświetlacza, niezbędne operacje nadające wartości początkowe zmiennym oraz wysterowanie wybranych rejestrów układu MX7219. Każde wysłanie kodów do tego układu sprowadza się do wywołania metody MAX7219Disp::SendControlToMAX7219.

(…)

——– ciach! ——–

To jest tylko fragment artykułu, którego pełna wersja ukazała się w sierpniowym numerze czasopisma Zrozumieć Elektronikę (ZE 8/2026). Pełną wersję czasopisma znajdziesz pod tym linkiem. Natomiast niepełna, okrojona wersja, pozwalająca zapoznać się z zawartością numeru ZE 8/2026 znajduje się tutaj.

Andrzej Pawluczuk
apawluczuk@vp.pl

Uwaga! Wskazówki, jak nabyć pełne wersje dowolnych numerów ZE znajdują się na stronie:
https://piotr-gorecki.pl/n11.