суббота, 21 декабря 2013 г.

Thread-Safe Circular Queue

Often in data processing applications are needed circular queue implemented in thread-safe way. This is the such implementation without any mutex, locks and so on:
/**
 * @file cqueue.h
 * @defgroup cqueue Circular queue support
 * @{
 * @brief Fast circular queue support
 */
#ifndef _CQUEUE_H
#define _CQUEUE_H

/** It's possible to use another (wrapper) structure or structure with another
name for cqueue implementation. CQUEUE_STRUCT_IMPL should be defined as name
of such structure in this case before to include cqueue.h. This structure
must have members:
 - unsigned integer "size"
 - integer "rpos"
 - integer "wpos"
 - integer "rwrap"
 - integer "wwrap"
 - any other custom fields
*/
#ifdef CQUEUE_STRUCT_IMPL
typedef CQUEUE_STRUCT_IMPL cqueue_t;
#else
/// Circular queue
typedef struct cqueue_t {
    /// Capacity of queue
    unsigned int size;
    /// Reader position
    int rpos;
    /// Writer position
    int wpos;
    /// Reader wrapping flag
    unsigned char rwrap;
    /// Writer wrapping flag
    unsigned char wwrap;
} cqueue_t;

#endif

/** Initializer for circular queue structure instance. Additional args are
needs if separate implementation struct is used */
#define CQUEUE_INITIALIZER(SIZE, ...) { \
    .size = (SIZE), \
    .rpos = 0, \
    .wpos = 0, \
    .wwrap = 0, \
    .rwrap = 0, \
    __VA_ARGS__ }

/// Init. function for queue structure instance
#define CQUEUE_INIT(Q, SIZE) do { \
    (Q)->size = (SIZE); \
    (Q)->rpos = (Q)->wpos = 0; \
    (Q)->wwrap = (Q)->rwrap = 0; \
} while (0)

/// Reset circular queue into initial state
#define CQUEUE_RESET(Q) CQUEUE_INIT(Q, (Q)->size)

/// General position incrementing
#define CQUEUE_INCR(Q, PPOS, PWRAP) do { \
    *(PPOS) = *(PPOS) +1; \
    if (*(PPOS) == (Q)->size) { \
        *(PWRAP) ^= 1; \
        *(PPOS) = 0; \
    } \
} while (0)

/// Reader position (for user-side)
#define CQUEUE_RPOS(Q) ((Q)->rpos)

/// Writer position (for user-side)
#define CQUEUE_WPOS(Q) ((Q)->wpos)

/// Reader position incrementing
#define CQUEUE_INCR_R(Q) do { \
    CQUEUE_INCR(Q, &((Q)->rpos), &((Q)->rwrap)); \
} while (0)

/// Writer position incrementing
#define CQUEUE_INCR_W(Q) do { \
    CQUEUE_INCR(Q, &((Q)->wpos), &((Q)->wwrap)); \
} while (0)

/// Test of circular queue fullness
#define CQUEUE_IS_FULL(Q) \
    ((Q)->wpos == (Q)->rpos && (Q)->rwrap != (Q)->wwrap)

/// Test of circular queue emptiness
#define CQUEUE_IS_EMPTY(Q) \
    ((Q)->wpos == (Q)->rpos && (Q)->rwrap == (Q)->wwrap)

/// Number of free slots in circular queue
#define CQUEUE_NFREE(Q) \
    ((Q)->rwrap == (Q)->wwrap ? \
     ((Q)->size - (Q)->wpos + (Q)->rpos) : \
     ((Q)->rpos - (Q)->wpos))

/// Number of used slots in circular queue
#define CQUEUE_LEN(Q) \
    ((Q)->rwrap != (Q)->wwrap ? \
     ((Q)->size - (Q)->rpos + (Q)->wpos) : \
     ((Q)->wpos - (Q)->rpos))

