gpio

点亮第一个led灯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/**
* 操作stm32的gpio总共需要3个步骤
* 1.使用rcc开启gpio时钟
* 2.使用GPIO_Init初始化gpio
* 3.使用输出或者输入的函数控制gpio
*/

/**
* 在推挽输出(PP)模式下,高低电平都具有驱动能力——将发光二极管反插,同样正常工作;
* 在开漏输出(OD)模式下,只有低电平有驱动能力。
*/


int main(void)
{
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);


while(1)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); // GPIO_ResetBits(GPIOA, GPIO_Pin_0);
Delay_ms(1000);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); // GPIO_SetBits
Delay_ms(1000);
}
}

流水灯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*3-2流水灯*/

#include "stm32f10x.h" // Device header
#include "Delay.h"

/**
* 操作stm32的gpio总共需要3个步骤
* 1.使用rcc开启gpio时钟
* 2.使用GPIO_Init初始化gpio
* 3.使用输出或者输入的函数控制gpio
*/


int main(void)
{
// 初始化gpio
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);

// 流水灯
uint16_t turn_on = 0x0001;
while(1)
{
for(int i = 0 ; i < 8; ++i)
{
GPIO_Write(GPIOA, ~turn_on);
Delay_ms(100);
turn_on = turn_on << 1;
}
turn_on = 0x0001;
}
}

蜂鸣器

A15,B3,B4是JTAG的调试端口,不便于用于普通的io口

给蜂鸣器输出低电平,就会响;输出高电平就不响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*3-3蜂鸣器*/

#include "stm32f10x.h" // Device header
#include "Delay.h"

int main(void)
{
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; // 蜂鸣器的io插入A0
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);


while(1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
Delay_ms(100);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
Delay_ms(100);
}
}

按键控制led灯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*3-3按键控制led*/

#include "stm32f10x.h" // Device header
#include "Delay.h"

/**
* LED 初始化
*/
void init_led()
{
// 开启gpio时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

// 初始化gpio
uint16_t led_pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = led_pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);

// 置位led_pin,以熄灭led
GPIO_SetBits(GPIOA, led_pin);
}

/**
* 初始化按键io
*/
void init_btn()
{
// 开启gpio时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );

// 初始化gpio
uint16_t led_pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_InitStruct.GPIO_Pin = led_pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}

/**
* 检测按键是否按下,返回是哪一个按键按下
* 返回1,B1按下
* 返回2,B11按下
*/
uint8_t key_getNum()
{
uint8_t keyNum = 0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Delay_ms(20); // 消抖
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); // 松手检测
Delay_ms(20); // 消抖
keyNum = 1;
}
else if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
Delay_ms(20); // 消抖
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); // 松手检测
Delay_ms(20); // 消抖
keyNum = 2;
}

return keyNum;
}

/**
* led1的亮灭随B1按键切换
*/
void led1_turn()
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0) // 读取A1的输出状态
{
// 若A1 = 0,则置1
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
}
else{
// 否则清0
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
}
}
/**
* led2的亮灭随B11按键切换(同led1_turn())
*/
void led2_turn()
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0) // 读取A2的输出状态
{
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);
}
else{
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
}
}



int main(void)
{
init_led();
init_btn();

uint8_t keyNum = 0;
while(1)
{
keyNum = key_getNum();
if(keyNum == 1)
{
//A1上的led,按一次B1按钮亮,再按一次灭
led1_turn();
}
if(keyNum == 2)
{
led2_turn();
}
}
}

通过光敏电阻控制蜂鸣器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*3-4通过光敏电阻控制蜂鸣器*/

#include "stm32f10x.h" // Device header
#include "Delay.h"


/**
* 蜂鸣器初始化
*/
void init_buzzer()
{
// 开启gpio时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );

// 初始化gpio
uint16_t led_pin = GPIO_Pin_12;
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // 上拉输入
GPIO_InitStruct.GPIO_Pin = led_pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}

/**
* 光敏电阻初始化
*/
void init_lightSensor()
{
// 开启gpio时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );

// 初始化gpio
uint16_t led_pin = GPIO_Pin_13;
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_InitStruct.GPIO_Pin = led_pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}


/*
* 蜂鸣器低电平触发
*/
void buzzer_on()
{
GPIO_WriteBit(GPIOB, GPIO_Pin_12, Bit_RESET);
}

void buzzer_off()
{
GPIO_WriteBit(GPIOB, GPIO_Pin_12, Bit_SET);
}


int main(void)
{
init_buzzer();
init_lightSensor();
while(1)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0)
{
// 有亮光时蜂鸣器响
buzzer_on();
}
else
{
buzzer_off();
}
}

}

中断

