четверг, 23 июня 2011 г.

Usage of UART for debug messages on AVR

If you need to print out a debug messages to UART port of AVR chip, you can use a simple way for it: switch stdout stream to UART. This is the code of the solution:
#include <avr/io.h>
#include <stdio.h>

#ifndef NDEBUG

#  if !defined(F_DEBUG_UART) && !defined(F_CPU)
#    error F_DEBUG_UART frequency or F_CPU frequency should be defined!
#  endif

#  ifndef F_DEBUG_UART
#    define F_DEBUG_UART F_CPU
#  endif

#  ifndef DEBUG_NUART
#    error DEBUG_NUART should be defined!
#  endif

#  define __CONC3(X, Y, Z) X ## Y ## Z
#  define _CONC3(X, Y, Z) __CONC3(X, Y, Z)
// _CONC3(UDRE, DEBUG_NUART,) does not works
#  define _UDRE (_CONC3(UDR, E, DEBUG_NUART))
#  define _UDR (_CONC3(UDR, DEBUG_NUART,))
#  define _UBRRH (_CONC3(UBRR, DEBUG_NUART, H))
#  define _UBRRL (_CONC3(UBRR, DEBUG_NUART, L))
#  define _UCSRA (_CONC3(UCSR, DEBUG_NUART, A))
#  define _UCSRB (_CONC3(UCSR, DEBUG_NUART, B))
#  define _UCSRC (_CONC3(UCSR, DEBUG_NUART, C))
#  define _RXEN (_CONC3(RXEN, DEBUG_NUART,))
#  define _TXEN (_CONC3(TXEN, DEBUG_NUART,))
#  define _UCSZ0 (_CONC3(UCSZ, DEBUG_NUART, 0))
#  define _U2X (_CONC3(U2X, DEBUG_NUART,))
#  define DEBUG_UART_SUPPORT() \
static int debug_uart_putchar(char c, FILE *stream); \
static FILE mystdout = FDEV_SETUP_STREAM(debug_uart_putchar, NULL, _FDEV_SETUP_WRITE); \
static int debug_uart_putchar(char c, FILE *stream) \
{ \
        while (0 == (_UCSRA & (1<<_UDRE))) ; \
        _UDR = c; \
        return (0); \
}

/* 8 bits data, 1 stop bit, 9600 kbps, async */
#  define INIT_DEBUG_UART() do { \
        stdout = &mystdout; \
        _UCSRC = 0x06; \
        _UCSRA = 0x00; \
        _UCSRB = 0x18; /* receive, transmit, no interrupts */ \
        uint16_t ubrr = (F_DEBUG_UART/16/9600-1); \
        _UBRRL = (unsigned char)ubrr; \
        _UBRRH = (unsigned char)(ubrr>>8); \
} while (0)

#  define DEBUG_UART_PRN(...) printf(__VA_ARGS__)

