当前位置:网站首页>潘多拉 IOT 开发板学习(HAL 库)—— 实验8 定时器中断实验(学习笔记)
潘多拉 IOT 开发板学习(HAL 库)—— 实验8 定时器中断实验(学习笔记)
2022-07-05 18:39:00 【小辉_Super】
本文代码参考正点原子例程
实验功能
例程源码:(main.c)
main() 中只有 TIM3 的初始化函数,定时器中断相关代码在定时器中断服务(回调)函数中。
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "timer.h"
/********************************************************************************* ___ _ _____ _____ _ _ _____ _____ _ __ / _ \ | | |_ _|| ___|| \ | ||_ _|| ___|| | / / / /_\ \| | | | | |__ | \| | | | | |__ | |/ / | _ || | | | | __| | . ` | | | | __| | \ | | | || |_____| |_ | |___ | |\ | | | | |___ | |\ \ \_| |_/\_____/\___/ \____/ \_| \_/ \_/ \____/ \_| \_/ * ****************************************************************************** * 正点原子 Pandora STM32L475 IoT开发板 实验8 * 定时器中断实验 HAL库版本 * 技术支持:www.openedv.com * 淘宝店铺:http://openedv.taobao.com * 关注微信公众平台微信号:"正点原子",免费获取STM32资料。 * 广州市星翼电子科技有限公司 * 作者:正点原子 @ALIENTEK * ******************************************************************************/
int main(void)
{
HAL_Init();
SystemClock_Config(); //初始化系统时钟为80M
delay_init(80); //初始化延时函数 80M系统时钟
uart_init(115200); //初始化串口,波特率为115200
LED_Init(); //初始化LED
TIM3_Init(5000 - 1, 8000 - 1); //定时器3初始化,定时器时钟为80M,分频系数为8000-1,
//所以定时器3的频率为80M/8000=10K,自动重装载为5000-1,那么定时器周期就是500ms
while(1)
{
LED_R_TogglePin; //提示程序运行情况
delay_ms(100);
}
}
代码剖析
HAL_Init()
HAL_Init()
定义如下:(具体实现的功能见注释)
HAL_StatusTypeDef HAL_Init(void)
{
HAL_StatusTypeDef status = HAL_OK;
/* 配置 Flash 预取,指令缓存,数据缓存 */
/* 默认配置为:预存取关闭 指令缓存和数据缓存开启 */
#if (INSTRUCTION_CACHE_ENABLE == 0) // Flash开启预存取配置,能加速CPU代码的执行
__HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */
#if (DATA_CACHE_ENABLE == 0)
__HAL_FLASH_DATA_CACHE_DISABLE();
#endif /* DATA_CACHE_ENABLE */
#if (PREFETCH_ENABLE != 0)
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); // 配置 NVIC 优先级分组
/* Use SysTick as time base source and configure 1ms tick (default clock after Reset is MSI) */
if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) //初始化滴答定时器,时钟节拍设置为 1ms
{
status = HAL_ERROR;
}
else
{
/* Init the low level hardware */
HAL_MspInit(); // 低速的外设初始化,比如 GPIO、中断等的设置(使用 STM32CubeMx 生成代码时会将低速外设初始
// 代码当这类函数里,其他情况下可以忽略这个函数
}
/* Return function status */
return status;
}
HAL_InitTick()
滴答定时器时钟节拍初始化函数
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
HAL_StatusTypeDef status = HAL_OK;
/*Configure the SysTick to have interrupt in 1ms time basis*/
if (HAL_SYSTICK_Config(SystemCoreClock/1000UL) != 0U) // 系统时钟/1000,中断周期为 1ms
{
status = HAL_ERROR;
}
else
{
/*Configure the SysTick IRQ priority */
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0); // 将滴答定时器的中断优先级设置为最高
}
/* Return function status */
return status;
}
SystemClock_Config()
SystemClock_Config()
函数定义如下:(具体实现的功能见注释,仅供参考)
void SystemClock_Config(void)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStruct; // 定义振荡器初始化结构体变量
RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义时钟初始化结构体变量
__HAL_RCC_PWR_CLK_ENABLE(); // 使能电源控制时钟
/*Initializes the CPU, AHB and APB busses clocks*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 将 HSE(外部高速时钟)作为时钟源
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 开启 HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启 PLL(锁相环)
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 将 HSE 作为 PLL 的时钟源
RCC_OscInitStruct.PLL.PLLM = 1; // PLL-VCO 输入时钟分频系数,1 表示 2 分频(8 / 2 = 4M,本开发板外部晶振频率为 8MHz)
RCC_OscInitStruct.PLL.PLLN = 20; // PLL-VCO 输出时钟倍频系数,4 * 20 = 80M,即输出时钟频率为 80MHz
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI 时钟的分频系数
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC1, RNG 和 USB 的时钟分频系数
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 主系统时钟的分频系数
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); //初始化时钟配置
if(ret != HAL_OK) while(1);
/*Initializes the CPU, AHB and APB busses clocks*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; // 将所有时钟同时进行配置
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 将 PLL 作为系统时钟源
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB 不分频
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1 不分频
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 不分频
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); // 配置时钟初始结构体变量,
//使用 Flash 延迟4,等待状态(延迟)的数量需要根据CPU时钟(HCLK)的频率和内部电压范围来选择,具体怎么
//选需要参考芯片手册
if(ret != HAL_OK) while(1);
/*Configure the main internal regulator output voltage*/
ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); //内部寄存器输出电压配置
// 下面是 HAL_PWREx_ControlVoltageScaling() 函数说明的部分内容:
//PWR_REGULATOR_VOLTAGE_SCALE1 Regulator voltage output range 1 mode, typical output voltage
// at 1.2 V, system frequency up to 80 MHz.
if(ret != HAL_OK) while(1);
}
delay_init()
滴答定时器已经在 HAL_Init()
中进行了初始化,下面这个函数实际上就是给 fac_us
赋了一个值(目前暂不涉及操作系统,其他代码暂时不去研究)。
static u32 fac_us = 0; //us延时倍乘数
/** * @brief 初始化延迟函数,SYSTICK的时钟固定为AHB时钟 * * @param SYSCLK 系统时钟频率 * * @return void */
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us = SYSCLK; //不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload = SYSCLK; //每秒钟的计数次数 单位为K
reload *= 1000000 / delay_ostickspersec; //根据delay_ostickspersec设定溢出时间
//reload为24位寄存器,最大值:16777216,在80M下,约209.7ms左右
fac_ms = 1000 / delay_ostickspersec; //代表OS可以延时的最少单位
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD = reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
#endif
}
LED_Init()
/** * @brief LED IO初始化函数 * * @param void * * @return void */
void LED_Init(void)
{
/* LED-B PE9 LED-G PE8 LED-R PE7 */
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
}
TIM3_Init()
本实验使用 TIM3 来计时,下面是 TIM3 的初始化函数,TIM3 的时钟频率为 80MHz,main() 函数中分频系数为 8000 - 1,800000000 / 8000 = 10000Hz,对应的计时周期就是 100us;重载值为 5000 - 1,那么定时器计时溢出中断的触发周期就是 500ms。
/** * @brief 通用定时器3中断初始化函数 * 定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us. * Ft=定时器工作频率,单位:Mhz * 这里使用的是定时器3!(定时器3挂在APB1上,时钟为HCLK:80Mhz) * * @param arr 自动重装值。 * @param psc 时钟预分频数 * * @return void */
void TIM3_Init(u16 arr, u16 psc)
{
TIM3_Handler.Instance = TIM3; //通用定时器3
TIM3_Handler.Init.Prescaler = psc; //分频系数
TIM3_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数器
TIM3_Handler.Init.Period = arr; //自动装载值
TIM3_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;//时钟分频因子
HAL_TIM_Base_Init(&TIM3_Handler);
HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE
}
HAL_TIM_Base_MspInit()
上面的 HAL_TIM_Base_Init()
函数会调用定时器的底层驱动初始化函数,该函数中配置了定时器的中断属性。
/** * @brief 定时器底层驱动,开启时钟,设置中断优先级 * 此函数会被HAL_TIM_Base_Init()函数调用 * * @param htim 定时器句柄 * * @return void */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE(); //使能TIM3时钟
HAL_NVIC_SetPriority(TIM3_IRQn, 1, 3); //设置中断优先级,抢占优先级1,子优先级3
HAL_NVIC_EnableIRQ(TIM3_IRQn); //开启ITM3中断
}
}
LED 操作函数
LED 的控制函数是宏函数,分别用到了 HAL_GPIO_WritePin()
和 HAL_GPIO_TogglePin()
两个库函数。
//RGB接口定义
#define LED_R(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_RESET))
#define LED_R_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_7) //LED_R电平翻转
#define LED_G(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_RESET))
#define LED_G_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_8) //LED_G电平翻转
#define LED_B(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_RESET))
#define LED_B_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_9) //LED_B电平翻转
delay_ms()
delay_ms()
里运行的是 delay_us()
, delay_us()
通过滴答定时器实现延时。上面的 delay_init()
已经将 fac_us 设置为了 80,滴答定时器计数 80 次需要用 10-6 秒(系统时钟为 80MHz),即 1us。
/** * @brief 延时毫秒(ms)函数 * * @param nms 需要延时多少毫秒 * * @return void */
void delay_ms(u16 nms)
{
u32 i;
for(i = 0; i < nms; i++) delay_us(1000);
}
/** * @brief 延时微秒(us)函数 * * @remark nus:0~190887435(最大值即2^32/[email protected]_us=22.5) * * @param nus 需要延时多少微秒 * * @return void */
void delay_us(u32 nus)
{
u32 ticks;
u32 told, tnow, tcnt = 0;
u32 reload = SysTick->LOAD; //LOAD的值
ticks = nus * fac_us; //需要的节拍数
told = SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow = SysTick->VAL;
if(tnow != told)
{
if(tnow < told)tcnt += told - tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt += reload - tnow + told;
told = tnow;
if(tcnt >= ticks)break; //时间超过/等于要延迟的时间,则退出.
}
}
}
中断服务函数
在底层中断服务函数中调用 HAL 库的中断服务函数。
/** * @brief 定时器3中断服务函数 * * @param void * * @return void */
void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM3_Handler);
}
HAL_TIM_IRQHandler()
里会运行一些中断回调函数,其中就包括 HAL_TIM_PeriodElapsedCallback()
定时器计数溢出中断回调函数,
/** * @brief 回调函数,定时器中断服务函数调用 * * @param htim 定时器句柄 * * @return void */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == (&TIM3_Handler))
{
LED_B_TogglePin; //LED_B翻转
}
}
边栏推荐
- Find in MySQL_ in_ Detailed explanation of set() function usage
- c语言简便实现链表增删改查「建议收藏」
- Case sharing | integrated construction of data operation and maintenance in the financial industry
- Isprs2020/ cloud detection: transferring deep learning models for cloud detection between landsat-8 and proba-v
- 7-1 linked list is also simple fina
- Reading notes of Clickhouse principle analysis and Application Practice (5)
- 2022 Alibaba Android advanced interview questions sharing, 2022 Alibaba hand Taobao Android interview questions
- RPC协议详解
- You can have both fish and bear's paw! Sky wing cloud elastic bare metal is attractive!
- 2022最新Android面试笔试,一个安卓程序员的面试心得
猜你喜欢
FCN: Fully Convolutional Networks for Semantic Segmentation
《ClickHouse原理解析与应用实践》读书笔记(5)
ICML2022 | 长尾识别中分布外检测的部分和非对称对比学习
鱼和熊掌可以兼得!天翼云弹性裸金属一招鲜!
Trust counts the number of occurrences of words in the file
@Extension, @spi annotation principle
集合处理的利器
Overview of video self supervised learning
The road of enterprise digital transformation starts from here
彻底理解为什么网络 I/O 会被阻塞?
随机推荐
【历史上的今天】7 月 5 日:Google 之母出生;同一天诞生的两位图灵奖先驱
Lombok @builder annotation
How much does the mlperf list weigh when AI is named?
Word finds red text word finds color font word finds highlighted formatted text
C language makes it easy to add, delete, modify and check the linked list "suggested collection"
Memory leak of viewpager + recyclerview
Is the performance evaluation of suppliers in the fastener industry cumbersome? Choose the right tool to easily counter attack!
Use QT to traverse JSON documents and search sub objects
R语言使用lubridate包处理日期和时间数据实战
Summary of six points of MySQL optimization
AI Open2022|基于异质信息网络的推荐系统综述:概念,方法,应用与资源
The main thread anr exception is caused by too many binder development threads
CDB 实例的启动与关闭
Reading notes of Clickhouse principle analysis and Application Practice (5)
2022全网最全的腾讯后台自动化测试与持续部署实践【万字长文】
Postman核心功能解析 —— 参数化和测试报告
紧固件行业供应商绩效考核繁琐?选对工具才能轻松逆袭!
面试官:Redis 过期删除策略和内存淘汰策略有什么区别?
华律网牵手观测云,上线系统全链路可观测平台
如何快速进阶自动化测试?听听这3位BAT大厂测试工程师的切身感想....