第7章作业 | 嵌入式

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;
        }	
    }  
}
  1. 编译串口更新后的结果:倒计时为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秒一个周期,在一个周期内蓝灯闪烁频率逐渐提高。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值