EDN首页   博客首页

最新日志

发表于:2008-9-17 9:38:32
标签:AVR  外部RAM  RAM  ATMEGA64  

3

AVR单片机外部RAM访问

AVR单片机外部RAM访问

作者:原野之狼 Email:hugui_bj@163.com 日期:2008-9-17

最近的一个项目中,体会了一下AVR单片机的外部RAM扩展,发文记录于此。

本文以ATMEGA64单片机为测试平台,外扩74HC573(资料上讲用AHC系列,但是我用HC系列在16M晶振下配置成最快速度访问并没有发现什么不妥,当然这是个不严谨的设计)以及62256芯片。

我采用的是GCC编译器,并且在项目中采用C++来编写程序,根据理论上来讲,只要外部存储器使能了并且配置完成,那么变量的地址分配完全可以交给编译器来做,但是前提是存储器使能及配置代码要在上电复位后最先得到执行,而采用C或者C++来编写程序,上电复位之后最先得到执行的代码是由编译器自动增加的启动代码以及构造函数代码。我不清楚如何让存储器使能及配置代码在上电复位后最先得到执行,所以就写了下面一段宏来控制外部存储器的访问。

代码如下:

//extern_ram.h

#ifndef _H_EXTERN_RAM_H_
#define _H_EXTERN_RAM_H_

#include "type.h"
#define DEF_EX_VAR(name) EX_VAR name;

#define INIT_EX_VAR(name,ele_size,ele_n) do{\
name.size_ele=ele_size;\
name.n_ele=ele_n;\
name.addr=p;\
p+=((ele_size)*(ele_n));\
}while(0)

#define WR_EX_VAR(name,pos,pdat) do {\
for(UINT8 iiiii="0";iiiii<name.size_ele;iiiii++)\
 (name.addr+(pos)*name.size_ele)[iiiii]=*((INT8 *)pdat+iiiii);\
} while(0)


#define RD_EX_VAR(name,pos,pdat) do {\
for(UINT8 iiiii="0";iiiii<name.size_ele;iiiii++)\
*((INT8 *)pdat+iiiii)=((INT8 *)(name.addr+(pos)*name.size_ele))[iiiii];\
} while(0);

#define GET_P_EX_VAR(name,pos) (name.addr+(pos)*name.size_ele)

typedef struct _tag_ex_var
{
 UINT8 size_ele;
 UINT16 n_ele;
 INT8 *addr;
} EX_VAR;

#endif

应用示例

//main.cpp

#include "includes.h"

INT8 *p=(INT8*)0x3100;//外部RAM的地址从0X3100开始

DEF_EX_VAR(test);//定义一个外部变量

INT16 main(void)

{

UINT8 i

INT16 tmp;

    INT16 *p_ele;

    MCUCR|=0X80;//使能XRAM,并配制成最快速度访问

    INIT_EX_VAR(test,sizeof(INT16),100);//外部变量初始化,该变量有100个元素,元素大小为sizeof(INT16)

    tmp="0xaaaa";

    for(i=0;i<100;i++)

    {   

        WR_EX_VAR(test,i,&tmp);//写示例,对变量test的所有元素都赋值为0Xaaaa

    }

    for(i=0;i<100;i++)

    {   

        RD_EX_VAR(test,i,&tmp);//读示例

    }

    p_ele=( INT16 *)GET_P_EX_VAR(test,32);//获取变量test的第32个元素的指针

    while(1);

    return 0x00;

}

后记:

1、头文件请自行修改。

2、若要用于其它编译器,请注意变量的内存布局,本文所述采用大端模式。

3、局部变量的命名请不要使用iiiii,否则在宏展开的时候会被覆盖。

4、文章比较粗糙,但是为了表示对作者劳动的尊重,转帖请注明出处并保留版权信息。

 

PDF下载请点击这里:

pdf

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(0) | 阅读(140)
发表于:2008-9-16 23:04:55
标签:LCD  240128  LOGO  

1

240X128LCD Driver,还热乎着~

240X128LCD Driver,还热乎着~

先看效果图:


再看部分源码:
头文件以及调试语句请自行修改。
//lcd.h
#ifndef _H_LCD_H_
#define _H_LCD_H_