#else
#  define DEBUG_UART_SUPPORT()
#  define INIT_DEBUG_UART()
#  define DEBUG_UART_PRN(...)
#endif
Save it into debug_uart.h, for example.
What you need to use it, it's only defining F_DEBUG_UART or F_CPU, DEBUG_NUART and calling 3 macros: DEBUG_UART_SUPPORT() on module level (it generates code, so call it only once!), INIT_DEBUG_UART() to init UART and DEBUG_UART_PRN(...) where you need message printing. The example is:
#define F_DEBUG_UART 7372800
#define DEBUG_NUART 0
#include "debug_uart.h"
DEBUG_UART_SUPPORT();
int
main(void)
{
        INIT_DEBUG_UART();
        DEBUG_UART_PRN("Hello world, %d\n", 99);
        ... so on ...
F_DEBUG_UART is the exact amount of the system oscillator frequency to calculate UART baud rate. Default value is equals to F_CPU, but it's possible, that you have value of fosc = 7.3728MHz (see AVR Datasheet about "Examples of UBRR settings") and you use in the Makefile F_CPU value as 8MHz - if you use 8MHz for UBRR calculation too, you will get errors in transmission! So use for F_DEBUG_UART exact values only!
DEBUG_NUART defined number of UART. If you define NDEBUG, all macros will be empty - you don't need to delete it's calls. For more information see help on WinAVR.

вторник, 21 июня 2011 г.

Parsing of paths

Paths are often used in software environments: in file systems, in configuration files, in different IDs and so on. Here are two Python functions, used for parsing of them, they generate tracks from paths (function dothier()) and map based trees (function dottree()). Here you are:
def dothier(seq, sep='.'):
    """Generate path from dot-strings in order for creation
    tree: first - parents then children:
    >>> for x in dothier(["x.y.z", "a"]): print x, x.leaf
    a True
    x False
    x.y False
    x.y.z True

    Every returned string (unicode) has flag - leaf
    """
    # returns this, as unicode but with flag leaf
    _Node = type("dothier_node", (unicode,), {"leaf":False})

    seq = sorted(seq)
    mem = set()
    for p in seq:
        spl = p.split(sep)
        for ip in xrange(1, len(spl) + 1):
            pp = _Node(sep.join(spl[:ip]))
            if len(spl) == 1 or ip == len(spl):
                pp.leaf = True
            if not pp in mem:
                yield pp
                mem.add(pp)

def dottree(seq, sep='.', fullpath=False):
    """Returns dict of dicts like tree from sequence of dot separated strings.
    For example,
    >>> dottree(["x.y.z", "x.y", "a", "a.b.c", "a.b"])
    {'a': {'b': {'c': {}}}, 'x': {'y': {'z': {}}}}

    and

    >>> dottree(["x.y.z", "x.y", "a", "a.b.c", "a.b"], fullpath=True)
    {'a': {'a.b': {'a.b.c': {}}}, 'x': {'x.y': {'x.y.z': {}}}}
    """
    def ins(map, path):
        d = map
        parents = []
        for p in path:
            if fullpath:
                key = sep.join(parents + [p])
            else:
                key = p
            d = d.setdefault(key, {})
            parents.append(p)
    ret = {}
    seq = sorted(seq)
    for p in seq:
        pp = p.split(sep)
        if not pp:
            continue
        ins(ret, pp)
    return ret
Examples of usage are... in it's docstrings :) They are useful in GUI application: to construct tree or another hierarchical controls from paths (retrieved from config. files, for example).
This code is from my project "HMI pybase" (see Projects).

AVR timers support

These modules implements for AVR8, AVR128, AVR168P chips next functions:
void init_timers(void);
int alarm_action(int timer, timer_handler_t alarm_handler);
int _alarm(int timer, unsigned long usec);
int alarm(int timer, unsigned long usec);
unsigned long tclock(int timer);
unsigned long millis(void);
unsigned long micros(void);
alarm() is like standard C function (but for AVR), it's wrapped by cli()/sei() _alarm(). int timer is the number of the used timer. tclock() is like C's clock() but used timer id. clock() is the tclock(0) - standard C function, implemented on timer 0. millis()/micros() returns milliseconds/microseconds from last call. Before use, call init_timer() first.

Before to use, define F_CPU. CLOCKS_PER_SEC should not be defined. If is, this means that some timer for clock() is already in use. Tested with WinAVR-20100110, but may be needs more testing!

You can download source code from here on Google Project page.

пятница, 10 июня 2011 г.

Software SPI implementation for WinAVR

Sometimes you need software SPI MASTER (in my case it was needed to test one chip) implementation. It supports all SPI modes (0, 1, 2, 3), and clock dividers (but so it's software "SPI", that timing is not exact!), but sends only in MSBFIRST bit order (it's easy to add LSBFIRST support).
See the code.
To use it in your project, you need to define some macro-variables (with #define-s) before include the file. They are:
  • SWSPI_MOSI_PORT - port for MOSI "wire"
  • SWSPI_MOSI_PIN - pin for MOSI "wire"
  • SWSPI_MISO_PORT - like above but for MISO (only port)
  • SWSPI_MISO_PIN - line above, but pin for MISO
  • SWSPI_SCLK_PORT - port for SCLK "wire"
  • SWSPI_SCLK_PIN - pin for SCLK "wire"
  • SWSPI_MODE - mode of SPI communication
  • SWSPI_DIV - if not defined, 1 will be used

The example:

#define SWSPI_MOSI_PORT PORTB
#define SWSPI_MOSI_PIN PB3
#define SWSPI_MISO_PORT PINB
#define SWSPI_MISO_PIN PB4
#define SWSPI_SCLK_PORT PORTB
#define SWSPI_SCLK_PIN PB5
#define SWSPI_MODE SPI_MODE0
#define SWSPI_DIV SPI_CLOCK_DIV2

In my real application I included .c file to local file in my project, it's not convenient way for C, but... seems like this:

common/   |  myprj/
swspi.h   |    conf.h (#include <common/swspi.h> and defines macro-vars)
swspi.h   |    spi.c (#include "conf.h" then #include <common/swspi.c>

You can use another scheme, it's trivial.
Here is the code. The .h file:

#ifndef _COMMON_SWSPI_H
#define _COMMON_SWSPI_H

#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>

#define SPI_CLOCK_DIV4 4
#define SPI_CLOCK_DIV16 16
#define SPI_CLOCK_DIV64 64
#define SPI_CLOCK_DIV128 128
#define SPI_CLOCK_DIV2 2
#define SPI_CLOCK_DIV8 8
#define SPI_CLOCK_DIV32 32
#define SPI_CLOCK_DIV64_2 64

#define SPI_MODE0 0
#define SPI_MODE1 1
#define SPI_MODE2 2
#define SPI_MODE3 3

uint8_t spi_transfer(uint8_t data);
uint8_t spi_ntransfer(const uint8_t *req, int reqlen, uint8_t *resp, int resplen);
#define spi_interrupt_on()
#define spi_interrupt_off()

// for nCS (nSEL) select of device before sending
#define BEGIN_SPI_SENDING(PORT, PIN) do { \
        (PORT) &= ~_BV(PIN); \
        _delay_loop_1(4); \
} while (0)

// for nCS (nSEL) unselect of device after sending
#define END_SPI_SENDING(PORT, PIN) do { \
        _delay_loop_1(4); \
        (PORT) |= _BV(PIN); \
} while (0)

#endif /* !_COMMON_SWSPI_H*/

and the .c file (swspi.c):

#include "swspi.h"
#include <util/delay_basic.h>

#if !defined(SWSPI_MOSI_PORT) || !defined(SWSPI_MOSI_PIN) || \
    !defined(SWSPI_MISO_PORT) || !defined(SWSPI_MISO_PIN) || \
    !defined(SWSPI_SCLK_PORT) || !defined(SWSPI_SCLK_PIN) || \
    !defined(SWSPI_MODE)
#  error Software SPI not configured! SWSPI_* should be configured!
#endif

#define setbit(P, B) ((P) |= (_BV(B)))
#define clibit(P, B) ((P) &= ~(_BV(B)))
#define getbit(P, B) (((P) & (_BV(B)))? 1:0)

#ifdef SWSPI_DIV
#  define SPIDELAYTIME ((SWSPI_DIV)/2)
#else
#  define SPIDELAYTIME 1
#endif
#define SPIHALFDELAY() _delay_loop_1(SPIDELAYTIME) // half of tact (bit transfer) - min 1 CPU tact

#define SETMOSI() setbit(SWSPI_MOSI_PORT, SWSPI_MOSI_PIN)
#define CLIMOSI() clibit(SWSPI_MOSI_PORT, SWSPI_MOSI_PIN)
#define NOMOSI() setbit(SWSPI_MOSI_PORT, SWSPI_MOSI_PIN)

// instead of PORTX may be PINX?
#define READMISO() getbit(SWSPI_MISO_PORT, SWSPI_MISO_PIN)

#if (2 & SWSPI_MODE)
#  define ONSCLK() clibit(SWSPI_SCLK_PORT, SWSPI_SCLK_PIN)
#  define OFFSCLK() setbit(SWSPI_SCLK_PORT, SWSPI_SCLK_PIN)
#else
#  define ONSCLK() setbit(SWSPI_SCLK_PORT, SWSPI_SCLK_PIN)
#  define OFFSCLK() clibit(SWSPI_SCLK_PORT, SWSPI_SCLK_PIN)
#endif

#if (1 & SWSPI_MODE)
#  define SHIFTBIT(outbyte, inbyte) do { \
        (outbyte) & 0x80 ? (SETMOSI()) : (CLIMOSI()); \
        (outbyte) <<= 1; \
        ONSCLK(); \
        SPIHALFDELAY(); \
        (inbyte) <<=1; \
        (inbyte) |= READMISO(); \
        OFFSCLK(); \
        SPIHALFDELAY(); \
} while (0)
#else
#  define SHIFTBIT(outbyte, inbyte) do { \
        (outbyte) & 0x80 ? (SETMOSI()) : (CLIMOSI()); \
        (outbyte) <<= 1; \
        SPIHALFDELAY(); \
        ONSCLK(); \
        SPIHALFDELAY(); \
        (inbyte) <<=1; \
        (inbyte) |= READMISO(); \
        OFFSCLK(); \
} while (0)
#endif

