⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lcd-color.cpp

📁 LCD using AVR controller
💻 CPP
📖 第 1 页 / 共 2 页
字号:
{
	/* note: this could call FillRect to save some code space, but
	   this is a bit faster (less error-checking needed), and this
	   function is called a lot... */

	if (x >= dxLCDScreen || y >= dyLCDScreen)	// completely off-screen
		return;

	/* convert to LCD coordinates (so the variables really should
	   be xl/yl from here down...) */
	x += xlMin;
	y += ylMin;
	WriteCommand(lctCmd, PASET);	// Page Address Set
	WriteCommand(lctData, y);
	WriteCommand(lctData, y);
	WriteCommand(lctCmd, CASET);	// Column Address Set
	WriteCommand(lctData, x);
	WriteCommand(lctData, x);
	WriteCommand(lctCmd, RAMWR);	// Memory Write
	WriteCommand(lctData, clr);
}

/* draw a solid/filled rectangle with its upper left corner at (x, y),
   with the given width, height, and color */
void LCD::FillRect(uchar x, uchar y, uchar dx, uchar dy, uchar clr) const
{
	uint cpx;

	cpx = CpxSetAndValidateLCDRect(x, y, dx, dy);
	while (cpx-- > 0)
		WriteCommand(lctData, clr);
}

/* outline a rectangle with its upper left corner at (x, y),
   with the given width, height, and color */
void LCD::FrameRect(uchar x, uchar y, uchar dx, uchar dy, uchar clr) const
{
	FillRect(x, y, dx, 1, clr);			// top
	FillRect(x, y + dy -1, dx, 1, clr);	// bottom
	FillRect(x, y, 1, dy, clr);			// left
	FillRect(x + dx - 1, y, 1, dy, clr);// right
}

/* helper for DrawLine */
inline void Swap(uchar *px, uchar *py)
{
	uchar t;
	t = *px; *px = *py; *py = t;
}

/* draw a line from (x1, y1) to (x2, y2) */
void LCD::DrawLine(uchar x1, uchar y1, uchar x2, uchar y2, uchar clr) const
{
	uchar dx, dy;
	char dyStep;
	bool fMirror;
	int calc;

	/* handle simple horizontal / vertical lines */
	if (x1 == x2)
	{
		if (y1 > y2)	// swap values
			Swap(&y1, &y2);
		FillRect(x1, y1, 1, y2 - y1, clr);
		return;
	}
	if (y1 == y2)
	{
		if (x1 > x2)	// swap values
			Swap(&x1, &x2);
		FillRect(x1, y1, x2 - x1, 1, clr);
		return;
	}

	/* set up parameters */
	dx = Abs((int)x2 - (int)x1);
	dy = Abs((int)y2 - (int)y1);
	/* make sure we travel along longer side; if necessary,
	   we'll flip X and Y (here and below when drawing),
	   effectively mirroring along the line y=x */
	if ((fMirror = dy > dx) != fFalse)
	{
		/* swap X-Y */
		Swap(&x1, &y1);
		Swap(&x2, &y2);
		Swap(&dx, &dy);
	}
	/* start at the leftmost point */
	if (x1 > x2)
	{
		/* swap points */
		Swap(&x1, &x2);
		Swap(&y1, &y2);
	}
	/* rising or falling? */
	dyStep = y1 < y2 ? 1 : -1;

	/*
	normalized system: line at (0,0) positive slope to (r, s) [geometry coordinates]
		y = mx + b         m = s / r, b = 0     =>   ry = sx
	for each point x along line, y will stay the same as prev or change to y + 1
		ry = sx      or    r (y + 1) = sx
	choose left-side value that is closest to sx; change y if
		r (y + 1) - sx    <    sx - ry
	which simplifies to
		r - 2 (sx - ry) < 0
	the inequality will hold if we divide by two (accounting for integer math),
	so we can use:
		r/2 - sx + ry < 0
	at start, x = y = 0, left side is just r/2
	each iteration, x increases, which subtracts another s (from right side)
	when y increases, that adds another r (to right side)
	*/
	/* draw the line */
	calc = dx >> 1;
	while (x1 <= x2)
	{
		if (fMirror)
			ColorPixel(y1, x1, clr);
		else
			ColorPixel(x1, y1, clr);
		/* move to next x value */
		++x1;
		calc -= dy;
		/* see if should change y value */
		if (calc < 0)
		{
			y1 += dyStep;
			calc += dx;
		}
	}
}

