新手教程:驱动库的写法----从彗尾流水灯开始 ,高手绕行。。。
声明:这是新手教学贴,是专门为新手写的,高手们不要觉得幼稚。我是业余爱好,不是高手,高手也不会写。我只是说明了一种方法,而非真正的作品。http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_638854YC1NWX.JPG
(原文件名:100_0690.JPG) 暂停状态
视频效果 点击此处下载 ourdev_638856D4VB44.avi视频文件大小:694K) (原文件名:100_0688.avi)
这是一个彗尾流水灯(效果不太明显。哈哈哈。。。。。因为我的灯不够好,而且不够多,等多了的话效果会很好)。。。先给大家一个感性认识。
有很多刚刚入门单品机的新手想做一个比较复杂(500+的代码)的工程,昨天有人问我他的毕业设计要做一个32x64的点阵,说要实现上下滚动的效果,改怎么实现。
这问题我入门的时候也想过,如果竖排级联164的话,哈哈,不就轻而易举么。我现在知道了,软件功能和硬件不能混为一谈,竖排能硬件滚动,你横着滚一个给我看看。
然后他把程序给我一看,吓我一跳,乖乖,谁然程序不大,但是什么都写在一个文件里面。我问他会不会写驱动库,就是写成头文件的形式,他说不会。应为最近有好几个人问我程序怎么写,然后都不会写驱动库,所以我在这里统一说一下(里面也有显示缓冲区的用法哦。。)。
像上面的流水灯(可不是普通的流水灯哦(*^__^*) )。有人就想,这又得流水,又得又亮度分级,这怎么实现啊。
其实,硬件方面只要做成普通的流水灯就好了。就像这样---
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_638858UCGAIK.JPG
(原文件名:ISIS Professional - D--Proteus 7 Professional-SAMPLES-led 流水灯-led流水灯.JPG)
简单吧。。(*^__^*) 。。。。。
下面说说驱动库。。
个人理解:驱动库就是驱动硬件,然后供软件使用的库,就像API函数。
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_638859ZTAPAO.jpg
(原文件名:工程树.jpg)
这就是工程树,里面main就是主函数,led就是驱动库。
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_638863TN7167.jpg
(原文件名:头文件.jpg)
led头文件。里面声明了led驱动的各种函数,其中displaytemp就是显示缓冲区。
头文件写的时候加上
#ifndef LED_H
#define LED_H
////中间写你的声明////
#endif
这样头文件被调用的时候就不会被重复定义。我们在main函数中包含led.h,keil就会自动调用里面的函数。
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_638866YE9541.jpg
(原文件名:displayscan.jpg)
display扫描函数。在定时器t0中断中扫描。这个函数把displaytemp缓冲区的数扫描到8个led灯,而不管缓冲区里面的数值到底是什么。
void display(char lednum,unsigned griphy)
{
displaytemp=griphy;
}
这是display函数。在main函数中只要调用这个函数就能以特定的亮度点亮特定的led灯,它只操作显示缓冲区,而不管led灯到底是怎么连接的。
下面是猪函数中彗尾效果的写法。
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_638870NCVK8X.jpg
(原文件名:彗星.jpg)
注意一下,我们在main函数中用的全是c语言的通用语句,没有用到任何和硬件有关的语句,也就是说main函数在任何平台下都能编译通过,应为它里面只有软件算法,与硬件无关。
i=temp;
for(j=1;j<8;j++)
{
temp=temp;
}
temp=i;
简单的几句,就能实现移位,不需要硬件的左移右移。
这个缓冲区移位完全后,我就可以调用显示函数来进行显示
for(j=0;j<8;j++)
{
display(j,temp);
}
从main函数开始,都是用的c语言通用语句,我们不考虑硬件。恰恰相反,而在led驱动库里面,我们只考虑硬件,不考虑软件算法。
这就是驱动库的用法,这样的好处就是,我们可以把算法函数直接移植到其他平台,只要再重新写驱动库就可以了,写的时候不用考虑软件算法,只要能让一个灯亮,就能实现效果。
工程源码:点击此处下载 ourdev_638872AS3W92.rar(文件大小:14K) (原文件名:简单的驱动库.rar)
好了,就写了这里。小弟文笔不怎么好,大家凑活着看吧。。。。
如果下载后放在自己板子上的话,修改下ledport定义就好了。然后我用的是stc的1t单片机,如果放在普通51上的话延时用该缩小12倍。
led.h里面的#define on 100改成10就好了,分为1-10级亮度,0为灭。灯好的话可以多加几级亮度,里面的频率和脉宽要按照实际调试。 "从main函数开始,都是用的c语言通用语句,我们不考虑硬件。恰恰相反,而在led驱动库里面,我们只考虑硬件,不考虑软件算法。 "
excellent thought process. You are one of two individuals I have seen on this forum who have come to realize the importance of enhanced portability.
I take a similar approach as well.
Keep up the good work. 很少见millwood夸人……
为啥不是millwood,而多一个“0”呢,是马甲吗 回复【1楼】millwood0
-----------------------------------------------------------------------
の,英语,还算能顺下来。。。
多谢夸奖了。。 先标记,以后细看~~ 你用的好像是tkstudio编译器吧! "多谢夸奖了。。"
you have exceptional potential. just keep up the good work.
two words of advice for you:
1) the best embedded C code is the least embedded C code.
2) the money in coding is in breaking up a complex project into many workable modules, defining how those modules will interact with each other, so your project, once completed, can run reliably. if you are the best coder, you shouldn't code at all.
You are on the right track and you just need to keep pushing it. 回复【5楼】mcu_lan
-----------------------------------------------------------------------
TKS也是调用keil编译器。它只是一个编辑器,可以调用不同的编译器内核,无需再适应引得开发环境。 回复【6楼】millwood0
-----------------------------------------------------------------------
の。。。。又是英语。。。。俺六级没过。。。
您说的第二条我感觉已经做到了。
不过第一条的最小量代码还没做到。一直都是在代码量,速度,功能三者中间平衡。如果用高等的MCU,速度和代码量不用担心,只要封装足够强大的功能库就好了。 "您说的第二条我感觉已经做到了。"
I would give anyone 10 year or more to truly appreciate what those two points mean, :)
"不过第一条的最小量代码还没做到。一直都是在代码量,速度,功能三者中间平衡。如果用高等的MCU,速度和代码量不用担心,只要封装足够强大的功能库就好了。"
it has nothing to do with code size. the most difficult task in any embedded application is the hardware touch points: how you interact with the hardware, on different devices across vendor / family. the goal of any good embedded code is to minimize hardware interaction as much as possible and to limit your code's interaction with the hardware to a highly concentrated routines (aka API) in your words. for example, you should have one set of routines that deal with a given peripheral (spi for example). that set of routines should handle all of your spi-related work.
so if there is anything wrong with your spi communications, you go to that routine. Once you have that routine thoroughly debugged, you can use it next time without worrying about it not working.
and should you move the code to a different chip, all you need to do is to supply another spi routine specific to that target chip and your code will work right away.
if you look at a typical mcu, it has ports, timers, spi, i2c, usart, adc, etc. so you need to study them and define a common set of routines that operate those functionalities: for example, spi_write() will always write to an spi interface, whether it is software or hardware spi. spi_read() will always read from an spi interface. i2c_start() will always send an i2c start condition, no matter which chip you are using or if it is hardware or software i2c. etc.
you can then build chip specific "api" based on those routines: _24lc_write() will always write a byte to a given address on a _24lc device, whether it is using hardware / software protocols, or on PIC or AVR, etc. that piece of code is hardware independent and it looks as much "un-embedded" as it can be - aka it is 100% C and 100% portable.
once you have the chip specific "api" done, all your user-application code does is to initialize the device and write to / read from it, without caring about how the actual reading / writing is done. aka it is 100% C and 100% portable.
you have some really good intuition, intuition that 99% of the embedded programmers don't understand or appreciate in a life time. You just need a lot of practice, preferably real life practice to perfect your skills.
again, keep up the good work. mark MARK 学习 那个啥....
我也是新手,就进来学学。
谢谢楼主分享!! 想法很不错。。。。受教了 回复【9楼】millwood0
-----------------------------------------------------------------------
您还真较真呢。
1)我说的做到的是模块化程序,谁也不敢说能达到极致。
2)我说的高等mcu是通用型,即rom+ram+cpu,我是从pc转过来的,所以知道所有的操作都是通过总线来完成。
高等的mcu具有比较高的频率和流水线,这样可以以最少的时间完成计算,最后显示到人的效果是“不卡”。
在实际项目代码中也做过不少容错处理和加速。
3)我英语不好,可能理解您的语句上有偏差。
4)麻烦您以后发帖的时候适当的位置加回车,您一行太长了,得拉着滑调来回拖。
还是谢谢您的指导(*^__^*) 好思路,标记先。 我晕
millwood0的英文总算是吭哧吭哧的看完了
弱弱的问: millwood0是个外国朋友么? mark~好像不错的样子~ 做的不错~~~~~ mark mm remind 我是深圳这边做PCB的,各位老大们有没有用到这方面呢。13430918760孙浩 QQ:2223650885 思路好,先标记。 你的点阵是怎么驱动的? 跟着楼主学点灯,跟着millwood0学英文。 回复【24楼】eryueniao二月鸟
-----------------------------------------------------------------------
点阵。。。。???没有点阵啊。。。。灯是用脉宽调制驱动的。。。就是displayscan()。上面具体又讲到的。 用51做8通道的pwm是不是太费cpu资源了?上128级pwm刷新率可以到多少? 马克 回复【27楼】vvwvvw
-----------------------------------------------------------------------
用增强型51(25MIPS),刷128X64,100灰阶单色屏应该没有问题 楼主很厉害啊~~拜膜拜膜!!
我一直都很向着这个方向做的,把底层跟应用层分开。
但是当写的代码多了,还是会有点麻烦,的确会降低CPU的处理速度的。封装得完整了,占用的空间就大了,封装得不完整
比如我现在写的东西,C8051F410 因为懒就没写得太完整,串口,SMBus发送接收,都得用指针,有时候发送一个字节,就浪费了相当于好几十行汇编代码。
我觉得,应用和底层还是不应该把界线分得太清晰了,有些地方还是应该互相渗透一点的,简单的地方,我赞成直接在应用函数里面操控硬件。就算移植到其他地方,需要改动也不是很多。底层封装得太完整了,不可避免的就是接口会变得复杂,有时候反而变得不好理解了。也还不可避免地会有很多代码冗余。
我做SMBus的程序的时候,主,从,分别作了收发一共四种模式,700行代码,两片不同的单片机只用了其中不同的两种。
另一个问题是,这样做很大情况下,只能用“查询”的办法去“接收”信息,发送的话,应该还可以做到很好的实时性。接收的话,中断应该被归类到硬件驱动,按驱动与应用分开的思路,一般只能在中断里把值赋给变量,然后整个程序得等到下一个周期才能去获得值。我自己总觉得这样很不好。
这样做带来的延迟,肯定有,但是我觉得也不是想象中的那么大,可能我用的单片机速度比较快,感觉不到。
所以我觉得,应用和底层还是不应该把界线分得太清晰了
我只是个写了几行代码的小孩...说错什么了,还希望各位指出来,我们一起学习哈 回复【30楼】majianjia
-----------------------------------------------------------------------
单片机的uart通讯确实是个问题,发送太占用时间,而且中断的发送接收模式切换费cpu。
最近用了1t的51,把波特率跳到57600才稍微好一点。
感觉这方面要是有类似arm的FIFO就好了,可以省掉很多时间。 是调占空比 实现变化的吗? mark!!正要學習!! mark 好风格 mark mark http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3546104&bbs_page_no=9&bbs_id=1037
跟据这个内容做成了3216实物,154行,595列.仿真没问题,下载到板子上左移闪得厉害.大家有什么好方法吗?itssee@gmail.com
关于点阵的翻过去翻过来就这几贴,哪位大师来指点一二.. 回复【38楼】itssee
-----------------------------------------------------------------------
看了你的帖子。感觉没有问题。调整一下频率应该就没问题。我做过32x16双色的人眼看不出闪来。我用的是1t的stc12c5a单片机。
timer0进行扫描
void timer0(void) interrupt 1
{ displayscan();
TH0=(65536-9000)/256;
TL0=(65536-9000)/256;
timer0cnt++;
}
displayscan();是进行全部的扫描。每个灯的亮度用脉宽调节。
这样中间可以运行9000*12行其他程序(1t单片机的机器周期是计数器周期的1/12),也不会有闪烁。
如果只是普通的广告牌,计算程序不是很多,可以适当加快频率,这样脉宽调节也会更明显。 mark 看看 mark mark mark mark MARK mark 分层的思想吧,MARK MARK mark 提倡养成良好的编程习惯, 读着也明白, 还可以库代码重用. 顶! 那个啥....
我也是新手,就进来学学。
谢谢楼主分享!! Mark先 mark mark 彗星效果刚刚的。 仔细看完了,感觉不错 哎~?感觉不错哦,去试试~
页:
[1]