Craig Evans
/
ELEC2645_Pong2021
Pong game for ELEC1620 board.
Embed:
(wiki syntax)
Show/hide line numbers
N5110.cpp
00001 #include "mbed.h" 00002 #include "N5110.h" 00003 00004 // overloaded constructor includes power pin - LCD Vcc connected to GPIO pin 00005 // this constructor works fine with LPC1768 - enough current sourced from GPIO 00006 // to power LCD. Doesn't work well with K64F. 00007 N5110::N5110(PinName const pwrPin, 00008 PinName const scePin, 00009 PinName const rstPin, 00010 PinName const dcPin, 00011 PinName const mosiPin, 00012 PinName const sclkPin, 00013 PinName const ledPin) 00014 : 00015 _spi(new SPI(mosiPin,NC,sclkPin)), // create new SPI instance and initialise 00016 _led(new DigitalOut(ledPin)), 00017 _pwr(new DigitalOut(pwrPin)), 00018 _sce(new DigitalOut(scePin)), 00019 _rst(new DigitalOut(rstPin)), 00020 _dc(new DigitalOut(dcPin)) 00021 {} 00022 00023 // overloaded constructor does not include power pin - LCD Vcc must be tied to +3V3 00024 // Best to use this with K64F as the GPIO hasn't sufficient output current to reliably 00025 // drive the LCD. 00026 N5110::N5110(PinName const scePin, 00027 PinName const rstPin, 00028 PinName const dcPin, 00029 PinName const mosiPin, 00030 PinName const sclkPin, 00031 PinName const ledPin) 00032 : 00033 _spi(new SPI(mosiPin,NC,sclkPin)), // create new SPI instance and initialise 00034 _led(new DigitalOut(ledPin)), 00035 _pwr(NULL), // pwr not needed so null it to be safe 00036 _sce(new DigitalOut(scePin)), 00037 _rst(new DigitalOut(rstPin)), 00038 _dc(new DigitalOut(dcPin)) 00039 {} 00040 00041 00042 N5110::~N5110() 00043 { 00044 delete _spi; 00045 00046 if(_pwr) { 00047 delete _pwr; 00048 } 00049 00050 delete _led; 00051 delete _sce; 00052 delete _rst; 00053 delete _dc; 00054 } 00055 00056 // initialise function - powers up and sends the initialisation commands 00057 void N5110::init() 00058 { 00059 turnOn(); // power up 00060 reset(); // reset LCD - must be done within 100 ms 00061 initSPI(); 00062 00063 backLightOn(); 00064 setContrast(0.55); // this may need tuning (say 0.4 to 0.6) 00065 setBias(3); // datasheet - 48:1 mux - don't mess with if you don't know what you're doing! (0 to 7) 00066 setTempCoefficient(0); // datasheet - may need increasing (range 0 to 3) at very low temperatures 00067 normalMode(); // normal video mode by default 00068 00069 clearRAM(); // RAM is undefined at power-up so clear to be sure 00070 clear(); // clear buffer 00071 } 00072 00073 // sets normal video mode (black on white) 00074 void N5110::normalMode() 00075 { 00076 sendCommand(0b00100000); // basic instruction 00077 sendCommand(0b00001100); // normal video mode- datasheet 00078 } 00079 00080 // sets normal video mode (white on black) 00081 void N5110::inverseMode() 00082 { 00083 sendCommand(0b00100000); // basic instruction 00084 sendCommand(0b00001101); // inverse video mode - datasheet 00085 } 00086 00087 // function to power up the LCD and backlight - only works when using GPIO to power 00088 void N5110::turnOn() 00089 { 00090 if (_pwr != NULL) { 00091 _pwr->write(1); // apply power 00092 } 00093 } 00094 00095 // function to power down LCD 00096 void N5110::turnOff() 00097 { 00098 clear(); // clear buffer 00099 refresh(); 00100 backLightOff(); // turn backlight off 00101 clearRAM(); // clear RAM to ensure specified current consumption 00102 // send command to ensure we are in basic mode 00103 00104 sendCommand(0b00100000); // basic mode 00105 sendCommand(0b00001000); // clear display 00106 sendCommand(0b00100001); // extended mode 00107 sendCommand(0b00100100); // power down 00108 00109 // if we are powering the LCD using the GPIO then make it low to turn off 00110 if (_pwr != NULL) { 00111 thread_sleep_for(10); // small delay and then turn off the power pin 00112 _pwr->write(0); // turn off power 00113 } 00114 00115 } 00116 00117 // function to change LED backlight brightness 00118 void N5110::backLightOn() 00119 { 00120 _led->write(1); 00121 } 00122 00123 // function to change LED backlight brightness 00124 void N5110::backLightOff() 00125 { 00126 _led->write(0); 00127 } 00128 00129 void N5110::setContrast(float contrast) { 00130 00131 // enforce limits 00132 if (contrast > 1.0f) 00133 contrast = 1.0f; 00134 else if (contrast < 0.0f) 00135 contrast = 0.0; 00136 00137 // convert to char in range 0 to 127 (i.e. 6 bits) 00138 char ic = char(contrast*127.0f); 00139 00140 sendCommand(0b00100001); // extended instruction set 00141 sendCommand(0b10000000 | ic); // set Vop (which controls contrast) 00142 sendCommand(0b00100000); // back to basic instruction set 00143 } 00144 00145 void N5110::setTempCoefficient(char tc) { 00146 00147 // enforce limits 00148 if (tc>3) { 00149 tc=3; 00150 } 00151 00152 // temperature coefficient may need increasing at low temperatures 00153 00154 sendCommand(0b00100001); // extended instruction set 00155 sendCommand(0b00000100 | tc); 00156 sendCommand(0b00100000); // back to basic instruction set 00157 } 00158 00159 void N5110::setBias(char bias) { 00160 00161 // from data sheet 00162 // bias mux rate 00163 // 0 1:100 00164 // 1 1:80 00165 // 2 1:65 00166 // 3 1:48 (default) 00167 // 4 1:40/1:34 00168 // 5 1:24 00169 // 6 1:18/1:16 00170 // 7 1:10/1:9/1:8 00171 00172 // enforce limits 00173 if (bias>7) { 00174 bias=7; 00175 } 00176 00177 sendCommand(0b00100001); // extended mode instruction 00178 sendCommand(0b00010000 | bias); 00179 sendCommand(0b00100000); // end of extended mode instruction 00180 } 00181 00182 // pulse the active low reset line 00183 void N5110::reset() 00184 { 00185 _rst->write(0); // reset the LCD 00186 _rst->write(1); 00187 } 00188 00189 // function to initialise SPI peripheral 00190 void N5110::initSPI() 00191 { 00192 _spi->format(8,1); // 8 bits, Mode 1 - polarity 0, phase 1 - base value of clock is 0, data captured on falling edge/propagated on rising edge 00193 _spi->frequency(4000000); // maximum of screen is 4 MHz 00194 } 00195 00196 // send a command to the display 00197 void N5110::sendCommand(unsigned char command) 00198 { 00199 _dc->write(0); // set DC low for command 00200 _sce->write(0); // set CE low to begin frame 00201 _spi->write(command); // send command 00202 _dc->write(1); // turn back to data by default 00203 _sce->write(1); // set CE high to end frame (expected for transmission of single byte) 00204 } 00205 00206 // send data to the display at the current XY address 00207 // dc is set to 1 (i.e. data) after sending a command and so should 00208 // be the default mode. 00209 void N5110::sendData(unsigned char data) 00210 { 00211 _sce->write(0); // set CE low to begin frame 00212 _spi->write(data); 00213 _sce->write(1); // set CE high to end frame (expected for transmission of single byte) 00214 } 00215 00216 // this function writes 0 to the 504 bytes to clear the RAM 00217 void N5110::clearRAM() 00218 { 00219 _sce->write(0); //set CE low to begin frame 00220 for(int i = 0; i < WIDTH * HEIGHT; i++) { // 48 x 84 bits = 504 bytes 00221 _spi->write(0x00); // send 0's 00222 } 00223 _sce->write(1); // set CE high to end frame 00224 } 00225 00226 // function to set the XY address in RAM for subsequenct data write 00227 void N5110::setXYAddress(unsigned int const x, 00228 unsigned int const y) 00229 { 00230 if (x<WIDTH && y<HEIGHT) { // check within range 00231 sendCommand(0b00100000); // basic instruction 00232 sendCommand(0b10000000 | x); // send addresses to display with relevant mask 00233 sendCommand(0b01000000 | y); 00234 } 00235 } 00236 00237 // These functions are used to set, clear and get the value of pixels in the display 00238 // Pixels are addressed in the range of 0 to 47 (y) and 0 to 83 (x). The refresh() 00239 // function must be called after set and clear in order to update the display 00240 void N5110::setPixel(unsigned int const x, 00241 unsigned int const y, 00242 bool const state) 00243 { 00244 if (x<WIDTH && y<HEIGHT) { // check within range 00245 // calculate bank and shift 1 to required position in the data byte 00246 if(state) buffer[x][y/8] |= (1 << y%8); 00247 else buffer[x][y/8] &= ~(1 << y%8); 00248 } 00249 } 00250 00251 void N5110::clearPixel(unsigned int const x, 00252 unsigned int const y) 00253 { 00254 if (x<WIDTH && y<HEIGHT) { // check within range 00255 // calculate bank and shift 1 to required position (using bit clear) 00256 buffer[x][y/8] &= ~(1 << y%8); 00257 } 00258 } 00259 00260 int N5110::getPixel(unsigned int const x, 00261 unsigned int const y) const 00262 { 00263 if (x<WIDTH && y<HEIGHT) { // check within range 00264 // return relevant bank and mask required bit 00265 00266 int pixel = (int) buffer[x][y/8] & (1 << y%8); 00267 00268 if (pixel) 00269 return 1; 00270 else 00271 return 0; 00272 } 00273 00274 return 0; 00275 00276 } 00277 00278 // function to refresh the display 00279 void N5110::refresh() 00280 { 00281 setXYAddress(0,0); // important to set address back to 0,0 before refreshing display 00282 // address auto increments after printing string, so buffer[0][0] will not coincide 00283 // with top-left pixel after priting string 00284 00285 _sce->write(0); //set CE low to begin frame 00286 00287 for(int j = 0; j < BANKS; j++) { // be careful to use correct order (j,i) for horizontal addressing 00288 for(int i = 0; i < WIDTH; i++) { 00289 _spi->write(buffer[i][j]); // send buffer 00290 } 00291 } 00292 _sce->write(1); // set CE high to end frame 00293 00294 } 00295 00296 // fills the buffer with random bytes. Can be used to test the display. 00297 // The rand() function isn't seeded so it probably creates the same pattern everytime 00298 void N5110::randomiseBuffer() 00299 { 00300 int i,j; 00301 for(j = 0; j < BANKS; j++) { // be careful to use correct order (j,i) for horizontal addressing 00302 for(i = 0; i < WIDTH; i++) { 00303 buffer[i][j] = rand()%256; // generate random byte 00304 } 00305 } 00306 00307 } 00308 00309 // function to print 5x7 font 00310 void N5110::printChar(char const c, 00311 unsigned int const x, 00312 unsigned int const y) 00313 { 00314 if (y<BANKS) { // check if printing in range of y banks 00315 00316 for (int i = 0; i < 5 ; i++ ) { 00317 int pixel_x = x+i; 00318 if (pixel_x > WIDTH-1) // ensure pixel isn't outside the buffer size (0 - 83) 00319 break; 00320 buffer[pixel_x][y] = font5x7[(c - 32)*5 + i]; 00321 // array is offset by 32 relative to ASCII, each character is 5 pixels wide 00322 } 00323 00324 } 00325 } 00326 00327 // function to print string at specified position 00328 void N5110::printString(const char *str, 00329 unsigned int const x, 00330 unsigned int const y) 00331 { 00332 if (y<BANKS) { // check if printing in range of y banks 00333 00334 int n = 0 ; // counter for number of characters in string 00335 // loop through string and print character 00336 while(*str) { 00337 00338 // writes the character bitmap data to the buffer, so that 00339 // text and pixels can be displayed at the same time 00340 for (int i = 0; i < 5 ; i++ ) { 00341 int pixel_x = x+i+n*6; 00342 if (pixel_x > WIDTH-1) // ensure pixel isn't outside the buffer size (0 - 83) 00343 break; 00344 buffer[pixel_x][y] = font5x7[(*str - 32)*5 + i]; 00345 } 00346 str++; // go to next character in string 00347 n++; // increment index 00348 } 00349 } 00350 } 00351 00352 // function to clear the screen buffer 00353 void N5110::clear() 00354 { 00355 memset(buffer,0,sizeof(buffer)); 00356 } 00357 00358 // function to plot array on display 00359 void N5110::plotArray(float const array[]) 00360 { 00361 for (int i=0; i<WIDTH; i++) { // loop through array 00362 // elements are normalised from 0.0 to 1.0, so multiply 00363 // by 47 to convert to pixel range, and subtract from 47 00364 // since top-left is 0,0 in the display geometry 00365 setPixel(i,47 - int(array[i]*47.0f),true); 00366 } 00367 00368 } 00369 00370 // function to draw circle 00371 void N5110:: drawCircle(unsigned int const x0, 00372 unsigned int const y0, 00373 unsigned int const radius, 00374 FillType const fill) 00375 { 00376 // from http://en.wikipedia.org/wiki/Midpoint_circle_algorithm 00377 int x = radius; 00378 int y = 0; 00379 int radiusError = 1-x; 00380 00381 while(x >= y) { 00382 00383 // if transparent, just draw outline 00384 if (fill == FILL_TRANSPARENT) { 00385 setPixel( x + x0, y + y0,true); 00386 setPixel(-x + x0, y + y0,true); 00387 setPixel( y + x0, x + y0,true); 00388 setPixel(-y + x0, x + y0,true); 00389 setPixel(-y + x0, -x + y0,true); 00390 setPixel( y + x0, -x + y0,true); 00391 setPixel( x + x0, -y + y0,true); 00392 setPixel(-x + x0, -y + y0,true); 00393 } else { // drawing filled circle, so draw lines between points at same y value 00394 00395 int type = (fill==FILL_BLACK) ? 1:0; // black or white fill 00396 00397 drawLine(x+x0,y+y0,-x+x0,y+y0,type); 00398 drawLine(y+x0,x+y0,-y+x0,x+y0,type); 00399 drawLine(y+x0,-x+y0,-y+x0,-x+y0,type); 00400 drawLine(x+x0,-y+y0,-x+x0,-y+y0,type); 00401 } 00402 00403 y++; 00404 if (radiusError<0) { 00405 radiusError += 2 * y + 1; 00406 } else { 00407 x--; 00408 radiusError += 2 * (y - x) + 1; 00409 } 00410 } 00411 00412 } 00413 00414 void N5110::drawLine(unsigned int const x0, 00415 unsigned int const y0, 00416 unsigned int const x1, 00417 unsigned int const y1, 00418 unsigned int const type) 00419 { 00420 // Note that the ranges can be negative so we have to turn the input values 00421 // into signed integers first 00422 int const y_range = static_cast<int>(y1) - static_cast<int>(y0); 00423 int const x_range = static_cast<int>(x1) - static_cast<int>(x0); 00424 00425 // if dotted line, set step to 2, else step is 1 00426 unsigned int const step = (type==2) ? 2:1; 00427 00428 // make sure we loop over the largest range to get the most pixels on the display 00429 // for instance, if drawing a vertical line (x_range = 0), we need to loop down the y pixels 00430 // or else we'll only end up with 1 pixel in the x column 00431 if ( abs(x_range) > abs(y_range) ) { 00432 00433 // ensure we loop from smallest to largest or else for-loop won't run as expected 00434 unsigned int const start = x_range > 0 ? x0:x1; 00435 unsigned int const stop = x_range > 0 ? x1:x0; 00436 00437 // loop between x pixels 00438 for (unsigned int x = start; x<= stop ; x+=step) { 00439 // do linear interpolation 00440 int const dx = static_cast<int>(x)-static_cast<int>(x0); 00441 unsigned int const y = y0 + y_range * dx / x_range; 00442 00443 // If the line type is '0', this will clear the pixel 00444 // If it is '1' or '2', the pixel will be set 00445 setPixel(x,y, type); 00446 } 00447 } else { 00448 00449 // ensure we loop from smallest to largest or else for-loop won't run as expected 00450 unsigned int const start = y_range > 0 ? y0:y1; 00451 unsigned int const stop = y_range > 0 ? y1:y0; 00452 00453 for (unsigned int y = start; y<= stop ; y+=step) { 00454 // do linear interpolation 00455 int const dy = static_cast<int>(y)-static_cast<int>(y0); 00456 unsigned int const x = x0 + x_range * dy / y_range; 00457 00458 // If the line type is '0', this will clear the pixel 00459 // If it is '1' or '2', the pixel will be set 00460 setPixel(x,y, type); 00461 } 00462 } 00463 00464 } 00465 00466 void N5110::drawRect(unsigned int const x0, 00467 unsigned int const y0, 00468 unsigned int const width, 00469 unsigned int const height, 00470 FillType const fill) 00471 { 00472 if (fill == FILL_TRANSPARENT) { // transparent, just outline 00473 drawLine(x0,y0,x0+(width-1),y0,1); // top 00474 drawLine(x0,y0+(height-1),x0+(width-1),y0+(height-1),1); // bottom 00475 drawLine(x0,y0,x0,y0+(height-1),1); // left 00476 drawLine(x0+(width-1),y0,x0+(width-1),y0+(height-1),1); // right 00477 } else { // filled rectangle 00478 int type = (fill==FILL_BLACK) ? 1:0; // black or white fill 00479 for (int y = y0; y<y0+height; y++) { // loop through rows of rectangle 00480 drawLine(x0,y,x0+(width-1),y,type); // draw line across screen 00481 } 00482 } 00483 } 00484 00485 void N5110::drawSprite(int x0, 00486 int y0, 00487 int nrows, 00488 int ncols, 00489 int *sprite) 00490 { 00491 for (int i = 0; i < nrows; i++) { 00492 for (int j = 0 ; j < ncols ; j++) { 00493 00494 int pixel = *((sprite+i*ncols)+j); 00495 setPixel(x0+j,y0+i, pixel); 00496 } 00497 } 00498 }
Generated on Thu Jul 14 2022 15:46:36 by 1.7.2