/* calculates 1/8 circle and reflects/rotates to draw full circle */
void LCD::FrameCircle(uchar xCenter, uchar yCenter, uchar r, uchar clr) const
{
	uchar x, y;
	int calc;

	/* we'll draw pixels starting at (r, 0) and going counter-clockwise
	   (viewed in geometric space, not screen coordinates), rotating
	   and reflecting to fill in 8 sections at once, so we're done
	   when x == y (1/8 of circle calculated).
	   as we traverse that arc, we'll always move either up or up-left,
	   figure out which one is closer to the correct location;
	   change x if:
		  r^2 - left pixel x^2+y^2   <    right pixel x^2+y^2 - r^2
		  r^2 - ((x - 1)^2 + y^2)    <    x^2 + y^2 - r^2
		  r2 - (x2 - 2*x + 1 + y2)   <    x2 + y2 - r2
		  r2 - x2 - y2 + 2x - 1      <    x2 + y2 - r2
		  2r2 - 2x2 - 2y2 + 2x       <    1
		  r2 - x2 - y2 + x           <    1/2
		     (only int < 1/2 is 0 or negative)
		  r2 - x2 - y2 + x           <=   0
	   at beginning, x = r, y = 0, so left side is just x
	   when increment y, y^2 term increases by 2y-1 (so term decreases)
	   when decrement x, x^2 term decreases by 2x+1 (so term increases)
	   */

	x = r;
	y = 0;
	calc = x;

	while (x >= y)
	{
		/* set pixel at current locations */
		/* (negative values will become large pos numbers and get rejected) */
		ColorPixel(xCenter + x, yCenter + y, clr);
		ColorPixel(xCenter + x, yCenter - y, clr);
		ColorPixel(xCenter - x, yCenter + y, clr);
		ColorPixel(xCenter - x, yCenter - y, clr);
		ColorPixel(xCenter + y, yCenter + x, clr);
		ColorPixel(xCenter + y, yCenter - x, clr);
		ColorPixel(xCenter - y, yCenter + x, clr);
		ColorPixel(xCenter - y, yCenter - x, clr);
		/* should next pixel be up or up-left? */
		++y;			// move up
		calc -= 2 * y - 1;
		if (calc <= 0)	// move left?
		{
			--x;
			calc += 2 * x + 1;
		}
	}
}

/* calculates 1/8 circle and reflects/rotates to draw full circle;
   see FrameCircle for calc explanation */
void LCD::FillCircle(uchar xCenter, uchar yCenter, uchar r, uchar clr) const
{
	uchar x, y;
	int calc;
	uchar xT, d;

	x = r;
	y = 0;
	calc = x;

	while (x >= y)
	{
		/* if circle extends off left side, need to handle it here (no
		   negatives, so those values become large pos values and get
		   rejected by FillRect) */
		if (x <= xCenter)
		{
			xT = xCenter - x;
			d = 2 * x;
		}
		else
		{
			xT = 0;
			/* 2x - (x - xCenter) = x + xCenter */
			d = x + xCenter;
		}
		FillRect(xT, yCenter + y, d, 1, clr);
		FillRect(xT, yCenter - y, d, 1, clr);

		if (y <= xCenter)
		{
			xT = xCenter - y;
			d = 2 * y;
		}
		else
		{
			xT = 0;
			/* 2y - (y - xCenter) = y + xCenter */
			d = y + xCenter;
		}
		FillRect(xT, yCenter + x, d, 1, clr);
		FillRect(xT, yCenter - x, d, 1, clr);

		/* should next pixel be up or up-left? */
		++y;			// move up
		calc -= 2 * y - 1;
		if (calc <= 0)	// move left?
		{
			--x;
			calc += 2 * x + 1;
		}
	}
}

