文章目录[隐藏]
第3章 三八译码器
-
3.4 74HC138三八译码器的应用
-
变量的存储类别分为自动、静态、寄存器和外部这四种。auto、static
静态局部变量,在整个生存期中只赋一次初值;它可以保持前次的执行结果。若某个变量只在一个函数中使用,且在该函数的本次调用中该变量值的改变要依赖于上一次调用函数时的值。
第8章 函数进阶与按键
- 晶振:无源晶振也称为晶体(crystal),有缘晶振(振荡器,oscillator)
有源晶振:需要供电,信号质量好,利用石英晶体的压电效应来起振
无源晶振:内部的振荡电路振荡,两侧需要电容(10~40pF),2个引脚无正负之分
第9章 步进电机与蜂鸣器
28BYJ-48
28mm,B步进电机,Y永磁,J减速,48四相八拍;360/(8*4)=11.25/2=5.625
步进电机的启动频率:空载情况下能够正常启动的最高脉冲频率
9.3.5程序说明:
EA=0;
beats=(angle*4079)/360;
EA=1;
- 因为beats是unsigned long型,占用4个字节,而单片机是8位的,那么对它的赋值最少也要4次才能完成。假如在其中一个字节赋值后发生中断,那么数据就有可能不准确。
赋值语句在执行前关闭中断,执行完毕后,又打开中断,为什么这样做呢?
9.4 蜂鸣器
结构区分:压电式和电磁式;驱动方式区分:有源与无源。是否带振荡源。
电感:电流不能突变,导通时电流是逐渐加大的,这点没问题,但当关断时,电路截断,那么储存的电流往哪儿去呢,电感电流造成了反向冲击。这就是续流二极管名称的由来。
10.1 数字秒表实例
- C语言不同类型运算的时候,数值会转换为同一类型运算,是每一步运算都会进行识别判断,不是式子整体判断。
- 强制类型转换
- 赋值:截取
- bit类型特殊,
bit a=0; unsigned char b; a=(bit)b;
使用bit做强制类型转换,不是取b的最低位,它会判断b是0还是非0,若果是0,那么a=0;否则a=1;
字节操作技巧
- 数码管:digital tube
数码管消隐、数码管鬼影解决
- 刷新之前关闭所有的段,改变好了位选后,在打开段即可
- 关闭数码管的位,赋值过程做好后,再重新打开即可。
中断优先级
- 抢占优先级和固有优先级
- 抢占:优先级高的中断可以打断优先级低的中断
- 固有优先级不具有抢占性,也就是在低优先级中断执行过程中又发生了高优先级的中断,低优先级中断不能被打断,那么这个优先级有什么用呢?答案:在多个中断同时发生时仲裁,特别是,出于某种原因,我们暂时关闭了总中断,执行完代码后,又EA=1;那在中断关闭期间内就可能有多个中断发生,但因为EA=0;得不到相应,一旦EA=1,它们就会同时相应,这时必须有个先后顺序。
4.7 keil软件演示
simulator:模拟器
keil有的位置可以设置断点,有的地方不可以设置断点,可以通过在工程选项里把优化等级设置为0,就是告诉keil不要进行优化,那在任何代码位置都能设置断点。
10.1.5 秒表程序
程序涉及:定时器,数码管、中断、按键,
#include <reg52.h>
#define digitalTubeIO P0 //与数码管相连的74HC573芯片对应接口是P0
//普中科技开发板中hc138译码器的使能脚:E1、E2脚接地,E3接VCC;也就是使能端永远有效
sbit LS138A0=P2^2;
sbit LS138A1=P2^3;
sbit LS138A2=P2^4; //A2=1;A1=0;A0=0;对应值为4,0b11101111
//共阴极数码管编码
unsigned char digitalTubeChar[]={0x3f, 0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
// 共阳极数码管编码
//digitalTubeChar[]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8
// ,0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
sbit KEY1=P3^0;
sbit KEY2=P3^1;
sbit KEY3=P3^2;
sbit KEY4=P3^3;
unsigned char digitalTubeBuf[]={ //数码管显示缓存
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned char keyStatus[]={ //按键的当前状态
1, 1, 1, 1
};
bit stopwatchRunning = 0; //秒表运行标志
bit stopwatchRefresh = 1; //秒表计数刷新标志
unsigned char decimalPart = 0; //秒表的小数部分
unsigned char integerPart = 0; //秒表的整数部分
unsigned char T0RH; //定时器T0重载值的高字节
unsigned char T0RL; //定时器T0重载值的低字节
void configTimer0(unsigned int ms);
void stopwatchDisplay();
void keyDriver();
void main()
{
EA = 1; //打开总中断
configTimer0(2); //配置T0定时器定时2ms
while(1)
{
if (stopwatchRefresh) //判断秒表状态是否正在运行。
{
stopwatchDisplay(); //运行秒表显示程序;
}
keyDriver(); //调用按键扫描函数
}
}
/**
* 功能:配置并启动定时器T0,
* 参数:ms——定时时间间隔
**/
void configTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
//计算定时器T0寄存器的重载值;
tmp = 12000000 / 12; //得到1秒的机器周期数
tmp = tmp /1000; //1ms的机器周期数
tmp = tmp * ms; //计算延时ms所需计数值
tmp = 65536 - tmp ; //计算定时器重载值
tmp = tmp + 18; //补偿中断响应延时造成的误差
T0RH=(unsigned char)(tmp>>8); //定时器T0重载时高字节寄存器
T0RL=(unsigned char)tmp;
TMOD &=0xF0; //T0控制位清零,T1控制位不变
TMOD |=0x01; //设置T0定时器的模式为模式1;T1保持不变
TH0=T0RH;
TL0=T0RL; //装入重载值
ET0=1; //T0中断有效
TR0=1; //启动T0
}
/**
* 功能:秒表计数显示函数
* 参数:无
**/
void stopwatchDisplay()
{
signed char i;
unsigned char buf[4]; //数据转换缓冲区
//转换2位小数部分
digitalTubeBuf[0]=digitalTubeChar[decimalPart % 10];
digitalTubeBuf[1]=digitalTubeChar[decimalPart / 10] ;
//转换整数部分
buf[0]=integerPart % 10;
buf[1]=integerPart /10 % 10;
buf[2]=integerPart / 100 % 10;
buf[3]=integerPart /1000 % 10;
for(i=3;i>0;i--) //不显示整数部分前导0,转换为空字符
{
if (buf[i]==0)
digitalTubeBuf[i+2]=0x00;
else
break;
}
for(;i>=0;i--) //有效数字位转换为显示字符
{
digitalTubeBuf[i+2]=digitalTubeChar[buf[i]];
}
digitalTubeBuf[2] |= 0x80; //点亮小数点
}
/**
* 功能:秒表启停函数
* 参数:
**/
void stopwatchAction()
{
if(stopwatchRunning)
stopwatchRunning=0;
else
stopwatchRunning=1;
}
/**
* 功能:秒表复位函数
* 参数:无
**/
void stopwatchReset()
{
stopwatchRunning =0; //停止秒表
decimalPart = 0;
integerPart = 0;
stopwatchRefresh = 1; //置刷新标志
}
/**
* 功能:按键驱动函数,按键检测,调度响应动作函数,在主函数中调用
* 参数:无
**/
void keyDriver()
{
unsigned char i;
static unsigned char backup[4] = {1,1,1,1};
for (i=0; i<4; i++) //循环检测4个按键
{
if(backup[i] != keyStatus[i]) //上次按键状态与当前不一致
{
if(backup[i]!=0) //上次按键是松开的状态,那么本次就是按下
{
if(i==1) //ESC复位秒表
stopwatchReset();
else if (i==2) //3号键启停秒表
stopwatchAction();
}
backup[i] = keyStatus[i]; //更新按键状态的备份
}
}
}
/**
* 功能:按键扫描函数,需在定时中断中调用
* 参数:无
**/
void keyScan()
{
unsigned char i;
static unsigned char keybuf[4]= { //按键扫描缓冲区
0xFF, 0xff, 0xff, 0xff
};
//按键值移入缓冲区
keybuf[0]=(keybuf[0] << 1) | KEY1;
keybuf[1]=(keybuf[1] << 1) | KEY2;
keybuf[2]=(keybuf[2] << 1) | KEY3;
keybuf[3]=(keybuf[3] << 1) | KEY4;
//更新按键状态
for(i=0;i<4;i++)
{
if(keybuf[i]==0x00) //因为定时器中断2ms,连续8次扫描为0,则可认为按键已经稳定按下
{
keyStatus[i]=0;
}
else if (keybuf[i] == 0xff) //连续8次扫描为1,则可认为按键已经稳定的松开
{
keyStatus[i]=1;
}
}
}
/**
* 功能:数码管动态扫描刷新函数,需在定时中断中调用
* 参数:无
**/
void ledScan()
{
static unsigned char i = 0; //动态扫描索引
P0=0x00; //关闭段选位,消隐
P2=(P2 & 0xE3) | (i<<2); //P2口2、3、4位是位选索引值
P0=digitalTubeBuf[i]; //把缓冲区中索引位置的数据送到P0
if(i<5)
i++;
else
i=0;
}
/**
* 功能:秒表计数函数,每个10ms调用一次进行秒表计数累加
* 参数:无
**/
void stopwatchCount()
{
if(stopwatchRunning) //判断是否处于运行状态
{
decimalPart++;
if(decimalPart >= 100) //小数部分计数到10时,进位到整数部分
{
decimalPart=0;
integerPart++;
if (integerPart >=10000)
{
integerPart=0;
}
}
stopwatchRefresh=1; //设置秒表计数刷新标志
}
}
/**
* 功能:T0中断服务函数,完成数码管、按键扫描与秒表计数
* 参数:无
**/
void timer0() interrupt 1
{
static unsigned char tmr10ms=0;
TH0=T0RH;
TL0=T0RL;
ledScan(); //数码管动态显示
keyScan(); //扫描键盘
//定时10ms进行一次秒表计数
tmr10ms++;
if (tmr10ms >=5)
{
tmr10ms=0;
stopwatchCount(); //调用秒表计数函数
}
}
10.4 单片机RAM区域划分
Keil C51语言中的关键字
- data: 片内RAM从0x00~0x7f,直接寻址
- idata: 片内RAM从0x000xff,通用寄存器间接寻址,0x80H0xFF区域通常用于中断与函数调用的堆栈。
- pdata:片外RAM从0x00~0xff,通用寄存器间接寻址
- xdata:片外RAM从0x0000~0xFFFF,需要使用2个字节寄存器DPTRH和DPTRL来间接寻址,速度最慢
**PS:**一般,使用data区域,data不够用时,就用xdata,如果希望程序执行效率高一点,就使用pdata关键字定义。
10.5 长短按键的应用
版权声明:本文为CSDN博主「acktomas」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/acktomas/article/details/104602123
暂无评论