[JAK OBIEKTOWO NAPISAĆ DRIVER] STM32NUCLEO + mbed.org + graficzny LCD = druga aplikacja na STM32 w sieciowym środowisku programistycznym
Przedstawiony w poprzednim artykule sposób implementacji sterownika wyświetlacza oferuje skromne możliwości i należałoby ją rozbudować o dodatkowe funkcje, m.in. do rysowania linii oraz prostokątów. Takie zadanie można zrealizować na dwa sposoby. Pierwszy, typowy dla klasycznego języka C, polega na zdefiniowaniu funkcji, których prototypy mogłyby wyglądać w sposób następujący:
void drawRectangle(int x, int y, int width, int height, unsigned int color, unsigned char style); void drawLine(int x1, int y1, int x2, int y2, unsigned int color);
Drugi sposób, znany z programowania obiektowego (język C++), zamiast wartości konkretnych parametrów wykorzystuje obiekty:
void drawRectangle(const Rect& rect, unsigned int color, unsigned char fill); void drawLine(const Line& line, unsigned int color);
void drawRectangle(int x, int y, int width, int height, unsigned int color, unsigned char style); void drawLine(int x1, int y1, int x2, int y2, unsigned int color);
Jak można zauważyć funkcje zdefiniowane w ten drugi sposób są bardziej czytelne jednak wymagają zdefiniowania dwóch klas: Line oraz Rect. Klasa Line będzie zawierała pola typu int przechowujące położenie punktów początku i końca odcinka prostej, konstruktory za pomocą których będzie można utworzyć obiekt Line, a także funkcje umożliwiające przesunięcie oraz przeskalowanie linii. Klasa Rect będzie wyglądać podobnie z tą różnicą, że zamiast współrzędnych i wymiarów będzie przechowywać cztery podobiekty typu Line. W celu uzyskania bardziej przejrzystego projektu zdefiniujemy te klasy w pliku Graphics.h:
#ifndef GRAPHICS_H
#define GRAPHICS_H
class Line {
public:
int x1, y1;
int x2, y2;
Line();
Line(int lx1, int ly1, int lx2, int ly2);
Line operator*=(const double scaleFactor);
void move(int dx, int dy);
void moveTo(int lx, int ly);
};
class Rect {
public:
Line bottom, left, right, top;
Rect(int lx, int ly, int lwidth, int lheight);
Rect operator*=(const double scaleFactor);
void move(int dx, int dy);
void moveTo(int lx, int ly);
};
#endif /* GRAPHICS_H */
W pliku implementacyjnym Graphics.cpp definiujemy konstruktory oraz funkcje klas powyższych. W przypadku skalowania obu współrzędnych zastosujemy przeciążanie operatora „*=” (mnożenie z przypisaniem wartości wyniku).
Line::Line()
: x1(0), y1(0), x2(0), y2(0) {
// Konstruktor domyślny musi być zdefiniowany, ponieważ wymaga tego klasa-rodzic Rect, w której
// zmienne typu Line są podobiektami
}
Line::Line(int lx1, int ly1, int lx2, int ly2)
: x1(lx1), y1(ly1), x2(lx2), y2(ly2) {
// Po prostu inicjalizacja zmiennych
}
Line Line::operator*=(const double scaleFactor) {
// Wymnożenie długości rzutów linii przez współczynnik scaleFactor
x2 = ((double)(x2 - x1))*scaleFactor + x1;
y2 = ((double)(y2 - y1))*scaleFactor + y1;
return *this;
}
void Line::move(int dx, int dy) {
// Przesunięcie linii o (dx,dy)
x1 += dx;
y1 += dy;
x2 += dx;
y2 += dy;
}
void Line::moveTo(int lx, int ly) {
// Przesunięcie punktu początkowego linii (x1,y1) do punktu (lx,ly)
x2 = lx + (x2 - x1);
y2 = ly + (y2 - y1);
x1 = lx;
y1 = ly;
}
Rect::Rect(int lx, int ly, int lwidth, int lheight) {
// Utworzenie czterech obiektów typu Line stanowiących prostokąt
bottom = Line(lx, ly + lheight, lx + lwidth, ly + lheight);
left = Line(lx, ly, lx, ly + lheight);
right = Line(lx + lwidth, ly, lx + lwidth, ly + lheight);
top = Line(lx, ly, lx + lwidth, ly);
}
Rect Rect::operator*=(const double scaleFactor) {
// Skalowanie długości krawędzi prostokąta
bottom *= scaleFactor;
left *= scaleFactor;
right *= scaleFactor;
top *= scaleFactor;
// Przesunięcie dwóch krawędzi prostokąta
bottom.moveTo(left.x2, left.y2);
right.moveTo(top.x2, top.y2);
return *this;
}
void Rect::move(int dx, int dy) {
// Przesunięcie prostokąta o (dx,dy)
bottom.move(dx, dy);
left.move(dx, dy);
right.move(dx, dy);
top.move(dx, dy);
}
void Rect::moveTo(int lx, int ly) {
// Przesunięcie punktu początkowego prostokąta (x1,y1) (wraz z prostokątem) do punktu (lx,ly)
left.moveTo(lx, ly);
top.moveTo(lx, ly);
bottom.moveTo(left.x2, left.y2);
right.moveTo(top.x2, top.y2);
}
Funkcje rysujące linie oraz prostokąty (plik NokiaLCD.cpp) wyglądają jak poniżej:
void NokiaLCD::drawLine(const Line& line, unsigned int color) {
int x1 = line.x1;
int y1 = line.y1;
int x2 = line.x2;
int y2 = line.y2;
// Wyznaczenie różnicy między współrzędnymi punktów
int dx = x2 - x1;
int dy = y2 - y1;
// Rysuj punkt początkowy
drawPixel(x1, y1, color);
// Zmienną niezależną jest x
if( (abs(dx) >= abs(dy)) && abs(dx) != 0){
float a = (float) dy / (float) dx; // Wyznaczenie współczynnika kierunkowego
float b = y1 - a*x1; // Wyznaczenie przesunięcia
dx = (dx < 0) ? -1 : 1; // W zależności od tego który punkt znajduje
// się "wyżej" rysowanie będzie odbywać się w
// kierunku dodatnich lub ujemnych wartości
// Rysuj kolejne punkty, dopóki nie osiągnięto punktu x2
while(x1 != x2){
x1 += dx;
drawPixel(x1, (a*x1 + b), color);
}
}
// Zmienną niezależną jest y
else{
float a = (float) dx / (float) dy; // Wyznaczenie współczynnika kierunkowego
float b = x1 - a*y1; // Wyznaczenie przesunięcia
dy = (dy < 0) ? -1 : 1;
// Rysuj kolejne punkty, dopóki nie osiągnięto punktu y2
while(y1 != y2){
y1 += dy;
drawPixel((a*y1 + b), y1, color);
}
}
}
void NokiaLCD::drawRectangle(const Rect& rect, unsigned int color, unsigned char fill) {
int x1 = rect.top.x1;
int y1 = rect.top.y1;
int x2 = rect.bottom.x2;
int y2 = rect.bottom.y2;
// Wyznaczenie dlugosci bokow
int a = abs(x2 - x1);
int b = abs(y2 - y1);
switch(fill){
// Rysuj pełny prostokąt z wypełnieniem zadanym kolorem
case 1:{
for(int i=0; i < a; i++){
for(int j=0; j < b; j++){
drawPixel(x1+i, y1+j, color);
}
}
break;
}
// Rysuj 4 boki prostokąta z zadanym kolorem
default:{
drawLine(rect.top, color);
drawLine(rect.bottom, color);
drawLine(rect.left, color);
drawLine(rect.right, color);
break;
}
}
}


Technologie End of Life i bezpieczeństwo sieci – wyzwania Europy związane z tzw. długiem technologicznym
Najczęstsze błędy firm przy wyborze dostawcy energii i jak ich uniknąć
Fotorezystor, czyli czujnik światła dwojakiego działania. Przykład innowacji w automatyce i elektronice możliwej dzięki technologii fotooporników 



