当前位置:网站首页>潘多拉 IOT 开发板学习(HAL 库)—— 实验6 独立看门狗实验(学习笔记)
潘多拉 IOT 开发板学习(HAL 库)—— 实验6 独立看门狗实验(学习笔记)
2022-07-04 03:32:00 【小辉_Super】
本文代码参考正点原子例程
文章目录
实验功能
例程源码:(main.c)
该实验通过按下 WK_UP 按键来喂狗,如果规定的时间期限内没有喂狗,单片机将重启。
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "iwdg.h"
/********************************************************************************* ___ _ _____ _____ _ _ _____ _____ _ __ / _ \ | | |_ _|| ___|| \ | ||_ _|| ___|| | / / / /_\ \| | | | | |__ | \| | | | | |__ | |/ / | _ || | | | | __| | . ` | | | | __| | \ | | | || |_____| |_ | |___ | |\ | | | | |___ | |\ \ \_| |_/\_____/\___/ \____/ \_| \_/ \_/ \____/ \_| \_/ * ****************************************************************************** * 正点原子 Pandora STM32L475 IoT开发板 实验6 * 独立看门狗实验 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
KEY_Init(); //初始化按键
delay_ms(100); //延时100ms再初始化看门狗,LED_B的变化"可见"
IWDG_Init(IWDG_PRESCALER_64, 500); //分频数为64,重载值为500,溢出时间为1s(4*2^4*500/32=1000ms)
LED_B(0);
while(1)
{
if(KEY_Scan(0) == WKUP_PRES) //如果WK_UP按下,喂狗
{
IWDG_Feed(); //喂狗
}
delay_ms(10);
}
}
代码剖析
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; // 定义一个GPIO初始化结构体变量
__HAL_RCC_GPIOE_CLK_ENABLE(); // 使能GPIOE的时钟
GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9; // 同时配置 3 个引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 默认上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 速度设为高速(25 MHz to 50 MHz)
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); // 初始化结构体变量
// 将 3 个引脚同时置高
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
}
KEY_Init()
/** * @brief 按键初始化函数 * * @param void * * @return void */
void KEY_Init(void)
{
/* KEY0 - PD10 KEY1 - PD9 KEY2 - PD8 WK_UP - PC13 */
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOD_CLK_ENABLE(); //开启GPIOD时钟
GPIO_Initure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 ; //PD8.9.10
GPIO_Initure.Mode = GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull = GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed = GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOD, &GPIO_Initure);
GPIO_Initure.Pin = GPIO_PIN_13; //PC13
GPIO_Initure.Mode = GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull = GPIO_PULLUP; //上拉
GPIO_Initure.Speed = GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOC, &GPIO_Initure);
}
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; //时间超过/等于要延迟的时间,则退出.
}
}
}
IWDG_Init()
独立看门狗的初始化很简单,毕竟是库函数(HAL 库)代码,无须我们关心底层寄存器操作代码。
下面有个配置选项 IWDG_WINDOW_DISABLE
我以前没见过,但貌似不是很重要。
// 全局变量
IWDG_HandleTypeDef IWDG_Handler; //独立看门狗句柄
/** * @brief 初始化独立看门狗函数 * 时间计算(大概):Tout=((4*2^prer)*rlr)/32 ms * * @param prer 分频数:IWDG_PRESCALER_4~IWDG_PRESCALER_256 * @param rlr 自动重装载值,0~0XFFF * * @return void */
void IWDG_Init(u8 prer, u16 rlr)
{
IWDG_Handler.Instance = IWDG;
IWDG_Handler.Init.Prescaler = prer; //设置IWDG分频系数
IWDG_Handler.Init.Reload = rlr; //重装载值
IWDG_Handler.Init.Window = IWDG_WINDOW_DISABLE;
HAL_IWDG_Init(&IWDG_Handler); //初始化IWDG并开启独立看门狗
}
看门狗的分频系数只能是下面这几种,
/** @defgroup IWDG_Prescaler IWDG Prescaler * @{ */
#define IWDG_PRESCALER_4 0x00000000u /*!< IWDG prescaler set to 4 */
#define IWDG_PRESCALER_8 IWDG_PR_PR_0 /*!< IWDG prescaler set to 8 */
#define IWDG_PRESCALER_16 IWDG_PR_PR_1 /*!< IWDG prescaler set to 16 */
#define IWDG_PRESCALER_32 (IWDG_PR_PR_1 | IWDG_PR_PR_0) /*!< IWDG prescaler set to 32 */
#define IWDG_PRESCALER_64 IWDG_PR_PR_2 /*!< IWDG prescaler set to 64 */
#define IWDG_PRESCALER_128 (IWDG_PR_PR_2 | IWDG_PR_PR_0) /*!< IWDG prescaler set to 128 */
#define IWDG_PRESCALER_256 (IWDG_PR_PR_2 | IWDG_PR_PR_1) /*!< IWDG prescaler set to 256 */
/** * @} */
独立看门狗溢出时间计算公式为 Tout=((4*2^prer)*rlr)/32(ms)
,分频系数 IWDG_PRESCALER_4~IWDG_PRESCALER_256
的真实值其实是0~6,所以当分频参数选择 IWDG_PRESCALER_64
,重载值填 500 时,独立看门狗溢出时间为 1s (4 * 24 * 500 / 32 = 1000ms)。
LED_B()
LED 的控制函数是宏函数,分别用到了 HAL_GPIO_WritePin()
和 HAL_GPIO_TogglePin()
两个库函数。
#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)
#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)
#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)
KEY_Scan()
按键的扫描最基本函数是下面这四个,即读取对应 IO 的电平状态,
#define KEY0 HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_10)
#define KEY1 HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_9)
#define KEY2 HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_8)
#define WK_UP HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)
下面这套按键扫描机制大家应该和熟悉,理解起来也很简单:
/** * @brief 按键处理函数 * * @remark 注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!! * * @param mode 0:不支持连续按,1:支持连续按 * * @return u8 返回按键值 * 0:没有任何按键按下,1:KEY0按下,2:KEY1按下,3:KEY2按下,4:WK_UP按下 */
u8 KEY_Scan(u8 mode)
{
static u8 key_up = 1; //按键松开标志
if(mode == 1)key_up = 1; //支持连按
if(key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WK_UP == 1))
{
delay_ms(10);
key_up = 0;
if(KEY0 == 0) return KEY0_PRES;
else if(KEY1 == 0) return KEY1_PRES;
else if(KEY2 == 0) return KEY2_PRES;
else if(WK_UP == 1) return WKUP_PRES;
}
else if(KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WK_UP == 0)key_up = 1;
return 0; //无按键按下
}
IWDG_Feed()
独立看门狗喂狗函数,其实就是将写寄存器操作进行了三层封装。HAL_IWDG_Refresh()
--> __HAL_IWDG_RELOAD_COUNTER()
--> __HAL_IWDG_RELOAD_COUNTER()
/** * @brief 喂独立看门狗 * * @param void * * @return void */
void IWDG_Feed(void)
{
HAL_IWDG_Refresh(&IWDG_Handler); //喂狗
}
/** * @brief Refresh the IWDG. * @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains * the configuration information for the specified IWDG module. * @retval HAL status */
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg)
{
/* Reload IWDG counter with value defined in the reload register */
__HAL_IWDG_RELOAD_COUNTER(hiwdg);
/* Return function status */
return HAL_OK;
}
#define __HAL_IWDG_RELOAD_COUNTER(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_RELOAD)
下面是独立看门狗几个基本的位掩码:
/** * @brief IWDG Key Register BitMask */
#define IWDG_KEY_RELOAD 0x0000AAAAu /*!< 喂狗 IWDG Reload Counter Enable */
#define IWDG_KEY_ENABLE 0x0000CCCCu /*!< 外设使能 IWDG Peripheral Enable */
#define IWDG_KEY_WRITE_ACCESS_ENABLE 0x00005555u /*!< 写使能 IWDG KR Write Access Enable */
#define IWDG_KEY_WRITE_ACCESS_DISABLE 0x00000000u /*!< 写失能 IWDG KR Write Access Disable */
边栏推荐
- How to use websocket to realize simple chat function in C #
- Solve the problems encountered by the laravel framework using mongodb
- Talking about custom conditions and handling errors in MySQL Foundation
- Bugku Zhi, you have to stop him
- Development of digital collection trading platform development of digital collection platform
- New year's first race, submit bug reward more!
- 2006 translation
- Solve the problem that the tabbar navigation at the bottom of vantui does not correspond to the page (window.loading.hash)
- Keepalived set the master not to recapture the VIP after fault recovery (it is invalid to solve nopreempt)
- Have you entered the workplace since the first 00???
猜你喜欢
Jenkins configures IP address access
Want to do something in production? Then try these redis commands
Hospital network planning and design document based on GLBP protocol + application form + task statement + opening report + interim examination + literature review + PPT + weekly progress + network to
Setting methods, usage methods and common usage scenarios of environment variables in postman
Tsinghua University product: penalty gradient norm improves generalization of deep learning model
Fudan released its first review paper on the construction and application of multimodal knowledge atlas, comprehensively describing the existing mmkg technology system and progress
The "message withdrawal" of a push message push, one click traceless message withdrawal makes the operation no longer difficult
Redis notes (I) Linux installation process of redis
Talking about custom conditions and handling errors in MySQL Foundation
@Scheduled scheduled tasks
随机推荐
The 37 year old programmer was laid off, and he didn't find a job for 120 days. He had no choice but to go to a small company. As a result, he was confused
Aperçu du code source futur - série juc
Stm32bug [stlink forced update prompt appears in keilmdk, but it cannot be updated]
Don't disagree, this is the most powerful "language" of the Internet
Third party login initial version
[database I] database overview, common commands, view the table structure of 'demo data', simple query, condition query, sorting data, data processing function (single row processing function), groupi
Want to do something in production? Then try these redis commands
Li Chuang EDA learning notes IX: layers
MySQL query
Fudan released its first review paper on the construction and application of multimodal knowledge atlas, comprehensively describing the existing mmkg technology system and progress
Session learning diary 1
Base d'apprentissage de la machine: sélection de fonctionnalités avec lasso
Contest3145 - the 37th game of 2021 freshman individual training match_ J: Eat radish
Audio and video technology development weekly | 232
查詢效率提昇10倍!3種優化方案,幫你解决MySQL深分頁問題
Backpropagation formula derivation [Li Hongyi deep learning version]
Short math guide for latex by Michael downs
Sword finger offer:55 - I. depth of binary tree
2022 registration examination for safety production management personnel of fireworks and firecracker production units and examination skills for safety production management personnel of fireworks an
Problems and solutions of several concurrent scenarios of redis