#include "type.h"

#define CM240_128_DAT_ADDR 0X8000
#define CM240_128_CMD_ADDR 0Xc000

#define LCD_WR_DAT(dat) do{(*(UINT8*)CM240_128_DAT_ADDR)=dat;}while(0)
#define LCD_WR_CMD(cmd) do{(*(UINT8*)CM240_128_CMD_ADDR)=cmd;}while(0)
#define LCD_RD_DAT() (*(UINT8*)CM240_128_DAT_ADDR)
#define LCD_RD_CMD() (*(UINT8*)CM240_128_CMD_ADDR)

#define LCD_CMD_ADD_PT_SET 0X24
#define LCD_CMD_GRAPHIC_ADDR_SET 0X42
#define LCD_CMD_GRAPHIC_AREA_SET 0X43
#define LCD_CMD_MODE_SET_CGRAM 0X88
#define LCD_CMD_DISP_MODE_T_OFF_G_ON 0X98
#define LCD_CMD_DAT_WR_NO_ADP_ACTION 0XC4
#define LCD_CMD_DAT_RD_NO_ADP_ACTION 0XC5

#define LCD_CHECK_TIME_MAX_MS 100


UINT8 LcdWrDat(UINT8 dat);
UINT8 LcdWrCmdByZeroPara(UINT8 cmd);
UINT8 LcdWrCmdByOnePara(UINT8 cmd,UINT8 dat);
UINT8 LcdWrCmdByTwoPara(UINT8 cmd,UINT8 dat1,UINT8 dat2);
UINT8 LcdCheckCmdDatWrRd(void);


UINT8 LcdInit(void);
void LcdTest(void);

#endif

//lcd.cpp
#include "includes.h"

static const  prog_uchar wolf[]={//数据比较多,21IC限字数,见链接.
};
UINT8 LcdWrDat(UINT8 dat)
{
    if(LcdCheckCmdDatWrRd())
    {
        LCD_WR_DAT(dat);
        return 0xff;
    }
    else
        return 0x00;
}



UINT8 LcdWrCmdByZeroPara(UINT8 cmd)
{
    if(LcdCheckCmdDatWrRd())
    {
        LCD_WR_CMD(cmd);
        return 0xff;
    }
    else
        return 0x00;
}


UINT8 LcdWrCmdByOnePara(UINT8 cmd,UINT8 dat)
{
    if(LcdCheckCmdDatWrRd())
    {
        LCD_WR_DAT(dat);

        if(LcdCheckCmdDatWrRd())
        {
            LCD_WR_CMD(cmd);
            return 0xff;
        }
        else
        {
            return 0x00;
        }
    }
    else
    {
        return 0x00;
    }
}




UINT8 LcdWrCmdByTwoPara(UINT8 cmd,UINT8 dat1,UINT8 dat2)
{
    if(LcdCheckCmdDatWrRd())
    {
        LCD_WR_DAT(dat1);

        if(LcdCheckCmdDatWrRd())
        {
            LCD_WR_DAT(dat2);
            
            if(LcdCheckCmdDatWrRd())
            {
                LCD_WR_CMD(cmd);
                
                return 0xff;
            }
            else
            {
                return 0x00;
            }
        }
        else
        {
            return 0x00;
        }
    }
    else
    {
        return 0x00;
    }
}

UINT8 LcdCheckCmdDatWrRd(void)
{
    static UINT32 ct="0";

    while((LCD_RD_CMD()&0x03)!=0x03)
    {
        wdt_reset();
        ct++;
        if(ct>(UINT32)(LCD_CHECK_TIME_MAX_MS*1.0/(1000.0/SYSTEM_FREQUENCY_HZ)))
        {
            ct=0;
            return 0x00;
        }
    }

    ct=0;
    
    return 0xff;
}



