LinkedIn YouTube Facebook
Szukaj

Newsletter

Proszę czekać.

Dziękujemy za zgłoszenie!

Wstecz
Artykuły

ZL31ARM: implementacja funkcji graficznych 2D

Funkcja czyszcząca ekran przyjmuje jako parametr wartość koloru z palety BRG, strukturę wartości którego opisane wyżej, i wypełnia nim cały ekran. Ostatnia funkcja z danej biblioteki ustawia kontrast. W celu jej regulacji można wykorzystać następujący fragment kodu w pętli while i który sprawdza czy włączony jest przycisk JOY_UP lub JOY_DOWN i odpowiednio dostraja wartość:

Delay(0xFFFF);
if(GPIO_ReadInputDataBit(JOY_PORT, JOY_UP) == 0)
SetContrast(contrast == 63 ? contrast : ++contrast);
else if(GPIO_ReadInputDataBit(JOY_PORT, JOY_DOWN) == 0)
SetContrast(contrast == -64 ? contrast : --contrast);

Warto zauważyć, że zmienna contrast przekazywana jako parametr przyjmuje wartości z zakresu od (-64) do 63. Wynika to z budowy sterownika PCF8833 odpowiedzialnego za pracę wyświetlacza. Poniższy kod rysuje kilka punktów testowych na ekranie:

LCDSetPixel(  1,  1,0x00F);//GREEN
LCDSetPixel(130,  1,0x0F0);//RED
LCDSetPixel(130,130,0xF00);//BLUE
LCDSetPixel(  1,130,0x000);//BLACK
LCDSetPixel( 65, 65,0xFFF);//WHITE

W zależności od tego czym został wypełniony ekran funkcją LCDClearScreen(), niektóre kolory mogą być słabo widoczne/rozróżnialne na danym tle.

Rysowanie linii

W prezentowanym projekcie za początek układu współrzędnych OXY został przyjęty punkt centralny ekranu i w tym celu na początku pliku graphics.c umieszczono następującą definicję, którą będzie wykorzystywana w funkcjach rysujących opisanych w danym pliku:

#define DISPLAY_CENTER	132/2

W funkcjach rysujących zawartych w pliku graphics.c w przypadku podawania współrzędnych wartość powinna zawierać się w przedziale od -65 do 64 włącznie co pozwoli na prawidłowe wyświetlenie wszystkich punktów.
Pierwszą i elementarną funkcją w pliku graphics.c jest funkcja rysująca linię drawLine(). Wywołuje się ją z podaniem dwóch punktów końcowych i koloru linii, a jej struktura wygląda w następujący sposób:

void drawLine(int x1, int y1, int x2, int y2, unsigned int color)
{
  //Wyznaczanie roznicy miedzy wspolrzednymi punktami
  int dx = x2 - x1;
  int dy = y2 - y1;

  //Rysuj punkt poczatkowy
  LCDSetPixel(x1+DISPLAY_CENTER, y1+DISPLAY_CENTER, color);

  //Zmienna niezalezna jest x
  if (abs(dx) >= abs(dy) || abs(dx) != 0){
    float a = (float) dy / (float) dx;	//Wyznaczenie wspolczynnika kierunkowego
    float b = y1 - a*x1;			//Wyznaczenie przesuniecia
    dx = (dx < 0) ? -1 : 1;		//W zaleznosci od tego ktory punkt znajduje
						//sie "wyzej" rysowanie bedzie odbywac sie w
						//kierunku dodatnich lub ujemnych wartosci

    //Rysuj kolejne punkty dopoki nie osiagnieto punktu x2
    while (x1 != x2){
      x1 += dx;
      LCDSetPixel(x1+DISPLAY_CENTER, lround(a*x1 + b)+DISPLAY_CENTER, color);
    }
  }
  //Zmienna niezalezna jest y
  else{
    float a = (float) dx / (float) dy;	//Wyznaczenie wspolczynnika kierunkowego
    float b = x1 - a*y1;			//Wyznaczenie przesuniecia
    dy = (dy < 0) ? -1 : 1;

    //Rusuj kolejne punkty dopoki nie osiagnieto punktu y2
    while (y1 != y2){
      y1 += dy;
      LCDSetPixel(lround(a*y1 + b)+DISPLAY_CENTER, y1+DISPLAY_CENTER, color);
    }
  }
}

Cała procedura polega na wybraniu zmiennej niezależnej x (lub y) znalezieniu współczynników funkcji liniowej opisanej równaniem y=ax+b oraz narysowaniu punktów zgodnie z funkcją y=f(x) (lub x=f(y)) za pomocą wcześniej opisanej funkcji LCDSetPixel(). Przykład rysujący 3 przekątne linie:

drawLine(-65,-65, 64, 64, 0x00F);//GREEN
drawLine(-65,-55, 54, 64, 0x0F0);//RED
drawLine(-55,-65, 64, 54, 0xF00);//BLUE

Rysowanie prostokąta

Rysowanie danej figury jest realizowane na dwa sposoby, z których pierwszy (standardowy) wyświetla tylko same boki prostokąta (za pomocą funkcji LCDSetPixel()), a drugi wypełnia zadanym kolorem obszar wyznaczony przez tą figurę. Podobnie jak w przypadku linii należy podać 2 punkty (x,y) leżące na przekątnej prostokąta. Wybór między tymi sposobami jest dokonywany przez podanie parametru style (podanie 1 powoduje rysowanie „pełnego” prostokąta, w innym wypadku będzie on „pusty”). Struktura danej funkcji wygląda następująco:

void drawRectangle(int x1, int y1, int x2, int y2, unsigned int color,
		     unsigned char style){
	unsigned char i, j;

	//Wyznaczenie dlugosci bokow
	int a = abs(x2 - x1);
	int b = abs(y2 - y1);

	switch(style){
		//Rysuj pelny prostokata z wypelnieniem zadanym kolorem
		case 1:{
			for(i=0; i < a; i++){
			  for(j=0; j < b; j++){
			    LCDSetPixel(x1+DISPLAY_CENTER+i, y1+DISPLAY_CENTER+j, color);
			  }
			}
			break;
		}
		//Rysuj 4 boki prostokata z zadanym kolorem
		default:{
			drawLine(x1,y1,x1,y2,color);
			drawLine(x1,y1,x2,y1,color);
			drawLine(x1,y2,x2,y2,color);
			drawLine(x2,y1,x2,y2,color);
			break;
		}
	}
}

Przykład rysowania „pustych” i „pełnych” prostokątów:

	//Rysowanie pustych prostokątów testowych
	drawRectangle(-50,-40,50,40,RED);
	drawRectangle(-40,-30,40,30,GREEN);
	drawRectangle(-30,-20,30,20,BLUE);

	//Rysowanie pelnych prostokątów testowych
	drawRectangle(-50,-40,50,40,RED,1);
	drawRectangle(-40,-30,40,30,GREEN,1);
	drawRectangle(-30,-20,30,20,BLUE,1);
Autor: Jan Szemiet
Tagi: ARM, BTC, GPIO, MEMS, STM32