diff --git a/README.md b/README.md index e12b80e..24d2b6d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ # discoverpixy -Project with the Pixy cam and the STM32F4 Discovery +A Project with the Pixy cam and the STM32F4 Discovery. The project can also be run on linux/windows using a qt-based emulator. + +![Pixy Logo](doc/pixy.png) + + + +## Documentation +Take a look at our [docu.pdf](./doc/docu.pdf) (German) ## Folder structure * *common*: device independent code and the "Application" itself diff --git a/common/app/pixy_control.c b/common/app/pixy_control.c new file mode 100644 index 0000000..c30418d --- /dev/null +++ b/common/app/pixy_control.c @@ -0,0 +1,69 @@ +/* + * pixy_control.c + * + * Notation + * -------- + * + * x : Sollwert (Führgrösse) + * w : Istwert (Reglergrösse) + * esum : Integralteil + * e : Regelabweichung + * y : Stellgrösse + * + * + */ +#include +#include + +// PID tuning factors +#define REG_PID_KP (0.5f) +#define REG_PID_KI (0.001f) +#define REG_PID_KD (0.001f) +#define REG_PID_TA (0.01f) + + +// PID controller implementatoin for the y-axis +int16_t pixy_PID_Y(int16_t x, int16_t w) +{ + float e = 0; + static float esum = 0; + static float eold = 0; + float y = 0; + + e = (float)(x - w); // calculate the controller offset + + //----PID-control------------------------------------------------------------------------- + esum = esum + e; // add e to the current sum + + y += REG_PID_KP * e; // add the proportional part to the output + y += REG_PID_KI * REG_PID_TA * esum; // add the integral part to the output + y += REG_PID_KD * (e - eold) / REG_PID_TA; // add the differential part to the output + //---------------------------------------------------------------------------------------- + + eold = e; // save the previous value + + return (int16_t)y; +} + +// PID controller implementation for the x-axis +int16_t pixy_PID_X(int16_t x, int16_t w) +{ + float e = 0; + static float esum = 0; + static float eold = 0; + float y = 0; + + e = (float)(x - w); // calculate the controller offset + + //----PID-control------------------------------------------------------------------------- + esum = esum + e; // add e to the current sum + + y += REG_PID_KP * e; // add the proportional part to the output + y += REG_PID_KI * REG_PID_TA * esum; // add the integral part to the output + y += REG_PID_KD * (e - eold) / REG_PID_TA; // add the differential part to the output + //---------------------------------------------------------------------------------------- + + eold = e; // save the previous value + + return (int16_t)y; +} diff --git a/common/app/pixy_control.h b/common/app/pixy_control.h new file mode 100644 index 0000000..708cac4 --- /dev/null +++ b/common/app/pixy_control.h @@ -0,0 +1,41 @@ +#ifndef PIXY_CONTROL_H_ +#define PIXY_CONTROL_H_ + +#include + +/** + * @addtogroup app + */ +/*@{*/ + +/** + * @defgroup pixy_control Pixy Control Helper + * A collection of helper functions that contain PID Control code used for object tracking. + */ +/*@}*/ + + +/** + * @addtogroup pixy_control + */ +/*@{*/ + +/** + * Execute one step of PID regulation for the Y-servo. + * @param x desired value (e.g. current y-position of the biggest block) + * @param w actual value (e.g desired y-position) + * @return The offset which needs to be added to the Y-Servo position + */ +int16_t pixy_PID_Y(int16_t x, int16_t w); + +/** + * Execute one step of PID regulation for the X-servo. + * @param x desired value (e.g. current x-position of the biggest block) + * @param w actual value (e.g desired x-position) + * @return The offset which needs to be added to the X-Servo position + */ +int16_t pixy_PID_X(int16_t x, int16_t w); + +/*@}*/ + +#endif /* PIXY_CONTROL_H_ */ diff --git a/common/app/pixy_helper.c b/common/app/pixy_frame.c similarity index 99% rename from common/app/pixy_helper.c rename to common/app/pixy_frame.c index d137d71..8c7dc0d 100644 --- a/common/app/pixy_helper.c +++ b/common/app/pixy_frame.c @@ -1,4 +1,4 @@ -#include "pixy_helper.h" +#include "pixy_frame.h" #include "pixy.h" #include "tft.h" #include diff --git a/common/app/pixy_helper.h b/common/app/pixy_frame.h similarity index 87% rename from common/app/pixy_helper.h rename to common/app/pixy_frame.h index 7f7cd40..5f70b42 100644 --- a/common/app/pixy_helper.h +++ b/common/app/pixy_frame.h @@ -1,10 +1,30 @@ -#ifndef PIXY_HELPER_H -#define PIXY_HELPER_H +#ifndef PIXY_FRAME_H +#define PIXY_FRAME_H #include #include #include "filesystem.h" + +/** + * @addtogroup app + */ +/*@{*/ + +/** + * @defgroup pixy_helper Pixy Frame Helper + * A collection of helper functions that allow receiving and rendering a frame from pixy onto the display. + * Furthermore you can select a color in a frame, to use for tracking. + */ +/*@}*/ + + +/** + * @addtogroup pixy_helper + */ +/*@{*/ + + /** * Receives a fullsized frame from pixy and display's it on the display with the topleft corner at (x,y) * @param x The x-Coordinate of the top left corner @@ -56,4 +76,6 @@ int pixy_save_cropped_frame(FILE_HANDLE* handle, uint16_t xoffset, uint16_t yoff */ int pixy_cc_set_region(uint8_t signum, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height); -#endif /* PIXY_HELPER_H */ +/*@}*/ + +#endif /* PIXY_FRAME_H */ diff --git a/common/app/screen_photomode.c b/common/app/screen_photomode.c index eb41ed5..01232f4 100644 --- a/common/app/screen_photomode.c +++ b/common/app/screen_photomode.c @@ -5,7 +5,7 @@ #include "touch.h" #include "pixy.h" #include "system.h" -#include "pixy_helper.h" +#include "pixy_frame.h" static bool pixy_connected = false; //Whether or not the pixy cam is currently connected diff --git a/common/app/screen_photomode_save.c b/common/app/screen_photomode_save.c index d05c4c8..e00f8dd 100644 --- a/common/app/screen_photomode_save.c +++ b/common/app/screen_photomode_save.c @@ -4,7 +4,7 @@ #include "tft.h" #include "touch.h" #include "pixy.h" -#include "pixy_helper.h" +#include "pixy_frame.h" #include #include diff --git a/common/app/screen_pixytest.c b/common/app/screen_pixytest.c index 05b5402..8b7b4cd 100644 --- a/common/app/screen_pixytest.c +++ b/common/app/screen_pixytest.c @@ -5,7 +5,7 @@ #include "touch.h" #include "pixy.h" #include "system.h" -#include "pixy_helper.h" +#include "pixy_frame.h" static volatile enum {detecting, idle,update_servos, update_ledcolor, update_ledcurrent} state; //Current state of the screen state machine diff --git a/common/app/screen_tracking.c b/common/app/screen_tracking.c index a1066d2..cf6af59 100644 --- a/common/app/screen_tracking.c +++ b/common/app/screen_tracking.c @@ -1,11 +1,12 @@ #include "screen_tracking.h" +#include "pixy_control.h" #include "button.h" #include "checkbox.h" #include "tft.h" #include "touch.h" #include "pixy.h" #include "system.h" -#include "pixy_helper.h" +#include "pixy_frame.h" static BUTTON_STRUCT b_back; //Button to navigate back static BUTTON_STRUCT b_select; //Button to start the color region selection @@ -80,12 +81,19 @@ typedef struct { } TRACKING_CONFIG_STRUCT; //Methods for our tracking implementation ahead +static int16_t servo_x = 0; +static int16_t servo_y = 0; //Method/Callback to start our tracking void tracking_our_start(void* tracking_config) { //Activate pixy's data send program int32_t response; int return_value; + + servo_x = servo_y = 500; // set a default value of 500 + pixy_rcs_set_position(0, servo_x); // set default + pixy_rcs_set_position(1, servo_y); // set default + return_value = pixy_command("runprog", INT8(0), END_OUT_ARGS, &response, END_IN_ARGS); } @@ -99,8 +107,31 @@ void tracking_our_stop(void* tracking_config) { //Method/Callback to calculate one step of our tracking void tracking_our_update(void* tracking_config, struct Block* blocks, int num_blocks) { - //TODO: Implement tracking! - //Calculate new servo pos and set the new servo pos + + if(num_blocks <= 0){ // Check if there are blocks available + return; // When there are none, do nothing + } + + uint16_t x = blocks[0].x; // Get x coordinate of the biggest object + uint16_t y = blocks[0].y; // Get y coordinate of the biggest object + + int16_t xset = 0; + int16_t yset = 0; + + xset = (servo_x + pixy_PID_X((FRAME_WIDTH / 2), x)); // calculate the PID output for x + yset = (servo_y - pixy_PID_Y((FRAME_HEIGHT / 2), y)); // calculate the PID output for y + + xset = (xset < 0) ? 0 : xset; // x lower boundary check + xset = (xset > 1000) ? 1000 : xset; // x upper boundary check + + yset = (yset < 0) ? 0 : yset; // y lower boundary check + yset = (yset > 1000) ? 1000 : yset; // y upper boundary check + + servo_x = xset; // update the global, static variable for x + servo_y = yset; // update the global, statuc variable for y + + pixy_rcs_set_position(0, servo_x); // set the new x position + pixy_rcs_set_position(1, servo_y); // set the new y position } //Variable which stores all the callbacks and settings for our tracking implementation diff --git a/common/gui/screen.c b/common/gui/screen.c index 1365a5e..8eb3072 100644 --- a/common/gui/screen.c +++ b/common/gui/screen.c @@ -6,6 +6,10 @@ * This makes it safe to change the screen from an touch interrupt (e.g. button callback) */ +/* Possible Improvements: + * Ensure that you can not navigate to a screen which is already in the history (because it will corrupt the list) + */ + static SCREEN_STRUCT* screen_list = NULL; //Head of the linked list which stores the screen history. static SCREEN_STRUCT* screen_current = NULL; //Pointer to the current screen (= tail of the list) static volatile SCREEN_STRUCT* screen_goto = NULL; //Screen we should navigate to once we enter the gui_screen_update() method again diff --git a/common/gui/screen.h b/common/gui/screen.h index fdef182..8ef82a2 100644 --- a/common/gui/screen.h +++ b/common/gui/screen.h @@ -12,7 +12,7 @@ /** * @defgroup screen Screen * The Screen Submodule provides an api to navigate between different "screens" on the UI. - * Each screen must provide an enter, update and a leave method; which will be called from this module at the right time. + * Each screen must provide an enter, update and a leave method; which will be called from this module at the right time. * The implemented screens of the application are documented in the \ref screens module. */ /*@}*/ @@ -44,6 +44,7 @@ typedef struct SCREEN_S{ /** * Navigate to the given screen as soon as the app enters the main loop again (and gui_screen_update() is called) * It's safe to call this method from an interrupt + * @note Do not pass a screen which is already in your history of screens! * @param screen A Pointer to the preinitialized SCREEN_STRUCT * @return true on success */ diff --git a/common/touch/touch.c b/common/touch/touch.c index 93eac9f..fbd9d5f 100644 --- a/common/touch/touch.c +++ b/common/touch/touch.c @@ -12,7 +12,6 @@ /* Possible improvements: * Exchange pointer-list "areas" with a linked list. This would ensure that we can always accept new regions - * Implement calibration stuff, and calculate the real coordinates out of the data provided in touch_add_raw_event() */ #define NUM_AREAS 50 //Number of Touch Areas we can manage diff --git a/discovery/src/ll_tft.c b/discovery/src/ll_tft.c index ba290e5..0b25998 100644 --- a/discovery/src/ll_tft.c +++ b/discovery/src/ll_tft.c @@ -273,7 +273,7 @@ static bool gpio_init() RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE); - // PORT_B init ------------------------------------------------------------------------------------- + // PORT_B init GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; @@ -282,7 +282,7 @@ static bool gpio_init() // configure PORT_B GPIO_Init(GPIOB, &GPIO_InitStructure); - // PORT_D init ------------------------------------------------------------------------------------- + // PORT_D init GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC); // PD0=FSMC_D2 -> DB2 GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC); // PD1=FSMC_D3 -> DB3 GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC); // PD4=FSMC_NOE -> RD @@ -305,7 +305,7 @@ static bool gpio_init() // configure PORT_D GPIO_Init(GPIOD, &GPIO_InitStructure); - // PORT_E init -------------------------------------------------------------------------------------- + // PORT_E init GPIO_PinAFConfig(GPIOE, GPIO_PinSource3, GPIO_AF_FSMC); // PE3=FSMC_A19 -> RS GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FSMC); // PE7=FSMC_D4 -> DB4 GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FSMC); // PE8=FSMC_D5 -> DB5 @@ -316,6 +316,7 @@ static bool gpio_init() GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FSMC); // PE13=FSMC_D10 -> DB12 GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FSMC); // PE14=FSMC_D11 -> DB13 GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FSMC); // PE15=FSMC_D12 -> DB14 + // PORT_E struct GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | @@ -325,6 +326,7 @@ static bool gpio_init() GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + // configure PORT_E GPIO_Init(GPIOE, &GPIO_InitStructure); @@ -334,6 +336,8 @@ static bool gpio_init() /* * ---------------------- display control functions ------------------------------------------------------- */ + +// Clear the whole screen by filling it with a specifig color void ll_tft_clear(uint16_t color) { uint32_t n = 0; @@ -346,6 +350,7 @@ void ll_tft_clear(uint16_t color) } } +// Set the cursorposition static void tft_set_cursor(uint16_t xpos, uint16_t ypos) { // set cursor @@ -354,61 +359,69 @@ static void tft_set_cursor(uint16_t xpos, uint16_t ypos) TFT_REG = TFT_SSD1289_REG_22; } +// Enable / Disable the backlight static void tft_set_backlight(bool state) { - if(state){ - GPIOB->BSRRH = GPIO_Pin_0; - } else { - GPIOB->BSRRL = GPIO_Pin_0; + if(state){ // if state is true + GPIOB->BSRRH = GPIO_Pin_0; // set the backlight output + } else { // else + GPIOB->BSRRL = GPIO_Pin_0; // reset the backlight } } +// Port operations on the screen RS PIN static void tft_reset(bool state) { - if(state){ - GPIOB->BSRRH = GPIO_Pin_0; - } else { - GPIOB->BSRRL = GPIO_Pin_0; + if(state){ // if state is ture + GPIOB->BSRRH = GPIO_Pin_0; // Set the reset pin + } else { // else + GPIOB->BSRRL = GPIO_Pin_0; // reset the reset pin } } +// Send a single command to the display controller static void tft_write_reg(uint8_t reg_adr, uint16_t reg_value) { - TFT_REG = reg_adr; - TFT_RAM = reg_value; + TFT_REG = reg_adr; // set adress + TFT_RAM = reg_value; // send command } +// Read a register value of the display controller static uint16_t tft_read_reg(uint8_t reg_adr) { - TFT_REG = reg_adr; - return TFT_RAM; + TFT_REG = reg_adr; // set adress + return TFT_RAM; // return value } +// This sets a window for current draw functions static void tft_set_window(uint16_t xstart, uint16_t ystart, uint16_t xend, uint16_t yend) { uint16_t start,end; uint16_t ystart_end; - start = (ystart & 0x00FF); - end = ((yend & 0x00FF) << 8); - ystart_end = (start | end); + start = (ystart & 0x00FF); // Start adress of the window + end = ((yend & 0x00FF) << 8); // End adress of the window + ystart_end = (start | end); // Calculate y endpoint - tft_write_reg(TFT_SSD1289_REG_44, ystart_end); - tft_write_reg(TFT_SSD1289_REG_45, 319-xend); - tft_write_reg(TFT_SSD1289_REG_46, 319-xstart); + tft_write_reg(TFT_SSD1289_REG_44, ystart_end); // Send y size + tft_write_reg(TFT_SSD1289_REG_45, 319-xend); // Send x start + tft_write_reg(TFT_SSD1289_REG_46, 319-xstart); // Send x end } +// Reset a Window void tft_reset_window() { - tft_write_reg(0x44,239<<8); - tft_write_reg(0x45,0); - tft_write_reg(0x46,319); + // Commands according to the datasheet + tft_write_reg(0x44, 239 << 8); + tft_write_reg(0x45, 0); + tft_write_reg(0x46, 319); } /* * ---------------------- draw functions ----------------------------------------------------------- */ +// Draw a line on the given coordinates void ll_tft_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { if(abs(x2-x1) > abs(y2-y1)) //line has more distance in x than y => iterate over x distance @@ -462,12 +475,14 @@ void ll_tft_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16 } } +// Draw a single pixel on (x,y) with the given color void ll_tft_draw_pixel(uint16_t x,uint16_t y,uint16_t color) { - tft_set_cursor(x,y); - TFT_RAM = color; + tft_set_cursor(x,y); // Set the cursor position + TFT_RAM = color; // Draw the pixel } +// Draw a rectangle at the given coordinates with the given color void ll_tft_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { unsigned int tmp; @@ -508,6 +523,7 @@ void ll_tft_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, u } +// Draw a filled rectangle at the given coordinates with the given color void ll_tft_fill_rectangle(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2, uint16_t color) { uint16_t area; diff --git a/doc/PID_REGELUNG.tgz b/doc/PID_REGELUNG.tgz deleted file mode 100644 index c1be5df..0000000 Binary files a/doc/PID_REGELUNG.tgz and /dev/null differ diff --git a/doc/PI_PD_REGELUNG.tgz b/doc/PI_PD_REGELUNG.tgz deleted file mode 100644 index 5f86a3d..0000000 Binary files a/doc/PI_PD_REGELUNG.tgz and /dev/null differ diff --git a/doc/Pinmap b/doc/Pinmap.txt similarity index 100% rename from doc/Pinmap rename to doc/Pinmap.txt diff --git a/doc/analyse_touch.jpg b/doc/analyse_touch.jpg new file mode 100644 index 0000000..7c329f7 Binary files /dev/null and b/doc/analyse_touch.jpg differ diff --git a/doc/datasheet_ssd1289.pdf b/doc/datasheet_ssd1289.pdf new file mode 100644 index 0000000..10ba9a3 Binary files /dev/null and b/doc/datasheet_ssd1289.pdf differ diff --git a/doc/docu.odt b/doc/docu.odt index f0c5685..e400a42 100644 Binary files a/doc/docu.odt and b/doc/docu.odt differ diff --git a/doc/docu.pdf b/doc/docu.pdf index 4894e60..4e45d7d 100644 Binary files a/doc/docu.pdf and b/doc/docu.pdf differ diff --git a/doc/idpa_software.tar.gz b/doc/idpa_software.tar.gz new file mode 100644 index 0000000..ba6a915 Binary files /dev/null and b/doc/idpa_software.tar.gz differ diff --git a/doc/mainscreen_emulator.png b/doc/mainscreen_emulator.png new file mode 100644 index 0000000..8bbbdfc Binary files /dev/null and b/doc/mainscreen_emulator.png differ diff --git a/doc/pixy.png b/doc/pixy.png new file mode 100644 index 0000000..f39b294 Binary files /dev/null and b/doc/pixy.png differ diff --git a/doc/screenshot_emulator.png b/doc/screenshot_emulator.png new file mode 100644 index 0000000..8fcf5ce Binary files /dev/null and b/doc/screenshot_emulator.png differ diff --git a/doc/touch_controller_responds_notouch.jpg b/doc/touch_controller_responds_notouch.jpg new file mode 100644 index 0000000..441171c Binary files /dev/null and b/doc/touch_controller_responds_notouch.jpg differ diff --git a/doc/touch_controller_responds_touch.jpg b/doc/touch_controller_responds_touch.jpg new file mode 100644 index 0000000..04f2b86 Binary files /dev/null and b/doc/touch_controller_responds_touch.jpg differ diff --git a/doc/tracking_emulator.png b/doc/tracking_emulator.png new file mode 100644 index 0000000..3b5a484 Binary files /dev/null and b/doc/tracking_emulator.png differ diff --git a/emulator/qt/ll_filesystem.cpp b/emulator/qt/ll_filesystem.cpp index a89e08d..8d1d47f 100644 --- a/emulator/qt/ll_filesystem.cpp +++ b/emulator/qt/ll_filesystem.cpp @@ -7,51 +7,53 @@ extern "C" { #include #include -QDir rootdir ("./emulated"); +QDir rootdir ("./emulated"); //Create a QDir which points to the "root" of the emulated filesystem bool ll_filesystem_init() { - if(!rootdir.exists()) { + if(!rootdir.exists()) { //if our root dir is nonexistent qWarning() << "Filesystem can not be emulated because the 'emulated' folder does not exist"; - return false; + return false; //mark an error } return true; } DIRECTORY_STRUCT* ll_filesystem_dir_open(const char* path) { - QDir d(rootdir); - d.cd(path); + QDir d(rootdir); //Make a copy of the rootdir QDir instance + d.cd(path); //Change the directory to the passed path if(!d.exists()) { - return NULL; + return NULL; //mark an error } DIRECTORY_STRUCT* directory = new DIRECTORY_STRUCT(); + //get all files and directories which are important to us. Filter out . and .. symlinks (linux) QFileInfoList entries = d.entryInfoList(QDir::NoDotAndDotDot|QDir::Files|QDir::Dirs|QDir::Readable|QDir::Writable|QDir::Hidden|QDir::System); + //Fill the directory structure for the user directory->path = path; directory->num_files = entries.count(); - directory->files = new FILE_STRUCT[directory->num_files]; + directory->files = new FILE_STRUCT[directory->num_files]; //allocate array of file structs - - for(int i=0; ifiles[i]); + FILE_STRUCT* entry = &(directory->files[i]); //get the pointer to the current filestruct (which should be filled) entry->fattrib = 0; - entry->fname = new char[fi.fileName().length()+1]; - strcpy(entry->fname,fi.fileName().toStdString().c_str()); - if(fi.isDir()) { - entry->fattrib|=F_DIR; + entry->fname = new char[fi.fileName().length()+1]; //reserve memory for filename + strcpy(entry->fname,fi.fileName().toStdString().c_str()); //copy filename into struct + if(fi.isDir()) { //it's a direcory + entry->fattrib|=F_DIR; //set directory attribute entry->fsize = 0; - } else { - entry->fsize = fi.size(); + } else { //it's a file + entry->fsize = fi.size(); //set filesize } - if(fi.isHidden()) { + if(fi.isHidden()) { //the file is hidden entry->fattrib|=F_HID; } - if(!fi.isWritable()) { - entry->fattrib|=F_RDO; + if(!fi.isWritable()) { //the file is not writable + entry->fattrib|=F_RDO; //set readonly attribue } + //Set date & time of file in structure QDateTime dt = fi.lastModified(); - entry->fdate.year = dt.date().year()-1980; + entry->fdate.year = dt.date().year()-1980; //year is realtive to 1980 entry->fdate.month = dt.date().month(); entry->fdate.day = dt.date().day(); entry->ftime.hour = dt.time().hour(); @@ -60,22 +62,22 @@ DIRECTORY_STRUCT* ll_filesystem_dir_open(const char* path) { } - return directory; + return directory; //return filled directory struct } void ll_filesystem_dir_close(DIRECTORY_STRUCT* dir) { - if(dir!=NULL) { - for(int i=0; inum_files; i++) { - delete dir->files[i].fname; + if(dir!=NULL) { //passed handle is valid + for(int i=0; inum_files; i++) { //foreach file + delete dir->files[i].fname; //delete filename buffer } - delete[] dir->files; - delete dir; + delete[] dir->files; //delete file array + delete dir; //delete structure itself } } - -struct QT_FILE_HANDLE : FILE_HANDLE { - QFile* file; +//Struct that represents a file handle for the emulator +struct QT_FILE_HANDLE : FILE_HANDLE { //..derived from the FILE_HANDLE (of the Filesystem modul) + QFile* file; //Pointer to the open QFile* instance }; @@ -83,17 +85,19 @@ FILE_HANDLE* ll_filesystem_file_open(const char* filename) { if(!rootdir.exists()) { return NULL; } - QString filepath = rootdir.absoluteFilePath(filename); - QFile* f = new QFile(filepath); - if(!f->exists()) { - return NULL; + QString filepath = rootdir.absoluteFilePath(filename); //get the absolute path to the requested file + QFile* f = new QFile(filepath); //create a QFile instance to the requested file + if(!f->exists()) { //File does not exist + return NULL; //mark error } - if(!f->open(QFile::ReadWrite)) { - return NULL; + if(!f->open(QFile::ReadWrite)) { //try to open the file, it it fails then ... + return NULL; //... mark error } - QT_FILE_HANDLE* fh = new QT_FILE_HANDLE(); + //At this point we have a valid QFile instance, pointing to an existing file + + QT_FILE_HANDLE* fh = new QT_FILE_HANDLE(); //Create Structure to return to user fh->file = f; fh->fname = filename; fh->fpos =0; @@ -102,12 +106,13 @@ FILE_HANDLE* ll_filesystem_file_open(const char* filename) { } void ll_filesystem_file_close(FILE_HANDLE* handle) { - if(handle!=NULL) { - QT_FILE_HANDLE* fh = static_cast(handle); - if(fh->file->isOpen()) { - fh->file->close(); + if(handle!=NULL) { //passed handle is valid + QT_FILE_HANDLE* fh = static_cast(handle); //cast pointer to QT_FILE_HANDLE + if(fh->file->isOpen()) { //if the file is still open + fh->file->close(); //close the file } - delete fh; + delete fh->file; //delete QFile instance + delete fh; //delete the fle } } @@ -115,18 +120,18 @@ FILE_STATUS ll_filesystem_file_seek(FILE_HANDLE* handle, uint32_t offset) { if(handle==NULL) { return F_INVALIDPARAM; } - QT_FILE_HANDLE* fh = static_cast(handle); - if(!fh->file->isOpen()) { + QT_FILE_HANDLE* fh = static_cast(handle); //cast pointer to QT_FILE_HANDLE + if(!fh->file->isOpen()) { //file is not open return F_DISKERROR; } - if(offset>=fh->file->size()) { + if(offset>=fh->file->size()) { //offset exeeds filesize return F_INVALIDPARAM; } - if(fh->file->seek(offset)) { - fh->fpos = offset; + if(fh->file->seek(offset)) { //try to seek to desired offset + fh->fpos = offset; //update offset in FILE_HANDLE (for user) return F_OK; - } else { + } else { //seek failed return F_DISKERROR; } } @@ -135,20 +140,20 @@ FILE_STATUS ll_filesystem_file_read(FILE_HANDLE* handle, uint8_t* buf, uint32_t if(handle==NULL || buf==NULL) { return F_INVALIDPARAM; } - QT_FILE_HANDLE* fh = static_cast(handle); - if(!fh->file->isOpen()) { + QT_FILE_HANDLE* fh = static_cast(handle); //cast pointer to QT_FILE_HANDLE + if(!fh->file->isOpen()) { //file is not open return F_DISKERROR; } - if(!fh->file->isReadable()) { + if(!fh->file->isReadable()) { //file is not readable return F_EACCESS; } - qint64 bytesRead = fh->file->read((char*)buf,size); - if(bytesRead<0) { + qint64 bytesRead = fh->file->read((char*)buf,size); //try to read desired amount of bytes + if(bytesRead<0) { //read failed return F_DISKERROR; } - fh->fpos+=bytesRead; - if(bytesRead!=size) { - return F_EOF; + fh->fpos+=bytesRead; //increase file position (for user) + if(bytesRead!=size) { //we got less bytes than expected + return F_EOF; //we reached the end of the file } else { return F_OK; } @@ -158,24 +163,22 @@ FILE_STATUS ll_filesystem_file_write(FILE_HANDLE* handle, uint8_t* buf, uint32_t if(handle==NULL) { return F_INVALIDPARAM; } - QT_FILE_HANDLE* fh = static_cast(handle); - if(!fh->file->isOpen()) { + QT_FILE_HANDLE* fh = static_cast(handle); //cast pointer to QT_FILE_HANDLE + if(!fh->file->isOpen()) { //file is not open return F_DISKERROR; } - if(!fh->file->isWritable()) { + if(!fh->file->isWritable()) { //file is not writable return F_EACCESS; } - qint64 bytesWritten = fh->file->write((char*)buf,size); - if(bytesWritten<0) { + qint64 bytesWritten = fh->file->write((char*)buf,size); //try to write desired amount of bytes + if(bytesWritten<0) { //write failed return F_DISKERROR; } - fh->fpos+=bytesWritten; - if(bytesWritten!=size) { - return F_EOF; + fh->fpos+=bytesWritten; //increase file position (for user) + if(bytesWritten!=size) { //we wrote less bytes than expected + return F_EOF; //we reached the end of the file } else { return F_OK; } } - - diff --git a/emulator/qt/ll_system.cpp b/emulator/qt/ll_system.cpp index 8d087b5..68efd72 100644 --- a/emulator/qt/ll_system.cpp +++ b/emulator/qt/ll_system.cpp @@ -6,18 +6,18 @@ extern "C" { } bool ll_system_init() { - return true; + return true; //nothing to initialize here, apart from the stuff which is done in the main method. } void ll_system_delay(uint32_t msec) { - QThread::msleep(msec); + QThread::msleep(msec); //Let the app_process() Thread sleep } void ll_system_process() { - QApplication::processEvents(); - QThread::msleep(1); + QApplication::processEvents(); //Process pending qt events + QThread::msleep(1); //Sleep for 1ms, to keep the cpu load down } void ll_system_toggle_led() { - + //No led emulated :( } diff --git a/emulator/qt/ll_tft.cpp b/emulator/qt/ll_tft.cpp index 9c63578..5963104 100644 --- a/emulator/qt/ll_tft.cpp +++ b/emulator/qt/ll_tft.cpp @@ -1,5 +1,4 @@ #include "mainwindow.h" -#include extern "C" { #include "ll_tft.h" @@ -8,13 +7,14 @@ extern "C" { MainWindow* mainwindow; bool ll_tft_init() { - qDebug() << "tft init done"; - mainwindow = new MainWindow(); - mainwindow->show(); + mainwindow = new MainWindow(); //create the designed window + mainwindow->show(); //open it (non blocking) return true; } +//the following functions redirect the call to the mainwindow, to a function with the same signature + void ll_tft_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { mainwindow->draw_line(x1,y1,x2,y2,color); } @@ -44,9 +44,11 @@ void ll_tft_draw_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color) { } uint8_t ll_tft_num_fonts() { - return 2; + return 2; //we have two fonts (see below) } +//Helper function to get the QFont to the corresponding font number +//Note: only return monospaced fonts!!! QFont get_font(uint8_t fontnum) { switch(fontnum) { case 0: @@ -62,20 +64,20 @@ QFont get_font(uint8_t fontnum) { uint8_t ll_tft_font_height(uint8_t fontnum) { QFont f = get_font(fontnum); if(f == QFont()) return -1; - QFontMetrics m(f); + QFontMetrics m(f); //use font metcris object to calculate height of font return m.height(); } uint8_t ll_tft_font_width(uint8_t fontnum) { QFont f = get_font(fontnum); if(f == QFont()) return -1; - QFontMetrics m(f); + QFontMetrics m(f); //use font metcris object to calculate width of font return m.averageCharWidth(); } void ll_tft_draw_char(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, char c) { QFont f = get_font(font); - if(f == QFont()) return; + if(f == QFont()) return; //if the font is the default-font, we want to abort. mainwindow->draw_char(x,y,color,bgcolor,f,c); } diff --git a/emulator/qt/main.cpp b/emulator/qt/main.cpp index dc67c83..96937c1 100644 --- a/emulator/qt/main.cpp +++ b/emulator/qt/main.cpp @@ -2,23 +2,23 @@ #include extern "C" { - //C Functions from the common folder + //Imported C Functions from the common folder void app_init(); //Initializes the app void app_process(); //Processes one eventloop of the app } void app_loop() { - while(!QApplication::closingDown()) { - app_process(); + while(!QApplication::closingDown()) { //as long as the application is not terminating + app_process(); //let the application process it's events } } int main(int argc, char* argv[]) { - QApplication app(argc,argv); - app_init(); + QApplication app(argc,argv); //Process qt-specific commandline arguments and create event loop + app_init(); //Let the application initialize it self - QtConcurrent::run(&app_loop); - return app.exec(); + QtConcurrent::run(&app_loop); //Start a thread that executes app_loop + return app.exec(); //Run the event loop until the last window is closed. } diff --git a/emulator/qt/mainwindow.cpp b/emulator/qt/mainwindow.cpp index 0ed5eea..99b2ae2 100644 --- a/emulator/qt/mainwindow.cpp +++ b/emulator/qt/mainwindow.cpp @@ -13,16 +13,18 @@ extern "C" { #define DISPLAY_WIDTH 320 #define DISPLAY_HEIGHT 240 +//function to calculate QColor out of a RGB565 16bit color QColor QColorFromRGB565(uint16_t color) { - + //interpolate colors int R8 = (int) floor( (color>>(5+6)) * 255.0 / 31.0 + 0.5); int G8 = (int) floor( ((color>>5)&0x3F) * 255.0 / 63.0 + 0.5); int B8 = (int) floor( (color&0x1F) * 255.0 / 31.0 + 0.5); return QColor::fromRgb(R8,G8,B8); } +//function to calculate QRgb out of a RGB565 16bit color QRgb QRgbFromRGB565(uint16_t color) { - + //interpolate colors int R8 = (int) floor( (color>>(5+6)) * 255.0 / 31.0 + 0.5); int G8 = (int) floor( ((color>>5)&0x3F) * 255.0 / 63.0 + 0.5); int B8 = (int) floor( (color&0x1F) * 255.0 / 31.0 + 0.5); @@ -32,156 +34,137 @@ QRgb QRgbFromRGB565(uint16_t color) { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), image(DISPLAY_WIDTH,DISPLAY_HEIGHT, QImage::Format_RGB16), ui(new Ui::MainWindow){ ui->setupUi(this); - image.fill(Qt::black); - currentScale = 1; - ui->widgetDisplay->setMouseTracking(true); - ui->widgetDisplay->installEventFilter(this); + image.fill(Qt::black); //clear display buffer + currentScale = 1; //start with scale factor 1 + ui->widgetDisplay->setMouseTracking(true); //enable mouse move events, even when mouse is not pressed + ui->widgetDisplay->installEventFilter(this); //install event filter for "display" widget, so that we receive those events as well } void MainWindow::draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { - //render_mutex.lock(); QPainter painter(&(image)); painter.setPen(QColorFromRGB565(color)); painter.drawLine(x1,y1,x2,y2); - //render_mutex.unlock(); update(); } void MainWindow::draw_pixel(uint16_t x, uint16_t y, uint16_t color) { - //render_mutex.lock(); image.setPixel(x,y,QRgbFromRGB565(color)); - //render_mutex.unlock(); update(); } void MainWindow::clear(uint16_t color) { - //render_mutex.lock(); image.fill(QColorFromRGB565(color)); - //render_mutex.unlock(); update(); } void MainWindow::draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { - //render_mutex.lock(); QPainter painter(&(image)); painter.setPen(QColorFromRGB565(color)); - painter.drawRect(qMin(x1,x2),qMin(y1,y2),abs((int)x2-(int)x1)+1,abs((int)y2-(int)y1)+1); - //render_mutex.unlock(); + painter.drawRect(qMin(x1,x2),qMin(y1,y2),abs((int)x2-(int)x1),abs((int)y2-(int)y1)); update(); } void MainWindow::fill_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { - //render_mutex.lock(); QPainter painter(&(image)); painter.fillRect(qMin(x1,x2),qMin(y1,y2),abs((int)x2-(int)x1)+1,abs((int)y2-(int)y1)+1,QColorFromRGB565(color)); - //render_mutex.unlock(); update(); } void MainWindow::draw_bitmap_unscaled(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint16_t *dat) { //Creating a new image and access it directly is faster than setPixel - QImage img(width,height,QImage::Format_RGB32); + QImage img(width,height,QImage::Format_RGB32); //create a new image - for(int yi=0; yiwidgetDisplay->geometry().topLeft(),QSizeF(DISPLAY_WIDTH*currentScale,DISPLAY_HEIGHT*currentScale)); - painter.drawImage(imgRect,image); - painter.setPen(QPen(Qt::green,2)); - painter.drawRect(imgRect.adjusted(-1,-1,1,1)); - - - //render_mutex.unlock(); + painter.drawImage(imgRect,image); //draw buffer + painter.setPen(QPen(Qt::green,2)); //set border color + painter.drawRect(imgRect.adjusted(-1,-1,1,1)); //draw border } void MainWindow::mousePressEvent(QMouseEvent *evt) { - //qDebug() << "down" << evt->pos(); + //the mouse was pressed checkAndSendEvent(evt->pos(),true); } void MainWindow::mouseReleaseEvent(QMouseEvent *evt) { - //qDebug() << "up" << evt->pos(); + //the mouse was released checkAndSendEvent(evt->pos(),false); } void MainWindow::mouseMoveEvent(QMouseEvent *evt) { - //qDebug() << "move" << evt->pos(); + //the mouse was released checkAndSendEvent(evt->pos(),true); } bool MainWindow::eventFilter(QObject *obj, QEvent *evt) { - if(obj==ui->widgetDisplay) { + if(obj==ui->widgetDisplay) { //we received a redirect event from the target rectangle switch(evt->type()) { - case QEvent::MouseMove: + case QEvent::MouseMove: //it's a mouse move event { - QMouseEvent* mouseEvent = static_cast(evt); - QPoint p = (mouseEvent->pos()-QPoint(1,1))/currentScale; - if(p.x()(evt); //get mouse event + QPoint p = (mouseEvent->pos()-QPoint(1,1))/currentScale; //calculate correct corrdinates (undo scale) + if(p.x()txtMousePos->setText(QString("Mouse Position: (%1,%2)").arg(p.x()).arg(p.y())); } } break; - default: break; } } - return false; } @@ -193,18 +176,16 @@ MainWindow::~MainWindow() void MainWindow::checkAndSendEvent(QPoint pos, bool down) { - QPoint p = pos - ui->widgetDisplay->geometry().topLeft(); - p/=currentScale; - if(p.x()<0 || p.y()<0 || p.x() >= DISPLAY_WIDTH || p.y() >= DISPLAY_HEIGHT) return; + QPoint p = pos - ui->widgetDisplay->geometry().topLeft(); //make coordinate relative to target area (0,0) + p/=currentScale; //undo scaling + if(p.x()<0 || p.y()<0 || p.x() >= DISPLAY_WIDTH || p.y() >= DISPLAY_HEIGHT) return; //abort if out of bounds - //qDebug() << down << p; - - touch_add_raw_event(p.x(),p.y(),down?TOUCH_DOWN:TOUCH_UP); + touch_add_raw_event(p.x(),p.y(),down?TOUCH_DOWN:TOUCH_UP); //submit touch event to touch module (common) } void MainWindow::on_cboZoom_currentIndexChanged(int index) { - currentScale=index+1; - update(); + currentScale=index+1; //zoom factor 1 is the 0th entry, zoom factor 2 is the 1st entry, zoom factor 3 is the 2nd entry + update(); //force redraw } diff --git a/emulator/qt/mainwindow.h b/emulator/qt/mainwindow.h index e9034e1..7c0f845 100644 --- a/emulator/qt/mainwindow.h +++ b/emulator/qt/mainwindow.h @@ -33,12 +33,11 @@ protected: ~MainWindow(); private slots: - void on_cboZoom_currentIndexChanged(int index); + void on_cboZoom_currentIndexChanged(int index); //slot that is called when the zoomlevel changed private: - //QMutex render_mutex; - QImage image; - int currentScale; + QImage image; //Display buffer + int currentScale; //current scale factor void checkAndSendEvent(QPoint pos, bool down); Ui::MainWindow *ui; diff --git a/genheader.sh b/utils/genheader.sh similarity index 93% rename from genheader.sh rename to utils/genheader.sh index 7be2999..26ee24b 100755 --- a/genheader.sh +++ b/utils/genheader.sh @@ -7,7 +7,7 @@ echo "* Institution: BFH Bern University of Applied Sciences" echo "* File: $1" echo "*" echo "* Version History:" -echo "* Date Autor Email SHA Changes" +echo "* Date Autor Email SHA Changes" git log --pretty=format:"* %ad%x09%ae%x09%h%x09%s" --date=short --date-order --no-merges --reverse $1 | grep -v -i "fileheader" diff --git a/utils/style.astylerc b/utils/style.astylerc new file mode 100644 index 0000000..8fdbb87 --- /dev/null +++ b/utils/style.astylerc @@ -0,0 +1,16 @@ + +# general style +--style=1tbs +--indent=spaces=4 # default +--lineend=linux #only \r as line ending + +# alignment +--align-pointer=type #put asterisks to the type. eg. char* a +--align-reference=type #put references to the type. eg. int& a + +# padding +--break-blocks # insert empty lines between header blocks +--unpad-paren # remove extra padding around parens +--pad-oper # spaces around operators +--pad-header # insert space between header blocks and the following paren +--add-brackets # add brackets to one line conditionals diff --git a/updateheaders.sh b/utils/updateheaders.sh similarity index 71% rename from updateheaders.sh rename to utils/updateheaders.sh index 84068bb..55d8b74 100755 --- a/updateheaders.sh +++ b/utils/updateheaders.sh @@ -3,14 +3,15 @@ FILES=`find common/ emulator/ discovery/ -name "*.c" -or -name "*.h" -or -name "*.cpp" | grep -v libs | grep -v /pixy/` + for FILE in $FILES; do echo "Adding Header to $FILE" - #remove old header - CONTENT=$(perl -0777 -pe 's%^/\*.*?discoverpixy.*?\*/%%igs' $FILE) + #remove old header, and format file with astyle + CONTENT=$(perl -0777 -pe 's%^/\*.*?discoverpixy.*?\*/%%igs' $FILE | astyle --options=./utils/style.astylerc) #add new header - ./genheader.sh $FILE > $FILE + ./utils/genheader.sh $FILE > $FILE #append file content echo "$CONTENT" >> $FILE