#endif
/// @}
USe doxygen to gen documentation of this module (it's only one .h-file).

суббота, 2 ноября 2013 г.

Portable effective C bit fields

As you know bit's fields in C are not portable when we're talking about external world (network/different architecture/file transmission): order of the fields is unspecified, also as real wrapper struct...
Often bitwise operations are used instead of bitfield struct syntax but result code may be less readable opposite to original idea: bitfields. But there is simple solution to workaround weird syntax. Here you are:
#define __BITSTRUCT_MASK(LEN) ((1<<(LEN))-1)

#ifndef BITSTRUCT_GETBITS_IMPL
# define BITSTRUCT_GETBITS_IMPL(BS, OFFSET, LEN) \
    (((BS)>>(OFFSET)) & __BITSTRUCT_MASK(LEN))
#endif
#ifndef BITSTRUCT_SETBITS_IMPL
# define BITSTRUCT_SETBITS_IMPL(BS, OFFSET, LEN, VALUE) \
    ((BS) & ~(__BITSTRUCT_MASK(LEN)<<(OFFSET)) | \
     (((VALUE) & __BITSTRUCT_MASK(LEN))<<(OFFSET)))
#endif

#define BITSTRUCT enum
#define BITFIELD(NAME, OFFSET, LEN) NAME##_OFF = (OFFSET), NAME##_LEN=(LEN)
#define BITFIELD_MASK(NAME) __BITSTRUCT_MASK(NAME##_LEN)
#define BITFIELD_GET(NAME, BS) \
  BITSTRUCT_GETBITS_IMPL(BS, NAME##_OFF, NAME##_LEN)
#define BITFIELD_SET(NAME, BS, VALUE) \
  BITSTRUCT_SETBITS_IMPL(BS, NAME##_OFF, NAME##_LEN, VALUE)
So now it's possible to write something like this to describe register X:
|15.....8|7....4|3.....0|
| ENABLE | FLAG | COUNT |
BITSTRUCT RegisterX {
        // BITFIELD(NAME, OFFSET, LENGTH)
        BITFIELD(RX_COUNT, 0, 4),
        BITFIELD(RX_FLAG, 4, 4),
        BITFIELD(RX_ENABLE, 8, 8)
};

int main(void)
{
        int x = 0x1234; // raw value of register
        // example of get/set field of bits' struct
        x = BITFIELD_SET(RX_ENABLE, x, 0x98);
        printf("%0X", BITFIELD_GET(RX_ENABLE, x));
        return (0);
}
As you can see code is readable as in usual bitfield's struct, but instead of struct BITSTRUCT should be used, and BITFIELD() descriptor instead of type NAME:len form. To set or to get field value, use BITFIELD_SET/GET() macros. BITFIELD_MASK() returns mask of field but from zero bit.
But note the *_IMPL macros, using they, you can customize behavior of SET/GET implementation, for example to use some DSP core what can improve program performance dramatically! What you need is only to define own GET/SET helpers-implementators (BITSTRUCT_GETBITS_IMPL/BITSTRUCT_SETBITS_IMPL).

суббота, 19 октября 2013 г.

Scala joke :)

See the Scala joke:
scala> 5 plus 5
I don't know
... this possible with implicits, sure:
scala> implicit class Joke(x:Int) {
     | def plus(y:Int) = { println("I don't know") }
     | }
defined class Joke
But why Scala is so sexy? :)

среда, 11 сентября 2013 г.

ZSH: restoring last working directory

This little snippet helps to save/restore working directory on start/exit zsh shell:
autoload -Uz add-zsh-hook
function save_pwd {
echo "$PWD" > "$HOME/.zshpwd"
}
add-zsh-hook chpwd save_pwd
cd `cat $HOME/.zshpwd 2>/dev/null`
put it in ~/.zshrc :)

вторник, 10 сентября 2013 г.

Android build simplifier

Add this alias to ~/.bashrc:
alias amake='_(){ rm -f ./build.log; . ./build/envsetup.sh; lunch $1; make -j8 2>&1|tee ./build.log; }; _'
and now you can avoid envsetup.sh, lunch running and get build log file :) Better is to run:
$ amake something-userdebug
from Android source directory.
Or the same but as explicit function (put it, for example in ~/.zshrc):
function amake() {
/bin/bash -s stdin << EOF
rm -f ./build.log
. ./build/envsetup.sh
lunch $1
make -j8 2>&1|tee ./build.log
EOF
}

суббота, 31 августа 2013 г.

IMSH - Image Processing learning shell

I have been started new public open source project: IMSH for studying image processing. This is the SIOD Scheme based shell with several test purpose scripts. All algorithms are C coded and Scheme scripts are used to drive and test C algorithms which now are:
  • Black and White
  • Greyscale
  • Negative
  • Convolution Matrix
  • Channel Mask
  • Greyscale Approximation
  • Pixalization
  • Median filter
Wiki of project and repository are available on public/free Fossil hosting: http://chiselapp.com/user/p4v31/repository/imsh/home

воскресенье, 25 августа 2013 г.

Portable Vim (from portableapps.com) and Tools

If you use portable Vim/GVim as PortableApps.com package and want to use external tools (ctags, uncrustify, astyle, global/gtags, so on) you can setup your paths (to tools executable files) based on $HOME variable. For example lets set folder of all tools:
let TOOLSDIR=$HOME . "/vimfiles/tools"
because HOME is setted in gVimPortable/App/AppInfo/Launcher/gVimPortable.ini:
[Environment]
VIM=%PAL:AppDir%\vim
VIMRUNTIME=%PAL:AppDir%\vim\vim74
HOME=%PAL:DataDir%\settings
LANG=%PAL:LanguageCustom%
Also you can add there any other environment variables to use them in vim config file.
I usually create there - in settings/ - "tools" directory and make there "ctags", "astyle" and other folders with my external tools binaries, config files, etc...
Now you can make something like:
" Beautifiers.
exec ':cabbr astyle ' . TOOLSDIR . '/astyle/astyle.exe --options=' . TOOLSDIR . '/astyle/.astylerc'
exec ':cabbr uncr ' . TOOLSDIR . '/uncrustify/uncrustify.exe -q -c ' . TOOLSDIR . '/uncrustify/uncrustify.cfg'
for your beautifiers. Select code fragment (or all text in file) and type :!astyle for example, to see how it works.

четверг, 15 августа 2013 г.

Android/smartphone hardware/software info

If you wanna get info (hardware configuration, software versions, memory info, etc.) you can do:
  1. "tap" top bar with "tray icons" down and then "Settings"/"About" and any info item what you see there;
  2. second way is more informative, but you should install additional FREE Application, ES File Explorer which is very usable (to manage all your files and folders even system - full control!). After install it, open application, go to root folder ("/") then "click" on "proc" folder and now you can open any file containing system info in standard Linux "/proc" (select "Open As Text" then with "ES Note Editor" after clicking on a file). You can see there interesting files like "cpuinfo", "version" and so on...

вторник, 28 мая 2013 г.

Holes (lost samples) detection policy for circular buffer

