完整的课程设计论文下载(word格式 可编辑):
基于K20的反应速度测试.docx
(6.65 MB, 下载次数: 43)
下面是部分内容预览(无图版):
Project1: 反应速度测试(主要内容,有扩展) 一、 设计内容 (1) 数码管倒计时进入准备状态; (2) 亮灯; (3) 测试人员看见亮灯后按键; (4) MCU记录反应时间; (5) 在数码管上显示反应时间; (6) 按另一键重新开始; (7) 如果出现抢跑的情况,给出提示。 二、设计思路 (1)数码管倒计时进入准备状态:检测SW1是否按下,如果按下,则让数码管SELECT-LED0按顺序显示四个数字:3、2、1、0。 (2)判断是否抢跑并亮灯:在倒计时中,判断SW5是否按下,如果按下,则为抢跑,点亮led1,2,3,4,且数码管显示0;如果倒计时过程中SW5没有按下,则在数码管正常倒计时到0的同时点亮led1灯; (3)设定定时中断中断时间:设定PIT时钟通道0的倒计时开始时间,令number_ms等于1,则设定了1ms的定时中断; (4)定时中断设计:每次中断都执行判断SW5是否按下,如果没有按下,则计数count=count+1;如果SW5按下,则进入数码管显示子程序,数码管显示当前count值,即单位为毫秒; (5)重新重启:循环检测SW1状态,SW1一旦按下,则重新进入程序初始,实现重新启动功能; 三、主要程序流程图(加简单文字解释)
file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C46.tmp.jpg 四、程序清单(加注释) 4.1:程序初始化: 4.1.1 数码管显示初始化: //第一步,使能port A和port D, 即将系统时钟门控寄存器SIM_SCGC5的第9位和第12位置1 IM_SCGC5 |=(0x1000 | 0x0200); //第二步,设置port A和port D为通用输入输出端口 PORTA_PCR12=0x100; PORTA_PCR13=0x100; PORTA_PCR14=0x100; PORTA_PCR15=0x100; PORTA_PCR16=0x100; PORTA_PCR17=0x100; PORTD_PCR0 = 0x0100; PORTD_PCR1 = 0x0100; PORTD_PCR2 = 0x0100; PORTD_PCR3 = 0x0100; PORTD_PCR4 = 0x0100; PORTD_PCR5 = 0x0100; PORTD_PCR6 = 0x0100; PORTD_PCR7 = 0x0100;
4.1.2 LED灯初始化: SIM_SCGC5|=SIM_SCGC5_PORTC_MASK; //使能port C,port C 的0到11位设置为通用输入输出端口 PORTC_PCR0=PORT_PCR_MUX(0X1); //LED1 PORTC_PCR1=PORT_PCR_MUX(0X1); PORTC_PCR2=PORT_PCR_MUX(0X1); PORTC_PCR3=PORT_PCR_MUX(0X1); //LED2 PORTC_PCR4=PORT_PCR_MUX(0X1); PORTC_PCR5=PORT_PCR_MUX(0X1); PORTC_PCR6=PORT_PCR_MUX(0X1); //LED3 PORTC_PCR7=PORT_PCR_MUX(0X1); PORTC_PCR8=PORT_PCR_MUX(0X1); PORTC_PCR9=PORT_PCR_MUX(0X1); //LED4 PORTC_PCR10=PORT_PCR_MUX(0X1); PORTC_PCR11=PORT_PCR_MUX(0X1); GPIOC_PDDR|=0XFFF; //设置port C的第0位到第11位为输出 GPIOC_PDOR=0XFFF; //将存放port C输出数据的寄存器相应位置1,表示LED灯全灭 4.1.3 左右按键初始化:
SIM_SCGC5|=SIM_SCGC5_PORTB_MASK; //使能port C SIM_SCGC5|=SIM_SCGC5_PORTE_MASK; //设置port B和port E为通用输入输出端口,使能中断,并设置中断方式为下降沿触发中断(将第0位和第1位置1,边沿触发使能;将IRQx位设置为1010表示下降沿触发) PORTB_PCR17=0x0A0103; PORTB_PCR16=0x0A0103; PORTB_PCR11=0x0A0103; PORTB_PCR10=0x0A0103; PORTE_PCR2=0x0A0103; PORTE_PCR3=0x0A0103; PORTE_PCR4=0x0A0103; PORTE_PCR5=0x0A0103; GPIOB_PDDR&= ~0x30C00; //按键端口全部设置为输入 GPIOE_PDDR&= ~0x3C; 4.1.4 IRQ使能初始化: int div; div=irq/32; //得到非优先寄存器的寄存器号 if (irq > 91) irq=91; //确定irq号为有效的irq号 switch(div) { case 0x0: NVICICPR0=1<<(irq%32); //中断清除使能 NVICISER0=1<<(irq%32); //中断设置使能 break; case 0x1: NVICICPR1=1<<(irq%32); NVICISER1=1<<(irq%32); break; case 0x2: NVICICPR2=1<<(irq%32); NVICISER2=1<<(irq%32); break; } 注意:调用该函数时需要带参数 IRQ number=1; 4.1.5 PIT时钟初始化: /*开启PIT 时钟,即将系统时钟门控寄存器SIM_SCGC6的PIT位置1*/ SIM_SCGC6|=SIM_SCGC6_PIT_MASK; PIT_MCR&=~(PIT_MCR_MDIS_MASK); //使能PIT时钟模块 PIT_LDVAL0=20000*number_ms; //设定PIT时钟通道0的倒计时开始时间 PIT_TCTRL0|=PIT_TCTRL_TIE_MASK;// 使能PIT时钟通道0的IRQ功能 PIT_TCTRL0|=PIT_TCTRL_TEN_MASK; //使能定时器 4.2 SWI中断程序: int a[4]={3,2,1,0}; //倒计时 for(i=0;i<=3;i++) { if(PORTE_ISFR&0X04||PORTE_ISFR&0X020) GPIOC_PDOR&=~0x249; //若抢跑,则四个LED灯均亮 else { GPIOD_PDOR = num[a]; GPIOA_PDOR = Select_LED0; if(a!=0) delay2(); } //3,2,1后面均有延时,0后面没有,保证数到0时灯即亮 }//数码管显示倒计时 GPIOC_PDOR&=~0x01; //LED1亮 PORTB_ISFR |= 0x00FF00; 4.3 定时中断程序: 根据前面的初始化程序可知,定时器中断周期为1ms,因此计数器counter加1,对应的延迟时间为1ms,为了简化程序,我们在数码管上显示的时间单位为ms,即测量反应时间的灵敏度为1ms;这样我们只需把计数器counter的值转化为十进制,在数码管上显示出来即可。 void pit_channel0_ISR(void) { if((PORTE_ISFR&0X04)==0) count++; //反应键未按下,计时 else renew(count); //反应键按下,停止计时,用数码管显示 PIT_TFLG0|=PIT_TFLG_TIF_MASK;//Clear the flag of channel0,PIT } 4.4 数码管显示子程序: void renew (int s) { int i; int f=s; f=f%1000000; int a[6]={10,10,10,10,10,10}; Select_LED=Select_LED0; for(i=5;i>=0;i--) { a=f%10; f/=10; if(f==0) break; } for(i=5;i>=0;i--) { GPIOD_PDOR = num[a]; GPIOA_PDOR =Select_LED; Select_LED=Select_LED<<1; Select_LED++; delay1(); } } 4.5 扩展部分程序: 4.5.1 数码管显示程序扩展: void renew1(int s) { int i; int f=s; f=f%1000; int a[6]={10,10,10,10,10,10}; Select_LED=Select_LED0; for(i=2;i>=0;i--) { a=f%10; f/=10; if(f==0) break; } for(i=2;i>=0;i--) { GPIOD_PDOR = num[a]; GPIOA_PDOR =Select_LED; Select_LED=Select_LED<<1; Select_LED++; delay1(); } } 同理,void renew2(int s)与void renew1(int s)非常相似,只要把LED=Select_LED0改为LED=Select_LED3即可。 4.5.2 定时中断程序扩展: void pit_channel0_ISR(void) { if((PORTE_ISFR&0X04)==0) count1++; //反应键sw5未按下,计时 else if((PORTE_ISFR&0X04)!=0) renew1(count1); //反应键按下,停止计时,用数码管显示 if((PORTE_ISFR&0X020)==0) count2++; //反应键sw8未按下,计时 else if((PORTE_ISFR&0X020)!=0) renew2(count2); //反应键按下,停止计时,用数码管显示 PIT_TFLG0|=PIT_TFLG_TIF_MASK; } 五、调试过程 1、在调试过程中,我们遇到了摁键中断和定时中断冲突的情况,后来通过一点一点理顺思路发现,正在运行摁键中断时,即使初始化了PIT时钟模块,也只能开启定时器倒计时的功能,但是定时器倒计时到0的时候并不能进入定时中断子程序中执行相应语句。 2、倒计时至0时,LED1灯没有立即亮,而是延迟了一会儿才亮,检查发现,原来程序中3,2,1和0后面均有延时,于是我们就将0从for循环中提取出来,单独显示,并且不加延时,保证数到0时灯即亮。 3、因为要使用摁键SW1来进行“开始”和“重启”操作,必须处理摁键抖动的问题,我们尝试了各种不同长度的延时,终于找到了合适时长,不影响操作和响应,也可以防抖。 4、我们要测试指示灯亮到摁键被摁下的反应时间,就必须防止摁键被重复触发。除了使用延时去除抖动的方式来防止硬件干扰,我们还必须利用在其它端口的中断服务子程序中清除该摁键中断标志的方式来防止人为错误操作影响测试结果的显示。 5、我们在定时中断服务子程序中要用到PORTE的中断标志位,如果在PORTE_Hanlder里清除中断,那么将不会执行定时中断服务子程序,也就不会有任何显示,所以应该把PORTE的中断清除语句拿出来,放到PORTB_Hanlder程序中,因为SW1是复位键,每按下一次,表示程序初始化,需要清除所有操作。 6、当反应时间较长时,数码管显示的数值不合逻辑,后来考虑到数码管可能出现溢出现象,于是就添加了f=f%1000000。 六、实际结果(有相关照片) | file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C57.tmp.pngfile:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C58.tmp.png |
| file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C59.tmp.png | | file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C5A.tmp.png | | file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C6B.tmp.png |
七、结论与改进(改进可以只有想法,没有实现) 1、结果:实现了基本功能; 2、实现了两个人比赛,分别显示各自的反应时间的功能; 3、实现了单人、双人两种模式且完成了单/双人模式切换; 4、实现了以秒和毫秒两种单位的数值显示; 5、改进:将倒计时部分做成准确的3秒倒计时,而不是仅用delay函数做。 |