《匠人手记》推荐网上购书渠道:
互动出版网(china-pub)购书入口   >>>
当当网(dangdang)购书入口   >>>
卓越亚马逊网 购书入口   >>>
淘宝网(taobao)购书入口   >>>
更多购书渠道……   >>> 

设为首页加入收藏联系匠人管理入口21IC首页21IC博客21IC社区侃单片机回复的贴参与的贴

天气预报
百宝日历
载入中...

百宝专栏

载入中...
最新货色

载入中...

粉丝评论

载入中...

载入中...



百宝信息

载入中...

百宝流量

(2006-07-01开始)


匠人手记

 匠人观点: 好记性不如烂笔头  
 黑色幽默:三鹿门——后世畅想

EMC单片机的宏定义学习手记(作者:etual)
程序匠人 发表于 2007-12-27 21:02:00  阅读全文 | 回复(0) | 引用通告 | 编辑

EMC单片机的宏定义学习手记(前辈们来拍拍砖)
etual 发表于 10/11/2007 10:25:54 PM 侃单片机 ←返回版面 按此给该网友发送邮件 按此察看该网友的资料 按此把文章加入收藏夹

前言:这几天在整理和生产EMC程序一些宏,发现这东西真的是好用的超乎了想象,将一些,以下以 EM78P260为主,其实其他型号大通小异,注意修改一下寄存器就可以。

(1)最常用的 PAGE 和 BANK
EMC 的IC是分几个page和几个bank的,低端的EM78P156等只有一个bank和一个page,所以不用切换,新一点的IC基本都要切换的了,这个经常用的冬冬,做成宏就最合适,代码如下:

/*****************************************************
*               BANK SELECTION                       *
*****************************************************/
BANK    macro  num
        if     num == 0
                bc   R4,6
                bc   R4,7
        elseif num == 1
                bs   R4,6
                bc   R4,7
        elseif num == 2
                bc   R4,6
                bs   R4,7
        elseif num == 3
                bs   R4,6
                bs   R4,7
        else
                message "warring!"
        endif
endm

/*****************************************************
*               PAGE SELECTION                       *
*****************************************************/
PAGE    macro    num
        if       num == 0

                 bc  psw,5
                 bc  psw,6
        elseif   num == 1
                 bs  psw,5
                 bc  psw,6
        elseif   num == 2
                 bc  psw,5
                 bs  psw,6
        elseif   num == 3
                 bs  psw,5
                 bs  psw,6
        else
                 message "warring!"
        endif
endm

调用格式是
BANK  num  (num是 0~3 代表4个BANK)
PAGE   num  (num是 0~3 代表4个PAGE)
这样方便多了,而且不会出错

(2)带参数的宏
作为例子,我们假定定义一个宏“ FUNC” ,带两个参数,功能是单纯的将传进来的数据传到PORT5 和 PORT6 而已,演示用法。
首先看定义:

FUNC   MACRO  ARG1,ARG2
    MOV A,@ARG1
    MOV PORT5,A
    MOV A,ARG2
    MOV PORT6,A
ENDM

注意到,为什么 ARG1前面有个 @ 的符号的呢?这个代表的是宏接收的第一个参数是一个立即数,而ARG2没有那个符号,代表宏接收的第二个参数是一个寄存器的地址。
好了,看在主程序怎么用:

FUNC     0X10, 0X20

这样就OK了,编辑器编译的时候,会自动进行宏替换,将0X10这个立即数作为第一个参数传递进去,而将0X20寄存器的内容,作为第二个参数传递进去,进行宏替换之后的结果,等效于:
    MOV A,@0x10
    MOV PORT5,A
    MOV A,0x20
    MOV PORT6,A

基本用法就是这样。不难,试一下就会用。

(3)说一点C语言的一种良好风格
C语言上面有一种比较好的编程风格,给个C51的例子:
我们想设置TIMER0在模式1,TIMER1在模式2
一般教程的思维和代码就是:
翻资料看看TMOD的位的定义,然后慢慢算,模式1和模式2该给什么值,最后写指令:
TMOD = 0x21;
完工…..
其实我们还可以有另外一种办法,那就是这样写:

TMOD = CT0_MODE1 | CT1_MODE2 ;

其中里面用到一些宏,具体定义是:
#define  CT0_MODE0 0x00                 //     Timer0/Counter0 Mode
#define  CT0_MODE1 0x01
#define  CT0_MODE2 0x02
#define  CT0_MODE3 0x03