Usually circular buffer is used for saving samples by writer task and for consume samples by reader task. Often reader and writer tasks has different speed - reader may be slower then writer, so there are lost (skipped) samples. What can we do with them?
There are many "policies" to process such situation but if we have only one fixed circular buffer, all what we can do is to detect such lost samples and to save somewhere. Some uses special attribute with each sample - time stamp/marker or control cyclic code. With this marker decoder of saved samples (in external file) can determines skipped samples and if decoder knows sampling frequency then he can determine width of such "hole" (number of skipped samples).
When writer task overtakes reader task (and we should know how to detect such situation) it can save (and increment) number of skipped samples until reader task shifts and frees TWO cells in queue to flush "hole" and current sample ("hole" will be saved in the same samples queue but with special flag - HOLE and number of lost samples in this hole). This is simple idea of holes processing.
On Wikipedia pages there are procedures for circular buffer. We need additional procedures for:
  • checking when there are 2 (or more) free cells - to save "hole" and current sample
  • adding new sample with "hole saving"
In pseudocode it can be:
how_many_free():
  if rwrap == wwrap:
    return len(samples) - wpos + rpos
  else:
    return rpos - wpos

how_many_used():   if rwrap != wwrap:     return len(samples) - rpos + wpos   else:     return wpos - rpos
add_sample(new_sample):   if holes != 0:     if how_many_free() >= 2:       samples[wpos] = special_hole_sample       increment_pos(wpos)       samples[wpos] = new_sample       increment_pos(wpos)     else:       holes++   else:     if how_many_free() >= 1:       samples[wpos] = new_sample       increment_pos(wpos)     else:       holes++
See idea for increment_pos() on Wikipedia page.
wwrap, rwrap are flags (bits) for wrapping writer/reader tasks and are modified in increment_pos() procedure.

If we save "hole" as sample but with a special flag (in file), then we get "flat" stream of structures with the same size.

пятница, 5 апреля 2013 г.

My modified version of local vim rc script (auto loading of local rc file from up or current folder):
function SetLocalOptions()
    let dirname = getcwd()
    let predirname = dirname
 while 1
  let lvimrc  = dirname . "/.lvimrc"
  if filereadable(lvimrc)
   execute "source " . lvimrc
   break
  endif
  let dirname = fnamemodify(dirname, ":p:h:h")
        if dirname == predirname
            break
        else
            let predirname = dirname
        endif
 endwhile
endfunction

au BufNewFile,BufRead * call SetLocalOptions()

четверг, 4 апреля 2013 г.

Qtouch sensors input processing (example for ASF)

There are several tasks when we try to process Qtouch input (Qtouch 60168): 1) fix random (false) signals (splashes or BOUNCE) 2) detecting PRESS state 3) detecting long "pressing" (touching) - sticking of key.

1) means: really signal from Qt is not 000..0 or 111..1 but something like 000001000000... - with error '1' in sequence of '0' (and vise verse) because of signal bouncing, so it should be 'filtered' 2) means: we should detect when touch sensor is "pressed" 3) means: when user touches screen for long time we should detect this state as 'hold key' (like you can see in old mobile phones or key sticking in Windows) and call some special routine (callback) for processing such event.

Next code processes all of this:
/* Input from qTouch (qt60168) with 4 buttons and one wheel
 * - There is some feature: sticking of key (long pressing detection) will occurs
 *   after usual key event: first key is pressed - and application open some
 *   window, only after this may occurs long-time-detection.
 * - test_event() test of PRESSED state, not released.
 * - simulation of repeatition simulate seq: pressed, released, no IDLE!
 */ 

#include "input.h"
#include "myutl.h"

#if !defined(FCPU)
# error Define FCPU to support refresh screen!
#endif

input_t _input = INPUT_INITIALIZER();

static void _qt60168_io_init(void);
static inline void _input_scan_keys(input_t* self);
static inline void _input_update_keys_state(input_t* self);
static inline void input_process_sticking(input_t* self, input_keys_t key, input_state_t new_state);
inline static bool input_fltbounce_filter(input_fltbounce_t* self, input_state_t new_state, int msdelay);
inline static void input_stick_fsm(input_stick_t* self, input_t* input, input_stick_fsm_event_t ev, input_keys_t key);

#define _INPUT_STICK_STOP(S) do { \
  cpu_stop_timeout(&(S)->long_to); \
  cpu_stop_timeout(&(S)->short_to); \
} while (0)

#define _INPUT_GET_SENSOR(INP, I) (INP)->sensors[I]

/// set wheel to left
#define _INPUT_SET_WHEEL_LEFT(INP) do { \
  if (INPUT_RIGHT == (INP)->keys[INPUT_WHEEL]) { \
    (INP)->wheel = 0; \
  } \
  (INP)->keys[INPUT_WHEEL] = INPUT_LEFT; \
  (INP)->wheel++; \
} while (0)
/// set wheel to right
#define _INPUT_SET_WHEEL_RIGHT(INP) do { \
  if (INPUT_LEFT == (INP)->keys[INPUT_WHEEL]) { \
    (INP)->wheel = 0; \
  } \
  (INP)->keys[INPUT_WHEEL] = INPUT_RIGHT; \
  (INP)->wheel++; \
} while (0)

#define _INPUT_SET_SENSOR(INP, I, ST) (INP)->sensors[I] = (ST)
#define _INPUT_SET_KEY(INP, I, ST) (INP)->keys[I] = (ST)

// apply state of bounce filter FSM
#define _INPUT_FLTBOUNCE_APPLY_STATE(F, NEW_STATE, MSDELAY) do { \
  (F)->state = NEW_STATE; \
  cpu_set_timeout(cpu_ms_2_cy(MSDELAY, FCPU), &(F)->to); \
} while (0)

// is timeout in bounce filter?
#define _INPUT_FLTBOUNCE_IS_TO(F) cpu_is_timeout(&(F)->to)

