8/16 位复合定时/计时器作为8FX 系列芯片中一种专门的备用定时/计时器。其最大的特点与优点在于广泛的适用性。8/16 位复合定时器/计时器可选择16 种不同的工作模式,用来模拟PPG 定时器,重载定时器,输入捕捉定时器的功能。
复合计时器中断说明如表7-1:.

复合计数器端口引脚如表7-2:


7.2.1 复合定时/计时器原理及应用
复合计时器功能说明:
间隔定时器功能(单步模式):当使用此功能时,计数器从0 开始计数,当达到寄存器中的设置值时,计数器输出取反,同时产生中断请求信号,计数停止。
间隔定时器功能(连续模式):当使用此功能时,计数器从0 开始计数,当达到寄存器中的设置值时,计数器输出取反,同时产生中断请求信号,计数重新由0 开始计数。计数器也因此输出一个方波。
间隔定时器功能(自由模式):当使用此功能时,计数器从0 开始计数,当达到寄存器中的设置值时,计数器输出取反,同时产生中断请求信号,计数仍然持续计数,直到0xFF 为止,又从0 开始计数,计数器用样输出一个方波。
PWM 波定时器模式(固定周期):当使用此功能时,在8 位定时器模式下,周期被固定为0xFF,在16 位定时器模式下,周期被固定为0xFFFF。高脉冲宽度有对应寄存器设置。
PWM 波定时器模式(可变周期):当使用此功能时,在8 位定时器模式和16 位定时器模式下,周期均为可变周期,通过对应的寄存器进行设置。同时高脉冲宽度也要由对应寄存器设置。
PWC 定时器功能:使用此功能时,可以对外部输入脉冲的宽度和周期进行测量。当扫描到外部输入脉冲边沿时,计数器开始从00H 计数,同时结合不同的情况产生对应的终端请求。(后面会有详细地介绍)
输入捕捉功能:使用此功能时,当捕捉到外部的输入信号时,计数器的计数值将会被存储到寄存器中。该功能有两种模式自由运行模式和清除模式。
清除模式下,计数器从0 开始计数,当扫描到输入信号,则将计数器此时的值存入寄存器中,同时计数器重新从0 开始计数。
在自由运行模式下,计数器从0 开始计数,当扫描到输入信号,计数器此时的值同样存入寄存器中,当计数器依然继续技术并不会被清除。
7.2.2 复合定时/计时器寄存器配置
8/16 位复合定时器/ 计时器的寄存器包括了T00CR0/T01CR0 控制寄存器,T00CR1/T01CR1 控制状态寄存器,TMCR0 定时器/计时器模式控制寄存器,T00DR/T01DR 专用的数据寄存器。
T00CR0/T01CR0 控制寄存器用来控制定时器的16 种运行模式,计数时钟的选择以及中断信号是否使能。
T00CR1/T01CR1 控制状态寄存器用来控制定时器的运行,停止,暂停,各种中断是否使能,各种中断信号以及寄存器是否输出和输出的初始值。
TMCR0 定时器/计时器模式控制定时器timer00 和timer01 的噪声抑制(高电平噪声抑制,低电平噪声抑制,高低电平噪声抑制或不进行抑制),定时器按8 或者16 位运行,计时器输入信号为外部输入信号还是内部输入信号,定时器timer00 和timer01 是否输出。
T00DR/T01DR 专用的数据寄存器用来设定或者记录定时器的数据值。
7.2.3 复合定时/计时器应用设计范例
前面介绍了复合计数器有多种功能,其中作为PWM 波输出又有两种主要的工作模式即固定周期模式和可变周期模式,下面将结合可变周期PWM 波输出模式来说明8/16 位复合定时器的应用。
由于可变周期模式,复合定时器/计时器需要使用T00DR/T01DR 分别进行占空比和周期的设置,因此周期最多只能是0xFF,且由于是周期循环所以不需要使用IR 中断来表示计时结束,BF 中断来显示寄存器存储的数值。而只需要使用IF 来表示一个周期的PWM 方波结束。可变周期PWM 波输出模式的原理和操作原理如图7-1 所示

PWM 可变周期模式下要用到的寄存器位:

