2012年4月19日星期四

IAP on NXP LPC1768

In LPC1768, there are 512KB FLASH memory. We can use some of it to store our configuration data. So In Application Programming (IAP) is worth try.

There are IAP support in the LPC1768 boot code. We can just call the IAP routines to implement it.
In my original design, FLASH sector 27 and 28 are utilised to store configuration data. But after trying a lot of times, I give up. It seems sector 28 can be erased, but can't be written. This may be a bug of the LPC1768 boot code.
 
source code are provided below:
  • iap_drv.c


#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "iap_drv.h"

static const unsigned int flash_sector_addrs[] = {
    0x0000,    0x1000,    0x2000,    0x3000,
    0x4000,    0x5000,    0x6000,    0x7000,
    0x8000,    0x9000,    0xa000,    0xb000,
    0xc000,    0xd000,    0xe000,    0xf000,
    0x10000,    0x18000,    0x20000,    0x28000,
    0x30000,    0x38000,    0x40000,    0x48000,
    0x50000,    0x58000,    0x60000,    0x68000,
    0x70000,    0x78000
};

static const unsigned int flash_sector_sizes[] = {
    0x1000,    0x1000,    0x1000,    0x1000,
    0x1000,    0x1000,    0x1000,    0x1000,
    0x1000,    0x1000,    0x1000,    0x1000,
    0x1000,    0x1000,    0x1000,    0x1000,
    0x8000,    0x8000,    0x8000,    0x8000,
    0x8000,    0x8000,    0x8000,    0x8000,
    0x8000,    0x8000,    0x8000,    0x8000,
    0x8000,    0x8000
};

enum command_code{
    IAPCommand_Prepare_sector_for_write_operation = 50,
    IAPCommand_Copy_RAM_to_Flash,
    IAPCommand_Erase_sector,
    IAPCommand_Blank_check_sector,
    IAPCommand_Read_part_ID,
    IAPCommand_Read_Boot_Code_version,
    IAPCommand_Compare,
    IAPCommand_Reinvoke_ISP,
    IAPCommand_Read_device_serial_number
};

typedef void (*iap_entry_t)(unsigned int [], unsigned int []);

static iap_entry_t iap_entry = (iap_entry_t)0x1fff1ff1;

static int cclk_kHz = 0;
extern uint32_t SystemFrequency;

void iap_init(void)
{
    cclk_kHz = SystemFrequency/1000;
}

int iap_read_id(void)
{
    unsigned int cmd[5], res[5];
    cmd[0] = IAPCommand_Read_part_ID;
    iap_entry(cmd, res);
    return ((int)res[1]);
}

int iap_read_version(void)
{
    unsigned int cmd[5], res[5];
    cmd[0] = IAPCommand_Read_Boot_Code_version;
    iap_entry(cmd, res);
    return ((int)res[1]);    //NOTE: different from document UM10360, P635
}


int iap_read_serial(void)
{
    unsigned int cmd[5], res[5];
    cmd[0] = IAPCommand_Read_device_serial_number;
    iap_entry(cmd, res);
    return ((int)res[1]);
}

int iap_blank_check(unsigned int sector)
{
    unsigned int cmd[5], res[5];
    cmd[0] = IAPCommand_Blank_check_sector;
    cmd[1] = (unsigned int)sector;
    cmd[2] = (unsigned int)sector;
    iap_entry(cmd, res);
    return ((int)res[0]);
}

static int iap_prepare(unsigned int sector)
{
    unsigned int cmd[5], res[5];
    cmd[0] = IAPCommand_Prepare_sector_for_write_operation;
    cmd[1] = (unsigned int)sector;
    cmd[2] = (unsigned int)sector;
    iap_entry(cmd, res);
    if(res[0]){
        printf("iap_prepare:result:%d\n",res[0]);
    }
    return ((int)res[0]);
}

int iap_erase(unsigned int sector)
{
    unsigned int cmd[5], res[5];
    iap_prepare(sector);
    cmd[0] = IAPCommand_Erase_sector;
    cmd[1] = (unsigned int)sector;
    cmd[2] = (unsigned int)sector;
    cmd[3] = cclk_kHz;
    iap_entry(cmd, res);
    if(res[0]){
        printf("iap_erase:result:%d\n",res[0]);
    }
    while(iap_blank_check(sector)){
        ;
    }
    return ((int)res[0]);
}