static void
_qt60168_io_init(void) {
  static const gpio_map_t QT60168_SPI_GPIO_MAP =
  {
    {QT60168_SPI_SCK_PIN, QT60168_SPI_SCK_FUNCTION},// SPI Clock.
    {QT60168_SPI_MISO_PIN, QT60168_SPI_MISO_FUNCTION},// MISO.
    {QT60168_SPI_MOSI_PIN, QT60168_SPI_MOSI_FUNCTION},// MOSI.
    {QT60168_SPI_NPCS0_PIN, QT60168_SPI_NPCS0_FUNCTION}// Chip Select NPCS.
  };

  // SPI options.
  spi_options_t spiOptions =
  {
    .reg    = QT60168_SPI_NCPS,
    .baudrate   = QT60168_SPI_MASTER_SPEED, // Defined in conf_qt60168.h.
    .bits   = QT60168_SPI_BITS,   // Defined in conf_qt60168.h.
    .spck_delay   = 0,
    .trans_delay  = 0,
    .stay_act   = 0,
    .spi_mode   = 3,
    .modfdis  = 1
  };

  // Assign I/Os to SPI.
  gpio_enable_module(QT60168_SPI_GPIO_MAP,
      sizeof(QT60168_SPI_GPIO_MAP) / sizeof(QT60168_SPI_GPIO_MAP[0]));
  // Initialize as master.
  spi_initMaster(QT60168_SPI, &spiOptions);
  // Set selection mode: variable_ps, pcs_decode, delay.
  spi_selectionMode(QT60168_SPI, 0, 0, 0);
  // Enable SPI.
  spi_enable(QT60168_SPI);
  // Initialize QT60168 with SPI clock Osc0 (=FCPU).
  spi_setupChipReg(QT60168_SPI, &spiOptions, FCPU);
}

/// call after INPUT(...) initializer calling
void
input_init(input_t* self) {
  // Initialize QT60168 resources: GPIO, SPI and QT60168.
  _qt60168_io_init();
  // Initialize QT60168 component.
  qt60168_init(FCPU);
  // TODO : need?
  for (int i=0; i<INPUT_NUMBER_OF_KEYS; i++) {
    _INPUT_STICK_STOP(&self->stick_keys[i]);
  }
}

// for input task!
static inline void
_input_scan_keys(input_t* self) {

  int all_keys = qt60168_report_all_key();
  int mask = 1;
  for (int i=0; i<QT60168_TOUCH_NUMBER_OF_SENSORS; i++ ) {
    if (all_keys & (mask<<i)) {
      // if pressed - set pressed
      if (input_fltbounce_filter(self->fltsensors+i, INPUT_PRESSED, INPUT_SENS_TIME)) {
        _INPUT_SET_SENSOR(self, i, INPUT_PRESSED);
      }
    }
    else if (INPUT_PRESSED == _INPUT_GET_SENSOR(self, i)) {
      // if WAS pressed but now is not - set released
      if (input_fltbounce_filter(self->fltsensors+i, INPUT_RELEASED, INPUT_SENS_TIME)) {
        _INPUT_SET_SENSOR(self, i, INPUT_RELEASED);
      }
      //if (i==QT60168_TOUCH_SENSOR_BUTTON_3) { gui_screen_prn(SCREEN, RED, WHITE, "BTN3 RELEASED"); }
    }
    else {
      // else (not pressed now and WASN'T pressed early) - set idle
      if (input_fltbounce_filter(self->fltsensors+i, INPUT_IDLE, INPUT_SENS_TIME)) {
        _INPUT_SET_SENSOR(self, i, INPUT_IDLE);
      }
      //if (i==QT60168_TOUCH_SENSOR_BUTTON_3) { gui_screen_prn(SCREEN, RED, WHITE, "BTN3 IDLE"); }
    }
  }
}

static inline void
_input_update_keys_state(input_t* self) {

  // reset 'ready' flag. If some will be pressed, set to true. If
  // released - not
  self->ready = false;

#define _UPDATE_BTKKEY(BTN, KEY) do { \
  if (INPUT_PRESSED == _INPUT_GET_SENSOR(self, BTN)) { \
    _INPUT_SET_KEY(self, KEY, INPUT_PRESSED); \
    input_process_sticking(self, KEY, INPUT_PRESSED); \
    self->ready = true; \
  } \
  else if (INPUT_RELEASED == _INPUT_GET_SENSOR(self, BTN)) { \
    _INPUT_SET_KEY(self, KEY, INPUT_RELEASED); \
    input_process_sticking(self, KEY, INPUT_RELEASED); \
  } \
} while (0)

  _UPDATE_BTKKEY(QT60168_TOUCH_SENSOR_BUTTON_0, INPUT_F1);
  _UPDATE_BTKKEY(QT60168_TOUCH_SENSOR_BUTTON_1, INPUT_F2);
  _UPDATE_BTKKEY(QT60168_TOUCH_SENSOR_BUTTON_2, INPUT_F3);
  _UPDATE_BTKKEY(QT60168_TOUCH_SENSOR_BUTTON_3, INPUT_F4);