可用位视读者的需要情况来定,如IE,IFE,使用中断就必须要将IE,IFE 位设定为1。不使用位可以不用设定使其为默认设置,也可以任意设定值均不会影响定时器的工作。本文中所有工程程序中的Start8FX.asm 初始化程序和中断向量表vectors.c 均省略,请读者根据前面的说明自行进行编写。样例说明:将8/16 位复合定时器的输出设定为按一定规律变化的可变周期及可变占空比的PWM 波,并用LED 显示周期及占空比变化的不同时期。主程序代码如下:
#include "mb95100.h"
//LED 的每一位晶体管设置以及0~9 预定义
#define SEG_A 0xFE
#define SEG_B 0xFD
#define SEG_C 0xFB
#define SEG_D 0xF7
#define SEG_E 0xEF
#define SEG_F 0xDF
#define SEG_G 0xBF
#define SEG_DP 0x7F
#define SEG_0 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F
#define SEG_1 SEG_B & SEG_C
#define SEG_2 SEG_A & SEG_B & SEG_D & SEG_E & SEG_G
#define SEG_3 SEG_A & SEG_B & SEG_C & SEG_D & SEG_G
#define SEG_4 SEG_B & SEG_C & SEG_F & SEG_G #define SEG_5 SEG_A & SEG_C & SEG_D & SEG_F & SEG_G #define SEG_6 SEG_A & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G #define SEG_7 SEG_A & SEG_B & SEG_C #define SEG_8 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G #define SEG_9 SEG_A & SEG_B & SEG_C & SEG_D & SEG_F & SEG_G
const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, 0xff, 0x00 };
/*************************************************************************/
/*主程序部分*/
/*************************************************************************/
void main(void)
{
unsigned int pwm_value; // 可变低电平宽度设置
unsigned int pwm_period; // 可变周期设置
unsigned int delay; // 延时设置
unsigned int direction = 0;
const int DELAY = 500;
// 各I/O 端口初始化
PDR0 = 0xff; // Port 0: DDR0 = 0xff; // 7 段显示
PDR1 = 0x00; // Port 1: DDR1 = 0xff;
PDR2 = 0x00; // Port 2: DDR2 = 0xff;
PDR3 = 0x00; // Port 3:
AIDRL = 0xff; // 作为普通I/O 端口无模拟输入信号
DDR3 = 0xff;
PDR4 = 0x00; // Port 4:
AIDRH = 0xff; //作为普通I/O 端口无模拟输入信号
DDR4 = 0xff;
PDR5 = 0x00; // Port 5:
DDR5 = 0xff;
PDR6 = 0xff; // Port 6:
DDR6 = 0xff;
PDR7 = 0x00; // Port 7:
DDR7 = 0xff;
PDR8 = 0x00; // Port 8:
DDR8 = 0xff;
PDRE = 0x00; // Port E:
DDRE = 0xfF;
InitIrqLevels(); // 初始化中断寄存器和 IRQ 中断向量表
__EI(); // 全局中断使能
__set_il(3); // 设置全局中断级别为3,最低级别
pwm_value = 0x10;
pwm_period = 0xFF;
T00CR0 = 0x54; //设定该计数器主时钟周期为3.2us ,禁止该计数器中断
T00CR1_STA = 0; // 关闭计数器,在寄存器设定完成之前,建议先单独设置开始位为0,以防止计数器在设置完成前误触发
T00CR1_HO = 0; // 计数器开启模式
T00CR1_OE = 1; // 开输出
T00CR1_IE = 0; // 中断位不使能
T00CR1_IF = 0; // 中断标志位为0
TMCR0_FE1 = 0;
TMCR0_FE0 = 0; // 两个寄存器均没有用到电平噪声抑制功能
T00DR = pwm_value;
T01DR = pwm_period;
T00CR1_STA = 1;
T01CR1_STA = 1; // 由于两个数据寄存器均用到,故两个计数器均开启
while(1) // " 主循环"
{
for (delay = 0; delay < DELAY; delay++)
{
asm("\tnop");
}//for()
if (direction == 0) // 第零阶段,低电平宽度增加
{ if ( pwm_value < 0xFF)
{ pwm_value++;}
else {direction = 1;}
}
if (direction == 1) // 第一阶段,低电平宽度减少
{ if ( pwm_value > 10)
{ pwm_value--;}
else {direction = 2;}
}
if (direction == 2) // 第二阶段,周期减小
{ if ( pwm_period > 30)
{ pwm_period--;}
else {direction = 3;}
}
if (direction == 3) // 第三阶段,周期增加
{ if ( pwm_period < 0xFF)
{ pwm_period++;}
else {direction = 0;}
}
T00DR = pwm_value;
T01DR = pwm_period;
PDR0 = seg_display[direction]; //LED 显示各个阶段
}//while(1)
}//main
这里的周期与低电平宽度变化设置是在主程序的循环中进行,也可以将此程序改为在中断中设置,具体的方法是在比较匹配中断发生后,设置中断子程序并在其中根据所处的阶段相应的改变周期或者低电平宽度。
除开PWM 波输出外复合计时器/定时器的另一个比较常用的功能是PWC 模式即端口高电平捕捉模式。
PWC 模式用来捕捉端口处高电平的持续时间。在各种PWC 模式下,当扫描到对应的电平边沿时,计数器开始计数。扫描结束则计数器停止工作。寄存器通过IR 结束中断位来表示扫描结束,同时存在BF 位表示结束中断时,计数器保存有数值,可以对其进行读取。此外当高电平持续时间过长,导致计数器的寄存器溢出时,寄存器可以由IF 位来进行提示。注意的是,8FX 系列的芯片对于中断都没有设置通常自动清零的功能,故这里所有的中断都要求程序员手动清零。
PWC 模式下的工作原理如图7-3 所示

