stm8 timer


原文链接: stm8 timer

Timer1

/*定时时间 = (TIM1_ARRL + 1) * 10ms */
void Timer1_init(void)
{
   CLK_PCKENR1 |= 0x80;  //打开TIM1时钟 = Fmaster = 2MHz
   asm("sim");             // 关全局中断
   TIM1_PSCRH = 0x4E; 
   TIM1_PSCRL = 0x20;    //时钟20000分频,2MHz/20000 = 100Hz = 10ms  0x4E20=20000
   TIM1_ARRH=0;        //自动重装载
   TIM1_ARRL=9;
   TIM1_IER |= 0X01;  //允许更新中断
   TIM1_EGR |= 0x01;  //产生更新事件
   TIM1_CR1 |= 0x01;  //开计数器
   asm("rim");        // 开全局中断  是不是像51里面的EA = 1;O(∩_∩)O~
}

void Init_TIM1(void)
{
    TIM1_IER = 0x00;  ////关闭更新中断
    TIM1_CR1 = 0x00;  //关闭计数器

    TIM1_EGR |= 0x01;
    TIM1_PSCRH = 399/256; // 16M系统时钟经预分频f=fck/(PSCR+1) TIM1 为16位分频器 
    TIM1_PSCRL = 399%256; // PSCR=0x1F3F,f=16M/(400)=40000Hz,每一个计数周期1/40000ms

    TIM1_CNTRH = 0x00;
    TIM1_CNTRL = 0x00;      

    TIM1_ARRH = 400/256;  // 自己主动重载寄存器ARR=400
    TIM1_ARRL = 400%256;  // 每记数400次产生一次中断。即10ms

    TIM1_CR1 |= 0x81;
    TIM1_IER |= 0x01;
}

// 好了下面开始编写主函数和中断服务函数了。
int main( void )
{
  System_Init();
  Gpio_Init();
  Timer1_init(); //100ms中断一次
  while (1);
}


// #pragma vector=TIM1_OVR_UIF_vector  //0x0D
// __interrupt void TIM1_OVF_IRQHandler(void)


 void TIM1_OVF_IRQHandler(void) __interrupt(TIM1_OVR_UIF_IRQ) 
{
   TIM1_SR1=0x00;  //清除中断标志位 该位由软件清零
   PE_ODR^=0x20;   //PE5取反
}

Timer2

/* TIM2 Update/Overflow interrupt handling routine */
void TIM2_update(void) __interrupt(TIM2_OVR_UIF_IRQ) {
    static int8_t flag =1;
    count++;
    
    if(count >= overflow){
        count =0;
        PORT(LED_PORT, ODR) |= LED_PIN; 
        PORT(RELAY_PORT, ODR) &= ~RELAY_PIN; 
        flag =1;
    }
    if(flag && count >= dur ){
        PORT(LED_PORT, ODR) &= ~LED_PIN; 
        PORT(RELAY_PORT, ODR) |= RELAY_PIN; 
        flag =0;
    }
    // Blink internal LED. Port B (or D) output data register. Flip pin 5 (or 3)
    // PORT(LED_PORT, ODR) ^= LED_PIN;

    // Clear Timer 2 Status Register 1 Update Interrupt Flag (UIF) (bit 0)
    TIM2_SR1 &= ~TIM_SR1_UIF;
}

int main(void)
{
    /* Set clock to full speed (16 Mhz) */
    CLK_CKDIVR = 0;

    /* GPIO of LED pin setup */
    // Set pin data direction as output
    PORT(LED_PORT, DDR)  |= LED_PIN; // i.e. PB_DDR |= (1 << 5);
    // Set pin as "Push-pull"
    PORT(LED_PORT, CR1)  |= LED_PIN; // i.e. PB_CR1 |= (1 << 5);

    PORT(RELAY_PORT, DDR)  |= RELAY_PIN; // i.e. PB_DDR |= (1 << 5);
    // Set pin as "Push-pull"
    PORT(RELAY_PORT, CR1)  |= RELAY_PIN; // i.e. PB_CR1 |= (1 << 5);

    /* TIM2 setup */
    // Prescaler register
    TIM2_PSCR = 14; // 2^14==16384, 16MHz/16384==976.5625 Hz
    // set Counter Auto-Reload Registers - TIM2_ARR=977 == 0x03D1, about once per second
    TIM2_ARRH = 0x03;
    TIM2_ARRL = 0xd1;
    // TIM2_IER (Interrupt Enable Register), Update interrupt (UIE) (bit 0)
    TIM2_IER |= TIM_IER_UIE;
    // TIM2_CR1 – Timer 2 Control Register 1, Counter ENable bit (CEN) (bit 0)
    TIM2_CR1 |= TIM_CR1_CEN;

    /* Loop infinitely waiting for an interrupt */
        while(1) {
        wfi();
    }
}

