вторник, 10 июля 2012 г.

Two usefull C macroses

As for me, there are 2 very simple but usefull C macros for safe calling of function (or method - callback in struct member):
#define SAFE_CALL(F, ...) do { if ((F)) (*(F))(##__VA_ARGS__); } while(0)
#define MCALL(SELF, CB, ...) do { if((SELF)->CB) (*(SELF)->CB)(SELF, ##__VA_ARGS__); } while(0)
Note two '#' - this is the GNU GCC extension to avoid problem with comma in variadic macros - with they we can use or not var. args. Usage is simple too:
MCALL(my_struct_ptr, onpaint, area_t* area);
Something like this... :)

пятница, 6 апреля 2012 г.

AVR32: development without IDE - only command line tools

How-To install environment for AVR32 development on Windows.


I downloaded avr-toolchain-installer-3.2.3.579-win32.win32.x86.exe and install it. Then downloaded asf-standalone-archive-3.0.1.zip (Atmel Software Framework)and unpacked it into c:\Program Files\Atmel\AVR Tools\. Then downloaded uname.exe (see bug with uname.exe, returns "windows32" on Windows instead of something like "MINGW32_NT-6.0") and replace with it installed c:\Program Files\Atmel\AVR Tools\AVR Toolchain\bin\uname.exe.

Testing of compiling ASF application: cd into c:\Program Files\Atmel\AVR Tools\asf-3.0.1\avr32\applications\uc3-dsplib-demo\at32uc3a3256_evk1104\gcc\ and run make:
"c:\Program Files\Atmel\AVR Tools\AVR Toolchain"\bin\make
after it you'll get .hex (and .elf and many other) files there!

Another way is to install AVR Studio and to use it's console but AVR Studio is so big :)

Programming a device


There are different ways to do it. For example, with avrdude, avr32program or atprogram. avrdude is free GNU program for Linux, Windows, Mac, it needs libusb to be installed. If you use Windows, then get it from http://sourceforge.net/projects/libusb-win32/, attach device, install (with inf-wizard.exe) driver (select your device from list, generate .inf and install), then run something like this:
avrdude.exe -P usb -c %PROGRAMMATOR% -p %MCU%
. Last (today:) version of avrdude for Windows is here: http://download.savannah.gnu.org/releases/avrdude/avrdude-5.11-Patch7610-win32.zip.
If you use AVR Dragon and Windows Vista, you can have problems with this!
For AVRDragon install last AVR Studio and use it's programmer tool: Jungo WinDriver. It's possible to install Jungo driver without studio, but you need also atprogram.exe or avr32program.exe tools. They needs XML files (database of MCUs and looks for themin installed Studio path).
Jungo driver will conflict with libusb drivers. So, remove libusb drivers first. For Windows: click WinKey+Pause, open Devices' manager and remove (deinstall) libusb drivers when device is attached. Then go to C:\Windows\System32 and delete file libusb0.dll. Then go to C:\Windows\System32\Drivers and delete libusb0.sys. Now detach device. Install Jungo drivers (automatically when install Studio or manually), attach device (AVRDragon I'm meaning) and if it ask you about driver files point to C:\Program Files\Atmel\Atmel USB (I thing Windows automatically can find it...). After it you should see in Deviceses' Manager this:
COMPUTER
  |
  +--Jungo
  |    |
  .    +--AVR Dragon
  .    +--WinDriver
and no any libusb32!!! AVRDragon and board should be on power (attached to USB). If Windows found "Unknown devices" (board, attached to USB for power) you can delete it from device list. Now if you use Studio 6 run in Atmel Studio Command Prompt (see Programs menu) something like this:
atprogram -t avrdragon -i jtag -d at32uc3b0512 read -fs -s 4
and you will see data bytes in console.
Better is to run Studio and try to open programming dialog. Studio will verify version of AVR Dragon firmware and upgrade it (if needed).

пятница, 16 марта 2012 г.

High-pass filter

In one of my posts I wrote about low-pass IIR filter (of 1st order). Here is the simple high-pass filter in form of IIR of 1st order. See more info here.
In my application HMC demo I poll sensor (accelerometer) and filter Ax signal:
HPFilter hpf
hpf configure -datatype flt -datatypes {phys}
Saver saver1
saver1 listen -on
saver listen -on
listen phvals:hpf
listen hpf:saver1
serial open 4
sensor poll -loop
This will create 2 CSV files with native and with filtered signals (after saving we rename them). We can render signals with GLE tool:
begin graph
    title "Accelerations"
    xtitle "Time [sec]"
    ytitle "Ax [g]"
    axis grid
    ticks color grey40
    subticks on
    subticks lstyle 2
    set font "texcmr"

    d1 file "c:/tmp/saver.csv,0,5"
    let d2 = d1
    d1 line color red
    d2 deresolve 10 average line color red lwidth 0.1

    d3 file "c:/tmp/saver1.csv,0,5"
    let d4 = d3
    d3 line color blue
    d4 deresolve 10 average line color blue lwidth 0.1
