完成视频播放器

This commit is contained in:
lxbpxylps@126.com 2021-09-28 14:25:22 +08:00
parent 4316843201
commit 56506db572
6 changed files with 680 additions and 0 deletions

276
System/systick/systick.c Normal file
View File

@ -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();
}

36
System/systick/systick.h Normal file
View File

@ -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

123
System/tim/tim.c Normal file
View File

@ -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);
}

8
System/tim/tim.h Normal file
View File

@ -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

229
User/APP_Video/APP_Video.c Normal file
View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
//视频播放器应用
#ifndef __APP_VIDEO
#define __APP_VIDEO
void APP_Video_Launcher(void);
#endif