基于Cortex-A9开发板(FS4412)完成中断、串口通信等操作

一、实训目的

  1. 熟悉嵌入式开发交叉编译环境的搭建,掌握ARM处理器GPIO、UART、定时器、中断、ADC等方面的编程开发。
  2. 运用所学知识,根据任务要求进行软硬件系统的设计和调试,对Exynos4412的芯片结构、控制原理、硬件和编程等方面有一定的感性认识和实践操作能力,提高应用知识能力、设计能力、调试能力以及报告撰写能力。

二、实训环境
硬件:Cortex-A9教学开发板(FS4412);
软件:Ubuntu系统;XShell7;SecureCRT-8;XCOM V2.0; Notepad++;CH341SER;
三、实训内容
本次实训任务包含三个项目:

  • 第一个项目要求使用按键k2控制LED灯的状态,依次按下k2后,LED亮度切换为全亮、流水灯和全灭;
  • 第二个项目在第一项目的基础上增加了UART串行通信接口的操作,可实现开发板向PC发送mode1、mode2、mode3等命令并控制LED灯的状态变化,同时PC端也可发送命令给开发板;
  • 第三个项目要求使用按键k3控制蜂鸣器的播放,按下k3后,蜂鸣器开始播放音乐,再次按下k3可实现歌曲的切换,再次按下则停止播放。这些任务涉及了LED、蜂鸣器、按键、中断、PWM定时器以及UART串行通信接口等方面。

首先,在主函数进行相关初始化设置,主要有LED引脚初始化、按键初始化(button_init)、PWM初始化(pwm_init)、串口通信初始化(uart2_init)、定时器初始化(timer_init)。

void button_init()//按键k2,k3初始化
{
    //KEY2 外: 配置管脚的工作模式
    GPX1.CON = (GPX1.CON & ~(0xF << 4)) | (0xF << 4);  //配置 GPX1_1为中断模式
    EXT_INT41_CON = (EXT_INT41_CON & ~(0x7 << 4)) | (0x2 << 4);  //设置GPX1_1的触发方式为 下降沿触发
    EXT_INT41_MASK = EXT_INT41_MASK & (~0x02);    //GPX1_1 中断使能
    // 内: 功能块设置
    ICDISER.ICDISER1 |= (1 << 25);  //EINT9 (GPX1_1)  GIC中断使能
    ICDIPTR.ICDIPTR14 = 0x01010101;   //设置 GPX1_1 的中断处理函数为 do_irq()
    ICDDCR = ICDDCR | 1; //GIC 分发总使能
    CPU0.ICCICR = 1;  // CPU0  中断使能
    CPU0.ICCPMR = 0xFF;   //设置CPU0的优先级门槛为最低
	//KEY3  外
	GPX1.CON = (GPX1.CON & ~(0xF<<8))|(0xF<<8);  //配置 GPX1_2为中断模式
	EXT_INT41_CON = (EXT_INT41_CON & ~(0x7<<8))|(0x2<<8);  //设置GPX1_2的触发方式未 下降沿触发
	EXT_INT41_MASK = EXT_INT41_MASK & (~0x04);		//GPX1_2 中断使能
	//内
	ICDISER.ICDISER1 |=  (1<<26);	//EINT9 (GPX1_2)  GIC中断使能
}
void pwm_init(void)
{ 
	//-----外: 配置管脚的工作模式
    GPD0.CON = (GPD0.CON & ~(0xF<<0))|(0x2<<0);  //配置 GPD0_0为pwm模式
    GPD0.PUD &= ~0xF;    // Disables Pull-up/Pull-down
	//-----内: 功能块设置
	//1. 设置PWM_OUT0 (GPD0_0) 输出625Hz 的的方波
	//   pwm_out = PCLK/TCFG0/TCFG1/TCNTB0 = 100M/100/8/200 = 625Hz
	// 定时器工作时钟频率 = PCLK/{prescaler value(TCFG0) + 1}/{divider value(TCFG1)}
    PWM.TCFG0 = (PWM.TCFG0 & ~(0xFF))|(99);      //设置第一级分频值为100
    PWM.TCFG1 = (PWM.TCFG1 & ~(0xF<<0))|(0x3<<0); //设置第二级分频值为8,定时器的工作频率为:f= 100M/100/8 =125000Hz
    PWM.TCNTB0 = 200;            
    PWM.TCMPB0 = 100;   //设置占空比为 = TCMPB0/TCNTB0 = 100/200 = 1/2  ,输出的是方波
    PWM.TCON =  (PWM.TCON & ~(0xF<<0) ) | (0x1<<1); //第一次,PWM0要手动更新TCNTB0和TCMPB0
    PWM.TCON =  (PWM.TCON & ~(0xF<<0) ) | (0x09<<0); //启动自动重装载,禁止手动更新,启动PWM0	 
}
void uart2_init(void)//初始化uart2
{
	
	GPA1.CON = (GPA1.CON & ~0xFF ) | (0x22); //将GPA1_0配置为接收端(RX),GPA1_1配置为发送端(TX)
	UART2.ULCON2 = 0x3; //普通模式,无校验位,一个停止位,8个数据位
	UART2.UCON2 = 0x5;  //中断请求或轮询模式
	UART2.UBRDIV2 = 0x35;//波特率除数,设置为53
	UART2.UFRACVAL2 = 0x5;//分频器,设置为5
}
void timer_init(void)
{ 
	ICDISER.ICDISER2 |= (0x1 << 8);  //EINT9 (GPX1_1)  GIC 中断使能
    ICDIPTR.ICDIPTR18 = 0x01<<0;   //设置 GPX1_1 的中断处理函数为 do_irq()
    ICDDCR =  1; //GIC 分发总使能
    CPU0.ICCICR |= 0x1;  // CPU0 中断使能
    CPU0.ICCPMR = 0xFF;   //设置 CPU0 的优先级门槛为最低
    PWM.TCFG0 = (PWM.TCFG0 & ~(0xFF<<8))|(249<<8);     
    PWM.TCFG1 = (PWM.TCFG1 & ~(0xF<<12))|(4<<12);           
    PWM.TCNTB3= 12500;                            
    PWM.TCON =  PWM.TCON  | (0x1<<17);                //设置 PWM0 手动更新 TCNTB0 和 TCMPB0
    PWM.TCON =  PWM.TCON  & (~(0xF<<16) ) | (0x8<<16);//动自动重装载,禁止手动更新,启动 PWM0	 
	PWM.TINT_CSTAT = PWM.TINT_CSTAT | (0x1 << 3);      // 清除 PWM 中断状态位并开启 PWM 中断
    PWM.TCON = PWM.TCON | (0x1 << 16);                 // 启动 PWM 输出
}

