diff --git a/System/systick/systick.c b/System/systick/systick.c new file mode 100644 index 0000000..b8786e9 --- /dev/null +++ b/System/systick/systick.c @@ -0,0 +1,276 @@ +//SysTick 及软件定时器库 + +#include "key.h" +#include "led.h" +#include "Clock.h" + +#include "systick.h" + +static volatile uint32_t s_uiDelayCount = 0; +static volatile uint8_t s_ucTimeOutFlag = 0; +static SOFT_TMR s_tTmr[TMR_COUNT] = {0}; //软件定时器结构体变量 +volatile uint32_t g_iRunTime = 0; //全局运行时间,单位 ms +static volatile uint8_t g_ucEnableSystickISR = 0; //等待变量初始化 + +void SysTick_ISR(void); +static void SysTick_SoftTimerDec(SOFT_TMR *_tmr); + +/** + * @brief 配置 SysTick 中断,并初始化软件定时器 + */ +void SysTick_Init(void) +{ + //清零所有的软件定时器 + for (uint8_t i = 0; i < TMR_COUNT; i++) + { + s_tTmr[i].Count = 0; + s_tTmr[i].PreLoad = 0; + s_tTmr[i].Flag = 0; + s_tTmr[i].Mode = TMR_ONCE_MODE; //缺省是一次性工作模式 + } + + //配置 SysTick 中断周期为 1ms,并启动 SysTick 中断 + SysTick_Config(SystemCoreClock / 1000); + + g_ucEnableSystickISR = 1; //执行systick中断 +} + +void SysTick_ISR(void) +{ + static uint8_t s_count = 0; + static uint16_t l_count = 0; + + //用于 Delay_ms + if (s_uiDelayCount > 0) + if (--s_uiDelayCount == 0) + s_ucTimeOutFlag = 1; + + //软件定时器的计数器减 1 + for (uint8_t i = 0; i < TMR_COUNT; i++) + SysTick_SoftTimerDec(&s_tTmr[i]); + + //全局运行时间增 1 + g_iRunTime++; + if (g_iRunTime == UINT32_MAX) + g_iRunTime = 0; + + //每隔 1ms 调用一次 + + if (++s_count >= 10) + { + s_count = 0; + + //每隔 10ms 调用一次 + KEY_Scan10ms(); + } + + if (++l_count >= 1000) + { + l_count = 0; + + //每隔 1000ms 调用一次 + Clock_Handler(); + } +} + +static void SysTick_SoftTimerDec(SOFT_TMR *_tmr) +{ + if (_tmr->Count > 0) + { + //如果定时器变量减到 1 则设置定时器到达标志 + if (--_tmr->Count == 0) + { + _tmr->Flag = 1; + + //如果是自动模式,则自动重装计数器 + if (_tmr->Mode == TMR_AUTO_MODE) + _tmr->Count = _tmr->PreLoad; + } + } +} + +/** + * @brief ms 延时 + * @param n: 延时数 + */ +void Delay_ms(uint32_t n) +{ + if (n == 0) + return; + else if (n == 1) + n = 2; + + DISABLE_INT(); + + s_uiDelayCount = n; + s_ucTimeOutFlag = 0; + + ENABLE_INT(); + + while (1) + { + if (s_ucTimeOutFlag == 1) + break; + } +} + +/** + * @brief us 延时 + * @param n: 延时数 + */ +void Delay_us(uint32_t n) +{ + uint32_t ticks; + uint32_t told; + uint32_t tnow; + uint32_t tcnt = 0; + uint32_t reload; + + reload = SysTick->LOAD; + ticks = n * (SystemCoreClock / 1000000); //需要的节拍数 + + tcnt = 0; + told = SysTick->VAL; //刚进入时的计数器值 + + while (1) + { + tnow = SysTick->VAL; + if (tnow != told) + { + if (tnow < told) + tcnt += told - tnow; + else + tcnt += reload - tnow + told; + told = tnow; + + //时间超过、等于要延迟的时间,则退出 + if (tcnt >= ticks) + break; + } + } +} + +/** + * @brief 启动一个定时器,并设置定时周期 + * @param _id: 定时器 ID + * @param _period: 定时周期,单位 ms + */ +void SysTick_StartTimer(uint8_t _id, uint32_t _period) +{ + DISABLE_INT(); + + s_tTmr[_id].Count = _period; //实时计数器初值 + s_tTmr[_id].PreLoad = _period; //计数器自动重装值,仅自动模式起作用 + s_tTmr[_id].Flag = 0; //定时时间到标志 + s_tTmr[_id].Mode = TMR_ONCE_MODE; //一次性工作模式 + + ENABLE_INT(); +} + +/** + * @brief 启动一个自动定时器,并设置定时周期 + * @param _id: 定时器 ID + * @param _period: 定时周期,单位 ms + */ +void SysTick_StartAutoTimer(uint8_t _id, uint32_t _period) +{ + DISABLE_INT(); + + s_tTmr[_id].Count = _period; //实时计数器初值 + s_tTmr[_id].PreLoad = _period; //计数器自动重装值,仅自动模式起作用 + s_tTmr[_id].Flag = 0; //定时时间到标志 + s_tTmr[_id].Mode = TMR_AUTO_MODE; //自动工作模式 + + ENABLE_INT(); +} + +/** + * @brief 停止一个定时器 + * @param _id: 定时器 ID + */ +void SysTick_StopTimer(uint8_t _id) +{ + DISABLE_INT(); + + s_tTmr[_id].Count = 0; //实时计数器初值 + s_tTmr[_id].Flag = 0; //定时时间到标志 + s_tTmr[_id].Mode = TMR_ONCE_MODE; //自动工作模式 + + ENABLE_INT(); +} + +/** + * @brief 检测定时器是否超时 + * @param _id: 定时器 ID + * @param _period: 定时周期 + * @retval 返回 0 表示定时未到,1 表示定时到 + */ +uint8_t SysTick_CheckTimer(uint8_t _id) +{ + if (_id >= TMR_COUNT) + return 0; + + if (s_tTmr[_id].Flag == 1) + { + s_tTmr[_id].Flag = 0; + return 1; + } + else + { + return 0; + } +} + +/** + * @brief 获取运行时间,单位 ms。最长可以表示 24.85 天 + * @retval 运行时间 + */ +int32_t SysTick_GetRunTime(void) +{ + int32_t runtime; + + DISABLE_INT(); + + runtime = g_iRunTime; + + ENABLE_INT(); + + return runtime; +} + +/** + * @brief 计算当前运行时间和给定时刻之间的差值。处理了计数器循环 + * @param _LastTime: 上个时刻 + * @retval 当前时间和过去时间的差值,单位 ms + */ +int32_t SysTick_CheckRunTime(int32_t _LastTime) +{ + int32_t now_time; + int32_t time_diff; + + DISABLE_INT(); + + now_time = g_iRunTime; + + ENABLE_INT(); + + if (now_time >= _LastTime) + time_diff = now_time - _LastTime; + else + time_diff = INT32_MAX - _LastTime + now_time; + + return time_diff; +} + +/** + * @brief SysTick 中断服务程序 + */ +void SysTick_Handler(void) +{ + HAL_IncTick(); + + if (g_ucEnableSystickISR == 0) + return; + + SysTick_ISR(); +} diff --git a/System/systick/systick.h b/System/systick/systick.h new file mode 100644 index 0000000..9e66b02 --- /dev/null +++ b/System/systick/systick.h @@ -0,0 +1,36 @@ +//SysTick 及软件定时器库 + +#ifndef __SYSTICK_H +#define __SYSTICK_H + +#include "sys.h" + +#define TMR_COUNT 4 //软件定时器的个数 + +//定时器结构体工作模式 +typedef enum +{ + TMR_ONCE_MODE = 0, //一次工作模式 + TMR_AUTO_MODE = 1 //自动定时工作模式 +} TMR_MODE_E; + +//定时器结构体 +typedef struct +{ + volatile uint8_t Mode; //计数器模式 + volatile uint8_t Flag; //定时到达标志 + volatile uint32_t Count; //计数器 + volatile uint32_t PreLoad; //计数器预装值 +} SOFT_TMR; + +void SysTick_Init(void); +void Delay_ms(uint32_t n); +void Delay_us(uint32_t n); +void SysTick_StartTimer(uint8_t _id, uint32_t _period); +void SysTick_StartAutoTimer(uint8_t _id, uint32_t _period); +void SysTick_StopTimer(uint8_t _id); +uint8_t SysTick_CheckTimer(uint8_t _id); +int32_t SysTick_GetRunTime(void); +int32_t SysTick_CheckRunTime(int32_t _LastTime); + +#endif diff --git a/System/tim/tim.c b/System/tim/tim.c new file mode 100644 index 0000000..7f9e46f --- /dev/null +++ b/System/tim/tim.c @@ -0,0 +1,123 @@ +//硬件定时器库 + +#include "sys.h" + +#include "tim.h" + +/** + * @brief 开启定时中断 + * @param TIMx: 定时器 + * @param _ulFreq: 定时频率 (Hz)。0 表示关闭 + * @param _PreemptionPriority: 抢占优先级 + * @param _SubPriority: 子优先级 + */ +void TIM_Set(TIM_TypeDef *TIMx, double _ulFreq, uint8_t _PreemptionPriority, uint8_t _SubPriority) +{ + TIM_HandleTypeDef TimHandle = {0}; + uint16_t usPeriod; + uint16_t usPrescaler; + uint32_t uiTIMxCLK; + + //使能TIM时钟 + if (TIMx == TIM1) + __HAL_RCC_TIM1_CLK_ENABLE(); + else if (TIMx == TIM2) + __HAL_RCC_TIM2_CLK_ENABLE(); + else if (TIMx == TIM3) + __HAL_RCC_TIM3_CLK_ENABLE(); + else if (TIMx == TIM4) + __HAL_RCC_TIM4_CLK_ENABLE(); + else if (TIMx == TIM5) + __HAL_RCC_TIM5_CLK_ENABLE(); + else if (TIMx == TIM6) + __HAL_RCC_TIM6_CLK_ENABLE(); + else if (TIMx == TIM7) + __HAL_RCC_TIM7_CLK_ENABLE(); + else if (TIMx == TIM8) + __HAL_RCC_TIM8_CLK_ENABLE(); + else if (TIMx == TIM12) + __HAL_RCC_TIM12_CLK_ENABLE(); + else if (TIMx == TIM13) + __HAL_RCC_TIM13_CLK_ENABLE(); + else if (TIMx == TIM14) + __HAL_RCC_TIM14_CLK_ENABLE(); + else if (TIMx == TIM15) + __HAL_RCC_TIM15_CLK_ENABLE(); + else if (TIMx == TIM16) + __HAL_RCC_TIM16_CLK_ENABLE(); + else if (TIMx == TIM17) + __HAL_RCC_TIM17_CLK_ENABLE(); + + if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM15) || (TIMx == TIM16) || (TIMx == TIM17)) + uiTIMxCLK = SystemCoreClock / 2; //APB2 定时器时钟 = 200M + else + uiTIMxCLK = SystemCoreClock / 2; //APB1 定时器 = 200M + + if (_ulFreq < 100) + { + usPrescaler = 10000 - 1; //分频比 = 10000 + usPeriod = ((double)uiTIMxCLK / 10000.0) / _ulFreq - 1.0; //自动重装的值 + } + else if (_ulFreq < 3000) + { + usPrescaler = 100 - 1; //分频比 = 100 + usPeriod = ((double)uiTIMxCLK / 100.0) / _ulFreq - 1.0; //自动重装的值 + } + else //大于 4k 的频率 + { + usPrescaler = 0; //分频比 = 1 + usPeriod = (double)uiTIMxCLK / _ulFreq - 1.0; //自动重装的值 + } + + //定时器中断更新周期 + TimHandle.Instance = TIMx; + TimHandle.Init.Prescaler = usPrescaler; + TimHandle.Init.Period = usPeriod; + TimHandle.Init.ClockDivision = 0; + TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP; + TimHandle.Init.RepetitionCounter = 0; + TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; + HAL_TIM_Base_Init(&TimHandle); + + //使能定时器中断 + __HAL_TIM_ENABLE_IT(&TimHandle, TIM_IT_UPDATE); + + //配置TIM定时更新中断 + { + uint8_t irq = 0; + + if (TIMx == TIM1) + irq = TIM1_UP_IRQn; + else if (TIMx == TIM2) + irq = TIM2_IRQn; + else if (TIMx == TIM3) + irq = TIM3_IRQn; + else if (TIMx == TIM4) + irq = TIM4_IRQn; + else if (TIMx == TIM5) + irq = TIM5_IRQn; + else if (TIMx == TIM6) + irq = TIM6_DAC_IRQn; + else if (TIMx == TIM7) + irq = TIM7_IRQn; + else if (TIMx == TIM8) + irq = TIM8_UP_TIM13_IRQn; + else if (TIMx == TIM12) + irq = TIM8_BRK_TIM12_IRQn; + else if (TIMx == TIM13) + irq = TIM8_UP_TIM13_IRQn; + else if (TIMx == TIM14) + irq = TIM8_TRG_COM_TIM14_IRQn; + else if (TIMx == TIM15) + irq = TIM15_IRQn; + else if (TIMx == TIM16) + irq = TIM16_IRQn; + else if (TIMx == TIM17) + irq = TIM17_IRQn; + + HAL_NVIC_SetPriority((IRQn_Type)irq, _PreemptionPriority, _SubPriority); + HAL_NVIC_EnableIRQ((IRQn_Type)irq); + } + + HAL_TIM_Base_Start(&TimHandle); +} diff --git a/System/tim/tim.h b/System/tim/tim.h new file mode 100644 index 0000000..017caee --- /dev/null +++ b/System/tim/tim.h @@ -0,0 +1,8 @@ +//硬件定时器库 + +#ifndef __TIM_H +#define __TIM_H + +void TIM_Set(TIM_TypeDef *TIMx, double _ulFreq, uint8_t _PreemptionPriority, uint8_t _SubPriority); + +#endif diff --git a/User/APP_Video/APP_Video.c b/User/APP_Video/APP_Video.c new file mode 100644 index 0000000..21c7c44 --- /dev/null +++ b/User/APP_Video/APP_Video.c @@ -0,0 +1,229 @@ +//视频播放器应用 + +#include "sys.h" +#include "systick.h" +#include "tim.h" + +#include "key.h" +#include "lcd.h" +#include "led.h" + +#include "GameEngine.h" +#include "SD.h" + +#include "APP_Video.h" + +/**************************************** 私有定义 ****************************************/ + +typedef struct +{ + double fps; + uint32_t frame_count; +} VideoInfo; //视频信息定义 + +/*****************************************************************************************/ + +/**************************************** 全局变量 ****************************************/ + +FIL video_file; //视频文件 +char video_filename[32]; //视频文件名 +char video_filepath[32]; //视频文件路径 + +VideoInfo video_info; + +volatile uint32_t current_frame = 0; +volatile uint8_t is_to_play = 0; +volatile uint8_t is_disp_progress = 0; + +/*****************************************************************************************/ + +/**************************************** 私有函数 ****************************************/ + +void APP_Video_Play(void); +uint8_t APP_Video_Menu(void); +void APP_Video_Msg(uint8_t *head, uint8_t *content); + +/*****************************************************************************************/ + +/** + * @brief 启动视频播放器 + */ +void APP_Video_Launcher(void) +{ + //GUI 选取文件 + uint8_t ret; + if ((ret = SD_SelectFile(video_filename, "32v")) != SD_OK) + { + if (ret == SD_ERROR) + APP_Video_Msg("警告", "选取文件出错!\n\n请按任意键返回"); + + return; + } + + SD_GetPath(video_filepath, video_filename); + + //打开文件 + uint8_t res; + if (res = f_open(&video_file, video_filepath, FA_OPEN_EXISTING | FA_READ) != FR_OK) + { + char temp[10]; + sprintf(temp, "%d", res); + APP_Video_Msg("警告", "打开文件出错!\n\n请按任意键返回"); + APP_Video_Msg("警告", temp); + + return; + } + + //读取视频信息 + uint32_t br; + if (f_read(&video_file, &video_info, sizeof(video_info), &br) != FR_OK) + { + APP_Video_Msg("警告", "读取视频信息错误!\n\n请按任意键返回"); + return; + } + + if (video_info.fps <= 0.0 || video_info.fps > 45.0) + { + APP_Video_Msg("警告", "不支持的视频格式!\n\n请按任意键返回"); + return; + } + + //设置定时器 + TIM_Set(TIM2, video_info.fps, 0, 0); + + f_lseek(&video_file, 153600); + APP_Video_Play(); +} + +/** + * @brief 播放视频 + * @retval 成功返回 0,失败返回 1 + */ +void APP_Video_Play(void) +{ + uint32_t br; + + while (1) + { + if (f_read(&video_file, GE_Draw_VRam, sizeof(GE_Draw_VRam), &br) != FR_OK) + { + APP_Video_Msg("警告", "视频播放出错!\n\n请按任意键返回"); + return; + } + + current_frame++; + + if (is_disp_progress != 0) + { + GE_Draw_Fill(8, 228, 304, 8, WHITE); + GE_Draw_Rectangle(10, 230, 300, 4, GREEN); + GE_Draw_Rectangle(11, 231, (float)current_frame / (float)video_info.frame_count * 298.0, 2, BLUE); + + is_disp_progress++; //溢出到 0 时进度条消失 + } + + switch (KEY_GetKey()) + { + case JOY_R_UP: //进度条 + { + is_disp_progress = !is_disp_progress; + } + break; + + case JOY_L_UP: //菜单 + { + //关闭定时器 + TIM_Set(TIM2, 0, 0, 0); + + if (APP_Video_Menu() == 1) + return; + + //打开定时器 + TIM_Set(TIM2, video_info.fps, 0, 0); + } + } + + while (is_to_play != 1) + ; + + GE_Draw_Disp(); + is_to_play = 0; + + if (br == 0) + { + f_lseek(&video_file, 153600); + current_frame = 0; + } + } +} + +/** + * @brief 播放视频 + * @retval 返回播放器返回 0,结束播放返回 1 + */ +uint8_t APP_Video_Menu(void) +{ + uint8_t content[3][GE_GUI_MENUBOX_CONTENT_LEN] = {"视频信息", "退出播放器", "返回"}; + while (1) + { + GE_Draw_Fill(50, 50, 220, 140, WHITE); + switch (GE_GUI_MenuBox(50, 50, 220, 140, "菜单", 3, content, NULL)) + { + case 0: //返回 + { + KEY_ClearKey(); + return 0; + } + break; + + case 1: //视频信息 + { + char str_info[50]; + sprintf(str_info, "视频帧率: %.2lf\n视频时长: %.2lfs", video_info.fps, (double)video_info.frame_count / video_info.fps); + GE_Draw_Fill(50, 50, 220, 140, WHITE); + GE_GUI_MsgBox(50, 50, 220, 140, "视频信息", str_info, NULL); + + KEY_WaitKey(JOY_L); + } + break; + + case 2: //退出播放器 + { + f_close(&video_file); + return 1; + } + break; + + case 3: //返回 + { + return 0; + } + } + } +} + +/** + * @brief 消息框,任意键按下后退出 + * @param head: 标题 + * @param content: 内容 + */ +void APP_Video_Msg(uint8_t *head, uint8_t *content) +{ + GE_Draw_Fill(60, 75, 200, 90, WHITE); + GE_GUI_MsgBox(60, 75, 200, 90, head, content, NULL); + KEY_WaitKey(JOY_L); +} + +/** + * @brief TIM2 中断处理 + */ +void TIM2_IRQHandler(void) +{ + if ((TIM2->SR & TIM_FLAG_UPDATE) != RESET) + { + //清除更新标志 + TIM2->SR = ~TIM_FLAG_UPDATE; + + is_to_play = 1; + } +} diff --git a/User/APP_Video/APP_Video.h b/User/APP_Video/APP_Video.h new file mode 100644 index 0000000..a5244f2 --- /dev/null +++ b/User/APP_Video/APP_Video.h @@ -0,0 +1,8 @@ +//视频播放器应用 + +#ifndef __APP_VIDEO +#define __APP_VIDEO + +void APP_Video_Launcher(void); + +#endif