24 Commits

Author SHA1 Message Date
id101010
f18a936129 Updated PID values with Ziegler/Nichols 2015-06-08 11:10:26 +02:00
id101010
72dc291e4d Corrected some Errors 2015-06-07 23:26:32 +02:00
id101010
20b20f10c1 Added Fazit and a picture 2015-06-07 22:04:10 +02:00
id101010
d006dbd6a5 Wrote even more documentation 2015-06-07 18:58:20 +02:00
id101010
7e3de6af80 Merge remote-tracking branch 'origin/master' into dev_aaron 2015-06-07 17:02:47 +02:00
t-moe
f432236628 Merge remote-tracking branch 'origin/dev_aaron' 2015-06-07 17:01:59 +02:00
id101010
ce27acef98 Added pictures 2015-06-07 16:57:21 +02:00
t-moe
87523569a7 Started with tests in docu. fixed a small bug in emulator when drawing a rectangle. 2015-06-07 16:45:06 +02:00
id101010
33815f0d6c Documentated TFT, Touch, PID, System 2015-06-07 16:43:03 +02:00
t-moe
4b5768cedc Improved Comments in whole emulator. Finalized emulator section in docu. 2015-06-07 15:29:46 +02:00
t-moe
3e4fbab00a Merge remote-tracking branch 'origin/dev_aaron' into emulator 2015-06-07 13:48:25 +02:00
t-moe
ca1459d9d4 Copied doc resources from master branch. 2015-06-07 13:46:13 +02:00
id101010
3d98ca93bc Minor changes 2015-06-07 13:41:07 +02:00
t-moe
422b1a623c Updated Docu from Emulator. 2015-06-07 13:39:29 +02:00
id101010
802d3df373 Fixed pid controller and refactored code 2015-06-07 13:13:49 +02:00
t-moe
f004cbffa6 Updated Docu: Documented touch module and systemmodule. Improved already documented topics. 2015-06-07 00:02:23 +02:00
t-moe
c06661d25b Fixed some outdated comments in source code. Documented Gui Module in docu. 2015-06-06 20:10:10 +02:00
t-moe
ef467d0fee Merge remote-tracking branch 'origin/dev_aaron' into emulator 2015-06-06 19:07:10 +02:00
t-moe
fb652e3670 Updated docu: Added links to online doxygen docu. Added some description to app module doc. 2015-06-06 19:01:30 +02:00
id101010
a04cda9fc2 Refactured comments and implemented a bugfix for the PID controller 2015-06-06 18:51:57 +02:00
id101010
8c264c237a Comment refactoring, updated PID values 2015-06-06 18:19:28 +02:00
t-moe
6db62a6b50 Completed Docu for Pixy&Usb. Started with new table structure for other parts. 2015-06-06 18:09:33 +02:00
id101010
e018a75cd3 Implemented basic pi and pid controller 2015-06-02 16:25:45 +02:00
id101010
7ad16797ab Merge remote-tracking branch 'origin/emulator' into dev_aaron 2015-06-01 21:34:32 +02:00
36 changed files with 316 additions and 2204 deletions

73
common/app/pixy_control.c Normal file
View File

@@ -0,0 +1,73 @@
/*
* pixy_control.c
*
* Notation
* --------
*
* x : Sollwert (Führgrösse)
* w : Istwert (Reglergrösse)
* esum : Integralteil
* e : Regelabweichung
* y : Stellgrösse
*
*
*/
#include<pixy_control.h>
#include<stdint.h>
// PID tuning factors
#define REG_PID_KP (0.41f)
#define REG_PID_KI (0.001f)
#define REG_PID_KD (0.00025f)
#define REG_PID_TA (0.001f)
#define REG_PID_YKOR (0.3f)
void int_init(void){
// TODO Init ports and outputs if needed.
}
// 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 + REG_PID_YKOR) * 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;
}

14
common/app/pixy_control.h Normal file
View File

@@ -0,0 +1,14 @@
/*
* pixy_control.h
*/
#ifndef _CONTROL_H_
#define _CONTROL_H_
#include<stdint.h>
void int_init(void);
int16_t pixy_PID_Y(int16_t x, int16_t w);
int16_t pixy_PID_X(int16_t x, int16_t w);
#endif

View File

@@ -1,4 +1,5 @@
#include "screen_tracking.h"
#include "pixy_control.h"
#include "button.h"
#include "checkbox.h"
#include "tft.h"
@@ -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

View File

@@ -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

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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;

Binary file not shown.

Binary file not shown.

BIN
doc/analyse_touch.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
doc/datasheet_ssd1289.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
doc/idpa_software.tar.gz Normal file

Binary file not shown.

BIN
doc/screenshot_emulator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

5
emulator/.gitignore vendored
View File