————————————————
版权声明:本文为CSDN博主「湖工电气」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhuming3834/article/details/45581417

STM8S的定时器分三类;
高级定时器TIM1
通用定时器TIM2,TIM3,TIM5
基本定时器TIM4,TIM6

其中除TIM4和TIM6是8位定时器外,其他的定时器都是16位计数的。
每个定时器都具有自动重装载功能
每个定时器的时钟都可以由系统时钟独立分频而来,其中高级定时器TIM1可以选择65536种分频,分频系数为1-65536.通用定时器可以选择16种分频,基本定时器只能选择8种分频。
除了TIM1可以选择计数方向外,其他的定时器都是向下计数的,而技术手册上说的向上计数是错误的。

基本中断定时用到的寄存器;

一、自动装载寄存器高位(TIMx_ARRH) 和 自动装载寄存器低位(TIMx_ARRL)
这两个寄存器复位值都是0x00。需要注意的是,高级定时器TIM1和通用定时器TIM2,TIM3,TIM5都是16位计数的定时器,操作寄存器的时候要先写高8位再写低8位。而基本定时器TIM4,TIM6是8位计数的定时器,是不分高8位和低8位的,所以操作基本定时器的时候寄存器名字是(TIMx_ARR)。 操作重装载寄存器的时候更新的值不会马上写入重装载寄存器,而是等到有中断产生的时候操作的数值才会写入寄存器,当然也可以用软件的方法产生中断。(这里省略了影子寄存器的概念,感觉那玩意就是忽悠人的)

二、计数器高位(TIMx_CNTRH) 和计数器低位(TIMx_CNTRL)
计数器高位和计数器低位复位值都是0x00,其中只有高级定时器TIM1和通用定时器TIM2,TIM3,TIM5才会用到这两个计数器,而基本定时器用的计数器是(TIMx_CNTR)。 最好在开启定时器前清零下计数器。基本定时器中(位5:1保留)。

三、中断使能寄存器(TIMx_IER)
这个寄存器对所有定时器都通用,复位值0x00。
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
保留 TIE 保留 保留 CC3IE CC2IE CC1IE UIE
RW RW RW RW RW RW

位7 保留
位6 TIE:触发中断使能
0;触发中断禁用
1;触发中断使能
位5:4 保留
位3 CC3IE:允许捕获/比较3中断
0;禁止捕获/比较3中断
1;允许捕获/比较3中断
注;基本定时器中该位保留
位2 CC2IE:允许捕获/比较2中断
0;禁止捕获/比较2中断
1;允许捕获/比较2中断
注;基本定时器中该位保留
位1 CC1IE:允许捕获/比较1中断
0;禁止捕获/比较1中断
1;允许捕获/比较1中断
注;基本定时器中该位保留
位0 UIE:允许更新中断
0;禁止更新中断
1;允许更新中断

四、状态寄存器 1(TIMx_SR1)
复位值0x00,这个寄存器对高级定时器和通用定时器适用,但是基本定时器的状态寄存器名字是(TIMx_SR) ,对应的功能是一样的,基本定时器中(位5:1保留)。
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
保留 TIF 保留 保留 CC3IF CC2IF CC1IF UIF
Rc_w0 Rc_w0 Rc_w0 Rc_w0 Rc_w0 Rc_w0

