对于每一个不同的MCU 芯片,其功能,引脚,寄存器,中断都是所不同。同时由于程序员自身的需要也会对使用的MCU 芯片,在时钟,中断等方面做出适当的调整。本节主要以mb95f100 为例,就其初始化配置,时钟调整,中断定义等进行说明。
6.1.1 Start8FX.asm 文件配置
Start8FX 如前面所介绍的主要是对MCU 进行一个初始化包括了对堆栈的定义,分配。同时对机器时钟进行配置,目标代码的执行从它开始,它的最后将调用主程序文件(main.c)。其中,对堆栈的定义,分配和初始化是Start8FX 的必要部分。在8FX 系列中,对时钟控制设定了专门的寄存器,可以在主程序中进行配置。
下面是mb95f100 的Start8FX 初始化文件代码
;=================================================================
;4 Settings 前面的1,2,3 位该代码内容的一些简单声明故省略
;=================================================================
;CHECK ALL OPTIONS WHETHER THEY FIT TO THE APPLICATION;
;
;Configure this startup file in the "Settings" section. Search for
; comments with leading "; <<<". This points to the items to be set.
;
#set OFF 0
#set ON 1
;
;=================================================================
; 4.1 Modbyte
;=================================================================
# set SINGLE_CHIP 0 ;single chip mode, no external bus interface
# set EXTERNAL_BUS 1 ;external bus interface
;# set MODEBYTE SINGLE_CHIP ;<<< 设置 modebyte ,定义为单片模式
;
;The Mode Byte defines whether an external bus interface is available or not.
;it is read after the reset vector. Please have a look at the Hardware manual
;of the used microcontroller for further information on that topic
;
;=================================================================
;4.2 Clock Mode Selection
;=================================================================
# set SUB 0 ; Sub clock mode
# set SUB_PLL 1 ; Sub PLL clock mode
# set MAIN 2 ; Main clock mode
# set MAIN_PLL 3 ; Main PLL mode
# set NOCLOCK FF ; Do not touch the clock and PLL register
;
#set CLOCKMODE MAIN_PLL ;<<< 内部时钟设置,这里设置为主PLL
;
;=================================================================
; 4.3 Main-PLL Clock Selection
;=================================================================
# set MAINPLL_OFF 0 ; No Main-clock setting
# set MAINPLLx1 1 ; Original oscillator clock x1
# set MAINPLLx2 2 ; Original oscillator clock x2
# set MAINPLLx2p5 3 ; Original oscillator clock x2.5 ;
#set CLOCKSPEED MAINPLLx2p5 ; <<< 设置PLL 时钟参数,为2.5 倍频
;
;=================================================================
; 4.5 Sub-PLL Clock Selection
;=================================================================
# set SUBPLL_OFF 0 ; No Sub-clock setting
# set SUBPLLx2 1 ; Original oscillator clock x2
# set SUBPLLx3 2 ; Original oscillator clock x3
# set SUBPLLx4 3 ; Original oscillator clock x4
;
#set SUBCLOCKSPEED SUBPLL_OFF ; <<< 设置子时钟PLL 速度
;
;=================================================================
; 4.5 DIV Clock Selection (Machine clock division ratio)
;=================================================================
# set CLK_0 0 ; Original oscillator div 1
# set CLK_4 1 ; Original oscillator div 4
# set CLK_8 2 ; Original oscillator div 8
# set CLK_16 3 ; Original oscillator div 16
;
#set CLOCKDIV CLK_0 ; <<< 设置内部时钟分频器为无分频
;
;=================================================================
; 4.6 Stack Size
;=================================================================
;
# set STACKSIZE 64 ; <<< 堆栈大小,默认为64byte
;
;=================================================================
; 4.7 Flash Security
;=================================================================
;
# set FLASHSECURITY OFF ; <<< Flash 安全设置(ON / OFF),默认为关闭
;
;<<< END OF SETTINGS >>>
上面的代码主要为时钟模式,主/子时钟速度的设置。不包括堆栈初始化,RAM,ROM 的设置。以及主程序接口。前面已经介绍过,在8FX 系列单片机中,有时钟控制的专用寄存器模块(CPU Controller)。
在(CPU Controller )时钟控制寄存器中包括了系统时钟控制寄存器(SYCC),PLL 控制寄存器(PLLC),晶振稳态等待时间设置寄存器(WATR),静态控制寄存器(STBC)。下面分别谈下各寄存器的作用:
系统时钟控制寄存器(SYCC )可以进行机器时钟分频设置;可以对源时钟进行不分频,4 分频,8 分频,16 分频;可以控制子时钟晶振运行/停止及静态设置;时钟模式选择。
PLL 控制寄存器(PLLC)可以对子时钟PLL 静态进行设置;子时钟倍频设置;副PLL 时钟晶振使能设置;主时钟倍频设置;主PLL 时钟晶振使能设置
晶振稳态等待时间设置寄存器(WATR )分别对主时钟和子时钟晶振稳态等待时间进行设置。
静态控制寄存器(STBC)主要对机器的监测模式,停止模式,睡眠模式,软件复位设置,针脚状态设置。
从start8FX.asm 程序中可以看出只是对时钟进行了2.5 倍频设置,同时设置子时钟关闭,机械时钟1 分频。
/* PLL */
PLLC = 0x90; /*设置时钟频率为:4MHz * 2.5 = 10MHz */
while(PLLC_MPRDY==0){;} /* PLL oscillation stability waiting */
SYCC = 0xF4; /* 机械时钟1 分频, 时钟模式为主PLL 时钟模式*/
while(SYCC_SCM1 == 0){;}
while(SYCC_SCM0 == 0){;} /* Main PLL clock mode shift waiting */
该段程序必须在主程序的最开始处进行编写,因为CPU 时钟总是在程序执行之前,就要预先设定的。在本书后面的程序段中,所有的时钟频率均为4MHz * 2.5 = 10MHz ,机械时钟1 分频,时钟模式为主PLL 时钟模式。同时时钟的设置全部在start8FX 中进行,就不再在程序中列出。该时钟频率也是目前8FX 系列的最快时钟频率,在新的富士通的低端单片机中,其时钟频率已经可以达到4MHz * 4 = 16MHz 。
6.1.2 中断配置
前面介绍过FFMC-8FX 定义了24 种中断,每一种中断对应于外部中断源,可独立的设置中断级别。那么中断究竟与那些函数有关,下面将具体讨论与中断有关的函数:
- 禁止中断函数:
一般格式为:void__DI(void) 用以清零中断标志位,从而禁止中断。由于8FX 支持扩充的C 源代码,所以通常在主程序中也可以写成“__DI();”。 - 允许中断函数:
一般格式为:void__EI(void) 置位中断允许标志位,从而允许中断,也可以写成“__EI()”。这是最常用的中断函数,如果工程要使用中断,那么在主程序main.c 中都会用到。 - 中断级别设置标志为:
一般格式:void__set_il(int level) 把中断级别设置为“level”。该函数主要在主程序中使用,如果不想在主程序中进行中断级别设置,也可以在中断向量表中进行设置。扩充的C 源代码:__set_il(int level)。 - 中断函数的定义和声明:
一般格式:__interrupt void Interrupt function(void) {……}和extern__ interrupt void Interrupt function(void) 。中断函数的声明和定义是通过在函数前面加类型限定符“__interrupt” 来实现。
由于中断函数的运行是有中断来触发,一般中断不带参数的,也不返回任何值。如果用普通的方法直接调用中断函数,而不是由于发生中断导致中断函数被调用,则无法保证这种直接调用(中断函数)方法的正确性和安全性。 - 中断向量表生成伪指令
一般格式:#pragma intevct Interrupt function name Vector number[Mode value] 和#pragma defevct Interrupt function name 说明#pragma intevct 伪指令为中断函数生成对应的中断向量表
#pragma defevct 伪指令为,没有使用#pragma intevct 伪指令的其他中断向量,生成缺省的中断向量表。
中断向量表被放置在一个单独的名为“INTVECT”的SECTION 里。
当使用#pragma defevct 伪指令,就产生所有的中断向量,而无一遗漏。因此,如果使用了#pragma defevct 伪指令,所有的中断向量都必须在这一源程序里定义,否则中断向量会产生冲突,编译里会有提示。反之,如果没有使用#pragma defevct 伪指令,就可以在多个源程
序里面使用pragma defevct 伪指令。
不能使用该函数在同一中断向量上,定义不同的中断函数/中断向量。但是,在同一源函数,同一中断向量号上,
介绍完有关中断的各种函数,现在来谈下中断流程。8FX 系列单片机的中断流程:外部源产生中断源,执行主程序,设置中断请求标志位,检测中断请求使能位,检测中断级别(ILR0 到ILR5),检测所有中断请求级别同时检测中断使能标志位。
当外部源的中断请求位被接受时,中断请求位不会自动清零,所以必须要通过软件在中断执行程序后进行清零
若中断程序在执行过程中产生了更高优先级的中断请求,CPU 会中止当前的中断进程并接受更高级别的中断请求。其具体的执行流程如图6-1 所示