@@ -5,9 +5,8 @@ qt/*.o
qt/*.a
qt/ui_*
qt/moc_*
qt/Makefile*
qt/debug
qt/release
qt/Makefile
libs/*/obj
libs/*/*.a

View File

@@ -10,7 +10,7 @@ QT_DIR=./qt
COMMON_DIR=../common
#Tools
CC=gcc #-fdiagnostics=auto
CC=gcc -fdiagnostics-color=auto
GDB=gdb
@@ -24,22 +24,17 @@ INCLUDES:=$(addprefix -I,$(INCLUDES))
QT_LIB=$(QT_DIR)/libemulatorqt.a
CPPFLAGS= -march=x86-64 -mtune=generic #-fPIC
CPPFLAGS+= $(INCLUDES)
CPPFLAGS= -march=x86-64 -mtune=generic -fPIC $(INCLUDES)
CFLAGS= -O0 -g -std=c99
LIBS= emulatorqt pixy usb-1.0
LIBS+= boost_system-mgw49-mt-1_58 boost_timer-mgw49-mt-1_58 boost_chrono-mgw49-mt-1_58
LIBS+= Qt5Core Qt5Gui Qt5Widgets m stdc++
LIBS= pixy usb-1.0 boost_system boost_timer boost_chrono
LIBS+=Qt5Core Qt5Gui Qt5Widgets emulatorqt m stdc++
LDFLAGS= -static-libgcc -static-libstdc++ #-Wl,-t -Wl,--Map=a.map
LDFLAGS+= -L$(QT_DIR)
LDFLAGS= -L$(QT_DIR) $(addprefix -l,$(LIBS))
LDFLAGS+= -L$(LIB_DIR)/Pixy
LDFLAGS+= -L$(LIB_DIR)/boost/lib
LDFLAGS+= -LC:/Qt/5.4/mingw491_32/lib
LDFLAGS+= -L$(LIB_DIR)/Pixy/windows
LDFLAGS+= $(addprefix -l,$(LIBS))
#Finding Input files
CFILES=$(shell find . -maxdepth 1 -name '*.c')
@@ -63,7 +58,7 @@ debug: all
$(GDB) ./build/emulator
$(QT_LIB):
cd $(QT_DIR) && qmake && $(MAKE)
cd $(QT_DIR) && qmake &&make
#objects to elf
$(BUILD_DIR)/$(TARGET): $(OBJS) $(COMMON_OBJS) $(QT_LIB)
@@ -88,7 +83,7 @@ $(OBJ_DIR)/%.o: $(COMMON_DIR)/%.c
#Clean Obj files and builded stuff
clean:
cd $(QT_DIR) && $(MAKE) clean && $(RM) Makefile* && $(RM) *.a && $(RMDIR) debug release
cd $(QT_DIR) && $(MAKE) clean && $(RM) Makefile && $(RM) *.a
$(RMDIR) $(BUILD_DIR) $(OBJ_DIR)

View File

@@ -17,13 +17,11 @@ OBJ_DIR=./obj
#Architecture flags
#Compiler, Linker Options
CPPFLAGS=-I$(INC_DIR) -DHOST=1 #-D__LINUX__=1 -DDEBUG=1
CPPFLAGS=-I$(INC_DIR) -D__LINUX__=1 -DHOST=1 #-DDEBUG=1
CFLAGS=$(ARCH_FLAGS) -O0 -g #-ffunction-sections -fdata-sections -g
#CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
#CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS+=-I/usr/include/libusb-1.0
CFLAGS+=-I../boost/include/
CFLAGS+=-I./windows/
#Finding Input files
CFILES=$(shell find $(SRC_DIR) -name '*.cpp')

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
include
lib

Binary file not shown.

View File

@@ -1,2 +0,0 @@
This directory is only needed on windows, and if you do not have boost installed.
Extract the rar file here, to get boost 1.58 header files and the libraries that we need for discoverpixy

View File

@@ -8,7 +8,7 @@ QT += widgets gui
TARGET = emulatorqt
TEMPLATE = lib
CONFIG += staticlib debug
CONFIG += staticlib
SOURCES += \
mainwindow.cpp \
@@ -30,6 +30,3 @@ INCLUDEPATH+= ../../common/lowlevel/ \
FORMS += \
mainwindow.ui
DESTDIR = $$_PRO_FILE_PWD_ #force windows to not create subfolders
#QMAKE_CXXFLAGS+= -v

View File

@@ -7,51 +7,53 @@ extern "C" {
#include <QFileInfoList>
#include <QDateTime>
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; i<entries.count(); i++){
for(int i=0; i<entries.count(); i++){ //foreach file that we found
QFileInfo fi = entries.at(i);
FILE_STRUCT* entry = &(directory->files[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; i<dir->num_files; i++) {
delete dir->files[i].fname;
if(dir!=NULL) { //passed handle is valid
for(int i=0; i<dir->num_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<QT_FILE_HANDLE*>(handle);
if(fh->file->isOpen()) {
fh->file->close();
if(handle!=NULL) { //passed handle is valid
QT_FILE_HANDLE* fh = static_cast<QT_FILE_HANDLE*>(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<QT_FILE_HANDLE*>(handle);
if(!fh->file->isOpen()) {
QT_FILE_HANDLE* fh = static_cast<QT_FILE_HANDLE*>(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<QT_FILE_HANDLE*>(handle);
if(!fh->file->isOpen()) {
QT_FILE_HANDLE* fh = static_cast<QT_FILE_HANDLE*>(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<QT_FILE_HANDLE*>(handle);
if(!fh->file->isOpen()) {
QT_FILE_HANDLE* fh = static_cast<QT_FILE_HANDLE*>(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;
}
}

View File

@@ -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 :(
}

View File

@@ -1,5 +1,4 @@
#include "mainwindow.h"
#include <QDebug>
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,15 +44,17 @@ 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:
return QFont("Courier New",8);
return QFont("Monospace",8);
case 1:
return QFont("Courier New",14);
return QFont("DejaVu Sans Mono",14);
default:
return QFont();
}
@@ -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);
}

View File

@@ -2,23 +2,23 @@
#include <QtConcurrent/QtConcurrent>
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.
}

View File

@@ -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; yi<height; yi++) {
uint32_t* line = (uint32_t*)img.scanLine(yi);
for(int xi=0; xi<width; xi++) {
*line++=QRgbFromRGB565(dat[yi*width+xi]);
for(int yi=0; yi<height; yi++) { //for each line
uint32_t* line = (uint32_t*)img.scanLine(yi); //get the pointer to the imagedata of the current line
for(int xi=0; xi<width; xi++) { //for each column
*line++=QRgbFromRGB565(dat[yi*width+xi]); //set pixel
}
}
//render_mutex.lock();
QPainter p(&image);
p.drawImage(x,y,img);
//render_mutex.unlock();
p.drawImage(x,y,img); //draw created image
update();
}
void MainWindow::draw_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color)
{
//render_mutex.lock();
QPainter painter(&(image));
painter.setPen(QColorFromRGB565(color));
painter.drawEllipse(QPoint(x,y), r, r);
//render_mutex.unlock();
update();
}
void MainWindow::draw_char(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, QFont font, char c)
{
//render_mutex.lock();
QPainter painter(&(image));
painter.setFont(font);
if(bgcolor!=TRANSPARENT) {
if(bgcolor!=TRANSPARENT) { //background color is not transparent
painter.setBackgroundMode(Qt::OpaqueMode);
painter.setBackground(QColorFromRGB565(bgcolor));
painter.setBackground(QColorFromRGB565(bgcolor)); //set text background
}
painter.setPen(QColorFromRGB565(color));
painter.setPen(QColorFromRGB565(color)); //set fontcolor
y+=QFontMetrics(font).ascent(); //use y pos as highest point of char, instead of baseline
painter.drawText(QPoint(x,y), QString(QChar(c)));
//render_mutex.unlock();
painter.drawText(QPoint(x,y), QString(QChar(c))); //draw char
update();
}
void MainWindow::paintEvent(QPaintEvent *)
{
//render_mutex.lock();
//this method is called whenever the window needs to be redrawn (or after update() is called)
QPainter painter(this);
//Create a QRectF which represents the rectangle to draw the buffered image to
QRectF imgRect (ui->widgetDisplay->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<QMouseEvent*>(evt);
QPoint p = (mouseEvent->pos()-QPoint(1,1))/currentScale;
if(p.x()<DISPLAY_WIDTH && p.y()<DISPLAY_HEIGHT) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(evt); //get mouse event
QPoint p = (mouseEvent->pos()-QPoint(1,1))/currentScale; //calculate correct corrdinates (undo scale)
if(p.x()<DISPLAY_WIDTH && p.y()<DISPLAY_HEIGHT) { //mouse position not out of bounds
//set mouse position text
ui->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
}

View File

@@ -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;

View File

@@ -1,26 +0,0 @@
Prerequisites:
- Qt5 for Windows (provides C:\Qt\5.4\mingw491_32 and C:\Qt\Tools\mingw491_32)
- Mingw msys-base (provides C:\MinGW\msys\1.0 and make, find )
- Boost (check emulator\libs\boost)
Steps:
- Open up the msys shell using the batchfile
- Navigate to the emulator folder of the project
- Source the qt binaries (qmake, gcc, g++) by executing
export PATH=$PATH:/c/Qt/Tools/mingw491_32/bin:/c/Qt/5.4/mingw491_32/bin
- navigate to libs/Pixy and execute make
- navigate back into the emulator folder and execute make
- this should generate you build/emulator.exe
Starting:
- Extract windows_dlls.rar to the build folder
- Copy the emulated folder into the build folder
- Make sure that you installed the pixy usb driver (e.g. by installing pixymon http://cmucam.org/projects/cmucam5/wiki/Latest_release)
- Start emulator.exe
Pitfalls:
- libstdc++ must be provided by qt. do not use one of msys or mingw. you will waste hours with debugging of the make process
Sources:
- libusb for windows was taken from https://github.com/charmedlabs/pixy/tree/master/src/host/windows
- Boost was compiled from sources, using http://andres.jaimes.net/718/how-to-install-the-c-boost-libraries-on-windows/ and http://www.boost.org/doc/libs/1_58_0/more/getting_started/windows.html

Binary file not shown.