//需要C语言基础
定时器理论
定时器就像是闹钟,当计时达到你设定的时间后执行你写的功能代码,在单片机中,一般我是将定时器当作第二线程来看的,因为他与主循环可以看作是同时运行又互不影响。注意,在定时器中不可写循环。
什么是定时器?
定时器通常是由计数器和一组控制寄存器组成,计数器通常用来计数,控制寄存器用来配置计数器的工作模式、计数范围、时钟源等参数。工作原理:设置一个值,计数器计数值不断累加,当达到预先设定的计数值时,就会触发中断或者输出一个脉冲信号。定时器的功能很强大,可以用来定时、计数、PWM产生、输入捕获或定时器中断等。
STM32L071KBUx定时的组成:低功耗定时器(LPTIM)、一个基本定时器、两个看门狗定时器和SysTick定时器。
下图4个可同步的通用计数器,
计数器分辨率:决定计数的范围,比如:16-bit(0-65535)
计数器类型:向上计数、向下计数、向上计数/向下计数
预分频系数:1-65536
捕捉/比较通道:PWM等。
定时器功能
要查看定时器是否有用,就要有明显的效果呈现,LED在之前的实验中已经做过了,查看原理图发现,板载继电器K1-LED与K2-LED分别对应PA11、PA12引脚,使用定时器1来控制这两个继电器的反转。
代码工程
创建工程
同理,我们先使用CubeMx创建工程,配置我们所需要模块。PA11与PA12配置为输出模式,在Timers中配置LPTIM1低功耗定时器,别忘了打开NVIC中断,工程创建过程中,我们需要配置系统时钟,我这里配置为24MHz。
代码
前面说我们将系统时钟配置为24MHz,定时器分频系数为128,那么计数器的时钟应该是24/128=187500hz,这时候将程序中的计数器值设置为18750,计数十次,那么刚好为1s。
在main中声明一个常量,计数器值为18750。
#define PERIOD 17850
在主函数初始化部分编写代码的启动函数。第一个参数是一个结构体,在lptim.c中有帮我们声明并初始化,第二个参数就是我们定义的终值,这个定时器以中断模式启动,从0x0001~0x493E(18750)反复执行,每到终值一次进入一次定时器中断。
/**
* @brief Start the Counter mode in interrupt mode.
* @param hlptim LPTIM handle
* @param Period Specifies the Autoreload value.
* This parameter must be a value between 0x0001 and 0xFFFF.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_LPTIM_Counter_Start_IT(LPTIM_HandleTypeDef *hlptim, uint32_t Period);
HAL_LPTIM_Counter_Start_IT(&hlptim1,PERIOD);
在启动文件中找到用week声明的中断函数入口,将其重新声明在main.c中,在实际操作中,我们发现他已经在stm32l0xx_it.c中声明过了,使用go to可以找到其位置,如果你想写在main.c中可以和我一样,将其注释并重申。
将.c与.h文件中的相关代码注释,也可以直接把功能写在这里。
在main.c中重写该函数并使用
每一次定时器溢出为0.1s,这里c=3,每0.3s切换一次继电器状态
int c=0;
void LPTIM1_IRQHandler(void)
{
/* USER CODE BEGIN LPTIM1_IRQn 0 */
if(++c>3)
{
c=0;
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12);
}
/* USER CODE END LPTIM1_IRQn 0 */
HAL_LPTIM_IRQHandler(&hlptim1);
/* USER CODE BEGIN LPTIM1_IRQn 1 */
/* USER CODE END LPTIM1_IRQn 1 */
}
最后
如果你是新手,不知道这些函数怎么来的,那么久按照我图片里的代码将其函数名完整敲出来,再使用Ctrl+F查找或者goto功能找,找得多了后面自然就知道了。
这个简单的实验就做完了,由于是用CubeMx直接创建的工程,跳过了代码初始化步骤,这对于不想了解代码意思的人很友好,会用就行,但是我还是要了解一下的。
代码详解
gpio.c
在工程创建时,我们直接用鼠标配置了引脚为输出模式,最后生成的初始化函数如下,
首先实例化结构体,在结构体中有pin、mode、pull、speed4个需要配置。
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);
/*Configure GPIO pins : PA11 PA12 */
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
顾名思义:
Pin:引脚,代表你要配置的具体引脚,多个引脚用|分割,整组引脚可使用all来选择。
GPIO_PIN_All
Mode:一般理解为模式,引脚可配置模式如下,常用的有GPIO_MODE_INPUT输入模式,GPIO_MODE_OUTPUT_PP推挽输出模式,(MODE_INPUT | EXTI_IT | TRIGGER_RISING | TRIGGER_FALLING)外部中断的上升沿、下降沿、浮空模式。
/** @defgroup GPIO_mode_define Mode definition
* @brief GPIO Configuration Mode
* Elements values convention: 0x00WX00YZ
* - W : EXTI trigger detection on 3 bits
* - X : EXTI mode (IT or Event) on 2 bits
* - Y : Output type (Push Pull or Open Drain) on 1 bit
* - Z : GPIO mode (Input, Output, Alternate or Analog) on 2 bits
* @{
*/
#define GPIO_MODE_INPUT MODE_INPUT /*!< Input Floating Mode */
#define GPIO_MODE_OUTPUT_PP (MODE_OUTPUT | OUTPUT_PP) /*!< Output Push Pull Mode */
#define GPIO_MODE_OUTPUT_OD (MODE_OUTPUT | OUTPUT_OD) /*!< Output Open Drain Mode */
#define GPIO_MODE_AF_PP (MODE_AF | OUTPUT_PP) /*!< Alternate Function Push Pull Mode */
#define GPIO_MODE_AF_OD (MODE_AF | OUTPUT_OD) /*!< Alternate Function Open Drain Mode */
#define GPIO_MODE_ANALOG MODE_ANALOG /*!< Analog Mode */
#define GPIO_MODE_IT_RISING (MODE_INPUT | EXTI_IT | TRIGGER_RISING) /*!< External Interrupt Mode with Rising edge trigger detection */
#define GPIO_MODE_IT_FALLING (MODE_INPUT | EXTI_IT | TRIGGER_FALLING) /*!< External Interrupt Mode with Falling edge trigger detection */
#define GPIO_MODE_IT_RISING_FALLING (MODE_INPUT | EXTI_IT | TRIGGER_RISING | TRIGGER_FALLING) /*!< External Interrupt Mode with Rising/Falling edge trigger detection */
#define GPIO_MODE_EVT_RISING (MODE_INPUT | EXTI_EVT | TRIGGER_RISING) /*!< External Event Mode with Rising edge trigger detection */
#define GPIO_MODE_EVT_FALLING (MODE_INPUT | EXTI_EVT | TRIGGER_FALLING) /*!< External Event Mode with Falling edge trigger detection */
#define GPIO_MODE_EVT_RISING_FALLING (MODE_INPUT | EXTI_EVT | TRIGGER_RISING | TRIGGER_FALLING) /*!< External Event Mode with Rising/Falling edge trigger detection */
Pull:上拉、下拉、不上也不下,理解为浮空。
/** @defgroup GPIO_pull_define Pull definition
* @brief GPIO Pull-Up or Pull-Down Activation
* @{
*/
#define GPIO_NOPULL (0x00000000U) /*!< No Pull-up or Pull-down activation */
#define GPIO_PULLUP (0x00000001U) /*!< Pull-up activation */
#define GPIO_PULLDOWN (0x00000002U) /*!< Pull-down activation */
Speed:速度。GPIO的输入输出速度,看注释的xMHz大小就好了,一般默认选择HIGH,
/** @defgroup GPIO_speed_define Speed definition
* @brief GPIO Output Maximum frequency
* @{
*/
#define GPIO_SPEED_FREQ_LOW (0x00000000U) /*!< range up to 0.4 MHz, please refer to the product datasheet */
#define GPIO_SPEED_FREQ_MEDIUM (0x00000001U) /*!< range 0.4 MHz to 2 MHz, please refer to the product datasheet */
#define GPIO_SPEED_FREQ_HIGH (0x00000002U) /*!< range 2 MHz to 10 MHz, please refer to the product datasheet */
#define GPIO_SPEED_FREQ_VERY_HIGH (0x00000003U) /*!< range 10 MHz to 35 MHz, please refer to the product datasheet */
lptim.c
再来看低功耗定时器,初始化函数如下。同样的,第一行我们理解为实例化结构体,再对结构体里的内容进行一一配置。在下方逐行解释,由于这部分源代码太多,感兴趣的可以使用goto去查看源代码与注释,配合手册效果更佳。
LPTIM_HandleTypeDef hlptim1;
void MX_LPTIM1_Init(void)
{
hlptim1.Instance = LPTIM1;
hlptim1.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
hlptim1.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV128;
hlptim1.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
hlptim1.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
hlptim1.Init.UpdateMode = LPTIM_UPDATE_ENDOFPERIOD;
hlptim1.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
if (HAL_LPTIM_Init(&hlptim1) != HAL_OK)
{
Error_Handler();
}
}
-
hlptim1.Instance = LPTIM1;
:指定使用的定时器实例为LPTIM1。。 -
hlptim1.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
:设置定时器时钟源为APB时钟和LPOSC(低功耗振荡器)。 -
hlptim1.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV128;
:设置时钟预分频器为128,这意味着输入到定时器的时钟信号将被分频为原始频率的1/128。 -
hlptim1.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
:设置定时器触发源为软件触发,也就是通过软件命令来触发定时器开始计数。 -
hlptim1.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
:设置定时器输出极性为高电平。 -
hlptim1.Init.UpdateMode = LPTIM_UPDATE_ENDOFPERIOD;
:设置定时器更新模式为在计时周期结束时更新。 -
hlptim1.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
:设置定时器计数源为内部计数器。