目录
1.LED灯初始化
//led.h
#ifndef _LED_H
#define _LED_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 引脚 定义 */
#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_PIN GPIO_PIN_5
#define LED0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */
#define LED1_GPIO_PORT GPIOE
#define LED1_GPIO_PIN GPIO_PIN_5
#define LED1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
/******************************************************************************************/
/* LED端口定义 */
#define LED0(x) do{ x ? \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED0翻转 */
#define LED1(x) do{ x ? \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED1翻转 */
/* LED取反定义 */
#define LED0_TOGGLE() do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0) /* 翻转LED0 */
#define LED1_TOGGLE() do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0) /* 翻转LED1 */
/******************************************************************************************/
/* 外部接口函数*/
void led_init(void); /* 初始化 */
#endif
//led.c
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
LED0_GPIO_CLK_ENABLE(); /* LED0时钟使能 */
LED1_GPIO_CLK_ENABLE(); /* LED1时钟使能 */
gpio_init_struct.Pin = LED0_GPIO_PIN; /* LED0引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct); /* 初始化LED0引脚 */
gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1引脚 */
HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化LED1引脚 */
LED0(1); /* 关闭 LED0 */
LED1(1); /* 关闭 LED1 */
}
在led初始化中需要使能对应GPIO的时钟,设置GPIO的模式推完输出,默认上拉。
2.LCD初始化
由于屏和主板都是使用正点原子的产品,引脚,屏的资源已经固定,所以采用正点原子的源码略做修改,大家有需要可以去看正点原子的资料。
3.按键初始化
//key.h
#ifndef __KEY_H
#define __KEY_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 引脚 定义 */
#define KEY0_GPIO_PORT GPIOE
#define KEY0_GPIO_PIN GPIO_PIN_4
#define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY1_GPIO_PORT GPIOE
#define KEY1_GPIO_PIN GPIO_PIN_3
#define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY2_GPIO_PORT GPIOE
#define KEY2_GPIO_PIN GPIO_PIN_2
#define KEY2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define WKUP_GPIO_PORT GPIOA
#define WKUP_GPIO_PIN GPIO_PIN_0
#define WKUP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
/******************************************************************************************/
#define KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) /* 读取KEY0引脚 */
#define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) /* 读取KEY1引脚 */
#define KEY2 HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) /* 读取KEY2引脚 */
#define WK_UP HAL_GPIO_ReadPin(WKUP_GPIO_PORT, WKUP_GPIO_PIN) /* 读取WKUP引脚 */
#define KEY0_PRES 1 /* KEY0按下 */
#define KEY1_PRES 2 /* KEY1按下 */
#define KEY2_PRES 3 /* KEY2按下 */
#define WKUP_PRES 4 /* KEY_UP按下(即WK_UP) */
void key_init(void); /* 按键初始化函数 */
uint8_t key_scan(uint8_t mode); /* 按键扫描函数 */
#endif
//key.c
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
/**
* @brief 按键初始化函数
* @param 无
* @retval 无
*/
void key_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */
KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */
KEY2_GPIO_CLK_ENABLE(); /* KEY2时钟使能 */
WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */
gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */
gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */
gpio_init_struct.Pin = KEY2_GPIO_PIN; /* KEY2引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY2_GPIO_PORT, &gpio_init_struct); /* KEY2引脚模式设置,上拉输入 */
gpio_init_struct.Pin = WKUP_GPIO_PIN; /* WKUP引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP引脚模式设置,下拉输入 */
}
uint8_t key_scan(uint8_t mode)
{
static uint8_t key_up = 1; /* 按键按松开标志 */
uint8_t keyval = 0;
if (mode) key_up = 1; /* 支持连按 */
if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WK_UP == 1)) /* 按键松开标志为1, 且有任意一个按键按下了 */
{
delay_ms(10); /* 去抖动 */
key_up = 0;
if (KEY0 == 0) keyval = KEY0_PRES;
if (KEY1 == 0) keyval = KEY1_PRES;
if (KEY2 == 0) keyval = KEY2_PRES;
if (WK_UP == 1) keyval = WKUP_PRES;
}
else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */
{
key_up = 1;
}
return keyval; /* 返回键值 */
}
使能按键的GPIO对应时钟,设置按键对应模式,采用扫描式,改成外部中断可能会更好。
4.蜂鸣器初始化
#ifndef __BEEP_H
#define __BEEP_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 引脚 定义 */
#define BEEP_GPIO_PORT GPIOB
#define BEEP_GPIO_PIN GPIO_PIN_8
#define BEEP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */
/******************************************************************************************/
/* 蜂鸣器控制 */
#define BEEP(x) do{ x ? \
HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_RESET); \
}while(0)
/* BEEP状态翻转 */
#define BEEP_TOGGLE() do{ HAL_GPIO_TogglePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN); }while(0) /* BEEP = !BEEP */
void beep_init(void); /* 初始化蜂鸣器 */
#endif
#include "./BSP/BEEP/beep.h"
/**
* @brief 初始化BEEP相关IO口, 并使能时钟
* @param 无
* @retval 无
*/
void beep_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
BEEP_GPIO_CLK_ENABLE(); /* BEEP时钟使能 */
gpio_init_struct.Pin = BEEP_GPIO_PIN; /* 蜂鸣器引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(BEEP_GPIO_PORT, &gpio_init_struct); /* 初始化蜂鸣器引脚 */
BEEP(0); /* 关闭蜂鸣器 */
}
LCD灯,按键,蜂鸣器本质都是GPIO所以代码类似。蜂鸣器采用的是有源蜂鸣器,只有响和不响两种状态。
5.rtc初始化
uint8_t rtc_init(void)
{
/* 检查是不是第一次配置时钟 */
uint16_t bkpflag = 0;
__HAL_RCC_PWR_CLK_ENABLE(); /* 使能电源时钟 */
__HAL_RCC_BKP_CLK_ENABLE(); /* 使能备份时钟 */
HAL_PWR_EnableBkUpAccess(); /* 取消备份区写保护 */
bkpflag = rtc_read_bkr(0); /* 读取BKP0的值 */
g_rtc_handle.Instance = RTC;
g_rtc_handle.Init.AsynchPrediv = RTC_AUTO_1_SECOND; /*时钟周期设置,理论值:32767, 这里也可以用 RTC_AUTO_1_SECOND */
g_rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
if (HAL_RTC_Init(&g_rtc_handle) != HAL_OK)
{
return 1;
}
if ((bkpflag != 0X5050) && (bkpflag != 0x5051)) /* 之前未初始化过,重新配置 */
{
rtc_set_time(2023, 4, 5, 10, 25, 35); /* 设置时间 */
}
//HAL_RTC_Init
__HAL_RTC_ALARM_ENABLE_IT(&g_rtc_handle, RTC_IT_SEC); /* 允许秒中断 */
HAL_NVIC_SetPriority(RTC_IRQn, 0x2, 0); /* 优先级设置 */
HAL_NVIC_EnableIRQ(RTC_IRQn); /* 使能RTC中断通道 */
rtc_get_time(); /* 更新时间 */
return 0;
}
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
uint16_t retry = 200;
__HAL_RCC_RTC_ENABLE(); /* RTC时钟使能 */
RCC_OscInitTypeDef rcc_oscinitstruct;
RCC_PeriphCLKInitTypeDef rcc_periphclkinitstruct;
/* 使用寄存器的方式去检测LSE是否可以正常工作 */
RCC->BDCR |= 1 << 0; /* 开启外部低速振荡器LSE */
while (retry && ((RCC->BDCR & 0X02) == 0)) /* 等待LSE准备好 */
{
retry--;
delay_ms(5);
}
if (retry == 0) /* LSE起振失败 使用LSI */
{
rcc_oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_LSI; /* 选择要配置的振荡器 */
rcc_oscinitstruct.LSEState = RCC_LSI_ON; /* LSI状态:开启 */
rcc_oscinitstruct.PLL.PLLState = RCC_PLL_NONE; /* PLL无配置 */
HAL_RCC_OscConfig(&rcc_oscinitstruct); /* 配置设置的rcc_oscinitstruct */
rcc_periphclkinitstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; /* 选择要配置的外设 RTC */
rcc_periphclkinitstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; /* RTC时钟源选择 LSI */
HAL_RCCEx_PeriphCLKConfig(&rcc_periphclkinitstruct); /* 配置设置的rcc_periphClkInitStruct */
rtc_write_bkr(0, 0X5051);
}
else
{
rcc_oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE ; /* 选择要配置的振荡器 */
rcc_oscinitstruct.LSEState = RCC_LSE_ON; /* LSE状态:开启 */
rcc_oscinitstruct.PLL.PLLState = RCC_PLL_NONE; /* PLL不配置 */
HAL_RCC_OscConfig(&rcc_oscinitstruct); /* 配置设置的rcc_oscinitstruct */
rcc_periphclkinitstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; /* 选择要配置外设 RTC */
rcc_periphclkinitstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; /* RTC时钟源选择LSE */
HAL_RCCEx_PeriphCLKConfig(&rcc_periphclkinitstruct); /* 配置设置的rcc_periphclkinitstruct */
rtc_write_bkr(0, 0X5050);
}
}
void RTC_IRQHandler(void)
{
if (__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_SEC) != RESET) /* 秒中断 */
{
rtc_get_time(); /* 更新时间 */
__HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_SEC); /* 清除秒中断 */
// printf("sec:%d\r\n", calendar.sec); /* 打印秒钟 */
}
/* 顺带处理闹钟标志 */
if (__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_ALRAF) != RESET) /* 闹钟标志 */
{
__HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_ALRAF); /* 清除闹钟标志 */
// rtc_get_time();
// time_arr[4]=calendar.hour;
// time_arr[5]=calendar.min;
//Create_show_medicinfo();
// printf("zd1\r\n");
Alar_listNum=Alerm_node->list;
BEEP(1);
EatFlag=1;
Shoe_medicinfiFlag=1;
// __HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RESET);
Chinge_alarm();
// printf("Alarm Time:%d-%d-%d %d:%d:%d\n", calendar.year, calendar.month, calendar.date, calendar.hour, calendar.min, calendar.sec);
}
__HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_OW); /* 清除溢出中断标志 */
while (!__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_RTOFF)); /* 等待RTC寄存器操作完成, 即等待RTOFF == 1 */
// printf("zd2\r\n");
}
rtc实时时钟:STM32RTC是STM32系列微控制器中的实时时钟模块,它可以提供精确的时间和日期信息。该模块可以通过外部低速晶振或者内部LSI时钟源来提供时钟信号。同时,它还可以提供闹钟功能和周期性唤醒功能。
rtc初始化需要先使能时钟,这里默认使用外部低速振荡器,当系统下电时可以转为内部时钟,保证时间正常。使能秒中断,实现每秒计数器加一,rtc时间的实现的本质就是对计数器的时间进行计算,系统时间为1970年1月1日0时0分0秒到当前时间的总秒数。使能闹钟中断,当闹钟中的值,和rtc秒的计数器的值一致时产生闹钟中断,处理闹钟过程。处理的闹钟过程包括更新下一个闹钟的信息,打开闹钟提示,更新标志位,其他任务通过获取标志位实现不同的功能。