矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 10055|回复: 29

一个单片机程序性能优化的小例子

[复制链接]
     
发表于 2009-10-11 11:51:29 | 显示全部楼层 |阅读模式
好久没玩51了,昨天帮人看一个点阵屏的程序
屏是64*16的
用2片138做行选择(8*2=16),用8片4094做列输出(8*8=64)
原来程序的关键数据发送部分如下
  1. void xianshi()
  2. {
  3.     unsigned char out_data = 0xff;
  4.     unsigned char i,j;

  5.     for (j=0;j<16;j++) //循环显示8行
  6.     {
  7.         out_OE = 0; // 输出先关了
  8.         set_row(j); // 设置要显示的那一行
  9.         for (i=0;i<8;i++) // 开始输出数据
  10.         {
  11.             CLK   = 0;
  12.             data0 = (bit)( ( show_array[j*2] >>i)& 0x01 );//输出对应那行的数据
  13.             data1 = (bit)( ( show_array[j*2+1] >>i)& 0x01 );
  14.             data2 = (bit)( ( show_array1[j*2] >>i)& 0x01 );//输出对应那行的数据
  15.             data3 = (bit)( ( show_array1[j*2+1] >>i)& 0x01 );
  16.             data4 = (bit)( ( show_array2[j*2] >>i)& 0x01 );//输出对应那行的数据
  17.             data5 = (bit)( ( show_array2[j*2+1] >>i)& 0x01 );
  18.             data6 = (bit)( ( show_array3[j*2] >>i)& 0x01 );//输出对应那行的数据
  19.             data7 = (bit)( ( show_array3[j*2+1] >>i)& 0x01 );
  20.             CLK   = 1;// 时钟一次上升沿 数据被4094接收
  21.         }
  22.         out_OE = 1; // 可以打开输出了
  23.         delay();
  24.     }
  25. }
复制代码
开始测试屏的时候效果尚可
现在显示4个汉字发现屏闪得利害,分析原来为发送数据的时候过长,导致关显示的时候过长.
每次要对要发送的数据进行重新定位,而且下标还要用乘法再用加法才能得出
因为4094没有像595那样的二级锁存,发送数据期间必需要关显示
软件仿真一下,发现发送一次数据要4ms左右(晶体频率11.0592M)
因为有16行,16*4=64ms 按每秒刷30帧算,64*30=1920ms
超出1秒两倍多,于是出现:
延时太短,可以不闪,但亮度极低
延时加长,亮度上来了,但发现可以看出是一行一行显示的,不能用
     
 楼主| 发表于 2009-10-11 11:55:22 | 显示全部楼层
于是开始分析优化程序
首先
  1. show_array[j*2]
复制代码
这样的肯定不能用了,于是采用指针的方式,
于是,定义4个指向4个汉字的指针及8个当前行每一列的数据
  1. unsigned char *pd1,*pd2,*pd3,*pd4;
  2.     unsigned char sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8; // 当前行要显示的数据
复制代码
然后把这4个指针指向要显示的数据
  1.     pd1 = show_array;
  2.     pd2 = show_array1;
  3.     pd3 = show_array2;
  4.     pd4 = show_array3;
复制代码
然后再分别显示16行,为了缩短关显示的时间,我们在关显示前就要算出当前行要显示的数据,
并算出下一行要显示的数据
  1.     for (j=0;j<16;j++) //循环显示8行
  2.     {
  3.         sd1 = *pd1;
  4.         sd2 = *(pd1+1);
  5.         sd3 = *pd2;
  6.         sd4 = *(pd2+1);
  7.         sd5 = *pd3;
  8.         sd6 = *(pd3+1);
  9.         sd7 = *pd4;
  10.         sd8 = *(pd4+1);
  11.         pd1 += 2;
  12.         pd2 += 2;
  13.         pd3 += 2;
  14.         pd4 += 2;

  15.         out_OE = 0; // 输出先关了
  16.         set_row(j); // 设置要显示的那一行
  17. ..........................................
  18. }
复制代码
经仿真,在关显示前就算出本行要显示的数据,可以节省0.2ms

然后是单行输出部分进行改造,首先show_array[j*2+1]这样的不必再用了,直接使用sd1~sd8即可
然后也不再使用
  1. for(i=0;i<8;i++)
  2. {
  3.      (变量 >>i) & 0x01;
  4. }
复制代码
这样的判断方法,因为这样,CPU只能一位一位地移位,而且还要判断是否移够了
于是我们进行优化成
  1. for(i=0;i<8;i++)
  2. {
  3.     (sd1 & 0x01);
  4.     sd1 >>= 1;//自移一位并保存起来,不用每次都移N位了
  5. }
复制代码
整个程序优化得差不多了,仿真测试结果为,单行数据发送的时间为0.64ms
和原来4ms比较,6倍之多呀....
还有没有优化的地方呢?
  1. for (i=0;i<8;i++) // 开始输出数据
复制代码
可以优化成
  1. for (i=8;i>0;i--) // 开始输出数据