UINT8 LcdInit(void)
{
    if(!LcdWrCmdByTwoPara(0x40, 0x00, 0x00))
        return 0x00;
    if(!LcdWrCmdByTwoPara(0x41, 0x1e, 0x00))
        return 0x00;
    if(!LcdWrCmdByTwoPara(LCD_CMD_GRAPHIC_ADDR_SET, 0x00, 0x00))
        return 0x00;
    if(!LcdWrCmdByTwoPara(LCD_CMD_GRAPHIC_AREA_SET, 0x1e, 0x00))
        return 0x00;
    if(!LcdWrCmdByZeroPara(0x80))// mode
        return 0x00;
    if(!LcdWrCmdByZeroPara(0xa4))
        return 0x00;
    if(!LcdWrCmdByZeroPara(0x98))
        return 0x00;
    return 0xff;
}

void LcdTest(void)
{
    UINT16 i;

    if(LcdInit())
    {
        WriteLog("lcd ok\r\n");
        for(i=0;i<30*128;i++)
        {
            wdt_reset();
            if(LcdWrCmdByTwoPara(LCD_CMD_ADD_PT_SET, i, (i)>>8))
            {
                if(!LcdWrCmdByOnePara(0xc4,pgm_read_byte(i+wolf)))
                {
                    WriteLog("E");
                }
                
            }
            else
            {
                WriteLog("e");
            }
            
        }
    }
    else
    {
        WriteLog("lcd error\r\n");
    }
}
点击下载源码

点击此处查看原文 >>

系统分类: 单片机   |    用户分类: 无分类    |    来源: 原创

评论(1) | 阅读(142)
发表于:2008-2-26 23:38:06
标签:无标签

5

一段面试经历