static int iap_compare(unsigned int src_addr, unsigned int dest_addr, unsigned int size)
{
    unsigned int cmd[5], res[5];

    cmd[0] = IAPCommand_Compare;
    cmd[1] = (unsigned int)dest_addr;
    cmd[2] = (unsigned int)src_addr;
    cmd[3] = size;
    iap_entry(cmd, res);
    if(res[0]){
        printf("\niap_compare(0x%x,0x%x,0x%x) result:%d,pos[0x%x]",
                src_addr, dest_addr, size, res[0],res[1]);
    }
    return ((int)res[0]);
}
int iap_write(unsigned int sector, unsigned int offset,
        const void *buf, unsigned int size)
{
    unsigned int cmd[5], res[5];
    char wbuf[256]; // this buffer is to make 256byte boundary, according to IAP requirement
    unsigned int i;
    unsigned int dest_addr = flash_sector_addrs[sector] + offset;
    for(i = 0; i<size; i+=sizeof(wbuf)){
        unsigned int s = size-i;
        iap_prepare(sector);
        if (s>sizeof(wbuf)) s = sizeof(wbuf);
        memset(wbuf, 0, sizeof(wbuf));
        memcpy(wbuf, buf+i, s);
        cmd[0] = IAPCommand_Copy_RAM_to_Flash;
        cmd[1] = (unsigned int)dest_addr+i;
        cmd[2] = (unsigned int)wbuf;
        cmd[3] = sizeof(wbuf);
        cmd[4] = cclk_kHz;
        iap_entry(cmd, res);
        if(res[0]){
            printf("cmd[%x,%x,%x], result:%d\n",cmd[1], cmd[2], cmd[3], res[0]);
        }
        if(iap_compare(dest_addr+i, (unsigned int)wbuf, sizeof(wbuf))){
            return -1;
        }
        if(memcmp((const void *)(dest_addr+i), wbuf, sizeof(wbuf))){
            printf("iap_write failed\n");
            return -1;
        }
        if(res[0]) return (int)res[0];
    }
    return ((int)res[0]);
}

void *iap_get_ptr(unsigned int sector, unsigned int offset)
{
    unsigned int src_addr = flash_sector_addrs[sector] + offset;
    return (void *)src_addr;
}


int iap_read(unsigned int sector, unsigned int offset,
        void *buf, unsigned int size)
{
    memcpy(buf, iap_get_ptr(sector, offset), size);
    return 0;
}

int iap_copy(unsigned int dest_sector, unsigned int src_sector,
        unsigned int offset, unsigned int size)
{
    unsigned int i;
    char buf[256];
    unsigned int s;
    for(i=0; i<size; i+= sizeof(buf)){
        s = sizeof(buf);
        if(i+s>size) s = size-i;
        iap_read(src_sector, offset+i, buf, s);
        iap_prepare(dest_sector);
        iap_write(dest_sector, offset+i, buf, s);
    }
    return 0;
}

int iap_cmp(unsigned int sector, unsigned int offset,
        const void *buf, unsigned int size)
{
    return memcmp(buf, iap_get_ptr(sector, offset), size);
}



  • iap_drv.h
#pragma once
void *iap_get_ptr(unsigned int sector, unsigned int offset);
int iap_read(unsigned int sector,unsigned int offset, void *buf,unsigned int size);
int iap_write(unsigned int sector,unsigned int offset, const void *buf,unsigned int size);
int iap_cmp(unsigned int sector, unsigned int offset, const void *buf, unsigned int size);
int iap_copy(unsigned int dest_sector, unsigned int src_sector,
        unsigned int offset, unsigned int size);
int iap_erase(unsigned int sector);
int iap_blank_check(unsigned int sector);
int iap_read_serial(void);
int iap_read_version(void);
int iap_read_id(void);
void iap_init(void);



How to use:

iap_erase(sector_id);
iap_write(sector_id, offset_in_sector, data, data_size);

Please note that offset_in_sector should be 256 aligned. data_size can be any.

Sector 26, 27 can store 32KiB data each, that's enough for most configuration data.

NXP bugs found:
  1. Sector 28 can't be written
  2. In page 635 of UM10360 LPC17xx User Manual, the returned version number is in Result1, NOT Result0 in the document