#define _UPDATE_WHKEY(SENS0, SENS1, ACT) do { \
  if (_INPUT_GET_SENSOR(self, SENS0) == INPUT_RELEASED && \
      _INPUT_GET_SENSOR(self, SENS1) == INPUT_PRESSED) { \
    ACT(self); \
    self->ready = true; \
  } \
} while (0)

  // scroll to right
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_0,
      QT60168_TOUCH_SENSOR_WHEEL_1, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_1,
      QT60168_TOUCH_SENSOR_WHEEL_2, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_2,
      QT60168_TOUCH_SENSOR_WHEEL_3, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_3,
      QT60168_TOUCH_SENSOR_WHEEL_4, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_4,
      QT60168_TOUCH_SENSOR_WHEEL_5, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_5,
      QT60168_TOUCH_SENSOR_WHEEL_6, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_6,
      QT60168_TOUCH_SENSOR_WHEEL_7, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_7,
      QT60168_TOUCH_SENSOR_WHEEL_8, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_8,
      QT60168_TOUCH_SENSOR_WHEEL_9, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_9,
      QT60168_TOUCH_SENSOR_WHEEL_10, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_10,
      QT60168_TOUCH_SENSOR_WHEEL_11, _INPUT_SET_WHEEL_RIGHT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_11,
      QT60168_TOUCH_SENSOR_WHEEL_0, _INPUT_SET_WHEEL_RIGHT);

  // scroll to left
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_0,
      QT60168_TOUCH_SENSOR_WHEEL_11, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_11,
      QT60168_TOUCH_SENSOR_WHEEL_10, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_10,
      QT60168_TOUCH_SENSOR_WHEEL_9, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_9,
      QT60168_TOUCH_SENSOR_WHEEL_8, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_8,
      QT60168_TOUCH_SENSOR_WHEEL_7, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_7,
      QT60168_TOUCH_SENSOR_WHEEL_6, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_6,
      QT60168_TOUCH_SENSOR_WHEEL_5, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_5,
      QT60168_TOUCH_SENSOR_WHEEL_4, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_4,
      QT60168_TOUCH_SENSOR_WHEEL_3, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_3,
      QT60168_TOUCH_SENSOR_WHEEL_2, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_2,
      QT60168_TOUCH_SENSOR_WHEEL_1, _INPUT_SET_WHEEL_LEFT);
  _UPDATE_WHKEY(QT60168_TOUCH_SENSOR_WHEEL_1,
      QT60168_TOUCH_SENSOR_WHEEL_0, _INPUT_SET_WHEEL_LEFT);

#undef _UPDATE_BTKKEY
#undef _UPDATE_WHKEY
}

void
input_task(input_t* self) {
  _input_scan_keys(self);
  _input_update_keys_state(self);
  if (self->ready) {
    MCALL(&self->gui_input, onready);
    input_stick_fsm(self->stick_keys+INPUT_F1, self, INPUT_STICK_FSM_RESET_EV, INPUT_F1);
    input_stick_fsm(self->stick_keys+INPUT_F2, self, INPUT_STICK_FSM_RESET_EV, INPUT_F2);
    input_stick_fsm(self->stick_keys+INPUT_F3, self, INPUT_STICK_FSM_RESET_EV, INPUT_F3);
    input_stick_fsm(self->stick_keys+INPUT_F4, self, INPUT_STICK_FSM_RESET_EV, INPUT_F4);
  }
}

int
input_test_event(input_t* self, gui_events_t event) {

#define _KEY_STATE(KEY) \
  (self->stick_keys[KEY].fsm.state==INPUT_STICK_FSM_NO_EMUL_ST? \
   self->keys[KEY] : self->stick_keys[KEY].fsm.state)

// FIXED: moved all FSM switching to input_task() to prevent problem with 2
// and more test_event() calling in ONE input cycle and getting different results!
  bool ret;
  switch (event) {
    case GUI_ESCAPE_EVENT:
      ret = (_KEY_STATE(INPUT_F1) == INPUT_PRESSED);
      //input_stick_fsm(self->stick_keys+INPUT_F1, self, INPUT_STICK_FSM_RESET_EV, INPUT_F1);
      break;
    case GUI_UP_EVENT:
      ret = (_KEY_STATE(INPUT_F2) == INPUT_PRESSED);
      //input_stick_fsm(self->stick_keys+INPUT_F2, self, INPUT_STICK_FSM_RESET_EV, INPUT_F2);
      break;
    case GUI_DOWN_EVENT:
      ret = (_KEY_STATE(INPUT_F3) == INPUT_PRESSED);
      //input_stick_fsm(self->stick_keys+INPUT_F3, self, INPUT_STICK_FSM_RESET_EV, INPUT_F3);
      break;
    case GUI_ENTER_EVENT:
      ret = (_KEY_STATE(INPUT_F4) == INPUT_PRESSED);
      //input_stick_fsm(self->stick_keys+INPUT_F4, self, INPUT_STICK_FSM_RESET_EV, INPUT_F4);
      break;
    default:
      ret = 0;
      break;
  }
  return (ret);
#undef _KEY_STATE
}

void
input_reset_event(input_t* self, gui_events_t event) {

  switch (event) {
    case GUI_ESCAPE_EVENT:
      _INPUT_SET_KEY(self, INPUT_F1, INPUT_IDLE);
      break;
    case GUI_UP_EVENT:
      _INPUT_SET_KEY(self, INPUT_F2, INPUT_IDLE);
      break;
    case GUI_DOWN_EVENT:
      _INPUT_SET_KEY(self, INPUT_F3, INPUT_IDLE);
      break;
    case GUI_ENTER_EVENT:
      _INPUT_SET_KEY(self, INPUT_F4, INPUT_IDLE);
      break;
  }
#undef _MAKE_STICK_PAUSE
}

inline static void
input_process_sticking(input_t* self, input_keys_t key, input_state_t new_state) {
  input_stick_fsm_event_t fsm_ev;
  // generate events for sticking fsm
  if (cpu_is_timeout(&self->stick_keys[key].long_to)) {
    fsm_ev = INPUT_STICK_FSM_RAISED_LTO_EV;
  } else if (cpu_is_timeout(&self->stick_keys[key].short_to)) {
    fsm_ev = INPUT_STICK_FSM_RAISED_STO_EV;
  } else if (new_state != INPUT_PRESSED) {
    fsm_ev = INPUT_STICK_FSM_CHG_KEYST_EV;
  } else if (new_state == INPUT_PRESSED) {
    fsm_ev = INPUT_STICK_FSM_SAME_KEYST_EV;
  } else
    return;

  input_stick_fsm(&self->stick_keys[key], self, fsm_ev, key);
}

