2024年重庆大学通信工程大二课程:《MCU原理及应用——基于恩智浦S12X的嵌入式系统开发》实验课程记录。(错误在所难免,请多斧正!)
目录
- 前言
- 一、实验目的
- 二、实验环境
- 三、实验内容
- 3.1 四位独立式按键扫描检测
- 3.2 4×4行列式键盘扫描检测
- 代码实现
- 硬件显示
- 3.3 软件译码、静态显示
- 代码实现
- 硬件显示
- 3.4 软件译码,动态扫描
- 代码实现
- 硬件显示
- 总结
前言
一、实验目的
熟悉恩智浦MCU GPIO功能及其应用规则,巩固C语言程序编程方法,熟悉数码显示、独立键盘、行列键盘等工作原理及其应用程序编制技巧,进一步熟悉软硬件连接调试方法。
二、实验环境
CodeWarrior IDE仿真调试软件 + S12XDEV开发板套件。
编程语言选择:单选C语言。
调试连接选择:硬件连接调试(PE USB BDM Multilink)。
三、实验内容
3.1 四位独立式按键扫描检测
SW1~SW4按键分别已上拉接至PT0~PT3,有键按下时返回对应键号1~4,无键按下时返回0。主函数中调用扫描函数,根据得到的键号点亮/熄灭B口对应的LED灯。
见 3.3 独立按键控制LED :实验二 并行I/O接口、IRQ中断-CSDN博客https://blog.csdn.net/m0_73918454/article/details/139250471?spm=1001.2014.3001.5501
3.2 4×4行列式键盘扫描检测
行线、列线已分别跳接在PH0~3和PH4~7检测到的键值送交B口的8位LED灯进行二进制表达。(教材P144)
【上拉电阻(pull-up)作用】
-
确保无按键按下时的默认状态:当没有任何按键被按下时,所有列线通过上拉电阻被拉至高电平(逻辑1)。这样,单片机或微控制器的输入引脚可以默认读取到一个已知的稳定状态,避免了输入信号的不确定性。
-
减少功耗:相比于直接将列线连接到电源,使用上拉电阻可以限制流经电路的电流,从而减少功耗。在没有按键按下的情况下,只有很小的漏电流流过上拉电阻。
-
简化电路设计:上拉电阻使得行列式键盘的每根列线在没有外加驱动的情况下都能保持在一个确定的电平,简化了检测按键逻辑的设计。单片机只需要读取列线的电平变化即可判断是否有按键被按下。
-
避免浮动输入:在没有上拉或下拉电阻的情况下,输入引脚在开路状态下可能会出现“浮空”现象,即电平不确定。上拉电阻确保了即使在没有外部信号连接时,引脚也有一个确定的高电平状态,减少了误读的可能性。
代码实现
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
byte KeyVal;
byte LineCode[4]={ //行扫描码
0xFE,0xFD,0xFB,0xF7
};
byte KeyBoardScan(void);
void delay_ms(unsigned int ms){
int i,j;
for(i=0;i<ms;i++)
for(j=0;j<1333;j++);
}
void main(void) {
/* put your own code here */
DisableInterrupts;
DDRB = 0xFF;
PORTB = 0xFF;
DDRH=0x0F; //PH7~4输入作列线,PH3~0输出作行线
PTH = 0xF0; //行送0,全扫描
for(;;) {
//_FEED_COP(); /* feeds the dog */
KeyVal = KeyBoardScan(); //获取键值(低有效)
PORTB = KeyVal; //送给LED显示
} /* loop forever */
/* please make sure that you never leave main */
}
byte KeyBoardScan(void){
byte i,key;
delay_ms(10); //去抖动
key = PTH; //读取键值
if((key&0xF0)!=0xF0){ //列线不全为1,表示有键按下
for(i=0;i<4;i++){ //逐行扫描
PTH=LineCode[i]; //送行扫描码
_asm("NOP"); //_asm("NOP"); 是一条嵌入在C语言代码中的汇编指令,用于指示编译器插入一个空操作(No Operation)指令。"NOP"指令在执行时什么也不做,只是消耗一个时钟周期。
_asm("NOP"); //此处作用为等待生效
key = PTH; //读取键值(低有效)
if(key!=LineCode[i]) //无键按下时,key == 行扫描码
break; //有按键找到,退出扫描
}
}
else key=0xFF; //无键按下,LED不亮
PTH = 0xF0; //恢复全扫描
return key;
}
【for循环查询缺点】
上方程序主循环不断查询,加重CPU(中央处理器)的负担。采用中断响应可避免:
- 加入中断电路。
- 利用MCU端口中断功能。P口、H口和J口有中断功能。
硬件显示
实验三 3.2 行列式键盘扫描
3.3 软件译码、静态显示
4位连排共阴级数码管(段给高电平亮)中,最低1位显示数字,具体数字由开发板上接T口的2个按键开关决定:SW1按下显示“1”,SW2按下显示“2”,都没按下熄灭显示,同时按下显示“E”。
数码管段(发光二极管简称“段”,共8段)选线已直接连至PA0~PA7;位选线(COM端)通过三极管驱动电路(逻辑有变反)已接在PK0~PK3,则PK0~PK3输出高电平时即为选通对应位的数码管。(教材P149)
【软件译码】指利用查表法获取LED数码管显示的编码。而硬件译码指专用译码,或驱动芯片译码。
【静态显示】两种LED数码管显示方式之一。静态显示亮度高,用于单个数码管,程序简单,CPU负担小,但所需硬件驱动器较多。
【按键去抖动】在检测是否同时按下时,如果在触点抖动期间检测按键的通与断状态,可能会导致判断出错。解决方法:软件延时。检测到有键按下,延时5~10ms再检测。
【LED数码管显示器】LED Segment(部分、段) Displays。
代码实现
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
byte KeyScan(void);
void delay_ms(unsigned int ms);
byte KeyVal;
byte SegCode[4] = {
0x00,0x06,0x5B,0x79
};
void main(void) {
/* put your own code here */
DisableInterrupts;
DDRK = 0xFF;
PORTK = 0x01;
DDRA = 0xFF;
PORTA = 0xFF;
delay_ms(1500);
for(;;) {
KeyVal = KeyScan();
switch(KeyVal){
case 1:PORTA = SegCode[1];delay_ms(200);break;
case 2:PORTA = SegCode[2];delay_ms(200);break;
case 3:PORTA = SegCode[3];delay_ms(200);break;
default:PORTA = SegCode[0];
}
} /* loop forever */
/* please make sure that you never leave main */
}
byte KeyScan(void){
if((PTT&0x01)==0){
delay_ms(10);
if((PTT&0x02)==0){
delay_ms(5);
//while((PTT&0x02)==0);
return 3;
}
//while((PTT&0x01)==0);
return 1;
}
if((PTT&0x02)==0){
delay_ms(10);
//while((PTT&0x02)==0);
if((PTT & 0x01) == 0){
delay_ms(5);
return 3;
}
return 2;
}
//if((PTT&0x03)==0){
// delay_ms(10);
// while((PTT&0x03)==0);
// return 3;
//}
}
void delay_ms(unsigned int ms){
int i,j;
for(i=0;i<ms;i++){
for(j=0;j<1333;j++);
}
}
硬件显示
实验三 3.3 软件译码 静态扫描
3.4 软件译码,动态扫描
4位连排共阴级数码管显示“4321”(没有按键输入)。
数码管段选线已直接连至PA0~PA7;位选线(COM端)通过三极管驱动电路(逻辑有变反)已接在PK0~PK3,即PK0~PK3输出高电平时则为选通对应位的数码管。
【动态扫描】用于多位数码管,利用人眼视觉残留效应进行轮流选中显示(间隔5ms左右),段码线共用,外围器件少,但需要耗费CPU较多时间。
代码实现
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
void delay_ms(unsigned int ms){
int i,j;
for(i=0;i<ms;i++){
for(j=0;j<1333;j++);
}
}
byte SegCode[4] = {
0x66,0x4F,0x5B,0x06
};
void display(){
PORTK = 0x01;
PORTA = SegCode[3];
delay_ms(5);
PORTK = 0x02;
PORTA = SegCode[2];
delay_ms(5);
PORTK = 0x04;
PORTA = SegCode[1];
delay_ms(5);
PORTK = 0x08;
PORTA = SegCode[0];
delay_ms(5);
}
void main(void) {
/* put your own code here */
DisableInterrupts;
DDRA = 0xFF;
PORTA = 0x00;
DDRK = 0xFF;
EnableInterrupts;
for(;;) {
//_FEED_COP(); /* feeds the dog */
display();
} /* loop forever */
/* please make sure that you never leave main */
}
硬件显示
实验三 3.4 软件译码 动态扫描
总结
首先明白概念,一开始不用完全理解,开始写题目遇到问题的话可以回头看。否则如果直接开始,遇到问题的话就无从下手。