使用Composit timer 在PWC 模式下具体要使用到的寄存器如图7-4 所示
这里,PWC 模式下设置了多种形式的高电平捕捉,故T00CR0 寄存器中的F0~F3 需要读者结合自己的需要并参考PDF 说明文档设定相应的捕捉模式。
PWC 模式在扫描设置中包括了内部扫描和外部扫描两种模式。内部扫描指的是通过LIN-UART 总线传入的信号,请详见LIN-UART 总线一章。外部模式则主要依靠EC0/1 端口来扫描高电平。

样例说明:设置复合计数器为外部PWM 波监测模式,通过LED 显示发生计数器溢出的次数,同时当扫描结束时LED 给出一个特殊的显示。程序代码如下:
#include "mb95100.h"
#define SEG_A 0x01
#define SEG_B 0x02
#define SEG_C 0x04
#define SEG_D 0x08
#define SEG_E 0x10
#define SEG_F 0x20
#define SEG_G 0x40
#define SEG_DP 0x80
#define SEG_0 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F
#define SEG_1 SEG_B | SEG_C
#define SEG_2 SEG_A | SEG_B | SEG_D | SEG_E | SEG_G
#define SEG_3 SEG_A | SEG_B | SEG_C | SEG_D | SEG_G
#define SEG_4 SEG_B | SEG_C | SEG_F | SEG_G
#define SEG_5 SEG_A | SEG_C | SEG_D | SEG_F | SEG_G
#define SEG_6 SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G
#define SEG_7 SEG_A | SEG_B | SEG_C
#define SEG_8 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G
#define SEG_9 SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G
const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4,
SEG_5, SEG_6, SEG_7, SEG_8,SEG_9, 0xff, 0x00 };
//LED 初始化
/*************************************************************************/
/*主程序部分*/
/*************************************************************************/
int a;
void PWC_init(void) // 初始化PWC
{
T00DR = 0x00; T00CR0 = 0x85; //开中断,机器时钟周期01us,PWC 工作模式
T00CR1_STA = 0;
T00CR1_SO = 0;
T00CR1_HO = 0;
T00CR1_IE = 1;
T00CR1_IF = 0;
T00CR1_IR = 0;
T00CR1_BF = 0;
TMCR0_FE1 = 0;
TMCR0_FE0 = 0;
TMCR0_MOD = 0;
TMCR0_IIS = 1;
T00CR1_STA = 1;
}
void main(void)
{
a = 0; //端口设置
PDR0 = 0x00;
DDR0 = 0xff;
PDR1 = 0x00; DDR1_D10=0; PUL1 = 0xff;
PDR2 = 0x00;
//DDR2 = 0xff;
DDR2_D24=0;
PDR3 = 0x00; // 运行模式 // 中断位使能
// 对3 个中断标志位赋初值
// 无噪声抑制作用 //8 位 //
// Port 0: // 7 段显示
// Port 1:
// P10 = UI0 设置输入,用来进行输入捕捉
// Port 2:
//
// Port 3:
AIDRL = 0xfe;
DDR3 = 0xfe;
PDR4 = 0x00; // Port 4:
AIDRH = 0xff;
DDR4 = 0xff;
PDR5 = 0x00; // Port 5:
DDR5 = 0xff;
PDR6 = 0xff; // Port 6:
DDR6 = 0xff;
PDR7 = 0x00; // Port 7:
DDR7 = 0xff;
PDR8 = 0x00; // Port 8:
DDR8 = 0xff;
PDRE = 0x00; // Port E:
DDRE = 0xff;
InitIrqLevels(); //中断级别设置,全局中断使能
__EI();
__set_il(3);
PWC_init();
PDR0 = seg_display[0];
while(1)
{}
}
__interrupt void PWC (void)
{
a++;
if(T00CR1_IF == 1) PDR0 = seg_display[a]; //出现数据溢出时,LED 开始显示
if(a == 9)a = 0;
T00CR1_IF = 0;
T00CR1_BF = 0;
T00CR1_IR = 0;
if(T00CR1_BF == 1 && T00CR1_IR == 1)
{PDR0 = 0x80;
a = 0;
} // 出现扫描结束时,LED 开始显示特殊标志 DP
}