在k2开关控制LED的部分,中断处理函数会进行消抖操作,根据按键触发次数来改变标志位flag,以控制LED灯的亮灭模式,同时使用timer3进行定时控制。在标志位为1时,将所有LED灯点亮,同时将计数器a归零;在标志位为2时,依次点亮四个LED灯,分别为GPX2_7、GPX1_0、GPF3_4和GPF3_5,当a计数到4后重新开始循环计数;在标志位为0时,将所有LED灯熄灭,同时将计数器a归零。通过计数器a mod 4的方法,保证a的值始终在0-3的范围内循环变化,确保了程序的稳定可靠。

// 处理中断函数
void do_irq()
{
    unsigned int irq_num = 0;
    irq_num = CPU0.ICCIAR & 0x3FF; // 获取中断 ID
    switch (irq_num) // 根据中断 ID 执行相应的外设中断处理程序
    {
        case 57: // k2 按下触发的中断
            mydelay_ms(20); // 按键消抖
            flag++; // 标志位加1,用于控制 LED 灯的状态
			flag=flag%3; // 实现循环控制,使标志位在 0-2 之间循环变化
            EXT_INT41_PEND = EXT_INT41_PEND | ((0x1 << 1)); // 取消挂起状态,将相应位置1
            ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25); //清除 GIC 中断标志位
            break;
		case 58: // k3 按下触发的中断
			mydelay_ms(20); // 按键消抖
            xyf++; // 标志位加1,用于控制 LED 灯的状态
			xyf=xyf%3; // 实现循环控制,使标志位在 0-2 之间循环变化
			EXT_INT41_PEND = EXT_INT41_PEND|(1<<2);  //清除 GPX1_2 中断标志	   
			ICDICPR.ICDICPR1 |= 1<<26;    //清除 GIC GPX1_2 中断标志
			break;
		case 72: // PWM 定时器中断
			mydelay_ms(20); // 消抖
			a++; // 计数器加1
			if(UART2.UTRSTAT2 & 0x1)  //检测发送是否为空
			  {
				gets(ch); // 获取串口输入的数据
				puts(ch); // 将获取到的数据通过串口发送回去
			  }
			if(flag==1) // 执行模式一,所有 LED 灯都亮
			{
					led_on();
					a=0;				
			}
			else if(flag==2) // 执行模式二,依次点亮四个 LED 灯
			{
						GPX2.DAT &= ~(0x1<<7);
						GPX1.DAT &= ~0x1;
						GPF3.DAT &= ~(0x1 << 4);       
						GPF3.DAT &= ~(0x1 << 5); 
						if(a==1){
						GPX2.DAT |= 0x1 << 7;
							}
						if(a==2){
						GPX1.DAT |= 0x1;
							}
						if(a==3){
						GPF3.DAT |= (0x1 << 4);
							}
						if(a==4){
						GPF3.DAT |= (0x1 << 5);
							}	
					a=a%4;		
			}
			else if(flag==0) // 执行模式三,所有 LED 灯都灭
			{
					led_off();
					a=0;
			}
			
		
		PWM.TINT_CSTAT |= 0X1 << 7;      // 清除 PWM 中断状态位
		ICDICPR.ICDICPR1 |= 1<<26;    //清除 GIC GPX1_2 中断标志
       break;	
	default:
	break;
	}
    
	CPU0.ICCEOIR = (CPU0.ICCEOIR&0x3FF)|irq_num; //清除 CPU 中断标志位,以便下一次进行中断操作
}

