联系我们
基于嵌入式PLC芯片组PLC驱动程序开发
作者: 2009-11-14 10:59:28 浏览:168
信息来源:Internet
[打印]

  前言:

  嵌入式PLC系统软件内核是用汇编语言编写的,但内核的作者在开发内核时留给了驱动程序足够的资源和良好的程序接口,对于习惯用C的读者而言,使用KEIL C开发嵌入式PLC驱动程序是完全可行、比较轻松的。笔者以自己用C语言开发的40点嵌入式PLC驱动程序为例,介绍开发流程。

  一、关于40点嵌入式PLC

  1.40点嵌入式PLC的功能:

  (1)基于嵌入式PLC芯片组的40点PLC具有24路开关量输入和16路开关量输出,且每个输入和输出通道都有相应的LED指示。

  (2)具有CANBUS网络功能,由内核系统软件管理,使用工具软件CANSet构建CANBUS总线网络。

  (3)UART0:内核管理,用于梯形图编程、监控,支持人机界面及用户驱动程序下载。

  (4)UART1:内核管理,用于下载CANBUS网络参数、构建RS485网络及支持第三方设备互连。

  2.40点嵌入式PLC软件组成:

  (1)内核程序:是嵌入式PLC的软件核心部分,且每个出厂的芯片组已内嵌了该内核。

  (2)内核和驱动程序的接口程序:这部分汇编代码是实现内核和驱动程序的衔接。无论是用汇编还是用C来写驱动程序,都必须有该接口代码。

  (3)驱动程序:是实现嵌入式PLC外围硬件功能的程序,可以用汇编和C语言根据实际的嵌入式PLC外围硬件来编写,并利用Downhex.exe工具软件通过串口0下载到嵌入式PLC。

  (4)梯形图程序:是由用户在梯形图开发工具软件上开发,以实现用户系统的功能,并通过串口0下载到嵌入式PLC。

  (5)CANSET程序:是由用户在CANSET开发工具软件上开发,实现对CANBUS通信的相关设置,并通过串口1下载到嵌入式PLC。

  3.40点嵌入式PLC的驱动程序代码的框架:

  (1)头文件:关于单片机C8051FO4O寄存器的头文件,以及用户自定义的头文件。

  (2)初始化代码部分:包括用户端口初始化,用户上电初始化,用户设置初始化,用户运行初始化四个函数。用户根据实际需要来编写。

  (3)中断程序:内核将2.5ms定时中断程序留给一个接口给用户,用户能利用此接口完成如输入采样和LED动态扫描刷新等动作功能。

  (4)主程序:主程序是从内核定义的代码接口地址SCAN开始编写,主程序主要完成输入采样值的的滤波,LED显示赋值等逻辑运算功能。

  二.关于KEIL C编译器及uVision2开发环境

  KEIL C是最著名的高效率的51单片机的C语言开发软件,它在ANSI C 的基础上作了较大的扩展,其编译原理和ANSI C也有不同,非常适合51单片机这种资源很有限的芯片。

  Vision2是一个功能十分强大的Windows下的KEIL C开发环境,在使用它来开发C程序时,用户必须对KEIL C编译器有充分了解,且须熟悉uVision2开发环境。

  三. 用C语言开发40点嵌入式PLC驱动程序的流程。

  1.建立项目

  首先在uVision2开发环境下建立一个工程项目,项目名如"EASY",选择CPU型号C8051F040,且不要uVision2默认的Startup.a配置文件。这样就完成了一个空的项目就建立。

  2.为项目添加相关文件

  (1)将头文件c8051f040.h头文件复制到项目所在目录。

  (2)将汇编接口文件Vector.asm复制到项目所在目录,并添加到项目中。

  (3)在项目中新建一个c文件,名称为Easyplc.c。

  (4)在项目中新建一个用户头文件,名称为Easyplc.h。

  3.编辑Easyplc.c文件和Easyplc.h头文件。

  4.设置工程项目属性和相关的编译连接属性。

  5.编译连接工程项目,并产生EASY.HEX文件。

  6.利用Downhex.exe工具软件通过串口0将EASY.HEX下载到嵌入式PLC芯片组。

  四. 用C语言开发40点嵌入式PLC驱动程序的重点解析

  1.关于Vector.asm接口文件代码分析

  Vector.asm文件完整的代码如下:

  ;*******************************************************************

  ; FILE NAME: VECTOR.ASM

  ;*******************************************************************

  ;*******************************************************************

  EXTRN CODE(InitConfigc);

  EXTRN CODE(InitStartc);

  EXTRN CODE(InitSetc);

  EXTRN CODE(InitRunc);

  EXTRN CODE(Stepc);

  EXTRN CODE(Tmsc);

  EXTRN CODE(Scanc);

  ;******************************************************************

  ;*******************中断向量重定向地址********************* ORG 0E000H

  ORG 0E003H ;external interrupt0 vecter (INT0)

  ORG 0E013H ;external interrupt1(INT1)

  ORG 0E033H ;Serial Peripheral Interface(SPI)

  ORG 0E03BH ;SMBus interface

  ORG 0E043H ;ADC0 Window Comparator

  ORG 0E04BH ;Programmable Counter Array

  ORG 0E053H ;Comparator 0