位7 保留
位6 TIF:触发中断标志
当产生触发事件时该位由硬件置1(在TRGI信号上检测到有效的触发沿,当选择门控模式时,上升及下降沿都有效)。它由软件清零。
0;没有触发事件发生
1;触发中断悬挂
注意;在TIM2,TIM3中该位保留
位5:4 保留
位3 CC3IF:捕获/比较3中断标志
位2 CC2IF:捕获/比较2中断标志
位1 CC1IF:捕获/比较1中断标志
如果通道CC1配置为输出模式;
当计数器值与比较值匹配时该位由硬件置1,由软件清零
0;无匹配发生
1;TIMX_CNT的值与TIMX_CCR1的值匹配
如果通道CC1配置为输入模式;
当捕获事件发生时该位由硬件置1,由软件清零或者通过读TIMX_CCR1L清零
0;无输入捕获产生
1;计数器值已被捕获(拷贝)至TIMX_CCR1(在IC1上检测到与所选极性相同的边沿)
位0 UIF:更新中断标志
当产生更新事件时该位由硬件置1,它由软件清零。
0;无更新事件产生
1;更新事件等待响应。当寄存器被更新时该位由硬件置1
-若TIMX_CR1寄存器的UDIS=0,计数器溢出
-若TIMX_CR1寄存器的UDIS=0、URS=0,当TIMX_EGR寄存器的UG=1时产生更新事件(软件对计数器CNT重新初始化)

五、控制寄存器 1(TIMx_CR1)
复位值0x00,这个寄存器对所有定时器适用。
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
ARPE 保留 保留 保留 OPM URS UDIS CEN
rw rw rw rw rw

位7 ARPE:自动重装载允许位
0;TIMX_ARR寄存器没有预装载寄存器可以缓冲,可以直接对其操作
1;TIMX_ARR寄存器通过预装载寄存器可以缓冲
位6:4 保留
位3 OPM:单脉冲模式
0;在发生更新事件时,计数器不停止
1;在发生下一次更新事件(清除CEN位)时,计数器停止
位2 URS:更新请求源
0;当更新请求使能时,只要寄存器被更新了就产生更新中断
1;当更新请求使能时,只有计数器溢出才会产生更新中断
位1 UDIS:禁止更新
软件通过该位允许/禁止UEV事件的产生
0;只要计数器溢出,或者产生了软件更新,或者通过时钟/触发模式控制器产生了硬件复位,就产生更新事件
1;不产生更新事件,影子寄存器(ARR,PSC,CCRX)保持他们的值。如果设置了UG则计数器和预分频器被重新初始化。
位0 CEN:使能计数器
0;禁止计数器
1;使能计数器

六、TIMX_PSCR预分频寄存器
这个寄存器在不同的定时器中有不同特点使用方法,在具体应用中介绍。

七、高级定时器TIM1中断定时的使用
高级定时器除了用到上面的寄存器外,还用到时钟预分频寄存器TIM1_PSCRH和TIM1_PSCRL,高级定时器可以选择对系统时钟在1-65536之间的任意分频,因为两个预分频寄存器共16位,最大计数刚好是65536。先写高8位再写低8位。

例;用TIM1中断定时,1ms进一次中断,LED灯1s翻转一次,向下计数模式

/* 这个工程模板是别人的,但是这个头文件不影响使用 ,在自己的工程中需更改为自己的头文件 */

#include

unsigned int ms_count=0;//毫秒计数

/************************************

各种函数声明

************************************/
void InitLED();
void InitTIM1();

int main(void)
{
CLK_CKDIVR = 0x00; //CPUDIV = 1 HSIDIV = 1 内部时钟 = 16Mhz

asm("sim"); //先关闭总中断

InitLED(); //各种初始化
InitTIM1();

asm("rim"); //打开总中断
while(1); //进入死循环,等待定时器周期中断
}

/*********************************************************

定时器1更新中断处理函数
中断向量也可以用十进制代替,这里使用16进制是为了方便而已,
因为头文件的宏定义就是用16进制的,
函数名可以随便写,开心就好

*****************************************************/
#pragma vector = 0x0D //这个是中断的格式,直接调用就好,
__interrupt void TIM1_OVR_UIF(void)
{
TIM1_SR1 &= ~(1<<0);//清除中断标志 ms_count++; if(ms_count>1000)//1ms*1000=1s
{

ms_count=0;
PE_ODR ^= 1<<5;//LED灯1s翻转一次

}
}

