目录
实验目的
学习利用CubeMX创建HAL库的工程
掌握定时器的基本原理
实验现象
使用stm32F1的TIM6定时器实现LED灯1hz的闪烁
实验设备
正点原子新战舰V3 STM32F103ZET6开发板学习板
st-link烧录器
定时器原理
F103定时器组成
STM32F103ZET6总共有8个定时器:
计数器类型 | 预分配系数 | 产生DMA | 捕获/比较通道 | 互补输出 | 计数器分辨率 | ||
---|---|---|---|---|---|---|---|
高级定时器 | TIM1 | 向上/向下 | 1-65535 | 可以 | 4 | 有 | 16位(2^16) |
TIM8 | 向上/向下 | 1-65535 | 可以 | 4 | 有 | 16位 | |
通用定时器 | TIM2 | 向上/向下 | 1-65535 | 可以 | 4 | 无 | 16位 |
TIM3 | 向上/向下 | 1-65535 | 可以 | 4 | 无 | 16位 | |
TIM4 | 向上/向下 | 1-65535 | 可以 | 4 | 无 | 16位 | |
TIM5 | 向上/向下 | 1-65535 | 可以 | 4 | 无 | 16位 | |
基本定时器 | TIM6 | 向上 | 1-65535 | 可以 | 0 | 无 | 16位 |
TIM7 | 向上 | 1-65535 | 可以 | 0 | 无 | 16位 |
总结:
TIM1和TIM8定时器特殊在有互补输出。高级定时器可以定时、输出比较、输入捕捉、还可以输出三相电机互补信号,每个高等定时器有8个外部IO口。
TIM6和TIM7基本定时器特殊在:基本定时器没有外部IO口,只能向上计数定时产生中断/DMA请求,没有捕获/比较通道
通用定时器(TIM2-TIM5)可以定时、输出比较、输入捕捉,每个通用定时器具有4个外部IO口。
定时器时间计算
基本定时器的计数次数由自动重装载寄存器决定的,基本定时器的计数器从0开始向上计数(计数模式为向上计数时),当计数器的值与自动重装载寄存器相等时,产生溢出。所以基本定时器的溢出时间计算公式如下:
T
i
m
e
=
(
P
S
C
+
1
)
∗
(
A
R
R
)
/
T
I
M
x
C
L
K
(
u
s
)
Time = (PSC+1)*(ARR)/ TIMxCLK(us)
Time=(PSC+1)∗(ARR)/TIMxCLK(us)
PSC是定时器的分频系数,TIMxCLK是内部时钟。
ARR是自动重装载寄存器的值(既计数多少产生一次溢出)。
假设基本定时器TIMxCLK = 72MHZ,PSC = 72-1,ARR = 1000,那么定时器的溢出时间为:
T
i
m
e
=
72
∗
1000
/
72
=
1000
(
u
s
)
=
1
(
m
s
)
Time = 72*1000/72 = 1000(us) = 1(ms)
Time=72∗1000/72=1000(us)=1(ms)
具体步骤
1.RCC时钟源配置
2.时钟树配置
3.GPIO配置,LED0对应PB5引脚
4.TIM6配置与中断使能
对应的时间为
T i m e = 7200 ∗ 5000 / 72 = 500 ( m s ) = 0.5 ( s ) ; 周 期 T = 1 s ; 频 率 f = 1 h z . Time = 7200*5000/72 = 500(ms)=0.5(s); 周期T=1s ; 频率f=1hz. Time=7200∗5000/72=500(ms)=0.5(s);周期T=1s;频率f=1hz.
5.项目配置
代码编写
1.CubeMX 生成的main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init(); //GPIO初始化,,函数definition在gpio.c
MX_TIM6_Init(); //Tim6定时器初始化,函数definition在tim.c
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
2.CubeMX 生成的tim.c
#include "tim.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
TIM_HandleTypeDef htim6;
/* TIM6 init function */
void MX_TIM6_Init(void) //定时器初始化函数
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim6.Instance = TIM6;
htim6.Init.Prescaler = 7200-1; //定时器预分频系数
htim6.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数模式
htim6.Init.Period = 5000-1; //自动重装载值
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; //自动重装功能使能
if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) //中断设置
{
if(tim_baseHandle->Instance==TIM6)
{
/* USER CODE BEGIN TIM6_MspInit 0 */
/* USER CODE END TIM6_MspInit 0 */
/* TIM6 clock enable */
__HAL_RCC_TIM6_CLK_ENABLE();
/* TIM6 interrupt Init */
HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0); //中断NVIC优先级设置为0,0
HAL_NVIC_EnableIRQ(TIM6_IRQn); //使能定时器6的中断服务
/* USER CODE BEGIN TIM6_MspInit 1 */
/* USER CODE END TIM6_MspInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle) //中断挂起函数
{
if(tim_baseHandle->Instance==TIM6)
{
/* USER CODE BEGIN TIM6_MspDeInit 0 */
/* USER CODE END TIM6_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM6_CLK_DISABLE();
/* TIM6 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM6_IRQn);
/* USER CODE BEGIN TIM6_MspDeInit 1 */
/* USER CODE END TIM6_MspDeInit 1 */
}
}
3.查看stm32f1xx_it.c文件,找到中断服务函数
void TIM6_IRQHandler(void)
{
/* USER CODE BEGIN TIM6_IRQn 0 */
/* USER CODE END TIM6_IRQn 0 */
HAL_TIM_IRQHandler(&htim6);
/* USER CODE BEGIN TIM6_IRQn 1 */
/* USER CODE END TIM6_IRQn 1 */
}
这个函数调用了下面这个函数
HAL_TIM_IRQHandler(&htim6) ;
4.找到中断回调服务函数
进入HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)函数,这里面的代码很长,其实就是不同的中断类型,进入不同的中断回调函数,都属于虚函数,所以我们只要在 main.c 或者 tim.c 文件中使能中断并重写中断服务函数即可
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{
/* Capture compare 1 event */
if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET)
{
if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) != RESET)
{
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
/* Input capture event */
if ((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00U)
{
..............................................
5.使能定时器中断并编写中断回调服务函数
使能定时器中断
MX_GPIO_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6); //放在初始化之后
编写中断回调服务函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6)
{
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
}
}
对应的源程序压缩包可以前往https://download.csdn.net/download/weixin_46074226/20533195 下载