第4章 系统软件设计
本次设计中,软件设计占据了非常大的比重,软件设计的好坏直接影响到系统的性能。首先进行单片机系统各个部分的初始化,初始化要具体到每个引脚,进而保单片机模块的正常启用。速度控制程序是本设计的最重要程序,从控制算法的选择到具体程序的编写,都要进行规划。超声波防撞报警程序也是重要的功能程序,涉及到超声波硬件的应用。最后是LCD显示程序,在其它程序运行无误的基础上进行此程序的设计编写。软件的实现是在CodeWarrior集成环境中实现的,因此一些用法取决于CodeWarrior所使用的编译器。
4.1 软件功能及流程
编程时候首先要对用到的单片机各个模块进行初始化,包括:系统初始化、端口初始化、中断初始化、PWM初始化、定时器初始化、时钟初始化等,然后是各个模块程序设计。
? 延时程序:采用循环体形式延时,本程序对其它功能模块程序的顺利
运行起到相当大的作用,比如采用软件延时产生40KHz方波,抵消LCD初始化时候的读取忙状态。
? 调速程序:作为本设计最重要的程序,采用增量式PID控制算法对
电机的速度进行闭环控制,使其运行在期望的速度值下。
? 超声波测距程序:测距程序是倒车防撞报警功能实现的前提,实现测
距后才能进行报警器的程序设计,完成倒车防撞报警。
? LCD初始化:先初始化LCD字符液晶,设置LCD的现实方式,换
行功能等,保证LCD能正常工作。
? 速度显示程序:LCD初始化后进行进行速度显示程序设计,液晶显
示程序完成的必要性在于:首先是在电机调速时候方便调速,其次大大完善了了设计的功能。
系统整体软件功能框图如图4.1所示。
21
堆栈指针初始化端口初始化中断初始化PWM初始化定时器初始化LCD初始化移动小车控制系统主程序延时程序电机控制算法超声波测距算法LCD显示程序防撞报警程序时钟初始化 图4.1 系统软件框图
4.2 单片机系统初始化
单片机系统初始化[16]是单片机进行功能程序设计前的准备工作,只有初始化正确才能进行子程序的编写。包括始化分为以下几个部分:单片机最小系统初始化、定时器初始化、PWM初始化。
初始化流程图如图4.2所示。
开始堆栈指针初始化始终初始化中断初始化I/O端口初始化结束
图4.2 单片机初始化
4.2.1 单片机最小系统初始化
MC9S12XS128单片机是一片超大规模集成电路,要让单片机工作,要供给电源、时钟源,要有能和人沟通的接口。这些是构成单片机最小系统的基本辅助硬件。让系统能工作,软件上要对系统进行初始化,系统初始化的
22
过程就是建立单片机运行环境的过程,具体完成三件事:
1、初始化堆栈指针
初始化堆栈是告诉CPU,单片机系统的堆栈空间从哪里开始,没有有效的堆栈指针,调用子程序的指令就无法正常工作[6]。这部分工作事实上不用我们完成,在CodeWarrior生成的系统工程中包含有一个start.c文件,该文件完成了系统最基本的初始化,其中就包括堆栈初始化。
堆栈的初始化只能通过一句汇编指令实现,因此为了在C语言中初始化堆栈,应该使用如下宏定义:
#define Initial_Stack_Pointer {_asm LDS #$3FD7;}
2、初始化时钟
由于为了减少干扰,片外晶振的频率趋向于尽可能低,因此有必要对片外晶振进行超频得到系统时钟。同时Flash的读写、串口通信都依靠时钟的设置[16]。
时钟模块的初始化要通过对几个寄存器的读写来实现。具体实现框图见图4.3。
设置REFDV设置SYNR否CRGFLG位是否为“1”?是设CLKSEL位为“1”
图4.3 始终初始化框图
23
其中REFDV,SYNR与外部晶振频率(OSCCLK)、锁相环时钟频率(PLLCLK)关系为:
PLLCLK=2OSCCLK?SYNR?1 (4.1)
REFDV?1图4.3中的判断作用是使锁相环稳定后选择锁相环时钟为系统时钟。实际使用的外部晶振为16MHz,因此选择SYNR为32,REFDV为15,就可以使时钟频率达到32MHz。
时钟初始化程序如下: void CRGInit(void) {
SYNR = 31; //32M REFDV = 15;
CRGINT_LOCKIE = 1; // 使能锁相环稳定中断 while(!CRGFLG_LOCK); //等待系统时钟稳定 CLKSEL_PLLSEL = 0x80; //选择PLL时钟作为系统时钟
//The source clock for the RTI is OSCCLK. //OSCCLK = 8MHz; RTICTL = 0x46; // 初始化实时时钟 CRGINT = 0x80; // 使能实时中断
}
3、中断初始化
S12系列单片机内部为了实现中断,在存贮器的高端放置有中断向量,具体的说是在$FF8C~$FFFF的地址段内,共有58个中断向量。当中断发生时,相应的中断向量中的地址就被装入程序计数器,并把当时的寄存器状态压入堆栈。当中断返回时又将堆栈内容送给各寄存器,使主程序继续运行。
Codewarrior中的工程文件中有个专门用来定位各代码段位置的文件,后缀为.prm,文件中定义了代码段的位置、数据段的位置、中断程序的位置、中断向量的位置等。因此如果要使用中断向量,首先要在.prm文件中定义需
24
要中断向量的位置。具体做法是在.prm文件的最后,添加类似如这样的声明语句:
VECTOR ADDRESS 0XFFE6 TIME4_ISR
这个语句的作用是声明在存储器0XFFE6处存放的是TIME4_ISR函数的入口地址,也就是完成了中断向量的填写工作。
这时应当在主函数中给该函数进行声明,声明的函数名必须和.prm文件中的函数名一致,和普通函数的声明有所不同,中断函数的声明需要对中断代码段进行定义。有两种方法:函数前面加interrput关键字,这是隐式的定义。或者使用预编译关键字#pragma具体做法是用如下语句:
#pragma code_seg default
这种是显式的定义。两种做法的作用都是使函数语句定位在直接寻址的存储器段,使得中断调用时可以直接寻址到该程序入口。在函数定义时进行同样的声明后就可以进行函数定义了。在使用两个或两个以上中断时,需要在每个中断函数声明时在函数名之前加上该中断的编号,这个编号为系统头文件中定义的中断名称的序号。 4.2.2 定时器初始化
程序中的中断我们只使用了定时器的输出比较功能和计数器溢出中断功能,因此这里先对S12的定时器功能做个简单介绍。
S12的定时器功能非常强大,该定时器的核心是一个16位的可编程计数器,计数的频率可以通过分频来调整,可以用来测量输入的脉冲宽度或者产生脉冲波形。定时器具有如下特性:
(1)用于4通道输入捕捉的16位缓冲寄存器; (2)4个8位脉冲累加器或两个16位脉冲累加器; (3)带有4位预分频的16位模数递减计数器; (4)4个可选择的延迟计数器用以增强抗干扰能力。
S12MCU输出比较的作用是产生输出动作或定时事件,该动作或事件与自由运行计数器同步。它由16比较器TCn和自由运行计数器TCNT组成。
25