注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
liufan的个人空间 https://home.eeworld.com.cn/space-uid-53362.html [收藏] [复制] [分享] [RSS]
日志

关于C51的一个16位二进制转BCD码的快速算法的代码

已有 9247 次阅读2014-9-24 11:58 |个人分类:MCU| 二进制, BCD, 算法, 转换, C51

    最近在做一个项目,用的C8051F系列的MCU,因为系统中的功能很多,还有UCOSII,CPU的负担很重,这其中需要用到 16位二进制转BCD码 的功能,为了能加快运行速度,在网上找了一些代码,其中最为突出的是阿莫论坛的一个帖子,帖子地址如下:

Hex2BCD再讨论——由自写printf引出的另类Hex2BCD算法  javascript:;   
其中 cowboy 兄,在16楼贴出了一段代码:

号称世上最快的16bit二进制数转5位压缩BCD码的程序,最长56周期(标准51)。原理还没弄懂。
原作者:DENGM
;R2R3-->R5R6R7 小端
BIN2BCD:
       MOV  A, R3       
       ANL  A, #0FCH
       RR   A
       RR   A
       MOV  R5,A
       ADD  A, R5
       ADD  A, R5
       MOV  R7,A
       MOV  A, R2
       ANL  A, #3
       MOV  R6,A
       XRL  A, R3
       ANL  A, #3
       XRL  A, R2
       RR   A
       RR   A
       ADD  A, R7
       JNC  L2
       INC  R5
       ADD  A, #6
L2:    ADD  A, R7
       MOV  B, #25
       JNC  L3
       INC  R5
       ADD  A, #6
       DIV  AB
       SJMP L4
L3:    DIV  AB
       CJNE A, #10, L4
       INC  R5
       CLR  A
L4:    MOV  R7,A
       MOV  A, #10
       XCH  A, B
       ADD  A, #(L5-$-3)
       MOVC A, @A+PC
       ADD  A, R6
       DA   A
       XCH  A, R5
       DIV  AB
       XCH  A, R7
       SWAP A
       ORL  A, B
       SWAP A
       MOV  R6,A
       RET
     
L5:    DB 00H, 04H, 08H, 12H, 16H
       DB 20H, 24H, 28H, 32H, 36H
       DB 40H, 44H, 48H, 52H, 56H
       DB 60H, 64H, 68H, 72H, 76H
       DB 80H, 84H, 88H, 92H, 96H
       END

KEIL建了个工程,运行后发现,速度真的是很快,50几个周期,只是因为是汇编,调用有点麻烦,后来又看了一个关于此贴的
地址如下:
改造好方便keilC调用的最快16位2进制转压缩BCD 51库(关键字bin2BCD HEX int) 
这其中的 eduhf_123 兄,在5楼将此段代码做了LIB,方便C语言的调用,很方便,原文如下:

我来捡个便宜,将程序写成可重定位的段的形式,修改入口参数与出口参数格式,制作成.LIB形式的库文件,可在C语言源文件中直接以“传递参数,取得返回值”的形式调用。
说明:
1、函数形式为“unsigned long bin2bcd(unsigned int)”,不再使用全局变量传递参数;
2、使用时将.H文件与.LIB文件复制到工程目录,包含.H文件并将.LIB文件添加进工程即可。
3、16位无符号整型参数通过R6、R7传递(C51采用大端格式,即R6中存高字节、R7中存低字节);
4、转成的压缩BCD码以“unsigned long int”32位无符号长整型的形式通过R4、R5、R6、R7返回(其中:R4中为随机
  值;R5高4位为0,低4位存BCD码的万位;R6高4位存千位,低4位存百位;R7高4位存十位,低4位存个位);
5、最多将花费57个机器周期,比原来的代码多了一个机器周期是因为入口参数与出口参数中的寄存器R6、R7重叠,原
  来代码中的倒数第二个XCH指令无法再使用,需要用两个MOV指令替代;
6、如果需要出口参数中的R4为0,可将源代码文件中的分号去掉,重新编译工程以得到合适的.LIB文件,代价为多使用
  2个字节的代码空间及2个额外的机器周期;
7、最后,附上Keil的完整工程及可用的.LIB库文件及.H头文件。

.LIB库文件、.H头文件、KEIL的整个工程ourdev_509015.rar(文件大小:4K) (原文件名:bin2bcd.rar)



经过建工程测试,速度确实很快, 帖子往下看,看到 cowboy 兄贴出了C源码,根据上面的汇编写的,经过测试,速度稍微慢了一点,80多个指令周期,但是他输出的不是压缩的BCD码,所以很实用,不用再转换,原文如下:

追加C51版本,82周期,个别数据需86或90周期,比汇编慢一些,但仍比传统算法快N++倍。
程序也很简洁,比汇编容易看。

#include <reg51.h>
unsigned char dig[5];
  
bin2bcd(unsigned int x)
{
    unsigned char i,j,k;
    k = x;
    i = x>>10;
    if (i > 41)        i++;
    j = (i+i+i)*2 + (x>>8<<6 | k>>2);
    if (CY == 1) {i++; j += 6;}
    if (j > 249) {i++; j += 6;}

    dig[0] = i / 10;            //万位
    dig[1] = B;                 //千位
    dig[2] = j / 25;            //百位
    dig[3] = (B*4 | k%4) / 10;  //十位
    dig[4] = B;                 //个位
}

因为我个人的应用需要将BCD码进行前缀0去掉,这里函数返回的是数字的有效位数,ps指向存放BCD码的缓冲区,x是需要转换的16数,经过测试,调用函数,执行完大约花掉180几个指令周期,还算不错。这其中直接调用了51的内部寄存器,所以无法移植到其他平台,如下代码:

unsigned char bin2bcd2(unsigned int x, unsigned char *ps) 
{
    unsigned char i,j,k,len,flag;
    unsigned char *p = ps;
    
    k = x;
    i = x>>10;
    if (i > 41)        i++;
    j = (i+i+i)*2 + (x>>8<<6 | k>>2);
    if (CY == 1) {i++; j += 6;}
    if (j > 249) {i++; j += 6;}

    *p = i / 10;            //万位
    if(*p!=0) {p++; len++; flag = 1;}
    else if(*p!=0) { p++; len++; flag = 1; }
    
    *p = B;                 //千位
if(flag) { p++; len++; }
else if(*p!=0) { p++; len++; flag = 1; }
    
    *p = j / 25;            //百位
if(flag) { p++; len++; }
else if(*p!=0) { p++; len++; flag = 1; }
    
    *p = (B*4 | k%4) / 10;  //十位
if(flag) { p++; len++; }
else if(*p!=0) { p++; len++; flag = 1; }
    
    *p = B;                 //个位
    len++;
return len;
}



评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章