/*********************************************

定时器1初始化
tim1向下计数模式,

************************************************/
void InitTIM1()
{
TIM1_PSCRH=0;//一定要先写高八位
TIM1_PSCRL=0;//1分频,定时器时钟等于系统时钟=16m

TIM1_ARRH=0X3e;//一定要先装高八位,再装低八位
TIM1_ARRL=0X80;//1ms重装值16000,

TIM1_CNTRH=0;
TIM1_CNTRL=0;//有必要清除下计数器

TIM1_IER |= 1<<0;//使能tim1更新中断
TIM1_SR1 |= 1<<0;//清除tim1更新中断标志

TIM1_CR1 |= 1<<7;//允许重装,使能定时器
TIM1_CR1 |= 1<<4;//选择向下计数模式
TIM1_CR1 |= 1<<0;//使能计数器
}

/******************************************

LED初始化

********************************************/
void InitLED()
{

PE_DDR |= 1<<5;//PE5方向为输出
PE_CR1 |= 1<<5;//PE5为推挽输出
PE_CR2 |= 1<<5;//最大输出速度为10M
PE_ODR |= 1<<5;//PE5输出1

}
例;用TIM1中断定时,1ms进一次中断,LED灯1s翻转一次,向上计数模式,好像这个程序不能用,进不了中断。

/* 这个工程模板是别人的,但是这个头文件不影响使用 ,在自己的工程中需更改为自己的头文件 */

#include

unsigned int ms_count=0;//毫秒计数

/************************************

各种函数声明

************************************/
void InitLED();
void InitTIM1();

int main(void)
{
CLK_CKDIVR = 0x00; //CPUDIV = 1 HSIDIV = 1 内部时钟 = 16Mhz

asm("sim"); //先关闭总中断

InitLED(); //各种初始化
InitTIM1();

asm("rim"); //打开总中断
while(1); //进入死循环,等待定时器周期中断
}

/*********************************************************

定时器1更新中断处理函数
函数名可以随便写,开心就好

*****************************************************/
#pragma vector = 0X0D //这个是中断的格式,直接调用就好,
__interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
{
TIM1_SR1 = 0;//清除中断标志
ms_count++;
if(ms_count>1000)//1ms*1000=1s
{

ms_count=0;
PE_ODR ^= 1<<5;//LED灯1s翻转一次

}
}

/*********************************************

定时器1初始化
tim1向上计数模式,
系统时钟1分频=定时器时钟=16m,所以每个时钟1/16us
定时1ms需要16000个时钟,所以初值=65536-16000=49536=0xc180
为啥向上计数用不了呢?根本进不了中断

************************************************/
void InitTIM1()
{
TIM1_PSCRH=0;//一定要先写高八位
TIM1_PSCRL=0;//1分频,定时器时钟等于系统时钟=16m

TIM1_ARRH=0XC1;//一定要先装高八位,再装低八位
TIM1_ARRL=0X80;//1ms重装值0xc180

TIM1_CNTRH=0;
TIM1_CNTRL=0;//有必要清除下计数器

TIM1_IER |= 1<<0;//使能tim1更新中断
TIM1_SR1 |= 1<<0;//清除tim1更新中断标志

TIM1_CR1 |= 1<<7;//允许重装,使能定时器
//....................................
TIM1_CR1 |= ~(1<<5);
TIM1_CR1 |= ~(1<<6);
TIM1_CR1 |= ~(1<<4);//选择向下计数模式
//.....................................
TIM1_CR1 |= 1<<0;//使能tim1计数器
}

/******************************************

LED初始化

********************************************/
void InitLED()
{

PE_DDR |= 1<<5;//PE5方向为输出
PE_CR1 |= 1<<5;//PE5为推挽输出
PE_CR2 |= 1<<5;//最大输出速度为10M
PE_ODR |= 1<<5;//PE5输出1

}
八、通用定时器TIM2、TIM3和TIM5的使用
通用定时器的时钟预分频器(TIMx_PSCR) 和高级定时器的有点不一样,通用定时器的预分频只有8位,而且还有4位是保留的,只用到了四位,一共可以选择16种分频,就是2的1-16次幂分频,1分频,2分频,4分频,8分频………….32768分频。
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
保留 PSC[3:0]
RW RW RW RW