/// filter new state: returns true - allowed, false - bounce (rejected)
inline static bool
input_fltbounce_filter(input_fltbounce_t* self, input_state_t new_state, int msdelay) {
  if (new_state == self->state) {
    if (_INPUT_FLTBOUNCE_IS_TO(self)) {
      return (true);
    }
  } else {
    _INPUT_FLTBOUNCE_APPLY_STATE(self, new_state, msdelay);
  }
  return (false);
}

inline static void
input_stick_fsm(input_stick_t* self, input_t* input, input_stick_fsm_event_t ev, input_keys_t key) {

  switch (self->fsm.state) {
    case INPUT_STICK_FSM_NO_EMUL_ST:
        if (ev == INPUT_STICK_FSM_RAISED_LTO_EV) {
          DBG_ASSERT(0);
        } else if (ev == INPUT_STICK_FSM_RAISED_STO_EV) {
          DBG_ASSERT(0);
        } else if (ev == INPUT_STICK_FSM_CHG_KEYST_EV) {
          ;
        } else if (ev == INPUT_STICK_FSM_SAME_KEYST_EV) {
          cpu_set_timeout(cpu_ms_2_cy(INPUT_KEY_STICK_TIME, FCPU), &self->long_to);
          self->fsm.state = INPUT_STICK_FSM_EMUL_PRESSED_ST;
        } else if (ev == INPUT_STICK_FSM_RESET_EV) {
          ;
        }
      break;


    case INPUT_STICK_FSM_EMUL_PRESSED_ST:
        if (ev == INPUT_STICK_FSM_RAISED_LTO_EV) {
          _INPUT_STICK_STOP(self);
          MCALL(input, onstickkey, key);
          self->fsm.state = INPUT_STICK_FSM_NO_EMUL_ST;
        } else if (ev == INPUT_STICK_FSM_RAISED_STO_EV) {
          DBG_ASSERT(0);
        } else if (ev == INPUT_STICK_FSM_CHG_KEYST_EV) {
          _INPUT_STICK_STOP(self);
          self->fsm.state = INPUT_STICK_FSM_NO_EMUL_ST;
        } else if (ev == INPUT_STICK_FSM_SAME_KEYST_EV) {
          ;
        } else if (ev == INPUT_STICK_FSM_RESET_EV) {
          cpu_set_timeout(cpu_ms_2_cy(INPUT_REPEAT_TIME, FCPU), &self->short_to);
          self->fsm.state = INPUT_STICK_FSM_EMUL_RELEASED_ST;
        }
      break;


    case INPUT_STICK_FSM_EMUL_RELEASED_ST:
        if (ev == INPUT_STICK_FSM_RAISED_LTO_EV) {
          _INPUT_STICK_STOP(self);
          MCALL(input, onstickkey, key);
          self->fsm.state = INPUT_STICK_FSM_NO_EMUL_ST;
        } else if (ev == INPUT_STICK_FSM_RAISED_STO_EV) {
          cpu_stop_timeout(&self->short_to);
          self->fsm.state = INPUT_STICK_FSM_EMUL_PRESSED_ST;
        } else if (ev == INPUT_STICK_FSM_CHG_KEYST_EV) {
          _INPUT_STICK_STOP(self);
          self->fsm.state = INPUT_STICK_FSM_NO_EMUL_ST;
        } else if (ev == INPUT_STICK_FSM_SAME_KEYST_EV) {
          ;
        } else if (ev == INPUT_STICK_FSM_RESET_EV) {
          ;
        }
      break;
  }
}
H file is:
#ifndef INPUT_H_
#define INPUT_H_

#include <stdbool.h>
#include <board.h>
#include <gpio.h>
#include <spi.h>
#include <qt60168.h>
#include <conf_qt60168.h>
#include <cycle_counter.h>
#include "config/conf_app.h"
#include "gui.h"

#if !defined(FCPU)
# error Define FCPU to support sticking of keys!
#endif

// config {{{
#ifndef INPUT_KEY_STICK_TIME
# define INPUT_KEY_STICK_TIME 2500
# warning Default INPUT_KEY_STICK_TIME is used: 2500 ms
#endif
#ifndef INPUT_SENS_TIME
# define INPUT_SENS_TIME 10
# warning Default INPUT_SENS_TIME is used: 10 ms
#endif
#ifndef INPUT_REPEAT_TIME
# define INPUT_REPEAT_TIME 300
# warning Default INPUT_REPEAT_TIME is used: 300 ms
#endif
// }}}

struct input_t;

/// state of key/sensor
typedef enum input_state_t {
  INPUT_IDLE,
  INPUT_PRESSED,
  INPUT_RELEASED,
  INPUT_LEFT,
  INPUT_RIGHT
} input_state_t;

/// keys
typedef enum input_keys_t {
  INPUT_F1, /* 0 */
  INPUT_F2,
  INPUT_F3,
  INPUT_F4,
  INPUT_WHEEL,
  INPUT_NUMBER_OF_KEYS ///< total number, should be last
} input_keys_t;

/// for filter key bounce (there is, yes!)
typedef struct input_fltbounce_t {
  t_cpu_time to;
  input_state_t state;
} input_fltbounce_t;
#define INPUT_FLTBOUNCE_INITIALIZER(...) { \
  .to = {0}, \
  .state = INPUT_IDLE, \
  __VA_ARGS__ }