uint8_t
spi_transfer(uint8_t data) {
        int nbit;
        uint8_t res = 0;
        for (nbit=0; nbit<8; nbit++) {
                SHIFTBIT(data, res);
        }
        NOMOSI();
        return (res);
}

/* resp - responce from slave device; resplen - max. expected resp. len (buf. size);
 * if resp is NULL, not saved. Returns always the last byte of the response.
 */
uint8_t
spi_ntransfer(const uint8_t *req, int reqlen, uint8_t *resp, int resplen) {
        int nbit;
        int nbyte;
        register uint8_t outbyte;
        uint8_t inbyte = 0;
        for (nbyte=0; nbyte<reqlen; nbyte++) {
                outbyte = req[nbyte];
                inbyte = 0;
                for (nbit=0; nbit<8; nbit++) {
                        SHIFTBIT(outbyte, inbyte);
                }
                if (resp && nbyte < resplen) {
                        resp[nbyte] = inbyte;
                }
        }
        NOMOSI();
        return (inbyte);
}

#undef ONSCLK
#undef OFFSCLK
#undef SETMOSI
#undef CLIMOSI
#undef NOMOSI
#undef READMISO
#undef SPIHALFDELAY
#undef SPIDELAYTIME
#undef SHIFTBIT
#undef SHIFT0
To send and receive response spi_transfer() and spi_ntransfer() functions are needed. First sends one byte and returns responce's byte (from MISO), seconde - sends several bytes and saves them into 'resp' buffer (contrained by 'resplen') and if resp i NULL, returns the last byte only.
Before any transfer (one or several bytes in the one request) to some chip you should call macro BEGIN_SPI_SENDING(CHIP_NSEL_PORT, CHIP_NSEL_PIN), where CHIP_NSEL_PORT and CHIP_NSEL_PIN defines nCS wire for this concrete chip. After sending you should call END_SPI_SENDING(CHIP_NSEL_PORT, CHIP_NSEL_PIN) to release the wire by nCS.

