1、利用SysTick定时器编写倒计时程序,如初始设置为2分30秒,每秒在屏幕上输出一次时间,倒计时为0后,红灯亮,停止屏幕输出,并关闭SysTick定时器的中断。
在isr.c中创建新的函数SecSub1()实现倒计时逻辑:
void SecSub1(uint8_t *p)
{
if (*(p+2) > 0) {
*(p+2)-=1; // 秒 -1
} else {
if (*(p+1) > 0) {
*(p+1)-=1; // 分 -1
*(p+2) = 59; // 秒 设为59
} else {
if (*(p) > 0) {
*(p)-=1; // 时 -1
*(p+1) = 59; // 分 设为59
*(p+2) = 59; // 秒 设为59
}
}
}
}
修改SysTick_Handler()定时器中断处理程序,将原有的正数修改为倒计时:
void SysTick_Handler()
{
//printf("***\n");
static uint8_t SysTickCount = 0;
SysTickCount++; //Tick单元+1
wdog_feed(); //看门狗“喂狗”
if (SysTickCount >= 100)
{
SysTickCount = 0;
SecSub1(gTime);//倒计时
}
}
main函数中实现具体逻辑:
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
int main(void)
{
uint8_t mSec; //记录当前秒的值
//【不变】关总中断
DISABLE_INTERRUPTS;
wdog_stop();
//给全局变量赋初值
//"时分秒"初始值为(00:02:30)
gTime[0] = 0; //时
gTime[1] = 2; //分
gTime[2] = 30; //秒
mSec = 0; //记住当前秒的值
//用户外设模块初始化
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF); //初始化红灯
systick_init(10); //设置systick为10ms中断
//【不变】开总中断
ENABLE_INTERRUPTS;
for(;;)
{
if (gTime[2] == mSec) continue;
mSec=gTime[2];
//以下是1秒到的处理,灯的状态切换(这样灯每秒闪一次)
if (gTime[0]!=0||gTime[1]!=0||gTime[2]!=0) //不为0时正常倒计时
{
printf("%d:%d:%d\n",gTime[0],gTime[1],gTime[2]);
}
else //倒计时为0,则亮红灯
{
printf("%d:%d:%d\n",gTime[0],gTime[1],gTime[2]);
gpio_set(LIGHT_RED,LIGHT_ON); //亮红灯
// 倒计时结束,关闭SysTick中断
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
break;
}
}
}
编译串口更新后的结果:
2、利用RTC显示日期(年月日、时分秒),每秒更新。并设置某个时间的闹钟。闹钟时间到时,屏幕上显示有你的姓名的文字,并点亮绿灯。
!!!易错点:需要将闹钟中断的优先级设置高于WAKE UP才能正确处理闹钟中断程序
【main.c代码】
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
int main(void)
{
uint32_t mMainLoopCount; //主循环次数变量
//【不变】关总中断
DISABLE_INTERRUPTS;
// 给主函数使用的局部变量赋初值
mMainLoopCount=0; //主循环次数变量
// 给全局变量赋初值
g_RTC_Flag=0;
// 用户外设模块初始化
gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF); //初始化绿灯
uart_init(UART_User,115200);
RTC_Init(); //RTC初始化
RTC_Set_Time(0,0,0); //设置时间为0:0:0
RTC_Set_Date(24,5,23,4); //设置日期
// 使能模块中断
RTC_PeriodWKUP_Enable_Int(); //使能唤醒中断
uart_enable_re_int(UART_User);
// 【不变】开总中断
ENABLE_INTERRUPTS;
RTC_Alarm_Enable_Int(0); //使能闹钟中断
RTC_Set_Alarm(0,4,0,0,10);//设置闹钟时间为星期4,0:0:10
RTC_Set_PeriodWakeUp(1); //配置WAKE UP中断,每秒中断一次
NVIC_SetPriority(RTC_WKUP_IRQn, 1); // 设置WAKE UP中断优先级为1
NVIC_SetPriority(RTC_Alarm_IRQn, 0); // 设置闹钟中断优先级为0(更高优先级)
for(;;) //for(;;)(开头)
{
//主循环次数变量+1
mMainLoopCount++;
//未达到主循环次数设定值,继续循环
if (mMainLoopCount<=12888999) continue;
//达到主循环次数设定值,执行下列语句,进行灯的亮暗处理
//清除循环次数变量
mMainLoopCount=0;
if(g_RTC_Flag==1) //根据串口接收的数据设置基准时间
{
g_RTC_Flag=0;
gcRTC_Date_Time.Year=(uint8_t)((gcRTCBuf[1]-'0')*10+(gcRTCBuf[2]-'0'));
gcRTC_Date_Time.Month=(uint8_t)((gcRTCBuf[4]-'0')*10+(gcRTCBuf[5]-'0'));
gcRTC_Date_Time.Date=(uint8_t)((gcRTCBuf[7]-'0')*10+(gcRTCBuf[8]-'0'));
gcRTC_Date_Time.Hours=(uint8_t)((gcRTCBuf[10]-'0')*10+(gcRTCBuf[11]-'0'));
gcRTC_Date_Time.Minutes=(uint8_t)((gcRTCBuf[13]-'0')*10+(gcRTCBuf[14]-'0'));
gcRTC_Date_Time.Seconds=(uint8_t)((gcRTCBuf[16]-'0')*10+(gcRTCBuf[17]-'0'));
gcRTC_Date_Time.Weekday=(uint8_t)((gcRTCBuf[23]-'0'));
RTC_Set_Time(gcRTC_Date_Time.Hours,gcRTC_Date_Time.Minutes,gcRTC_Date_Time.Seconds); //设置时间
RTC_Set_Date(gcRTC_Date_Time.Year,gcRTC_Date_Time.Month,gcRTC_Date_Time.Date,gcRTC_Date_Time.Weekday); //设置日期
}
}
}
修改中断处理程序isr.c中的相关函数,实现RTC闹钟中断后的相关处理
/======================================================================
//程序名称:RTC_WKUP_IRQHandler
//函数参数:无
//中断类型:RTC闹钟唤醒中断处理函数
//======================================================================
void RTC_WKUP_IRQHandler(void)
{
uint8_t hour,min,sec;
uint8_t year,month,date,week;
char *p;
if(RTC_PeriodWKUP_Get_Int()) //唤醒中断的标志
{
RTC_PeriodWKUP_Clear(); //清除唤醒中断标志
RTC_Get_Date(&year,&month,&date,&week); //获取RTC记录的日期
RTC_Get_Time(&hour,&min,&sec); //获取RTC记录的时间
p=NumToStr("%02d/%02d/%02d %02d:%02d:%02d 星期%d\n",year,month,date,hour,min,sec,week);
uart_send_string(UART_User,p);
printf("%02d/%02d/%02d %02d:%02d:%02d 星期%d\n",year,month,date,hour,min,sec,week);
}
}
//======================================================================
//程序名称:RTC_Alarm_IRQHandler
//中断类型:RTC闹钟中断处理函数
//======================================================================
void RTC_Alarm_IRQHandler(void)
{
//默认使用闹钟A-0
//实现闹钟时间到时,屏幕上显示姓名,并亮灯
if (RTC_Alarm_Get_Int(A))//获取闹钟中断标志
{
RTC_Alarm_Clear(A);
printf("闹钟响了!\n");
printf("姓名:刘婷\n"); // 在闹钟时间到时显示姓名
gpio_set(LIGHT_GREEN, LIGHT_ON); // 点亮绿灯
}
}
结果:
3、利用PWM脉宽调制,交替显示红灯的5个短闪和5个长闪。
【main.c】
通过设置“短闪”占空比为20,“长闪”占空比为80来实现效果
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
void Delay_ms(uint16_t u16ms);
int main(void)
{
//声明main函数使用的局部变量
uint8_t Flag; //电平高低标志
uint8_t mFlag;
double m_duty; //占空比
uint32_t m_i; //控制循环次数
uint8_t m_K; //控制打印次数
//【不变】关总中断
DISABLE_INTERRUPTS;
// 给主函数使用的局部变量赋初值
Flag=1;
mFlag=0;
m_duty = 20.0;
m_K=0;
//用户外设模块初始化
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF); //初始化红灯
pwm_init(PWM_USER,1500,1000,10.0,PWM_CENTER,PWM_MINUS); //PWM输出初始化
//【不变】开总中断
ENABLE_INTERRUPTS;
for(;;) //for(;;)(开头)
{
// 调节占空比以实现闪烁模式
if (Flag == 1) // 交替闪烁短闪
{
m_duty = 20.0; // 设置短闪的占空比
}
else // 交替闪烁长闪
{
m_duty = 80.0; // 设置长闪的占空比
}
pwm_update(PWM_USER, m_duty); // 更新PWM占空比
m_K=0; //保证每次输出打印完整的PWM波,再进入下一个循环
do{
// 检查PWM输出状态,判断是短闪还是长闪
mFlag = gpio_get(PWM_USER);
if ((mFlag == 1) && (Flag == 1))
{
printf("短闪\n");
Flag = 0;
m_K++;
}
else if ((mFlag == 0) && (Flag == 0))
{
printf("长闪\n");
Flag = 1;
m_K++;
}
}while (m_K<1);
// 确保每种模式下完成5次闪烁
for (m_i = 0; m_i < 5; m_i++)
{
if (Flag == 0) // 短闪
{
gpio_set(LIGHT_RED, LIGHT_ON); // 点亮红灯
Delay_ms(200); // 短闪延时(保持红灯亮)
gpio_set(LIGHT_RED, LIGHT_OFF); // 关闭红灯
Delay_ms(200); // 短闪间隔
}
else // 长闪
{
gpio_set(LIGHT_RED, LIGHT_ON); // 点亮红灯
Delay_ms(800); // 长闪延时(保持红灯亮)
gpio_set(LIGHT_RED, LIGHT_OFF); // 关闭红灯
Delay_ms(800); // 长闪间隔
}
printf("%d\n", m_i + 1);
}
}
}
结果:
【短闪】
【长闪】
4、GEC39定义为输出引脚,GEC10定义为输入引脚,用杜邦线将两个引脚相连,验证捕捉实验程序Incapture-Outcmp-20211110,观察输出的时间间隔。
实验输出结果:
0分钟:0秒:99毫秒此刻是上升沿
0分钟:0秒:550毫秒此刻是下降沿
0分钟:1秒:1毫秒此刻是上升沿
0分钟:1秒:402毫秒此刻是下降沿
0分钟:1秒:803毫秒此刻是上升沿
0分钟:2秒:154毫秒此刻是下降沿
0分钟:2秒:505毫秒此刻是上升沿
0分钟:2秒:806毫秒此刻是下降沿
0分钟:3秒:107毫秒此刻是上升沿
0分钟:3秒:358毫秒此刻是下降沿
0分钟:3秒:609毫秒此刻是上升沿
0分钟:3秒:810毫秒此刻是下降沿
0分钟:4秒:11毫秒此刻是上升沿
0分钟:4秒:162毫秒此刻是下降沿
0分钟:4秒:313毫秒此刻是上升沿
0分钟:4秒:414毫秒此刻是下降沿
0分钟:4秒:515毫秒此刻是上升沿
0分钟:4秒:566毫秒此刻是下降沿
0分钟:4秒:617毫秒此刻是上升沿
0分钟:5秒:118毫秒此刻是下降沿
0分钟:5秒:618毫秒此刻是上升沿
0分钟:6秒:69毫秒此刻是下降沿
0分钟:6秒:520毫秒此刻是上升沿
0分钟:6秒:921毫秒此刻是下降沿
0分钟:7秒:322毫秒此刻是上升沿
0分钟:7秒:673毫秒此刻是下降沿
0分钟:8秒:24毫秒此刻是上升沿
0分钟:8秒:325毫秒此刻是下降沿
0分钟:8秒:626毫秒此刻是上升沿
0分钟:8秒:877毫秒此刻是下降沿
0分钟:9秒:128毫秒此刻是上升沿
0分钟:9秒:329毫秒此刻是下降沿
0分钟:9秒:530毫秒此刻是上升沿
0分钟:9秒:681毫秒此刻是下降沿
0分钟:9秒:832毫秒此刻是上升沿
0分钟:9秒:933毫秒此刻是下降沿
0分钟:10秒:34毫秒此刻是上升沿
0分钟:10秒:85毫秒此刻是下降沿
0分钟:10秒:136毫秒此刻是上升沿
0分钟:10秒:637毫秒此刻是下降沿
0分钟:11秒:138毫秒此刻是上升沿
0分钟:11秒:589毫秒此刻是下降沿
0分钟:12秒:40毫秒此刻是上升沿
0分钟:12秒:441毫秒此刻是下降沿
0分钟:12秒:842毫秒此刻是上升沿
0分钟:13秒:193毫秒此刻是下降沿
0分钟:13秒:544毫秒此刻是上升沿
0分钟:13秒:845毫秒此刻是下降沿
0分钟:14秒:146毫秒此刻是上升沿
0分钟:14秒:397毫秒此刻是下降沿
0分钟:14秒:648毫秒此刻是上升沿
0分钟:14秒:849毫秒此刻是下降沿
0分钟:15秒:50毫秒此刻是上升沿
0分钟:15秒:201毫秒此刻是下降沿
0分钟:15秒:351毫秒此刻是上升沿
0分钟:15秒:452毫秒此刻是下降沿
0分钟:15秒:553毫秒此刻是上升沿
0分钟:15秒:604毫秒此刻是下降沿
0分钟:15秒:655毫秒此刻是上升沿
- 观察输出的时间间隔: 大致5秒一个周期,在一个周期内蓝灯闪烁频率逐渐提高 【短闪->快闪】