/// for key sticking, repeat (long pressing)
typedef enum input_stick_fsm_state_t {
  INPUT_STICK_FSM_NO_EMUL_ST = INPUT_IDLE,
  INPUT_STICK_FSM_EMUL_PRESSED_ST = INPUT_PRESSED,
  INPUT_STICK_FSM_EMUL_RELEASED_ST = INPUT_RELEASED
} input_stick_fsm_state_t;

typedef enum input_stick_fsm_event_t {
  INPUT_STICK_FSM_RAISED_LTO_EV,   // 0
  INPUT_STICK_FSM_RAISED_STO_EV,   // 1
  INPUT_STICK_FSM_CHG_KEYST_EV,  // 2
  INPUT_STICK_FSM_SAME_KEYST_EV,   // 3
  INPUT_STICK_FSM_RESET_EV   // 4
} input_stick_fsm_event_t;

typedef struct input_stick_t {
  t_cpu_time long_to, ///< long timeout
       short_to; ///< short (for emulating)
  struct {
    input_stick_fsm_state_t state; ///< the same values as input_state_t!
  } fsm;
} input_stick_t;

#define INPUT_STICK_INITIALIZER(...) { \
  .long_to = {.delay_start_cycle=0, .delay_end_cycle=0, .timer_state=CPU_TIMER_STATE_STOPPED}, \
  .short_to = {.delay_start_cycle=0, .delay_end_cycle=0, .timer_state=CPU_TIMER_STATE_STOPPED}, \
  .fsm = { .state=INPUT_STICK_FSM_NO_EMUL_ST }, \
  __VA_ARGS__ }

/// input class {{{
typedef void (*input_onstickkey_t)(struct input_t* self, enum input_keys_t key);
typedef struct input_t {
  gui_input_if_t gui_input;
  input_state_t sensors[QT60168_TOUCH_NUMBER_OF_SENSORS]; ///< states of sensors
  input_fltbounce_t fltsensors[QT60168_TOUCH_NUMBER_OF_SENSORS]; ///< filtered states
  input_state_t keys[INPUT_NUMBER_OF_KEYS]; ///< states of keys
  input_stick_t stick_keys[INPUT_NUMBER_OF_KEYS]; ///< sticking of keys
  int wheel; ///< wheel amount (counter)
  bool ready; ///< there is some input
  input_onstickkey_t onstickkey;
} input_t;

#define INPUT_INITIALIZER(...) { \
  .gui_input = GUI_INPUT_IF_INITIALIZER( \
      .test_event = (gui_input_if_test_event_t)input_test_event, \
      .reset_event = (gui_input_if_reset_event_t)input_reset_event \
      ), \
  .sensors = {INPUT_IDLE}, \
  .fltsensors = {INPUT_FLTBOUNCE_INITIALIZER()}, \
  .keys = {INPUT_IDLE}, \
  .stick_keys = {INPUT_STICK_INITIALIZER()}, \
  .wheel = 0, \
  .ready = false, \
  .onstickkey = NULL, \
  __VA_ARGS__ }

// }}}

/// Global instance (use *INPUT* pointer)
extern struct input_t _input;
#define INPUT (&_input)

void input_init(input_t* self);
void input_task(input_t* self);
/// Check if event occurs (key pressing)
int input_test_event(input_t* self, enum gui_events_t event);
//int input_get_event(input_t* self, gui_event_t *event);
void input_reset_event(input_t* self, enum gui_events_t event);

#endif /* INPUT_H_ */
Example of usage:
...
void onstickkey(input_t* self, input_keys_t key) {
 // do something...
}

void main(void) {
  ...
  input_init(INPUT);
  INPUT->onstickkey = onstickkey;
  ...
  while (1) { // main loop with "tasks"
    ...
    input_task(INPUT);
    ...
  }
}
onstickkey (callback on long pressing) will call automatically when user touch Qtouch sensor for long time.
Somewhere in the program you can test pressed key:
if (input->test_event(input, GUI_UP_EVENT)) {
  // event UP
  // do something...
  input->reset_event(input, GUI_UP_EVENT);
}
This apptoach of processing Qtouch sensors has only one limitation: it treats 'pressing' as touch ("down"), not as sensor release, IMHO it's more natural way when user has deal with touch sensors instead of real keys.

пятница, 22 марта 2013 г.

ASF/AVR32: how to read/write to FLASH

