宁静以致远
zgf的blog
<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

留言簿(14)

随笔分类

随笔档案

文章档案

友情链接

资料收藏

搜索

最新评论

阅读排行榜

评论排行榜

 
VC知识库BLOG   首页  新随笔  联系  聚合  登录 
  随笔-28 文章-8 评论-84 Trackbacks-0
2008年7月14日

        本来不想用ttf字体的。因为变态的硬件工程师只接了4M Bytes的nor flash。除去系统和驱动,给应用程序剩下的空间只有2M。这年头2M的flash够干嘛呀!  但新的客户要求程序更花哨,更漂亮,添加了很多效果。其中不同大小的字就有几种。如果用点阵字库,一种字体也需要创建多种字库。而客户给的汉仪菱心体ttf字库才1.5M bytes。综合比较觉得使用ttf字体在存储空间上会划算一些。 参照http://www.minigui.org/cgi-bin/lb5000/topic.cgi?forum=6&topic=5548帖子的步骤终于添加了ttf的支持。步骤如下

1 . 安装freetype-1.3.1库
      手动编译成动态库后 编译minigui程序总是报错误error: no memory region specified for loadable section `.plt'  。 我只好编译成静态库libttf.a。将库拷贝到编译器的连接库目录。在编译器的inlcude目录下创建freetype1目录。并将头文件拷贝到该目录下。

2. 启用ttf
     可以按常规make menuconfig中启用。我直接修改config.h文件,找到#undef  _TTF_SUPPORT, 改为#define _TTF_SUPPORT 1 ,找到#undef  _UNICODE_SUPPORT, 改为#define _UNICODE_SUPPORT 1 。 然后make install编译minigui库

3. MiniGUI.cfg文件修改
     将arial.ttf和stxinwei.ttf 拷贝到设备的/uar/local/lib/minigui/res/fonts目录下。
    truetypefonts改为
[truetypefonts]
font_number=2
name0=ttf-arial-rrncnn-0-0-ISO8859-1
fontfile0=/usr/local/lib/minigui/res/font/arial.ttf
name1=ttf-stxinwei-rrncnn-0-0-GB2312
fontfile1=/usr/local/lib/minigui/res/font/stxinwei.ttf

4. 应用程序编写

    HWND hwnd;
    HDC hdc;
    HWND timeedit, spin;
    SIZE size;
    
    /* 创建编辑框使用的逻辑字体 */
    timefont = CreateLogFont ("ttf", "stxinwei", "GB2312",
    FONT_WEIGHT_REGULAR, FONT_SLANT_ROMAN, FONT_SETWIDTH_NORMAL,
    FONT_SPACING_CHARCELL, FONT_UNDERLINE_NONE, FONT_STRUCKOUT_NONE,
    30, 0);

    spin = CreateWindow (CTRL_SLEDIT,
    "nihao?",
    WS_VISIBLE,
    10241,
    0, 250, 400, 200, hWnd, 0);  

    SetWindowFont (spin, timefont); 


    注意CreateLogFont 创建的字体大小好像不能超过32。超过后显示汉字会异常。还没有查原因。英文则可以更大。另外不知是不是库的问题,我的应用程序在不使用ttf库前,9M的空闲内存可以跑好几次。而使用ttf库后,仅仅连接了ttf库,跑一次都经常内存不够。也不知道什么原因。




发表于 2008-07-14 21:24 zgf的blog 阅读(121) | 评论 (0)编辑 收藏
 

        同事在wince下写了个网络驱动,让我帮忙写个小程序测试网络流量。这还不简单。三下五除二一下子写了个测试程序出来。主体思想是建立连接后创建两个线程,一个专门收,一个专门发。然后定时统计收发的数据长度。主要代码如下:

//发送线程
void * __stdcall thread_send(void * lpparam)
{
 int slen;
 SOCKET m_sock = (SOCKET)lpparam;
 char * send_buf = (char *)malloc(1024);

 while(!bthreadstop)
 {
  slen = send(m_sock,send_buf,1024,0);
  if(slen == SOCKET_ERROR)
  {
   CString str;
   str.Format(_T("send error:%d"), WSAGetLastError());
   AfxMessageBox(str);
   break;
  }
  total_send += slen;
  Sleep(50);
 }

 free(send_buf);

 return 0;
}
//接收线程
void * __stdcall thread_recv(void * lpparam)
{
 int rlen;
 SOCKET m_sock = (SOCKET)lpparam;
 char * recv_buf = (char *)malloc(1024);

 while(!bthreadstop)
 {
  rlen = recv(m_sock,recv_buf,1024,0);
  if(rlen == SOCKET_ERROR)
  {
   CString str;
   str.Format(_T("recv error:%d"), WSAGetLastError());
   AfxMessageBox(str);
   break;
  }
  total_recv += rlen;
 }

 free(recv_buf);

 return 0;
}

void CTcptest_ceDlg::OnButtonConnect()
{
    ......        //连接代码省略
   
   //connect ok.
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread_recv,(void *)m_sock,0,0);
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread_send,(void *)m_sock,0,0);
}

     满怀信心的交给同事去测试。才一会儿问题就出现了。使用同样的代码,一个用evc编译后在wince模拟器上跑,另一个用vc编译后在PC上跑。
现象:
1.  终端发  PC收  持续一分钟没断线。
2.  终端收 PC发 持续一分钟没断线。
3. 终端和PC同时收发,几秒后终端发送和接收的数据都为零。

         改用两台PC互相通信。 同样的代码,同时收发速度达到11.9Mbyte/s。这个速度还算正常,也证明了我的代码至少表面上是OK的。

         添加打印后发现 PC 在开始的1秒内发送64K的数据后就停住了,后面持续很久发送数据量都为零。总的表现就是PC端猛的发一下数据,然后等待,然后又猛的发一下数据,又等待。如是循环。 根据http://www.techrss.cn/html/2007/05-20/18912.htm帖子说明似乎是PC发送太快了。 在PC的send线程中sleep一下,情况确实有所改善。等待10ms时,传输大约3M的数据后不能再发送。 等待50ms时,传输了大约50M的数据。

         这是个很奇怪的问题。为什么PC发得太快会导致wince终端网络不能收发呢?

           

        

发表于 2008-07-14 20:31 zgf的blog 阅读(205) | 评论 (6)编辑 收藏
2008年4月28日
          一般嵌入式产品量产时烧写bin文件包含了整个内核和文件系统,这样所有的产品的MAC地址就是一样的。批量自动更改MAC地址呢?自己做个烧写器是个办法,这样可以用程序自动修改烧写的bin文件,不过不可行。
发表于 2008-04-28 09:56 zgf的blog 阅读(895) | 评论 (7)编辑 收藏
2008年4月27日
            分析AEC程序时认为运算量重点在滤波和更新滤波器系数部分。这两个部分也最早将浮点运算转换成了定点运算。而判断远端是否讲话部分程序很简单,只有三个浮点乘法,就没有换成定点运算。在花了一番功夫将滤波部分用汇编实现后才发现优化的时间占总比重很小,而时间大部分花在三个浮点乘法上。
            将代码一部分一部分的注释掉后统计时间差异。结果:  (总共43.88秒的语音数据)
                              有三个浮点的代码          全定点的c代码                滤波部分用汇编实现的代码
时间(毫秒)                 8010                                     2220                                   1772
             
            处理器是bf536  400MHz 。也就是说定点c代码只用了20MHz的处理器资源,速度足够了。
            虽然走了弯路,但用汇编实现关键代码也是有益的,仅滤波部分就省了25%的时间。
            bf536有两个乘法器,理论上可以加快速度一倍。实际上由于数据对齐问题只有一半的数据能使用汇编。下面是滤波部分的代码
//c代码
static int filt16_c(short *coe, short * buf, int tap)
{
 int j;
 int tmp = 0;
 for(j=0;j<tap;j++)  
    tmp += buf[j]*coe[j];
 return tmp;
}
//汇编代码
.global _filt16_asm;
.type _filt16_asm,STT_FUNC;
_filt16_asm :
 I0 = R0;
 P1 = R1;
 A1 = A0 = 0;
 R2 >>= 1;
 P0 = R2;
LSETUP(loop1, loop1) LC0 = P0; 
 R1 = [ P1++ ] || R0 = [ I0++ ];
loop1: A0 += R0.L * R1.L , A1 += R0.H * R1.H(IS) || R1 = [ P1++ ] || R0 = [ I0++ ];
 R0 = A0 , R1 = A1;
 R0 = R0 + R1;
 RTS;
.size _filt16_asm, .-_filt16_asm

发表于 2008-04-27 17:39 zgf的blog 阅读(572) | 评论 (2)编辑 收藏
2008年4月25日

       minigui网站上有开源的eDillo浏览器下载。但需要1.6.8版本的minigui支持。而1.6版本的开源版很简单,不能实用。于是看能不能用1.33版的minigui库编译eDillo。
       编译时指定1.33库,发现只少了DrawTextEx2函数,参考1.6版的开源代码在1.33版中实现DrawTextEx2函数。很快编译通过。
       直接运行simple-edillo会显示程序中的默认页面。但导航到google主页会异常。不知道是什么原因。


参考DrawTextEx添加到minigui库中。代码可以跑,肯定有问题,但至少可以显示eDillo中的默认网页

int DrawTextEx2(HDC hdc, const char* pText, int nCount,
                RECT* pRect, int indent, UINT nFormat,DTFIRSTLINE *firstline)
{
    PDC pdc;
    int n, nLines = 0, width = 0;
    BOOL bOutput = TRUE;
    int x, y;
    RECT rcDraw, rcOutput;
    int nTabWidth, tabs;
    SIZE size;
    int line_height;

 firstline = NULL;

    pdc = dc_HDC2PDC(hdc);

    if (nCount == -1)
        nCount = strlen (pText);

    line_height = pdc->pLogFont->size + pdc->alExtra + pdc->blExtra;

    if (nFormat & DT_TABSTOP)
        nTabWidth = HIWORD (nFormat) *
                    (*pdc->pLogFont->sbc_devfont->font_ops->get_ave_width)
                    (pdc->pLogFont, pdc->pLogFont->sbc_devfont);

    else
        nTabWidth = pdc->tabstop *
                    (*pdc->pLogFont->sbc_devfont->font_ops->get_ave_width)
                    (pdc->pLogFont, pdc->pLogFont->sbc_devfont);

    // Transfer logical to device to screen here.
    rcDraw = *pRect;
    coor_LP2SP(pdc, &rcDraw.left, &rcDraw.top);
    coor_LP2SP(pdc, &rcDraw.right, &rcDraw.bottom);
    NormalizeRect (&rcDraw);

    if (dc_IsGeneralDC (pdc)) {
        LOCK (&pdc->pGCRInfo->lock);
        if (!dc_GenerateECRgn (pdc, FALSE))
            bOutput = FALSE;
    }

#ifdef _LITE_VERSION
    if (CHECK_DRAWING (pdc)) bOutput = FALSE;
#endif

    // Draw text here.
    if (nFormat & DT_CALCRECT || firstline)
        bOutput = FALSE;

    y = rcDraw.top;
    if (nFormat & DT_SINGLELINE) {
        if (nFormat & DT_BOTTOM)
            y = rcDraw.bottom - pdc->pLogFont->size;
        else if (nFormat & DT_VCENTER)
            y = rcDraw.top + ((RECTH (rcDraw) - pdc->pLogFont->size) >> 1);
    }

    while (nCount != 0) {
        int nOutput;
        int maxwidth;

        if (nLines == 0) {
            maxwidth = rcDraw.right - rcDraw.left - indent;
            if (maxwidth <= 0) {
                // new line
                y += pdc->pLogFont->size;
                nLines ++;
                continue;
            }
        }
        else
            maxwidth = rcDraw.right - rcDraw.left;
       
        gdi_start_new_line (pdc->pLogFont);
        tabs = txtGetOneLine (pdc, pText, nCount, nTabWidth, maxwidth, nFormat, &n);

        gdi_get_TextOut_extent (pdc, pdc->pLogFont, pText, n, &size);
        width = size.cx + tabs * nTabWidth;
        n += tabs;

        if ( (pText[n-1] == '\n' || pText[n-1] == '\r')
             && !(nFormat & DT_SINGLELINE) ) {
            int tmpx = 0, tmpy = 0;
            nOutput = n - 1;
            width -= gdi_width_one_char (pdc->pLogFont, pdc->pLogFont->sbc_devfont,
                            pText + n - 1, 1, &tmpx, &tmpy);
        }
        else
            nOutput = n;
           
        if (nFormat & DT_RIGHT)
            x = rcDraw.right - width;
        else if (nFormat & DT_CENTER)
            x = rcDraw.left + ((RECTW (rcDraw) - width) >> 1);
        else
            x = rcDraw.left;
        x += (nLines ? 0 : indent);

        if (firstline) {
            firstline->nr_chars = nOutput;
            firstline->startx = x;
            firstline->starty = y;
            firstline->width = width;
            firstline->height = line_height;
            break;
        }  

        rcOutput.left   = x;
        rcOutput.top    = y;
        rcOutput.right  = rcOutput.left + width;
        rcOutput.bottom = rcOutput.top + line_height;

        if (nFormat & DT_CALCRECT) {
            if (nLines == 0)
                *pRect = rcOutput;
            else
                GetBoundRect (pRect, pRect, &rcOutput);
        }

        // draw one line
        if (bOutput && width > 0) {
            if (nFormat & DT_NOCLIP)
                txtDrawOneLine (pdc, pText, nOutput, x, y,
                        &rcOutput, nFormat, nTabWidth);
            else {
                RECT rcClip;
                IntersectRect (&rcClip, &rcOutput, &rcDraw);
                txtDrawOneLine (pdc, pText, nOutput, x, y,
                        &rcClip, nFormat, nTabWidth);
            }
        }

        pText += n;

        // new line
        y += line_height;
        nLines ++;

        // left characters
        nCount = nCount - n;
    }

    /* we are done, so release global clipping region */
    UNLOCK_GCRINFO (pdc);

    if (firstline) {
        coor_SP2LP (pdc, &firstline->startx, &firstline->starty);
        return 0;
    }

    if (nFormat & DT_CALCRECT) {
        coor_SP2LP (pdc, &pRect->left, &pRect->top);
        coor_SP2LP (pdc, &pRect->right, &pRect->bottom);
    }

    if (!(nFormat & DT_CALCRECT)) {
        // update text out position
        x += width;
        y -= line_height;
        coor_SP2LP (pdc, &x, &y);
        pdc->CurTextPos.x = x;
        pdc->CurTextPos.y = y;
    }

    return line_height * nLines;
}

发表于 2008-04-25 11:18 zgf的blog 阅读(594) | 评论 (2)编辑 收藏
2008年4月23日
          AEC也就是声学回声消除。回声问题是免提通信避不过的槛。很久以前用34118将全双工的通话变成对讲机式的半双工通话,切断了声音的回传路径,也就消除了回声。随着技术的进步,全双工的语音通话成了标准配置。
          公司现在使用的fm1182芯片去回声。fm1182性能很好,白皮书上说它能衰减65db的回声。由于fm1182启动后只有串口能修改参数,其i2c方式必须reset后才能修改。而我们的平台上没有多于的串口,修改参数很麻烦。并且fm1182会处理输出的声音,导致播放音乐的音质严重走样。再者fm1182芯片很贵,竟然比主芯片价格贵好几倍。自己实现AEC是难免的了。
          辛辛苦苦2个月,终于实现了一个基本的AEC。采用NLMS算法,双滤波器结构。单端讲话时,ERLE平均达到了30db, 好的情况下在50-60db。双端讲话时语音不失真。本地用音箱大声的放音乐,远端讲话也能很好的传过来。 在回声路径激烈变化时误差不大,并且在500ms内恢复到最佳状态。使用c语言加O2编译,使用了大约80mips。虽然比不上fm1182,但足够应用了。ERLE至少可以再提高10db。因为也不知道是录音驱动还是硬件的问题,录音时每40ms有个脉冲噪音,并且幅度超过了大部分残余噪音的幅度。
        程序还有很多要继续改进的地方。没有做舒适噪音。需要汇编优化代码,速度至少要加快一倍。当对着mic吹气或吼叫,导致声音超出范围,不能消除回声,需要改进。
          
发表于 2008-04-23 22:24 zgf的blog 阅读(411) | 评论 (0)编辑 收藏
2007年5月31日
 

摘    要: 嵌入式操作系统是嵌入式系统应用的核心。本文通过对两种典型的开源嵌入式操作系统的对比,分析和总结了嵌入式操作系统应用中的若干问题,归纳了嵌入式操作系统的选型依据。

引言  
 
随着现代计算机技术的飞速发展和互联网技术的广泛应用,从PC时代过渡到了以个人数字助理、手持个人电脑和信息家电为代表的3C(计算机、通信、消费电子)一体的后PC时代。后PC时代里,嵌入式系统扮演了越来越重要的角色,被广泛应用于信息电器、移动计算机设备、网络设备和工控仿真等领域。嵌入式系统的开发也成为近年IT行业的技术热点。  
 
完成简单功能的嵌入式系统一般不需要操作系统,如以前许多M CS51系列单片机组成的小系统就只是利用软件实现简单的控制环路。但是随着所谓后PC时代的来临,嵌入式系统设计日趋复杂,嵌入式操作系统就必不可少了。  
 
一般而言,嵌入式操作系统不同于一般意义的计算机操作系统,它有占用空间小、执行效率高、方便进行个性化定制和软件要求固化存储等特点。  
 
从八十年代起,国际上就有一些IT组织、公司,开始进行商用嵌入式系统和专用操作系统的研发。这其中涌现了一些著名的嵌入式系统,如Microsoft公司的WinCE和WindRiverSystem公司的VxWorks就分别是非实时和实时嵌入式操作系统的代表。但是商用产品的造价都十分昂贵,用于一般用途会提高产品成本从而失去竞争力。  
 
uC/OS和uClinux操作系统是两种性能优良源码公开且被广泛应用的的免费嵌入式操作系统,可以作为研究实时操作系统和非实时操作系统的典范。本文通过对uC/OS和uClinux的对比,分析和总结了嵌入式操作系统应用中的若干重要问题,归纳了嵌入式系统开发中操作系统的选型依据。  
 
两种开源嵌入式操作系统介绍  
 
uC/OS和uClinux操作系统,是当前得到广泛应用的两种免费且公开源码的嵌入式操作系统。uC/OS适合小型控制系统,具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点,最小内核可编译至2k。uClinux则是继承标准Linux的优良特性,针对嵌入式处理器的特点设计的一种操作系统,具有内嵌网络协议、支持多种文件系统,开发者可利用标准Linux先验知识等优势。其编译后目标文件可控制在几百K量级。  
 
uC/OS是一种免费公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统。其内核提供任务调度与管理、时间管理、任务间同步与通信、内存管理和中断服务等功能。  
 
uClinux是一种优秀的嵌入式Linux版本。uClinux是Micro-Conrol-Linux的缩写。同标准Linux相比,它集成了标准Linux操作系统的稳定性、强大网络功能和出色的文件系统等主要优点。但是由于没有MMU(内存管理单元),其多任务的实现需要一定技巧。  
 
两种嵌入式操作系统主要性能比较  
 
嵌入式操作系统是嵌入式系统软硬件资源的控制中心,它以尽量合理的有效方法组织多个用户共享嵌入式系统的各种资源。其中用户指的是系统程序之上的所有软件。所谓合理有效的方法,指的就是操作系统如何协调并充分利用硬件资源来实现多任务。复杂的操作系统都支持文件系统,方便组织文件并易于对其规范化操作。  
 
嵌入式操作系统还有一个特点就是针对不同的平台,系统不是直接可用的,一般需要经过针对专门平台的移植操作系统才能正常工作。   
 
进程调度、文件系统支持和系统移植是在嵌入式操作系统实际应用中最常见的问题,下文就从这几个角度入手对uC/OS和uClinux进行分析比较。  
 
进程调度  
 
任务调度主要是协调任务对计算机系统内资源(如内存、I/O设备、CPU)的争夺使用。进程调度又称为CPU调度,其根本任务是按照某种原则为处于就绪状态的进程分配CPU。由于嵌入式系统中内存和I/O设备一般都和CPU同时归属于某进程,所以任务调度和进程调度概念相近,很多场合不加区分,下文中提到的任务其实就是进程的概念。  
 
进程调度可分为"剥夺型调度"和"非剥夺型调度"两种基本方式。所谓"非剥夺型调度"是指:一旦某个进程被调度执行,则该进程一直执行下去直至该进程结束,或由于某种原因自行放弃CPU进入等待状态,才将CPU重新分配给其他进程。所谓"剥夺型调度"是指:一旦就绪状态中出现优先权更高的进程,或者运行的进程已用满了规定的时间片时,便立即剥夺当前进程的运行(将其放回就绪状态),
把CPU分配给其他进程。  
 
作为实时操作系统,uC/OS是采用的可剥夺型实时多任务内核。可剥夺型的实时内核在任何时候都运行就绪了的最高优先级的任务。uC/OS中最多可以支持64个任务,分别对应优先级0~63,其中0为最高优先级。调度工作的内容可以分为两部分:最高优先级任务的寻找和任务切换。  
 
其最高优先级任务的寻找是通过建立就绪任务表来实现的。uC/OS中的每一个任务都有独立的堆栈空间,并有一个称为任务控制块TCB(Task Control Block)数据结构,其中第一个成员变量就是保存的任务堆栈指针。任务调度模块首先用变量OSTCBHighRdy记录当前最高级就绪任务的TCB地址,然后调用OS_TASK_SW()函数来进行任务切换。  
 
uClinux的进程调度沿用了Linux的传统,系统每隔一定时间挂起进程,同时系统产生快速和周期性的时钟计时中断,并通过调度函数(定时器处理函数)决定进程什么时候拥有它的时间片。然后进行相关进程切换,这是通过父进程调用fork函数生成子进程来实现的。  uClinux系统fork调用完成后,要么子进程代替父进程执行(此时父进程已经sleep),直到子进程调用exit退出;要么调用exec执行一个新的进程,这个时候产生可执行文件的加载,即使这个进程只是父进程的拷贝,这个过程也不可避免。当子进程执行exit或exec后,子进程使用wakeup把父进程唤醒,使父进程继续往下执行。  
 
uClinux由于没有MMU管理存储器,其对内存的访问是直接的,所有程序中访问的地址都是实际的物理地址。操作系统队内存空间没有保护,各个进程实际上共享一个运行空间。这就需要实现多进程时进行数据保护,也导致了用户程序使用的空间可能占用到系统内核空间,这些问题在编程时都需要多加注意,否则容易导致系统崩溃。  
 
由上述分析可以得知,uC/OS内核是针对实时系统的要求设计实现的,相对简单,可以满足较高的实时性要求。而uClinux则在结构上继承了标准Linux的多任务实现方式,仅针对嵌入式处理器特点进行改良。其要实现实时性效果则需要使系统在实时内核的控制下运行,RT-Linux就是可以实现这一个功能的一种实时内核。  
 
文件系统  
 
所谓文件系统是指负责存取和管理文件信息的机构,也可以说是负责文件的建立、撤销、组织、读写、修改、复制及对文件管理所需要的资源(如目录表、存储介质等)实施管理的软件部分。  
 
uC/OS是面向中小型嵌入式系统的,如果包含全部功能(信号量、消息邮箱、消息队列及相关函数),编译后的uC/OS内核仅有6~10KB,所以系统本身并没有对文件系统的支持。但是uC/OS具有良好的扩展性能,如果需要的话也可自行加入文件系统的内容。  
 
uClinux则是继承了Linux完善的文件系统性能。其采用的是romfs文件系统,这种文件系统相对于一般的ext2文件系统要求更少的空间。空间的节约来自于两个方面,首先内核支持romfs文件系统比支持ext2文件系统需要更少的代码,其次romfs文件系统相对简单,在建立文件系统超级块(superblock)需要更少的存储空间。Romfs文件系统不支持动态擦写保存,对于系统需要动态保存的数据采用虚拟ram盘的方法进行处理(ram盘将采用ext2文件系统)。  
 
uClinux还继承了Linux网络操作系统的优势,可以很方便的支持网络文件系统且内嵌TCP/IP协议,这为uClinux开发网络接入设备提供了便利。  
 
由两种操作系统对文件系统的支持可知,在复杂的需要较多文件处理的嵌入式系统中uClinux是一个不错的选择。而uC/OS则主要适合一些控制系统。  
 
操作系统的移植  
 
嵌入式操作系统移植的目的是指使操作系统能在某个微处理器或微控制器上运行。uC/OS和uClinux都是源码公开的操作系统,且其结构化设计便于把与处理器相关的部分分离出来,所以被移植到新的处理器上是可能的。  
 
以下对两种系统的移植分别予以说明。  
 
(1)uC/OS的移植  
 
要移植uC/OS,目标处理器必须满足以下要求;  
 
·处理器的C编译器能产生可重入代码,且用C语言就可以打开和关闭中断;  
 
·处理器支持中断,并能产生定时中断;  
 
·处理器支持足够的RAM(几K字节),作为多任务环境下的任务堆栈;  
 
·处理器有将堆栈指针和其他CPU寄存器读出和存储到堆栈或内存中的指令。  
 
在理解了处理器和C编译器的技术细节后,uC/OS的移植只需要修改与处理器相关的代码就可以了。具体有如下内容:  
 
·OS_CPU.H中需要设置一个常量来标识堆栈增长方向;  
 
·OS_CPU.H中需要声明几个用于开关中断和任务切换的宏;  
 
·OS_CPU.H中需要针对具体处理器的字长重新定义一系列数据类型;  
 
·OS_CPU_A.ASM需要改写4个汇编语言的函数;  
 
·OS_CPU_C.C需要用C语言编写6个简单函数;  
 
·修改主头文件INCLUDE.H,将上面的三个文件和其他自己的头文件加入。  
 
(2)uClinux的移植  
 
由于uClinux其实是Linux针对嵌入式系统的一种改良,其结构比较复杂,相对uC/OS,uClinux的移植也复杂得多。一般而言要移植uClinux,目标处理器除了应满足上述uC/OS应满足的条件外,还需要具有足够容量(几百K字节以上)外部ROM和RAM。  
 
uClinux的移植大致可以分为3个层次:  
 
·结构层次的移植,如果待移植处理器的结构不同于任何已经支持的处理器结构,则需要修改linux/arch目录下相关处理器结构的文件。虽然uClinux内核代码的大部分是独立于处理器和其体系结构的,但是其最低级的代码也是特定于各个系统的。这主要表现在它们的中断处理上下文、内存映射的维护、任务上下文和初始化过程都是独特的。这些例行程序位于linux/arch/目录下。由于Linux所支持体系结构的种类繁多,所以对一个新型的体系,其低级例程可以模仿与其相似的体系例程编写。  
 
·平台层次的移植,如果待移植处理器是某种uClinux已支持体系的分支处理器,则需要在相关体系结构目录下建立相应目录并编写相应代码。如MC68EZ328就是基于无MMU的m68k内核的。此时的移植需要创建linux/arch/m68knommu/platform/ MC68EZ328目录并在其下编写跟踪程序(实现用户程序到内核函数的接口等功能)、中断控制调度程序和向量初始化程序等。  
 
·板级移植,如果你所用处理器已被uClinux支持的话,就只需要板级移植了。板级移植需要在linux/arch/?platform/中建立一个相应板的目录,再在其中建立相应的启动代码crt0_rom.s或crt0_ram.s和链接描述文档rom.ld或ram.ld就可以了。板级移植还包括驱动程序的编写和环境变量设置等内容。  
 
结语  
 
通过对uC/OS和uClinux的比较,可以看出这两种操作系统在应用方面各有优劣。uC/OS占用空间少,执行效率高,实时性能优良,且针对新处理器的移植相对简单。uClinux则占用空间相对较大,实时性能一般,针对新处理器的移植相对复杂。但是,uCLinux具有对多种文件系统的支持能力、内嵌了TCP/IP协议,可以借鉴Linux丰富的资源,对一些复杂的应用,uClinux具有相当优势。例如CISCO公司的 2500/3000/4000 路由器就是基于uClinux操作系统开发的。  总之,操作系统的选择是由嵌入式系统的需求决定的。简单的说就是,小型控制系统可充分利用uC/OS小巧且实时性强的优势,如果开发PDA和互联网连接终端等较为复杂的系统则uClinux是不错的选择。

Linux联盟收集整理

发表于 2007-05-31 13:34 zgf的blog 阅读(842) | 评论 (0)编辑 收藏
2007年5月25日
    公司的晚上静悄悄的。一个人独自的敲着键盘。听着啪啪的键盘声,思绪到处飘荡,不知不觉的想到了她。不是说要忘记她的吗?我努力想摆脱,却毫无效果。突然间一阵更比一阵强烈的思恋涌了出来,头嗡嗡的。你还好吗?你最近过得怎样?。。。。
     慢慢的从思恋中缓过来,心里不由自主的感到悲伤,无比的悲伤,想好好的哭一下。
                                                           2007年5月25日  珠海
发表于 2007-05-25 19:50 zgf的blog 阅读(809) | 评论 (0)编辑 收藏
2007年3月17日

          语音通话时,如果采用免提方式,麦克风会录到本机喇叭发出的声音并传回给对方。在对方一端就会明显的听到回声。如果仔细调整麦克风和喇叭的位置,可以很大程度减少麦克风录到喇叭声音的大小。但这样对设备的位置限制太多。
          原本真正的解决办法应该是做滤波器将回声虑掉,考虑到几个方面:1。 原理不清楚,看代码也看得一头雾水。(真后悔逃了太多的高等数学课) 2。效果不知能否理想。麦克风和喇叭质量没保证,而且加了简单的放大电路,失真严重。导致一般的回声消除算法效果很可能并不理想。 3。 运算量不够,一般的回声消除算法运算量不低,原本不是很宽裕的系统可能承受不起。 总之一系列的原因让我现阶段采用MC34118的方法。
            MC34118用在普通模拟电话机内消除侧音的一块芯片,能够比较满意的禁止麦克风录到本机喇叭播放的声音。它比较放音和录音的电平,谁小就禁止谁。晕哦,好好的全双工通信变成了半双工的。不过通话时一般感觉不到,并且软件实现起来很简单。

            MC34118的工作原理看看datasheet就清楚了。它内部有点复杂,有4个电平比较器,两个背景噪音监视器,一个AGC,一个拨号音检测器,两个衰减器,一个控制衰减器的控制模块,还有一些放大。其实我们只需知道通过开关麦克风和喇叭的方法可以有效的避免回声就可以了。

            语音的电平也就是能量的大小可以计算采样的平方和得到。为了计算简单,采用计算采样的绝对值和得到。
static int check_val(const short * pdata,int len)
{
 int tmp = len;
 int all = 0;
 short val;
 while(tmp--)
 {
  val = *pdata++;
   if(val < 0)
      val  = -val;
   all += val
 }
 return all/len;
}

        由于信号的不稳定,可能播放和录音的计算结果虽然相差不大,但大小频繁交叉,从而频繁开关麦克风和喇叭。所以将计算出来的平方和划分到8个等级中。
  if(check < 15)
   r_level = 0;
  else
  if(check < 40)
   r_level = 1;
  else
  if(check < 90)
   r_level = 2;
  else
  if(check < 150)
   r_level = 3;
  else
  if(check < 240) 
   r_level = 4;
  else
  if(check < 350)
   r_level = 5;
  else
  if(check < 480)
   r_level = 6;
  else
   r_level = 7;

            另外,如果两台机的麦克风和喇叭的参数不一样,可修正这个等级来调整。

            然后比较录音和播放的等级,谁低就关闭谁。

            实践证明这样用非常少的计算量能很好的达到MC34118所做到的效果。

发表于 2007-03-17 17:03 zgf的blog 阅读(1688) | 评论 (1)编辑 收藏
2007年2月5日

一.概述:

MP3文件是由帧(frame)构成的,帧是MP3文件最小的组成单位。MP3的全称应为MPEG1 Layer-3音频文件,MPEG(Moving Picture Experts Group)在汉语中译为活动图像专家组,特指活动影音压缩标准,MPEG音频文件是MPEG1标准中的声音部分,也叫MPEG音频层,它根据压缩质量和编码复杂程度划分为三层,即Layer-1Layer2Layer3,且分别对应MP1MP2MP3这三种声音文件,并根据不同的用途,使用不同层次的编码。MPEG音频编码的层次越高,编码器越复杂,压缩率也越高,MP1MP2的压缩率分别为4161-81,而MP3的压缩率则高达101-121,也就是说,一分钟CD音质的音乐,未经压缩需要10MB的存储空间,而经过MP3压缩编码后只有1MB左右。不过MP3对音频信号采用的是有损压缩方式,为了降低声音失真度,MP3采取了感官编码技术,即编码时先对音频文件进行频谱分析,然后用过滤器滤掉噪音电平,接着通过量化的方式将剩下的每一位打散排列,最后形成具有较高压缩比的MP3文件,并使压缩后的文件在回放时能够达到比较接近原音源的声音效果。

 

二.整个MP3文件结构:

MP3文件大体分为三部分:TAG_V2(ID3V2)Frame,  TAG_V1(ID3V1)

ID3V2

包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量。

Frame

.

.

.

Frame

一系列的帧,个数由文件大小和帧长决定

每个FRAME的长度可能不固定,也可能固定,由位率bitrate决定

每个FRAME又分为帧头和数据实体两部分

帧头记录了mp3的位率,采样率,版本等信息,每个帧之间相互独立

ID3V1

包含了作者,作曲,专辑等信息,长度为128BYTE

 

 

三.MP3FRAME格式:

每个FRAME都有一个帧头FRAMEHEADER,长度是4BYTE32bit,帧头后面可能有两个字节的CRC校验,这两个字节的是否存在决定于FRAMEHEADER信息的第16bit,为0则帧头后面无校验,为1则有校验,校验值长度为2个字节,紧跟在FRAMEHEADER后面,接着就是帧的实体数据了,格式如下:

 

FRAMEHEADER

CRCfree

MAIN_DATA

4 BYTE

0 OR 2 BYTE

长度由帧头计算得出

 

1.帧头FRAMEHEADER格式如下:

AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM

13个帧头字符的含义如下:

Sign

Length
(bits)

Position
(bits)

Description

A

11

(31-21)

Frame sync (all bits set)

B

2

(20,19)

MPEG Audio version
00 - MPEG Version 2.5
01 - reserved
10 - MPEG Version 2
11 - MPEG Version 1

C

2

(18,17)

Layer description
00 - reserved
01 - Layer III
10 - Layer II
11 - Layer I

D

1

(16)

Protection bit
0 - Protected by CRC (16bit crc follows header)
1 - Not protected

E

4

(15,12)

Bitrate index

bits

V1,L1

V1,L2

V1,L3

V2,L1

V2,L2

V2,L3

0000

free

free

free

free

free

free

0001

32

32

32

32

32

8 (8)

0010

64

48

40

64

48

16 (16)

0011

96

56

48

96

56

24 (24)

0100

128

64