蔡逢煌, 王 武
(福州大学 电气工程与自动化学院, 福建 福州 350116)
我院在“单片机原理与应用”这门课程的教学上逐步形成了具有自己特点的教学模式,我们把该课程的教学分成七大模块:GPIO模块、中断模块、定时器模块、PWM 模块、捕获模块、AD模块和通信模块,把各个模块之间通过案例联系起来,使各个模块的教学即相对独立又前后联系。
其中,定时器模块、捕获模块、PWM模块都是与时间有关系的,其处理本质上都是通过计数器来实现的。定时器是通过计数来实现计时的;PWM模块在计数的基础上,实现特定时刻对引脚输出状态的控制;捕获模块则是把引脚输出状态改变的那个时刻对应的计数值记录下来。
在先前的教学中,这三个模块是相对独立讲授的,没有把它们之间的内在联系介绍给学生。因此,学生学完后,只懂得单个模块的应用。由于没有实际工程经验,不懂得如何把它们联系起来应用。在新教学安排上,我们采用循序渐进的方法,结合案例引入解决了这一问题。首先学习定时器模块,然后学习PWM模块,最后学习捕获模块。在教学案例的设计上特别关注这些模块之间的内在联系,逐渐深入,最后接近于工程实际。
教学案例的设计是依托硬件平台实现的。硬件平台采用价格低廉、携带方便和功能强大的“ 口袋实验室”,也就是TI公司的C2000 Piccolo TMS320F28027 LaunchPad最小系统板。该板通过USB接口与电脑连接,利用CCS软件开发平台进行程序的编写、调试和烧写。“口袋实验室”使学生能超越课堂的空间和时间的限制,完成应用程序的设计和理论算法的实践,从而减轻了学生与教师的压力[1-3]。
该板上有1个按键和4个LED灯。案例的设计是基于按键和LED以及编制程序实现的。程序代码要求可移植,也就是后面的案例可以在前面案例的基础上完成,代码有继承性。
通过这几年的教学实践,我们设计和这些模块相关的案例如下。
设计一个按键控制显示模式的工程案例,要求:
(1) 按键有:短按、长按、双击三种功能;
(2) 显示模式:跑马灯显示;显示间隔时间由定时器计量。
(3) 按键控制功能:短按减少跑马灯的显示间隔时间;长按增加跑马灯的显示间隔时间;双击恢复跑马灯的显示间隔时间为初始时间。
(1) 按键有:短按、长按、双击三种种功能(可直接移植定时器案例的按键处理程序);
(2) 显示模式:跑马灯模式或者呼吸灯模式。呼吸灯模式是利用PWM控制LED的显示亮度,从暗逐渐到亮。全亮后再逐渐变暗,全暗后,重复进行。跑马灯模式继承1.1的案例。
(3) 按键控制功能:短按减少显示间隔的周期;长按增加显示间隔的周期;双击进行显示模式的切换。
(1) 继承之前案例的按键处理功能和呼吸灯显示功能。
(2) 显示模式:双击进行显示切换----呼吸灯模式和捕获值显示模式。
(3) 呼吸灯模式------LED1慢慢亮,到最亮的时候保持住,然后LED2开始慢慢变亮,到最亮的时候保持住,依次四个LED灯完全亮,保持一秒钟,LED4慢慢暗,到熄灭后,LED3开始慢慢变暗,依次四个LED灯完全暗。熄灭一秒钟,再重复。(可移植案例1.2的呼吸灯显示代码进行修改)。
(4) 按捕获值控制LED显示亮度--------对PWM输出进行捕获(把PWM3A引脚接到捕获口),根据捕获的占空比值控制LED显示亮度。
0--25%: LED1(由PWM1A控制)亮度根据占空比按比例调节;
25%--50%: LED1全亮、LED2(由PWM1B控制)亮度根据占空比(0-25%)按比例调节;
50%--75%: LED1、LED2全亮, LED3(由PWM2A控制)亮度根据占空比(0-25%)按比例调节;
75%--100%: LED1、LED2、 LED3全亮,LED4(由PWM2B控制)亮度根据占空比(0-25%)按比例调节;
(5) 按键控制功能:短按-----PWM3A输出占空比减小;长按-----PWM3A输出占空比增大;双击-----切换显示模式。
1.1节的定时器案例涉及到“单片机原理与应用”课程GPIO模块、定时器模块、中断模块的应用。寄存器的操作调用TI提供的底层驱动库函数。部分代码如下:
/*定时器中断周期1ms*/
interrupt void myTimer_CpuTimer0_isr(void)
{
msecond_key_counter++; //按键识别用
display_delay++; //跑马灯显示间隔时间
PIE_clearInt(myPie, PIE_GroupNumber_1);
//中断应答标志位清零
}
说明:定时器模块提供授时服务。通过相关变量把时间传递给按键识别模块和跑马灯控制模块。
学生在这部分容易出现的问题:在定时器函数里面实现按键识别和跑马灯控制,程序功能没有按模块化编程,各个功能融合一起时,程序可读性差,可移植性差。
/*跑马灯程序*/
void LED_Display(void)
{
if(display_delay > time_display_delay)
//time_display_delay为按键更改的时间延时
{
LED_off(LED[LED_Count]); //熄灭当前LED
LED_Count++; //指向下一个LED
if(LED_Count >=4) LED_Count=0;
LED_on(LED[LED_Count]); //LED点亮
PIE_clearInt(myPie, PIE_GroupNumber_1);
//中断应答标志位清零
}
}
按键对应的IO口为外部中断的输入源。按键按下为高电平,按键放开为低电平。上升沿和下降沿都可以触发外部中断。一个按键要识别成三个不同的功能:短按、长按、双击。按键的算法思路为:上升沿中断时判断是否双击,并清零相关时间变量。下降沿时判断长按、短按。
学生在这部分功能实现上存在困难,主要是对算法的分析能力比较薄弱。特别是对双击的处理。程序编制方式虽然多样,但是总体上不够优化。
/*按键中断程序*/
interrupt void KEY_XINT1_isr3(void)
{
if(msecond_key_counter < 10) //两次进入中断的时间小于10ms,判断为抖动
{
PIE_clearInt(myPie, PIE_GroupNumber_1);
return; //为按键抖动,退出中断
}
//上升沿
if(KEYPRESSED == GetKeyStatus(KEY1))
{
if(msecond_key_counter <250) //距上次按键放开时间小于250ms,判断为双击 {
Flag_KEY_PRESS_TWICE = 1; //双击标志位,等待按键释放后再处理 }
else key_status = 0;
msecond_key_counter = 0; //清计时
}
//下降沿时对应按键放开,进行按键动作的确认
else if(KEYPRESSED != getKeyStatus(KEY1))
{
if(Flag_KEY_PRESS_TWICE == 1)
{
key_status = KEY_PRESS_TWICE; //双击
Flag_KEY_PRESS_TWICE = 0;
}
else if(msecond_key_counter > 350)
{
key_status = KEY_PRESS_LONG; //长按
}
else if(msecond_key_counter > 100)
{
key_status = KEY_PRESS_SHORT; //短按
}
msecond_key_counter = 0; //清计时
}
PIE_clearInt(myPie, PIE_GroupNumber_1);
}
/*按键短按、长按、双击处理*/
void KEY_MODE(void)
{
if(msecond_key_counter > 500)
{ switch(key_status)
{
case 1: time_dispaly_delay=time_dispaly_delay-200;
// 案例1
// mypwm_period = mypwm_period - 200; //案例2
// myCmp3A=myCmp3A-3000; //案例3
key_status = KEY_PRESS_NO;
break;
case 2: time_dispaly_delay=time_dispaly_delay+200;
// 案例1
// mypwm_period = mypwm_period + 200; //案例2
// myCmp3A=myCmp3A+3000; //案例3
key_status = KEY_PRESS_NO;
break;
case 3:
// time_dispaly_delay = TIME_INITIAL;
// 恢复初始值
display_mode=!display_mode; //显示模式切换
key_status = KEY_PRESS_NO;
break;
default: break;
}
}
}
三个案例的按键程序基本一样,可以直接进行移植,在按键处理程序里面针对功能的不同可做部分修改。
学生容易出现的问题是:程序没有按模块化编程,各个模块的程序代码嵌套在一起,可移植性差。
/*pwm中断函数*/
interrupt void LED_EPWM3_isr(void)
{
PWM_huxideng1(); //呼吸灯改变占空比
PWM_capture(); //捕获反馈模式改变占空比
if(pwm_mode == 0) //呼吸灯显示模式
{
PWM_setCmpA(myPwm1,myCmp1A_huxi);
PWM_setCmpB(myPwm1,myCmp1B_huxi);
PWM_setCmpA(myPwm2,myCmp2A_huxi);
PWM_setCmpB(myPwm2,myCmp2B_huxi);
}
if(pwm_mode == 1) //捕获显示模式
{
PWM_setCmpA(myPwm1,myCmp1A_cap);
PWM_setCmpB(myPwm1,myCmp1B_cap);
PWM_setCmpA(myPwm2,myCmp2A_cap);
PWM_setCmpB(myPwm2,myCmp2B_cap);
}
PWM_setCmpA(myPwm3,myCmp3A);
//PWM3A输出
PWM_clearIntFlag(myPwm3); //清中断标志位
PIE_clearInt(myPie, PIE_GroupNumber_3);
}
PWM_huxideng1()函数对应呼吸灯模式改变占空比,PWM_capture()函数对捕获反馈模式改变占空比,两种方式相对独立运行, 按键切换显示方式。当一种显示模式工作时,另一种仍然在后台运行。
学生容易产生的问题是:两种显示模式互相影响,即当一种模式运行时,另一种就停止运行。这些问题都是对软件的模块化思想不熟悉导致的。
由于篇幅限制,对其他软件代码感兴趣者可以跟笔者联系获得。
从以上的案例设计来看,由于前后模块有继承性,通过这种训练培养学生对软件工程的模块化思维。从近三年的教学效果来看,学生在学完这些模块后,对知识的理解和接受程度上出现明显的分化,有的学生逻辑思维能力和编程能力比较强,能顺利地完成课堂布置的作业(根据统计,这部分学生只占10%左右);其他的大部分学生,完成相关的课堂作业有困难,主要体现在:①对模块的工作原理较难理解;②对模块大概理解了,但是不知道怎么转换为程序代码;③对模块理解了,也能转换为程序代码,但是如何完成工程实现存在困难;④可以完成功能实现,但是整个程序结构混乱,程序可读性差。
导致产生这些问题的原因是:①基础比较薄弱,“电路”和“数字电子技术”课程的知识点没掌握好;②计算机的相关概念没有理解,对软件控制硬件的原理理解不清楚;还有C语言的基础较薄弱;③对模块之间的联系缺乏整体概念,不善于从系统的层面来分析问题;④缺少软件工程的思维,程序的结构化、标准化理解不透彻,软件代码质量低。
针对上述存在的问题,在后续的教学过程中,我们打算努力做到:学生首先独立完成案例,然后教师对每个学生的软件进行检测,针对学生学习过程中存在的典型问题进行分析讲解,然后学生对自己编写的程序再次进行改进,逐步完善,达到程序的标准化和规范化。
定时器模块、PWM模块、捕获模块是单片机的重要组成部分,它们都与时间有关。通过案例的设计,既让学生懂得单个模块的使用,更懂得如何综合应用各个模块完成一项工程,满足软件的模块化、规范化和可移植性。实践证明,引入工程案例和任务驱动法开展课堂和实验实践教学,能激发学生学习兴趣,培养学生工程素养。
[1] 张江印.高校单片机教学模式的研究[J].上海:实验室研究与探索,2011,30(9):103-106.(蔡逢煌等文)
[2] 蔡逢煌,王武. 嵌入式类课程教学的思考和实践[J],南京:电气电子教学学报,2015,37(1):24-25.
[3] 曹静,江世明.模块化单片机实验平台的设计[J].邵阳:邵阳学院学报(自然科学版),2011,08(3):23-25.