最近突然间有些想法,琢磨着换份工作,于是就发了些简历,并托些朋友帮忙留意一下工作机会。
其实目前这份工作干了有一年半了,收获也是很大的,但是总感觉遇到了发展的瓶颈。
这段时间也收到了一些意向,但是一直没有确定下来。因为目前公司的事情还是很多,一时半会儿也走不开,而且对于未来的职业规划我还是一片茫然。
昨天快下班的时候去跟老总请个假,打算今天上午去趟医院。请完假之后顺便聊了下工作上的事情,我把目前公司的一些问题讲了讲,也把我的一些想法讲了讲。
老总听说我有要走的意思,也没有很大的震惊,我们还是像以前一样的谈话,很平静的谈话,就像朋友一样,他说觉得我这个人还是可以信任的,能力也不错,认可我对公司的一些看法,希望我能转到管理的方向上来,帮他把公司目前的一些问题解决掉,并不断发展壮大。
我还是犹豫不决的,因为我对自己到底要干什么还没有清晰的看法,以及不想过早的放弃技术。目前的处境还真需要勇气来改变。也许一不小心,未来的道路就完全不一样。
其实在大学期间,基本上没有在电子技术上花什么业余时间,一直是在忙于一些社团活动,执着于一些理想主义色彩的东西。以至于在参加工作之前,从来没有去过中发电子市场,不知道如何焊接贴片的元器件,更不知道如何画电路板。凭着仅有的一次失败的全国大学生电子设计大赛的经历,毕业之后随着潮流踏进了电子行业,而且还干上了软件开发的道路,离老本行机械渐行渐远。
这次老总提出希望我转行到管理上来,做他助手,我还真是没有很大的思想准备,仍然对技术恋恋不舍。
谈话当中来了好多电话,都被我挂掉了,一般这种谈话下我是不接电话的。平时电话几乎没有,这个关键时候却来了这么多个电话。看来有些事情总是突然间发生的。
谈话完毕后,我查了下通话记录,然后一个个回拨,有一个座机号,被我挂断了两次,经核实,此电话乃华为公司约我去面试的电话,最近我一同学把我简历推荐到华为公司了。
接通电话,我连忙说对不起,刚才有事在忙,所以挂电话了,对方说话挺客气的,然后我问什么时候面试,对方说今天怎么样,我问大概需要多少时间,对方说一两个小时,我本来请假是说今天去医院做B超检查的,估算了一下时间,差不多,然后就答应了。
昨晚还打算复习一下C语言的内容,因为我知道华为软件方向的的笔试面试主要是测试C语言功底,时间已不早,再说也没什么心思去看书,在学校的时候已经厌倦考试一类的东西,看了一会儿书后就洗洗睡了。期间,噩梦不断,还梦见手机掉水里了。看来不是什么好兆头。
今天正常时间起床,然后没有吃早餐,因为还打算上午有空去医院作B超检测。出门,打黑车,到五道口,乘地铁,到西二旗,然后出站。人生地不熟,不知道往那个方向走,走到路边打车,许久没有空车。尔后,一貌似出租车的黑车的司机朝我吆喝,我说去港湾科技,然后他说上来,车上已有一姑娘。司机其实不知道港湾具体地址,先把那姑娘送到目的地,然后再载我去找港湾,车子倒车掉头时,司机一不小心把车撞到电线杆上了,车破了,司机充着电线杆直骂孙子。看来又是一个不好的兆头。
好不容易找到目的地,然后我付了十元钱车费,下车后给考官打电话,核实了一下地点有没有错,考官说没有错,但是我来得太早了,华为还没有上班,让我到大厅的沙发上坐着等。我进入港湾办公区,瞎逛起来,楼很高,地很广,人很多,脚步匆匆地,我独自晃荡着,极不合群。室外有些冷,再加上没吃早餐,竟有些晕乎,但还是不愿意去办公楼里坐着,那楼就像笼子,会让人压抑,外边空气不错。
主楼门口前有些许人站着,不晓得是什么人,胸前挂着牌,抽着烟,说笑着。旁边走过一群群的年轻的华为员工。没有任何问候与招呼,匆匆而过,只是烟圈晃了晃。
快到九点半的时候,我进去找了个地方静静的坐了下来,尔后一胸前挂牌的中年人士走了过来,顿了一下,掏出手机拨号,然后我的电话响了,我站立并举手示意,谈后我被此考官带着通过门禁,一路上我愣是跟不上考官的步伐,他走得太快了。
然后进入餐厅,找了张桌子坐下,简单说了说,然后让我做题。
餐厅很香,我很饿。时不时传来餐厅员工集体讲评的口号声:微笑服务。。。
题目不难,很基础的C语言的题目,但是我不会。很多平时我很少用到的东西,SIZEOF,内存布局,枚举,最要命的是还有关于运算优先级的题目,我平时可是凭括号了事啊。
题目快做玩的时候发现竟然少给了我一张试卷,看来运气真是不好。
这时考官带着另外一个面试的年轻人过来了,安排在我右手边做题,我向考官示意,表示我少了张试卷,考官看了看,说了几句话,我也没大明白是什么意思,继续做题,做完之后,考官还没有拿缺失的试卷过来,我于是冲右手边的年轻人借最后一张试卷,不知道他是没有听清楚我的意思还是怎么着,反正他说他也没有,我于是就歇着,等着,并填填应聘表格什么的,表格好几张纸。过了许久,旁边的哥们给我递了份试卷过来,问我是不是少这张试卷,我说是啊,此时那哥们已经答完题了,或许是从紧张的考试中缓过神来想起了我之前的请求。我接过试卷继续答题。过了两分钟,考官过来了,问我做完没有,我说没有,考官面露些许不悦,因为旁边的哥们已经答完题了,考官似乎已经忘记了我少了张试卷。然后考官把我俩的答题卷收去评分,我继续拿着刚刚从旁边那哥们送来的试卷看了看,有些难度。试卷评分完毕后,考官说让我不要再答题了,过去谈谈,原来是考官对我的答题表示很失望,因为错的太多。第一部分十道选择题,我错了一半。第二部分填空题,我有两空没填,幸好其它的填对了。第三部分程序巡查,我做了两个,做对了,而其它的没有试卷所以没做。谈话期间考官一直对我做错了一半的选择题表示失望,一再强调那些是很基础的东西。我倒还好,不怎么失望,相反,我对于这个笔试成绩表示满意。然后考官问了一些别的问题:51单片机有多少RAM?我回答道:传统51128字节,增强型的有512字节甚至好几K字节。96有多少RAM?我回答说:我不知道96是什么。用单片机做过那些方面的东西?我不知道考官到底要问什么,于是随便说了些,做过仪器仪表的开发,主要有键盘扫描,存储,显示,与PC通信等。考官再问:通信用什么协议。我回答道:232。考官再问:232协议是什么?我又不明白考官想问我什么,愣了一下,随便回答道,我一般用的是9600bps8bit无校验1停止,你说的是这个么?考官没吱声。然后看见我写了我熟悉FAT文件系统,于是就让我讲讲FAT文件系统。这下我更晕了,说实话,我只是看明白了这个东西,没有用它做过实际的东西,再说,过去已经好几个月了,FAT文件系统又是很繁杂的一个东西,没有资料,还真不知道该怎么讲起。于是继续忽悠了几句。考官依旧没怎么吱声。之后考官还是继续对我的C基础表示感冒。唉,可怜又可恨的选择题啊,竟然错了一半。后来又对我不是毕业于211学校表示感冒。然后让我回去等通知。整个过程不痛不痒的就结束了。
中午和同学在华为餐厅吃饭,我要了两个菜,两碗饭,一碗汤,恶狠狠的吃了一顿。
打车,转地铁,回公司。
等待中,一段特殊的起始于05年的华为情缘渐渐地淡了。。。

