|
#include "SPI.h" |
|
#include "Adafruit_GFX.h" |
|
#include "Adafruit_ILI9340.h" |
|
#include <SD.h> |
|
|
|
#if defined(__SAM3X8E__) |
|
#undef __FlashStringHelper::F(string_literal) |
|
#define F(string_literal) string_literal |
|
#endif |
|
|
|
// These are the pins used for the Mega |
|
// for Due/Uno/Leonardo use the hardware SPI pins (which are different) |
|
#define _sclk 52 |
|
#define _miso 50 |
|
#define _mosi 51 |
|
#define _cs 53 |
|
#define _rst 7 |
|
#define _dc 6 |
|
#define SD_CS 5 |
|
|
|
|
|
Adafruit_ILI9340 tft = Adafruit_ILI9340(_cs, _dc, _rst); |
|
int potPin=0; |
|
int count=0; |
|
int prevCount=1; |
|
int countdigits[] = {0, 0, 0}; |
|
int prevdigits[] = {0, 0, 0}; |
|
int digitpos[] = {30, 90, 150}; |
|
int x=0; |
|
|
|
void setup() { |
|
Serial.begin(9600); |
|
|
|
//Init the SD Card |
|
Serial.print("Initializing SD card..."); |
|
if (!SD.begin(SD_CS)) { |
|
Serial.println("failed!"); |
|
return; |
|
} |
|
Serial.println("OK!"); |
|
|
|
//Start the TFT screen and paint it black |
|
tft.begin(); |
|
tft.setRotation(1); |
|
tft.fillScreen(ILI9340_BLACK); |
|
|
|
//Draw TRD Logo before turing on backlight |
|
bmpDraw("trdlogo.bmp", 40, 60); |
|
|
|
//fade in back lighting |
|
for(int b=0; b < 230; b++){ |
|
analogWrite(2, b); |
|
delay(20); |
|
} |
|
|
|
//Sleep a while to display the logo |
|
delay(2000); |
|
tft.fillScreen(ILI9340_BLACK); |
|
tft.drawFastHLine(0, 180, 320, ILI9340_RED); |
|
|
|
//Draw static screen elements |
|
bmpDraw("trdsml24.bmp", 110, 195); |
|
tft.setTextColor(ILI9340_WHITE); |
|
tft.setTextSize(4); |
|
tft.setCursor(230, 90); |
|
tft.print("mph"); |
|
} |
|
|
|
void loop(void) { |
|
//This simulates input of a VSS |
|
//This will be replaced with pulse counting of the VSS sensor. |
|
count = analogRead(potPin) / 8; |
|
|
|
//Split each digit of the speed into an array |
|
//This will allow printing of just numbers that have changed. |
|
//The speed of a car can exceed a two digit number, certantly |
|
//in kph and occasionally in mph. The splitting is done by |
|
//taking the modulo, dividing or a combination of both. |
|
|
|
//Grab the last digit of the speed |
|
countdigits[2] = count % 10; |
|
//How to handle the middle digit depends on if the |
|
//the speed is a two or three digit number |
|
if(count > 99){ |
|
countdigits[1] = (count / 10) % 10; |
|
}else{ |
|
countdigits[1] = count / 10; |
|
} |
|
//Grab the first digit |
|
countdigits[0] = count / 100; |
|
|
|
//Split out the digits of the previous speed |
|
prevdigits[2] = prevCount % 10; |
|
if(prevCount > 99){ |
|
prevdigits[1] = (prevCount / 10) % 10; |
|
}else{ |
|
prevdigits[1] = prevCount / 10; |
|
} |
|
prevdigits[0] = prevCount / 100; |
|
|
|
//Now print the digits on the TFT screen. |
|
//Only execute this block if the speed has changed. |
|
if(count != prevCount){ |
|
tft.setTextSize(10); |
|
//Compare each digit to the value from the previous loop. |
|
//The digit will only be redrawn if it has changed. |
|
for(x=0; x < 3; x++){ |
|
if(countdigits[x] != prevdigits[x]){ |
|
//black out old value first. |
|
//Draw digit in black over the top of white digit |
|
tft.setCursor(digitpos[x], 70); |
|
tft.setTextColor(ILI9340_BLACK); |
|
tft.print(prevdigits[x]); |
|
//print new value in white |
|
if((x == 0) and (count > 99) and (countdigits[x] > 0)){ |
|
tft.setCursor(digitpos[x], 70); |
|
tft.setTextColor(ILI9340_WHITE); |
|
tft.print(countdigits[x]); |
|
} |
|
if((x == 1) and (count >= 99)){ |
|
tft.setCursor(digitpos[x], 70); |
|
tft.setTextColor(ILI9340_WHITE); |
|
tft.print(countdigits[x]); |
|
}else if((x == 1) and (count < 99) and (countdigits[x] > 0)){ |
|
tft.setCursor(digitpos[x], 70); |
|
tft.setTextColor(ILI9340_WHITE); |
|
tft.print(countdigits[x]); |
|
} |
|
if(x == 2){ |
|
tft.setCursor(digitpos[x], 70); |
|
tft.setTextColor(ILI9340_WHITE); |
|
tft.print(countdigits[x]); |
|
} |
|
} |
|
} |
|
} |
|
delay(999); //Delay screen updates to simulate 1 second pulse counting. |
|
prevCount = count; //Store current speed for comparison on the next loop. |
|
} |
|
|
|
//-------------------------------------------------------------------------------- |
|
//I didn't write anything below here. The code below is from the |
|
//Adafruit library and it is used to load images from the SD card. |
|
//-------------------------------------------------------------------------------- |
|
|
|
|
|
// This function opens a Windows Bitmap (BMP) file and |
|
// displays it at the given coordinates. It's sped up |
|
// by reading many pixels worth of data at a time |
|
// (rather than pixel by pixel). Increasing the buffer |
|
// size takes more of the Arduino's precious RAM but |
|
// makes loading a little faster. 20 pixels seems a |
|
// good balance. |
|
|
|
#define BUFFPIXEL 50 |
|
|
|
void bmpDraw(char *filename, uint8_t x, uint8_t y) { |
|
|
|
File bmpFile; |
|
int bmpWidth, bmpHeight; // W+H in pixels |
|
uint8_t bmpDepth; // Bit depth (currently must be 24) |
|
uint32_t bmpImageoffset; // Start of image data in file |
|
uint32_t rowSize; // Not always = bmpWidth; may have padding |
|
uint8_t sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel) |
|
uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer |
|
boolean goodBmp = false; // Set to true on valid header parse |
|
boolean flip = true; // BMP is stored bottom-to-top |
|
int w, h, row, col; |
|
uint8_t r, g, b; |
|
uint32_t pos = 0, startTime = millis(); |
|
|
|
if((x >= tft.width()) || (y >= tft.height())) return; |
|
|
|
Serial.println(); |
|
Serial.print("Loading image '"); |
|
Serial.print(filename); |
|
Serial.println('\''); |
|
|
|
// Open requested file on SD card |
|
if ((bmpFile = SD.open(filename)) == NULL) { |
|
Serial.print("File not found"); |
|
return; |
|
} |
|
|
|
// Parse BMP header |
|
if(read16(bmpFile) == 0x4D42) { // BMP signature |
|
Serial.print("File size: "); Serial.println(read32(bmpFile)); |
|
(void)read32(bmpFile); // Read & ignore creator bytes |
|
bmpImageoffset = read32(bmpFile); // Start of image data |
|
Serial.print("Image Offset: "); Serial.println(bmpImageoffset, DEC); |
|
// Read DIB header |
|
Serial.print("Header size: "); Serial.println(read32(bmpFile)); |
|
bmpWidth = read32(bmpFile); |
|
bmpHeight = read32(bmpFile); |
|
if(read16(bmpFile) == 1) { // # planes -- must be '1' |
|
bmpDepth = read16(bmpFile); // bits per pixel |
|
Serial.print("Bit Depth: "); Serial.println(bmpDepth); |
|
if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed |
|
|
|
goodBmp = true; // Supported BMP format -- proceed! |
|
Serial.print("Image size: "); |
|
Serial.print(bmpWidth); |
|
Serial.print('x'); |
|
Serial.println(bmpHeight); |
|
|
|
// BMP rows are padded (if needed) to 4-byte boundary |
|
rowSize = (bmpWidth * 3 + 3) & ~3; |
|
|
|
// If bmpHeight is negative, image is in top-down order. |
|
// This is not canon but has been observed in the wild. |
|
if(bmpHeight < 0) { |
|
bmpHeight = -bmpHeight; |
|
flip = false; |
|
} |
|
|
|
// Crop area to be loaded |
|
w = bmpWidth; |
|
h = bmpHeight; |
|
if((x+w-1) >= tft.width()) w = tft.width() - x; |
|
if((y+h-1) >= tft.height()) h = tft.height() - y; |
|
|
|
// Set TFT address window to clipped image bounds |
|
tft.setAddrWindow(x, y, x+w-1, y+h-1); |
|
|
|
for (row=0; row<h; row++) { // For each scanline... |
|
|
|
// Seek to start of scan line. It might seem labor- |
|
// intensive to be doing this on every line, but this |
|
// method covers a lot of gritty details like cropping |
|
// and scanline padding. Also, the seek only takes |
|
// place if the file position actually needs to change |
|
// (avoids a lot of cluster math in SD library). |
|
if(flip) // Bitmap is stored bottom-to-top order (normal BMP) |
|
pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize; |
|
else // Bitmap is stored top-to-bottom |
|
pos = bmpImageoffset + row * rowSize; |
|
if(bmpFile.position() != pos) { // Need seek? |
|
bmpFile.seek(pos); |
|
buffidx = sizeof(sdbuffer); // Force buffer reload |
|
} |
|
|
|
for (col=0; col<w; col++) { // For each pixel... |
|
// Time to read more pixel data? |
|
if (buffidx >= sizeof(sdbuffer)) { // Indeed |
|
bmpFile.read(sdbuffer, sizeof(sdbuffer)); |
|
buffidx = 0; // Set index to beginning |
|
} |
|
|
|
// Convert pixel from BMP to TFT format, push to display |
|
b = sdbuffer[buffidx++]; |
|
g = sdbuffer[buffidx++]; |
|
r = sdbuffer[buffidx++]; |
|
tft.pushColor(tft.Color565(r,g,b)); |
|
} // end pixel |
|
} // end scanline |
|
Serial.print("Loaded in "); |
|
Serial.print(millis() - startTime); |
|
Serial.println(" ms"); |
|
} // end goodBmp |
|
} |
|
} |
|
|
|
bmpFile.close(); |
|
if(!goodBmp) Serial.println("BMP format not recognized."); |
|
} |
|
|
|
// These read 16- and 32-bit types from the SD card file. |
|
// BMP data is stored little-endian, Arduino is little-endian too. |
|
// May need to reverse subscript order if porting elsewhere. |
|
|
|
uint16_t read16(File f) { |
|
uint16_t result; |
|
((uint8_t *)&result)[0] = f.read(); // LSB |
|
((uint8_t *)&result)[1] = f.read(); // MSB |
|
return result; |
|
} |
|
|
|
uint32_t read32(File f) { |
|
uint32_t result; |
|
((uint8_t *)&result)[0] = f.read(); // LSB |
|
((uint8_t *)&result)[1] = f.read(); |
|
((uint8_t *)&result)[2] = f.read(); |
|
((uint8_t *)&result)[3] = f.read(); // MSB |
|
return result; |
|
} |