понедельник, 6 июня 2011 г.

Another approach to Literate Programming

I tried for Literate Programming (LP) two solutions: Nuweb and Python tool Pyweb and found Pyweb tool is better. But with LP there are some problems:
  1. it's not very easy to work with "hidden" source: after tangling http://www.blogger.com/img/blank.gifand weaving you get pure source code and should debug source code (this is usually a "solid" process), after this - should modify .w file

  2. usage of "placeholders" lines split code (difficult for debugging)

Another approach is to use reStructuredText document generator system (see docutils). How to? It's easy: instead of embedding into source code the hyper-text formatting, images, links, you can write your program like story (in usual LP style), use RST syntax (like WiKi) for images embedding and various styles - your code is Literate Programming story as before. But now you can embed code fragments where you need with "include" directive of docutils. This directive requires "startd-after" and "end-before" options for this case. These options defines line (string fragment) the code starts after, and line - the code ends before. I use for this lines Vim foldering lines. See:
// Source code:
int x;
// Opening port {{{
int open_port(int port) {
...
}
// }}}

This is very usuable for folding big source code!
And in your .rst file (which plays a role of the .w file but for weaving only):
and we define the function for a port opening:

.. include:: port.c
:start-after: // Opening port {{{
:end-before: // }}}

Thos technique is not pure LP but is like LP without LP cons. And it's simple documentation approach :)

среда, 1 июня 2011 г.

Programming languages translated to C

There are:
  • ooc
  • BCX (it's Basic)
  • Nimrod
  • Eiffel
  • Scheme (shlep, Gambit-C, Bigloo, Chicken)
  • gcl (it's Common Lisp)
  • Virgil (it's Java-like)
  • Vala, Genie (from GNOME project)
  • Seed7 (Pascal-like)
  • Modula-2/Oberon-2
  • Mercury
and also there are translaters from Algol, Ada, Fortran, Pascal, Ada. Use Google to find it's repos :)