#define  CT1_MODE0 0x00                 //     Timer1/Counter1 Mode
#define  CT1_MODE1 0x10
#define  CT1_MODE2 0x20
#define  CT1_MODE3 0x30

TMOD = CT0_MODE1 | CT1_MODE2 ;
这个应该很容易看的懂吧?中间的 “|”是或运算,这个就是编译器在编译之前先做的运算了,具体CT0_MODE1 代表 0X01 CT1_MODE2 代表0x20,然后“与运算”之后结果就是0X21 了,跟上面一样。但是老实说,大家愿意用哪个办法去做呢?我会毫不犹豫的选择第二种。

(4)用我们的EMC的汇编编译器模仿这种风格
我们的EMC汇编编译器同样支持这种编译时候的运算,让编译器帮我们先处理一些基本的运算,面对C编译器这个小功能真实见惯不怪,但是汇编编译器也能,小小的有点意外。

EMC的芯片的功能寄存器分配,真有点乱七八糟,唉,看着吐血,用定一种型号的IC那还好,如果用了几种IC的话,那个叫郁闷,一个例子就是EM78P447 和EM78P156,本来前者是升级版,但是为啥有些控制差别会那么大呢,每次都要疯狂的查DATASHEET,为了缓慢脑细胞的死亡速度,俺决定用宏……

例如: 我们需要开启EM78P260的TCCA计数器来用,初始化时候的工作,我们用带参数的宏来实现。分几步走

1 首先定义一个宏,以后可以用这个宏来初始化了
TCCA_SETUP     MACRO     TCCACNT
         clr       0x04             ; 0x04 是用来做临时寄存器用的
         ior       0x08             ; 0x08是控制TCCA的寄存器
         and       a,@0xf8          ; 屏蔽掉TCCA相关的
         mov       0x04,a           
         mov       a,@TCCACNT    ; 读取传递进来的参数
         or        a,0x04
         iow       0x08
         mov       a,@TCCACNT   ; 如果允许TCCA的话,开TCCA的中断
         and       a,@0x04         ; 否则直接跳出
         jbc       0x03,2
         jmp       $+4
         ior       0x0f
         or        a,@0x08
         iow       0x0f                        
ENDM
(因为这个程序在初始化阶段,所以改变0x04寄存器没有所谓,不过在正常跑的时候千万不要乱来,那个是会切换BANK的,跑飞了可不是说着玩,当然,这里可以在RAM开辟一个寄存器来用,那就没事了。喜欢的自己改)

2 第二部就是定义一些宏的具体数值了(跟C类似)
TCCA_ENABLE              ==                     0X04
TCCA_DISABLE             ==                     0X00
TCCA_SRC_INT             ==                     0X00
TCCA_SRC_EXT             ==                     0X02
TCCA_EDGE_RISE           ==                     0X00
TCCA_EDGE_FALL           ==                     0X01

3 第三步就是华丽的开始用了,在主程序里面,
               /*
               TCCA_SETUP setup MACRO
                    argument : TCCA_ENABLE    / TCCA_DISABLE     是否允许
                               TCCA_SRC_INT   / TCCA_SRC_EXT     计数源选择
                               TCCA_EDGE_RISE / TCCA_EDGE_FALL   出发弦选择
               */
               TCCA_SETUP      TCCA_DISABLE|TCCA_SRC_INT|TCCA_EDGE_RISE
看到了吧?
(TCCA_DISABLE|TCCA_SRC_INT|TCCA_EDGE_RISE)一堆有意义的参数,异或之后作为一个参数传递给宏 TCCA_SETUP ,修改的时候我们很简单就能搞定,甚至绝对不需要查资料,例如,我们想改成外部TCCA脉冲计数,只需要简单的修改

TCCA_SETUP      TCCA_DISABLE|TCCA_SRC_EXT|TCCA_EDGE_RISE

完工了,想禁止TCCA的话,改成 TCCA_DISABLE 就OK了,是不是很简单?很方便? 当然,方便的代价就是增加程序代码,不过就多那么10来行,没有哈大问题的,重要是不要过多的抹煞脑细胞~~hoho~ 可持续发展啊~~~