PSC[3:0] 分频系数
0000 1
0001 2
0010 4
0011 8
……………….. ……………………………
1111 32768

例;用TIM2中断定时,1ms进一次中断,LED灯1s翻转一次。

/* 这个工程模板是别人的,但是这个头文件不影响使用 */
#include

unsigned int ms_count=0;//毫秒计数

//***********************************
//各种函数声明
void InitLED();
void InitTIM2();

int main(void)
{
CLK_CKDIVR = 0x00; //CPUDIV = 1 HSIDIV = 1 内部时钟 = 16Mhz

asm("sim"); //先关闭总中断

InitLED(); //各种初始化
InitTIM2();

asm("rim"); //打开总中断
while(1); //进入死循环,等待定时器周期中断
}

//*********************************************************
//定时器2更新中断处理函数
#pragma vector = 15 //设置定时器2重载的中断向量号 = 15,这个是中断的格式,直接调用就好
__interrupt void TIM2_UPDATE_IRQHandler(void)
{
TIM2_SR1=0;//清除中断标志
ms_count++;
if(ms_count>1000)//1ms*1000=1s
{

ms_count=0;
PE_ODR^=1<<5;//LED灯1s翻转一次

}
}

//*********************************************
//定时器2初始化
void InitTIM2()
{
TIM2_PSCR=0;//1分频,定时器时钟等于系统时钟=16m

TIM2_ARRH=0X3e;//一定要先装高八位,再装低八位
TIM2_ARRL=0X80;//1ms重装值16000,这是个坑,技术手册里说tim2是向上计数,其实是向下计数的

TIM2_CNTRH=0;
TIM2_CNTRL=0;//有必要清除下计数器

TIM2_IER=0X01;//使能tim2更新中断
TIM2_SR1=0X01;//清除tim2更新中断标志
TIM2_CR1=0X81;//允许重装,使能定时器
}

//******************************************
//LED初始化
void InitLED()
{

PE_DDR |= 1<<5;//PE5方向为输出
PE_CR1 |= 1<<5;//PE5为推挽输出
PE_CR2 |= 1<<5;//最大输出速度为10M
PE_ODR |= 1<<5;//PE5输出1

}
例;用TIM3中断定时,1ms进一次中断,LED灯1s翻转一次。

/* 这个工程模板是别人的,但是这个头文件不影响使用 ,在自己的工程中需更改为自己的头文件 */

#include

unsigned int ms_count=0;//毫秒计数

/************************************

各种函数声明

************************************/
void InitLED();
void InitTIM3();

int main(void)
{
CLK_CKDIVR = 0x00; //CPUDIV = 1 HSIDIV = 1 内部时钟 = 16Mhz

asm("sim"); //先关闭总中断

InitLED(); //各种初始化
InitTIM3();

asm("rim"); //打开总中断
while(1); //进入死循环,等待定时器周期中断
}

/*********************************************************

定时器3更新中断处理函数
中断向量也可以用十进制代替,这里使用16进制是为了方便而已,
因为头文件的宏定义就是用16进制的,
函数名可以随便写,开心就好

*****************************************************/
#pragma vector = 0x11 //这个是中断的格式,直接调用就好,
__interrupt void TIM3_OVR_UIF(void)
{
TIM3_SR1 = 0;//清除中断标志
ms_count++;
if(ms_count>1000)//1ms*1000=1s
{

ms_count=0;
PE_ODR ^= 1<<5;//LED灯1s翻转一次

}
}

