07 第7章作业 | 嵌入式

190 阅读9分钟

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

编译串口更新后的结果:

58bb69ad755f8a18ddbeffded34f61a.jpgfa6554a1b0c2e327c1d774b32320d6f.jpg

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); // 点亮绿灯
    }
}

结果:

7efc34eba682da772f0e6e24005add9.jpgc736ebeb9dcb5c85c7b3d7e4a3359b2.jpg

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

结果:

【短闪】

d1b6e1cf30af43e08d42ec95a30c1ce.jpgd0485124aac5e32e13d903cb76b0c1b.jpg

【长闪】

295d49193f29696a94b795216d25dc0.jpg61d9d8e1f43416ff8234abe9cf77269.jpg

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秒一个周期,在一个周期内蓝灯闪烁频率逐渐提高 【短闪->快闪】

tt.jpgtest.jpg