(5)寄存器自动分配
终于到了尾声,到了最BT的地方了,也是最有成就感的东西,怎么让寄存器自动分配空间,汇编跟C一个很大的区别就是,C的变量是自动分配,看着都眼红,那是多少好的东西啊,被汇编虐待了好些日子,突然发现,原来咱们EMC的汇编编译器也有这个功能,大喜!可能已经有前辈懂得怎么用了,那就算在下班门弄斧好,拍拍砖~~~
平时写程序的习惯就是,定义一个有意义,容易记的名字去代替抽象的寄存器名,例如定义一个临时变量用的寄存器

TEMP  EQU  0X10

这样,我们定义了TEMP,以后都用 TEMP 来代替 0X10 寄存器,这是最最常规的办法。但是,问题是,我们必须每次写程序之前都重新定义一次TEMP  EQU  0X10 ,当然,也不是说很烦,但是我们都有一些常用功能的子程序,子程序里面用到寄存器的话,也需要定义,然后做项目的时候,这里copy一个子程序,那里copy一个子程序,好了,一大堆冲突的寄存器定义,必须慢慢仔细的检查,如果不走运,有两个名字定义到同一个寄存器上面,好,惨了,很隐蔽的逻辑错误就来了,那是恶梦。

但是用宏可以做到自动分配
用到的是变量宏,WICE手册里面也有说,用法是
TEST   VAR   1
MOV  A,@TEST
TEST  VAR  TEST+1
MOV  A,@TEST

对比两次的A值,我们发现,第一个A值为1,第二个A值为2 !!这个就是变量宏的基本原理,编译器当它是一个变量,可以改变的,不过这个改变,只发生在编译的时候,生成代码之后就没有用的了。
好了,下面说说我们的核心,具体怎么分配。
首先定义个分配变量的宏,代码如下

ADDR_ASSIGN MACRO REGISTER
           REGISTER   EQU   ADDRESS           
           ADDRESS    VAR   ADDRESS+1
ENDM   

用了一个参数,传递进来的变量的名字。例如我们在主程序里面写了
ADDRESS   VAR   0X10   (首先定义开始分配的地址,我们是由 0X10 开始)
ADDR_ASSIGN     Temp0
Temp0 作为参数传递进来,实际上就是执行了   
Temp    EQU   0X10
ADDRESS = ADDRESS+1 (现在的ADDRESS已经是 0X11了!因为它是一个变量宏!)
下次如果我们继续定义
ADDR_ASSIGN     Temp1
现在 Temp1 已经自动被定义为 0X11 了,然后ADDRESS滚到0X12为下个寄存器定义用。
这样就方便了,例如我们定义一堆寄存器
ADDR_ASSIGN     Temp0
ADDR_ASSIGN     Temp1    
ADDR_ASSIGN     Temp2
ADDR_ASSIGN     Temp3

天啊,这实在是太好用了!!!我们完全不用关心具体分配到哪个寄存器上面,反正就是分配了,反正就是可以用了,哈~~TEST一下就知道。
牵涉的问题1
越界问题,当分配到 0X3F 的时候一个页面结束了,但是ADDRESS还是继续加上去,怕不怕?不怕,编译器已经报错了,不能编译,这样就不怕越界,可以放心的定义了
牵涉的问题2
多也bank的怎么分配?其实可以在定义宏的时候加多一个参数,通过条件宏来跳转定义就OK了,不过我怕麻烦,用了一下的办法:
/*---------------------------BANK 0 入口地址-------------------------------------*/
ADDRESS   VAR   0X10                             ; 可分配 0x10 ~ 0x3f
/*---------------------------   BANK 0   ----------------------------------------*/
这里就是我们需要定义的寄存器的
/*---------------------------BANK0 调试信息输出----------------------------------*/
MESSAGE         "Bank0最大分配RAM:"
ADDR_DISP       ADDRESS-1
/*-------------------------------------------------------------------------------*/

/*---------------------------BANK 1 入口地址-------------------------------------*/
ADDRESS   VAR   0X20                             ; 可分配 0x20 ~ 0x3f
/*---------------------------   BANK 1   ----------------------------------------*/
这里我门需要定义的bank 1 的寄存器
/*---------------------------BANK1 调试信息输出----------------------------------*/
MESSAGE          "Bank 1 最大分配RAM:"
ADDR_DISP        ADDRESS-1
/*-------------------------------------------------------------------------------*/