对射式红外传感器计次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*5-1 对射式红外传感器计次*/

#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"

uint32_t CountSensor = 0;

/**
* 中断的初始化步骤
* 1.配置rcc,将外设涉及的时钟都打开
* 2.配置gpio
* 3.配置afio,中断引脚选择 (也在gpio的库文件中)
* 4.配置exti,设置触发方式及触发响应方式(中断响应或事件响应)
* 5.配置nvic,为中断响应以合适的优先级
*/

void CountSensor_Init()
{
//RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);


//GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);

//AFIO
/**
* 选择gpio pin作为外部中断线
* 第一个参数:选择某个gpio外设作为外部中断源
* 第二个参数:指定配置的外部中断线
*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);


//EXTI
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line14; //中断线14
EXTI_InitStruct.EXTI_LineCmd = ENABLE; //开启中断
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_Init(&EXTI_InitStruct);



// nvic
/*配置优先级分组(一个工程只需配置一次)*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

/*初始化nvic*/
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn; // 指定中断通道来开启或关闭
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 响应优先级
NVIC_Init(&NVIC_InitStruct);


}

/**
* 中断函数的函数名在启动文件中以*IRQHandler命名
*/
void EXTI15_10_IRQHandler()
{

if( EXTI_GetITStatus(EXTI_Line14) == SET)
//判断是否是外部中断14号线触发的中断
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0) //消抖
{
++CountSensor;
}
EXTI_ClearITPendingBit(EXTI_Line14); //手动清除中断标志位
}
}

uint32_t CountSensor_Get()
{
return CountSensor;
}

int main(void)
{
CountSensor_Init();

OLED_Init();
OLED_ShowString(1,1,"count:");
while(1)
{
OLED_ShowNum(1,7,CountSensor_Get(),5);
}

}

旋转编码器计次

B0和B1的相位相差90°,以此来判断正反转,判断方式如下。在代码中如中断函数内所示。

image-20240706114718807

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*5-2 旋转编码器计次*/

#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"

uint32_t CountSensor = 0;

/**
* 中断的初始化步骤
* 1.配置rcc,将外设涉及的时钟都打开
* 2.配置gpio
* 3.配置afio,中断引脚选择 (也在gpio的库文件中)
* 4.配置exti,设置触发方式及触发响应方式(中断响应或事件响应)
* 5.配置nvic,为中断响应以合适的优先级
*/

void Encoder_Init()
{
//RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);


//GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);

//AFIO
/**
* 选择gpio pin作为外部中断线
* 第一个参数:选择某个gpio外设作为外部中断源
* 第二个参数:指定配置的外部中断线
*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);


//EXTI
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line0 | EXTI_Line1; //中断线14
EXTI_InitStruct.EXTI_LineCmd = ENABLE; //开启中断
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_Init(&EXTI_InitStruct);



// nvic
/*配置优先级分组(一个工程只需配置一次)*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

/*初始化nvic*/
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; // 指定中断通道来开启或关闭
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 响应优先级
NVIC_Init(&NVIC_InitStruct);


NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn; // 指定中断通道来开启或关闭
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; // 响应优先级
NVIC_Init(&NVIC_InitStruct);

}

/**
* 中断函数的函数名在启动文件中以*IRQHandler命名
*/
uint16_t Encoder_Count = 0;
void EXTI0_IRQHandler()
{
//判断中断源
if( EXTI_GetITStatus(EXTI_Line0) == SET) // B0是下降沿的同时,B1是0
{
//if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) // 消抖,B0确实下降为0了
// {
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Encoder_Count--;
}
// }
EXTI_ClearITPendingBit(EXTI_Line0); //手动清除中断标志位
}
}


void EXTI1_IRQHandler()
{
if(EXTI_GetITStatus(EXTI_Line1) == SET) // B1是下降沿的同时,B0是0
{
// if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
// {
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count++; // 在中断里操作变量、标志位
}
// }
EXTI_ClearITPendingBit(EXTI_Line1);
}
}

uint16_t Encoder_Get()
{
int16_t tmp = 0;
tmp = Encoder_Count;
Encoder_Count = 0;
return tmp;
}

int main(void)
{
Encoder_Init();

OLED_Init();
OLED_ShowString(1,1,"num:");

int16_t num;
while(1)
{
num += Encoder_Get(); // 当中断返回时,在对变量或标志位进行操作
OLED_ShowSignedNum(1,5,num,5);
}

}

/*
中断的注意事项
1.中断中的程序简短快速
2.中断函数和主函数不要调用相同的函数或硬件
*/

TIM定时器

定时器定时中断

初始化流程

image-20240706154729669

定时器的选择

image-20240706154955910