end graph

begin key
    set fill yellow
    line color red text "Native"
    line color blue text "Filtered"
end key
and get this image:
 Blue line is signal after high-pass filtering: it shows effect of removing slow drift.
 Removing of slow drift is used in position estimation of sensor (from acceleration) in multicascading scheme:
HPF -> INTEGR -> HPF -> INTEGR -> HPF
Here is the C code (for SWIG) of this kind of filter:
double* filter(double *v) {
        int i;
        int vlen;

        vlen = (int)v[0];
        if (vlen != $self->n) {
            return (NULL);
        }

        for (i=1; i <= vlen; i++) {
            $self->v[i] = $self->a * ($self->v[i] + v[i] - $self->x[i]);
            $self->x[i] = v[i];
        }
        return ($self->v);
    }
$self is the pointer to CHPFilter struct:
typedef struct CHPFilter {
        int n; // length of v
        double a; // alpha
        double *v; // calculated values (keep prev. before filter)
        double *x; // prev. input
} CHPFilter;

понедельник, 12 марта 2012 г.

Docstrings in Tcl (and IncrTcl)

Docstrings are very popular in Python, Lisp... This is the easy way to include documentation into implementation (reverse LP). But in Tcl are can be usable to generate help of commands for user. Here is the listing of one (it support procs and Itcl methods):
package provide mydoc 1.0

namespace eval mydoc {
    namespace export doc
}

set _NOHELPMSG "No help."

