|
这个想法主要是为了一个目的:就是在51上已经开发了好多的有关IO口操作的驱动程序,要移植到
AVR之上,要求做最小的改动,同时兼顾效率问题就更好了。
我有一个好方法,假如我有一段51上的程序如下:
#include<reg52.h>
sbit Led = P1^0;
void Delay(int n)
{
while(--n>0);
}
int main()
{
while(1)
{
Led = 1;
Delay(1000);
Led = 0;
Delay(1000);
}
}
上面这段程序作了一件非常简单的事情,就是让一发光二极管不停的闪烁。
那么移植到AVR上我们希望作最小的改动:
#include <avr/io.h>
#include "tPortClass.h"
Tport<PB,0>Led(1); // 在avr中IO定义发生改变
void Delay(int n)
{
while(--n>0);
}
int main()
{
while(1)
{
Led = 1;
Delay(1000);
Led = 0;
Delay(1000);
}
}
上面的程序包含的头文件改变了,其中包含了一个神秘的头文件"tPortClass.h",我们
暂且不去管它,请看只有在IO定义的地方发生了改变,而对于使用这个IO口的地方,两
种不同的单片机对其操作方式可以做到一模一样.上面还只是一个简单的例子,想象一
项较大的项目中有好多IO操作,没有一个良好的IO移植方法,恐怕要大量的修改源文件
了.
其中对这个IO移植起到了好处的正是那个神秘的tPortClass.h头文件,那么让我们看看
这个头文件里都做了那些事情.
#ifndef _tPortClass
#define _tPortClass
#include <avr/io.h>
#include <stdint.h>
// IO寄存器地址定义区域
#if defined (__AVR_ATmega8__ )
#define PD 0x12
#define PC 0x15
#define PB 0x18
#elif defined (__AVR_ATmega16__)
#define PD 0x12
#define PC 0x15
#define PB 0x18
#define PA 0x1B
#elif defined (__AVR_ATmega32__)
#define PD 0x12
#define PC 0x15
#define PB 0x18
#define PA 0x1B
#else
#error"this device type PortPin not defined"
#endif
template<uint8_t portx,uint8_t pxx> // 模板参数:IO寄存器地址以及引脚偏移量
class Tport {
public:
Tport(uint8_t direction = 0)
{
if(direction > 0)
{
_SFR_IO8(portx-1) |= (1<<pxx); // 设置方向寄存器 ddrx=portx-1
}
else
{
_SFR_IO8(portx-1) &= ~(1<<pxx);
}
}
void in (void) { _SFR_IO8(portx-1) &= ~(1<<pxx); }
void out(void) { _SFR_IO8(portx-1) |= (1<<pxx); }
uint8_t operator =(uint8_t rt)
{
if(rt > 0)
{
_SFR_IO8(portx) |= (1<<pxx); // IO输出操作
return 1;
}
else
{
_SFR_IO8(portx) &= ~(1<<pxx);
return 0;
}
}
operator uint8_t() // IO输入操作 pinx=portx-2
{
return ( (_SFR_IO8(portx-2) & (1<<pxx)) ? 1 : 0 ) ;
}
private:
uint8_t config;
};
#endif
这里用到C++中的概念类模板,听到这个先不要拿砖头,想想上面的例子带给我们的好处先.
由于avr-gcc支持C++语言编译,于是就利用它做IO移植喽.整个文件看起来很抽象,不容易
看懂,我们先不要管它,就像51里面sbit定义的IO变量一样,我们不必去理会编译器是如何
实现的一样.
sbit Led = P1^0;
Tport<PB,0>Led(1);
好好看这两个表达式,它们有两分相象,PB相当于P1,Tport中的0相当于sbit中的0同样代表
某个IO口上的偏移量.只有Tport中Led后面有个(1),这个"1"到底是起什么作用的呢?AVR
和51到底有很大的区别。这个1是用来设置该IO口方向的,"1"表示输出,"0"表示输入。而
51不用设置IO方向。
写道这里,我想有人已迫不及待得去做实验了,把上面的定义粘贴到一新头文件中去。并且
在makefile中把"CC = avr-gcc"改写为"CC = avr-g++",即使用C++编译整个项目文件,即可。
有兴趣的可以看看该语句生成的汇编代码,会让你非常的吃惊。因为模板相当于一种高级宏
定义,不会产生冗余代码。
demo程序 miracleIO.rar