TIM_Period 和 TIM_Prescaler 的计算
参考STM32学习笔记——TIM_Period 和 TIM_Prescaler_stm32period怎么设置?-CSDN博客

概念

TIM_TimeBaseStructure.TIM_Period 和 TIM_TimeBaseStructure.TIM_Prescaler 是STM32定时器(Timer)的两个重要参数。

  1. TIM_Period:这个参数代表的是定时器的自动重装载值(Auto-reload value)。当定时器的计数值达到这个值时,定时器就会产生一个中断或更新事件。这个参数可以用来控制定时器中断的间隔时间。
    **例如,如果设置TIM\_Period为7199,那么定时器每计数7199次就会产生一个中断。**
  1. TIM_Prescaler:这个参数代表的是定时器的预分频值(Prescaler value)。它决定了定时器的时钟频率被分频的倍数。这可以用来控制定时器的分辨率和计数速度。通过调整TIM_Prescaler的值,可以实现对定时器行为的精细控制。
    **例如,如果设置TIM\_Prescaler为9,那么定时器的时钟频率将被除以9,从而降低定时器的计数速度。**

这两个参数共同决定了定时器的中断间隔时间。在STM32的TIM2中,可以通过设置这两个参数来实现在特定的时间间隔产生中断的功能。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//对于72MHZ的频率,2s中断一次:

TIM_Prescaler=7200-1; //预分频值

TIM_Period=20000-1; //重装载值

//72M / 7200 = 72* 10 ^ 6 / 7200= 72000000/7200=10000Hz

//周期等于频率的倒数 1/10000 = 0.0001s

//2s = 2000ms 2s/0.0001s = 20000

//要运行20000 从0开始就有20000 - 1 = 19999

//即通过19999次系统的运行,就是2000ms

总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
定时器中断时间(s) = (TIM_Prescaler + 1)* (TIM_Period +1) / 时钟频率

定时器中断时间(ms)=(TIM_Prescaler + 1)* (TIM_Period +1) * 1000 / 时钟频率





```c
/*6-1 定时器定时中断*/
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"

uint16_t num = 0;


/**
* 初始化流程:
* 1.rcc开启时钟
* 2.选择时钟源
* 3.配置时基单元(如何计算Period和Prescaler?)
* 4.配置输出中断控制
* 5.配置nvic
* 6.使能TIM2,定时器开始运行
*/

void Time_Init()
{
//开启rcc
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

//选择时钟源
TIM_InternalClockConfig(TIM2); //内部时钟配置

//配置时基单元(1s中断一次)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,用于配置滤波器时钟
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);


//配置输出中断控制
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断


//配置nvic
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置中断分组,配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure);


//使能TIM2,定时器开始运行
TIM_Cmd(TIM2, ENABLE);

}


void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) // 中断位确实被置1了
{
num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}

}

int main()
{
Time_Init();
OLED_Init();

while(1)
{
OLED_ShowNum(1,1,num,5);
OLED_ShowNum(2,1,TIM_GetCounter(TIM2),5); // TIM_GetCounter():获取 TIM2 计数器值。
}
}

外部触发中断

TIM_ETRClockMode2Config()最后一个参数——采样滤波器的配置

image-20240710102145356

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*6-1-2 定时器外部时钟*/
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"


/**
* 初始化流程:
* 1.rcc开启时钟
* 2.选择时基单元的时钟源
* 3.配置时基单元(如何计算Period和Prescaler?)
* 4.配置输出中断控制
* 5.配置nvic
* 6.运行控制并使能计数器
*/

void Timer_Init()
{
//开启rcc
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

//配置gpio
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; //推挽输出模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);

//选择时钟源——etr引脚的外部时钟
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F); //内部时钟配置

//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10 - 1; //满10进位
TIM_TimeBaseInitStruct.TIM_Prescaler = 2 - 1; //触发两次记一次中断
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);


//配置输出中断控制
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断


//配置nvic
/*中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置

NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设


//运行控制并使能计数器
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行

}

uint16_t Num; //定义在定时器中断里自增的变量
/**
* 函 数:TIM2中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
Num ++; //Num变量自增,用于测试定时中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}

/**
* 函 数:返回定时器CNT的值
* 参 数:无
* 返 回 值:定时器CNT的值,范围:0~65535
*/
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2); //返回定时器TIM2的CNT
}

int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Timer_Init(); //定时中断初始化

/*显示静态字符串*/
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
OLED_ShowString(2, 1, "CNT:"); //2行1列显示字符串CNT:

while (1)
{
OLED_ShowNum(1, 5, Num, 5); //不断刷新显示Num变量
OLED_ShowNum(2, 5, Timer_GetCounter(), 5); //不断刷新显示CNT的值
}
}

输出比较波形——输出pwm波形