点击此处查看原文 >>

系统分类: 生活点滴   |    用户分类:    |    来源: 原创

评论(10) | 阅读(1825)
发表于:2008-1-3 23:47:50
标签:单片机  键盘扫描  状态机  

2

单片机键盘扫描之状态机实现

下载PDF版本:

pdf 

 

                     单片机键盘扫描之状态机实现

                   作者:原野之狼 日期 2008.1.3 23:37

 

 

一、概述

 

在编写单片机程序的过程中,键盘作为一种人机接口的实现方式,是很常用的。

而一般的实现方法大概有:

 

1、外接键盘扫描芯片(例如8279,7279等等),然后由该芯片来完成去抖、键值读取、中断请求等功能。然后单片机响应中断并读取键值,有的时候也可以采用轮训的方式。

 

2、如果按键数比较少,那么可以直接将按键接到单片机的IO口,然后各按键取逻辑或再送到单片机的中断管脚(对于51体系),单片机响应中断后再去读取IO口的数据。如果单片机的中断向量比较多(例如AVR系列的单片机,每个IO都可以作为中断),那么也可以直接把各个按键接到各个具有中断功能的IO上面。在中断处理程序中往往需要执行这样一个操作序列:延时一定时间来去抖,如果按键有效那么等待按键释放。

 

这两种方法都有比较明显的缺陷:

 

第一种方法需要专门的外围芯片,增加成本,且一般不容易检测按键的按下、释放以及长按键等一些事件。

 

第二种方法同样不容易检测按键的按下、释放以及长按键等一些事件。且采用软件延时的方式,浪费CPU资源,很不可取。

 

 

二、扫描式方法

 

鉴于以上两种方法的缺点,我们可以采用扫描式的方法来判断按键事件。

扫描方法即CPU在一定的节奏下去扫描按键数据线上的信号,然后分析并确定按键事件。扫描节奏一般为20MS

 

 

三、状态机

 

在软件工程中,有这样一个概念,即状态机。

状态机是一个抽象的概念,即把一个过程抽象为若干个状态之间的切换,这些状态之间存在着一定的联系。

 

举个例子:在操作系统中有一个关于进程调度的经典论述。

即把进程的调度过程抽象为运行、就绪、挂起以及睡眠等状态之间的切换。

各个状态之间在满足一定条件下才能进行切换。

 

在状态机程序的编写中有两点需要注意:

1、过程的抽象。

2、切换的条件以及如何切换。

 

 

四、按键过程的状态机分析

 

众所周知,一个键按下之后的波形是这样的(假定低有效):

在有键按下后,数据线上的信号出现一段时间的抖动,然后为低,然后当按键释放时,信号抖动一段时间后变高。当然,在数据线为低或者为高的过程中,都有可能出现一些很窄的干扰信号。

 

1、状态抽象

 

因此,我们对这个过程抽象为这么几个阶段:

 

1)、空闲状态,即数据线信号为高,这里假定为S1状态

2)、确认真的有键按下的状态,这里假定为S2状态。

3)、键按下的状态,这里假定为S3状态。

