PROTEUS VSN自建元器件

电子专业 徐 自远 681℃ 0评论

一、Proteus VSM仿真模型简介
   
在使用Proteus仿真单片机系统的过程中,经常找不到所需的元件,这就需要自己编写。Proteus VSM的一个主要特色是使用基于DLL组件模型的可扩展性。这些模型分为两类:电气模型(Electrical Model)和绘图模型(Graphical Model)。电气模型实现元件的电气特性,按规定的时序接收数据和输出数据;绘图模型实现仿真时与用户的交互,例如LCD的显示。一个元件可以只实现电气模型,也可以都实现电气和绘图模型。
    Proteus
VSM模型提供了一些C++抽象类接口,用户创建元件时需要在DLL中实现相应的抽象类。VSM模型和Proteus系统通信的原理如下图:

[url=http://www.dzkf.cn/upimg/allimg/0612/1_05124036.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]
绘图模型接口抽象类:
ICOMPONENT――ISIS
内部一个活动组件对象,为VSM模型提供在原理图上绘图和用户交互的服务。
IACTIVEMODEL――
用户实现的VSM绘图模型要继承此类,并实现相应的绘图和键盘鼠标事件处理。
电气模型接口抽象类:
IINSTANCE――
一个PROSPICE仿真原始模型,为VSM模型提供访问属性、模拟节点和数据引脚的服务,还允许模型通过仿真日志发出警告和错误信息。
ISPICECKT
(模拟)――SPICE拥有的模拟元件,提供的服务:访问、创建和删除节点,在稀疏矩阵上分配空间,同时还允许模型在给定时刻强制仿真时刻点的发生和挂起仿真。
ISPICEMODEL
(模拟)――用户实现的VSM模拟元件要继承此类,并实现相应的载入数据,在完成的时间点处理数据等。
IDSIMCKT
(数字)――DSIM拥有的数字元件,提供的服务:访问数字系统的变量,创建回调函数和挂起仿真。
IDSIMMODEL
(数字)――用户实现的VSM数字元件要继承此类,并实现相应的引脚状态变化的判断和回调事件的处理。
IDSIMPIN
(数字)――数字组件的引脚,提供检测引脚状态和创建输出事务事件的服务。
IDBUSPIN
(数字)――数字组件的数据或地址总线,提供检测总线状态和创建总线输出事务事件的服务。
IMIXEDMODEL
(混合)――同时继承了ISPICEMODEL IDSIMMODEL,元件既有模拟特性,又有数字特性。
      
为了让Proteus访问用户模型中的成员函数,必须创建用户模型的一个实例。这不能通过类的接口来实现,只能通过从DLL中导出几个C函数来实现,在用户模型中必须实现这些C函数,达到构造和析构用户模型实例的效果。
1)构造和析构绘图模型实例:
IACTIVEMODEL *createactivemodel (CHAR *device, ILICENCESERVER *ils)
VOID deleteactivemodel (IACTIVEMODEL *model)
2)构造和析构模拟电气模型实例:
ISPICEMODEL *createspicemodel (CHAR *device, ILICENCESERVER *ils)
VOID deletespicemodel (ISPICEMODEL *model)
3)构造和析构数字电气模型实例:
IDSIMMODEL *createdsimmodel (CHAR *device, ILICENCESERVER *ils)
VOID deletedsimmodel (IDSIMMODEL *model)
4)构造和析构混合电气模型实例:
IMIXEDMODEL *createmixedmodel (CHAR *device, ILICENCESERVER *ils)
VOID deletemixedmodel (IDSIMMODEL *model)
二、Proteus VSM仿真模型开发流程
1
.绘制元件图形、引脚和相关符号。
2
.制作元件,设置元件属性。
3
.用C++编写元件,实现电气和绘图模型,编译生成DLL
4
.搭建电路仿真测试。
三、VSM模型开发实例
下面以TG19264A点阵式液晶显示元件的开发为实例详细讲解开发过程。

1.打开Proteus,选择菜单
查看>>Snap 10 th,选择左边绘图工具栏的2D graphics box,绘制如图所示的三个图形。

