1、利用SysTick定时器编写倒计时程序,如初始设置为2分30秒,每秒在屏幕上输出一次时间,倒计时为0后,红灯亮,停止屏幕输出,并关闭SysTick定时器的中断。
创建函数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;
}
}
}
- 编译串口更新后的结果:倒计时为0后,红灯亮:
2、利用RTC显示日期(年月日、时分秒),每秒更新。并设置某个时间的闹钟。闹钟时间到时,屏幕上显示有你的姓名的文字,并点亮绿灯。
修改main.c:
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF); //初始化红灯
gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
uart_init(UART_User,115200);
RTC_Init(); //RTC初始化
RTC_Set_Time(18,15,18); //设置时间为0:0:0
RTC_Set_Date(24,6,1,14); //设置日期
//(1.6)使能模块中断
RTC_PeriodWKUP_Enable_Int(); //使能唤醒中断
RTC_Alarm_Enable_Int(0);
RTC_Set_Alarm(0,6,18,15,30);
RTC_Alarm_Get_Int(A);
uart_enable_re_int(UART_User);
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
RTC_Set_PeriodWakeUp(1); //配置WAKE UP中断,每秒中断一次
printf("32106200121_ZJS_时间闹钟开始\n");
设置号初始的时间和加入设置闹钟的代码。
修改isr.c:
void RTC_Alarm_IRQHandler(void)
{
if(RTC_Alarm_Get_Int(A)) //闹钟A的中断标志位
{
RTC_Alarm_Clear(A); //清闹钟A的中断标志位
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_GREEN,LIGHT_ON);
printf("32106200121_ZJS_闹钟到绿灯亮\n");
}
if(RTC_Alarm_Get_Int(B)) //闹钟A的中断标志位
{
RTC_Alarm_Clear(B); //清闹钟A的中断标志位
printf("This is ALARM_B!!!\n");
}
}
闹钟时间到时,屏幕上显示有我的学号和姓名拼音缩写的文字,并点亮绿灯:
3、利用PWM脉宽调制,交替显示红灯的5个短闪和5个长闪。
#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);
}
}
}
3、交替显示红灯的5个短闪和5个长闪。
短闪:
长闪:
4、GEC39定义为输出引脚,GEC10定义为输入引脚,用杜邦线将两个引脚相连,验证捕捉实验程序Incapture-Outcmp-20211110,观察输出的时间间隔。
void INCAP_USER_Handler(void)
{
//声明INCAP_USER_Handler中需要的变量
static uint8_t flag = 0;
DISABLE_INTERRUPTS; //关总中断
//------------------------------------------------------------------
//(在此处增加功能)
if(cap_get_flag(INCAP_USER))
{
//在捕获到上升沿之后,输出此刻捕获的是上升沿和时间
if(gpio_get(INCAP_USER)==1 && flag == 0){
printf("%d分钟:%d秒:%d毫秒此刻是上升沿\r\n",
gTime[0],gTime[1],gTime[2]);
flag = 1;
//修改自动重装载寄存器的值
period -= 100;
if (period < 100 || period > 1000)
period = 1000;
//通过修改自动重装载寄存器的值控制电平翻转时刻
outcmp_init(OUTCMP_USER,3000,period,50.0,CMP_REV); //修改输出比较电平周期,会触发中断,进入INCAP_USER_Handler(void)函数
}
//在捕获到下降沿之后,输出此刻捕获的是下降沿和时间
else if(gpio_get(INCAP_USER)==0 && flag == 1){
printf("%d分钟:%d秒:%d毫秒此刻是下降沿\r\n",
gTime[0],gTime[1],gTime[2]);
flag = 0;
//通过修改自动重装载寄存器的值控制电平翻转时刻
outcmp_init(OUTCMP_USER,3000,period,50.0,CMP_REV); //修改发输出比较电平周期,会触中断,进入INCAP_USER_Handler(void)函数
}
cap_clear_flag(INCAP_USER); //清中断
}
//------------------------------------------------------------------
ENABLE_INTERRUPTS; //关总中断
}
:
void incapture_init(uint16_t capNo,uint32_t clockFre,uint16_t period,uint8_t capmode)
{
GPIO_TypeDef *gpio_ptr; //声明gpio_ptr为GPIO结构体类型指针
uint8_t port,pin; //声明端口port、引脚pin变量
uint8_t TIM_i,chl; //由tpmx_Chy解析出的tim模块号、通道号临时变量
uint32_t temp; //临时存放寄存器里的值
//gpio引脚解析
port = (capNo>>8); //解析出的端口
pin = capNo; //解析出的引脚号
tim_mux_val(capNo,&TIM_i,&chl);//由对应模块号(PTx_num|y))得出时钟模块号和通道号
timer_init2(TIM_i,clockFre,period);//时钟模块初始化
//GPIO寄存器引脚复用
gpio_ptr=GPIO_ARR[port];
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN_Msk<< (port * 1u);//使能GPIO时钟
//进行模式选择,引脚选择备用功能
temp = gpio_ptr->MODER;
temp &= ~(GPIO_MODER_MODE0 << (pin * 2u));
temp|=2<<(pin * 2u);
gpio_ptr->MODER = temp;
gpio_ptr->OTYPER &= ~(GPIO_OTYPER_OT0_Msk<<(pin * 1u));//推挽输出
gpio_ptr->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED0<<(pin * 2u));//设置速度
gpio_ptr->PUPDR &= ~(GPIO_PUPDR_PUPD0<<(pin * 2u));//下拉
//引脚复用选择对应通道功能
gpio_ptr->AFR[0]&=~(GPIO_AFRL_AFSEL0_Msk<< (pin * 4u));
gpio_ptr->AFR[0]|=14<<(pin * 4u);
if(TIM_i==15)
{
if(chl==1)
{
TIM15->CCMR1&=~TIM_CCMR1_IC1F;//无滤波器
TIM15->CCMR1&=~TIM_CCMR1_IC1PSC;//无预分频器,捕获输入上每检测到一个边沿便执行捕获
TIM15->CCMR1|=TIM_CCMR1_CC1S_0;//CC1 通道配置为输入,IC1 映射到 TI1 上
//输入捕捉参数设定CC1P与CC1NP配合使用
if(capmode == CAP_UP)//上升沿捕捉
{
TIM15->CCER&=~TIM_CCER_CC1P;
TIM15->CCER&=~TIM_CCER_CC1NP;
}
else if(capmode == CAP_DOWN)//下降沿捕捉
{
TIM15->CCER|=TIM_CCER_CC1P;
TIM15->CCER&=~TIM_CCER_CC1NP;
}
else if(capmode == CAP_DOUBLE)//双边沿捕捉
{
TIM15->CCER|=TIM_CCER_CC1P;
TIM15->CCER|=TIM_CCER_CC1NP;
}
TIM15->CCER|=TIM_CCER_CC1E;//使能捕获,决定了是否可以实际将计数器值捕获到输入捕获/比较寄存器 1(TIMx_CCR1)
}
if(chl==2)
{
TIM15->CCMR1&=~TIM_CCMR1_IC2F;//无滤波器
TIM15->CCMR1&=~TIM_CCMR1_IC2PSC;//无预分频器,捕获输入上每检测到一个边沿便执行捕获
TIM15->CCMR1|=TIM_CCMR1_CC2S_0;//CC2 通道配置为输入,IC2 映射到 TI2 上
//输入捕捉参数设定CC2P与CC2NP配合使用
if(capmode == CAP_UP)//上升沿捕捉
{
TIM15->CCER&=~TIM_CCER_CC2P;
TIM15->CCER&=~TIM_CCER_CC2NP;
}
else if(capmode == CAP_DOWN)//下降沿捕捉
{
TIM15->CCER|=TIM_CCER_CC2P;
TIM15->CCER&=~TIM_CCER_CC2NP;
}
else if(capmode == CAP_DOUBLE)//双边沿捕捉
{
TIM15->CCER|=TIM_CCER_CC2P;
TIM15->CCER|=TIM_CCER_CC2NP;
}
TIM15->CCER|=TIM_CCER_CC2E;//使能捕获,决定了是否可以实际将计数器值捕获到输入捕获/比较寄存器2(TIMx_CCR2)
}
}
}
验证捕捉实验程序Incapture-Outcmp-20211110,观察输出的时间间隔。
观察输出的时间间隔: 大致5秒一个周期,在一个周期内蓝灯闪烁频率逐渐提高。