怎么样?和谐了吧? 将变量严格分开,你需要放在 bank0 的就填到 bank0 的区域,需要分到bank1 的就填到bank1那里,因为在bank1开头,重新定义了 ADDRESS 为 0X20 ,那样就可以继续从 0X20开始分配,如果有多个page的,按照同样的办法。
在每个bank结束的时候,我还放了两个宏,他们是
MESSAGE         "Bank0最大分配RAM:"
ADDR_DISP       ADDRESS-1
第一个,简单的显示文字而已,第二个 ADDR_DISP 是用来显示一共最大分配到哪个寄存器,这个宏的原型是:
ADDR_DISP      macro       reg
        IF     reg==0x10
        MESSAGE "0x10"
        ELSEIF reg==0x11
        MESSAGE "0x11"
        ELSEIF reg==0x12
        MESSAGE "0x12"
        ELSEIF reg==0x13
        ……
        ……
(下面的自己写了….)
ENDM

很简单,将ADDRESS最后的地址传进去,现实一下而已,因为ADDRESS执行多了一条自加指令的,所以我们减回,那就OK了。

尾声
宏,确实是好东西,宏,能将生活变得更加美好……

看《匠人手记》,与匠人同行!北航出版,正在热卖!

  • 标签:EMC 
  • 发表评论:
    载入中...

    芯片专题

    器件专题

    软件专题

    硬件专题

    综合专题

    项目专题

    原创专题

    器件检测
    LCD LED
    按键 触摸键
    E2PROM
    电池 电机
    电阻 电容 电感

    指令系统
    软件算法
    编程规范
    滤波算法
    串行通讯

    PCB设计
    I2C PWM
    红外遥控
    充电技术
    中断 ADC 

    匠人手记
    匠人夜话
    网络心路
    一周热点串烧
    从零开始玩PIC
    DIY旋转时钟

    广告5号位 [投放]


    学习板、开发板、编程器、下载器、仿真器(查看详情……)

    广告3号位 [投放]

    站内搜索


    站外搜索


    百度  google
    mp3  歌词 
    图片  FLASH 
    知道  文档
    新闻  词典 
    地图  mp3 
    软件  天网 
    雅虎  爱问 
    搜狗  讯雷 
    网讯  华军 
    天空 

    21IC器件搜索
    百宝箱分站
  • 《匠人的百宝箱》21IC站
  • 《匠人的百宝箱》21IC笔记团队
  • 《匠人手记》21IC书友会
  • 《匠人的百宝箱》MCUBLOG站
  • 《匠人的百宝箱》MCUBLOG笔记团队
  • 《匠人的百宝箱》EDN站
  • 《匠人手记》EDN书友会
  • 《匠人的百宝箱》与非网站
  • 《匠人的百宝箱》新浪站
  • 《匠人的百宝箱》百度站
  • 《匠人的百宝箱》网易126站
  • 《匠人的百宝箱》网易163站
  • 《匠人的百宝箱》互动出版网站
  • 广告4号位 [投放]

     
     

    匠人原创

    往日酷贴

     
     
     

    大千八卦

    友情连接

    新浪新闻:
    新浪财经:
    AK58新闻:
    新浪股票:
    新浪股票:
    证券之星:

     [更多酷站连接]

     

     

    [欢迎交换连接]

    [百宝箱之与非门分舵]

    [电脑圈圈的家当]

    [IC921的博客]

    [柔月阁]

    [八楼的呼吸]

    [hotpower 的水潭]

    [xwj的文君阁]

    [所长的BLOG]

    [阿摆手记]

    [电子伙伴]

    [unaided的笔记]

    [小飞的笔记]

    [单片机开发联盟]

    [网址之家]

    [好东西网址大全]

    [美萍中文精选]

    [数字电视之家]

    [SMARTCODE电子书斋]

    [软件开发之窗]

    [Armoric]

    [我爱研发网]

    [infernal的笔记]

    [雄鹰的空中加油站]

    [SunK]

    [逍遥电子]

    [ningpanda的博客]

    [C-Design]

    [一网见天下]

    [海边淘沙]

    [嵌入式365]

    [水牛的仓库]

    [股剩是怎样炼成的]

    [PIC论坛]

    [ICC AVR开发网]

    [中国高校自动化网]

     

     

     

    MCU博客-中国电子工程师博客网 

    大学生电子网 

     

     

     

     

     

    !!! 《匠人的百宝箱》 !!!