中断向量表声明程序如下:
#include "mb95100.h"
/*---------------------------------------------------------------------------
InitIrqLevels()
初始中断级别设置。这里将所有中断的级别进行了设置。这里有0xFF 对中断的级别进行设置,每个0xFF 对应了4 个中断。故每个中断的级别有两个bit 构成,最高优先级为0,最低为3。需要注意的是这里定义了8FX 芯片全部可能用到的中断,但实际的芯片并不一定拥有
下面全部的中断。读者在使用时需要根据自己的需要,自行调整中断的级别。在这里可以看到对16 位重载定时器的中断级别设置为0,即最高级别。在今后的样例
中,由于在主程序中设置了只有中断级别高于3 的中断才可以发生。故通常都会对要用到的中断级别进行对应的修改。
/*-----------------------------------------------------------------------------*/
void InitIrqLevels(void)
{
/* ILRx IRQs defined by ILRx */
ILR0 = 0xFF; // IRQ0: external interrupt ch0 | ch4
// IRQ1: external interrupt ch1 | ch5
// IRQ2: external interrupt ch2 | ch6
// IRQ3: external interrupt ch3 | ch7
ILR1 = 0xFF; // IRQ4: UART/SIO ch0
// IRQ5: 8/16-bit timer ch0 (lower)
// IRQ6: 8/16-bit timer ch0 (upper)
// IRQ7: LIN-UART (reception)
ILR2 = 0x3F; // IRQ8: LIN-UART (transmission) // IRQ9: 8/16-bit PPG ch1 (lower) | UART/SIO ch1 // IRQ10: 8/16-bit PPG ch1 (upper) | I2C ch1 // IRQ11: 16-bit reload timer ch0 <<<--- Level 00
ILR3 = 0xFF; // IRQ12: 8/16-bit PPG ch0 (upper)
// IRQ13: 8/16-bit PPG ch0 (lower)
// IRQ14: 8/16-bit timer ch1 (upper)
// IRQ15: 16-bit PPG ch0 + ch2
ILR4 = 0xFF; // IRQ16: 16-bit reload timer ch1 | I2C ch0
// IRQ17: 16-bit PPG ch1
// IRQ18: 10-biat A/D-converter
// IRQ19: Timebase timer
ILR5 = 0xFF; // IRQ20: Watch timer / counter
// IRQ21: external interrupt ch 8-11
// IRQ22: 8/16-bit timer ch1 (lower) | external interrupt ch 12-15
// IRQ23: Flash | Custom ch1
}
/*---------------------------------------------------------------------------
中断处理声明,可以看到这里有两个中断的处理声明。一个是DefaultIRQHandler 。另一个是IRQ_ReloadTimer0 。DefaultIRQHandler 的处理声明主要是当发生误中断时,程序会自动寻找子程序名为DefaultIRQHandler 的中断处理子程序,在下面中会谈到。而IRQ_ReloadTimer0 则是主程序中的中断子程序在这里的声明。必须要在这里先声明,然后中断程序才能在主程序中正常运行。
-----------------------------------------------------------------------------*/
__interrupt void DefaultIRQHandler (void);
__interrupt void IRQ_ReloadTimer0 (void);
/*--------------------------------------------------------------------------------------------------------------
这里定义了全部的中断,其中23 中断很少用到。可以看到由于要用重载定时器的中断,
因而中断名也被改为和上面的声明以及main.c 中的子程序的名称一致。相应的当读者要用到其他的中断时,同样也要对对应的中断名进行修改。没有用到的中断可以不用进行预定义。当然如果不进行预定义的话,则如果在未定义的地方发生了中断将会导致程序跑飞,增加编译时检查错误的难度。
--------------------------------------------------------------------------------------------------------------*/
#pragma intvect DefaultIRQHandler 0 // IRQ0: external interrupt ch0 | ch4
#pragma intvect DefaultIRQHandler 1 // IRQ1: external interrupt ch1 | ch5
#pragma intvect DefaultIRQHandler 2 // IRQ2: external interrupt ch2 | ch6
#pragma intvect DefaultIRQHandler 3 // IRQ3: external interrupt ch3 | ch7
#pragma intvect DefaultIRQHandler 4 // IRQ4: UART/SIO ch0
#pragma intvect DefaultIRQHandler 5 // IRQ5: 8/16-bit timer ch0 (lower)
#pragma intvect DefaultIRQHandler 6 // IRQ6: 8/16-bit timer ch0 (upper)
#pragma intvect DefaultIRQHandler 7 // IRQ7: LIN-UART (reception)
#pragma intvect DefaultIRQHandler 8 // IRQ8: LIN-UART (transmission)
#pragma intvect DefaultIRQHandler 9 // IRQ9: 8/16-bit PPG ch1 (lower) | UART/SIO ch1
#pragma intvect DefaultIRQHandler 10 // IRQ10: 8/16-bit PPG ch1 (upper) | I2C ch1
#pragma intvect IRQ_ReloadTimer0 11 // IRQ11: 16-bit reload timer ch0
#pragma intvect DefaultIRQHandler 12 // IRQ12: 8/16-bit PPG ch0 (upper)
#pragma intvect DefaultIRQHandler 13 // IRQ13: 8/16-bit PPG ch0 (lower)
#pragma intvect DefaultIRQHandler 14 // IRQ14: 8/16-bit timer ch1 (upper)
#pragma intvect DefaultIRQHandler 15 // IRQ15: 16-bit PPG ch0 + ch2
#pragma intvect DefaultIRQHandler 16 // IRQ16: 16-bit reload timer ch1 | I2C ch0
#pragma intvect DefaultIRQHandler 17 // IRQ17: 16-bit PPG ch1
#pragma intvect DefaultIRQHandler 18 // IRQ18: 10-biat A/D-converter
#pragma intvect DefaultIRQHandler 19 // IRQ19: Timebase timer
#pragma intvect DefaultIRQHandler 20 // IRQ20: Watch timer / counter
#pragma intvect DefaultIRQHandler 21 // IRQ21: external interrupt ch 8-11
#pragma intvect DefaultIRQHandler 22 // IRQ22: 8/16bit timer ch1 | ext. interrupt ch 12-15
#pragma intvect DefaultIRQHandler 23 // IRQ23: Flash | Custom ch1
//#pragma asm
// .SECTION INTVECT23,CONST,LOCATE=0FFCCH
// .DATA.W _DefaultIRQHandler
// IRQ22: 8/16bit timer ch1 | ext. interrupt ch 12-15
// .SECTION INTVECT22,CONST,LOCATE=0FFCEH
// .DATA.W _DefaultIRQHandler // IRQ23: Flash | Custom ch1
//#pragma endasm
/*---------------------------------------------------------------------------
DefaultIRQHandler()
这个模块主要是用来处理除你的程序定义外的所有的其他中断。它的主要作用在于防止因为一些对寄存器的错误设置所导致的不应有的中断,进而使得程序跑飞。同时,它也能够间接的帮助编程者检查自己的程序。当在调试时,结束整个程序的连续运行后,发现程序最终跑到这里,则很有可能是程序中错误的是用了一些中断。真正需要用到的中断处理程序应该在main.c 的文件里编写。
-----------------------------------------------------------------------------*/
__interrupt void DefaultIRQHandler (void)
{
__DI(); // disable interrupts
while(1)
__wait_nop(); // halt system
}
上面的中断程序,包含了读者在编程时可能要用到的几乎所有的中断和对应的声明。读者需要根据自己的实际情况来合理的编写程序,才能将中断用到灵活自如。