2012年3月23日星期五

Let fakeroot keep file attributes between sessions

By default, fakeroot keeps file attributes in memory. So when a session exit, attributes lost.
I met this problem when modifying a rootfs directory. Attributes of files in "/dev"  always lost when initial session exit.
A quick and dirty hack is to add this line to .bashrc:
alias fakeroot="fakeroot -i $HOME/local/share/fakeroot/fakeroot.sav -s $HOME/local/share/fakeroot/fakeroot.sav"

and create the related directory:
mkdir -p ~/local/share/fakeroot
touch ~/local/share/fakeroot/fakeroot.sav

Then restart bash.
Attributes will be saved to fakeroot.sav when session exit, and will be loaded when session start.

Limitation:
Always use only single fakeroot instance in one time.

2011年10月26日星期三

Ubuntu 11.04中 VirtualBox 4.0 更新时遇到的问题及解决

最近在我的Ubuntu 11.04更新Linux Kernel时出现VirtualBox 4.0驱动升级失败的情况。

命令:
sudo /etc/init.d/vboxdrv setup

输出:

* Stopping VirtualBox kernel modules                                            *  done.
 * Uninstalling old VirtualBox DKMS kernel modules                               *  done.
 * Trying to register the VirtualBox kernel modules using DKMS                 
Error! Bad return status for module build on kernel: 2.6.38-12-generic (x86_64)
Consult the make.log in the build directory
/var/lib/dkms/vboxhost/4.0.0/build/ for more information.

 * Failed, trying without DKMS
 * Recompiling VirtualBox kernel modules                                       
 * Look at /var/log/vbox-install.log to find out what went wrong


试着手动编译,发现是VirtualBox试图使用linux/autoconf.h,而在较新的Kernel中此文件更改了路径。

解决方法如下:

cd /usr/src/linux-headers-2.6.38-12-generic/include/linux
sudo ln -s ../generated/autoconf.h .
sudo /etc/init.d/vboxdrv setup

对于不同的Linux Kernel 版本,应使用对应的/usr/src下的路径名。

2011年10月14日星期五

一个简单的ringbuffer (circular buffer) 程序(C语言)

最近需要在使用一个ringbuffer,用于缓冲音频数据。
数据在两个线程之间传递。按说是很简单的功能,但合用的C库还不太好找。
找了一下,看到合适一点的库也是GPL的,不太适合我用在闭源工程中,于是自己重新写了一个简单的,以LGPL方式发行。

功能比较简单,数据分块,每次读或写一个块。
块大小和块数量在ringbuffer初始化时设定。
同时仅支持一个reader和一个writer。

测试环境:
ARM Linux, gcc 4.1.1, uClibc

代码附在后面,其使用方法:

initialize:
    rb_t *rbuf = rb_open(chunk_size, chunk_count);

reader:

    while(rb_read_ok(rb,buf)==false){
        idle_task();
    }

writer:

    while(rb_write_ok(rb,buf)==false){
        idle_task();
    }

deinitialize:

    rb_close(rbuf);

 
ringbuf.h:
#pragma once
#include <stdint.h>
#include <stdbool.h>

struct rb_s;
typedef struct rb_s rb_t;

rb_t *rb_open(unsigned int size, unsigned int count);
void rb_reset(rb_t *rb);
void rb_close(rb_t *rb);
bool rb_writable(rb_t *rb);
bool rb_readable(rb_t *rb);
bool rb_write_ok(rb_t *rb, const void *data);
bool rb_read_ok(rb_t *rb, void *buf);


ringbuf.c:

#include <malloc.h>
#include <string.h>
#include "ringbuf.h"
struct rb_s{
    unsigned int size;
    unsigned int count;
    unsigned int rptr, wptr;
    unsigned char *buf;
};

rb_t *rb_open(unsigned int size, unsigned int count)
{
    rb_t *rb = calloc(1, sizeof(rb_t));
    rb->buf = calloc(count, size);
    if(rb->buf==NULL){
        free(rb);
        return NULL;
    }
    rb->size = size;
    rb->count = count;
    return rb;
}
void rb_reset(rb_t *rb)
{
    rb->wptr = 0;
    rb->rptr = 0;
}

