Introduction
The Traffic Light Intersection Simulator records user input through a touch screen of traffic flow at a four-way intersection for play back. Additionally it is capable of storing and reading to a FAT 32 formatted microSD card. The simulator is also capable of clearing the memory, removing the last input and play back of what is currently saved.
High Level Design
The idea originated from a project one of our parents made (Carlos Contreras Aponte) during his masters to assist with an assignment requiring the data logging of traffic flow at an intersection. This version is a more modern approach utilizing a resistive touch screen for user input as well as a microSD card for storage. The TFT screen and microSD card are connected as individual SPI channels, while the resistive touch film on the screen is connected through two digital and two analog pins. Although the TFT screen and resistive touch film share the same breakout board and come together, these need to be calibrated through software for a touch value to match its equivalent location on the screen. It is important to note that the program will not finish boot unless there is a microSD card.
The logical structure for the program consists of two threads. One reads the resistive touch film input while the other interprets the input. If the program determines the input to be a traffic movement gesture, this gesture will be saved in an array. In the case the input is not a traffic movement gesture it determines if it was a press of a “button” for either Clear, Undo, Play, Save to microSD or Load from microSD card. Each gesture is accompanied with a message of to confirm what gesture was input. While the simulator is playing back the traffic flow from the gesture array, the simulator becomes locked and will not read input from the resistive touch film.
Hardware design
The simulator we built uses an Adafruit 2.8″ TFT LCD with Resistive Touchescreen Breakout Board (ILI9341) and an Adafruit MicroSD Card Breaout Board+. The TFT screen it uses the PIC32’s SPI channel 1, while the microSD card reader uses SPI channel 2. The TFT’s CS, D/C and RST and the microSD card reader’s CS and CD pins were connected as regular GPIO pins. The SD card library we use include for a Write Enable pin which the microSD card reader breakout board does not include. The resistive touch film on the TFT screen uses four pins: Y+, Y-, X+ and X-. To read where it is being touched on X axis, the X+ and X- must be set as digital outputs, with X+ being a logical 1 and the X- a logical 0. Then you read through an analog input from either Y+ or Y-, which we used Y-. To read on Y axis you simply repeat the process as reading on X axis, but invert the axis. We used X- as the analog input pin. We also include 330Ω resistors between the microcontroller and touchscreen to protect them from overcurrent. The value of the resistors used was decided to be same one as used for same reason on previous projects for the course.
Program design
Our touch screen library and SD card implementation were built off of libraries previously written by Syed Tahmid Mahbub. The SD card library are originally from Microchip, but unable to obtain them from Microchip’s webside directly we found a project by Syed Tahmid Mahbub which used them. This library was already modified by Syed Tahmid Mahbub to be compatible with protothreads as well as a modification for lack of CD and WE from the microSD card, as well as the pins and connections for his project. We modified to use CD from microSD card, as well as for our specific connections which include TFT. The main function initializes the TFT and SD card, then draws an intersection with 5 buttons: PLAY, UNDO, CLEAR, STORE (to SD card), and LOAD (to SD card). The function then begins two threads: a touch thread and a gesture thread.
The touch thread detects the locations where the user touches the TFT, and the thread runs forever. A state machine DRAG_STATE is used to switch the state of the TFT between WAITING, TOUCHING, and DONE. While a touch point is not registered, a while loop within the touch thread waits for an input. The while loop breaks when a touch point is found and, if the state is WAITING, the most recent point found is determined to be the initial point of the gesture. The state of the state machine is also changed to TOUCHING. While the user is still touching the screen, the while loop mentioned above continuously gets the touch point. Once the TFT no longer detects a user touching the screen for 5 iterations of the while loop, the final point is determined to be the last valid point captured. The state is changed to DONE.
The gesture thread interprets the touch data from the touch thread, and it runs forever. The gesture thread yields until it detects that the state of the state machine DRAG_STATE is DONE. A function getGesture is called with the initial and final points from the touch thread. The getGesture function checks if the initial and final points are within the intersection. If they are, then the function returns a value from 0-15, which signifies one of the 16 combinations of entry and exit from the intersection. Otherwise, the function returns GESTURE_ILLEGAL with a value of 16. GESTURE_ILLEGAL is reserved for the various buttons on the screen.
If the gesture was not illegal, then the user is informed which gesture was drawn and the gesture is added to an int array called gestures. If the gesture was illegal, then the gesture thread determines if a button was pushed, in the same way that getGesture recognizes a gesture. The UNDO button will remove the last gesture from the gestures array and print on the TFT either the gesture that was deleted or that no gestures were found. The CLEAR button will remove all gestures from the gestures array and print on the TFT either the array was emptied or that no gestures were found. These two functions are simply implemented by changing the index of the next empty gesture, gInd, to 0.
The PLAY button will play all gestures stored in the gestures array up to, but not including, the gesture at index gInd. The program will print on the TFT either the gesture that was most recently played or that no gestures were found. To play the gestures, a function gesture_draw() is called on each gesture in the gestures array. As a sidenote, originally gesture_draw was going to be its own separate thread instead of a function, which explains our choice of global variables. Since each gesture follows a different path, gesture_draw does a case-by-case analysis of all gestures. The movement is shown by drawing new circles which move in the direction of the gesture and erasing old circles once there are 100 circles on screen.
The STORE button will write all gestures stored in the gestures array into a text file, inter.txt, on the SD card. The first four digits indicate the number of gestures stored in the gestures array, padded with zeros. Every subsequent pair of digits describes a gesture, from 00 to 15, denoting the 16 possible movements in the 4-way intersection. The user can now clear the gestures array without fearing for lost data.
The LOAD button will load all gestures stored on the SD card into the gestures array. The first four digits indicate the number of gestures stored in the gestures array, which is set equal to the position of the pointer of the next empty index in the gestures array. Every subsequent pair of digits describes a gesture, and the program loops over those digits and adds them to the gestures array. The user can now playback the newly loaded gestures, if desired.
Results of the design
The program would respond in less than 250 ms to user input. Playback took 500 ms per gesture, which is slow enough that it is easy to see the data, yet fast enough to keep the user’s attention. Sometimes a touch would not be registered or the wrong gesture would be recorded, but the UNDO button and the constant prints to screen would help ensure that the user is inputting the correct values. There is no potential for interference with any external signals or noise. The design is inexpensive, intuitive, and efficient, with potential for use in academia and in industry.
Conclusions
We expected the TFT to be able to take in touch data and for the SD card to store the touch data. We also expected efficient playback of our gestures input. The results were consistent with our expectations. Next time, we would try to make the text files written to the SD card easier to read, as it is currently a single sequence of digits. Other avenues of improvement include assigning timestamps for vehicle entry and exit, adding traffic lights and optimizing throughput, storing multiple files in the SD card and adding a UI for file creation on the TFT, and parsing real traffic data for replay on the TFT. The libraries used in this code were developed by Syed Tahmid Mahbub, and they are publicly available on his blog. No legal, safety, or ethical concerns apply.
code
/* * File: TFT, touchscreen, SD card * Author: Carlos Contreras, Kevin Chaudhari * Using touch screen library by Syed Tahmid Mahbub * http://tahmidmc.blogspot.com/2015/08/pic32-tic-tac-toe-demonstration-of.html * For use with Sean Carroll's Small Board * http://people.ece.cornell.edu/land/courses/ece4760/PIC32/target_board.html * Target PIC: PIC32MX250F128B * SD Card implementation based off: SYED TAHMID MAHBUB ? PIC32 based audio player with microSD card and 12-bit audio out * http://tahmidmc.blogspot.com/2015/01/audio-player-using-pic32-microsd-card.html */ /* SPI pins configuration for SD Card: * Using SPI2 (defined in HardwareProfile.h) * SDO: RA1 (TRIS defined in HardwareProfile.h and PPS defined in SD-SPI.c) * physical pin 3 * SDI: RA2 (TRIS defined in HardwareProfile.h and PPS defined in SD-SPI.c) * physical pin 9 * SCK: RB14 (TRIS defined in HardwareProfie.h) * physical pin 25 * CS: RB3 (defined in HardwareProfile.h) * physical pin 7 * CD: RA3 (defined in HardwareProfile.h) * physical pin 10 * WE: RB4 (defined in HardwareProfile.h) * physical pin 17 * SD card breaktout doesn't have WE pin - overriden in code * * * SPI Pins configuration for TFT * Using SPI 1 * * SDO: RB11 (TRIS defined in tft_master_fp.h and PPS defined in tft_master_fp.c) * physical pin 22 * SDI: RB8 * physical pin 17 * SCK: RB15 (Defined in tft_master_fp.h) * physical pin 26 * CS: RB1 (TRIS defined in tft_master_fp.h) * physical pin 5 * D/C: RB0 (TRIS defined in tft_master_fp.h) * physical pin 4 * RST: RB2 (TRIS defined in tft_master_fp.h) * physical pin 6 * * Resistive Touchscreen Pins * Digital: * X+: RB10 (Defined in touch_master.h) * physical pin 21 * Y+: RB9 (Defined in touch_master.h) * physical pin 18 * Analog: * X-: RB13 AN11 (Defined in touch_master.h) * physical pin 24 * Y-: RA0 AN0 (Defined in touch_master.h) * physical pin 2 * * */ //////////////////////////////////// // clock AND protoThreads configure! // You MUST check this file! // TEST OLD CODE WITH NEW THREADS #include "config_1_3_2.h" // threading library #include "pt_cornell_1_3_2.h" //////////////////////////////////// // graphics libraries // SPI channel 1 connections to TFT #include "tft_master_fp.h" #include "tft_gfx_fp.h" #include "touch_master.h" //SD Card #include "FSIO.h" // need for rand function #include <stdlib.h > // need for sin function #include < math.h & gt #include < stdio.h > //////////////////////////////////// /* Demo code for interfacing TFT (ILI9341 controller) to PIC32 * The library has been modified from a similar Adafruit library */ // Adafruit data: /*************************************************** This is an example sketch for the Adafruit 2.2" SPI display. This library works with the Adafruit 2.2" TFT Breakout w/SD card ----> http://www.adafruit.com/products/1480 Check out the links above for our tutorials and wiring diagrams These displays use SPI to communicate, 4 or 5 pins are required to interface (RST is optional) Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution ****************************************************/ /* * */ /*DRAG_STATE: refers to current state regarding if touch screen is being touched * or not. * STATE_WAITING: currently waiting for user to initiate a gesture * STATE_TOUCHING: store first point of touch, remain on this state as long as * there is a constant valid touch input * STATE_DONE: user has stopped touching the screen, store final point of touch * Determine the gesture input * */ #define STATE_WAITING 0 #define STATE_TOUCHING 1 #define STATE_DONE 2 volatile int DRAG_STATE; /*Gestures * First letter represents start * Second letter represents end * L = left = 0 * R = right = 1 * D = down = 2 * U = up = 3 * * Value of gesture: initial touch point shall be GS, final touch point GE * GS*4 + GE * Example: RU = R*4 + U = 7 */ #define GESTURE_LL 0 #define GESTURE_LR 1 #define GESTURE_LD 2 #define GESTURE_LU 3 #define GESTURE_RL 4 #define GESTURE_RR 5 #define GESTURE_RD 6 #define GESTURE_RU 7 #define GESTURE_DL 8 #define GESTURE_DR 9 #define GESTURE_DD 10 #define GESTURE_DU 11 #define GESTURE_UL 12 #define GESTURE_UR 13 #define GESTURE_UD 14 #define GESTURE_UU 15 #define GESTURE_ILLEGAL 16 //Directions for turning on Play mode #define DIRECTION_LEFT -1 #define DIRECTION_RIGHT 1 //Top is 0, bottom is 319 #define DIRECTION_UP -1 #define DIRECTION_DOWN 1 #define DIRECTION_NONE 0 // string buffer char buffer[60]; //Gestures storage array, init to Illegal gesture int gestures[1024] = {16}; //Gestures index, as well as how many gestures in gestures storage array volatile int gInd = 0; /*File pointer for SD card * FAT 32 format * Program will save and load from inter.txt in SD card root folder * First four characters indicate the amount of gestures from 0000 to 1024 * Following are gestures in sets of two characters from 00 to 15 * Example of file text: * 00050012051402 * gInd = 5 * Gestures: 00, 12, 05, 14, 02 */ FSFILE * pointer; //Protothreads static struct pt pt_gesture, pt_touch; /*Touch points for obtaining gestures * initial_point: first point of touch * last_point: last point of touch at a moment, used to determine if still being touched * final_point: actual final point of touch before screen is stopped being pressed */ volatile point_t initial_point; volatile point_t last_point; volatile point_t final_point; //Gesture to draw, initial is illegal gesture volatile int drawGesture = 16; /*Draw gestures variables * done: has a gesture finished being drawn? * Idea is to draw a line, that erases itself as well * x, y: coordinates of where to draw * xclr, yclr: coordinate of where to erase, follows x, y at 100 pixels behind * xdir, ydir, xclrdir, yclrdir: direction the line (and its erasure) is moving */ static int done; static int x; static int y; static int xclr; static int yclr; static int xdir; static int ydir; static int xclrdir; static int yclrdir; // === print a line on TFT ===================================================== // Utilities to print a line on the TFT // Predefined colors definitions (from tft_master.h) //#define ILI9340_BLACK 0x0000 //#define ILI9340_BLUE 0x001F //#define ILI9340_RED 0xF800 //#define ILI9340_GREEN 0x07E0 //#define ILI9340_CYAN 0x07FF //#define ILI9340_MAGENTA 0xF81F //#define ILI9340_YELLOW 0xFFE0 //#define ILI9340_WHITE 0xFFFF void printLine(int line_number, char* print_buffer, short text_color, short back_color){ // line number 0 to 31 /// !!! assumes tft_setRotation(0); // print_buffer is the string to print int v_pos; v_pos = line_number * 10 ; // erase the pixels tft_fillRoundRect(0, v_pos, 239, 8, 1, back_color);// x,y,w,h,radius,color tft_setTextColor(text_color); tft_setCursor(0, v_pos); tft_setTextSize(1); tft_writeString(print_buffer); } void printLine2(int line_number, char* print_buffer, short text_color, short back_color){ // line number 0 to 31 /// !!! assumes tft_setRotation(0); // print_buffer is the string to print int v_pos; v_pos = line_number * 20 ; // erase the pixels tft_fillRoundRect(0, v_pos, 239, 16, 1, back_color);// x,y,w,h,radius,color tft_setTextColor(text_color); tft_setCursor(0, v_pos); tft_setTextSize(2); tft_writeString(print_buffer); } /*Using the start and end touch points determine the gesture for the appropriate * car movement on the intersection */ int getGesture(point_t start, point_t end){ /*Touch input does not extend from 0 to 1023 ADC units * After testing multiple testing, these were the ranges for it. * 320<x<600 * 280<y<650 */ int gesture = GESTURE_ILLEGAL; //Gesture start int startG; //Gesture End int endG; if(start.X<413 && start.Y >400 && start.Y<520 ) // Left startG = 0; else if(start.X>506 && start.Y>400 && start.Y<520 ) // Right startG = 1; else if(start.Y>520 && start.X>413 && start.X<506 ) // Bottom startG = 2; else if(start.Y<400 && start.X>413 && start.X<506 ) // Top startG = 3; //Illegal start point: corners or center else return gesture; if(end.X<413 && end.Y >330 && end.Y<600 ) endG = 0; else if(end.X>506 && end.Y>330 && end.Y<600 ) endG = 1; else if(end.Y>520 && end.X>413 && end.X<506 ) endG = 2; else if(end.Y<400 && end.X>413 && end.X<506 ) endG = 3; //Illegal end point: corners or center else return gesture; gesture = startG*4 + endG; gestures[gInd] = gesture; gInd = (gInd + 1) % 1024; return gesture; } //Draw intersection and corner "buttons" void drawIntersection(){ tft_drawFastVLine(79,0, 106, ILI9341_WHITE); tft_drawFastVLine(79,214, 106, ILI9341_WHITE); tft_drawFastVLine(159,0, 106, ILI9341_WHITE); tft_drawFastVLine(159, 214, 106, ILI9341_WHITE); tft_drawFastHLine(0, 105, 80, ILI9341_WHITE); tft_drawFastHLine(159, 105, 80, ILI9341_WHITE); tft_drawFastHLine(0, 214, 80, ILI9341_WHITE); tft_drawFastHLine(159, 214, 80, ILI9341_WHITE); // undo button tft_fillRect(160, 215, 79, 105, ILI9341_RED); tft_setTextColor(ILI9341_WHITE); tft_setCursor(180, 260); tft_setTextSize(2); tft_writeString("UNDO"); // clear button tft_fillRect(160, 0, 79, 105, ILI9341_ORANGE); tft_setTextColor(ILI9341_WHITE); tft_setCursor(170, 45); tft_setTextSize(2); tft_writeString("CLEAR"); // store button tft_fillRect(0, 0, 79, 52, ILI9341_BLUE); tft_setTextColor(ILI9341_WHITE); tft_setCursor(10, 25); tft_setTextSize(2); tft_writeString("STORE"); // load button tft_fillRect(0, 53, 79, 52, ILI9341_MAGENTA); tft_setTextColor(ILI9341_WHITE); tft_setCursor(10, 65); tft_setTextSize(2); tft_writeString("LOAD"); // play button tft_fillRect(0, 215, 79, 105, ILI9341_GREEN); tft_setTextColor(ILI9341_WHITE); tft_setCursor(20, 260); tft_setTextSize(2); tft_writeString("PLAY"); } //Draws a gesture for play mode void gesture_draw() { switch(drawGesture){ case 0: //LL //draw u-turn done = 0; x = 0; y = 200; xclr = -100; yclr = 200; xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x>=0) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(x + xdir>119){ xdir = DIRECTION_NONE; ydir = DIRECTION_UP; } if(xclr + xclrdir> 119){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; } if(y + ydir<120){ xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; } if(yclr + yclrdir<120){ xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr<0 && xclrdir == DIRECTION_LEFT) done = 1; } break; case 1: //LR done = 0; x = 0; y = 200; xclr = -100; yclr = 200; xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x>=0 && x < = 239) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0 && xclr < = 239) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr>239) done = 1; } break; case 2: //LD done = 0; x = 0; y = 200; xclr = -100; yclr = 200; xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x>=0 && y < = 319) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0 && yclr < = 319) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(x + xdir>145){ xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; } if(xclr + xclrdir> 145){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr>319) done = 1; } break; case 3: //LU done = 0; x = 0; y = 200; xclr = -100; yclr = 200; xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x>=0 && y > =20) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0 && yclr > =0) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(x + xdir>145){ xdir = DIRECTION_NONE; ydir = DIRECTION_UP; } if(xclr + xclrdir> 145){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr<20) done = 1; } break; case 4: //RL done = 0; x = 239; y = 120; xclr = 339; yclr = 120; xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x > =0 && x < = 239) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0 && xclr < = 239) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr<0) done = 1; } break; case 5: //RR done = 0; x = 239; y = 120; xclr = 339; yclr = 120; xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x > =0 && x < = 239) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0 && xclr < = 239) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(x + xdir<129){ xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; } if(xclr + xclrdir<129){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; } if(y + ydir>200){ xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; } if(yclr + yclrdir>200){ xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr>239 && xclrdir == DIRECTION_RIGHT) done = 1; } break; case 6: //RD done = 0; x = 239; y = 120; xclr = 339; yclr = 120; xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x>=0 && y < = 319) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0 && yclr < = 319) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(x + xdir<95){ xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; } if(xclr + xclrdir<95){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr>319) done = 1; } break; case 7: //RU done = 0; x = 239; y = 120; xclr = 339; yclr = 120; xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; while(done == 0){ if(x>=0 && y > =20) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr > =0 && yclr > =0) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(x + xdir<95){ xdir = DIRECTION_NONE; ydir = DIRECTION_UP; } if(xclr + xclrdir<95){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr<20) done = 1; } break; case 8: //DL done = 0; x = 145; y = 319; xclr = 145; yclr = 419; xdir = DIRECTION_NONE; ydir = DIRECTION_UP; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; while(done == 0){ if(x>0 && y < = 319) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr>0 && yclr < = 319) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(y + ydir<120){ xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; } if(yclr + yclrdir<120){ xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr<0) done = 1; } break; case 9: //DR done = 0; x = 145; y = 319; xclr = 145; yclr = 419; xdir = DIRECTION_NONE; ydir = DIRECTION_UP; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; while(done == 0){ if(x < = 239 && y < = 319) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr < = 239 && yclr < = 319) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(y + ydir<200){ xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; } if(yclr + yclrdir<200){ xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr>239) done = 1; } break; case 10: //DD done = 0; x = 145; y = 319; xclr = 145; yclr = 419; xdir = DIRECTION_NONE; ydir = DIRECTION_UP; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; while(done == 0){ if(y < = 319) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(yclr < = 319) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(y + ydir<200){ xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; } if(yclr + yclrdir<200){ xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; } if(x + xdir<95){ xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; } if(xclr + xclrdir<95){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr>319 && yclrdir == DIRECTION_DOWN) done = 1; } break; case 11: //DU done = 0; x = 145; y = 319; xclr = 145; yclr = 419; xdir = DIRECTION_NONE; ydir = DIRECTION_UP; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; while(done == 0){ if(y > =20) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(yclr > =0) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr<20) done = 1; } break; case 12: //UL done = 0; x = 95; y = 20; xclr = 95; yclr = -80; xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; while(done == 0){ if(x>0 && y >=20) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr>0 && yclr > =20) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(y + ydir>120){ xdir = DIRECTION_LEFT; ydir = DIRECTION_NONE; } if(yclr + yclrdir>120){ xclrdir = DIRECTION_LEFT; yclrdir = DIRECTION_NONE; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr < 0) done = 1; } break; case 13: //UR done = 0; x = 95; y = 20; xclr = 95; yclr = -80; xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; while(done == 0){ if(x < = 239 && y > =20) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(xclr < = 239 && yclr > =20) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(y + ydir > 200){ xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; } if(yclr + yclrdir > 200){ xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(xclr>239) done = 1; } break; case 14: //UD done = 0; x = 95; y = 20; xclr = 95; yclr = -80; xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; while(done == 0){ if(y >=20 && y < = 319) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(yclr >=20 && yclr < = 319) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr>319) done = 1; } break; case 15: //UU done = 0; x = 95; y = 20; xclr = 95; yclr = -80; xdir = DIRECTION_NONE; ydir = DIRECTION_DOWN; xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_DOWN; while(done == 0){ if(y > =20) tft_drawCircle(x, y, 2, ILI9341_CYAN); if(yclr > =20) tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK); if(y + ydir>120){ xdir = DIRECTION_RIGHT; ydir = DIRECTION_NONE; } if(yclr + yclrdir>120){ xclrdir = DIRECTION_RIGHT; yclrdir = DIRECTION_NONE; } if(x + xdir>145){ xdir = DIRECTION_NONE; ydir = DIRECTION_UP; } if(xclr + xclrdir>145){ xclrdir = DIRECTION_NONE; yclrdir = DIRECTION_UP; } x += xdir; y += ydir; xclr += xclrdir; yclr += yclrdir; if(yclr < 20 && yclrdir == DIRECTION_UP) done = 1; } break; } } /* Maintains gesture state * Get gesture based of initial and final points * If the gesture is not a valid traffic flow, determine if it was a "button" press * In case of button press, do action of the button */ static PT_THREAD (protothread_gesture(struct pt *pt)) { PT_BEGIN(pt); while(1) { // yield time 1 second PT_YIELD_TIME_msec(32) ; if(DRAG_STATE==STATE_DONE){ //Obtain a gesture based of the initial and final point int gestureVal = getGesture(initial_point, final_point); //Gesture if (gestureVal == GESTURE_ILLEGAL) { //Undo Button if ((initial_point.X > 506 && initial_point.Y > 520) && (final_point.X > 506 && final_point.Y>520)){ if ((gInd-1)%1024 > =0) { gInd=(gInd-1)%1024; #ifdef DEBUG_GESTURE sprintf(buffer,"Removing Gesture=%d", gestures[gInd]); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif gestures[gInd] = 16; } else { sprintf(buffer,"No gestures found"); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); } } //Play back all gestures in storage else if ((initial_point.X < 413 && initial_point.Y > 520) && (final_point.X < 413 && final_point.Y>520)){ if (gInd>0) { static int gi; for (gi = 0; gi < gInd; gi++){ sprintf(buffer,"Playing Gesture=%d", gestures[gi]); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); drawGesture = gestures[gi]; gesture_draw(); PT_YIELD_TIME_msec(250) ; } } else { sprintf(buffer,"No gestures found"); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); } } //Clear Gestures in storage else if ((initial_point.X > 506 && initial_point.Y < 400) && (final_point.X>506 && final_point.Y < 400)){ gInd= 0; sprintf(buffer,"Clearing Gestures"); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); } //Store from SD Card else if ((initial_point.X < 413 && initial_point.Y < 330) && (final_point.X<413 && final_point.Y<330)){ #ifdef DEBUG_GESTURE sprintf(buffer,"Storing to SD Card"); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif //Either it creates or overwrites inter.txt if it already exists pointer = FSfopen("inter.txt", FS_WRITE); if(gInd < = 0){ FSfprintf(pointer,"0000"); } else{ int ii; if(gInd < 10) FSfprintf(pointer, "000%i", gInd); else if(gInd < 100) FSfprintf(pointer, "00%i", gInd); //FSfprintf(pointer, "%i\n", gInd); else if(gInd < 1000) FSfprintf(pointer, "0%i", gInd); else FSfprintf(pointer, "%i", gInd); for(ii = 0; ii < gInd; ii++){ if(gestures[ii] > 9) FSfprintf(pointer, "%i", gestures[ii]); else FSfprintf(pointer, "0%i", gestures[ii]); } } FSfclose(pointer); } //Load from SD Card else if ((initial_point.X < 413 && initial_point.Y < 400) && (final_point.X<413 && final_point.Y<400)){ sprintf(buffer,"Loading to SD Card"); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); pointer = FSfopen("inter.txt", FS_READ); if(pointer != NULL){ char gindin[2]; int gind; FSfread(gindin, 2, 2, pointer); sscanf(gindin, "%d", &gind); #ifdef DEBUG_SD_LOAD sprintf(buffer, "stored gind:%d", gind);//gind); printLine2(7, buffer, ILI9341_BLACK, ILI9341_YELLOW); sprintf(buffer, gindin); printLine2(8, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif int ii; for(ii = 0; ii < gInd; ii++){ gestures[ii] = 16; } gInd = gind; char junk[1]; char ges_raw[1]; int ges; for(ii = 0; ii < gInd; ii++){ FSfread(ges_raw, 2, 1, pointer); sscanf(ges_raw, "%d", &ges); gestures[ii]=ges; #ifdef DEBUG_SD_LOAD sprintf(buffer, "%d", ges);//gind); printLine2(ii, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif } } FSfclose(pointer); } } else { #ifdef DEBUG_GESTURE sprintf(buffer,"Gesture=%d", gestureVal); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif } DRAG_STATE = STATE_WAITING; } // NEVER exit while } // END WHILE(1) PT_END(pt); } /*Thread to read touch points */ static PT_THREAD (protothread_touch(struct pt *pt)) { PT_BEGIN(pt); while(1) { PT_YIELD_TIME_msec(32) ; touch_reset(); static int last_valid_touch = 0; //Waiting for touch input to be processed as gesture while(DRAG_STATE == STATE_DONE){ PT_YIELD_TIME_msec(5); } do{ // until touch is registered and holds a valid point PT_YIELD_TIME_msec(5); touch_getPoint(); //Debounce purposes, verify that there is no longer a touch if(DRAG_STATE == STATE_TOUCHING && last_valid_touch>5){ if(touch_point.DONE == 0 || touch_point.Z == 0){ //touch point still being detected or touch not registered DRAG_STATE = STATE_DONE; final_point = last_point; #ifdef DEBUG_TOUCH sprintf(buffer,"Final x=%d, y =%d", final_point.X, final_point.Y); printLine2(3, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif } } last_valid_touch+=1; #ifdef DEBUG_TOUCH sprintf(buffer,"Last valid touch: %d", last_valid_touch); printLine2(6, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif }while(touch_point.DONE == 0 || touch_point.Z == 0); last_point = touch_point; //Began touching screen, save initial point, change state if(DRAG_STATE == STATE_WAITING){ DRAG_STATE = STATE_TOUCHING; initial_point = touch_point; } last_valid_touch = 0; #ifdef DEBUG_TOUCH sprintf(buffer,"x=%d, y =%d, z =%d", touch_point.X, touch_point.Y, touch_point.Z); printLine2(1, buffer, ILI9341_BLACK, ILI9341_YELLOW); sprintf(buffer,"Start x=%d, y =%d", initial_point.X, initial_point.Y); printLine2(2, buffer, ILI9341_BLACK, ILI9341_YELLOW); sprintf(buffer,"Last x=%d, y =%d", (last_point).X, (last_point).Y); printLine2(4, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif // NEVER exit while } // END WHILE(1) PT_END(pt); } // timer thread int main(int argc, char** argv) { //SYSTEMConfigPerformance(PBCLK); ANSELA = 0; ANSELB = 0; // === setup system wide interrupts ======== INTEnableSystemMultiVectoredInt(); // === TFT setup ============================ // init the display in main since more than one thread uses it. // NOTE that this init assumes SPI channel 1 connections tft_init_hw(); tft_begin(); tft_fillScreen(ILI9341_BLACK); //240x320 vertical display tft_setRotation(0); // Use tft_setRotation(1) for 320x240 touch_init(); touch_reset(); last_point = touch_point; DRAG_STATE = STATE_WAITING; // === config threads ========== // turns OFF UART support and debugger pin, unless defines are set PT_setup(); sprintf(buffer,"Booting up"); printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW); //SD Card Init while (!MDD_MediaDetect()); #ifdef DEBUG_FSINIT sprintf(buffer,"Starting FS Init"); printLine2(1, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif while (!FSInit()); #ifdef DEBUG_FSINIT sprintf(buffer,"FS init: success"); printLine2(2, buffer, ILI9341_BLACK, ILI9341_YELLOW); #endif tft_fillScreen(ILI9341_BLACK); drawIntersection(); while (1){ PT_SCHEDULE(protothread_gesture(&pt_gesture)); PT_SCHEDULE(protothread_touch(&pt_touch)); } }