复制代码
原因是我用的C51 51有自减并判断非0的指令一条指令就搞定,
如果判断是否小于8,则要多条指令才能完成

那么
  1. for (j=0;j<16;j++) //循环显示16行
复制代码
可不可以也这样优化呢?
答案是可以,但实际测试结果是反而程序执行的时间太长了,
原因是下面我们用了到j这个变量
  1. set_row(j); // 设置要显示的那一行
复制代码
如果也优化成那样了,则要改成
  1. set_row(16-j); // 设置要显示的那一行
复制代码
多了一次运算,反而慢了....
于是这里就不能改了,

最终于测试结果: 单行发送数据耗时0.63ms (11.0592M晶体,12T普通51单片机)
0.63*16=10.08ms
10.08*30帧=302.4ms
302ms/1秒 = 0.3 也就是说刷30帧的时候,有30%的时候屏是关闭的,勉强能用了
所以,这屏要是用595来驱动的话,就没这个问题了,不过,算法亦然重要

附件为新旧源程序...欢迎你再进行更高级的优化

[ 本帖最后由 残剑饮血 于 2009-10-12 00:05 编辑 ]

评分

3

查看全部评分

回复 支持 反对

使用道具 举报

     
发表于 2009-10-11 12:27:37 | 显示全部楼层
原来程序的关键数据,看明白太累
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2009-10-11 12:28:44 | 显示全部楼层
原帖由 8031 于 2009-10-11 12:27 发表
原来程序的关键数据,看明白太累

重要的是了解其原理....
完整的程序反而没什么意义,换了环境就用不上了
回复 支持 反对

使用道具 举报

     
发表于 2009-10-11 12:58:49 | 显示全部楼层
记得读大学时,有两同学编程做练习题《八皇后问题》,一个同学的程序可以在286/640K的电脑上一次通过,另一同学的则提示内存不够,堆栈溢出。另一同学不服,认为学校电脑太次,到第三个同学家里电脑(386/2M)运行,也通过了。
回复 支持 反对

使用道具 举报

发表于 2009-10-11 14:22:41 | 显示全部楼层
八皇后和跑马灯有什么区别???
回复 支持 反对

使用道具 举报

     
发表于 2009-10-11 15:47:24 | 显示全部楼层

回复 6# 衡水家维 的帖子

八皇后问题是指:在一个国际象棋棋盘上,任意摆放八个皇后,使其互相不能攻击,求八个皇后的摆放位置。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2009-10-13 02:01:23 | 显示全部楼层
上次发的这个程序是没有在硬件上面仿真的
今天发到那位同学手中发现不能正常显示,查了好几个小时,才找到BUG..

  1.         sd8 = *(pd4+1);
  2.         pd1 += 2;
  3.         pd2 += 2;
  4.         pd3 += 2;
  5.         pd4 += 2;
复制代码
原因为,一个字是16*16的,单个字的一行占了两个字节,而我原来是写的pdN++;
结果当然是乱显示了啦...

哎,这点小问题,浪费了我几个小时,教训呀....
其实,这屏,8片4094的数据引脚是独立的,可以使用P0 = 数据,这样的方式去驱动,效率上面是最高了,而且也很方便做成滚动显示
不过这样得要求纵向取模,一般的字库都是横向的,需要有上位机来转化,这个有难度 ,那同学还做不到

sources.rar

1.81 KB, 下载次数: 968

回复 支持 反对

使用道具 举报

     
发表于 2009-10-13 08:20:45 | 显示全部楼层
还在用汇编,C现在只能大体看懂,写是不会了。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2009-10-13 11:18:18 | 显示全部楼层
原帖由 清风无痕 于 2009-10-13 08:20 发表
还在用汇编,C现在只能大体看懂,写是不会了。

呵呵算法优化倒不分汇编还是C,其它语言同样需要算法优化,重要的是思路
回复 支持 反对

使用道具 举报

     
发表于 2009-10-13 14:32:32 | 显示全部楼层
思路决定成败
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2009-10-13 15:15:46 | 显示全部楼层
其实种屏,最利害的送数据算法是:
当有外部SRAM时(早期用51驱动这类屏并实现滚动一般都用外部SRAM了),
使用SRAM的RD信号做为时钟信号,直接把数据不经过CPU内部发送出去
不过这属于"九阴白骨爪"类的功夫了,有一定的局限性,非一般人练得成,练成了也很难用上

此类手段在使用AVR同时驱动摄像头和TFT屏幕的时候也用得上
回复 支持 反对

使用道具 举报

     
发表于 2009-10-13 19:15:27 | 显示全部楼层
我不太会用C语音,学习的时候就学汇编,后来怎么别都感觉别过不过来了!
回复 支持 反对

使用道具 举报

     
发表于 2009-10-13 19:21:09 | 显示全部楼层
顶14楼
回复 支持 反对

使用道具 举报

发表于 2009-10-13 20:01:13 | 显示全部楼层
虽然我学的专业和软件编程没什么关系,但是我觉得一个优秀的算法应该在达到相同目的的前提下尽量减少对硬件的需求。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2025-4-30 07:34

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表