2024年重庆大学通信工程大二课程:《MCU原理及应用——基于恩智浦S12X的嵌入式系统开发》实验课程记录。(错误在所难免,请多斧正!🙏)
一、实验目的
掌握C语言综合编程方法,深化并行I/O口、PIT定时器(Period Interrupt Timer)、中断等功能部件的使用规则和应用方法,全面掌握中断处理程序的安排。
二、实验内容
2.1 实现1s精确定时
利用MCU的PIT定时器通道0、中断功能部件,实现1s精确定时( 8MHz总线频率)。
每定时时间到,使开发板上PE2高电平驱动的继电器控制LED10亮灭一下,观察是否进入中断。调试时,可在中断服务程序中设置断点,以方便观察是否进入中断。
【定时时间公式】(比Timer定时时间多一倍,PIT最大定时2.09715 s,8MHZ下)
TimeOutPeriod:定时时间 1s
PITMTLD:微(Micro)定时器装载寄存器 = 159 (设置8位递减计数器载入的值,最大255)
PITLD:定时器装载(Load)寄存器 = 49999 (设置16位递减计数器载入的值,最大65535)
:总线频率 =
【PIT使用与设置】PIT定时器初始化完成前,先不要使能(Enable)它,避免刚开始一段时间的定时不正确。完成装载寄存器的设置、8位微计数器的选择、定时器通道的打开等初始化工作后,再使能PIT。
代码实现
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
void main(void) {
/* put your own code here */
DisableInterrupts;
DDRE = 0xFF; //PE2为输出
PORTE = 0x0F; //LED10灭
PITCFLMT =0x00; //关闭PIT定时器
PITTF = 0x01; //PIT通道0超时标志位写1清零
PITMUX = 0x00; //PIT通道0使用微计数器0
PITMTLD0 = 159; //设置微计数器0的装载寄存器
PITLD0 = 49999; //设置16位计数器0的装载计数器
PITINTE = 0x01; //使能PIT通道0中断
PITCE = 0x01; //使能通道0计数
PITCFLMT = 0x80; //使能PIT定时器
EnableInterrupts;
for(;;) {
//_FEED_COP(); /* feeds the dog */
} /* loop forever */
/* please make sure that you never leave main */
}
#pragma CODE_SEG NON_BANKED //中断服务函数定位声明
interrupt 66 void PIT0_ISR(){
PITTF = 0x01; //PIT通道0超时标志位写1清零
PORTE = ~PORTE; //1s到,PE2电平翻转,LED10亮灭交替
}
硬件显示
实验五_2_1_实现1s定时
2.2 LED数码管显示4×4行列式键号
设计软件译码、动态扫描的4位连排共阴级数码管显示函数(display),以及中断响应方式的进行4x4行列式键盘(PH0~7)扫描检测中断服务函数(PTH_ISR),主函数(main)中从而将键号按01~16在数码管的低2位上以动态扫描方式显示出来。(教材P149)
代码实现
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
byte KeyVal,KeyNum,tmp;
byte SegCode[16] ={ //共阴极数码管段码0~F
0x3F,0x06,0x5B,0x4F,0x66,
0x6D,0x7D,0x07,0x7F,0x6F,
0x77,0x7C,0x39,0x5E,0x79,
0x71
};
byte LineCode[4] = {0xFE,0xFD,0xFB,0xF7}; //行扫描码
void delay_ms(unsigned int ms){
int i,j;
for(i=0;i<ms;i++){
for(j=0;j<1333;j++);
}
}
void display(){
PORTK = 0x00;
tmp = KeyNum&0x0F;
PORTA = SegCode[tmp];
PORTK = 0xF1;
delay_ms(5);
tmp = ((KeyNum&0xF0)>>4);
PORTA = SegCode[tmp];
PORTK = 0xF2;
delay_ms(5);
}
void main(void) {
/* put your own code here */
DisableInterrupts;
DDRA = 0xFF; //PA口输出
PORTA = 0x00; //输出低电平
DDRK = 0xFF;
DDRH = 0x0F;
PIFH = 0xFF;
PIEH = 0xF0;
PTH = 0xF0;
EnableInterrupts;
for(;;) {
//_FEED_COP(); /* feeds the dog */
display();
} /* loop forever */
/* please make sure that you never leave main */
}
#pragma CODE_SEG NON_BANKED
interrupt 25 void PTH_ISR(void){
int i;
delay_ms(10); //延时去抖动,等待键按下
for(i=0;i<4;i++){ //逐行扫描
PTH = LineCode[i]; //送行扫描码
_asm("NOP");
_asm("NOP"); //等待生效
KeyVal = PTH; //读取键值
if(KeyVal!=LineCode[i]){ //当前行有键按下时则按键找到,退出扫描
break; //无键按下则二者相等
}
}
if((KeyVal&0xF0)==0xF0){ //高4位列线值为F表示无键按下
KeyNum = 0x00;
}
switch(KeyVal){
case 0xEE:KeyNum = 0x01;break;
case 0xDE:KeyNum = 0x02;break;
case 0xBE:KeyNum = 0x03;break;
case 0x7E:KeyNum = 0x04;break;
case 0xED:KeyNum = 0x05;break;
case 0xDD:KeyNum = 0x06;break;
case 0xBD:KeyNum = 0x07;break;
case 0x7D:KeyNum = 0x08;break;
case 0xEB:KeyNum = 0x09;break;
case 0xDB:KeyNum = 0x10;break;
case 0xBB:KeyNum = 0x11;break;
case 0x7B:KeyNum = 0x12;break;
case 0xE7:KeyNum = 0x13;break;
case 0xD7:KeyNum = 0x14;break;
case 0xB7:KeyNum = 0x15;break;
case 0x77:KeyNum = 0x16;break;
}
PIFH = 0xFF;
PTH = 0xF0;
}
硬件显示
实验五_2_2显示行列式键号
2.3 10s倒计时秒表设计
硬件定时器选PIT通道0中断,秒表数字变动使用动态扫描显示的低2位LED数码管,秒表启动使用按键SW5(即IRQ中断引脚)。
特别注意:10s到9s的时间也须间隔精确。
代码实现
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
//变量设置
byte IRQ_IntFlag = 0; //IRQ中断标志
byte num[11] ={ //数字0~10
0x00,0x01,0x02,0x03,0x04,
0x05,0x06,0x07,0x08,0x09,
0x10};
byte SegCode[10] ={ //0~9的共阴极数码管段码
0x3F,0x06,0x5B,0x4F,0x66,
0x6D,0x7D,0x07,0x7F,0x6F,
};
byte tmp;
byte cnt = 10; //指10~0秒
//延时
void delay_ms(unsigned int ms){
int i,j;
for(i=0;i<ms;i++){
for(j=0;j<1333;j++);
}
}
//LED动态扫描显示
void display(){
PORTK = 0x00;
//个位显示
tmp = num[cnt]&0x0F;
PORTA = SegCode[tmp];
PORTK = 0xF1;
delay_ms(5);
//十位显示
tmp = ((num[cnt]&0xF0)>>4);
PORTA = SegCode[tmp];
PORTK = 0xF2;
delay_ms(5);
}
void main(void) {
/* put your own code here */
DisableInterrupts;
//LED设置
DDRA = 0xFF;
PORTA = 0xFF;
DDRK = 0x0F;
//设置PIT定时器(定时1s)
PITCFLMT =0x00; //关闭PIT定时器
PITTF = 0x01; //PIT通道0超时标志位写1清零
PITMUX = 0x00; //PIT通道0使用微计数器0
PITMTLD0 = 159; //设置微计数器0的装载寄存器
PITLD0 = 49999; //设置16位计数器0的装载计数器
PITINTE = 0x01; //使能PIT通道0中断
PITCE = 0x01; //使能通道0计数
PITCFLMT = 0x80; //使能PIT定时器
//SW5(IRQ中断设置)
IRQCR = 0xC0; //外部中断IRQ使能,下降沿触发
EnableInterrupts;
for(;;) {
//计时前LED先显示10
if(IRQ_IntFlag == 0){
display();
}
//计时开始
if(IRQ_IntFlag == 1){
display();
if(cnt == 0){
IRQ_IntFlag = 0; //10s倒计时结束
cnt = 10; //重新显示10,等待计数开启
}
//_FEED_COP(); /* feeds the dog */
} /* loop forever */
/* please make sure that you never leave main */
}
}
#pragma CODE_SEG NON_BANKED //PIT定时中断服务函数定位声明
interrupt 66 void PIT0_ISR(){
//IRQ中断后才开始计时
PITTF = 0x01; //PIT通道0超时标志位写1清零
if(IRQ_IntFlag == 1){
PITTF = 0x01; //PIT通道0超时标志位写1清零
cnt--;
}
}
#pragma CODE_SEG NON_BANKED //中断服务函数定位声明
interrupt 6 void IRQ_ISR(){
IRQ_IntFlag = 1; //置启动标志1
}
硬件实现
实验五_2_3 10s倒计时
总结
要有耐心...很多代码之前有,复制过来修改下数据就能用了。
代码一定要有注释。时间久的话很容易忘记怎么写,重看代码又看不懂,要复制修改数据又不知道相应代码什么意思。