/* draw the specified bitmap (located in flash memory) at the given
   coordinates (upper left corner); the first two bytes of the memory
   specify the width and height, with remaining bytes as 8-bit color
   values; will not draw anything if not enough room for full image */
void LCD::ShowBitmap(const uchar *pab, uchar x, uchar y) const
{
	uchar dz[2], clr;
	uint cpx;

	memcpy_P(dz, pab, 2 * sizeof(uchar));
	pab += 2;
	cpx = CpxSetAndValidateLCDRect(x, y, dz[0], dz[1]);
	if (cpx != (uint)dz[0] * dz[1])
		return;

	while (cpx-- > 0)
	{
		memcpy_P(&clr, pab++, sizeof(uchar));
		WriteCommand(lctData, clr);
	}
}

/*
*	LCD setup
*/

/* sets "volume" for the LCD, which is essentially a brightness
   level for the LED backlight;
   ratio: 0-7, "resistance ratio of built-in voltage regulating resistor" 
      this is a coarse setting
   volume: 0-63, "electronic volume value"
      this will fine-tune the brightness
   technique -- find a ratio value that works well, then tune volume */
void LCD::SetVolume(uchar ratio, uchar volume) const
{
	WriteCommand(lctCmd, VOLCTR);	// Electronic Volume Control (LCD brightness)
	WriteCommand(lctData, volume & 0x3f);
	WriteCommand(lctData, ratio & 0x03);
}

void LCD::IncVolumn() const
{
	WriteCommand(lctCmd, VOLUP);	// Increment Electronic Control
}

void LCD::DecVolumn() const
{
	WriteCommand(lctCmd, VOLDOWN);	// Decrement Electronic Control
}

/*
*	helper functions
*/

/* send an instruction to the LCD */
void LCD::WriteCommand(uchar lct, uchar ch) const
{
	/* enable chip */
	s_poutE->SetLow();

	/* send first bit manually */
	/* set data bit for command or data */
	if (lct == lctCmd)
		s_poutMOSI->SetLow();
	else
		s_poutMOSI->SetHigh();
	/* pulse clock */
	s_poutSCK->SetHigh();
	asm("nop");		// just to be sure...
	s_poutSCK->SetLow();

	/* enable SPI for remaining 8 bits */
	SetBit(regSPCR, bitSPE);
	regSPDR = ch;
	/* wait for transmission to complete */
	while (bit_is_clear(regSPSR, bitSPIF))
		;
	ClearBit(regSPCR, bitSPE);	// disable it so we can send manually...
	
	/* signal done with this transmit */
	s_poutE->SetHigh();
}

/* ensures that rectangle is fully on screen and sets LCD for drawing there;
   returns number of pixels available to fill */
uint LCD::CpxSetAndValidateLCDRect(uchar x, uchar y, uchar dx, uchar dy) const
{
	uchar xlFirst, ylFirst, xlLast, ylLast;	// LCD coordinates

	/* check upper left corner */
	/* (x and y aren't too low since unsigned can't be < 0!) */
	if (x >= dxLCDScreen || y >= dyLCDScreen)	// completely off-screen
		return 0;

	/* check lower right corner */
	if (x + dx > dxLCDScreen)
		dx = dxLCDScreen - x;
	if (y + dy > dyLCDScreen)
		dy = dyLCDScreen - y;

	/* convert to LCD coordinates */
	xlLast = (xlFirst = xlMin + x) + dx - 1;
	ylLast = (ylFirst = ylMin + y) + dy - 1;

	/* note: for PASET/CASET, docs say that start must be < end,
	   but <= appears to be OK; end is a "last" not "lim" value */
	WriteCommand(lctCmd, PASET);	// Page Address Set
	WriteCommand(lctData, ylFirst);	// start page (line)
	WriteCommand(lctData, ylLast);	// end page
	WriteCommand(lctCmd, CASET);	// Column Address Set
	WriteCommand(lctData, xlFirst);	// start address
	WriteCommand(lctData, xlLast);	// end address
	WriteCommand(lctCmd, RAMWR);	// Memory Write

	return (uint)dx * dy;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -