stm32配备了2个高级定时器TIM1和TIM8,4个通用定时器 TIM2,TIM3,TIM4和TIM5,还有两个基本定时器TIM6和TIM7。 高级定时器常用于电机控制,因为其加入了死区控制,紧急制动,定时器同步等高级特性。基本定时器可以为数模转化器提供准确的时间基准。
stm32的通用定时器由一个通过可编程预分频器驱动的
16位自动装载计数器构成。通用定时器可以用于测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。
通用计时器的使用,需要先配置一个时基单元,就是设定一个基准时间,确定计数一次耗去的时间,可以设定在几个微妙到几个毫秒之间。
通用定时器的都有4个独立通道(TIMx_CH1~4),这些通道可以用来作为:
- 输入捕获
- 输出比较
- PWM生成
- 单脉冲模式输出
时基单元核心部件是一个16位分频器,通过对定时器时钟的分频实现确定时间基准的功能。
根据手册可以知道 基准时钟的计算公式:
T = (分频寄存器+1)/TIM时钟
需要注意的是TIM时钟的大小,以TIM2为例,虽然其挂载在APB1总线上,PCLK时钟为36Mhz,但TIM2得到的却是72Mhz。所有挂载在APB1总线上的通用定时器时钟频率都为72Mhz;
通用寄存器的四个通道,每一个通道相当于一个中断触发源,可以设置一个计数值,当TIM计数值和此计数值相等时,触发中断。
本例实现以TIM2为例产生一组不同频率的时钟,使4个LED不同频率闪烁
直接操作寄存器
首先是控制寄存器(TIMx_CR1),该寄存器各位描述如下:
需要注意的是
ARPE位 :要开启自动重装必须将此为置1;
DIR位: 0:计数器向上计数; 1:计数器向下计数。 注:当计数器配置为中央对齐模式或编码器模式时,该位为只读。
CEN位:计数器使能位
计数器中断使能寄存器:
TIE:触发中断使能位;
UIE:允许更新中断位,允许由更新事件来产生中断;
- 更新事件包括: 计数器向上/向下溢出,计数器初始化
- 触发时间包括:计数器启动,停止,初始化
CC1IE~CC4IE:允许捕获/比较1~4中断
TDE,UDE,CC1DE~CC4DE为DMA相关中断设置,这里不讨论。
预分频寄存器(TIMx_PSC),低16位有效,该寄存器用于设置时钟进行分频,然后提供给计数器作为时钟。
自动重装载寄存器(TIMx_ARR),低16位有效。
状态寄存器(TIMx_SR),该寄存器用于标识当前与定时器相关的各种事件和中断是否发生。
描述如下:
UIF:更新中断标记 (Update interrupt flag) 当产生更新事件时该位由硬件置’1’。它由软件清’0’。
- 若TIMx_CR1寄存器的UDIS=0、URS=0,当TIMx_EGR寄存器的UG=1时产生更新事件(软件对计数器CNT重新初始化);
- 若TIMx_CR1寄存器的UDIS=0、URS=0,当计数器CNT被触发事件重初始化时产生更新事件。
CC1IF~CC4IF:捕获/比较1~4 中断标记 (Capture/Compare 1 interrupt flag)
TIF:触发器中断标记 (Trigger interrupt flag)
当发生触发事件(当从模式控制器处于除门控模式外的其它模式时,在TRGI输入端检测到有效边沿,或门控模式下的任一边沿)时由硬件对该位置’1’。它由软件清’0’。
代码如下:(system.h 和 stm32f10x_it.h 等相关代码参照
stm32 直接操作寄存器开发环境配置)
User/main.c
01 | #include <stm32f10x_lib.h> |
02 | #include "system.h" |
03 | #include "tim.h" |
04 |
05 | void Gpio_Init( void ); |
06 |
07 | int main( void ) |
08 | { |
09 |
10 | Rcc_Init(9); //系统时钟设置 |
11 | //Usart1_Init(72,9600); //设置系统时钟和波特率 |
12 |
13 | // 相关TIM_x,CCR_x参数定义tim.h文件 |
14 |
15 | Tim_Init(TIM_2,65535,7199); //初始化TIM2定时器,设定重装值和分频值 |
16 |
17 | Tim_CCR_Set(TIM_2,CCR_1,40000); |
18 | Tim_CCR_Set(TIM_2,CCR_2,20000); |
19 | Tim_CCR_Set(TIM_2,CCR_3,10000); |
20 | Tim_CCR_Set(TIM_2,CCR_4,5000); |
21 | |
22 | Nvic_Init(0,0,TIM2_IRQChannel,0); //设置抢占优先级为0,响应优先级为0,中断分组为0 |
23 |
24 | Gpio_Init(); |
25 |
26 | while (1); |
27 |
28 | } |
29 |
30 | void Gpio_Init( void ) |
31 | { |
32 | RCC->APB2ENR|=1<<2; //使能PORTA时钟 |
33 |
34 | GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出 |
35 | GPIOA->CRL|=0x33334444; |
36 | //GPIOA->ODR &=0xFFFFFFF0; |
37 | |
38 | //USART1 串口I/O设置 |
39 |
40 | //GPIOA -> CRH&=0xFFFFF00F; //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入 |
41 | //GPIOA -> CRH|=0x000008B0; |
42 | } |
User/stm32f10x_it.c
01 | #include "stm32f10x_it.h" |
02 | #include "system.h" |
03 |
04 | #define LED0 PAout(4) |
05 | #define LED1 PAout(5) |
06 | #define LED2 PAout(6) |
07 | #define LED3 PAout(7) |
08 | |
09 | void TIM2_IRQHandler( void ) |
10 | { |
11 | if (TIM2->SR&0x02) //捕获比较中断1触发 |
12 | { |
13 | LED0 = !LED0; |
14 | |
15 | TIM2 ->CCR1 = TIM2 -> CNT + 40000; //更新捕获/比较1的值 |
16 |
17 | TIM2->SR &= 0xFD; //清除捕获比较中断 |
18 | } |
19 |
20 | if (TIM2->SR&0x04) //捕获比较中断2触发 |
21 | { |
22 | LED1 = !LED1; |
23 | TIM2 ->CCR2 = TIM2 -> CNT + 20000; |
24 |
25 | TIM2->SR &= 0xFB; //清除捕获比较中断 |
26 | } |
27 |
28 | if (TIM2->SR&0x08) //捕获比较中断3触发 |
29 | { |
30 | LED2 = !LED2; |
31 |
32 | TIM2 ->CCR3 = TIM2 -> CNT + 10000; |
33 | TIM2->SR &= 0xF7; //清除捕获比较中断 |
34 | |
35 | } |
36 |
37 | if (TIM2->SR&0x10) //捕获比较中断4触发 |
38 | { |
39 | LED3 = !LED3; |
40 |
41 | TIM2 ->CCR4 = TIM2 -> CNT + 5000; |
42 | TIM2->SR &= 0x0F; //清除捕获比较中断 |
43 | |
44 | } |
45 | |
46 | TIM2->SR &= ~(1<<0); //清除中断 |
47 |
48 | } |
Library/src/tim.c
001 | #include <stm32f10x_lib.h> |
002 | #include "tim.h" |
003 |
004 | //通用定时器初始化 |
005 | //参数说明:TIM_x 为选择定时器 TIM_1为通用寄存器1又一次类推(定义于tim.h), arr为自动重装值 ;psc 为时钟预分频数 |
006 | //待完善 目前只支持TIM2 |
007 | void Tim_Init(u8 TIM_x,u16 arr,u16 psc) |
008 | { |
009 | switch (TIM_x) |
010 | { |
011 | case 1 :{ |
012 |
013 | RCC->APB2ENR |=1<<11; |
014 |
015 | break ; |
016 | } |
017 |
018 | case 2 :{ |
019 | RCC->APB1ENR |=1<<0; |
020 |
021 | TIM2->ARR = arr; //设定自动重装值 |
022 | TIM2->PSC = psc; //设定预分频值 |
023 | TIM2->DIER |= 1<<0; //允许更新中断 |
024 | TIM2->DIER |= 1<<6; //允许触发中断 |
025 |
026 | TIM2->CR1 |= 0x81; //使能定时器,自动重装允许 |
027 |
028 | break ; |
029 | } |
030 |
031 | case 3 :{ |
032 | RCC->APB1ENR |=1<<1; |
033 |
034 | break ; |
035 | } |
036 | case 4 :{ |
037 | RCC->APB1ENR |=1<<2; |
038 |
039 | break ; |
040 | } |
041 |
042 | case 5 :{ |
043 | RCC->APB1ENR |=1<<3; |
044 |
045 | break ; |
046 | } |
047 | case 6 :{ |
048 |
049 | RCC->APB1ENR |=1<<4; |
050 |
051 | break ; |
052 | } |
053 | case 7 :{ |
054 |
055 | RCC->APB1ENR |=1<<5; |
056 |
057 | break ; |
058 | } |
059 |
060 | case 8 :{ |
061 |
062 | RCC->APB2ENR |=1<<13; |
063 |
064 | break ; |
065 | } |
066 |
067 | } |
068 | } |
069 |
070 | //捕获比较值设定函数 |
071 | //参数说明: |
072 | // TIM_x 为选择定时器 TIM_1为通用寄存器1又一次类推(定义于tim.h) |
073 | // CCR_x 为选择捕获/比较寄存器(1~4)(定义于tim.h) |
074 | // val 为要设定的捕获/比较寄存器的值 |
075 | // 待完善,目前只支持TIM2 |
076 |
077 | void Tim_CCR_Set(u8 TIM_x,u8 CCR_x,u32 val) |
078 | { |
079 | switch (TIM_x) |
080 | { |
081 | case 1 :{ |
082 | break ; |
083 | } |
084 |
085 | case 2 :{ |
086 |
087 | TIM2->DIER |= 1 << CCR_x; //开启相应允许捕获/比较中断 |
088 |
089 | switch (CCR_x){ |
090 |
091 | case 1: { |
092 | TIM2 ->CCR1 = val; //设置捕获/比较1的值 |
093 | break ; |
094 | } |
095 |
096 | case 2: { |
097 | TIM2 ->CCR2 = val; //设置捕获/比较2的值 |
098 | break ; |
099 | } |
100 |
101 | case 3: { |
102 | TIM2 ->CCR3 = val; //设置捕获/比较3的值 |
103 | break ; |
104 | } |
105 |
106 | case 4: { |
107 | TIM2 ->CCR4 = val; //设置捕获/比较4的值 |
108 | break ; |
109 | } |
110 | } |
111 | |
112 | break ; |
113 | } |
114 |
115 | case 3 :{ |
116 | break ; |
117 | } |
118 | case 4 :{ |
119 | break ; |
120 | } |
121 |
122 | case 5 :{ |
123 | break ; |
124 | } |
125 | case 6 :{ |
126 | break ; |
127 | } |
128 | case 7 :{ |
129 | break ; |
130 | } |
131 |
132 | case 8 :{ |
133 | break ; |
134 | } |
135 |
136 | } |
137 |
138 | } |
Library/inc/tim.h
01 | #include <stm32f10x_lib.h> |
02 |
03 | #define TIM_1 0x01 |
04 | #define TIM_2 0x02 |
05 | #define TIM_3 0x03 |
06 | #define TIM_4 0x04 |
07 | #define TIM_5 0x05 |
08 | #define TIM_6 0x06 |
09 | #define TIM_7 0x07 |
10 | #define TIM_8 0x08 |
11 |
12 |
13 | #define CCR_1 0x01 |
14 | #define CCR_2 0x02 |
15 | #define CCR_3 0x03 |
16 | #define CCR_4 0x04 |
17 |
18 | void Tim_Init(u8 TIM_x,u16 arr,u16 psc); |
19 | void Tim_CCR_Set(u8 TIM_x,u8 CCR_x,u32 val); |
库函数操作
通用定时器有4个通道,每个通道都有6种工作模式:
TIM_OCMode | 描述 |
---|---|
TIM_OCMODE_Timging | TIM输出比较时间模式,中断时管脚无变化 |
TIM_OCMODE_Active | TIM输出比较时间模式,中断时管脚强制为有效电平 |
TIM_OCMODE_Inactive | TIM输出比较时间模式,中断时管脚强制为无效电平 |
TIM_OCMODE_Toggle | TIM输出比较时间模式,中断时管脚状态翻转,高变低,低变高 |
TIM_OCMODE_PWM1 | TIM脉冲宽度调制模式1 |
TIM_OCMODE_PWM2 | TIM脉冲宽度调制模式2 |
PS:至于有效电平是高还是低,要看CCER寄存器的CCxP位设置。两种PWM模式,区别在于通道的电平极性是相反的。
main.c
001 | #include "stm32f10x.h" |
002 |
003 | vu16 CCR1_Val = 40000; |
004 | vu16 CCR2_Val = 20000; |
005 | vu16 CCR3_Val = 10000; |
006 | vu16 CCR4_Val = 5000; |
007 |
008 | void RCC_Configuration( void ); |
009 | void GPIO_Configuration( void ); |
010 | void NVIC_Configuration( void ); |
011 | void TIM_Configuration( void ); |
012 |
013 | int main( void ) |
014 | { |
015 | |
016 | RCC_Configuration(); |
017 | GPIO_Configuration(); |
018 | NVIC_Configuration(); |
019 | TIM_Configuration(); |
020 | while (1); |
021 | } |
022 |
023 | void TIM_Configuration( void ) |
024 | { |
025 | TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; |
026 | TIM_OCInitTypeDef TIM_OCInitStructure; |
027 | TIM_TimeBaseStructure.TIM_Period = 65535; |
028 | TIM_TimeBaseStructure.TIM_Prescaler = 7199; |
029 | TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; |
030 | TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); |
031 |
032 | //TIM_PrescalerConfig(TIM2,7199,TIM_PSCReloadMode_Immediate); |
033 |
034 | TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing; |
035 | TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; |
036 | TIM_OCInitStructure.TIM_Pulse = CCR1_Val; |
037 | TIM_OC1Init(TIM2,&TIM_OCInitStructure); |
038 | TIM_OCInitStructure.TIM_Pulse = CCR2_Val; |
039 | TIM_OC2Init(TIM2,&TIM_OCInitStructure); |
040 | TIM_OCInitStructure.TIM_Pulse = CCR3_Val; |
041 | TIM_OC3Init(TIM2,&TIM_OCInitStructure); |
042 | TIM_OCInitStructure.TIM_Pulse = CCR4_Val; |
043 | TIM_OC4Init(TIM2,&TIM_OCInitStructure); |
044 | |
045 | TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Disable); |
046 | TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Disable); |
047 | TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Disable); |
048 | TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Disable); |
049 |
050 | TIM_ITConfig(TIM2,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE); |
051 | |
052 | TIM_Cmd(TIM2,ENABLE); |
053 |
054 | } |
055 |
056 | void NVIC_Configuration( void ) |
057 | { |
058 | NVIC_InitTypeDef NVIC_InitStructure; |
059 |
060 | #ifdef VECT_TAB_RAM |
061 | NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0); |
062 | #else |
063 | NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0); |
064 | #endif |
065 |
066 | NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); |
067 | NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; |
068 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; |
069 | NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; |
070 | NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; |
071 | NVIC_Init(&NVIC_InitStructure); |
072 |
073 | } |
074 |
075 | |
076 | void GPIO_Configuration( void ) |
077 | { |
078 | GPIO_InitTypeDef GPIO_InitStructure; |
079 |
080 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; |
081 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; |
082 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; |
083 | GPIO_Init(GPIOA , &GPIO_InitStructure); |
084 | } |
085 |
086 |
087 | void RCC_Configuration( void ) |
088 | { |
089 | /* 定义枚举类型变量 HSEStartUpStatus */ |
090 | ErrorStatus HSEStartUpStatus; |
091 |
092 | /* 复位系统时钟设置*/ |
093 | RCC_DeInit(); |
094 | /* 开启HSE*/ |
095 | RCC_HSEConfig(RCC_HSE_ON); |
096 | /* 等待HSE起振并稳定*/ |
097 | HSEStartUpStatus = RCC_WaitForHSEStartUp(); |
098 | /* 判断HSE起是否振成功,是则进入if()内部 */ |
099 | if (HSEStartUpStatus == SUCCESS) |
100 | { |
101 | /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */ |
102 | RCC_HCLKConfig(RCC_SYSCLK_Div1); |
103 | /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */ |
104 | RCC_PCLK2Config(RCC_HCLK_Div1); |
105 | /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */ |
106 | RCC_PCLK1Config(RCC_HCLK_Div2); |
107 | /* 设置FLASH延时周期数为2 */ |
108 | FLASH_SetLatency(FLASH_Latency_2); |
109 | /* 使能FLASH预取缓存 */ |
110 | FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); |
111 | /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */ |
112 | RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); |
113 | /* 使能PLL */ |
114 | RCC_PLLCmd(ENABLE); |
115 | /* 等待PLL输出稳定 */ |
116 | while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); |
117 | /* 选择SYSCLK时钟源为PLL */ |
118 | RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); |
119 | /* 等待PLL成为SYSCLK时钟源 */ |
120 | while (RCC_GetSYSCLKSource() != 0x08); |
121 | } |
122 | /* 打开APB2总线上的GPIOA时钟*/ |
123 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); |
124 |
125 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); |
126 | |
127 | } |
stm32f10x_it.c
01 | #include "stm32f10x_it.h" |
02 |
03 | extern vu16 CCR1_Val; |
04 | extern vu16 CCR2_Val; |
05 | extern vu16 CCR3_Val; |
06 | extern vu16 CCR4_Val; |
07 |
08 |
09 |
10 | void TIM2_IRQHandler( void ) |
11 | { |
12 | vu16 capture=0; |
13 | if (TIM_GetITStatus(TIM2,TIM_IT_CC1) != RESET) |
14 | { |
15 | GPIO_WriteBit(GPIOA , GPIO_Pin_4,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4))); |
16 | capture = TIM_GetCapture1(TIM2); |
17 | TIM_SetCompare1(TIM2,capture + CCR1_Val); |
18 | TIM_ClearITPendingBit(TIM2,TIM_IT_CC1); |
19 | } |
20 | else if (TIM_GetITStatus(TIM2,TIM_IT_CC2) != RESET) |
21 | { |
22 | GPIO_WriteBit(GPIOA , GPIO_Pin_5,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5))); |
23 | capture = TIM_GetCapture2(TIM2); |
24 | TIM_SetCompare2(TIM2,capture + CCR2_Val); |
25 | TIM_ClearITPendingBit(TIM2,TIM_IT_CC2); |
26 | } |
27 | else if (TIM_GetITStatus(TIM2,TIM_IT_CC3) != RESET) |
28 | { |
29 | GPIO_WriteBit(GPIOA , GPIO_Pin_6,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_6))); |
30 | capture = TIM_GetCapture3(TIM2); |
31 | TIM_SetCompare3(TIM2,capture + CCR3_Val); |
32 | TIM_ClearITPendingBit(TIM2,TIM_IT_CC3); |
33 | } |
34 | else |
35 | { |
36 | GPIO_WriteBit(GPIOA , GPIO_Pin_7,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_7))); |
37 | capture = TIM_GetCapture4(TIM2); |
38 | TIM_SetCompare4(TIM2,capture + CCR4_Val); |
39 | TIM_ClearITPendingBit(TIM2,TIM_IT_CC4); |
40 | } |
41 | } |