4)、确认真的有键释放的状态,这里假定为S4状态。

 

2、状态切换

 

S1状态,如果信号线为高,那么继续保持S1状态,如果信号线为低,那么切换到S2状态。

S2状态,如果信号线为高,那么切换到S1状态,如果信号线为低,那么切换到S3状态,此时表示有了键按下的消息事件,把此事件存入消息队列(如果系统不需要此消息,那么为了简单起见,此时可以不存入这个键按下事件)。

S3状态,如果信号线为高,那么切换到S4状态,如果信号线为低,那么保持S3状态,并对信号为低这一状态进行计数。

S4状态,如果信号线为高,那么切换到S1状态,此时表示有了键释放的消息事件,把此消息存入消息队列(如果系统不需要此消息,那么为了简单起见,此时可以不存入这个键按下事件),同时还需要对信号为低这一状态的计数进行判断,如果大于一定的阈值,那么表示之前是一个长按键消息事件,小于此阈值,则表示之前为一个短按键消息事件。如果信号线为低,则切换到S3状态。

 

五、程序实现

 

/************************************************************

    FileName: type.h

 

    Author:原野之狼

 

    Version :V1.0     

 

    Date: 2008.1.2

 

    Description:

 

    History:

 

           

 

 

************************************************************/

 

#ifndef _TYPE_H_

#define _TYPE_H_

 

//type define

#define UINT8 unsigned char

#define INT8 char

#define UINT16 unsigned int

#define INT16 signed int

#define UINT32 unsigned long

#define INT32 signed long

#define FLP32 float

 

#endif

 

/************************************************************

    FileName: includes.h

 

    Author:原野之狼

 

    Version :V1.0     

 

    Date: 2008.1.2

 

    Description:

 

    History:

 

           

 

 

************************************************************/

 

#ifndef _INCLUDES_H_

#define _INCLUDES_H_

 

//system header files

#include

#include

#include

#include

#include

#include

#include

 

//user header files

#include "type.h"

#include "queue.h"

#include "key.h"

 

 

#define SYSTEM_FREQUENCY_HZ 8000000

 

 

#endif

 

 

/************************************************************

    FileName: queue.h

 

    Author:原野之狼

 

    Version :V1.0     

 

    Date: 2008.1.2

 

    Description:

 

    History:

 

           

 

 

************************************************************/

 

 

#ifndef _QUEUE_H_

#define _QUEUE_H_

 

#include "type.h"

 

class CQueue

{

private:

    INT8 *pBuf;

    UINT16 BufLen;

    INT8 *pHead;

    INT8 *pTail;

    UINT16 count;

public:

    UINT8 IsQueueEmpty(void);  

    void QueueInit(INT8 *pBuffer,UINT16 len);

    UINT8 AddInQueue(INT8 dat);

    UINT8 RequestFrQueue(INT8 *pDat);

};

 

 

 

#endif

 

 

/************************************************************

    FileName: queue.cpp

 

    Author:原野之狼

 

    Version :V1.0     

 

    Date: 2008.1.2

 

    Description:

      

       management of queue

   

    History:

 

           

 

 

************************************************************/

 

 

#include "includes.h"

 

 

UINT8 CQueue::IsQueueEmpty(void)

{

    return 1;

}

 

 

void CQueue::QueueInit(INT8 *pBuffer,UINT16 len)

{

    pBuf=pBuffer;

    pHead=pBuf;

    pTail=pBuf;

    count=0;

    BufLen=len;

}

 

UINT8 CQueue::AddInQueue(INT8 dat)

{

    if(count

    {

       (*pTail++)=dat;

       count++;

 

       if(pTail==(pBuf+BufLen))

       {

           pTail=pBuf;

       }

       return 1;

    }

    else

    {

       return 0;

    }

}

UINT8 CQueue::RequestFrQueue(INT8 *pDat)

{

     if(count)

    {

       *pDat=(*pHead++);

       if(pHead==(pBuf+BufLen))

           pHead=pBuf;

       count--;

       return 1;

    }

    else

    {

       return 0;

    }

}

 

/************************************************************

    FileName: key.h

 

    Author:原野之狼

 

    Version :V1.0      </