proc ::mydoc::_docstring {body} {
    set body [string trim $body]
    set docstring ""
    # without 1st '^' will match any FIRST docstring block even after
    # commands!
    if {[regexp {^#\s*([^\n]+\n)+(\n\n)} $body docstring]} {
        set docstring [regsub -all {\s*#\s?} $docstring \n]
        set docstring [string trim $docstring]
        return $docstring
    }
}

proc ::mydoc::doc args {
    # Help on command: procedure or class method. Call:
    #   doc some_object some_method
    #   doc some_class some_method
    #   doc some_proc


    global _NOHELPMSG
    set found ""
    switch -- [llength $args] {
        1 {
            # args: proc
            set name [lindex $args 0]
            set arguments [info args $name]
            set body [info body $name]
            set found [_docstring $body]
        }
        2 {
            # FIXME not optimal!
            # args: object|class method
            lassign $args cls_obj meth
            set objs [itcl::find objects]
            # cls_obj may be object OR class. What is it?
            if {-1 != [lsearch -regexp $objs :*$cls_obj]} {
                # this is the object
                set arguments [$cls_obj info args $meth]
                set body [$cls_obj info body $meth]
                set found [_docstring $body]
            } else {
                # this is the class
                set arguments [namespace eval ::$cls_obj info args $meth]
                set body [namespace eval ::$cls_obj info body $meth]
                set found [_docstring $body]
            }
        }
        default { error "wrong args: proc | object method | class method" }
    }
    if {$found eq ""} {
        return $_NOHELPMSG
    } else {
        return $found
    }
}

# txt is the string with \n, shifter is like '\t' or '\t\t'..
proc mydoc::_shift_strings {txt shifter} {
    if {$txt ne ""} {
        return "$shifter[regsub -all \n $txt \n$shifter]"
    }
}


# Generate only for documented with docstrings
proc mydoc::_genrst {fname} {
    set result {}
    # Collect help on objects and it's methods
    set clshelp {}
    foreach cls [itcl::find classes] {
        set her [namespace eval $cls "info heritage"]

        set varhelp {}
        foreach v [namespace eval $cls info variable] {
            catch {
                #lappend varhelp [namespace eval $cls info variable $v -protection public]
                if {[string first "::_" $v] == -1} {
                    switch -- [namespace eval $cls info variable $v -protection] {
                        public { set vprot "public" }
                        protected { set vprot "protected" }
                        private { set vprot "private" }
                    }
                    lappend varhelp "- $vprot $v"
                }
            }
        }

        set methelp {}
        foreach func [namespace eval $cls "info function"] {
            catch {
                set body [string trim [namespace eval $cls "info body $func"]]
                if {$body ne ""} {
                    set arguments [namespace eval $cls "info args $func"]
                    if {$arguments eq ""} { set arguments "no args." }
                    set docstring [_shift_strings [_docstring $body] \t]
                    if {$docstring ne ""} {
                        lappend methelp "*${func}*: **${arguments}**"
                        lappend methelp ""
                        lappend methelp "::"
                        lappend methelp ""
                        lappend methelp $docstring
                        lappend methelp ""
                        lappend methelp ""
                    }
                }
            }
        }
        if {$methelp ne ""} {
            # there are methods with docstrings!
            if {[llength $her] > 1} {
                # there are base classes
                set bases [lrange $her 1 end]
                set her "[lindex $her 0] (*extends ${bases}*)"
            }
            lappend clshelp "$her"
            lappend clshelp [string repeat "-" [string length $her]]
            lappend clshelp ""
            lappend clshelp "Variables"
            lappend clshelp "~~~~~~~~~"
            lappend clshelp ""
            if {$varhelp ne ""} {
                lappend clshelp [join $varhelp "\n"]
            } else {
                lappend clshelp "No variables."
            }
            lappend clshelp ""
            lappend clshelp "Methods"
            lappend clshelp "~~~~~~~"
            lappend clshelp ""
            lappend clshelp {*}$methelp
        }
    }
    # Collect procs help
    set prochelp {}
    foreach func [uplevel #0 info procs] {
        catch {
            set body [string trim [uplevel #0 info body $func]]
            if {$body ne ""} {
                set arguments [uplevel #0 info args $func]
                if {$arguments eq ""} { set arguments "no args." }
                set docstring [_shift_strings [_docstring $body] \t]
                if {$docstring ne ""} {
                    lappend prochelp "*${func}*: **${arguments}**"
                    lappend prochelp ""
                    lappend prochelp "::"
                    lappend prochelp ""
                    lappend prochelp $docstring
                    lappend prochelp ""
                    lappend prochelp ""
                }
            }
        }
    }

    if {$clshelp ne ""} {
        lappend result ""
        lappend result "Classes"
        lappend result "======="
        lappend result ""
        lappend result {*}$clshelp
    }
    if {$prochelp ne ""} {
        lappend result ""
        lappend result "Procedures"
        lappend result "=========="
        lappend result ""
        lappend result {*}$prochelp
    }


    set fid [open $fname w]
    puts -nonewline $fid [join $result "\n"]
    close $fid
}
This mechanism supports not only interactive help system for user (doc proc) but also help generating (to ReStructured Text format). Example of using (remember that two empty lines are used to end docstring section:
proc fun {args} {
  # Do something. Syntax:
  #  -one -- first action
  #  -two -- second action


  .. code ..
}

# OR

itcl::class C {
  method f {args} {
    # My method. Syntax:
    #  f one two -- do something
    #  f three four -- do something else


    .. code ..
  }
}
and then call:
doc fun
doc C f
C cobj
doc cobj f
_genrst procedure is used to generate documentation. It looks like this:

Classes

::HMC6343Protocol

Variables

  • protected ::HMC6343Protocol::this

Methods

::HMC6343Protocol::get: cmd what
Returns protocol info (what) about command cmd. What is:
  -def - definition of command (Tcl commands list to be execute for request)
  -resplen - length of response in bytes
  -respfmt - format of response
  -all - as fields as list
 
::HMC6343Protocol::unpack: cmd buf
Returns unpacked (as list) values from buf for cmd

::HMC6343Protocol::commands: no args.
Returns known commands names

::HMC6343Protocol::constructor: defs
Creates object.
  - defs is the list of CMD RESPONSE_LEN RESPONSE_FMT { Serial commands }...
  - RESPONSE_FMT is like in binary command
It's easy to use it in Makefile for help generation:
RST2HTML = C:/Python32/python.exe C:/Python32/Scripts/rst2html.py
hlp:
 tclkit.exe gendoc.tcl
 $(RST2HTML) hlp.rst hlp.html
Use tclkit.exe if you have Tk code (instead of tclkitsh.exe). Python and docutils Python package are used to trnasform .rst into .html. gendoc.tcl looks like this:
# Generate hlp.rst file with references to all classes, its methods
# and procedures with docstrings (see lib/mydoc.tcl)
package require starkit
starkit::startup
starkit::autoextend [file join $starkit::topdir]
package require myutl
set packages [glob -directory lib -tails "*.tcl"]
foreach pkg $packages {
    set pkg [regsub {.tcl$} $pkg ""]
    if {$pkg ne "pkgIndex"} {
        package require $pkg
    }
}
mydoc::_genrst help.rst
exit 0
it imports all packages from lib directory and generates help.rst file for its' procs and methods. Makefile magic:
make hlp
:)