Merge remote-tracking branch 'origin/emulator' into dev_aaron
This commit is contained in:
241
common/app/pixy_helper.c
Normal file
241
common/app/pixy_helper.c
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "pixy_helper.h"
|
||||
#include "pixy.h"
|
||||
#include "tft.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
static int renderBA81(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame);
|
||||
static int saveBA81(FILE_HANDLE* handle, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame);
|
||||
|
||||
|
||||
int pixy_render_full_frame(uint16_t x, uint16_t y) {
|
||||
return pixy_render_cropped_frame(x,y,0,0,320,200);
|
||||
}
|
||||
|
||||
|
||||
int pixy_render_cropped_frame(uint16_t x, uint16_t y, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height) {
|
||||
uint8_t* videodata;
|
||||
int32_t response;
|
||||
int32_t fourccc;
|
||||
int8_t renderflags;
|
||||
uint16_t xwidth;
|
||||
uint16_t ywidth;
|
||||
uint32_t size;
|
||||
|
||||
|
||||
int return_value = pixy_command("cam_getFrame", // String id for remote procedure
|
||||
INT8(0x21), // mode
|
||||
INT16(xoffset), // xoffset
|
||||
INT16(yoffset), // yoffset
|
||||
INT16(width), // width
|
||||
INT16(height), // height
|
||||
END_OUT_ARGS, // separator
|
||||
&response, // pointer to mem address for return value
|
||||
&fourccc,
|
||||
&renderflags,
|
||||
&xwidth,
|
||||
&ywidth,
|
||||
&size,
|
||||
&videodata, // pointer to mem address for returned frame
|
||||
END_IN_ARGS);
|
||||
|
||||
if(return_value==0) {
|
||||
return_value = renderBA81(x,y,xwidth,ywidth,size,videodata);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
int pixy_save_full_frame(FILE_HANDLE* handle) {
|
||||
return pixy_save_cropped_frame(handle,0,0,320,200);
|
||||
}
|
||||
|
||||
int pixy_save_cropped_frame(FILE_HANDLE* handle, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height) {
|
||||
uint8_t* videodata;
|
||||
int32_t response;
|
||||
int32_t fourccc;
|
||||
int8_t renderflags;
|
||||
uint16_t xwidth;
|
||||
uint16_t ywidth;
|
||||
uint32_t size;
|
||||
|
||||
|
||||
int return_value = pixy_command("cam_getFrame", // String id for remote procedure
|
||||
INT8(0x21), // mode
|
||||
INT16(xoffset), // xoffset
|
||||
INT16(yoffset), // yoffset
|
||||
INT16(width), // width
|
||||
INT16(height), // height
|
||||
END_OUT_ARGS, // separator
|
||||
&response, // pointer to mem address for return value
|
||||
&fourccc,
|
||||
&renderflags,
|
||||
&xwidth,
|
||||
&ywidth,
|
||||
&size,
|
||||
&videodata, // pointer to mem address for returned frame
|
||||
END_IN_ARGS);
|
||||
|
||||
if(return_value==0) {
|
||||
return_value = saveBA81(handle,xwidth,ywidth,size,videodata);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void interpolateBayer(uint16_t width, uint16_t x, uint16_t y, uint8_t *pixel, uint8_t* r, uint8_t* g, uint8_t* b)
|
||||
{
|
||||
if (y&1)
|
||||
{
|
||||
if (x&1)
|
||||
{
|
||||
*r = *pixel;
|
||||
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
|
||||
*b = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r = (*(pixel-1)+*(pixel+1))>>1;
|
||||
*g = *pixel;
|
||||
*b = (*(pixel-width)+*(pixel+width))>>1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x&1)
|
||||
{
|
||||
*r = (*(pixel-width)+*(pixel+width))>>1;
|
||||
*g = *pixel;
|
||||
*b = (*(pixel-1)+*(pixel+1))>>1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
|
||||
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
|
||||
*b = *pixel;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int renderBA81(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame)
|
||||
{
|
||||
uint16_t x, y;
|
||||
uint8_t r, g, b;
|
||||
|
||||
|
||||
// skip first line
|
||||
frame += width;
|
||||
|
||||
// don't render top and bottom rows, and left and rightmost columns because of color
|
||||
// interpolation
|
||||
//uint32_t decodedimage[(width-2)*(height-2)];
|
||||
uint16_t* decodedimage = malloc(sizeof(uint16_t)*(width-2)*(height-2));
|
||||
|
||||
if(decodedimage==NULL) { //not enough free space to decode image in memory
|
||||
//decode & render image pixel by pixel
|
||||
for (y=1; y<height-1; y++)
|
||||
{
|
||||
frame++;
|
||||
for (x=1; x<width-1; x++, frame++)
|
||||
{
|
||||
interpolateBayer(width, x, y, frame, &r, &g, &b);
|
||||
tft_draw_pixel(xpos+x-1,ypos+y-1,RGB(r,g,b));
|
||||
}
|
||||
frame++;
|
||||
}
|
||||
} else { //enough space
|
||||
uint16_t* line = decodedimage;
|
||||
for (y=1; y<height-1; y++)
|
||||
{
|
||||
//line = (unsigned int *)img.scanLine(y-1);
|
||||
frame++;
|
||||
for (x=1; x<width-1; x++, frame++)
|
||||
{
|
||||
interpolateBayer(width, x, y, frame, &r, &g, &b);
|
||||
//*line++ = (0xff<<24) | (r<<16) | (g<<8) | (b<<0);
|
||||
*line++ = RGB(r,g,b);
|
||||
}
|
||||
frame++;
|
||||
}
|
||||
|
||||
tft_draw_bitmap_unscaled(xpos,ypos,width-2,height-2,decodedimage);
|
||||
|
||||
free(decodedimage);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int saveBA81(FILE_HANDLE* handle, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame)
|
||||
{
|
||||
uint16_t x, y;
|
||||
uint8_t r, g, b;
|
||||
|
||||
uint32_t fpos = handle->fpos;
|
||||
uint32_t row_size_padded = ((width-2)*3 + 3) & (~3); //row size aligned to 4 bytes
|
||||
uint32_t fpos_end = fpos + row_size_padded* (height-2);
|
||||
|
||||
|
||||
// skip first line
|
||||
frame += width;
|
||||
|
||||
// don't render top and bottom rows, and left and rightmost columns because of color
|
||||
// interpolation
|
||||
|
||||
for (y=1; y<height-1; y++)
|
||||
{
|
||||
frame++;
|
||||
uint8_t rowbuf[row_size_padded];
|
||||
|
||||
//Bitmaps are saved "bottom-up". Seek to the right row.
|
||||
if(filesystem_file_seek(handle,fpos_end-row_size_padded*y)!=F_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (x=1; x<width-1; x++, frame++)
|
||||
{
|
||||
interpolateBayer(width, x, y, frame, &r, &g, &b);
|
||||
//bitmaps are saved in 24bit b,g,r format
|
||||
rowbuf[(x-1)*3] = b;
|
||||
rowbuf[(x-1)*3+1] = g;
|
||||
rowbuf[(x-1)*3+2] = r;
|
||||
}
|
||||
|
||||
if(filesystem_file_write(handle,rowbuf,row_size_padded)!=F_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
frame++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pixy_cc_set_region(uint8_t signum, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height) {
|
||||
int32_t response;
|
||||
|
||||
int return_value = pixy_command("cc_setSigRegion", // String id for remote procedure
|
||||
INT32(0), // type = normal color code
|
||||
INT8(signum),
|
||||
INT16(xoffset), // xoffset
|
||||
INT16(yoffset), // yoffset
|
||||
INT16(width), // width
|
||||
INT16(height), // height
|
||||
END_OUT_ARGS, // separator
|
||||
&response, // pointer to mem address for return value
|
||||
END_IN_ARGS);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
59
common/app/pixy_helper.h
Normal file
59
common/app/pixy_helper.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef PIXY_HELPER_H
|
||||
#define PIXY_HELPER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "filesystem.h"
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param y The y-Coordinate of the top left corner
|
||||
* @return 0 on success, otherwise the errorcode from pixy
|
||||
*/
|
||||
int pixy_render_full_frame(uint16_t x, uint16_t y);
|
||||
|
||||
/**
|
||||
* Receives a cropped 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 to draw the image
|
||||
* @param y The y-Coordinate of the top left corner to draw the image
|
||||
* @param xoffset The x-Coordinate on the pixy image from where on you want the frame data
|
||||
* @param yoffset The y-Coordinate on the pixy image from where on you want the frame data
|
||||
* @param width The width of the image recorded from pixy
|
||||
* @param height The height of the image recorded from pixy
|
||||
* @return 0 on success, otherwise the errorcode from pixy
|
||||
*/
|
||||
int pixy_render_cropped_frame(uint16_t x, uint16_t y, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height);
|
||||
|
||||
/**
|
||||
* Receives a fullsized frame from pixy and saves it to the given file in the 24bit (b,g,a) format.
|
||||
* Use this method to write the bitmap-data part of a windows bitmap (.bmp).
|
||||
* This method will neither open nor close the passed file.
|
||||
* @param handle The file to write the data to. The file must be open and it should be seeked to the right position.
|
||||
* @return 0 on success, otherwise the errorcode from pixy
|
||||
*/
|
||||
int pixy_save_full_frame(FILE_HANDLE* handle);
|
||||
|
||||
/**
|
||||
* Receives a cropped frame from pixy and saves it to the given file in the 24bit (b,g,a) format.
|
||||
* @param handle The file to write the data to. The file must be open and it should be seeked to the right position.
|
||||
* @param xoffset The x-Coordinate on the pixy image from where on you want the frame data
|
||||
* @param yoffset The y-Coordinate on the pixy image from where on you want the frame data
|
||||
* @param width The width of the image recorded from pixy
|
||||
* @param height The height of the image recorded from pixy
|
||||
* @return 0 on success, otherwise the errorcode from pixy
|
||||
*/
|
||||
int pixy_save_cropped_frame(FILE_HANDLE* handle, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height);
|
||||
|
||||
/**
|
||||
* Sets the color signature to the color in the selected region of the frame
|
||||
* @param signum the color signature number (1..7)
|
||||
* @param xoffset The x-Coordinate of the topleft point of the region
|
||||
* @param yoffset The y-Coordinate of the topleft point of the region
|
||||
* @param width The width of the region
|
||||
* @param height The height of the region
|
||||
* @return 0 on success, otherwise the errorcode from pixy
|
||||
*/
|
||||
int pixy_cc_set_region(uint8_t signum, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height);
|
||||
|
||||
#endif /* PIXY_HELPER_H */
|
||||
@@ -70,6 +70,7 @@ static void enter(void* screen) {
|
||||
y+=14;
|
||||
}
|
||||
|
||||
filesystem_dir_close(dir);
|
||||
|
||||
y+=14;
|
||||
|
||||
@@ -129,46 +130,10 @@ SCREEN_STRUCT* get_screen_filetest() {
|
||||
}
|
||||
|
||||
static void image_test() {
|
||||
//Source: http://stackoverflow.com/a/17040962/2606757
|
||||
|
||||
FILE_HANDLE* file = filesystem_file_open("cpu.bmp");
|
||||
if(file==NULL) {
|
||||
|
||||
if(!tft_draw_bitmap_file_unscaled(250,170,"cpu.bmp")) {
|
||||
tft_print_line(10,180,BLUE,TRANSPARENT,0,"Could not open cpu.bmp");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
unsigned char info[54];
|
||||
if(filesystem_file_read(file,info,54)!=F_OK) {
|
||||
tft_print_line(10,180,BLUE,TRANSPARENT,0,"Could not read header of cpu.bmp");
|
||||
filesystem_file_close(file);
|
||||
return;
|
||||
}
|
||||
|
||||
// extract image height and width from header
|
||||
int width = *(int*)&info[18];
|
||||
int height = *(int*)&info[22];
|
||||
|
||||
filesystem_file_seek(file,*(int*)&info[10]);
|
||||
|
||||
unsigned char data [width*4];
|
||||
tft_draw_rectangle(100-1,160-1,100-1+width,160-1+height,BLACK);
|
||||
|
||||
for(int i = 0; i < height; i++)
|
||||
{
|
||||
filesystem_file_read(file,data,width*4);
|
||||
for(int j = 0; j < width*4; j += 4)
|
||||
{
|
||||
unsigned char a = data[j];
|
||||
unsigned char r = data[j+1];
|
||||
unsigned char g = data[j+2];
|
||||
unsigned char b = data[j+3];
|
||||
if(a!=0) {
|
||||
tft_draw_pixel(100+j/4,160+height-1-i,RGB(r,g,b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filesystem_file_close(file);
|
||||
|
||||
tft_draw_rectangle(250-1,170-1,250-1+64,170-1+64,BLACK);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup filetest Filetest
|
||||
* @defgroup filetest Filetest (Screen)
|
||||
* The File-Test Screen tests the filesystem module. It read/writes from/to files and shows a bitmap
|
||||
*/
|
||||
/*@{*/
|
||||
@@ -19,4 +19,5 @@
|
||||
*/
|
||||
SCREEN_STRUCT* get_screen_filetest();
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup guitest Guitest
|
||||
* @defgroup guitest Guitest (Screen)
|
||||
* The Gui-Test Screen tests the gui and the tft module.
|
||||
*/
|
||||
/*@{*/
|
||||
@@ -20,4 +20,5 @@
|
||||
*/
|
||||
SCREEN_STRUCT* get_screen_guitest();
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
|
||||
@@ -2,13 +2,35 @@
|
||||
#include "screen_guitest.h"
|
||||
#include "screen_pixytest.h"
|
||||
#include "screen_filetest.h"
|
||||
#include "screen_photomode.h"
|
||||
#include "screen_tracking.h"
|
||||
#include "button.h"
|
||||
#include "tft.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
BUTTON_STRUCT b_guitest;
|
||||
BUTTON_STRUCT b_pixytest;
|
||||
BUTTON_STRUCT b_filetest;
|
||||
|
||||
BUTTON_STRUCT b_our_tracking;
|
||||
BUTTON_STRUCT b_ref_tracking;
|
||||
BUTTON_STRUCT b_photo_mode;
|
||||
|
||||
|
||||
static void b_our_tracking_cb(void* button) {
|
||||
tracking_set_mode(OUR_TRACKING);
|
||||
gui_screen_navigate(get_screen_tracking());
|
||||
}
|
||||
|
||||
static void b_ref_tracking_cb(void* button) {
|
||||
tracking_set_mode(REFERENCE_TRACKING);
|
||||
gui_screen_navigate(get_screen_tracking());
|
||||
}
|
||||
|
||||
static void b_photo_mode_cb(void* button) {
|
||||
gui_screen_navigate(get_screen_photomode());
|
||||
}
|
||||
|
||||
static void b_guitest_cb(void* button) {
|
||||
gui_screen_navigate(get_screen_guitest());
|
||||
}
|
||||
@@ -25,47 +47,108 @@ static void b_pixytest_cb(void* button) {
|
||||
static void enter(void* screen) {
|
||||
tft_clear(WHITE);
|
||||
|
||||
//button to reach guitest
|
||||
b_guitest.base.x1=25; //Start X of Button
|
||||
b_guitest.base.y1=45; //Start Y of Button
|
||||
b_guitest.base.x2=AUTO; //b_guitest.base.x1+160; //Auto Calculate X2 with String Width
|
||||
b_guitest.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_guitest.txtcolor=WHITE; //Set foreground color
|
||||
b_guitest.bgcolor=HEX(0xDE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_guitest.font=0; //Select Font
|
||||
b_guitest.text="Gui/Tft Test"; //Set Text (For formatted strings take sprintf)
|
||||
b_guitest.callback=b_guitest_cb; //Call b_guitest_cb as Callback
|
||||
gui_button_add(&b_guitest); //Register Button (and run the callback from now on)
|
||||
//Heading
|
||||
tft_print_line(10,10,BLUE,TRANSPARENT,1,"Discoverpixy");
|
||||
tft_draw_line(0,40,319,40,BLACK);
|
||||
|
||||
//button to reach pixy test
|
||||
b_pixytest.base.x1=150; //Start X of Button
|
||||
b_pixytest.base.y1=45; //Start Y of Button
|
||||
b_pixytest.base.x2=AUTO; //b_pixytest.base.x1+160; //Auto Calculate X2 with String Width
|
||||
b_pixytest.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_pixytest.txtcolor=BLUE; //Set foreground color
|
||||
b_pixytest.bgcolor=HEX(0x10DE10); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_pixytest.font=0; //Select Font
|
||||
b_pixytest.text="Pixy Test"; //Set Text (For formatted strings take sprintf)
|
||||
b_pixytest.callback=b_pixytest_cb; //Call b_pixytest_cb as Callback
|
||||
gui_button_add(&b_pixytest); //Register Button (and run the callback from now on)
|
||||
#define X_TAB 90
|
||||
#define BUTTON_SPACING 7
|
||||
|
||||
//button to reach filesystem test
|
||||
b_filetest.base.x1=240; //Start X of Button
|
||||
b_filetest.base.y1=45; //Start Y of Button
|
||||
b_filetest.base.x2=AUTO; //b_filetest.base.x1+160; //Auto Calculate X2 with String Width
|
||||
b_filetest.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_filetest.txtcolor=WHITE; //Set foreground color
|
||||
b_filetest.bgcolor=HEX(0x501EA0); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_filetest.font=0; //Select Font
|
||||
b_filetest.text="File Test"; //Set Text (For formatted strings take sprintf)
|
||||
b_filetest.callback=b_filetest_cb; //Call b_filetest_cb as Callback
|
||||
gui_button_add(&b_filetest); //Register Button (and run the callback from now on)
|
||||
//First line of buttons
|
||||
#define Y_FIRST 60
|
||||
tft_print_line(10,Y_FIRST,BLACK,TRANSPARENT,0,"Tracking:");
|
||||
|
||||
b_our_tracking.base.x1=X_TAB; //Start X of Button
|
||||
b_our_tracking.base.y1=Y_FIRST-3; //Start Y of Button
|
||||
b_our_tracking.base.x2=AUTO; //Auto Calculate X2 with String Width
|
||||
b_our_tracking.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_our_tracking.txtcolor=WHITE; //Set foreground color
|
||||
b_our_tracking.bgcolor=HEX(0xE30535); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_our_tracking.font=0; //Select Font
|
||||
b_our_tracking.text="Our Tracking"; //Set Text (For formatted strings take sprintf)
|
||||
b_our_tracking.callback=b_our_tracking_cb; //Call b_our_tracking when the button get's pressed
|
||||
gui_button_add(&b_our_tracking); //Register Button (and run the callback from now on)
|
||||
|
||||
|
||||
b_ref_tracking.base.x1=b_our_tracking.base.x2+BUTTON_SPACING;
|
||||
b_ref_tracking.base.y1=Y_FIRST-3;
|
||||
b_ref_tracking.base.x2=AUTO;
|
||||
b_ref_tracking.base.y2=AUTO;
|
||||
b_ref_tracking.txtcolor=WHITE;
|
||||
b_ref_tracking.bgcolor=HEX(0xFF2151);
|
||||
b_ref_tracking.font=0;
|
||||
b_ref_tracking.text="Reference Tracking";
|
||||
b_ref_tracking.callback=b_ref_tracking_cb;
|
||||
gui_button_add(&b_ref_tracking);
|
||||
|
||||
//Second line of buttons
|
||||
#define Y_SECOND Y_FIRST+25
|
||||
tft_print_line(10,Y_SECOND,BLACK,TRANSPARENT,0,"Photo mode:");
|
||||
|
||||
b_photo_mode.base.x1=X_TAB;
|
||||
b_photo_mode.base.y1=Y_SECOND-3;
|
||||
b_photo_mode.base.x2=AUTO;
|
||||
b_photo_mode.base.y2=AUTO;
|
||||
b_photo_mode.txtcolor=WHITE;
|
||||
b_photo_mode.bgcolor=HEX(0x21B1FF);
|
||||
b_photo_mode.font=0;
|
||||
b_photo_mode.text="Photo Mode";
|
||||
b_photo_mode.callback=b_photo_mode_cb;
|
||||
gui_button_add(&b_photo_mode);
|
||||
|
||||
|
||||
//Third line of buttons
|
||||
#define Y_THIRD Y_SECOND+25
|
||||
tft_print_line(10,Y_THIRD,BLACK,TRANSPARENT,0,"Tests:");
|
||||
|
||||
b_guitest.base.x1=X_TAB;
|
||||
b_guitest.base.y1=Y_THIRD-3;
|
||||
b_guitest.base.x2=AUTO;
|
||||
b_guitest.base.y2=AUTO;
|
||||
b_guitest.txtcolor=BLACK;
|
||||
b_guitest.bgcolor=HEX(0x00FA21);
|
||||
b_guitest.font=0;
|
||||
b_guitest.text="Gui Test";
|
||||
b_guitest.callback=b_guitest_cb;
|
||||
gui_button_add(&b_guitest);
|
||||
|
||||
|
||||
b_pixytest.base.x1=b_guitest.base.x2+BUTTON_SPACING;
|
||||
b_pixytest.base.y1=Y_THIRD-3;
|
||||
b_pixytest.base.x2=AUTO;
|
||||
b_pixytest.base.y2=AUTO;
|
||||
b_pixytest.txtcolor=BLACK;
|
||||
b_pixytest.bgcolor=HEX(0x00FA96);
|
||||
b_pixytest.font=0;
|
||||
b_pixytest.text="Pixy Test";
|
||||
b_pixytest.callback=b_pixytest_cb;
|
||||
gui_button_add(&b_pixytest);
|
||||
|
||||
|
||||
b_filetest.base.x1=b_pixytest.base.x2+BUTTON_SPACING;
|
||||
b_filetest.base.y1=Y_THIRD-3;
|
||||
b_filetest.base.x2=AUTO;
|
||||
b_filetest.base.y2=AUTO;
|
||||
b_filetest.txtcolor=BLACK;
|
||||
b_filetest.bgcolor=HEX(0x00FAC4);
|
||||
b_filetest.font=0;
|
||||
b_filetest.text="File Test";
|
||||
b_filetest.callback=b_filetest_cb;
|
||||
gui_button_add(&b_filetest);
|
||||
|
||||
|
||||
//Bottom line
|
||||
tft_draw_line(0,145,319,145,BLACK);
|
||||
tft_print_line(10,150,BLUE,TRANSPARENT,0,"Powered by");
|
||||
tft_draw_bitmap_file_unscaled(10,165,"pixy_small.bmp");
|
||||
tft_draw_bitmap_file_unscaled(165,165,"stm_small.bmp");
|
||||
|
||||
}
|
||||
|
||||
static void leave(void* screen) {
|
||||
gui_button_remove(&b_our_tracking);
|
||||
gui_button_remove(&b_ref_tracking);
|
||||
gui_button_remove(&b_photo_mode);
|
||||
gui_button_remove(&b_guitest);
|
||||
gui_button_remove(&b_pixytest);
|
||||
gui_button_remove(&b_filetest);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup main Main
|
||||
* @defgroup main Main (Screen)
|
||||
* The Main Screen is the start-screen for the application
|
||||
*/
|
||||
/*@{*/
|
||||
@@ -31,4 +31,5 @@
|
||||
*/
|
||||
SCREEN_STRUCT* get_screen_main();
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
|
||||
166
common/app/screen_photomode.c
Normal file
166
common/app/screen_photomode.c
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "screen_photomode.h"
|
||||
#include "screen_photomode_save.h"
|
||||
#include "button.h"
|
||||
#include "tft.h"
|
||||
#include "touch.h"
|
||||
#include "pixy.h"
|
||||
#include "system.h"
|
||||
#include "pixy_helper.h"
|
||||
|
||||
static bool pixy_connected = false; //Whether or not the pixy cam is currently connected
|
||||
|
||||
static BUTTON_STRUCT b_back; //Button to navigate back
|
||||
static BUTTON_STRUCT b_save; //Button to save the current image
|
||||
static TOUCH_AREA_STRUCT a_area; //Touch Area, where the frame is drawn. Used to drag the image around
|
||||
static bool subMenu=false; //Whether or not we left the current screen for a submenu
|
||||
|
||||
//Callback for when the user presses the "back" button
|
||||
static void b_back_cb(void* button) {
|
||||
subMenu = false; //we're not entering a submenu
|
||||
gui_screen_back(); //navigate back to the previous screen
|
||||
}
|
||||
|
||||
//Callback for when the user presses the "save" button
|
||||
static void b_save_cb(void* button) {
|
||||
subMenu = true; //we're entering a submenu
|
||||
gui_screen_navigate(get_screen_photomodesave()); //navigate to the save screen
|
||||
}
|
||||
|
||||
static POINT_STRUCT pixy_pos; //The current position of pixy's servos
|
||||
static POINT_STRUCT old_pos; //The last touch position on the screen
|
||||
|
||||
//Callback for when the user drags the image around
|
||||
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
|
||||
POINT_STRUCT p = touch_get_last_point(); //get the last touched point
|
||||
switch(triggeredAction) {
|
||||
case PEN_ENTER:
|
||||
case PEN_DOWN:
|
||||
old_pos = p; //If the user "newly" enters the touch area, we set the "last" position to the current
|
||||
break;
|
||||
case PEN_MOVE: //the user is moving around, he entered the screen a while ago (old_pos is set)
|
||||
{
|
||||
int16_t deltaX = p.x - old_pos.x; //Calculate x difference between last and current touch
|
||||
int16_t deltaY = p.y - old_pos.y; //Calculate y difference between last and current touch
|
||||
old_pos=p; //store the current touch point for the next time
|
||||
//printf("%d %d\n",deltaX,deltaY);
|
||||
if(pixy_connected) {
|
||||
//Calculate new servo coordinates. 2 is just a proportional factor
|
||||
int16_t new_x = pixy_pos.x+deltaX*2;
|
||||
int16_t new_y = pixy_pos.y-deltaY*2;
|
||||
|
||||
//check limits
|
||||
if(new_x<0) new_x=0;
|
||||
if(new_x>1000) new_x=1000;
|
||||
if(new_y<0) new_y=0;
|
||||
if(new_y>1000) new_y=1000;
|
||||
|
||||
//set pixy_pos so that the main routine can send it to the servos
|
||||
pixy_pos.x = new_x;
|
||||
pixy_pos.y= new_y;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PEN_UP:
|
||||
case PEN_LEAVE:
|
||||
//printf("Leave/up\n");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Callback for when the screen is entered/loaded
|
||||
static void enter(void* screen) {
|
||||
tft_clear(WHITE);
|
||||
|
||||
tft_print_line(5,5,BLACK,TRANSPARENT,0,"Drag the image around and ");
|
||||
|
||||
//Back button
|
||||
b_back.base.x1=5; //Start X of Button
|
||||
b_back.base.y1=19; //Start Y of Button
|
||||
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
|
||||
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_back.txtcolor=WHITE; //Set foreground color
|
||||
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_back.font=0; //Select Font
|
||||
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
|
||||
b_back.callback=b_back_cb; //Call b_back_cb as Callback
|
||||
gui_button_add(&b_back); //Register Button (and run the callback from now on)
|
||||
|
||||
//Save button
|
||||
b_save.base.x1=190;
|
||||
b_save.base.y1=3;
|
||||
b_save.base.x2=AUTO;
|
||||
b_save.base.y2=AUTO;
|
||||
b_save.txtcolor=WHITE;
|
||||
b_save.bgcolor=HEX(0x1010AE);
|
||||
b_save.font=0;
|
||||
b_save.text="Save it!";
|
||||
b_save.callback=b_save_cb;
|
||||
gui_button_add(&b_save);
|
||||
|
||||
//Frame Coordinates: topleft = (1,40); bottomright = (318,238)
|
||||
//Leave a 10px border for the area
|
||||
|
||||
//Area to drag the image around
|
||||
a_area.hookedActions = PEN_DOWN | PEN_MOVE | PEN_ENTER | PEN_UP | PEN_LEAVE;
|
||||
a_area.x1 = 11;
|
||||
a_area.y1 = 50;
|
||||
a_area.x2 = 308;
|
||||
a_area.y2 = 228;
|
||||
a_area.callback = touchCB;
|
||||
touch_register_area(&a_area);
|
||||
|
||||
//Pixy stuff
|
||||
pixy_connected = (pixy_init()==0); //try to connect to pixy
|
||||
if(pixy_connected && !subMenu) { //pixy is connected, but we are not coming from a submenu
|
||||
pixy_pos.x=pixy_pos.y=500; //reset servo positions to center
|
||||
}
|
||||
}
|
||||
|
||||
//Callback for when the screen is left/unloaded
|
||||
static void leave(void* screen) {
|
||||
//remove buttons and touch area.
|
||||
gui_button_remove(&b_back);
|
||||
gui_button_remove(&b_save);
|
||||
touch_unregister_area(&a_area);
|
||||
}
|
||||
|
||||
//Callback for when the screen should be updated
|
||||
//This is the main loop of the screen. This method will be called repeatedly
|
||||
static void update(void* screen) {
|
||||
//Note: The only way to detect that pixy has been disconnected is if a command fails. There's no pixy_is_connected method yet :'(
|
||||
|
||||
if(!pixy_connected) { //Pixy not connected
|
||||
pixy_close(); //Ensure that all pixy resources are freed (failsafe)
|
||||
if(pixy_init()==0) { //try to connect to pixy
|
||||
pixy_connected=true;
|
||||
if(!subMenu) { //we're not coming from a submenu
|
||||
pixy_pos.x=pixy_pos.y=500; //reset servo positions to center
|
||||
}
|
||||
printf("pixy (re)initialized\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(pixy_connected) { //If we are connected (now)
|
||||
pixy_service(); //Handle pending pixy events (e.g. color info retrival)
|
||||
|
||||
pixy_render_full_frame(1,40); //render the pixy video at point (1,40)
|
||||
|
||||
//set the servo positions to the coordinates form the touch interrupt
|
||||
pixy_rcs_set_position(0,pixy_pos.x);
|
||||
pixy_rcs_set_position(1,pixy_pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
//Declare screen callbacks
|
||||
static SCREEN_STRUCT screen = {
|
||||
enter,
|
||||
leave,
|
||||
update
|
||||
};
|
||||
|
||||
|
||||
SCREEN_STRUCT* get_screen_photomode() {
|
||||
return &screen;
|
||||
}
|
||||
22
common/app/screen_photomode.h
Normal file
22
common/app/screen_photomode.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "screen.h"
|
||||
|
||||
/**
|
||||
* @addtogroup screens
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup photomode Photo Mode (Screen)
|
||||
* The Photo Mode Screen allows taking snapshots of the current pixy cam feed
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* Returns a pointer to the photomode screen
|
||||
* \sa gui_screen_navigate
|
||||
* @return
|
||||
*/
|
||||
SCREEN_STRUCT* get_screen_photomode();
|
||||
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
301
common/app/screen_photomode_save.c
Normal file
301
common/app/screen_photomode_save.c
Normal file
@@ -0,0 +1,301 @@
|
||||
#include "screen_photomode_save.h"
|
||||
#include "filesystem.h"
|
||||
#include "button.h"
|
||||
#include "tft.h"
|
||||
#include "touch.h"
|
||||
#include "pixy.h"
|
||||
#include "pixy_helper.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static BUTTON_STRUCT b_back; //Button to navigate back
|
||||
static TOUCH_AREA_STRUCT a_area; //Touch area to select the save-file
|
||||
|
||||
//Callback for when the user presses the "back" button
|
||||
static void b_back_cb(void* button) {
|
||||
gui_screen_back();
|
||||
}
|
||||
|
||||
static int num_files_ok; //number of files into which we can write the image (size, flags ok)
|
||||
static enum {init, error, showlist, picking, saving, done} state; //Current state of the screen state machine
|
||||
static int fontheight; //The space between one line of text to the next
|
||||
static int liststart; //The y-Coordinate of the Start of the File-List
|
||||
static const char* picked_file; //The filename picked by the user, to save the image to
|
||||
|
||||
//Linked list structure to save all files which are suitable for saving.
|
||||
typedef struct FILE_LIST_ENTRY_S{
|
||||
char* filename; //Name of the file
|
||||
struct FILE_LIST_ENTRY_S* next; //Pointer to the next entry in the list or NULL
|
||||
} FILE_LIST_ENTRY;
|
||||
|
||||
static FILE_LIST_ENTRY* files_ok; //Pointer to the head of the list
|
||||
|
||||
//Callback for when the user selects a file to save the image into
|
||||
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
|
||||
|
||||
int y = touch_get_last_point().y-liststart; //Calculate the y-Coordinate of the touch point relative to the start of the file-list
|
||||
int elem = y/fontheight; //Calculate the file index
|
||||
if(elem<0 | elem>= num_files_ok) return; //Check if the file index is valid (0,1,..,num_files_ok-1)
|
||||
|
||||
//Search for the corresponding entry in the linked list
|
||||
FILE_LIST_ENTRY* current_entry = files_ok; //Start walking through the list, starting by the head of the list
|
||||
for(int i=0; i<elem; i++) { //Until we have reached the file (index)
|
||||
current_entry= current_entry->next; //traverse to the next file
|
||||
}
|
||||
|
||||
picked_file = current_entry->filename; //save the picked filename. It will be used by the statemachine in the main loop
|
||||
touch_unregister_area(&a_area); //unregister the touch area, we no longer need it. No more interrupts will be fired.
|
||||
state=saving; //Change the state of the statemachine
|
||||
}
|
||||
|
||||
//Text-Lines to show if we have no matching files (num_files_ok==0)
|
||||
static const char* nomatch_text [] = {
|
||||
"Due to limitations of the filesystem",
|
||||
"implementation you can only write to",
|
||||
"existing files.",
|
||||
"",
|
||||
"The files need to have a .bmp",
|
||||
"extension and must be at least",
|
||||
"189410 bytes (185kb) large.",
|
||||
"Unfortunately there were no such",
|
||||
"files found in the root directory.",
|
||||
"",
|
||||
"Please create some files and come",
|
||||
"back again.",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
//Bitmap header for a 318x198x24bit windows bitmap. data starts at 0x7A (= after this header)
|
||||
//This header has been taken from a white bitmap saved with gimp.
|
||||
//Wikipedia has a pretty good description on the header: http://de.wikipedia.org/wiki/Windows_Bitmap
|
||||
static unsigned char bmpheader_data[0x7A] = {
|
||||
0x42, 0x4d, 0xe2, 0xe3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x3e, 0x01, 0x00, 0x00, 0xc6, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xe3,
|
||||
0x02, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
//Callback for when the screen is entered/loaded
|
||||
static void enter(void* screen) {
|
||||
tft_clear(WHITE);
|
||||
|
||||
|
||||
#define X_OFS 5
|
||||
|
||||
//Back button
|
||||
b_back.base.x1=X_OFS; //Start X of Button
|
||||
b_back.base.y1=210; //Start Y of Button
|
||||
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
|
||||
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_back.txtcolor=WHITE; //Set foreground color
|
||||
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_back.font=0; //Select Font
|
||||
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
|
||||
b_back.callback=b_back_cb; //Call b_back_cb as Callback
|
||||
gui_button_add(&b_back); //Register Button (and run the callback from now on)
|
||||
|
||||
state =init; //Start with the init state
|
||||
fontheight = tft_font_height(0)+2; //Save the height of the used font, for fast access
|
||||
files_ok = NULL; //initialize the linked list with 0 elements
|
||||
num_files_ok = 0; //we have zero! elements
|
||||
}
|
||||
|
||||
//Callback for when the screen should be updated
|
||||
//This is the main loop of the screen. This method will be called repeatedly
|
||||
static void update(void* screen) {
|
||||
switch(state) {
|
||||
case init: //Init State: The user just entered the screen
|
||||
{
|
||||
DIRECTORY_STRUCT* dir = filesystem_dir_open("."); //open root directory
|
||||
if(dir==NULL) { //error while opening root directory
|
||||
tft_print_line(X_OFS,5,BLACK,TRANSPARENT,0,"Error accessing Filesystem");
|
||||
state=error;
|
||||
break;
|
||||
}
|
||||
|
||||
bool nomatch= true; //whether or not we have zero files which are suitable for saving
|
||||
|
||||
for(int i=0; i<dir->num_files; i++) { //walk through all files in the directory
|
||||
FILE_STRUCT* file = &(dir->files[i]); //Pointer to the current file/subdirectory
|
||||
|
||||
//Ignore directories, archives, hidden files, system files and files we cannot write to
|
||||
if(file->fattrib&(F_SYS|F_HID|F_ARC|F_DIR|F_RDO)) continue;
|
||||
|
||||
//ignore files which are not large enough
|
||||
if(file->fsize<189410) continue; //size taken from an example bitmap (318x198x24)
|
||||
|
||||
nomatch=false; //at least one file matches
|
||||
break;
|
||||
}
|
||||
|
||||
if(nomatch) { //not one file is suitable for writing
|
||||
int y=5; //y-Coordinate where to start writing the error text
|
||||
int i=0;
|
||||
while(nomatch_text[i]!=NULL) { //for every line in the big error array
|
||||
//Write the line's text and go to the next line
|
||||
tft_print_line(X_OFS,y+i*fontheight,BLACK,TRANSPARENT,0,nomatch_text[i]);
|
||||
i++;
|
||||
}
|
||||
state = error;
|
||||
} else { //we have a least one suitable file
|
||||
state = showlist;
|
||||
}
|
||||
|
||||
filesystem_dir_close(dir); //free directory struct
|
||||
}
|
||||
break;
|
||||
|
||||
case showlist: //Show List State: Where we load and present the suitable file's to the user in a list
|
||||
{
|
||||
DIRECTORY_STRUCT* dir2 = filesystem_dir_open("."); //Open the directory again
|
||||
if(dir2==NULL) return; //Error on opening? This should never happen, since it's handled in the previous state
|
||||
|
||||
int y = 5; //y-Coordinate where to start drawing/writing text/list-elements
|
||||
|
||||
tft_print_line(X_OFS,y,BLACK,TRANSPARENT,0,"Pick a file to save the image to");
|
||||
y+=fontheight+5;
|
||||
|
||||
tft_print_line(X_OFS,y,BLUE,TRANSPARENT,0,"Name Modified Size");
|
||||
y+=fontheight;
|
||||
|
||||
liststart = y; //store the y coordinate of the start of the list away (used in toucharea callback)
|
||||
num_files_ok = 0; //we start with 0 matching files
|
||||
|
||||
FILE_LIST_ENTRY* current_entry = NULL; //We start with an empty list
|
||||
for(int i=0; i<dir2->num_files && num_files_ok<10; i++) { //go through all the files of the directory, abort if we have 10 matches
|
||||
FILE_STRUCT* file = &(dir2->files[i]);
|
||||
|
||||
//Ignore directories, archives, hidden files, system files and files we cannot write to
|
||||
if(file->fattrib&(F_SYS|F_HID|F_ARC|F_DIR|F_RDO)) continue;
|
||||
|
||||
//ignore files which are not large enough
|
||||
if(file->fsize<189410) continue; //size taken from an example bitmap (318x198x24)
|
||||
|
||||
//Print out filename, modified date,time and file size
|
||||
tft_print_formatted(X_OFS,y,BLACK,
|
||||
TRANSPARENT,0,"%-16s %02u.%02u.%02u %02u:%02u:%02u %u",
|
||||
file->fname,
|
||||
file->fdate.day,
|
||||
file->fdate.month,
|
||||
(file->fdate.year+1980)%100,
|
||||
file->ftime.hour,
|
||||
file->ftime.min,
|
||||
file->ftime.sec*2,
|
||||
file->fsize);
|
||||
|
||||
if(current_entry==NULL) { //The list is empty
|
||||
current_entry = malloc(sizeof(FILE_LIST_ENTRY)); //create new entry
|
||||
files_ok = current_entry; //assign it to the list head
|
||||
} else { //there's a least one entry in the list
|
||||
current_entry->next = malloc(sizeof(FILE_LIST_ENTRY)); //append entry to previous entry
|
||||
current_entry = current_entry->next; //newly created entry is the current now.
|
||||
}
|
||||
current_entry->next = NULL; //we're at the end of the list (for now)
|
||||
current_entry->filename = malloc(strlen(file->fname)+1); //allocate space for the filename + zero-termination
|
||||
strcpy(current_entry->filename,file->fname); //copy filename (so that we can close the directory after scanning)
|
||||
|
||||
//since we have found a suitable file we need to increment the position in the list
|
||||
num_files_ok++;
|
||||
y+=fontheight;
|
||||
}
|
||||
|
||||
//Touch area for file-selection (in the list)
|
||||
a_area.hookedActions = PEN_UP; //we're only interested in PEN_UP events
|
||||
a_area.x1 = X_OFS; //Left border
|
||||
a_area.y1 = liststart; //Start where the list started
|
||||
a_area.x2 = 320-X_OFS; //Right border
|
||||
a_area.y2 = liststart+fontheight*num_files_ok; //stop at the end of the list
|
||||
a_area.callback = touchCB; //execute our callback when PEN_UP occurs
|
||||
touch_register_area(&a_area); //register the touch area and receive events from now on
|
||||
|
||||
filesystem_dir_close(dir2); //we no longer need the directory struct, since we have our own linked list now
|
||||
|
||||
state=picking;
|
||||
}
|
||||
break;
|
||||
|
||||
case picking: //Picking State: Where we wait on the users file choice
|
||||
pixy_service(); //Handle pending pixy events
|
||||
//do nothing and wait on user to pick a file
|
||||
break;
|
||||
|
||||
case saving: //Saving State: Where we save the image to the selected file
|
||||
{
|
||||
FILE_HANDLE* file = filesystem_file_open(picked_file); //try to open the selected file
|
||||
if(file==NULL) { //opening the file failed
|
||||
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Could not open %s",picked_file);
|
||||
state=error;
|
||||
break;
|
||||
}
|
||||
|
||||
filesystem_file_seek(file,0); //seek to the start of the file (optional?)
|
||||
if(filesystem_file_write(file,bmpheader_data,0x7A)!=F_OK) { //Writing the header failed
|
||||
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Error while writing to %s",picked_file);
|
||||
filesystem_file_close(file);
|
||||
state=error;
|
||||
break;
|
||||
}
|
||||
|
||||
if(pixy_save_full_frame(file)!=0) { //Writing the imagedata failed
|
||||
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Error while writing to %s",picked_file);
|
||||
filesystem_file_close(file);
|
||||
state=error;
|
||||
break;
|
||||
}
|
||||
|
||||
//if we reach this point, we have written all data out successfully
|
||||
|
||||
filesystem_file_close(file); //close/finalize the file
|
||||
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Image saved to %s",picked_file);
|
||||
state = done;
|
||||
}
|
||||
break;
|
||||
|
||||
case error: //Error State: Where we show an error message and leave the user no other choice than to click the backbutton
|
||||
case done: //Done State: When saving the file was successful
|
||||
pixy_service(); //Handle pending pixy events
|
||||
//wait on user to click the back button
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Callback for when the screen is left/unloaded
|
||||
static void leave(void* screen) {
|
||||
gui_button_remove(&b_back); //Remove/Free the back button
|
||||
|
||||
if(state==picking){ //The user left the screen in the "picking"-phase
|
||||
touch_unregister_area(&a_area); //remove the touch area (for the list)
|
||||
}
|
||||
|
||||
if(state==picking|| state==saving || state==done) { //the user left the screen after we created the linked list
|
||||
//Iterate through the linked list and free all resources
|
||||
FILE_LIST_ENTRY* current_entry = files_ok; //start with the list head
|
||||
while(current_entry!=NULL) { //while we're not at the end
|
||||
FILE_LIST_ENTRY* temp = current_entry->next; //save the next pointer because we free the current element on the next line
|
||||
free((void*)(current_entry->filename)); //free filename
|
||||
free(current_entry); //free element itself
|
||||
current_entry= temp; //advance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Declare screen callbacks
|
||||
static SCREEN_STRUCT screen = {
|
||||
enter,
|
||||
leave,
|
||||
update
|
||||
};
|
||||
|
||||
SCREEN_STRUCT* get_screen_photomodesave() {
|
||||
return &screen;
|
||||
}
|
||||
23
common/app/screen_photomode_save.h
Normal file
23
common/app/screen_photomode_save.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "screen.h"
|
||||
|
||||
/**
|
||||
* @addtogroup screens
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup photomodesave Photo Mode Save (Screen)
|
||||
* The Photo Mode Save Screen helps the user saving a file to the filesystem
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* Returns a pointer to the photomode save screen
|
||||
* \sa gui_screen_navigate
|
||||
* @return
|
||||
*/
|
||||
SCREEN_STRUCT* get_screen_photomodesave();
|
||||
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
|
||||
@@ -1,68 +1,120 @@
|
||||
#include "screen_pixytest.h"
|
||||
#include "button.h"
|
||||
#include "numupdown.h"
|
||||
#include "tft.h"
|
||||
#include "touch.h"
|
||||
#include "pixy.h"
|
||||
#include <stdlib.h>
|
||||
#include "system.h"
|
||||
#include "pixy_helper.h"
|
||||
|
||||
static volatile bool pixy_connected = false;
|
||||
|
||||
static volatile enum {detecting, idle,update_servos, update_ledcolor, update_ledcurrent} state; //Current state of the screen state machine
|
||||
|
||||
static BUTTON_STRUCT b_back;
|
||||
static BUTTON_STRUCT b_runstop;
|
||||
static TOUCH_AREA_STRUCT a_area;
|
||||
|
||||
static BUTTON_STRUCT b_servos_center;
|
||||
static BUTTON_STRUCT b_servos_topleft;
|
||||
static BUTTON_STRUCT b_servos_topright;
|
||||
static BUTTON_STRUCT b_servos_bottomleft;
|
||||
static BUTTON_STRUCT b_servos_bottomright;
|
||||
static uint16_t servo_x;
|
||||
static uint16_t servo_y;
|
||||
|
||||
static BUTTON_STRUCT b_led_off;
|
||||
static BUTTON_STRUCT b_led_white;
|
||||
static BUTTON_STRUCT b_led_red;
|
||||
static BUTTON_STRUCT b_led_green;
|
||||
static BUTTON_STRUCT b_led_blue;
|
||||
static uint32_t led_color;
|
||||
|
||||
static uint32_t led_maxcurrent;
|
||||
static NUMUPDOWN_STRUCT n_led_powerlimit;
|
||||
|
||||
|
||||
static void b_back_cb(void* button) {
|
||||
gui_screen_back();
|
||||
}
|
||||
|
||||
|
||||
static volatile bool pixy_running = false;
|
||||
static bool old_pixy_running= false;
|
||||
static void b_runstop_cb(void* button) {
|
||||
pixy_running=!pixy_running;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static POINT_STRUCT pixy_pos;
|
||||
static POINT_STRUCT old_pos;
|
||||
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
|
||||
POINT_STRUCT p = touch_get_last_point();
|
||||
switch(triggeredAction) {
|
||||
case PEN_ENTER:
|
||||
case PEN_DOWN:
|
||||
old_pos = p;
|
||||
break;
|
||||
case PEN_MOVE:
|
||||
{
|
||||
int16_t deltaX = p.x - old_pos.x;
|
||||
int16_t deltaY = p.y - old_pos.y;
|
||||
old_pos=p;
|
||||
printf("%d %d\n",deltaX,deltaY);
|
||||
if(pixy_connected) {
|
||||
int16_t new_x = pixy_pos.x+deltaX*2;
|
||||
int16_t new_y = pixy_pos.y-deltaY*2;
|
||||
if(new_x<0) new_x=0;
|
||||
if(new_x>1000) new_x=1000;
|
||||
if(new_y<0) new_y=0;
|
||||
if(new_y>1000) new_y=1000;
|
||||
pixy_pos.x = new_x;
|
||||
pixy_pos.y= new_y;
|
||||
static void b_servos_center_cb(void* button) {
|
||||
if(state==idle) {
|
||||
servo_x=500;
|
||||
servo_y=500;
|
||||
state=update_servos;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PEN_UP:
|
||||
case PEN_LEAVE:
|
||||
printf("Leave/up\n");
|
||||
default: break;
|
||||
|
||||
static void b_servos_topleft_cb(void* button) {
|
||||
if(state==idle) {
|
||||
servo_x=0;
|
||||
servo_y=0;
|
||||
state=update_servos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void b_servos_topright_cb(void* button) {
|
||||
if(state==idle) {
|
||||
servo_x=1000;
|
||||
servo_y=0;
|
||||
state=update_servos;
|
||||
}
|
||||
}
|
||||
|
||||
static void b_servos_bottomleft_cb(void* button) {
|
||||
if(state==idle) {
|
||||
servo_x=0;
|
||||
servo_y=1000;
|
||||
state=update_servos;
|
||||
}
|
||||
}
|
||||
|
||||
static void b_servos_bottomright_cb(void* button) {
|
||||
if(state==idle) {
|
||||
servo_x=1000;
|
||||
servo_y=1000;
|
||||
state=update_servos;
|
||||
}
|
||||
}
|
||||
|
||||
static void b_led_off_cb(void* button) {
|
||||
if(state==idle) {
|
||||
led_color=0x000000;
|
||||
state=update_ledcolor;
|
||||
}
|
||||
}
|
||||
|
||||
static void b_led_white_cb(void* button) {
|
||||
if(state==idle) {
|
||||
led_color=0xFFFFFF;
|
||||
state=update_ledcolor;
|
||||
}
|
||||
}
|
||||
|
||||
static void b_led_red_cb(void* button) {
|
||||
if(state==idle) {
|
||||
led_color=0xFF0000;
|
||||
state=update_ledcolor;
|
||||
}
|
||||
}
|
||||
|
||||
static void b_led_green_cb(void* button) {
|
||||
if(state==idle) {
|
||||
led_color=0x00FF00;
|
||||
state=update_ledcolor;
|
||||
}
|
||||
}
|
||||
|
||||
static void b_led_blue_cb(void* button) {
|
||||
if(state==idle) {
|
||||
led_color=0x0000FF;
|
||||
state=update_ledcolor;
|
||||
}
|
||||
}
|
||||
|
||||
static void n_led_powerlimit_cb(void* numupdown, int16_t value) {
|
||||
if(state==idle) {
|
||||
led_maxcurrent=value;
|
||||
state=update_ledcurrent;
|
||||
}
|
||||
}
|
||||
|
||||
static void enter(void* screen) {
|
||||
tft_clear(WHITE);
|
||||
@@ -70,7 +122,7 @@ static void enter(void* screen) {
|
||||
//Back button
|
||||
b_back.base.x1=10; //Start X of Button
|
||||
b_back.base.y1=210; //Start Y of Button
|
||||
b_back.base.x2=AUTO; //b_back.base.x1+160; //Auto Calculate X2 with String Width
|
||||
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
|
||||
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_back.txtcolor=WHITE; //Set foreground color
|
||||
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
@@ -80,94 +132,198 @@ static void enter(void* screen) {
|
||||
gui_button_add(&b_back); //Register Button (and run the callback from now on)
|
||||
|
||||
|
||||
//Back button
|
||||
b_runstop.base.x1=60; //Start X of Button
|
||||
b_runstop.base.y1=210; //Start Y of Button
|
||||
b_runstop.base.x2=AUTO; //b_runstop.base.x1+160; //Auto Calculate X2 with String Width
|
||||
b_runstop.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_runstop.txtcolor=WHITE; //Set foreground color
|
||||
b_runstop.bgcolor=HEX(0xAE1010); //Set runstopground color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_runstop.font=0; //Select Font
|
||||
b_runstop.text="Run/Stop"; //Set Text (For formatted strings take sprintf)
|
||||
b_runstop.callback=b_runstop_cb; //Call b_runstop_cb as Callrunstop
|
||||
gui_button_add(&b_runstop); //Register Button (and run the callrunstop from now on)
|
||||
//Servo stuff
|
||||
#define SERVO_BUTTON_Y 10
|
||||
#define SERVO_BUTTON_SPACING 5
|
||||
tft_print_line(5,SERVO_BUTTON_Y,BLACK,TRANSPARENT,0,"Servos:");
|
||||
|
||||
b_servos_center.base.x1=55;
|
||||
b_servos_center.base.y1=SERVO_BUTTON_Y-3;
|
||||
b_servos_center.base.x2=AUTO;
|
||||
b_servos_center.base.y2=AUTO;
|
||||
b_servos_center.txtcolor=WHITE;
|
||||
b_servos_center.bgcolor=HEX(0xAE1010);
|
||||
b_servos_center.font=0;
|
||||
b_servos_center.text="Center";
|
||||
b_servos_center.callback=b_servos_center_cb;
|
||||
gui_button_add(&b_servos_center);
|
||||
|
||||
//Area test
|
||||
a_area.hookedActions = PEN_DOWN | PEN_MOVE | PEN_ENTER | PEN_UP | PEN_LEAVE;
|
||||
a_area.x1 = 0;
|
||||
a_area.y1 = 0;
|
||||
a_area.x2 = 317;
|
||||
a_area.y2 = 197;
|
||||
a_area.callback = touchCB;
|
||||
touch_register_area(&a_area);
|
||||
b_servos_topleft.base.x1=b_servos_center.base.x2+SERVO_BUTTON_SPACING;
|
||||
b_servos_topleft.base.y1=SERVO_BUTTON_Y-3;
|
||||
b_servos_topleft.base.x2=AUTO;
|
||||
b_servos_topleft.base.y2=AUTO;
|
||||
b_servos_topleft.txtcolor=WHITE;
|
||||
b_servos_topleft.bgcolor=HEX(0xAE1010);
|
||||
b_servos_topleft.font=0;
|
||||
b_servos_topleft.text="ToLe";
|
||||
b_servos_topleft.callback=b_servos_topleft_cb;
|
||||
gui_button_add(&b_servos_topleft);
|
||||
|
||||
b_servos_topright.base.x1=b_servos_topleft.base.x2+SERVO_BUTTON_SPACING;
|
||||
b_servos_topright.base.y1=SERVO_BUTTON_Y-3;
|
||||
b_servos_topright.base.x2=AUTO;
|
||||
b_servos_topright.base.y2=AUTO;
|
||||
b_servos_topright.txtcolor=WHITE;
|
||||
b_servos_topright.bgcolor=HEX(0xAE1010);
|
||||
b_servos_topright.font=0;
|
||||
b_servos_topright.text="ToRi";
|
||||
b_servos_topright.callback=b_servos_topright_cb;
|
||||
gui_button_add(&b_servos_topright);
|
||||
|
||||
b_servos_bottomleft.base.x1=b_servos_topright.base.x2+SERVO_BUTTON_SPACING;
|
||||
b_servos_bottomleft.base.y1=SERVO_BUTTON_Y-3;
|
||||
b_servos_bottomleft.base.x2=AUTO;
|
||||
b_servos_bottomleft.base.y2=AUTO;
|
||||
b_servos_bottomleft.txtcolor=WHITE;
|
||||
b_servos_bottomleft.bgcolor=HEX(0xAE1010);
|
||||
b_servos_bottomleft.font=0;
|
||||
b_servos_bottomleft.text="BoLe";
|
||||
b_servos_bottomleft.callback=b_servos_bottomleft_cb;
|
||||
gui_button_add(&b_servos_bottomleft);
|
||||
|
||||
b_servos_bottomright.base.x1=b_servos_bottomleft.base.x2+SERVO_BUTTON_SPACING;
|
||||
b_servos_bottomright.base.y1=SERVO_BUTTON_Y-3;
|
||||
b_servos_bottomright.base.x2=AUTO;
|
||||
b_servos_bottomright.base.y2=AUTO;
|
||||
b_servos_bottomright.txtcolor=WHITE;
|
||||
b_servos_bottomright.bgcolor=HEX(0xAE1010);
|
||||
b_servos_bottomright.font=0;
|
||||
b_servos_bottomright.text="BoRi";
|
||||
b_servos_bottomright.callback=b_servos_bottomright_cb;
|
||||
gui_button_add(&b_servos_bottomright);
|
||||
|
||||
//Led Color stuff
|
||||
#define LED_COLOR_BUTTON_Y 35
|
||||
#define LED_COLOR_BUTTON_SPACING 5
|
||||
tft_print_line(5,LED_COLOR_BUTTON_Y,BLACK,TRANSPARENT,0,"Led Color:");
|
||||
|
||||
b_led_off.base.x1=85;
|
||||
b_led_off.base.y1=LED_COLOR_BUTTON_Y-3;
|
||||
b_led_off.base.x2=AUTO;
|
||||
b_led_off.base.y2=AUTO;
|
||||
b_led_off.txtcolor=WHITE;
|
||||
b_led_off.bgcolor=BLACK;
|
||||
b_led_off.font=0;
|
||||
b_led_off.text="Off";
|
||||
b_led_off.callback=b_led_off_cb;
|
||||
gui_button_add(&b_led_off);
|
||||
|
||||
b_led_white.base.x1=b_led_off.base.x2+LED_COLOR_BUTTON_SPACING;
|
||||
b_led_white.base.y1=LED_COLOR_BUTTON_Y-3;
|
||||
b_led_white.base.x2=AUTO;
|
||||
b_led_white.base.y2=AUTO;
|
||||
b_led_white.txtcolor=BLACK;
|
||||
b_led_white.bgcolor=HEX(0xEEEEEE);
|
||||
b_led_white.font=0;
|
||||
b_led_white.text="White";
|
||||
b_led_white.callback=b_led_white_cb;
|
||||
gui_button_add(&b_led_white);
|
||||
|
||||
b_led_red.base.x1=b_led_white.base.x2+LED_COLOR_BUTTON_SPACING;
|
||||
b_led_red.base.y1=LED_COLOR_BUTTON_Y-3;
|
||||
b_led_red.base.x2=AUTO;
|
||||
b_led_red.base.y2=AUTO;
|
||||
b_led_red.txtcolor=WHITE;
|
||||
b_led_red.bgcolor=HEX(0xEE0000);
|
||||
b_led_red.font=0;
|
||||
b_led_red.text="Red";
|
||||
b_led_red.callback=b_led_red_cb;
|
||||
gui_button_add(&b_led_red);
|
||||
|
||||
b_led_green.base.x1=b_led_red.base.x2+LED_COLOR_BUTTON_SPACING;
|
||||
b_led_green.base.y1=LED_COLOR_BUTTON_Y-3;
|
||||
b_led_green.base.x2=AUTO;
|
||||
b_led_green.base.y2=AUTO;
|
||||
b_led_green.txtcolor=WHITE;
|
||||
b_led_green.bgcolor=HEX(0x00EE00);
|
||||
b_led_green.font=0;
|
||||
b_led_green.text="Green";
|
||||
b_led_green.callback=b_led_green_cb;
|
||||
gui_button_add(&b_led_green);
|
||||
|
||||
b_led_blue.base.x1=b_led_green.base.x2+LED_COLOR_BUTTON_SPACING;
|
||||
b_led_blue.base.y1=LED_COLOR_BUTTON_Y-3;
|
||||
b_led_blue.base.x2=AUTO;
|
||||
b_led_blue.base.y2=AUTO;
|
||||
b_led_blue.txtcolor=WHITE;
|
||||
b_led_blue.bgcolor=HEX(0x0000EE);
|
||||
b_led_blue.font=0;
|
||||
b_led_blue.text="Blue";
|
||||
b_led_blue.callback=b_led_blue_cb;
|
||||
gui_button_add(&b_led_blue);
|
||||
|
||||
//Led MaxPower stuff
|
||||
#define LED_POWER_BUTTON_Y 70
|
||||
tft_print_line(5,LED_POWER_BUTTON_Y,BLACK,TRANSPARENT,0,"Led Maximum Current:");
|
||||
|
||||
//Num up down test
|
||||
n_led_powerlimit.x=160;
|
||||
n_led_powerlimit.y=LED_POWER_BUTTON_Y-7;
|
||||
n_led_powerlimit.fgcolor=WHITE;
|
||||
n_led_powerlimit.value = 10;
|
||||
n_led_powerlimit.max=40;
|
||||
n_led_powerlimit.min =0;
|
||||
n_led_powerlimit.callback=n_led_powerlimit_cb;
|
||||
gui_numupdown_add(&n_led_powerlimit);
|
||||
|
||||
|
||||
|
||||
//Pixy stuff
|
||||
pixy_connected = (pixy_init()==0); //try to connect to pixy
|
||||
if(pixy_connected) {
|
||||
pixy_pos.x=pixy_pos.y=500;
|
||||
}
|
||||
state=detecting;
|
||||
}
|
||||
|
||||
static void leave(void* screen) {
|
||||
gui_button_remove(&b_back);
|
||||
gui_button_remove(&b_runstop);
|
||||
touch_unregister_area(&a_area);
|
||||
}
|
||||
gui_button_remove(&b_servos_center);
|
||||
gui_button_remove(&b_servos_topleft);
|
||||
gui_button_remove(&b_servos_topright);
|
||||
gui_button_remove(&b_servos_bottomleft);
|
||||
gui_button_remove(&b_servos_bottomright);
|
||||
gui_button_remove(&b_led_off);
|
||||
gui_button_remove(&b_led_white);
|
||||
gui_button_remove(&b_led_red);
|
||||
gui_button_remove(&b_led_green);
|
||||
gui_button_remove(&b_led_blue);
|
||||
gui_numupdown_remove(&n_led_powerlimit);
|
||||
|
||||
int pixy_led_test();
|
||||
int pixy_frame_test();
|
||||
}
|
||||
|
||||
|
||||
static void update(void* screen) {
|
||||
|
||||
//Note: The only way to detect that pixy has been disconnected is if a command fails. There's no pixy_is_connected method yet :'(
|
||||
|
||||
if(!pixy_connected) { //Pixy not connected
|
||||
pixy_close(); //Ensure that all pixy resources are freed (failsafe)
|
||||
if(pixy_init()==0) { //try to connect to pixy
|
||||
pixy_connected=true;
|
||||
pixy_pos.x=pixy_pos.y=500;
|
||||
printf("pixy reinitialized\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(pixy_connected) {
|
||||
pixy_service(); //Send/receive event data from/to pixy failed
|
||||
|
||||
|
||||
if(pixy_frame_test()!=0) {
|
||||
pixy_connected=false;
|
||||
}
|
||||
|
||||
/*if(pixy_led_test()!=0) {
|
||||
pixy_connected=false;
|
||||
}*/
|
||||
|
||||
if(!pixy_running) {
|
||||
pixy_rcs_set_position(0,pixy_pos.x);
|
||||
pixy_rcs_set_position(1,pixy_pos.y);
|
||||
}
|
||||
|
||||
if(pixy_running!=old_pixy_running) {
|
||||
old_pixy_running=pixy_running;
|
||||
if(pixy_running) { //start tracking
|
||||
|
||||
int32_t response;
|
||||
int return_value;
|
||||
return_value = pixy_command("runprog", INT8(2), END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
|
||||
} else { //stop tracking
|
||||
switch(state) {
|
||||
case detecting: //Detecting State: Where we try to connect to the pixy
|
||||
if(pixy_init()==0) { //Pixy connection ok
|
||||
int32_t response;
|
||||
int return_value;
|
||||
return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
pixy_led_set_max_current(10);
|
||||
|
||||
state = idle; //Go to next state
|
||||
}
|
||||
break;
|
||||
case idle:
|
||||
pixy_service();
|
||||
break;
|
||||
case update_servos:
|
||||
pixy_rcs_set_position(0,servo_x);
|
||||
pixy_rcs_set_position(1,servo_y);
|
||||
state = idle;
|
||||
break;
|
||||
|
||||
case update_ledcolor:
|
||||
{
|
||||
int32_t response;
|
||||
int return_value;
|
||||
return_value = pixy_command("led_set", INT32(led_color), END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
state = idle;
|
||||
}
|
||||
break;
|
||||
|
||||
case update_ledcurrent:
|
||||
pixy_led_set_max_current(led_maxcurrent);
|
||||
state = idle;
|
||||
break;
|
||||
}
|
||||
|
||||
//system_delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,157 +340,3 @@ SCREEN_STRUCT* get_screen_pixytest() {
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
int colorind;
|
||||
const uint32_t colors [] = {0xFF0000, 0x00FF00,0x0000FF,0xFFFF00,0x00FFFF,0xFF00FF,0xFFFFFF,0x000000};
|
||||
const int num_colors = sizeof(colors)/sizeof(uint32_t);
|
||||
|
||||
int pixy_led_test() {
|
||||
if(colorind==0) {
|
||||
pixy_led_set_max_current(5);
|
||||
}
|
||||
|
||||
int32_t response;
|
||||
int return_value;
|
||||
return_value = pixy_command("led_set", INT32(colors[colorind++]), END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
colorind%=num_colors;
|
||||
|
||||
if(return_value!=0) {
|
||||
colorind=0; //reset color ind, to start at zero when plugging pixy in again
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
int renderBA81(uint8_t renderFlags, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame);
|
||||
|
||||
|
||||
int pixy_frame_test() {
|
||||
|
||||
uint8_t* videodata;
|
||||
int32_t response;
|
||||
int32_t fourccc;
|
||||
int8_t renderflags;
|
||||
uint16_t xwidth;
|
||||
uint16_t ywidth;
|
||||
uint32_t size;
|
||||
|
||||
|
||||
int return_value = pixy_command("cam_getFrame", // String id for remote procedure
|
||||
INT8(0x21), // mode
|
||||
INT16(0), // xoffset
|
||||
INT16(0), // yoffset
|
||||
INT16(320), // width
|
||||
INT16(200), // height
|
||||
END_OUT_ARGS, // separator
|
||||
&response, // pointer to mem address for return value
|
||||
&fourccc,
|
||||
&renderflags,
|
||||
&xwidth,
|
||||
&ywidth,
|
||||
&size,
|
||||
&videodata, // pointer to mem address for returned frame
|
||||
END_IN_ARGS);
|
||||
|
||||
if(return_value==0) {
|
||||
return_value = renderBA81(renderflags,xwidth,ywidth,size,videodata);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void interpolateBayer(uint16_t width, uint16_t x, uint16_t y, uint8_t *pixel, uint8_t* r, uint8_t* g, uint8_t* b)
|
||||
{
|
||||
if (y&1)
|
||||
{
|
||||
if (x&1)
|
||||
{
|
||||
*r = *pixel;
|
||||
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
|
||||
*b = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r = (*(pixel-1)+*(pixel+1))>>1;
|
||||
*g = *pixel;
|
||||
*b = (*(pixel-width)+*(pixel+width))>>1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x&1)
|
||||
{
|
||||
*r = (*(pixel-width)+*(pixel+width))>>1;
|
||||
*g = *pixel;
|
||||
*b = (*(pixel-1)+*(pixel+1))>>1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
|
||||
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
|
||||
*b = *pixel;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int renderBA81(uint8_t renderFlags, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame)
|
||||
{
|
||||
uint16_t x, y;
|
||||
uint8_t r, g, b;
|
||||
|
||||
|
||||
// skip first line
|
||||
frame += width;
|
||||
|
||||
// don't render top and bottom rows, and left and rightmost columns because of color
|
||||
// interpolation
|
||||
//uint32_t decodedimage[(width-2)*(height-2)];
|
||||
uint16_t* decodedimage = malloc(sizeof(uint16_t)*(width-2)*(height-2));
|
||||
|
||||
if(decodedimage==NULL) { //not enough free space to decode image in memory
|
||||
//decode & render image pixel by pixel
|
||||
uint16_t* line = decodedimage;
|
||||
for (y=1; y<height-1; y++)
|
||||
{
|
||||
frame++;
|
||||
for (x=1; x<width-1; x++, frame++)
|
||||
{
|
||||
interpolateBayer(width, x, y, frame, &r, &g, &b);
|
||||
tft_draw_pixel(x-1,y-1,RGB(r,g,b));
|
||||
}
|
||||
frame++;
|
||||
}
|
||||
} else { //enough space
|
||||
uint16_t* line = decodedimage;
|
||||
for (y=1; y<height-1; y++)
|
||||
{
|
||||
//line = (unsigned int *)img.scanLine(y-1);
|
||||
frame++;
|
||||
for (x=1; x<width-1; x++, frame++)
|
||||
{
|
||||
interpolateBayer(width, x, y, frame, &r, &g, &b);
|
||||
//*line++ = (0xff<<24) | (r<<16) | (g<<8) | (b<<0);
|
||||
*line++ = RGB(r,g,b);
|
||||
}
|
||||
frame++;
|
||||
}
|
||||
|
||||
tft_draw_bitmap_unscaled(0,0,width-2,height-2,decodedimage);
|
||||
|
||||
free(decodedimage);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup pixytest Pixytest
|
||||
* @defgroup pixytest Pixytest (Screen)
|
||||
* The Pixy-Test Screen tests the pixy module.
|
||||
*/
|
||||
/*@{*/
|
||||
@@ -18,4 +18,5 @@
|
||||
*/
|
||||
SCREEN_STRUCT* get_screen_pixytest();
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
|
||||
349
common/app/screen_tracking.c
Normal file
349
common/app/screen_tracking.c
Normal file
@@ -0,0 +1,349 @@
|
||||
#include "screen_tracking.h"
|
||||
#include "button.h"
|
||||
#include "checkbox.h"
|
||||
#include "tft.h"
|
||||
#include "touch.h"
|
||||
#include "pixy.h"
|
||||
#include "system.h"
|
||||
#include "pixy_helper.h"
|
||||
|
||||
static BUTTON_STRUCT b_back; //Button to navigate back
|
||||
static BUTTON_STRUCT b_select; //Button to start the color region selection
|
||||
static CHECKBOX_STRUCT c_frame_toggle; //Checkbox to toggle video data on/off
|
||||
static TOUCH_AREA_STRUCT a_area; //Touch area for the color region selection
|
||||
|
||||
//Callback for when the user presses the "back" button
|
||||
static void b_back_cb(void* button) {
|
||||
gui_screen_back(); //navigate back to the previous screen
|
||||
}
|
||||
|
||||
static volatile bool frame_visible = false; //Whether or not the video data should be displayed
|
||||
static void c_frame_toggle_cb(void *checkbox, bool checked) {
|
||||
frame_visible=checked; //Set the visibility of the frame to the checked state of the checkbox
|
||||
//Frame will be drawn in the main loop below
|
||||
}
|
||||
|
||||
static enum {detecting, init, tracking, preselecting, abortselecting, selecting, selected, error} state; //Current state of the screen state machine
|
||||
|
||||
static POINT_STRUCT point1; //First point of the rectangle selected by the user (color region selection)
|
||||
static POINT_STRUCT point2; //End point of the rectangle selected by the user (color region selection)
|
||||
static bool point1_valid; //Whether or not we have a valid first point
|
||||
|
||||
//Callback for when the user presses the "select color" button
|
||||
static void b_select_cb(void* button) {
|
||||
if(state==selecting) { //we're currently selecting a color region
|
||||
state = abortselecting; //Abort selecting!!
|
||||
} else if (state==tracking) { //we're currently watching the tracking
|
||||
state = preselecting; //start selecting
|
||||
}
|
||||
}
|
||||
|
||||
//Video Region properties
|
||||
//The camera records with 320*200px, but we need to keep a 1px border because of color interpolation (bayer format)
|
||||
#define FRAME_START_X 1 //x-Coordinate of the top-left point of the frame rectangle on display
|
||||
#define FRAME_START_Y 41 //y-Coordinate of the top-left point of the frame rectangle on display
|
||||
#define FRAME_WIDTH 318 //Width of the video frame
|
||||
#define FRAME_HEIGHT 198 //Height of the video frame
|
||||
#define FRAME_END_X FRAME_START_X +FRAME_WIDTH-1 //x-Coordinate of the bottom-right point of the frame rectangle
|
||||
#define FRAME_END_Y FRAME_START_Y +FRAME_HEIGHT-1 //y-Coordinate of the bottom-right point of the frame rectangle
|
||||
|
||||
//Callback for when the user touches the frame area to select a color region.
|
||||
//Note: It doesn't matter in which direction the user draws the rectangle, we'll normalize the coordinates later
|
||||
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
|
||||
POINT_STRUCT p = touch_get_last_point();
|
||||
switch(triggeredAction) {
|
||||
case PEN_DOWN: //The user just put down the pen
|
||||
point1.x = p.x-FRAME_START_X; //Calculate x-Coordinate relative to frame start
|
||||
point1.y = p.y-FRAME_START_Y; //Calculate y-Coordinate relative to frame start
|
||||
point1_valid= true; //The point1 is now valid
|
||||
break;
|
||||
case PEN_UP: //The user took the pen away
|
||||
if(point1_valid) { //only execute if point1 is valid
|
||||
point2.x = p.x-FRAME_START_X; //Calculate x-Coordinate relative to frame start
|
||||
point2.y = p.y-FRAME_START_Y; //Calculate y-Coordinate relative to frame start
|
||||
state = selected;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Prototype for tracking start/stop methods
|
||||
typedef void (*TRACKING_VOID_CALLBACK)(void* tracking_config);
|
||||
//Prototype for tracking update method
|
||||
typedef void (*TRACKING_BLOCK_CALLBACK)(void* tracking_config, struct Block* blocks, int num_blocks );
|
||||
|
||||
//Structure to save callbacks and settings of a tracking implementation
|
||||
typedef struct {
|
||||
TRACKING_VOID_CALLBACK start;
|
||||
TRACKING_VOID_CALLBACK stop;
|
||||
TRACKING_BLOCK_CALLBACK update;
|
||||
} TRACKING_CONFIG_STRUCT;
|
||||
|
||||
//Methods for our tracking implementation ahead
|
||||
|
||||
//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;
|
||||
return_value = pixy_command("runprog", INT8(0), END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
}
|
||||
|
||||
//Method/Callback to stop our tracking
|
||||
void tracking_our_stop(void* tracking_config) {
|
||||
//Stop pixy's data send programm
|
||||
int32_t response;
|
||||
int return_value;
|
||||
return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
//Variable which stores all the callbacks and settings for our tracking implementation
|
||||
static TRACKING_CONFIG_STRUCT tracking_our = {
|
||||
tracking_our_start,
|
||||
tracking_our_stop,
|
||||
tracking_our_update
|
||||
};
|
||||
|
||||
//Methods for reference tracking implementation ahead
|
||||
|
||||
//Method/Callback to start reference tracking
|
||||
void tracking_reference_start(void* tracking_config) {
|
||||
//Run reference tracking
|
||||
int32_t response;
|
||||
int return_value;
|
||||
return_value = pixy_command("runprog", INT8(2), END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
}
|
||||
|
||||
//Method/Callback to stop reference tracking
|
||||
void tracking_reference_stop(void* tracking_config) {
|
||||
//Stop reference tracking
|
||||
int32_t response;
|
||||
int return_value;
|
||||
return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
|
||||
}
|
||||
|
||||
//Method/Callback to calculate one step of the reference tracking
|
||||
void tracking_reference_update(void* tracking_config, struct Block* blocks, int num_blocks) {
|
||||
//Nothing to do here. Pixy does it all.
|
||||
}
|
||||
|
||||
//Variable which stores all the callbacks and settings for the reference tracking implementation
|
||||
static TRACKING_CONFIG_STRUCT tracking_reference = {
|
||||
tracking_reference_start,
|
||||
tracking_reference_stop,
|
||||
tracking_reference_update
|
||||
};
|
||||
|
||||
//Pointer to the currently active tracking implementation. See also tracking_set_mode
|
||||
static TRACKING_CONFIG_STRUCT* tracking_current;
|
||||
|
||||
//Method to set the current tracking implementation. This function is exported and should be called before getting the screen
|
||||
void tracking_set_mode(enum Tracking_Implementation impl) {
|
||||
//Depending on the enum value let tracking_current point to a different setting/callback structure
|
||||
switch(impl) {
|
||||
case OUR_TRACKING:
|
||||
tracking_current = &tracking_our;
|
||||
break;
|
||||
case REFERENCE_TRACKING:
|
||||
tracking_current = &tracking_reference;
|
||||
break;
|
||||
default:
|
||||
tracking_current=NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Callback for when the screen is entered/loaded
|
||||
static void enter(void* screen) {
|
||||
tft_clear(WHITE);
|
||||
|
||||
//"Back" button
|
||||
b_back.base.x1=5; //Start X of Button
|
||||
b_back.base.y1=5; //Start Y of Button
|
||||
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
|
||||
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
|
||||
b_back.txtcolor=WHITE; //Set foreground color
|
||||
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
|
||||
b_back.font=0; //Select Font
|
||||
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
|
||||
b_back.callback=b_back_cb; //Call b_back_cb as Callback
|
||||
gui_button_add(&b_back); //Register Button (and run the callback from now on)
|
||||
|
||||
|
||||
//"Select color" button
|
||||
b_select.base.x1=150;
|
||||
b_select.base.y1=5;
|
||||
b_select.base.x2=AUTO;
|
||||
b_select.base.y2=AUTO;
|
||||
b_select.txtcolor=WHITE;
|
||||
b_select.bgcolor=HEX(0xAE1010);
|
||||
b_select.font=0;
|
||||
b_select.text="Select Color";
|
||||
b_select.callback=b_select_cb;
|
||||
gui_button_add(&b_select);
|
||||
|
||||
//"Frame visible" checkbox
|
||||
c_frame_toggle.base.x1 = 50;
|
||||
c_frame_toggle.base.x2 = 50+16;
|
||||
c_frame_toggle.base.y1 = 5;
|
||||
c_frame_toggle.base.y2 = 5+16;
|
||||
c_frame_toggle.checked = frame_visible;
|
||||
c_frame_toggle.fgcolor = CHECKBOX_WIN_FG_COLOR;
|
||||
c_frame_toggle.callback = c_frame_toggle_cb;
|
||||
gui_checkbox_add(&c_frame_toggle);
|
||||
tft_print_line(73,8,BLACK,TRANSPARENT,0,"Show Video");
|
||||
|
||||
|
||||
//Area to select a "color region"
|
||||
a_area.hookedActions = PEN_DOWN | PEN_UP;
|
||||
a_area.x1 = FRAME_START_X;
|
||||
a_area.y1 = FRAME_START_Y;
|
||||
a_area.x2 = FRAME_END_X;
|
||||
a_area.y2 = FRAME_END_Y;
|
||||
a_area.callback = touchCB;
|
||||
//Do not register it here, we do that later
|
||||
|
||||
if(tracking_current==NULL) {
|
||||
state = error;
|
||||
} else {
|
||||
state = detecting; //Start with the detecting state
|
||||
}
|
||||
}
|
||||
|
||||
//Callback for when the screen is left/unloaded
|
||||
static void leave(void* screen) {
|
||||
//Remove buttons and checkbox
|
||||
gui_button_remove(&b_back);
|
||||
gui_button_remove(&b_select);
|
||||
gui_checkbox_remove(&c_frame_toggle);
|
||||
|
||||
if(state==selecting) { //the user left the screen in the "selecting" phase
|
||||
touch_unregister_area(&a_area); //remove the touch area
|
||||
}
|
||||
|
||||
if(state==tracking) { //the user left the screen in the "tracking" phase
|
||||
tracking_current->stop(tracking_current); //stop tracking
|
||||
pixy_led_set_RGB(0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
//Callback for when the screen should be updated
|
||||
//This is the main loop of the screen. This method will be called repeatedly
|
||||
static void update(void* screen) {
|
||||
switch(state) {
|
||||
case detecting: //Detecting State: Where we try to connect to the pixy
|
||||
if(pixy_init()==0) { //Pixy connection ok
|
||||
state = init; //Go to next state
|
||||
}
|
||||
break;
|
||||
|
||||
case init: //Init State: Where we start the tracking
|
||||
tracking_current->start(tracking_current);
|
||||
state=tracking;
|
||||
break;
|
||||
|
||||
case tracking: //Tracking state: Where we render the frame and the tracked objects
|
||||
pixy_service(); //Receive events (e.g. block-data) from pixy
|
||||
|
||||
if(pixy_blocks_are_new()) { //There are new blocks available
|
||||
if(frame_visible) { //If the user want's us to draw the video data
|
||||
pixy_render_full_frame(FRAME_START_X,FRAME_START_Y);
|
||||
} else { //the user want's a colored background
|
||||
tft_fill_rectangle(FRAME_START_X,FRAME_START_Y,FRAME_END_X,FRAME_END_Y,RGB(200,200,200));
|
||||
}
|
||||
|
||||
#define BLOCK_BUFFER_SIZE 5 //The maximum amount of blocks that we want to receive
|
||||
struct Block blocks[BLOCK_BUFFER_SIZE]; //Storage to receive blocks from pixy
|
||||
int blocks_received= pixy_get_blocks(BLOCK_BUFFER_SIZE,blocks); //Try to receive up to BLOCK_BUFFER_SIZE Blocks from pixy
|
||||
|
||||
if(blocks_received>=0) { //block receiving ok
|
||||
tracking_current->update(tracking_current,blocks,blocks_received); //apply tracking
|
||||
|
||||
//Draw blocks
|
||||
for(int i=0; i<blocks_received; i++) { //for each received block
|
||||
struct Block* block = &(blocks[i]);
|
||||
//block.x and block.y are the center coordinates of the object relative to the camera origin.
|
||||
uint16_t x = block->x-1+FRAME_START_X -block->width/2; //Calculate x-Coordinate on the display
|
||||
uint16_t y = block->y-1+FRAME_START_Y -block->height/2; //Calculate y-Coordinate on the display
|
||||
tft_draw_rectangle(x,y,x+block->width-1, y+block->height-1,WHITE); //Draw a white rectangle
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case preselecting: //Pre-Selecting State: Where we set up the color region selection
|
||||
{
|
||||
tracking_current->stop(tracking_current); //Stop tracking
|
||||
|
||||
pixy_render_full_frame(FRAME_START_X,FRAME_START_Y); //Render one frame
|
||||
|
||||
touch_register_area(&a_area); //Register touch area and receive events from now on
|
||||
point1_valid=false; //we start with an invalid point1
|
||||
|
||||
b_select.text="Abort"; //Change the button text to "Abort"
|
||||
gui_button_redraw(&b_select); //redraw button
|
||||
|
||||
state = selecting; //The user can now select a region
|
||||
}
|
||||
break;
|
||||
|
||||
case selected: //Selected State: Where we send the users selection to pixy
|
||||
{
|
||||
//Ensure that (x1,y1) represent the top-left point and (x2,y2) the bottom-right.
|
||||
unsigned int tmp;
|
||||
if(point1.x > point2.x){
|
||||
tmp = point1.x;
|
||||
point1.x = point2.x;
|
||||
point2.x = tmp;
|
||||
}
|
||||
|
||||
if(point1.y > point2.y){
|
||||
tmp = point1.y;
|
||||
point1.y = point2.y;
|
||||
point2.y = tmp;
|
||||
}
|
||||
//Send pixy the selected region
|
||||
pixy_cc_set_region(1,point1.x,point1.y,point2.x-point1.x,point2.y-point1.y);
|
||||
}
|
||||
//no break here: We want the following code to be executed as well
|
||||
|
||||
case abortselecting: //Abort-Selecting State: Where we deinitialize the stuff we used for region selection
|
||||
{
|
||||
touch_unregister_area(&a_area); //Remove the touch area. We'll no longer receive touch events
|
||||
|
||||
b_select.text="Select Color"; //Change the button text back to "Select Color"
|
||||
gui_button_redraw(&b_select); //redraw button
|
||||
|
||||
tracking_current->start(tracking_current); //Start tracking again
|
||||
state=tracking;
|
||||
}
|
||||
break;
|
||||
|
||||
case selecting: //Selecting State: Where we wait on the user to select a color region
|
||||
pixy_service(); //receive pixy events
|
||||
//wait on user to select the image area
|
||||
break;
|
||||
|
||||
case error: //Error State: Where we show an error message and leave the user no other choice than to click the backbutton
|
||||
//wait on user to click the back button
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Declare screen callbacks
|
||||
static SCREEN_STRUCT screen = {
|
||||
enter,
|
||||
leave,
|
||||
update
|
||||
};
|
||||
|
||||
|
||||
SCREEN_STRUCT* get_screen_tracking() {
|
||||
return &screen;
|
||||
}
|
||||
37
common/app/screen_tracking.h
Normal file
37
common/app/screen_tracking.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "screen.h"
|
||||
|
||||
/**
|
||||
* @addtogroup screens
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup tracking Tracking (Screen)
|
||||
* The Tracking-Screen shows the object-tracking and allows some configuration
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
/**
|
||||
* Enum which contains the available tracking implementations
|
||||
*/
|
||||
enum Tracking_Implementation {
|
||||
OUR_TRACKING, //!< Our own tracking PID implementation
|
||||
REFERENCE_TRACKING//!< Pixy's internal tracking implementation
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current Mode/Tracking Implementation. Call this before using the screen obtained by get_screen_tracking()
|
||||
* @param impl The new mode
|
||||
*/
|
||||
void tracking_set_mode(enum Tracking_Implementation impl);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the tracking screen
|
||||
* \sa gui_screen_navigate
|
||||
* @return
|
||||
*/
|
||||
SCREEN_STRUCT* get_screen_tracking();
|
||||
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
@@ -10,7 +10,7 @@ DIRECTORY_STRUCT* filesystem_dir_open(const char* path) {
|
||||
}
|
||||
|
||||
void filesystem_dir_close(DIRECTORY_STRUCT* dir) {
|
||||
filesystem_dir_close(dir);
|
||||
ll_filesystem_dir_close(dir);
|
||||
}
|
||||
|
||||
FILE_HANDLE* filesystem_file_open(const char* filename) {
|
||||
|
||||
@@ -4,68 +4,144 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @defgroup filesystem Filesystem
|
||||
* The Filesystem Module provides access to files and directories of a the native filesystem.
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* File Attributes used by implementation
|
||||
* See http://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#attributes for detailed description
|
||||
*/
|
||||
typedef enum {
|
||||
F_DIR=1,
|
||||
F_RDO=2,
|
||||
F_HID=4,
|
||||
F_SYS=8,
|
||||
F_ARC=16
|
||||
F_RDO=0x01,//!< File is readonly. You cannot write to it
|
||||
F_HID=0x02,//!< File is hidden
|
||||
F_SYS=0x04,//!< File is a system file
|
||||
F_DIR=0x10,//!< It's a directory and not a file
|
||||
F_ARC=0x20 //!< File has the archive flag set (probably unused)
|
||||
} FILE_ATTRIBUTES;
|
||||
|
||||
|
||||
/**
|
||||
* Structure which represents last modified date of a file / directory
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned year : 7; //year from 1980 (0..127)
|
||||
unsigned month: 4; //month (1..12)
|
||||
unsigned day: 5; //day (1..31)
|
||||
unsigned year : 7; //!< year from 1980 (0..127)
|
||||
unsigned month: 4; //!< month (1..12)
|
||||
unsigned day: 5; //!< day (1..31)
|
||||
} FILE_DATE_STRUCT;
|
||||
|
||||
|
||||
/**
|
||||
* Structure which represents last modified time of a file / directory
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned hour : 5; //hour (0..23)
|
||||
unsigned min: 6; //minute (0..59
|
||||
unsigned sec: 5; //second/2 (0..29)
|
||||
unsigned hour : 5; //!< hour (0..23)
|
||||
unsigned min: 6; //!< minute (0..59
|
||||
unsigned sec: 5; //!< second/2 (0..29)
|
||||
} FILE_TIME_STRUCT;
|
||||
|
||||
|
||||
/**
|
||||
* Structure which represents a file/directory entry. \sa DIRECTORY_STRUCT
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t fsize; /* File size */
|
||||
FILE_DATE_STRUCT fdate; /* Last modified date */
|
||||
FILE_TIME_STRUCT ftime; /* Last modified time */
|
||||
uint8_t fattrib; /* Attribute */
|
||||
char* fname; /* File name */
|
||||
uint32_t fsize; //!< File size in bytes. 0 for directories
|
||||
FILE_DATE_STRUCT fdate; //!< Last modified date
|
||||
FILE_TIME_STRUCT ftime; //!< Last modified time
|
||||
uint8_t fattrib; //!< File/Directory Attributes
|
||||
char* fname; //!< File/Directory name
|
||||
} FILE_STRUCT;
|
||||
|
||||
/**
|
||||
* Structure which represents an open directory with all it's entries. \sa filesystem_dir_open
|
||||
*/
|
||||
typedef struct {
|
||||
const char* path;
|
||||
uint16_t num_files;
|
||||
FILE_STRUCT* files;
|
||||
const char* path; //!< Directory path (absolute)
|
||||
uint16_t num_files; //!< Number of files/directories in this directory
|
||||
FILE_STRUCT* files; //!< An array with \ref num_files FILE_STRUCT entries
|
||||
} DIRECTORY_STRUCT;
|
||||
|
||||
/**
|
||||
* Structure which represents an open file. \sa filesystem_file_open
|
||||
*/
|
||||
typedef struct {
|
||||
const char* fname;
|
||||
uint32_t fpos;
|
||||
uint32_t fsize;
|
||||
const char* fname; //!< The absolute file name
|
||||
uint32_t fpos; //!< The current byte-position in the file. \sa filesystem_file_seek
|
||||
uint32_t fsize; //!< The total file size in bytes
|
||||
} FILE_HANDLE;
|
||||
|
||||
/**
|
||||
* Enum to represent the success or error-code of the filesystem_file_* functions
|
||||
*/
|
||||
typedef enum {
|
||||
F_OK,
|
||||
F_EOF,
|
||||
F_EACCESS,
|
||||
F_INVALIDPARAM,
|
||||
F_DISKERROR
|
||||
F_OK, //!< Everything ok
|
||||
F_EOF, //!< The write/read operation tried to write/read past the end of the file. This is not a fatal error.
|
||||
F_EACCESS, //!< The file can not be read/written due to access problems. This is a fatal error.
|
||||
F_INVALIDPARAM,//!< You passed invalid parameters to the function
|
||||
F_DISKERROR //!< A lowlevel disk-error occoured. This is a fatal error.
|
||||
} FILE_STATUS;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the filesystem.
|
||||
* Call this method before using any filesystem_* functions
|
||||
* @return true on success
|
||||
*/
|
||||
bool filesystem_init();
|
||||
|
||||
/**
|
||||
* Opens a directory and returns a structure which contains all files/subdirectories. \sa filesystem_dir_close()
|
||||
* @param path The absolute path to the directory to open/read
|
||||
* @return A Pointer to an initialized DIRECTORY_STRUCT on success, NULL on error
|
||||
*/
|
||||
DIRECTORY_STRUCT* filesystem_dir_open(const char* path);
|
||||
|
||||
/**
|
||||
* Closes a previously opened directory. Free's all allocated resources.
|
||||
* @param dir A Pointer to a DIRECTORY_STRUCT obtained by filesystem_dir_open().
|
||||
*/
|
||||
void filesystem_dir_close(DIRECTORY_STRUCT* dir);
|
||||
|
||||
/**
|
||||
* Opens a file for read/writing. \note Depending on the implementation you may only open one file at a time
|
||||
* @param filename The absolute file path
|
||||
* @return A Pointer to a FILE_HANDLE on success, NULL on error.
|
||||
*/
|
||||
FILE_HANDLE* filesystem_file_open(const char* filename);
|
||||
|
||||
/**
|
||||
* Closes a file.
|
||||
* @param handle The FILE_HANDLE obtained by filesystem_file_open()
|
||||
*/
|
||||
void filesystem_file_close(FILE_HANDLE* handle);
|
||||
|
||||
/**
|
||||
* Set's the read/write position to a new position
|
||||
* @param handle The FILE_HANDLE obtained by filesystem_file_open()
|
||||
* @param offset The new read/write position in bytes (absolute).
|
||||
* @return \ref F_OK on success, an error Code otherwise.
|
||||
*/
|
||||
FILE_STATUS filesystem_file_seek(FILE_HANDLE* handle, uint32_t offset);
|
||||
|
||||
/**
|
||||
* Reads some bytes from an open file.
|
||||
* @param handle The FILE_HANDLE obtained by filesystem_file_open()
|
||||
* @param buf The Buffer to write the bytes to
|
||||
* @param size The number of bytes to read
|
||||
* @return \ref F_OK on success, \ref F_EOF if less than \p size bytes could be read, an error Code otherwise.
|
||||
*/
|
||||
FILE_STATUS filesystem_file_read(FILE_HANDLE* handle, uint8_t* buf, uint32_t size);
|
||||
|
||||
/**
|
||||
* Writes some bytes to a open file.
|
||||
* \note Depending on the implementation the file may not be shrinked or expanded.
|
||||
* @param handle The FILE_HANDLE obtained by filesystem_file_open()
|
||||
* @param buf The Buffer to take the bytes from
|
||||
* @param size The number of bytes to write
|
||||
* @return \ref F_OK on success, \ref F_EOF if less than \p size bytes could be written, an error Code otherwise.
|
||||
*/
|
||||
FILE_STATUS filesystem_file_write(FILE_HANDLE* handle, uint8_t* buf, uint32_t size);
|
||||
|
||||
|
||||
/*@}*/
|
||||
|
||||
#endif /* FILESYSTEM_H */
|
||||
|
||||
|
||||
@@ -3,187 +3,152 @@
|
||||
#include "button.h"
|
||||
#include <string.h>
|
||||
|
||||
#define BRIGHTNESS_VAL 3 //How much the Brightness is in/decreased for button shadows (3 -> Add 1/3 off Full Value)
|
||||
/* The Idea is as follows:
|
||||
* When the user add's a button we create a touch area for that region and wait for PEN_DOWN events.
|
||||
* Once the user puts the pen down in this area we'll redraw the button with different shadows (feedback)
|
||||
* and we'll now wait on PEN_UP or PEN_LEAVE events.
|
||||
* If the user takes the pen away while in the area (PEN_UP), we call the provided user callback
|
||||
* Otherwise (PEN_LEAVE) we only restore the initial shadows
|
||||
*/
|
||||
|
||||
/* Possible improvements:
|
||||
* Move the button by 1 pixel while he is pressed, to create a "full 3d" experience
|
||||
* Add events for the case when the button is pressed for a long time, without release
|
||||
*/
|
||||
|
||||
//Method to calculate the shadow colors used to create the "3d" effect
|
||||
void calculate_shadows(uint16_t bgcolor, uint16_t* light_shadow, uint16_t* dark_shadow) {
|
||||
#define BRIGHTNESS_VAL 3 //How much the Brightness is in/decreased for button shadows (3 -> Add/Subtract 1/3 off Full Value)
|
||||
|
||||
uint16_t c_light,c_dark; //c_light and c_dark will be filled with a lighter and a darker color as the background color (for the shadows)
|
||||
uint8_t r,g,b;
|
||||
|
||||
//separate the channels of the 16-bit rgb565 color
|
||||
r=(bgcolor&0xF800)>>11;
|
||||
g=(bgcolor&0x07E0)>>5;
|
||||
b=(bgcolor&0x001F)>>0;
|
||||
|
||||
//For the light shadow color:
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) > 0x1F) //Adding one third would exceed the maximum of the red channel
|
||||
c_light=0xF800; //Use full red
|
||||
else //adding one third to the red channel is fine
|
||||
c_light=(r+0x1F/BRIGHTNESS_VAL)<<11; //Use same red as in the background, but add one third
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) > 0x3F) //same for the green channel
|
||||
c_light|=0x07E0;
|
||||
else
|
||||
c_light|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) > 0x1F) //and the blue channel
|
||||
c_light|=0x0018;
|
||||
else
|
||||
c_light|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
|
||||
//For the dark shadow color
|
||||
if(r > (0x1F/BRIGHTNESS_VAL)) //Subtracting one third would NOT exceed the minimum of the red channel
|
||||
c_dark=(r-0x1F/BRIGHTNESS_VAL)<<11; //Use same red as in the background, but subtract one third
|
||||
else //Subtracting one third would give us a number below zero
|
||||
c_dark=0x0000; //use no red channel
|
||||
if(g > (0x3F/BRIGHTNESS_VAL)) //Same for the green channel
|
||||
c_dark|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL)) //and the blue channel
|
||||
c_dark|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
|
||||
//Assign the calculated shadows to out parameters
|
||||
if(light_shadow!=NULL) *light_shadow = c_light;
|
||||
if(dark_shadow!=NULL) *dark_shadow = c_dark;
|
||||
|
||||
}
|
||||
|
||||
//Callback which is called when the user touches the touch-area we created for the button
|
||||
void buttons_cb(void* touchArea, TOUCH_ACTION triggeredAction)
|
||||
//Method shared between normal Buttons and Bitmap Buttons-> Look at comment in headerfile for explanation.
|
||||
{
|
||||
TOUCH_AREA_STRUCT * area = (TOUCH_AREA_STRUCT*)touchArea;
|
||||
BUTTON_STRUCT* button = (BUTTON_STRUCT*)touchArea;
|
||||
unsigned int c1,c2;
|
||||
unsigned char r,g,b;
|
||||
r=(button->bgcolor&0xF800)>>11;
|
||||
g=(button->bgcolor&0x07E0)>>5;
|
||||
b=(button->bgcolor&0x001F)>>0;
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c1=0xF800;
|
||||
else
|
||||
c1=(r+0x1F/BRIGHTNESS_VAL)<<11;
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) >0x3F)
|
||||
c1|=0x07E0;
|
||||
else
|
||||
c1|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c1|=0x0018;
|
||||
else
|
||||
c1|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
if(r > (0x1F/BRIGHTNESS_VAL))
|
||||
c2=(r-0x1F/BRIGHTNESS_VAL)<<11;
|
||||
else
|
||||
c2=0x0000;
|
||||
if(g > (0x3F/BRIGHTNESS_VAL))
|
||||
c2|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL))
|
||||
c2|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
|
||||
uint16_t c_light,c_dark; //c_light and c_dark will be filled with a lighter and a darker color as the background color (for the shadows)
|
||||
calculate_shadows(button->bgcolor,&c_light,&c_dark);
|
||||
|
||||
switch(triggeredAction)
|
||||
{
|
||||
case PEN_DOWN:
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c2); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c2);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c1); //S<EFBFBD>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c1); //Ost
|
||||
case PEN_DOWN: //If the user touches the area for the "first time"
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE; //for the future we only want PEN_UP and PEN_LEAVE events
|
||||
|
||||
//Draw shadows
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_dark); //North
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_dark);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_light); //South
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c_light); //East
|
||||
break;
|
||||
case PEN_UP:
|
||||
case PEN_LEAVE:
|
||||
area->hookedActions=PEN_DOWN;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c1); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c1);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c2); //S<EFBFBD>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c2); //Ost
|
||||
if(triggeredAction==PEN_UP && button->callback!=NULL)
|
||||
button->callback(button);
|
||||
case PEN_UP: //If the user took the pen away, while in the area (=button pressed!)
|
||||
case PEN_LEAVE: //or the user "slided out" of the area
|
||||
area->hookedActions=PEN_DOWN; //for the future we only want PEN_DOWN events
|
||||
|
||||
//Draw inverse shadows
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_light); //North
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_light);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_dark); //South
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c_dark); //East
|
||||
|
||||
if(triggeredAction==PEN_UP && button->callback!=NULL) //If the button got "pressed" instead of left, and the user provided a callback
|
||||
button->callback(button); //execute the user callback
|
||||
break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool gui_button_add(BUTTON_STRUCT* button)//Registers a button (fill Struct first). Return false if no more Space in the Pointertable (-->Change NUM_AREAS).
|
||||
bool gui_button_add(BUTTON_STRUCT* button)
|
||||
{
|
||||
if(touch_have_empty(1))
|
||||
if(touch_have_empty(1)) //Check if the touch module can handle one additional area
|
||||
{
|
||||
//Calculate width and height of the button text
|
||||
unsigned int strwidth=tft_font_width(button->font)*strlen(button->text);
|
||||
unsigned char strheight=tft_font_height(button->font);
|
||||
button->base.hookedActions=PEN_DOWN;
|
||||
button->base.callback = buttons_cb;
|
||||
if(button->base.x2==AUTO)
|
||||
button->base.x2= button->base.x1 -1 + strwidth+(tft_font_width(button->font)/2);
|
||||
else if((button->base.x2-button->base.x1+1)<(strwidth+2))
|
||||
return false;
|
||||
|
||||
if(button->base.y2==AUTO)
|
||||
button->base.y2=button->base.y1 -1 +strheight+(strheight/2);
|
||||
else if((button->base.y2-button->base.y1+1)<(strheight+2))
|
||||
return false;
|
||||
gui_button_redraw(button);
|
||||
return touch_register_area(&button->base);
|
||||
button->base.hookedActions=PEN_DOWN; //At first we are interested in PEN_DOWN events
|
||||
button->base.callback = buttons_cb; //Use our own callback for the touch area events
|
||||
|
||||
if(button->base.x2==AUTO) { //The user wants us to calculate the button width automatically
|
||||
//Use string width + half of a character width as button width
|
||||
button->base.x2= button->base.x1 -1 + strwidth+(tft_font_width(button->font)/2);
|
||||
} else if((button->base.x2-button->base.x1+1)<(strwidth+2)) { //the provided width is too small to fit the entire text
|
||||
return false; //report error
|
||||
}
|
||||
|
||||
if(button->base.y2==AUTO) { //The user wants us to calculate the button height automatically
|
||||
//Use one and a half character heights as button height
|
||||
button->base.y2=button->base.y1 -1 +strheight+(strheight/2);
|
||||
} else if((button->base.y2-button->base.y1+1)<(strheight+2)) { //the provided height is too small to fit the text
|
||||
return false;
|
||||
}
|
||||
gui_button_redraw(button); //call the redraw method, which will take care of drawing the entire button
|
||||
return touch_register_area(&button->base); //Register the touch area and receive events for this button, from now on
|
||||
}
|
||||
|
||||
return false; //no more touch areas left
|
||||
}
|
||||
|
||||
void gui_button_redraw(BUTTON_STRUCT* button)
|
||||
{
|
||||
//Calculate text dimensions and shadow colors
|
||||
unsigned int strwidth=tft_font_width(button->font)*strlen(button->text);
|
||||
unsigned char strheight=tft_font_height(button->font);
|
||||
unsigned char r,g,b;
|
||||
unsigned int c;
|
||||
r=(button->bgcolor&0xF800)>>11;
|
||||
g=(button->bgcolor&0x07E0)>>5;
|
||||
b=(button->bgcolor&0x001F)>>0;
|
||||
uint16_t c_light,c_dark;
|
||||
calculate_shadows(button->bgcolor,&c_light,&c_dark);
|
||||
|
||||
//Draw the background and the 4 lines (shadow colors)
|
||||
tft_fill_rectangle(button->base.x1+1,button->base.y1+1,button->base.x2-1,button->base.y2-1,button->bgcolor);
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c=0xF800;
|
||||
else
|
||||
c=(r+0x1F/BRIGHTNESS_VAL)<<11;
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) >0x3F)
|
||||
c|=0x07E0;
|
||||
else
|
||||
c|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c|=0x0018;
|
||||
else
|
||||
c|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c);//West
|
||||
if(r > (0x1F/BRIGHTNESS_VAL))
|
||||
c=(r-0x1F/BRIGHTNESS_VAL)<<11;
|
||||
else
|
||||
c=0x0000;
|
||||
if(g > (0x3F/BRIGHTNESS_VAL))
|
||||
c|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL))
|
||||
c|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c); //S<>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c); //Ost
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_light); //North
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_light);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_dark); //South
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c_dark); //East
|
||||
|
||||
//Draw the text
|
||||
tft_print_line(button->base.x1+(button->base.x2-button->base.x1+1-strwidth)/2,button->base.y1+(button->base.y2-button->base.y1+1-strheight)/2,button->txtcolor,button->bgcolor,button->font,button->text);
|
||||
|
||||
}
|
||||
|
||||
void gui_button_remove(BUTTON_STRUCT* button)
|
||||
{
|
||||
//We only need to unregister the touch area, as we have not allocated anything else
|
||||
touch_unregister_area((TOUCH_AREA_STRUCT*)button);
|
||||
}
|
||||
|
||||
/*
|
||||
bool guiAddBitmapButton (BITMAPBUTTON_STRUCT* button)
|
||||
{
|
||||
if(touchHaveEmpty(1))
|
||||
{
|
||||
button->base.hookedActions=PEN_DOWN;
|
||||
button->base.callback = buttons_cb;
|
||||
if(button->base.x2==AUTO)
|
||||
button->base.x2= button->base.x1 -1 + button->imgwidth + button->imgwidth/4;
|
||||
else if((button->base.x2-button->base.x1+1)<(button->imgwidth+2))
|
||||
return false;
|
||||
|
||||
if(button->base.y2==AUTO)
|
||||
button->base.y2=button->base.y1 -1 +button->imgheight + button->imgheight/4;
|
||||
else if((button->base.y2-button->base.y1+1)<(button->imgheight+2))
|
||||
return false;
|
||||
guiRedrawBitmapButton(button);
|
||||
return touchRegisterArea(&button->base);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void guiRedrawBitmapButton(BITMAPBUTTON_STRUCT* button)
|
||||
{
|
||||
|
||||
unsigned char r,g,b;
|
||||
unsigned int c;
|
||||
r=(button->bgcolor&0xF800)>>11;
|
||||
g=(button->bgcolor&0x07E0)>>5;
|
||||
b=(button->bgcolor&0x001F)>>0;
|
||||
tftFillRectangle(button->base.x1+1,button->base.y1+1,button->base.x2-1,button->base.y2-1,button->bgcolor);
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c=0xF800;
|
||||
else
|
||||
c=(r+0x1F/BRIGHTNESS_VAL)<<11;
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) >0x3F)
|
||||
c|=0x07E0;
|
||||
else
|
||||
c|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c|=0x0018;
|
||||
else
|
||||
c|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c);//West
|
||||
if(r > (0x1F/BRIGHTNESS_VAL))
|
||||
c=(r-0x1F/BRIGHTNESS_VAL)<<11;
|
||||
else
|
||||
c=0x0000;
|
||||
if(g > (0x3F/BRIGHTNESS_VAL))
|
||||
c|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL))
|
||||
c|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c); //S<>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c); //Ost
|
||||
tftDrawBitmapUnscaledStreamedRaw(button->base.x1+(button->base.x2-button->base.x1+1-button->imgwidth)/2,button->base.y1+(button->base.y2-button->base.y1+1-button->imgheight)/2,button->imgwidth,button->imgheight,button->filename);
|
||||
}
|
||||
void guiRemoveBitmapButton(BITMAPBUTTON_STRUCT* button)
|
||||
{
|
||||
touchUnregisterArea((TOUCH_AREA_STRUCT*)button);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef BUTTON_H
|
||||
#define BUTTON_H
|
||||
|
||||
#include "touch.h"
|
||||
|
||||
/**
|
||||
* @defgroup gui Gui
|
||||
* The Gui Module
|
||||
@@ -9,16 +11,21 @@
|
||||
|
||||
/**
|
||||
* @defgroup button Button
|
||||
* The Button Gui-Element
|
||||
* The Button Gui-Element is a clickable, rectangular box with a label inside.
|
||||
* When it is pressed and released you will be notified via the provided callback.
|
||||
*/
|
||||
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup button
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
|
||||
#include "touch.h"
|
||||
|
||||
/**
|
||||
* Prototype for Event Listeners (called when the button is pressed)
|
||||
* \note You should NOT execute long running things in this callback nor should you update the gui. But you can call gui_screen_navigate() for instance.
|
||||
* @param button The pointer to the BUTTON_STRUCT where to corresponding Button was pressed
|
||||
*/
|
||||
typedef void (*BUTTON_CALLBACK)(void *button);
|
||||
@@ -30,7 +37,7 @@ typedef void (*BUTTON_CALLBACK)(void *button);
|
||||
typedef struct {
|
||||
TOUCH_AREA_STRUCT base; //!< Basic geometry of the button. You only need to set the x1, y1, x2, y2 members of this struct.
|
||||
uint16_t bgcolor; //!< The 16-bit background color of the button
|
||||
BUTTON_CALLBACK callback; //!< Callback
|
||||
BUTTON_CALLBACK callback; //!< Callback which is executed when the button is pressed
|
||||
uint16_t txtcolor; //!< The 16-bit text color
|
||||
uint8_t font; //!< The number of the font to use
|
||||
const char *text; //!< The label of the button
|
||||
@@ -58,27 +65,7 @@ void gui_button_remove(BUTTON_STRUCT* button);
|
||||
*/
|
||||
void gui_button_redraw(BUTTON_STRUCT* button);
|
||||
|
||||
/*
|
||||
bool guiAddBitmapButton(BITMAPBUTTON_STRUCT* button);
|
||||
void guiRemoveBitmapButton(BITMAPBUTTON_STRUCT* button);
|
||||
void guiRedrawBitmapButton(BITMAPBUTTON_STRUCT* button);
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
typedef struct {
|
||||
TOUCH_AREA_STRUCT base;
|
||||
unsigned int bgcolor;
|
||||
BUTTON_CALLBACK callback; //Callback
|
||||
unsigned char imgwidth;
|
||||
unsigned char imgheight;
|
||||
char* filename;
|
||||
} BITMAPBUTTON_STRUCT;
|
||||
*/
|
||||
//Notice that the first 3 Members are Equal, so it's possible to cast it to a BUTTON_STRUCT even if it's a BITMAPBUTTON_STRUCT (when changeing only the first 3 Members).
|
||||
|
||||
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
|
||||
#endif /* BUTTON_H */
|
||||
|
||||
@@ -3,29 +3,44 @@
|
||||
#include "checkbox.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define BRIGHTNESS_VAL 2 //How much the Brightness is in/decreased for checkbox shadows (3 -> Add 1/3 off Full Value)
|
||||
#define ACTIVE_COLOR RGB(251,208,123)
|
||||
#define BORDER_COLOR RGB(29,82,129)
|
||||
#define BACKGROUND_COLOR WHITE
|
||||
/* The idea is as follows:
|
||||
* When the user creates a checkbox we create a touch area for that region and wait for PEN_DOWN events.
|
||||
* Once the user puts the pen down in this area we'll redraw the checkbox with different shadows (feedback)
|
||||
* and we'll now wait on PEN_UP or PEN_LEAVE events.
|
||||
* If the user takes the pen away while in the area (PEN_UP), we toggle the checkbox and we call the provided user callback
|
||||
* Otherwise (PEN_LEAVE) we only restore the initial shadows
|
||||
*/
|
||||
|
||||
|
||||
#define ACTIVE_COLOR RGB(251,208,123) //shadow color (inside of border)
|
||||
#define BORDER_COLOR RGB(29,82,129) //1px border color
|
||||
#define BACKGROUND_COLOR WHITE //Background color
|
||||
|
||||
//Callback which is called when the user touches the touch-area we created for the checkbox
|
||||
void checkboxes_cb(void* touchArea, TOUCH_ACTION triggeredAction)
|
||||
{
|
||||
TOUCH_AREA_STRUCT * area = (TOUCH_AREA_STRUCT*)touchArea;
|
||||
CHECKBOX_STRUCT* checkbox = (CHECKBOX_STRUCT*)touchArea;
|
||||
switch(triggeredAction)
|
||||
{
|
||||
case PEN_DOWN:
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE;
|
||||
case PEN_DOWN: //If the user touches the area for the "first time"
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE; //for the future we only want PEN_UP and PEN_LEAVE events
|
||||
|
||||
//Draw active shadows
|
||||
tft_draw_rectangle(checkbox->base.x1+1,checkbox->base.y1+1,checkbox->base.x2-1,checkbox->base.y2-1,ACTIVE_COLOR);
|
||||
tft_draw_rectangle(checkbox->base.x1+2,checkbox->base.y1+2,checkbox->base.x2-2,checkbox->base.y2-2,ACTIVE_COLOR);
|
||||
break;
|
||||
case PEN_UP:
|
||||
checkbox->checked=!checkbox->checked;
|
||||
gui_checkbox_update(checkbox);
|
||||
if(checkbox->callback!=NULL)
|
||||
checkbox->callback(checkbox,checkbox->checked);
|
||||
case PEN_LEAVE:
|
||||
area->hookedActions=PEN_DOWN;
|
||||
case PEN_UP: //If the user took the pen away, while in the area (=toggle checkbox!)
|
||||
checkbox->checked=!checkbox->checked; //Toggle checkbox state
|
||||
gui_checkbox_update(checkbox); //redraw/overdraw tickmark
|
||||
if(checkbox->callback!=NULL) { //The user provided a callback
|
||||
checkbox->callback(checkbox,checkbox->checked); //Call the provided callback with the new checked state
|
||||
}
|
||||
// no break statement here!
|
||||
case PEN_LEAVE: //if the user "slided out" of the area
|
||||
area->hookedActions=PEN_DOWN; //for the future we only want PEN_DOWN events
|
||||
|
||||
//Draw inactive shadows
|
||||
tft_draw_rectangle(checkbox->base.x1+1,checkbox->base.y1+1,checkbox->base.x2-1,checkbox->base.y2-1,BACKGROUND_COLOR);
|
||||
tft_draw_rectangle(checkbox->base.x1+2,checkbox->base.y1+2,checkbox->base.x2-2,checkbox->base.y2-2,BACKGROUND_COLOR);
|
||||
break;
|
||||
@@ -35,48 +50,66 @@ void checkboxes_cb(void* touchArea, TOUCH_ACTION triggeredAction)
|
||||
|
||||
bool gui_checkbox_add(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
if(touch_have_empty(1))
|
||||
if(touch_have_empty(1)) //Check if the touch module can handle one additional area
|
||||
{
|
||||
unsigned char size=0;
|
||||
checkbox->base.hookedActions=PEN_DOWN;
|
||||
checkbox->base.callback = checkboxes_cb;
|
||||
checkbox->base.hookedActions=PEN_DOWN; //At first we are interested in PEN_DOWN events
|
||||
checkbox->base.callback = checkboxes_cb; //Use our own callback for the touch area events
|
||||
|
||||
//Check the size of the checkbox
|
||||
if(checkbox->base.x2>checkbox->base.x1)
|
||||
size = checkbox->base.x2 - checkbox->base.x1;
|
||||
size = checkbox->base.x2 - checkbox->base.x1; //use width a as size
|
||||
if(checkbox->base.y2>checkbox->base.y1)
|
||||
{
|
||||
if((checkbox->base.y2 - checkbox->base.y1)>size)
|
||||
size = checkbox->base.y2 - checkbox->base.y1;
|
||||
if((checkbox->base.y2 - checkbox->base.y1)>size) //height is larger than size
|
||||
size = checkbox->base.y2 - checkbox->base.y1; //use height as size
|
||||
}
|
||||
if((size&0x01))
|
||||
size++;
|
||||
if(size==0) { //no size found (maybe swap x2 and x1 or y2 and y1 ?)
|
||||
return false; //signal error
|
||||
}
|
||||
if((size&0x01)) //the size is an odd number
|
||||
size++; //make size an even number
|
||||
|
||||
//Correct x2,y2 so that the checkbox is quadratic
|
||||
checkbox->base.x2 = checkbox->base.x1 + size;
|
||||
checkbox->base.y2 = checkbox->base.y1 + size;
|
||||
gui_checkbox_redraw(checkbox);
|
||||
return touch_register_area(&checkbox->base);
|
||||
|
||||
gui_checkbox_redraw(checkbox);//Call redraw method, which will take care of the drawing of the entire checkbox
|
||||
|
||||
return touch_register_area(&checkbox->base); //Register the touch area and receive events for this checkbox, from now on
|
||||
}
|
||||
return false;
|
||||
|
||||
return false; //no more touch areas left
|
||||
}
|
||||
|
||||
void gui_checkbox_redraw(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
//Draw background and border
|
||||
tft_fill_rectangle(checkbox->base.x1+1,checkbox->base.y1+1,checkbox->base.x2-1,checkbox->base.y2-1,BACKGROUND_COLOR);
|
||||
tft_draw_rectangle(checkbox->base.x1,checkbox->base.y1,checkbox->base.x2,checkbox->base.y2,BORDER_COLOR);
|
||||
if(checkbox->checked)
|
||||
gui_checkbox_update(checkbox);
|
||||
|
||||
if(checkbox->checked) { //checkbox is currently checked
|
||||
gui_checkbox_update(checkbox); //Call update method which will draw the tickmark
|
||||
}
|
||||
}
|
||||
|
||||
void gui_checkbox_remove(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
//We only need to unregister the touch area, as we have not allocated anything else
|
||||
touch_unregister_area((TOUCH_AREA_STRUCT*)checkbox);
|
||||
}
|
||||
|
||||
void gui_checkbox_update(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
unsigned int c = (checkbox->checked)? checkbox->fgcolor:BACKGROUND_COLOR;
|
||||
unsigned int c = (checkbox->checked)? checkbox->fgcolor:BACKGROUND_COLOR; //color to use for the tickmark
|
||||
|
||||
//helper points inside the checkbox
|
||||
unsigned int xcent = checkbox->base.x1+(checkbox->base.x2-checkbox->base.x1)*6/14;
|
||||
unsigned int yleft = checkbox->base.y2 - (xcent- checkbox->base.x1) - 1 ;
|
||||
unsigned int yright = checkbox->base.y2 - (checkbox->base.x2 - xcent) - 1 ;
|
||||
unsigned int ybot = checkbox->base.y2 - 4;
|
||||
|
||||
//Draw tickmark as a 3pixel wide line
|
||||
tft_draw_line(checkbox->base.x1+3,yleft-1,xcent,ybot -1,c);
|
||||
tft_draw_line(checkbox->base.x1+3,yleft,xcent,ybot ,c);
|
||||
tft_draw_line(checkbox->base.x1+3,yleft+1,xcent,ybot + 1,c);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef CHECKBOX_H
|
||||
#define CHECKBOX_H
|
||||
|
||||
#include "touch.h"
|
||||
|
||||
/**
|
||||
* @addtogroup gui
|
||||
*/
|
||||
@@ -8,26 +10,61 @@
|
||||
|
||||
/**
|
||||
* @defgroup checkbox Checkbox
|
||||
* The Checkbox Gui-Element
|
||||
* The Checkbox Gui-Element is a clickable, rectangular box with an optional tickmark inside of it.
|
||||
* When the checkbox is pressed and released it's tick state changes and you will be notified via the provided callback.
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup checkbox
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* Prototype for Event Listeners (called when the checkbox state has changed)
|
||||
* \note You should NOT execute long running things in this callback nor should you update the gui. But you can call gui_screen_navigate() for instance.
|
||||
* @param checkbox The pointer to the CHECKBOX_STRUCT where to corresponding Checkbox has changed the state
|
||||
* @param checked A boolean which indicates whether the checkbox is now checked or not.
|
||||
*/
|
||||
typedef void (*CHECKBOX_CALLBACK)(void *checkbox, bool checked);
|
||||
|
||||
typedef void (*CHECKBOX_CALLBACK)(void *checkbox, bool checked); //!< Function pointer used...
|
||||
/**
|
||||
* Structure to configure the Checkbox
|
||||
*/
|
||||
typedef struct {
|
||||
TOUCH_AREA_STRUCT base;
|
||||
uint16_t fgcolor;
|
||||
bool checked;
|
||||
CHECKBOX_CALLBACK callback; //Callback
|
||||
TOUCH_AREA_STRUCT base; //!< Basic geometry of the Checkbox. You only need to set the x1, y1, x2, y2 members of this struct.
|
||||
uint16_t fgcolor; //!< The 16-bit color of the tickmark
|
||||
bool checked; //!< A boolean which indicates whether or not the checkbox is currently checked.
|
||||
CHECKBOX_CALLBACK callback; //!< Callback which is executed when the checkbox changes state
|
||||
} CHECKBOX_STRUCT;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a checkbox. Your Callback will be called from now on, if the checkbox changes state
|
||||
* @param checkbox A Pointer to the preinitialized CHECKBOX_STRUCT
|
||||
* @return true on success
|
||||
*/
|
||||
bool gui_checkbox_add(CHECKBOX_STRUCT* checkbox);
|
||||
|
||||
/**
|
||||
* Removes the checkbox. You will no longer receive events for this checkbox. This function will not overdraw the region where the checkbox was located.
|
||||
* @param checkbox A Pointer to the CHECKBOX_STRUCT
|
||||
*/
|
||||
void gui_checkbox_remove(CHECKBOX_STRUCT* checkbox);
|
||||
|
||||
/**
|
||||
* Updates the checkbox. Call this function when you change the state of the checkbox through code.
|
||||
* @param checkbox A Pointer to the CHECKBOX_STRUCT
|
||||
*/
|
||||
void gui_checkbox_update(CHECKBOX_STRUCT* checkbox);
|
||||
|
||||
/**
|
||||
* Redraws the checkbox. Call this method if you have to redraw the entire screen or if you want to draw a checkbox on top of an image.
|
||||
* @param checkbox A Pointer to the CHECKBOX_STRUCT
|
||||
*/
|
||||
void gui_checkbox_redraw(CHECKBOX_STRUCT* checkbox);
|
||||
|
||||
#define CHECKBOX_WIN_FG_COLOR RGB(32,161,34)
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
|
||||
#endif /* CHECKBOX_H */
|
||||
|
||||
@@ -6,39 +6,51 @@
|
||||
#include <stddef.h> //for offsetof macro
|
||||
#include <stdlib.h> //for abs
|
||||
|
||||
/* The idea is as follows:
|
||||
* When the user add's a numupdown we create two buttons, one with a plus and one with a minus sign in it
|
||||
* When the user presses one of the buttons we check and increase the value and execute the provided user callback
|
||||
*/
|
||||
|
||||
#define BASE_COLOR RGB(90,90,90)
|
||||
|
||||
#define BASE_COLOR RGB(90,90,90) //Background color for the whole element
|
||||
|
||||
//Callback which is called when the user presses the "plus" button
|
||||
void button_up_cb(void* button)
|
||||
{
|
||||
//Get the pointer to the numupdown: subtract the offset of the buttonUp member in the struct from the button pointer
|
||||
NUMUPDOWN_STRUCT* element = button-offsetof(NUMUPDOWN_STRUCT,buttonUp);
|
||||
if(element->value<element->max) {
|
||||
element->value++;
|
||||
gui_numupdown_update(element);
|
||||
if(element->callback!=NULL) {
|
||||
element->callback(element,element->value);
|
||||
|
||||
if(element->value<element->max) { //old value lies below the maximum
|
||||
element->value++; //let's increase the value
|
||||
gui_numupdown_update(element); //and redraw everything
|
||||
if(element->callback!=NULL) { //the user provided a callback
|
||||
element->callback(element,element->value); //Call the user callback with the new value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Callback which is called when the user presses the "minus" button
|
||||
void button_down_cb(void* button)
|
||||
{
|
||||
//Get the pointer to the numupdown: subtract the offset of the buttonDown member in the struct from the button pointer
|
||||
NUMUPDOWN_STRUCT* element = button-offsetof(NUMUPDOWN_STRUCT,buttonDown);
|
||||
if(element->value>element->min) {
|
||||
element->value--;
|
||||
gui_numupdown_update(element);
|
||||
if(element->callback!=NULL) {
|
||||
element->callback(element,element->value);
|
||||
|
||||
if(element->value>element->min) { //old value lies above the minimum
|
||||
element->value--; //let's decrease the value
|
||||
gui_numupdown_update(element); //and redraw everything
|
||||
if(element->callback!=NULL) { //the user provided a callback
|
||||
element->callback(element,element->value); //Call the user callback with the new value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Method to calculate the number of characters needed to print the provided number in decimal notation (with optional sign)
|
||||
static uint8_t calc_text_width(int16_t val) {
|
||||
uint8_t width = 1 + (val<0); //1 if positive, 2 if negative (to let space for sign)
|
||||
val=abs(val);
|
||||
while(val>=10) {
|
||||
val/=10;
|
||||
width++;
|
||||
val=abs(val); //Make the number positive
|
||||
while(val>=10) { //while we have two or more digits
|
||||
val/=10; //remove one digit
|
||||
width++; //add one character
|
||||
}
|
||||
return width;
|
||||
}
|
||||
@@ -46,24 +58,24 @@ static uint8_t calc_text_width(int16_t val) {
|
||||
|
||||
bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
if(touch_have_empty(2)) //We require 2 TouchAreas (for Buttons)
|
||||
if(touch_have_empty(2)) //Check if the touch module can handle two additional areas
|
||||
{
|
||||
if(numupdown->min > numupdown->max) return false;
|
||||
|
||||
if(numupdown->value > numupdown->max) {
|
||||
numupdown->value = numupdown->max;
|
||||
}
|
||||
if(numupdown->value < numupdown->min) {
|
||||
numupdown->value = numupdown->min;
|
||||
} else if(numupdown->value > numupdown->max) {
|
||||
numupdown->value = numupdown->max;
|
||||
if(numupdown->min > numupdown->max) { //min is bigger than max?
|
||||
return false; //invalid parameter
|
||||
}
|
||||
|
||||
uint8_t tw1 = calc_text_width(numupdown->max);
|
||||
uint8_t tw2 = calc_text_width(numupdown->min);
|
||||
if(tw2 > tw1) tw1 = tw2;
|
||||
uint8_t width= tft_font_width(0)*(tw1+1);
|
||||
if(numupdown->value < numupdown->min) { //value is smaller than min?
|
||||
numupdown->value = numupdown->min; //normalize value
|
||||
} else if(numupdown->value > numupdown->max) { //value is bigger than max?
|
||||
numupdown->value = numupdown->max; //normalize value
|
||||
}
|
||||
|
||||
uint8_t tw1 = calc_text_width(numupdown->max); //Calculate character width to render maximum value
|
||||
uint8_t tw2 = calc_text_width(numupdown->min); //Calculate character width to render minimum value
|
||||
if(tw2 > tw1) tw1 = tw2; //ensure tw1 contains the larger number of the two
|
||||
uint8_t width= tft_font_width(0)*(tw1+1); //Calculate width of the number area
|
||||
|
||||
//Add "minus" button to the left side of the number area
|
||||
numupdown->buttonDown.base.x1=numupdown->x;
|
||||
numupdown->buttonDown.base.y1=numupdown->y;
|
||||
numupdown->buttonDown.base.x2=AUTO;
|
||||
@@ -74,6 +86,8 @@ bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
|
||||
numupdown->buttonDown.txtcolor=WHITE;
|
||||
numupdown->buttonDown.callback = button_down_cb;
|
||||
gui_button_add(&numupdown->buttonDown);
|
||||
|
||||
//Add "plus" button to the right side of the number area
|
||||
numupdown->buttonUp.base.x1=numupdown->buttonDown.base.x2+width+2;
|
||||
numupdown->buttonUp.base.y1=numupdown->y;
|
||||
numupdown->buttonUp.base.x2=AUTO;
|
||||
@@ -85,36 +99,45 @@ bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
|
||||
numupdown->buttonUp.callback = button_up_cb;
|
||||
gui_button_add(&numupdown->buttonUp);
|
||||
|
||||
//Draw background and label of the number area
|
||||
tft_fill_rectangle(numupdown->buttonDown.base.x2+2,numupdown->y,numupdown->buttonDown.base.x2+width,numupdown->buttonUp.base.y2,BASE_COLOR);
|
||||
tft_print_formatted(numupdown->buttonDown.base.x2+2+tft_font_width(0)/2,numupdown->y+tft_font_height(0)/2,numupdown->fgcolor,BASE_COLOR,0,"%*d",tw1,numupdown->value);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return false; //not enough touch areas left
|
||||
}
|
||||
|
||||
void gui_numupdown_remove(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
//remove the two buttons, we have no other allocated resources
|
||||
gui_button_remove(&numupdown->buttonUp);
|
||||
gui_button_remove(&numupdown->buttonDown);
|
||||
}
|
||||
|
||||
|
||||
void gui_numupdown_redraw(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
//redraw the two buttons
|
||||
gui_button_redraw(&numupdown->buttonUp);
|
||||
gui_button_redraw(&numupdown->buttonDown);
|
||||
|
||||
//call update method which will take care of the number-area rendering
|
||||
gui_numupdown_update(numupdown);
|
||||
}
|
||||
|
||||
void gui_numupdown_update(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
|
||||
|
||||
//Calculate the number area width again (see above)
|
||||
uint8_t tw1 = calc_text_width(numupdown->max);
|
||||
uint8_t tw2 = calc_text_width(numupdown->min);
|
||||
if(tw2 > tw1) tw1 = tw2;
|
||||
uint8_t width= tft_font_width(0)*(tw1+1);
|
||||
|
||||
//Draw background and label of the number area
|
||||
tft_fill_rectangle(numupdown->buttonDown.base.x2+2,numupdown->y,numupdown->buttonDown.base.x2+width,numupdown->buttonUp.base.y2,BASE_COLOR);
|
||||
tft_print_formatted(numupdown->buttonDown.base.x2+2+tft_font_width(0)/2,numupdown->y+tft_font_height(0)/2,numupdown->fgcolor,BASE_COLOR,0,"%*d",tw1,numupdown->value);
|
||||
}
|
||||
|
||||
void gui_numupdown_redraw(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
gui_button_redraw(&numupdown->buttonUp);
|
||||
gui_button_redraw(&numupdown->buttonDown);
|
||||
gui_numupdown_update(numupdown);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef NUMUPDOWN_H
|
||||
#define NUMUPDOWN_H
|
||||
|
||||
#include "button.h"
|
||||
|
||||
/**
|
||||
* @addtogroup gui
|
||||
*/
|
||||
@@ -10,32 +12,62 @@
|
||||
* @defgroup numupdown NummericUpDown
|
||||
* The NummericUpDown Gui Element
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup numupdown
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* Prototype for Event Listeners (called when the NummericUpDown value has changed)
|
||||
* \note You should NOT execute long running things in this callback nor should you update the gui. But you can call gui_screen_navigate() for instance.
|
||||
* @param numupdown The pointer to the NUMUPDOWN_STRUCT where to corresponding NummericUpDown has changed it's value
|
||||
* @param value The new value of the NummericUpDown
|
||||
*/
|
||||
typedef void (*NUMUPDOWN_CALLBACK)(void *numupdown, int16_t value);
|
||||
|
||||
#include "button.h"
|
||||
|
||||
typedef void (*NUMUPDOWN_CALLBACK)(void *numupdown, int16_t value); //!< Function pointer used...
|
||||
/**
|
||||
* Structure to configure the NummericUpDown
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint16_t fgcolor;
|
||||
int16_t value;
|
||||
int16_t min;
|
||||
int16_t max;
|
||||
NUMUPDOWN_CALLBACK callback; //Callback
|
||||
uint16_t x; //!< The x-Coordinate of the Top-Left Starting Point.
|
||||
uint16_t y; //!< The y-Coordinate of the Top-Left Starting Point.
|
||||
uint16_t fgcolor; //!< The 16-bit color of the value-text
|
||||
int16_t value; //!< The current/default value
|
||||
int16_t min; //!< The minimum possible value (inclusive)
|
||||
int16_t max; //!< The maximum possible value (inclusive)
|
||||
NUMUPDOWN_CALLBACK callback; //!< Callback which is executed when the value changes
|
||||
|
||||
//Internally used:
|
||||
BUTTON_STRUCT buttonUp;
|
||||
BUTTON_STRUCT buttonDown;
|
||||
BUTTON_STRUCT buttonUp; //!< For internal use, don't change, don't initialize
|
||||
BUTTON_STRUCT buttonDown; //!< For internal use, don't change, don't initialize
|
||||
} NUMUPDOWN_STRUCT;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a NummericUpDown. Your Callback will be called from now on, if the numupdown's value changes
|
||||
* @param numupdown A Pointer to the preinitialized NUMUPDOWN_STRUCT
|
||||
* @return true on success
|
||||
*/
|
||||
bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown);
|
||||
|
||||
/**
|
||||
* Removes the NummericUpDown. You will no longer receive events for this numupdown. This function will not overdraw the region where the numupdown was located.
|
||||
* @param numupdown A Pointer to the NUMUPDOWN_STRUCT
|
||||
*/
|
||||
void gui_numupdown_remove(NUMUPDOWN_STRUCT* numupdown);
|
||||
|
||||
/**
|
||||
* Updates the NummericUpDown. Call this function when you change the value/min/max of the numupdown through code.
|
||||
* @param numupdown A Pointer to the NUMUPDOWN_STRUCT
|
||||
*/
|
||||
void gui_numupdown_update(NUMUPDOWN_STRUCT* numupdown);
|
||||
|
||||
/**
|
||||
* Redraws the NummericUpDown. Call this method if you have to redraw the entire screen or if you want to draw a numupdown on top of an image.
|
||||
* @param numupdown A Pointer to the NUMUPDOWN_STRUCT
|
||||
*/
|
||||
void gui_numupdown_redraw(NUMUPDOWN_STRUCT* numupdown);
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
|
||||
#endif /* NUMUPDOWN_H */
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
#include "screen.h"
|
||||
|
||||
/* The idea is as follows:
|
||||
* We only call screen callbacks from the gui_screen_update() method, which is called from the applications main loop.
|
||||
* Instructions to switch the screen will be delayed until the gui_screen_update() method is called again.
|
||||
* This makes it safe to change the screen from an touch interrupt (e.g. button callback)
|
||||
*/
|
||||
|
||||
static SCREEN_STRUCT* screen_list = NULL;
|
||||
static SCREEN_STRUCT* screen_current = NULL;
|
||||
static volatile SCREEN_STRUCT* screen_goto = NULL;
|
||||
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
|
||||
|
||||
SCREEN_STRUCT* gui_screen_get_current() {
|
||||
return screen_current;
|
||||
}
|
||||
|
||||
|
||||
void gui_screen_update() {
|
||||
if(screen_goto!=NULL) { //we received the task to switch the screen
|
||||
SCREEN_STRUCT* go = (SCREEN_STRUCT*) screen_goto; //Backup volatile variable
|
||||
screen_goto=NULL;
|
||||
if(go->next!=NULL) { //we're going back
|
||||
if(go->next!=screen_current) return; //list corrupted?
|
||||
screen_current->on_leave(screen_current);
|
||||
go->next=NULL;
|
||||
} else { //we're going forward
|
||||
screen_goto=NULL; //reset the "goto instruction", since we're processing it now
|
||||
if(go->next!=NULL) { //The screen is not the last in the list, so we're going back
|
||||
if(go->next!=screen_current) { //this condition should always be false
|
||||
return; //list corrupted?
|
||||
}
|
||||
screen_current->on_leave(screen_current); //let the current screen free/unregister it's resources
|
||||
go->next=NULL; //remove the current screen from the list
|
||||
} else { //we're going forward (to a new screen)
|
||||
if(screen_current!=NULL) { //this is not the first screen
|
||||
screen_current->on_leave(screen_current);
|
||||
screen_current->next = go;
|
||||
screen_current->on_leave(screen_current); //let the current screen free/unregister it's resources
|
||||
screen_current->next = go; //append the new screen to the end of the list
|
||||
} else { //first screen ever seen
|
||||
screen_list=go;
|
||||
screen_list=go; //set the new screen as list-head
|
||||
}
|
||||
}
|
||||
go->on_enter(go);
|
||||
screen_current =go;
|
||||
go->on_enter(go); //let the new screen allocate/register it's resources
|
||||
screen_current = go; //the new screen is now the current screen. Transition done
|
||||
}
|
||||
|
||||
if(screen_current!=NULL) { //A screen has been set
|
||||
@@ -37,16 +45,18 @@ void gui_screen_update() {
|
||||
|
||||
|
||||
bool gui_screen_navigate(SCREEN_STRUCT* screen) {
|
||||
if(screen==NULL) return false;
|
||||
screen->next = NULL;
|
||||
screen_goto=screen; //send message to main loop, to switch the screen
|
||||
if(screen==NULL) { //invalid argument passed
|
||||
return false;
|
||||
}
|
||||
screen->next = NULL; //this will become the new tail of the list, so the next pointer must be NULL
|
||||
screen_goto=screen; //"send message" to main loop, to switch the screen
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool gui_screen_back() {
|
||||
if(screen_list==NULL) return false;
|
||||
if(screen_list==NULL) { //the list head is emtpy, nothing to go back to
|
||||
return false;
|
||||
}
|
||||
SCREEN_STRUCT* current = screen_list;
|
||||
SCREEN_STRUCT* last = NULL;
|
||||
//Find second last element in list
|
||||
@@ -54,15 +64,8 @@ bool gui_screen_back() {
|
||||
last = current;
|
||||
current = current->next;
|
||||
}
|
||||
if(last==NULL) return false; //There's only a single screen.
|
||||
if(current!=screen_current) return false; //List corrupted?
|
||||
screen_goto=last; //send message to main loop, to switch the screen
|
||||
if(last==NULL) return false; //There's only a single screen, there's no going back here
|
||||
if(current!=screen_current) return false; //The last entry in the list is not the current screen. List corrupted?
|
||||
screen_goto=last; //"send message" to main loop, to switch the screen
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef SCREEN_H
|
||||
#define SCREEN_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @addtogroup gui
|
||||
*/
|
||||
@@ -8,38 +11,65 @@
|
||||
|
||||
/**
|
||||
* @defgroup screen Screen
|
||||
* The Screen Submodule
|
||||
* 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.
|
||||
* The implemented screens of the application are documented in the \ref screens module.
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup screen
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void (*SCREEN_CALLBACK)(void* screen); //!< Function pointer used...
|
||||
/**
|
||||
* Prototype for Event Listeners (called when the screen is entered, left or should be updated)
|
||||
* @param screen The pointer to the SCREEN_STRUCT where the event occurred
|
||||
*/
|
||||
typedef void (*SCREEN_CALLBACK)(void* screen);
|
||||
|
||||
/**
|
||||
* Structure to configure the Screen
|
||||
*/
|
||||
typedef struct SCREEN_S{
|
||||
SCREEN_CALLBACK on_enter;
|
||||
SCREEN_CALLBACK on_leave;
|
||||
SCREEN_CALLBACK on_update;
|
||||
|
||||
|
||||
struct SCREEN_S* next; //Used internally. do not modify
|
||||
SCREEN_CALLBACK on_enter; //!< The Callback which is called when the screen is entered. Add/Register all UI-Elements here
|
||||
SCREEN_CALLBACK on_leave; //!< The Callback which is called when the screen is left. Remove/Unregister all UI-Elements here
|
||||
SCREEN_CALLBACK on_update; //!< The Callback which is called repeatedly when the screen should be updated. Update/Redraw all UI-Elements here
|
||||
|
||||
struct SCREEN_S* next; //!< Used internally. do not modify, do not initialize
|
||||
} SCREEN_STRUCT;
|
||||
|
||||
|
||||
//Navigate to the given string as soon as the app enters the main loop again. Method can be called from an interrupt
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param screen A Pointer to the preinitialized SCREEN_STRUCT
|
||||
* @return true on success
|
||||
*/
|
||||
bool gui_screen_navigate(SCREEN_STRUCT* screen);
|
||||
|
||||
//Navigate one screen back as soon as the app enters the main loop again. Method can be called from an interrupt
|
||||
/**
|
||||
* Navigate one screen back as soon as the app enters the main loop again.
|
||||
* It's safe to call this method from an interrupt
|
||||
* @return true on success
|
||||
*/
|
||||
bool gui_screen_back();
|
||||
|
||||
//Returns the current active screen
|
||||
|
||||
/**
|
||||
* Returns the currently active screen
|
||||
* @return A Pointer to the active SCREEN_STRUCT
|
||||
*/
|
||||
SCREEN_STRUCT* gui_screen_get_current();
|
||||
|
||||
//Updates/switches the screens. Call this from the app main loop, as fast as you can.
|
||||
/**
|
||||
* Updates the current screen. Switches the screen if gui_screen_navigate() or gui_screen_back() have been called since the last call to this method.
|
||||
* This method should be called repeatedly from the main loop (e.g. app_process())
|
||||
*/
|
||||
void gui_screen_update();
|
||||
|
||||
/*@}@}*/
|
||||
/*@}*/
|
||||
|
||||
#endif /* SCREEN_H */
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
#include "filesystem.h"
|
||||
|
||||
/**
|
||||
* @addtogroup lowlevel
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup ll_filesystem Filesystem (LowLevel)
|
||||
* Low level functions for the \ref filesystem module
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup ll_filesystem
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
bool ll_filesystem_init();
|
||||
|
||||
DIRECTORY_STRUCT* ll_filesystem_dir_open(const char* path);
|
||||
@@ -9,3 +26,5 @@ void ll_filesystem_file_close(FILE_HANDLE* handle);
|
||||
FILE_STATUS ll_filesystem_file_seek(FILE_HANDLE* handle, uint32_t offset);
|
||||
FILE_STATUS ll_filesystem_file_read(FILE_HANDLE* handle, uint8_t* buf, uint32_t size);
|
||||
FILE_STATUS ll_filesystem_file_write(FILE_HANDLE* handle, uint8_t* buf, uint32_t size);
|
||||
|
||||
/*@}*/
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup lowlevel LowLevel
|
||||
* The Low-Level platform abstraction layer
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup ll_system System (LowLevel)
|
||||
* Low level functions of the \ref system Module
|
||||
*/
|
||||
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup ll_system
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
bool ll_system_init();
|
||||
void ll_system_delay(uint32_t msec);
|
||||
void ll_system_process();
|
||||
void ll_system_toggle_led();
|
||||
|
||||
/*@}*/
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/**
|
||||
* @addtogroup lowlevel
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup ll_tft TFT (LowLevel)
|
||||
* Low level functions for the \ref tft module
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup ll_tft
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
// init functions
|
||||
bool ll_tft_init();
|
||||
|
||||
@@ -19,6 +36,6 @@ uint8_t ll_tft_font_height(uint8_t fontnum);
|
||||
uint8_t ll_tft_font_width(uint8_t fontnum);
|
||||
void ll_tft_draw_char(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, char c);
|
||||
|
||||
|
||||
/*@}*/
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @addtogroup lowlevel
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @defgroup ll_touch Touch (LowLevel)
|
||||
* Low level functions for the \ref touch module
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* @addtogroup ll_touch
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
bool ll_touch_init();
|
||||
|
||||
|
||||
/*@}*/
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#define __PIXY_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include "pixydefs.h"
|
||||
|
||||
// Pixy C API //
|
||||
@@ -27,6 +26,13 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup pixy Pixy
|
||||
* The Pixy Module
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
#define PIXY_MAX_SIGNATURE 7
|
||||
|
||||
// Pixy x-y position values
|
||||
@@ -164,7 +170,7 @@ extern "C"
|
||||
|
||||
/**
|
||||
@brief Enable or disable pixy camera auto white balance.
|
||||
@param enable 1: Enable white balance.
|
||||
@param value 1: Enable white balance.
|
||||
0: Disable white balance.
|
||||
@return 0 Success
|
||||
@return Negative Error
|
||||
@@ -220,7 +226,7 @@ extern "C"
|
||||
@return 0 Success
|
||||
@return Negative Error
|
||||
*/
|
||||
int pixy_cam_set_exposure_compensation(uint8_t gain, uint16_t compensation);
|
||||
int pixy_cam_set_exposure_compensation(uint8_t gain, uint16_t comp);
|
||||
|
||||
/**
|
||||
@brief Get pixy camera exposure compensation.
|
||||
@@ -229,7 +235,7 @@ extern "C"
|
||||
@return 0 Success
|
||||
@return Negative Error
|
||||
*/
|
||||
int pixy_cam_get_exposure_compensation(uint8_t * gain, uint16_t * compensation);
|
||||
int pixy_cam_get_exposure_compensation(uint8_t * gain, uint16_t * comp);
|
||||
|
||||
/**
|
||||
@brief Set pixy camera brightness.
|
||||
@@ -279,6 +285,8 @@ extern "C"
|
||||
*/
|
||||
int pixy_get_firmware_version(uint16_t * major, uint16_t * minor, uint16_t * build);
|
||||
|
||||
/*@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @defgroup system System
|
||||
* The System Module provides access to delay functions, leds and provides a system init function
|
||||
@@ -8,9 +11,6 @@
|
||||
/*@{*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Initializes the system. Call this method at the start of your app_init() function and before using any system_* functions
|
||||
* @return true on success
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "filesystem.h"
|
||||
|
||||
/* The idea is as follows:
|
||||
* Most of the tft_* functions can be forwarded to the lowlevel implementation.
|
||||
* The exceptions are commented below.
|
||||
* Make sure to have a look at the doxygen comments for the lowlevel functions and for the tft_* functions
|
||||
*/
|
||||
|
||||
/* Possible improvements:
|
||||
* For formatted printing implement putchar, instead of writing into a buffer and drawing that buffer afterwards
|
||||
*/
|
||||
|
||||
bool tft_init() {
|
||||
return ll_tft_init();
|
||||
@@ -51,19 +62,82 @@ uint8_t tft_font_width(uint8_t fontnum) {
|
||||
return ll_tft_font_width(fontnum);
|
||||
}
|
||||
|
||||
//Print line can be done with multiple calls to draw_char
|
||||
void tft_print_line(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, const char* text) {
|
||||
if(font>=ll_tft_num_fonts()) return;
|
||||
for(int i=0; i<strlen(text); i++) {
|
||||
ll_tft_draw_char(x,y,color,bgcolor, font, text[i]);
|
||||
x+=ll_tft_font_width(font);
|
||||
if(font>=ll_tft_num_fonts()) return; //invalid font index
|
||||
for(int i=0; i<strlen(text); i++) { //for each char in the line
|
||||
ll_tft_draw_char(x,y,color,bgcolor, font, text[i]); //draw the char
|
||||
x+=ll_tft_font_width(font); //and increase the x position
|
||||
}
|
||||
}
|
||||
|
||||
//Printing a formatted line can be done by printing the line in a buffer using "sprintf" and then calling print_line
|
||||
void tft_print_formatted(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, const char* format, ...) {
|
||||
static char buffer[256]; //not sure if that's the best solution. It would propbably better to implement putchar and use vprintf
|
||||
static char buffer[128]; //buffer to save the formatted text into
|
||||
|
||||
//Since we have variable arguments, we need to forward them. We have to use vsprintf instead of sprintf for that.
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
vsprintf(buffer,format,args);
|
||||
tft_print_line(x,y,color,bgcolor,font,buffer);
|
||||
va_end(args);
|
||||
va_start (args, format); //start the varg-list
|
||||
vsprintf(buffer,format,args); //let vsprintf render the formatted string
|
||||
tft_print_line(x,y,color,bgcolor,font,buffer); //print the string as normal text
|
||||
va_end(args); //end the varg-list
|
||||
}
|
||||
|
||||
bool tft_draw_bitmap_file_unscaled(uint16_t x, uint16_t y, const char* filename) {
|
||||
//This method reads a .bmp file from the filesystem and tries to draw it.
|
||||
//Note: The bmp implementation is not complete, it has some limitations and it makes assumptions. See doxygen comment for this method.
|
||||
//Source Copied and adapted from: http://stackoverflow.com/a/17040962/2606757
|
||||
|
||||
FILE_HANDLE* file = filesystem_file_open(filename); //try to open the file
|
||||
if(file==NULL) { //file opening failed
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char info[54];
|
||||
if(filesystem_file_read(file,info,54)!=F_OK) { //try to read the 54 byte header
|
||||
filesystem_file_close(file);
|
||||
return false; //reading the header failed
|
||||
}
|
||||
|
||||
// extract image height and width from header
|
||||
uint32_t width = *(uint32_t*)&info[18]; //width in pixel
|
||||
uint32_t height = *(uint32_t*)&info[22]; //height in pixel
|
||||
uint16_t depth = *(uint16_t*)&info[28]; //bit's per pixel (color depth)
|
||||
depth/=8; //we want the number of bytes per pixel
|
||||
|
||||
filesystem_file_seek(file,*(uint32_t*)&info[10]); //seek to the place where img data begins
|
||||
|
||||
uint32_t row_padded = (width*depth + 3) & (~3); //row size must be aligned to 4 bytes
|
||||
|
||||
unsigned char data [row_padded]; //allocate space for one row (incl. padding)
|
||||
|
||||
for(int i = 0; i < height; i++) //for each row
|
||||
{
|
||||
filesystem_file_read(file,data,row_padded); //read row into buffer
|
||||
for(int j = 0; j < width*depth; j += depth) //for each pixel
|
||||
{
|
||||
unsigned char a,r,g,b;
|
||||
if(depth==4) { //a,r,g,b 8bit each
|
||||
a = data[j];
|
||||
r = data[j+1];
|
||||
g = data[j+2];
|
||||
b = data[j+3];
|
||||
} else if (depth==3) { // b,g,r, 8bit each
|
||||
a = 255;
|
||||
r = data[j+2];
|
||||
g = data[j+1];
|
||||
b = data[j];
|
||||
}
|
||||
|
||||
if(a!=0) {
|
||||
//bmp's are stored "bottom-up", so we start drawing at the bottom
|
||||
tft_draw_pixel(x+j/depth,y+height-1-i,RGB(r,g,b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filesystem_file_close(file);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef TFT_H
|
||||
#define TFT_H
|
||||
|
||||
#include<stdbool.h>
|
||||
#include<stdint.h>
|
||||
|
||||
/**
|
||||
* @defgroup tft TFT
|
||||
* The TFT Modul provides access to the display
|
||||
@@ -13,9 +16,6 @@
|
||||
/*@{*/
|
||||
|
||||
|
||||
#include<stdbool.h>
|
||||
#include<stdint.h>
|
||||
|
||||
/**
|
||||
* Creates a 16bit color from 8bit * 3 colors (r,g,b)
|
||||
* @return
|
||||
@@ -67,7 +67,7 @@ void tft_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t
|
||||
* Draws a pixel onto the display.
|
||||
* @param x The x-Coordinate of the pixel
|
||||
* @param y The y-Coordinate of the pixel
|
||||
* @param The 16-bit color to draw the pixel with
|
||||
* @param color The 16-bit color to draw the pixel with
|
||||
*/
|
||||
void tft_draw_pixel(uint16_t x,uint16_t y,uint16_t color);
|
||||
|
||||
@@ -78,7 +78,7 @@ void tft_draw_pixel(uint16_t x,uint16_t y,uint16_t color);
|
||||
* @param y1 The y-Coordinate of the start-point
|
||||
* @param x2 The x-Coordinate of the end-point
|
||||
* @param y2 The y-Coordinate of the end-point
|
||||
* @param The 16-bit color to draw the pixel with
|
||||
* @param color The 16-bit color to draw the pixel with
|
||||
*/
|
||||
void tft_draw_rectangle(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2, uint16_t color);
|
||||
|
||||
@@ -88,7 +88,7 @@ void tft_draw_rectangle(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2, uint16_
|
||||
* @param y1 The y-Coordinate of the start-point
|
||||
* @param x2 The x-Coordinate of the end-point
|
||||
* @param y2 The y-Coordinate of the end-point
|
||||
* @param The 16-bit color to draw the pixel with
|
||||
* @param color The 16-bit color to draw the pixel with
|
||||
*/
|
||||
void tft_fill_rectangle(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2, uint16_t color);
|
||||
|
||||
@@ -103,6 +103,18 @@ void tft_fill_rectangle(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2, uint16_
|
||||
*/
|
||||
void tft_draw_bitmap_unscaled(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint16_t* dat);
|
||||
|
||||
|
||||
/**
|
||||
* Draws a bitmap from the filesystem onto the display without scaling/cropping
|
||||
* The bitmap must be saved in the windows bitmap format (.bmp) without compression and with 24 (b,g,r) or 32 (a,r,g,b) bits per pixel
|
||||
* @param x The x-coordinate of the top-left corner to draw the bitmap at
|
||||
* @param y The y-coordinate of the top-left corner to draw the bitmap at
|
||||
* @param filename The absolute path to the .bmp file
|
||||
* @return true on success
|
||||
*/
|
||||
bool tft_draw_bitmap_file_unscaled(uint16_t x, uint16_t y, const char* filename);
|
||||
|
||||
|
||||
/**
|
||||
* Draws the outline of a circle onto the display
|
||||
* @param x The x-Coordinate of the center point
|
||||
@@ -151,6 +163,7 @@ void tft_print_line(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, ui
|
||||
* @param bgcolor The 16-bit background color of the text. You may pass TRANSPARENT as Color
|
||||
* @param font The Fontnum to use for drawing
|
||||
* @param format The format string (like printf)
|
||||
* @param ... The arguments to format (like printf)
|
||||
*/
|
||||
void tft_print_formatted(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, const char* format, ...);
|
||||
|
||||
|
||||
@@ -2,11 +2,23 @@
|
||||
#include "ll_touch.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define NUM_AREAS 50 //Number of Structs Reserved in Memory for TouchAreas (e.g Buttons)
|
||||
TOUCH_AREA_STRUCT* areas[NUM_AREAS] = {NULL};
|
||||
/* The idea is as follows:
|
||||
* The user can add "touch-areas" which basically represent a rectangles on the screen.
|
||||
* Once the user touches such a rectangle with the pen, we forward events to his provided callback.
|
||||
* Touch events are provided to us from the low level implementation via touch_add_raw_event().
|
||||
* We then need to check which touch areas are effected by that event
|
||||
*/
|
||||
|
||||
volatile POINT_STRUCT pos;
|
||||
volatile TOUCH_STATE oldState=TOUCH_UP;
|
||||
/* 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
|
||||
TOUCH_AREA_STRUCT* areas[NUM_AREAS] = {NULL}; //list with pointers to all managed touch area's
|
||||
|
||||
volatile POINT_STRUCT pos; //the last touch point
|
||||
volatile TOUCH_STATE oldState=TOUCH_UP; //the last touch state
|
||||
|
||||
bool touch_init() {
|
||||
return ll_touch_init();
|
||||
@@ -14,45 +26,49 @@ bool touch_init() {
|
||||
|
||||
|
||||
bool touch_add_raw_event(uint16_t touchX, uint16_t touchY, TOUCH_STATE state) {
|
||||
//Update current and old position/state
|
||||
bool penDown = (state==TOUCH_DOWN);
|
||||
bool oldPenDown = (oldState==TOUCH_DOWN);
|
||||
oldState=state;
|
||||
pos.x=touchX;
|
||||
pos.y=touchY;
|
||||
if(penDown)
|
||||
|
||||
if(penDown) //pen is down now
|
||||
{
|
||||
// tftDrawPixel(touchX,touchY,WHITE);
|
||||
if(!oldPenDown) //First Touch
|
||||
//tft_draw_pixel(touchX,touchY,WHITE);
|
||||
if(!oldPenDown) //pen wasn't down before (positive edge) => First Touch
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(areas[z]!=NULL && touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2 )
|
||||
{
|
||||
areas[z]->flags=1; //PenInside=1
|
||||
if(areas[z]->hookedActions & PEN_DOWN)
|
||||
areas[z]->callback(areas[z],PEN_DOWN);
|
||||
areas[z]->flags=1; //Save PenInside=1
|
||||
if(areas[z]->hookedActions & PEN_DOWN) //The user wants to receive pen down events
|
||||
areas[z]->callback(areas[z],PEN_DOWN); //Send event to user callback
|
||||
}
|
||||
}
|
||||
}
|
||||
else //Second, Third
|
||||
else //Pen was down before => Second, Third event in row
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL )
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2)
|
||||
{
|
||||
if(areas[z]->flags==0) //PenInside ==0
|
||||
if(areas[z]->flags==0) //Pen was not inside before (PenInside==0)
|
||||
{
|
||||
areas[z]->flags=1; //PenInside=1
|
||||
if(areas[z]->hookedActions & PEN_ENTER)
|
||||
areas[z]->flags=1; //Pen is inside now (PenInside=1)
|
||||
if(areas[z]->hookedActions & PEN_ENTER) //The user wants to receive pen enter events
|
||||
areas[z]->callback(areas[z],PEN_ENTER);
|
||||
}
|
||||
}
|
||||
else if(areas[z]->flags) //PenInside==1
|
||||
else if(areas[z]->flags) //Pos not inside area, but it was before (PenInside==1)
|
||||
{
|
||||
areas[z]->flags=0; //PenInside=0
|
||||
if(areas[z]->hookedActions & PEN_LEAVE)
|
||||
areas[z]->flags=0; //Pen is no longer inside (PenInside=0)
|
||||
if(areas[z]->hookedActions & PEN_LEAVE) //The user wants to receive pen leave events
|
||||
areas[z]->callback(areas[z],PEN_LEAVE);
|
||||
}
|
||||
}
|
||||
@@ -60,8 +76,9 @@ bool touch_add_raw_event(uint16_t touchX, uint16_t touchY, TOUCH_STATE state) {
|
||||
}
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL && areas[z]->hookedActions&PEN_MOVE)
|
||||
if(areas[z]!=NULL && (areas[z]->hookedActions&PEN_MOVE)) //User want's to receive pen move events
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2)
|
||||
{
|
||||
areas[z]->callback(areas[z],PEN_MOVE);
|
||||
@@ -69,59 +86,61 @@ bool touch_add_raw_event(uint16_t touchX, uint16_t touchY, TOUCH_STATE state) {
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else //pen is not down now
|
||||
{
|
||||
if(oldPenDown) //Was the pen ever down (or was it a too short touch)
|
||||
if(oldPenDown) //but it was down before (negative edge)
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(areas[z]!=NULL && touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2 )
|
||||
{
|
||||
areas[z]->flags=0; //PenInside = 0;
|
||||
if(areas[z]->hookedActions & PEN_UP)
|
||||
areas[z]->flags=0; //The pen is no longer inside (PenInside = 0);
|
||||
if(areas[z]->hookedActions & PEN_UP) //user want's to receive pen up events
|
||||
areas[z]->callback(areas[z],PEN_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
touchX=0xFFFF;
|
||||
touchY=0xFFFF;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touch_have_empty(unsigned char num)
|
||||
{
|
||||
//go through pointer array and check for free spaces
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==NULL) num--;
|
||||
if(num==0) return true;
|
||||
if(areas[i]==NULL) num--; //a free space was found, we need one less
|
||||
if(num==0) return true; //enough free spaces found
|
||||
}
|
||||
return false; //not enough free spaces found
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
bool touch_register_area(TOUCH_AREA_STRUCT* area) //Registers an Area (fill Struct first). Return false if no more Space in the Pointertable (-->Change NUM_AREAS).
|
||||
bool touch_register_area(TOUCH_AREA_STRUCT* area)
|
||||
{
|
||||
|
||||
//go through pointer array and check for free space
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==NULL)
|
||||
if(areas[i]==NULL) //free space found
|
||||
{
|
||||
area->flags=0;
|
||||
areas[i]=area;
|
||||
area->flags=0; //we start with empty flags (PenInside=0)
|
||||
areas[i]=area; //save pointer into list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false; //no free space found
|
||||
}
|
||||
|
||||
void touch_unregister_area(TOUCH_AREA_STRUCT* area)//Unregisters an Area
|
||||
void touch_unregister_area(TOUCH_AREA_STRUCT* area)
|
||||
{
|
||||
if(area==NULL) return;
|
||||
|
||||
//go through pointer array and find the area to remove
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==area)
|
||||
if(areas[i]==area) //area found in pointer array at pos i
|
||||
{
|
||||
areas[i]=NULL;
|
||||
areas[i]=NULL; //set pointer in list to NULL again
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef TOUCH_H
|
||||
#define TOUCH_H
|
||||
|
||||
#include<stdbool.h>
|
||||
#include<stdint.h>
|
||||
|
||||
/**
|
||||
* @defgroup touch Touch
|
||||
* The Touch module provides access to the touch controller, and executes a callback if a certain region is touched
|
||||
@@ -12,10 +15,6 @@
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
#include<stdbool.h>
|
||||
#include<stdint.h>
|
||||
|
||||
/**
|
||||
Enum to describe the current Touch State. \sa touch_add_raw_event
|
||||
*/
|
||||
@@ -39,6 +38,7 @@ typedef enum {
|
||||
|
||||
/**
|
||||
* Prototype for Event Listeners (called for every occurring, hooked action)
|
||||
* \note You should NOT execute long running things in this callback nor should you update the gui. But you can call gui_screen_navigate() for instance.
|
||||
* @param touchArea The pointer to the TOUCH_AREA_STRUCT in which the event occurred
|
||||
* @param triggeredAction The Action which occurred
|
||||
*/
|
||||
@@ -53,8 +53,8 @@ typedef struct {
|
||||
uint16_t y1; //!< Top Left Y-Coordinate of Area
|
||||
uint16_t x2; //!< Bottom Right X-Coordinate of Area
|
||||
uint16_t y2; //!< Bottom Right Y-Coordinate of Area
|
||||
TOUCH_CALLBACK callback; //!< Callback
|
||||
uint8_t flags; //!< For internal Used, don't change, don't initialize
|
||||
TOUCH_CALLBACK callback; //!< Callback which is executed when an event occurred in this Area.
|
||||
uint8_t flags; //!< For internal use, don't change, don't initialize
|
||||
} TOUCH_AREA_STRUCT;
|
||||
|
||||
|
||||
|
||||
BIN
doc/docu.odt
BIN
doc/docu.odt
Binary file not shown.
BIN
doc/docu.pdf
BIN
doc/docu.pdf
Binary file not shown.
BIN
doc/inheritance.dia
Normal file
BIN
doc/inheritance.dia
Normal file
Binary file not shown.
BIN
doc/inheritance.png
Normal file
BIN
doc/inheritance.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
emulator/emulated/pixy_small.bmp
Normal file
BIN
emulator/emulated/pixy_small.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
emulator/emulated/shot1.bmp
Normal file
BIN
emulator/emulated/shot1.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
BIN
emulator/emulated/stm_small.bmp
Normal file
BIN
emulator/emulated/stm_small.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -1 +0,0 @@
|
||||
hallo welt
|
||||
@@ -44,13 +44,15 @@ void ll_tft_draw_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color) {
|
||||
}
|
||||
|
||||
uint8_t ll_tft_num_fonts() {
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
QFont get_font(uint8_t fontnum) {
|
||||
switch(fontnum) {
|
||||
case 0:
|
||||
return QFont("Monospace",8);
|
||||
case 1:
|
||||
return QFont("DejaVu Sans Mono",14);
|
||||
default:
|
||||
return QFont();
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), image(DISPLAY_WID
|
||||
ui->setupUi(this);
|
||||
image.fill(Qt::black);
|
||||
currentScale = 1;
|
||||
ui->widgetDisplay->setMouseTracking(true);
|
||||
ui->widgetDisplay->installEventFilter(this);
|
||||
}
|
||||
|
||||
void MainWindow::draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
|
||||
@@ -159,7 +161,28 @@ void MainWindow::mouseMoveEvent(QMouseEvent *evt)
|
||||
{
|
||||
//qDebug() << "move" << evt->pos();
|
||||
checkAndSendEvent(evt->pos(),true);
|
||||
}
|
||||
|
||||
|
||||
bool MainWindow::eventFilter(QObject *obj, QEvent *evt)
|
||||
{
|
||||
if(obj==ui->widgetDisplay) {
|
||||
switch(evt->type()) {
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(evt);
|
||||
QPoint p = (mouseEvent->pos()-QPoint(1,1))/currentScale;
|
||||
if(p.x()<DISPLAY_WIDTH && p.y()<DISPLAY_HEIGHT) {
|
||||
ui->txtMousePos->setText(QString("Mouse Position: (%1,%2)").arg(p.x()).arg(p.y()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
void draw_char(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, QFont font, char c);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject * obj , QEvent * env);
|
||||
void paintEvent(QPaintEvent * evt);
|
||||
void mousePressEvent(QMouseEvent* evt);
|
||||
void mouseReleaseEvent(QMouseEvent* evt);
|
||||
|
||||
@@ -43,6 +43,13 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="txtMousePos">
|
||||
<property name="text">
|
||||
<string>Mouse Position: out of bounds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
Reference in New Issue
Block a user