void rb_close(rb_t *rb)
{
    if(rb){
        free(rb->buf);
        memset(rb,0, sizeof(rb_t));
        free(rb);
    }
}

bool rb_writable(rb_t *rb)
{
    unsigned int wptr = rb->wptr;
    wptr++;
    if(wptr>=rb->count) wptr = 0;
    if(wptr == rb->rptr) return false;
    return true;
}

bool rb_readable(rb_t *rb)
{
    if(rb->wptr==rb->rptr) return false;
    return true;
}

bool rb_write_ok(rb_t *rb, const void *data)
{
    if(rb_writable(rb)){
        memcpy(rb->buf+rb->size*rb->wptr,data, rb->size);
        rb->wptr++;
        if(rb->wptr == rb->count) rb->wptr = 0;
        return true;
    }
    return false;
}

bool rb_read_ok(rb_t *rb, void *buf)
{
    if(rb_readable(rb)){
        memcpy(buf, rb->buf+rb->size*rb->rptr, rb->size);
        rb->rptr++;
        if(rb->rptr == rb->count) rb->rptr = 0;
        return true;
    }
    return false;
}

2011年3月20日星期日

今天遇到的Ubuntu 10.04 Server 中的LANG设置问题

今天新申请了一个VPS,在其中安装了Ubuntu 10.04
第一次ssh登录上去,遇到这样的问题:
>adduser user

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:

看来是LANG设置有问题,查看一下:

root@www:~# locale
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.utf8
LC_CTYPE="en_US.utf8"
LC_NUMERIC="en_US.utf8"
LC_TIME="en_US.utf8"
LC_COLLATE="en_US.utf8"
LC_MONETARY="en_US.utf8"
LC_MESSAGES="en_US.utf8"
LC_PAPER="en_US.utf8"
LC_NAME="en_US.utf8"
LC_ADDRESS="en_US.utf8"
LC_TELEPHONE="en_US.utf8"
LC_MEASUREMENT="en_US.utf8"
LC_IDENTIFICATION="en_US.utf8"
LC_ALL=

网上google了一下,编辑/etc/default/locale

LANG="en_US.UTF-8"

再执行locale,情况相同。 装一个strace看看。(节选)

open("/usr/lib/locale/en_US.utf8/LC_CTYPE", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en_US/LC_CTYPE", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en.utf8/LC_CTYPE", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en/LC_CTYPE", O_RDONLY) = -1 ENOENT (No such file or directory)

看来en_US.utf8还没装呢。
网上google一下, 这个 http://romyli.javaeye.com/blog/853426 情况差不多,不过咱们这里情况更惨点,可以说什么locale都没装。

root@www:~# cd /usr/share/locales/
root@www:/usr/share/locales# ./install-language-pack en_US
Generating locales...
  en_US.UTF-8... done
Generation complete.
dpkg-trigger: dpkg-trigger must be called from a maintainer script (or with a --by-package option)


同样的方法,装上en_US
退出再重新ssh登录进去,看起来就正常了。
再找了一下,直接用 locale-gen 应该也可以。
root@www:~# locale-gen
Generating locales...
  en_US.UTF-8... up-to-date
Generation complete.

2011年3月6日星期日

互补的中西音乐(律)

题目写得挺大。

最近发现,一个新手用钢琴或者电子琴,能很容易的弹奏出中国古曲“沧海一声笑”。
如何弹奏呢,试着只按键盘上的黑键(就是那些半音的键)就好了。

钢琴或者电子琴是典型的西洋乐器,其音律应该是符合典型的西方乐律,是典型的七音乐器。
而“沧海一声笑”这样的古曲是典型的符合中国古典乐律的五音乐曲。

看起来中国五音和西洋的七音两者在十二律中可以说正好是一个互补关系。
生成这五音和七音的三分损益法和五度相生法也正好是兼容的。
某种意义上说,是否这也是中西方文化的关系呢?
也就是说 ,十二律是神来之物,算是一个音乐之总和吧。(十二也是一个特殊数字,比如一年十二个月)两种文化在此基础上各自选取了五音和七音,这两者正好能构成一种互补的关系。

当然,这只是说通常情况下,民间常见的形式。从“高技术”来说,中国古代也不是没有七音的乐律;西方应该也有其较为完整的乐律。

想到了,就记下了。欢迎指点探讨。