#p##e#

  ORG 0E05BH ;Comparator 1

  ORG 0E063H ;Comparator 2

  ORG 0E07BH ;ADC0 end of Conversion

  ORG 0E083H ;Timer 4

  ORG 0E08BH ;ADC2 end of Conversion

  ORG 0E093H ;ADC2 Window Comparator

  ;*******************用户应用程序调用入口*******************

  ORG 0E0A0H ;用户端口初始化

  LJMP INIT_CONFIG

  ORG 0E0A3H ;用户上电初始化

  LJMP INIT_START

  ORG 0E0A6H ;用户设置初始化

  LJMP INIT_SET

  ORG 0E0A9H ;用户运行初始化

  LJMP INIT_RUN

  ORG 0E0ACH ;演算周期扫描

  LJMP SCAN

  ORG 0E0B0H ;指令周期扫描

  LJMP STEP

  ORG 0E0B3H ;2.5ms周期扫描

  LJMP TMS

  ORG 0E100H

  ;********************汇编程序调用C函数**********************

  INIT_CONFIG: LCALL INITCONFIGC

  RET

  INIT_START: LCALL INITSTARTC

  LCALL SCANC;

  RET

  INIT_RUN: LCALL INITRUNC

  RET

  INIT_SET: LCALL INITSETC

  RET

  STEP: LCALL STEPC

  RET

  TMS: LCALL TMSC

  RET

  SCAN: LCALL SCANC

  RET

  END

  ;********************************************************************

  由以上完整接口代码分析,内核共留给用户14个向量接口,但40点嵌入式PLC只使用了七个向量接口,每个接口就是调用一个C函数,共7个C函数。由于是汇编文件调用外部C函数,故要作外部函数声明。

  2.关于驱动程序所能使用的单片机的资源

  由于内核占用了大部分资源,驱动程序所使用的资源不能覆盖内核,因此在编写驱动程序时必须特别注意单片机资源的使用,以下列出若干注意事项,详细内容请参阅《EASY原理及应用》。

  (1)驱动程序代码地址范围必须定义在0XE000-0XF7FF共6KB之间,否则会覆盖内核程序!解决方法:在工程项目属性的Code Range编辑框中输入0xe000-0xf7ff"。

  (2)能使用的XDATA RAM地址范围为0X3600-0X3FFF,总共2560字节。解决方法:在工程项目属性的XDATA Range编辑框中输入"0x3600-0x3fff"。

  (3)能使用的DATA RAM地址范围为0X58-0X67,总共16字节,解决方法:在工程项目属性的Data base编辑框里输入"0x58";由于可使用的data ram数量十分有限,因此在定义变量时当data ram不够,应将变量定义为xdata存储器类型。为了提高程序运行实时性,使用频繁的变量应优先考虑安排为data 存储器型变量。

  (4)能使用的BDATA RAM地址范围为0X20-0X23,总共4字节,解决方法:在工程项目属性的Bit base编辑框里输入"0x00";

  (5)任何情况下,用户只能使用工作寄存器0组,不要修改非0组的寄存器的值。在中断函数中使用0组寄存器必须先保存个工作寄存器,并在函数结束前恢复。

  3.关于头文件Easyplc.h分析

  头文件仅列出如下和内核相关的变量来分析。

  unsigned char xdata Ram_px[4] _at_ 0x240;//内核定义的接口变量,由驱动程序传递给内核的嵌入式PLC输入端口映射值。

  unsigned char xdata Ram_py[2] _at_ 0x180;//内核定义的接口变量,由内核传递给驱动程序的嵌入式PLC输出端口映射值。

  unsigned char bdata Corebd _at_ 0x027;//内核定义的接口变量,由内核传递给驱动程序的嵌入式PLC正常运行的位标志,和PLC出现运行错误的位标志所在的bdata地址.

  sbit F_key_set=Corebd^7;//嵌入式PLC正常运行的位标志.

  sbit F_plcerr=Corebd^5; //嵌入式PLC出现运行错误的位标志.

  unsigned char data cpye_reg[8] _at_ 0x00;//定义这个绝对地址数组,其作用仅仅是在中断函数中保存0组工作寄存器。

  4.关于Easyplc.c代码分析

  (1)初始化函数:共有4个初始化函数,分别为用户端口初始化函数void InitStartc(void);用户上电初始化函数void Init_Startc(void);

  用户设置初始化函数 void InitSetc(void);用户运行初始化函数void InitRunc(void)。这四个函数功能都是初始化用户定义的变量和配置c8051f040单片机的特殊寄存器。

  (2)2.5ms周期扫描函数void Tmsc(void)代码分析:该函数代码如下

  void Tmsc() using 0

  {

  unsigned char xdata len[8];

  len[0]=cpye_reg[0];// code 1

#p##e#

  len[1]=cpye_reg[1];

  len[2]=cpye_reg[2];

  len[3]=cpye_reg[3];

  len[4]=cpye_reg[4];

  len[5]=cpye_reg[5];

  len[6]=cpye_reg[6];

  len[7]=cpye_reg[7];// code 2

  UserDisplay(Xlamp);// code 3

  if(Input_cnt-- >0)

  {

  UserSampl(Input_cnt,Xinput); //code 4

  P1MDOUT=0xff;

  }

  cpye_reg[0]=len[0];// code 5

  cpye_reg[1]=len[1];

  cpye_reg[2]=len[2];

  cpye_reg[3]=len[3];

  cpye_reg[4]=len[4];

  cpye_reg[5]=len[5];

  cpye_reg[6]=len[6];

  cpye_reg[7]=len[7];//code 6

  }

  由于该函数是被内核的定时中断函数所调用,属于中断程序的一部份,因此定义该函数时用关键字"using 0"来确定使用0组工作寄存器。

  code 1-code 2:是将0组工作寄存器保存起来。

  code 3:调用此函数实现LED动态扫描刷新。

  code 4:调用此函数实现输入信号采样,每路输入信号连续采样8次。

  code 5-code6:将0组工组寄存器恢复。

  以上代码除了code 3和code4是由用户来根据实际硬件来编写外,其余部分可作为固定模式。需要特别注意:该函数代码运行总时间不要超过40us,以免影响内核的运行。另外,由于Tmsc()是运行于中断期间(内核定时中断所调用),所以Tmsc()以及它所调用的函数的局部变量数据段不能和其它函数局部变量数据段相覆盖,因此需要在项目编译连接属性的overlay列表框中输入:"*!Tmsc,"。

  (3)演算周期扫描函数void Stepc(void)分析:

  该函数是内核的主循环所调用的代码如下

  void scanc()

  {

  if(Input_cnt <= 0)

  {

  UserScanI(Ram_px); //code 1

  Input_cnt=8;

  }

  UserScanOut(Xoutput); //code 2

  UserScanLamp(); //code 3

#p##e#

 

  UserScanSignal(); //code 4

  }

  code 1:调用该函数实现对输入采样值滤波后赋给内核接口变量Ram_px。

  code 2:调用该函数实现对输出值刷新。

  code 3:该函数实现对输入输出LED刷新。

  code 4:该函数实现对"RUN"LED和"ERR"LED刷新。

  完成程序编辑,并设置好项目编译连接属性后,便可编译连接工程项目,然后可打看Easy.M51列表文件,查看程序所有的占用的数据存储器以及程序存储器和工作寄存器是否在允许使用范围之内。如有超出范围,应对程序作适当调整,直到完全符合后,生成的Easy.hex文件。利用Downhex.exe工具软件,通过串口0下载到40点嵌入式PLC中。

  五.其他说明

  笔者写的40点嵌入式PLC驱动程序没有开辟其他的中断,如果用户需要自己使用中断,如使用T4中断,须用关键字"interrupt 16"来定义T4中断函数。并且使用关键字"using 0"来确定使用0工作寄存器组。在项目属性的interrupt vector编辑框里将中断矢量由0改为0XEOOO,使中断矢量重定位为0XE000。并且同样需要将0工作寄存器保存。如以下是T4中断函数代码范例:

  void int_t4c() interrupt 16 using 0

  {

  unsigned char xdata copy[8];

  unsigned char adcount=0;

  copy[0]=cpye_reg[0]; //code 1

  copy[1]=cpye_reg[1];

  copy[2]=cpye_reg[2];

  copy[3]=cpye_reg[3];

  copy[4]=cpye_reg[4];

  copy[5]=cpye_reg[5];

  copy[6]=cpye_reg[6];

  copy[7]=cpye_reg[7]; //code 2

  /* ...*/ //用户代码

  cpye_reg[1]=copy[1]; //code 3

  cpye_reg[2]=copy[2];

  cpye_reg[3]=copy[3];

  cpye_reg[4]=copy[4];

  cpye_reg[5]=copy[5];

  cpye_reg[6]=copy[6];

  cpye_reg[7]=copy[7]; //code 4

  }

  六、总结:

  对于习惯用C的读者,使用KEIL C来开发嵌入式PLC的驱动程序是十分轻松的,调试简单,维护方便,开发效率高。但需要注意的是由于驱动程序对硬件操作较多,而C对硬件的支持如涉及到寄存器的读写,代码的定位,堆栈的操作则没有汇编灵活,要充分考虑到C的不足之处。要保证程序编译连接之后所生成的代码,能满足内核对驱动程序使用资源的规约。要求用户须在熟悉uVsions开发环境,充分了解KEIL C编译连接的原理的基础上,再着手驱动程序的编写。Vector.asm接口文件可作为一固定模式,用户只须编写C语言模块文件,完成Vector.asm调用的相应的函数即可。

注:本站招标、拟在建信息为企业单位免费自行发布,投标前请严格审查,谨慎交易!
声明:本网站所登载之内容,如有侵犯他人声誉、版权或著作权等当事人合法权益的,请来电或来函告之,我们将予以更正。
相关资讯
暂无相关资讯
  • 上周热点