1、利用SysTick定时器编写倒计时程序,如初始设置为2分30秒,每秒在屏幕上输出一次时间,倒计时为0后,红灯亮,停止屏幕输出,并关闭SysTick定时器的中断。
运行结果
倒计时进行中,绿灯亮
倒计时结束,红灯亮
main.c
#define GLOBLE_VAR
#include "includes.h"
int main(void)
{
uint8_t mSec; //记当前秒的值
//关总中断
DISABLE_INTERRUPTS;
//"时分秒"缓存初始化(00:02:30)
gTime[0] = 0; //时
gTime[1] = 2; //分
gTime[2] = 30; //秒
mSec = 0; //记住当前秒的值
//用户外设模块初始化
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF); //所有灯初始化为熄灭状态
systick_init(10); //设置systick为10ms中断
//开总中断
ENABLE_INTERRUPTS;
printf("------------------------------------------------------\n");
printf("利用SysTick定时器编写倒计时程序 \n");
printf("初始设置为2分30秒,每秒在屏幕上输出一次时间,倒计时为0后,红灯亮,停止屏幕输出,并关闭SysTick定时器的中断\n");
printf("------------------------------------------------------\n");
for(;;) //for(;;)(开头)
{
if (gTime[2] == mSec) continue;
mSec=gTime[2];
//以下是1秒到的处理
if (gTime[0]!=0||gTime[1]!=0||gTime[2]!=0) //不为0正常倒计时且绿灯亮
{
gpio_set(LIGHT_GREEN,LIGHT_ON); //设置绿灯亮
printf("%d:%d:%d\n",gTime[0],gTime[1],gTime[2]);
}
else//倒计时为0,亮红灯并关闭SysTick定时器的中断
{
printf("%d:%d:%d\n",gTime[0],gTime[1],gTime[2]);
gpio_set(LIGHT_GREEN,LIGHT_OFF); //关闭绿灯
gpio_set(LIGHT_RED,LIGHT_ON); //设置红灯亮
// 倒计时结束,关闭SysTick中断
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
break;
}
}
}
isr.c
#include "includes.h"
//声明使用到的内部函数
void SecSub1(uint8_t *p);
void SysTick_Handler()
{
//printf("***\n");
static uint8_t SysTickCount = 0;
SysTickCount++; //Tick单元+1
wdog_feed(); //看门狗“喂狗”
if (SysTickCount >= 100)
{
SysTickCount = 0;
SecSub1(gTime);
}
}
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
}
}
}
}
2、利用RTC显示日期(年月日、时分秒),每秒更新。并设置某个时间的闹钟。闹钟时间到时,屏幕上显示有你的姓名的文字,并点亮绿灯。
运行结果
main.c
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
int main(void)
{
//关总中断
DISABLE_INTERRUPTS;
//用户外设模块初始化
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF); //初始化红灯
gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF); //初始化绿灯
gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF); //初始化蓝灯
RTC_Init(); //RTC初始化
RTC_Set_Time(23,59,59); //设置时间
RTC_Set_Date(24,6,5,3); //设置日期
//使能模块中断
RTC_PeriodWKUP_Enable_Int(); //使能唤醒中断
RTC_Alarm_Enable_Int(0); //0:闹钟A,1:闹钟B
//开总中断
ENABLE_INTERRUPTS;
RTC_Set_PeriodWakeUp(1); //配置WAKE UP中断,每秒中断一次
RTC_Set_Alarm(0,4,0,0,6);//设置闹钟A
printf("------------------------------------------------------\n");
printf("利用RTC显示日期 \n");
printf("闹钟时间到时,显示姓名,并点亮绿灯\n");
printf("------------------------------------------------------\n");
for(;;)
{
}
}
isr.c
#include "includes.h"
//======================================================================
//程序名称:RTC_WKUP_IRQHandler
//函数参数:无
//中断类型:RTC闹钟唤醒中断处理函数
//======================================================================
void RTC_WKUP_IRQHandler(void)
{
uint8_t hour,min,sec;
uint8_t year,month,date,week;
if(RTC_PeriodWKUP_Get_Int()) //唤醒中断的标志
{
RTC_PeriodWKUP_Clear(); //清除唤醒中断标志
RTC_Get_Date(&year,&month,&date,&week); //获取RTC记录的日期
RTC_Get_Time(&hour,&min,&sec); //获取RTC记录的时间
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)
{
gpio_set(LIGHT_GREEN,LIGHT_ON);//绿灯亮
if(RTC_Alarm_Get_Int(0)) //闹钟A的中断标志位
{
RTC_Alarm_Clear(0); //清闹钟A的中断标志位
printf("闹钟A:800030\n");
}
}
3、利用PWM脉宽调制,交替显示红灯的5个短闪和5个长闪。
运行结果
小灯闪烁与输出一致
main.c
#define GLOBLE_VAR
#include "includes.h" // 包含总头文件
//main.c使用的内部函数声明处
void Delay_ms(uint16_t u16ms);
int main(void)
{
uint8_t short_flash_count = 0; // 计数短闪次数
uint8_t long_flash_count = 0; // 计数长闪次数
uint8_t mode = 0; // 0 表示短闪模式,1 表示长闪模式
double m_duty; // 占空比
//关总中断
DISABLE_INTERRUPTS;
//用户外设模块初始化
gpio_init(LIGHT_RED, GPIO_OUTPUT, LIGHT_OFF); // 初始化红灯
pwm_init(PWM_USER, 1500, 1000, 10.0, PWM_CENTER, PWM_MINUS); // PWM 输出初始化
//开总中断
ENABLE_INTERRUPTS;
printf("------------------------------------------------------\n");
printf("利用PWM脉宽调制,交替显示红灯的5个短闪和5个长闪\n");
printf("------------------------------------------------------\n");
for (;;)
{
if (mode == 0) {
// 短闪模式
m_duty = 10.0;
short_flash_count++;
printf("短闪 %d\n", short_flash_count);
if (short_flash_count >= 5) {
mode = 1;
short_flash_count = 0;
}
} else {
// 长闪模式
m_duty = 90.0;
long_flash_count++;
printf("长闪 %d\n", long_flash_count);
if (long_flash_count >= 5) {
mode = 0;
long_flash_count = 0;
}
}
pwm_update(PWM_USER, m_duty); // 调节占空比
// 控制灯的状态
gpio_reverse(LIGHT_RED);
// 调整延时以实现短闪和长闪的效果
if (mode == 0) {
Delay_ms(100); // 短闪延时 100 毫秒
} else {
Delay_ms(900); // 长闪延时 900 毫秒
}
}
}
//======以下为主函数调用的子函数存放处=====================================
void Delay_ms(uint16_t u16ms)
{
uint32_t u32ctr;
for (u32ctr = 0; u32ctr < 8000 * u16ms; u32ctr++)
{
__ASM("NOP");
}
}
4、GEC39定义为输出引脚,GEC10定义为输入引脚,用杜邦线将两个引脚相连,验证捕捉实验程序Incapture-Outcmp-20211110,观察输出的时间间隔。
运行结果
每五秒为一周期,小灯闪烁频率不断提高
main.c
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
void Delay_ms(uint16_t u16ms);
int main(void)
{
//声明main函数使用的局部变量
uint8_t mFlag; //灯的状态标志
uint8_t flag; //标记高低电平
//关总中断
DISABLE_INTERRUPTS;
//给主函数使用的局部变量赋初值
mFlag='A'; //灯的状态标志
//给全局变量赋初值
gTime[0] = 0; //分钟
gTime[1] = 0; //秒
gTime[2] = 0; //毫秒
period = 1000; //自动重装载寄存器初始值
//用户外设模块初始化
gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON); //初始化蓝灯
outcmp_init(OUTCMP_USER,3000,200,50.0,CMP_REV); //输出比较初始化
incapture_init(INCAP_USER,375,1000,CAP_DOUBLE); //上升沿捕捉初始化
systick_init(1); //设置systick为1ms中断
//使能模块中断
cap_enable_int(INCAP_USER); //使能输入捕捉中断
//开总中断
ENABLE_INTERRUPTS;
printf("------------------------------------------------------\n");
printf(" (1)蓝灯每秒闪烁一次,作为运行指示\n");
printf(" (2)设置GEC39为输出比较引脚,\n");
printf(" 设置GEC10为输入捕捉引脚,沿跳变捕捉\n");
printf(" (3)用导线将GEC39与GEC10连接 \n");
printf(" (4)程序使得输出比较引脚输出高低电平,输入捕捉引脚捕捉\n");
printf(" 后用printf输出,PC机程序据此显示波形引脚捕捉\n");
printf("------------------------------------------------------\n");
for(;;)
{
flag = gpio_get(INCAP_USER);
//灯状态标志mFlag为'L',改变灯状态及标志
if (mFlag=='L' && flag == 1) //判断灯的状态标志
{
mFlag='A'; //灯的状态标志
gpio_set(LIGHT_BLUE,LIGHT_ON); //灯“亮”
}
//如灯状态标志mFlag为'A',改变灯状态及标志
else if(mFlag=='A' && flag == 0) //判断灯的状态标志
{
mFlag='L'; //灯的状态标志
gpio_set(LIGHT_BLUE,LIGHT_OFF); //灯“暗”
}
}
}
//======以下为主函数调用的子函数存放处
void Delay_ms(uint16_t u16ms)
{
uint32_t u32ctr;
for(u32ctr = 0; u32ctr < 8000*u16ms; u32ctr++)
{
__ASM("NOP");
}
}
总结
1.关闭SysTick中断,SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;语句中,SysTick->CTRL是SysTick定时器的控制和状态寄存器,该寄存器有多个位用于不同的控制功能,其中包括:
- SysTick_CTRL_ENABLE_Msk:使能位。当该位被设置时,SysTick定时器开始计数;当该位被清除时,定时器停止计数。
- SysTick_CTRL_TICKINT_Msk:中断使能位。设置该位允许在计数到0时产生中断。
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk将SysTick_CTRL_ENABLE_Msk位置0,即清除SysTick定时器的使能位,来停止SysTick定时器,从而关闭SysTick中断。
2.查找使能闹钟中断函数定义
可以看到0代表闹钟A,1代表闹钟B。因此main.c使能闹钟中断函数中先代入0。即使能闹钟A中断。
3.设置闹钟的函数中,第一个参数0也是对应的闹钟A
RTC_Set_Alarm(0,4,0,0,6);//设置闹钟A,星期四,在第六秒时闹钟响
4.利用PWM脉宽调制时,初始化为pwm_init(PWM_USER, 1500, 1000, 10.0, PWM_CENTER, PWM_MINUS),即用的中心对齐方式,且负极性,分别利用占空比为10和90来控制灯的闪烁。可适当增加延迟函数以便更好的观察灯的闪烁。