/*********************************************

定时器初始化
也是坑,技术手册上说tim3是向上计数,实际上定时器3是向下计数的,
系统时钟1分频=定时器时钟=16m,所以每个时钟1/16us
定时1ms需要16000个时钟,所以初值=16000=0X3E80

************************************************/
void InitTIM3()
{
TIM3_PSCR=0;//1分频,定时器时钟等于系统时钟=16m

TIM3_ARRH=0X3e;//一定要先装高八位,再装低八位
TIM3_ARRL=0X80;//1ms重装值0X3E80

TIM3_CNTRH=0;
TIM3_CNTRL=0;//有必要清除下计数器

TIM3_IER |= 1<<0;//使能tim3更新中断
TIM3_SR1 |= 1<<0;//清除tim3更新中断标志

TIM3_CR1 |= 1<<7;//允许重装,使能定时器
TIM3_CR1 |= 1<<0;//使能tim3计数器
}

/******************************************

LED初始化

********************************************/
void InitLED()
{

PE_DDR |= 1<<5;//PE5方向为输出
PE_CR1 |= 1<<5;//PE5为推挽输出
PE_CR2 |= 1<<5;//最大输出速度为10M
PE_ODR |= 1<<5;//PE5输出1

}
例;用TIM5中断定时,1ms进一次中断,LED灯1s翻转一次。
好像我的STM8SK4T6最小系统上没有TIM5…………………………………………..

九、基本定时器TIM4和TIM6的使用
基本定时器的时钟预分频器(TIMx_PSCR) 和高级定时器、通用定时器都不一样,基本定时器的预分频只有8位,而且还有5位是保留的,只用到了3位,一共可以选择8种分频,就是2的1-8次幂分频,1分频,2分频,4分频,8分频………….256分频。
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
保留 PSC[3:0]
RW RW RW

PSC[3:0] 分频系数
000 1
001 2
010 4
011 8
……………….. ……………………………
111 256

例;用TIM4中断定时,1ms进一次中断,LED灯1s翻转一次。

/* 这个工程模板是别人的,但是这个头文件不影响使用 ,在自己的工程中需更改为自己的头文件 */

#include<iostm8s105k6.h>


unsigned int ms_count=0;//毫秒计数


/************************************

各种函数声明

************************************/
void InitLED();
void InitTIM4();

int main(void)
{
  CLK_CKDIVR = 0x00;  //CPUDIV = 1 HSIDIV = 1  内部时钟 = 16Mhz 


  asm("sim");         //先关闭总中断

  InitLED();          //各种初始化
  InitTIM4();

  asm("rim");         //打开总中断
  while(1);           //进入死循环,等待定时器周期中断
}


/*********************************************************

定时器4更新中断处理函数
函数名可以随便写,开心就好

*****************************************************/
#pragma vector = 0x19    //这个是中断的格式,直接调用就好,
__interrupt void TIM4_OVR_UIF(void)
{
  TIM4_SR  = 0;//清除中断标志
  ms_count++;
  if(ms_count>50000)//10US*50000=500MS
  {
    ms_count=0;
    PE_ODR ^= 1<<5;//LED灯500MS翻转一次
  }
}


/*********************************************

定时器初始化
也是坑,技术手册上说tim4是向上计数,实际上定时器4是下计数的,
系统时钟1分频=定时器时钟=16m,所以每个时钟1/16us
定时10US需要160个时钟,所以初值=160=0XA0

************************************************/
void InitTIM4()
{
  TIM4_PSCR=0;//1分频,定时器时钟等于系统时钟=16m

  TIM4_ARR=0XA0;//1ms重装值0XA0

  TIM4_CNTR=0;//有必要清除下计数器

  TIM4_IER |= 1<<0;//使能tim4更新中断
  TIM4_SR  |= 1<<0;//清除tim4更新中断标志

  TIM4_CR1 |= 1<<7;//允许重装,使能定时器
  TIM4_CR1 |= 1<<0;//使能tim4计数器
}


/******************************************

LED初始化

********************************************/
void InitLED()
{
    PE_DDR |= 1<<5;//PE5方向为输出
    PE_CR1 |= 1<<5;//PE5为推挽输出
    PE_CR2 |= 1<<5;//最大输出速度为10M
    PE_ODR |= 1<<5;//PE5输出1
}

————————————————
版权声明:本文为CSDN博主「深蓝判官」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/LHChandsome/article/details/78102720

`