First, open example ASF project on FLASHC read/write. Get code from and paste into your project. Also add *.lds file from example (...asf/avr32/drivers/flashc/flash_example//.lds) into your project. Then add to Linker options next:
--rodata-writable -Wl,--direct-data -T../src/asf/avr32/drivers/flashc/flash_example/at32uc3a3256_evk1104/link_uc3a3256.lds
(first in options line on tab "Miscellaneous" and correct path).
Now build and no any linker errors about regions...

четверг, 14 марта 2013 г.

AVR32/ASF: ANSI terminal on UART

It's good known goal - to run terminal (with some kind of shell) of one (or all) of the UARTs. This snippet shows how to process input byte from UART and to emulate ANSI terminal behavior:

/**
 * Handle input byte via USART; process it to emulate edit line behaviour.
 * After processing, sets self->response array to 4 possible responses (until
 * one is NULL). sh_task() will send these responses via UART...
 * Each response can be usual ASCIIZ but also can be ANSI ESC codes, so user
 * terminal should be in ANSI TERM mode. Supported:
 * - BS/DEL - delete prev. char
 * - ^U - delete entire line
 * - ^L - clear screen but keep edited line
 * - TAB - changed to SPACE
 * On error sends BEL ('\7')
 */
void handle_input_byte(sh_t *self, uint8_t ch)
{
    switch (ch) {
        case '\r':
            ch = '\n';
        case '\n':
            self->inpos = 0;
            self->inbuf[0] = 0;
            _SETRESP(self, "\r\n", self->promptbuf, NULL, NULL);
        break;

        /* backspace, del */
        case '\b':
        case '\x7f':
            if (self->inpos > 0) {
                self->inpos--;
                self->inbuf[self->inpos] = 0;
                _SETRESP(self, "\b \b", NULL, NULL, NULL);
            } else {
                _SETRESP(self, "\a", NULL, NULL, NULL);
            }
        break;

        /* ^U - del line */
        case 'u' & 0x1f:
            self->inpos = 0;
            self->inbuf[self->inpos] = 0;
            _SETRESP(self, "\x1B[2K\r", self->promptbuf, NULL, NULL);
        break;

        /* ^L - clear screen */
        case 'l' & 0x1f:
            _SETRESP(self, "\x1B[2J\x1B[H", self->promptbuf, self->inbuf, NULL);
        break;

        /* all other chars */
        case '\t':
            ch = ' ';
        default:
            if ((ch >= (uint8_t)' ' && ch <= (uint8_t)'\x7e')
                            || ch >= (uint8_t)'\xa0') {
                // printable
                if (self->inpos < sizeofarray(self->inbuf) - 1) {
                    // in bounds (-2 is for ch, -1 is for '\0')
                    self->inbuf[self->inpos++] = ch;
                    self->inbuf[self->inpos] = 0;
                    _SETRESP(self, self->inbuf + (self->inpos-1), NULL, NULL, NULL);
                } else {
                    _SETRESP(self, "\a", NULL, NULL, NULL);
                }
            } else {
                // non-printable
                _SETRESP(self, "\a", NULL, NULL, NULL);
            }
        break;
    }
}
Nothing special: no support of editing in the middle of the line, only at the end, clearing of line, screen, deleting last char. It generates ANSI ESC sequencies. self is the pointer to special struct with inbuf char buffer and inpos - current position of added bytes, so entered line will be in the inbuf. _SETRESP() is the macros - it sets 4 char pointers to be sent in sh_task() function (sends all 4 or until one of them is NULL) - it's looks similar like yielding of responces in WSGI Python Web application, but limited with 0 to 4 parts only :)
sh_task() is call in main loop and sends these responces, handle_input_byte() is called in UART ISR routine to save input and prepare responces. Using of struct, wrapped all needed resources helps us to avoid global and single shell only, so we can have several shells on several UARTs.
sizeofarray() is trivial macros, returns array items number. Short example how it looks:

ANSI terminal on AVR chip from bapcyk on Vimeo.

понедельник, 25 февраля 2013 г.

Roadmap to NanoLP 1.0

1.0 is planned to be beta version. So plans are:
  • several new features(for collabortive usage, flex text transformation, etc.)
  • tests improvement
  • fixing tests on Linux (problem with paths)
  • fixing lookup of installed files on Linux (ploblem with paths)
  • README files for every test
  • presentation and Quick References
  • other BUGFIX
And possible another documentation materials...

четверг, 14 февраля 2013 г.

NanoLP 1.0i released

Special Web edition: now LP code base can be published on the Web as online documentation and at the same time - as library (for including/importing into your LP programs).

Added new functions, new parser, total code refactoring, added events handling mechanism (with chaining support), URL support in filenames, Web-publishing and more...

вторник, 15 января 2013 г.

New Literate Programming Tool: Nano-LP

Yes, yet another and, I thing, last of my attempts. Done WiKi (on project page). Main goal is to keep input file clean and readable (not so cryptic like many other tools) with very "regualar" and simple, intuitive syntax. Main features:
  • OpenOffice/LibreOffice
  • MarkDown/MultiMarkdown
  • Creole
  • reStructuredText
  • TeX/LaTeX
  • txt2tags
  • Asciidoc
  • HTML/XML
  • ... all based on these
Utility is little Python script and supports:
  • defining command (macros) with placeholders in the body (code chunk)
  • pasting command code chunk
  • defining multiple parts code-chunks
  • joining, 'ending', etc. several code chunks
  • globbing command when paste
  • including one file to another (library)
  • auto-detecting of cycles
  • configurable via simple .INI like file
  • works with Python 2.7.x - Python 3.x
  • works with Unicode (UTF8)
TODO:
  1. Improvements of error reporting: file, line locating, (a special in OO - missed)
  2. More tests, better testing
  3. Improve Asciidoc parser
  4. Realize 1.0 beta
  5. ...

воскресенье, 13 января 2013 г.

Re-raise exceptions in Python in compatible (2to3) way

Good practice is to keep original traceback (stack-trace) when you re-raise modified exception. In Python 2 it's simple:
        try:
            return buggy()
        except Exception as x:
            cls,obj,tb = sys.exc_info()
            # modify obj OR create new the same class (cls), like here
            raise cls, SOME_ARGS, tb
In Python 3 it's simple too, but now there is a special BaseException method for this and new syntax, so:
        try:
            return buggy()
        except Exception as x:
            cls,obj,tb = sys.exc_info()
            # modify obj OR create new the same class (cls), like here
            raise cls(SOME_ARGS).with_traceback(tb)
Now to make common code compatible with Python 2 and 3 we SHOULD save it in DIFFERENT files, to avoid syntax errors when Python compile it to byte-code. For example, let's save it to 'comp2.py' and 'comp3.py'. And lets represent it as function... with signature reraise(exclass, msg, traceb). In 'comp2.py':
def reraise(exc, msg, tb):
    raise exc, msg, tb

and similar in 'comp3.py' but with with_traceback() usage. Now use it:
import sys
_PY23 = 3 if sys.version_info.major > 2 else 2
if _PY23 == 2:
    from comp2 import *
elif _PY23 == 3:
    from comp3 import *
That is it :)