在UART串口通信的部分,使用初始化UART2串口模块的函数设置使用哪些管脚作为接收端和发送端,数据位数、停止位数、波特率等参数,并实现了向串口发送字符或字符串、从串口接收字符或字符串和比较两个字符串是否相同的函数。当PC给开发板发送不同的控制命令后,会解析出对应的指令,并根据指令修改相应的标志位来完成LED灯的控制。

在k3控制蜂鸣器播放音乐的部分,同样首先记录按下k3按键次数,用标志位a在switch中进行查找对应的现象。当次数为1时,使用PWM定时器播放音乐将PWM控制寄存器TCON的低4位清零,并设置第0位和第3位为1,以启动PWM输出,实现播放音乐;当次数为2时,可以切换歌曲,选择对应的播放位;当次数为3时,将PWM控制寄存器TCON的第0位清零,以停止PWM输出,到达停止播放的效果。

int main()
{
    GPX2.CON = (GPX2.CON & ~(0xf << 28)) | 1 << 28;							//GPX2_7 配置引脚为输出模式
    GPX1.CON = (GPX1.CON & ~(0xf)) | 1; 									//LED3 配置引脚为输出模式
    GPF3.CON = (GPF3.CON & ~(0xf << 16 | 0xf << 20)) | (1 << 16 | 1 << 20);//配置 LED4/LED5 引脚为输出模式
    button_init(); //初始化按键 k2,k3
	pwm_init();
	play_off();
	uart2_init();
	char *q = "hello UART2!";
	puts(q);
	timer_init();
   while (1)
    {
		if(UART2.UTRSTAT2 & 0x1)  // 检测发送是否为空
      {
        gets(ch); // 获取串口输入的数据
		puts(ch); // 将获取到的数据通过串口发送回去
		if(!strcmp(ch, "mode1\r\n")) // 如果接收到 "mode1\r\n" 的数据,进入模式一
			flag = 1;
		if(!strcmp(ch, "mode2\r\n")) // 如果接收到 "mode2\r\n" 的数据,进入模式二
			flag = 2;
		if(!strcmp(ch, "mode3\r\n")) // 如果接收到 "mode3\r\n" 的数据,进入模式三
			flag = 0;
      }	
	switch (xyf) //音乐播放控制
        {
            case 1:
               play1();												 
                break;
            case 2:
               play2();
                break;
            case 0:
                play_off(); 
                break;
            default:
                break;
        }
  
	}
    return 0;
}	  

四、实训结果
在实训过程中遇到较多问题,以下问题较为突出:
问题一:在使用蜂鸣器播放音乐时,按下k3不能够直接切换音乐,只能在当前音乐播放完比赛使才能切换。
解决方法:在播放音符数组中加入了,判断语句,对 xyf 的值进行判断,如果按键次数等于2 则直接退出函数,停止音乐播放。
问题二:编译烧录后蜂鸣器,出现长鸣,播放没有旋律。
解决方法:起初在timer3配置完成后,开发板现象,k2的功能全部实现,按下k2蜂鸣器长鸣,且不能关闭,没有音乐旋律,后来尝试删除了,主函数中多余的流水灯代码,因为在do_irq函数有流水灯代码,尝试的编译烧录,功能恢复正常,达到了实验目的,初步判断为逻辑结构问题。
问题三:对flag和flag1,定义用法有误区。
解决问题:flag是自己定义的记录按键按下的变量,两个按键分别定义为flag、flag1,在switch中flag对应的是一个标志位,只是查找是否对应,并不是赋值,两个变量易混淆所以改成了xyf。
问题四:按键k2和按键k3的功能不能同步实现,播放歌曲社会不能同时点亮LED。
解决方法:添加使用PWM定时器,定时寄存器选择timer3,
并对其进行相关配置,在do_irq函数加入中断ID的对应功能,将流水灯模块添加其中。

  • 35
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值