[url=http://www.dzkf.cn/upimg/allimg/0612/1_05133657_lit.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]

2.选择2D graphics line,给出两条直线,设置width36th,颜色为灰色。选择2D graphics circle,给四个角绘制安装孔。选择Markers for component origin,给三个图形分别绘图符号原点(图中红色部分)。

[url=http://www.dzkf.cn/upimg/allimg/0612/1_05133821_lit.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]
3
.选择Device pin,顺时针旋转90度,放置20个引脚,如图所示。GNDVCCV0VeeLED+的电气类型选择PP-Power PinD/IR/WECS1RETCS2CS3的电气类型选择IP-InputD0~D7的电气类型选择IO- Bidirectional

[url=http://www.dzkf.cn/upimg/allimg/0612/1_05133914_lit.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]

4.右键拖出选择框选择第一个符号,选择菜单库>>制作符号,命名为LCD19264A_C,确定。同理,第二和第三个分别命名为LCD19264A_1 LCD19264A_0。当用户调用drawsymbol (-1),将绘制LCD19264A_C,调用drawsymbol (1),将绘制LCD19264A_1,调用drawsymbol (0),将绘制LCD19264A_0

[url=http://www.dzkf.cn/upimg/allimg/0612/1_05134000_lit.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]

5.右键拖出选择框选择符号LCD19264A_C,选择菜单库>>制作元件,Device Properties设置如图,
[url=http://www.dzkf.cn/upimg/allimg/0612/1_05134213.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]
点击Next>。跳过封装设置,点击Next>。组件属性设置如图,
[url=http://www.dzkf.cn/upimg/allimg/0612/1_05134248.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]
[url=http://www.dzkf.cn/upimg/allimg/0612/1_05134322.JPG]0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}” border=”0″>[/url]
点击Next>。选择数据手册(可选),点击Next>。选择器件库,点击OK

6
.打开VC,新建工程,选择Win32 Dynamic-Link Library,给工程命名,建立空的DLL工程。从Proteus安装目录的INCLUDE文件夹中将VSM.HPP复制到当前工程目录,新建文件 LCD19264A.HLCD19264A.CPP,编写如下代码。

CODE:
/*****************************************************************
* 文件:LCD19264A.H
*
说明:不支持以下特性
* (1)
不支持显示开关控制
* (2)
不支持设置显示起始行
*****************************************************************/
i nclude “vsm.hpp”
//LCD
常量
#define LCD_BLK_NUM  3  //lcd block number
#define LCD_BLK_LEN  64  //lcd block length
#define LCD_LINE_NUM 8  //lcd line number
#define LCD_LENGTH  (LCD_BLK_LEN*LCD_BLK_NUM)  //lcd length
#define LCD_WIDTH  64  //lcd width
#define BLANK_WIDTH  50  //the width of blank
#define SYM_LINEWIDTH 28  //the width of symbol line
//LCD
命令掩码
#define CMD_MASK  0xc0
//LCD
命令
#define DISP_ONOFF  0x00 //
开关背光
#define SET_STARTLINE 0xc0 //
设置起始行
#define SET_XADDRESS 0x80 //
设置X地址
#define SET_YADDRESS 0x40 //
设置Y地址
//
延时常量
#define DELAY_1s  1000000000000
#define DELAY_1ms 1000000000
#define DELAY_1us 1000000
#define DELAY_1ns 1000
#define DELAY_1ps 1
/*
LCD
元件既有数字电气特性,也有绘图特性,所以要继承IACTIVEMODELIDSIMMODEL
*/
class LCD19264A : public IACTIVEMODEL,public IDSIMMODEL
{
public:
/*
电气模型成员函数 */
//
数字电路总是返回TRUE
INT isdigital (CHAR *pinname);
//
当创建模型实例时被调用,做初始化工作
VOID setup (IINSTANCE *inst, IDSIMCKT *dsim);
//
仿真运行模式控制,交互仿真中每帧开始时被调用
VOID runctrl (RUNMODES mode);
//
交互仿真时用户改变按键等的状态时被调用
VOID actuate (REALTIME time, ACTIVESTATE newstate);
//
交互仿真时每帧结束时被调用,通过传递ACTIVEDATA数据与绘图模型通信,从而调用animate()进行绘图
BOOL indicate (REALTIME time, ACTIVEDATA *data);
//
当引脚状态变化时被调用,主要用来处理数据输入和输出
VOID simulate (ABSTIME time, DSIMMODES mode);
//
可通过setcallback()设置在给定时间调用的回调函数
VOID callback (ABSTIME time, EVENTID eventid);
/*
绘图模型成员函数 */
//
当创建模型实例时被调用,做初始化工作
VOID initialize (ICOMPONENT *cpt);
//
PROSPICE调用,返回模拟电气模型
ISPICEMODEL *getspicemodel (CHAR *device);
//
PROSPICE调用,返回数字电气模型
IDSIMMODEL *getdsimmodel (CHAR *device);
//
当原理图需要重绘时被调用
VOID plot (ACTIVESTATE state);
//
当相应的电气模型产生活动事件时被调用,常用来更新图形
VOID animate (INT element, ACTIVEDATA *newstate);
//
用来处理键盘和鼠标事件
BOOL actuate (WORD key, INT x, INT y, DWORD flags);
private:
IINSTANCE *instance; //PROSPICE
仿真原始模型
IDSIMCKT *ckt;   //DSIM
的数字元件
ICOMPONENT *component; //ISIS
内部一个活动组件对象
//
引脚定义
IDSIMPIN *di; //D/I
IDSIMPIN *rw; //R/W
IDSIMPIN *en; //E
IDSIMPIN *cs1; //CS1
IDSIMPIN *cs2; //CS2
IDSIMPIN *cs3; //CS3
IDSIMPIN *d[8]; //D0~D7
IBUSPIN *databus; //D[0..7]
//LCD
参数
BYTE x_addr; //X
地址(见手册)
BYTE y_addr; //Y
地址(见手册)
BYTE status; //
状态(见手册)
BYTE cur_blk; //
当前块号(总共分3块,见手册)
BYTE DDRAM[LCD_BLK_NUM][LCD_BLK_LEN*LCD_WIDTH/8]; //LCD
显示RAM
BOOL new_flag; //
新数据到达标志
//
显示参数
BOX lcdarea; //LCD
显示区域
float pix_width, pix_height; //
每象素对应矩形的宽和高
};

CODE:
/*****************************************************************
* 文件:LCD19264A.CPP
*
说明:不支持以下特性
* (1)
不支持显示开关控制
* (2)
不支持设置显示起始行
*****************************************************************/
i nclude
i nclude “LCD19264A.h”
//—————————————————————————-
//
电气模型的实现
//
构造数字电气模型实例
extern “C” IDSIMMODEL __declspec(dllexport) * createdsimmodel (CHAR *device, ILICENCESERVER *ils)
{
//
授权认证
ils->authorize(0x88888888, 0x69); //
版本为6.9
return new LCD19264A; //
创建模型实例
}
//
析构数字电气模型实例
extern “C” VOID __declspec(dllexport) deletedsimmodel (IDSIMMODEL *model)
{
delete (LCD19264A *)model; //
删除模型实例
}
//
数字电路总是返回TRUE
INT LCD19264A::isdigital (CHAR *pinname)
{
return 1;
}
//
当创建模型实例时被调用,做初始化工作
VOID LCD19264A::setup (IINSTANCE *inst, IDSIMCKT *dsim)
{
instance = inst; //PROSPICE
仿真原始模型
ckt = dsim;  //DSIM
的数字元件
//
获取引脚
di = instance->getdsimpin(“D/I,d/i”, true);
di->setstate(FLT); //FLOAT
rw = instance->getdsimpin(“R/W,r/w”, true);
rw->setstate(FLT);
en = instance->getdsimpin(“E,e”, true);
en->setstate(FLT);
cs1 = instance->getdsimpin(“CS1,cs1”, true);
cs1->setstate(FLT);
cs2 = instance->getdsimpin(“CS2,cs2”, true);
cs2->setstate(FLT);
cs3 = instance->getdsimpin(“CS3,cs3”, true);
cs3->setstate(FLT);
d[0] = instance->getdsimpin(“D0,d0”, true);
d[0]->setstate(FLT);
d[1] = instance->getdsimpin(“D1,d1”, true);
d[1]->setstate(FLT);
d[2] = instance->getdsimpin(“D2,d2”, true);
d[2]->setstate(FLT);
d[3] = instance->getdsimpin(“D3,d3”, true);
d[3]->setstate(FLT);
d[4] = instance->getdsimpin(“D4,d4”, true);
d[4]->setstate(FLT);
d[5] = instance->getdsimpin(“D5,d5”, true);
d[5]->setstate(FLT);
d[6] = instance->getdsimpin(“D6,d6”, true);
d[6]->setstate(FLT);
d[7] = instance->getdsimpin(“D7,d7”, true);
d[7]->setstate(FLT);
//
为方便操作,将D0~D7映射为8位总线
databus = instance->getbuspin(“LCD_DBUS”, d, 8);
databus->settiming(100,100,100); //
设置时间延迟
databus->setstates(SHI,SLO,FLT); //
设置总线逻辑为[1,0,三态]时的驱动状态
//lcd model
x_addr = 0; //X
地址(见手册)
y_addr = 0; //Y
地址(见手册)
status = 0; //
状态(见手册)
new_flag = TRUE; //
新数据到达标志
}
//
仿真运行模式控制,交互仿真中每帧开始时被调用
VOID LCD19264A::runctrl (RUNMODES mode)
{
}
//
交互仿真时用户改变按键等的状态时被调用
VOID LCD19264A::actuate (REALTIME time, ACTIVESTATE newstate)
{
}
//
交互仿真时每帧结束时被调用,通过传递ACTIVEDATA数据与绘图模型通信,从而调用animate()进行绘图
BOOL LCD19264A::indicate (REALTIME time, ACTIVEDATA *data)
{
if(new_flag){ //
有新数据到达
  data->type = ADT_REAL; //call back animate() to refresh lcd
  data->realval = (float)time*DSIMTICK;
}
return TRUE;
}
//
当引脚状态变化时被调用,主要用来处理数据输入和输出
VOID LCD19264A::simulate (ABSTIME time, DSIMMODES mode)
{
BYTE data;
if(en->isnegedge()){  //E
的下降沿到达
  if((rw->istate()==SLO)||(rw->istate()==WLO)){ //R/W
为低表示写
   //
读块选择
   if((cs1->istate()==SLO)||(cs1->istate()==WLO))
    cur_blk = 0;
   else if((cs2->istate()==SLO)||(cs2->istate()==WLO))
    cur_blk = 1;
   else if((cs3->istate()==SLO)||(cs3->istate()==WLO))
    cur_blk = 2;
   else
    return; //not select block
   
   data = (BYTE)databus->getbusvalue(); //
读数据
   if((di->istate()==SHI)||(di->istate()==WHI)){ //D/I
为高表示数据
    DDRAM[cur_blk][x_addr*LCD_BLK_LEN+y_addr] = data; //
写入数据
    new_flag = TRUE; //
新数据到达标志
    y_addr = ((y_addr+1)%LCD_BLK_LEN);  //y
地址自动加1
    if(y_addr==0)
     x_addr = ((x_addr+1)%LCD_LINE_NUM); //
自动换行
   }else{  //D/I
为低表示命令
    switch(data&CMD_MASK)
    {
    case DISP_ONOFF: //
开关背光
     break;
    case SET_STARTLINE: //
设置起始行
     break;
    case SET_XADDRESS: //
设置X地址
     x_addr = (data&0x07); //bit2~bit0
     break;
    case SET_YADDRESS: //
设置Y地址
     y_addr = (data&0x3f); //bit5~bit0
     break;
    default:
     break;
    }
   }
  }else{  //E
的下降沿到达,R/W为高表示读结束
   databus->drivetristate(time); //
驱动总线为三态
  }
}else if(en->isposedge()  //E
的上升沿到达
   && ((rw->istate()==SHI)||(rw->istate()==WHI))){ //R/W
为高表示读
  if((di->istate()==SHI)||(di->istate()==WHI)){ //D/I
为高表示数据
   //
读块选择
   if((cs1->istate()==SLO)||(cs1->istate()==WLO))
    cur_blk = 0;
   else if((cs2->istate()==SLO)||(cs2->istate()==WLO))
    cur_blk = 1;
   else if((cs3->istate()==SLO)||(cs3->istate()==WLO))
    cur_blk = 2;
   else
    return; //not select block
   data = DDRAM[cur_blk][x_addr*LCD_BLK_LEN+y_addr];
   databus->drivebusvalue(time, data);  //
输出数据
   y_addr = ((y_addr+1)%LCD_BLK_LEN);  //y
地址自动加1
   if(y_addr==0)
    x_addr = ((x_addr+1)%LCD_LINE_NUM); //
自动换行
  }else{  //D/I
为低表示命令
   databus->drivebusvalue(time, status); //
输出状态
  }
}
}
//
可通过setcallback()设置在给定时间调用的回调函数
VOID LCD19264A::callback (ABSTIME time, EVENTID eventid)
{
}
//—————————————————————————-
//
绘图模型的实现
// Exported constructor for active component models.
extern “C” IACTIVEMODEL __declspec(dllexport) * createactivemodel (CHAR *device, ILICENCESERVER *ils)
{
ils->authorize (0x88888888,0x69); //6.9
return new LCD19264A;
}
// Exported destructor for active component models.
extern “C” VOID  __declspec(dllexport) deleteactivemodel (IACTIVEMODEL *model)
{
delete (LCD19264A *)model;
}
//
当创建模型实例时被调用,做初始化工作
VOID LCD19264A::initialize (ICOMPONENT *cpt)
{
//
获取ICOMPONENT接口和初始化
component = cpt;
component->setpenwidth(0);
component->setpencolour(BLACK);
component->setbrushcolour(BLACK);
//
获取显示区域
component->getsymbolarea(0,&lcdarea);
//
计算每象素对应矩形的宽和高
pix_width = (float)(lcdarea.x2-lcdarea.x1-BLANK_WIDTH*2-SYM_LINEWIDTH*2)/LCD_LENGTH;
pix_height = (float)(lcdarea.y2-lcdarea.y1-BLANK_WIDTH*2-SYM_LINEWIDTH*2)/LCD_WIDTH;
}
//
PROSPICE调用,返回模拟电气模型
ISPICEMODEL *LCD19264A::getspicemodel (CHAR *)
{
return NULL;
}
//
PROSPICE调用,返回数字电气模型
IDSIMMODEL  *LCD19264A::getdsimmodel (CHAR *)
{
return this;
}
//
当原理图需要重绘时被调用
VOID LCD19264A::plot (ACTIVESTATE state)
{
//
绘制LCD19264A_C元件基本图形
component->drawsymbol(-1);
//
刷新LCD数据显示
new_flag = TRUE;
animate (0, NULL);
}
//
当相应的电气模型产生活动事件时被调用,常用来更新图形
VOID LCD19264A::animate (INT element, ACTIVEDATA *data)
{
BOX pix;
BYTE dat,block,line,byte_off,bit_off;
if(new_flag){ //
当有新数据到达
  new_flag = FALSE;
  component->begincache (lcdarea); //
打开缓冲
  component->drawsymbol(1);  //
显示LCD19264_1符号
  //
显示各点数据
  for(block=0; block<LCD_BLK_NUM; block
   for(line=0; line<LCD_LINE_NUM; line
    for(byte_off=0; byte_off<LCD_BLK_LEN; off
     dat = DDRAM[block][line*LCD_BLK_LEN+byte_off]; //get byte data
     for(bit_off=0; bit_off<8; bit_off++){
      if(dat&(1<<BIT_OFF)){ bit=”1
       pix.x1 = (int)(BLANK_WIDTH+(block*LCD_BLK_LEN+byte_off)*pix_width+0.5);
       pix.y1 = -(int)(BLANK_WIDTH+(line*8+bit_off)*pix_height+0.5);
       pix.x2 = pix.x1 + (int)(pix_width+0.5);
       pix.y2 = pix.y1 – (int)(pix_height+0.5);
       component->drawbox(pix); //
绘制1个象素点
      }
     }
    }
   }
  }
  component->endcache(); //
结束缓冲,显示数据
}
}
//
用来处理键盘和鼠标事件
BOOL LCD19264A::actuate (WORD key, INT x, INT y, DWORD flags)  
{
return FALSE;
}
7
.搭建电路如下电路,新建Keil C工程,编写代码测试元件。如下图:

转载请注明:徐自远的乱七八糟小站 » PROTEUS VSN自建元器件

喜欢 (0)

您必须 登录 才能发表评论!

苏ICP备18041234号-1 bei_an 苏公网安备 32021402001397号