1 定时器0延时模块
Timer0Delay.c 代码
#include <REGX52.H>
/**
* @brief 定时器0初始化,1ms中断一次
* @param 无
* @retval 无
*/
void Timer0Init()
{ //此代码及定时器初值可以用STC-ISP软件定时器计算器生成
TMOD &= 0xF0; //定时器0模式,工作 方式1,仅用TR0打开启动
TMOD |= 0x01; //设置定时器模式
TH0=0XFC; //给定时器赋初值,1毫秒@11.0592MHz,误差0.04%
TL0=0X66;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
/* 定时器0中断程序模板,copy到主程序中
void Timer0() interrupt 1
{
static u16 i;
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X66;
i++;
if(i==1000) //延时1000ms
{
i=0;
//此处放入定时1s到后执行代码
}
} */
2 延时模块
Delay.c 代码
#include<intrins.h>
/**
* @brief 延时程序N毫秒,阻塞
* @param xms 延时毫秒数
* @retval 无
*/
void Delay(unsigned int xms) //@11.059MHz,延时1ms
{ //用stc-isp程序软件延时计算器先生成延时1ms的程序,再修改加入参数
unsigned char i, j;
while(xms--)
{
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
3 独立按键判断程序
//独立按键,不用延时去抖动,用定时器20ms调用一次
#include <REGX52.H>
unsigned char TimeKey_get_key() //返回按键值1-4,无按键返回0
{
unsigned char key=0;
if(P3_1==0){key=1;}
if(P3_0==0){key=2;}
if(P3_2==0){key=3;}
if(P3_3==0){key=4;}
return key;
}
/*
void Timer0Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器16位模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
ET0=1;
EA=1;
PT0=0;
TR0 = 1; //定时器0开始计时
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
static unsigned char last_key,now_key;
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
T0Count++;
if(T0Count>=20)
{
T0Count=0;
last_key=now_key;
now_key=TimeKey_get_key();
if(last_key==0 && now_key!=0)
//执行代码,只有上次无按键,这次有按键才动作
}
}
*/
#include <REGX52.H>
#include "Delay.h"
sbit key=P3^1;
sbit led=P2^0;
void main()
{
while(1)
{
if(key==0) //如果K1按键按下
{
//1.有按键阻塞,松开按键才动作
Delay(20); //延时消抖
while(key==0); //松手检测
Delay(20); //延时消抖
led=~led; //LED1取反,松开按键才动作
//2.有按键阻塞,按键后就动作,等待释放
Delay(20); //延时消抖
if(key==0)
{
led=~led; //按键按下就动作
while(key==0); //松手检测,不松开就阻塞
}
//3.有按键阻塞,长按超时500ms退出函数(继续按压会检测到按键重复输入)。按键后就动作
unsigned char i=0;
Delay(20); //延时消抖
if(key==0)
{
led=~led; //按键按下就动作
while((key==0)&&(i<50)) //松手检测,不松开超时500ms后继续执行
{
Delay(10);
i++;
}
}
}
}
}
4 矩阵键盘获取按键模块
定时器20ms扫描矩阵键盘,不用延时去抖动
#include <REGX52.H>
#define GPIO_KEY P1 //P17-P14为行(P17在上),P13-P10为列(P13在左)
/**
* @brief 获取矩阵按键的值
* @param GPIO_KEY的四位用于键盘的行,另4位用于键盘的列
* @retval 按键的值(0-15),无按键返回255
*/
unsigned char TimeScanKey_getKey()
{
unsigned char key=255; //无按键返回255
GPIO_KEY = 0xf0; //四列置低
if(GPIO_KEY!=0xf0) //有按键,高四位就不全为高电平
{
GPIO_KEY = 0xf0; //先置列全为0
switch(GPIO_KEY)
{
case(0x70):key=0;break; //第一行有按键
case(0xb0):key=4;break;
case(0xd0):key=8;break;
case(0xe0):key=12;break;
}
GPIO_KEY = 0x0f; //再置行全为0
switch(GPIO_KEY)
{
case(0x07):key=key;break; //第一列有按键
case(0x0b):key=key+1;break;
case(0x0d):key=key+2;break;
case(0x0e):key=key+3;break;
}
}
return key;
}
/*
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
static unsigned char last_key,now_key;
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
T0Count++;
if(T0Count>=20)
{
T0Count=0;
last_key=now_key;
now_key=TimeScanKey_getKey();
if(last_key==255 && now_key!=255)
//有键执行代码,只有上次无按键,这次有按键才动作
}
}
*/
MatrixKey.c 代码(延时程序去抖动)
#include <REGX52.H>
#include "Delay.h"
#define GPIO_KEY P1 //P17-P14为行(P17在上),P13-P10为列(P13在左)
/**
* @brief 获取矩阵按键的值
* @param GPIO_KEY的四位用于键盘的行,另4位用于键盘的列
* @retval 按键的值(0-15),无按键返回255
*/
unsigned char getKey()
{
unsigned char key=255,a=0; //无按键返回255,a按键时间计时
GPIO_KEY = 0xf0; //四列置低
if(GPIO_KEY!=0xf0) //有按键,高四位就不全为高电平
{
Delay(10);
if(GPIO_KEY!=0xf0) //延时后,无按键返回
{ //开始按键扫描
GPIO_KEY = 0xf0; //先置列全为0
switch(GPIO_KEY)
{
case(0x70):key=0;break; //第一行有按键
case(0xb0):key=4;break;
case(0xd0):key=8;break;
case(0xe0):key=12;break;
}
GPIO_KEY = 0x0f; //再置行全为0
switch(GPIO_KEY)
{
case(0x07):key=key;break; //第一列有按键
case(0x0b):key=key+1;break;
case(0x0d):key=key+2;break;
case(0x0e):key=key+3;break;
}
while(GPIO_KEY!=0x0f && a < 50) //等待按键释放或者超时500ms退出函数
{
Delay(10);
a++;
}
}
}
return key;
}
5 动态数码管显示模块
Nixie.c (用定时器1ms中断一次,每1-2ms显示一位。
//定时器1ms中断一次,中断中调用一次,显示一位。
#include <REGX52.H>
//数码管段码表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7c,0x39,0x5e,0x79,0x71,
0xbf,0x86,0xdB,0xcF,0xe6,0xeD,0xfD,0x87,0xfF,0xeF,0xf7,0xfc,0xb9,0xde,0xf9,0xf1,
0x00}; //共阴数码管d0-d7对应abcdefg和dp,第一排是0-9a-f,第二排是0.-9.a.-f.,最后0x00是熄灭
//数码管显示缓存区
unsigned char Nixie_Buf[8]={0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}; //从左到右的8位led数码管的显示数据区,开始都存放熄灭段码,可以把数组放到头文件或者用extern共享
#define SegmentGPIO P0 //段码:P0口经过74hc245缓冲,1点亮
#define PosGPIO P2 //位选P24、P23、P22经过74HC138译码器,最左边数码管y7(对应3个全一)
void Nixie_scan(unsigned char location) //location 显示位置(从最左到最右依次是1到8)
{
SegmentGPIO=0x00; //消影
PosGPIO=(PosGPIO & 0xe3) | ((8-location)<<2); //送位选,位置1,位选应该是全一
SegmentGPIO=NixieTable[Nixie_Buf[location-1]];
}
void Nixie_loop(void)
{
static unsigned char i=0;
Nixie_scan(i+1);
i=++i & 0x07;
}
/*
void Timer0_Routine() interrupt 1
{
Nixie_loop();//1ms中断一次,调用显示1位
}
*/
LedNumberShow.c 代码(显示一位后,延时,直到8位都显示一遍)
#include <REGX52.H>
#include "Delay.h"
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7c,0x39,0x5e,0x79,0x71,
0xbf,0x86,0xdB,0xcF,0xe6,0xeD,0xfD,0x87,0xfF,0xeF,0xf7,0xfc,0xb9,0xde,0xf9,0xf1,
0x00}; //共阴数码管d0-d7对应abcdefg和dp,第一排是0-9a-f,第二排是0.-9.a.-f.,最后一排0x00是熄灭
#define SegmentGPIO P0
#define PosGPIO P2 //P2口的4、3、2位
/**
* @brief 八个数码管动态显示一遍,段码:P0口经过74hc245缓冲,位选P24、P23、P22经过74HC138译码器,最左边数码管y7(对应3个全一)
* @param 存放8个无符号字符的数组,
* @retval 无
*/
void LedNumShow(unsigned char table[8])
{
unsigned int i;
for(i=0;i<8;i++)
{
SegmentGPIO=NixieTable[table[7-i]]; //送段码,先送最右边
PosGPIO=(PosGPIO & 0xe3) | (i<<2); //送位选,先送最右边
Delay(1); //为避免按键消抖动延时影响动态刷新显示,可以用定时器中断,每10ms调用本函数一次
SegmentGPIO=0x00; //消影
}
}
5-2 LCD1602调试工具
lcd1602模块的DB0--DB7脚接P00-P07脚,E脚接P27,RW接P25,RS脚接P26
把LCD1602.c和LCD1602.h文件复制到main.c文件同目录下
#include <REGX52.H>
#include "LCD1602.h" //包含LCD1602头文件
#include "Delay.h" //包含Delay头文件
int Result=0;
void main()
{
LCD_Init();
while(1)
{
Result++; //Result自增
Delay(1000); //延时1秒
LCD_ShowNum(1,1,Result,3); //在LCD的1行1列显示Result,长度为3位
}
}
函数 作用
LCD_Init(); 初始化
LCD_ShowChar(1,1,'A'); 显示一个字符。第一个参数:行位置12,第二个参数:列位置116
LCD_ShowString(1,3,"Hello"); 显示字符串
LCD_ShowNum(1,9,123,3); 显示十进制数字。第四个参数:显示位数
LCD_ShowSignedNum(1,13,-66,2); 显示有符号十进制数字
LCD_ShowHexNum(2,1,0xA8,2); 显示十六进制数字
LCD_ShowBinNum(2,4,0xAA,8); 显示二进制数字
6 串口模块,初始化,发给上位机调试信息
UART.c 代码
#include <REGX52.H>
/**
* @brief 串口初始化,4800bps@11.0592MHz
* @param 无
* @retval 无
*/
void UART_Init()
{
SCON=0x50; //SM0 SM1为方式1,8位UART。REN为1,允许接收。TI RI为0
PCON &= 0x80; //SMOD保持不变为0,波特率不加倍。
TMOD &= 0x0F; //设置定时器1模式,不影响定时器0
TMOD |= 0x20; //设置定时器1,8位重装
TL1 = 0xFA; //设定定时初值 4800波特率
TH1 = 0xFA; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1; //打开总中断
ES=1; //打开串口中断
}
/**
* @brief 串口发送一个字节数据
* @param Byte 要发送的一个字节数据
* @retval 无
*/
void UART_SendByte(unsigned char Byte)
{
SBUF=Byte;
while(TI==0);
TI=0;
}
/* 用puts printf 输出到上位机串口调试信息
#include <REGX52.H>
#include <stdio.H>
void main()
{
unsigned int i=6235;
unsigned char j=200;
float k=2.569;
UART_Init();
TI=1; //puts printf执行之前先置TI为1
puts("sun wen ping."); //puts 输出串口字符串,并且换行
while(!TI);
TI=0;
TI=1;
printf("%f--%d--%c",k,i,j); // 2.569000--6235--c8 不换行
while(!TI);
TI=0;
while(1)
{
}
}
*/
/*串口中断函数模板
void UART_Routine() interrupt 4 // 函数名任意,interrupt 4 标明是串口中断处理程序
{
if(RI==1)
{
RI=0;
}
}
*/
9-2 LED点阵屏显示动画
8*8 led点阵,8列共阴极从左到右,接P07--P00. 8行共阳极从上到下分别接74hc595的D7--D0。
74hc595(74hc164类似):串行输入并行输出的移位寄存器。三脚控制,SER脚数据在SERCLK脚的上升沿移入移位寄存器,移位寄存器的数据在RCLK脚的上升沿写入输出寄存器。oe为高,输出高阻。MR为低,清空移位寄存器。
MatrixLED.c 代码
#include <REGX52.H>
#include "Delay.h"
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
#define MATRIX_LED_PORT P0
/**
* @brief 74HC595写入一个字节
* @param Byte 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //高位先进,位赋值规则,非零即1
SCK=1; //上升沿移位
SCK=0;
}
RCK=1; //上升沿移位到输出
RCK=0;
}
/**
* @brief 点阵屏初始化
* @param 无
* @retval 无
*/
void MatrixLED_Init()
{
SCK=0; //两位 置低,为上升沿做准备
RCK=0;
}
/**
* @brief LED点阵屏显示一列数据
* @param Column 要选择的列,范围:0~7,0在最左边,低电平点亮列
* @param Data 选择列显示的数据,高位在上,1为亮,0为灭
* @retval 无
*/
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data); //输出显示数据数据
MATRIX_LED_PORT=~(0x80>>Column); //输出列选
Delay(1);
MATRIX_LED_PORT=0xFF; //消除数据 列选的重叠
}
main.c 代码
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
//动画数据,用“字模提取”软件生成
unsigned char code Animation[]={ //关键字code,会把数组放到程序代码中,减少RAM占用
0x3C,0x42,0xA9,0x85,0x85,0xA9,0x42,0x3C,
0x3C,0x42,0xA1,0x85,0x85,0xA1,0x42,0x3C,
0x3C,0x42,0xA5,0x89,0x89,0xA5,0x42,0x3C,
};
void main()
{
unsigned char i,Offset=0,Count=0;
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++) //循环8次,显示8列数据
{
MatrixLED_ShowColumn(i,Animation[i+Offset]);
}
Count++; //计次延时,画面重复送led显示16次。
if(Count>15)
{
Count=0;
Offset+=8; //偏移+8,切换下一帧画面
if(Offset>16) //最后一个画面已经显示?
{
Offset=0; //切换到第一个画面
}
}
} //代码是动态显示3个完整画面,如果是动态左移显示,改变代码offset+=8 为 offset++
} //offset>16,根据数组点阵数据修改
10 c51变量定义
c51变量定义的四要素:
1【存储种类】(标准c) 2 数据类型 (标准c+c51)3【存储类型】(c51特有) 4变量名(标准c)
举例:extern(缺省为auto,static) unsigned char(sfr) data(code) temp;
-
存储种类:
- auto(自动型):缺省默认,变量的作用范围在定义它的函数体或语句块内,执行结束后,内存即被释放。
- extern(外部型):在一个源文件中定义的变量,在其它源文件中需要通过extern说明方可使用。
- static(静态型):变量定义所在的函数或语句块执行结束后,其分配的内存单元继续保留。
-
数据类型:
- 标准c数据类型:char、int、long 及三者的unsigned类型,float、double,p*
- c51增加的类型:bit(位变量)、sfr和sfr16(特殊功能寄存器)、sbit(sfr位地址变量)
-
存储类型:
- data:片内低128B存储区。
- bdata:片内可位寻址存储区(低128B中20H--2FH共16字节,位地址00H--7FH)
- idata:片内高128B存储区(地址80H-FFH,只有52系列才有)只能通过@R0或@R1间接寻址
- pdata:片外页RAM(一页256B,高8位地址一样)
- xdata:片外64KB RAM(stc89c52系列大容量单片机集成了1024B的扩展RAM,汇编中寻址用dptr、Ri间接寻址,c语言中声明为xdata)
- code:程序ROM(数据不可以更改)
- 三种编译模式对应三种缺省存储类型:small(小编译模式)对应data,Compact(紧凑模式)对应pdata,Large(大编译模式)对应xdata。
-
变量名:变量名可以由字母、数字和下划线三种字符组成,且第一个字符必须为字母或
11 stc89c52 定时计数器
定时器1.2相关寄存器
TF1(TF0):定时器/计数器T1(T0)溢出标志。响应中断后,由硬件清零(也可由程序查询清零)。
TR1(TR0): 定时器T1(T0)的运行控制位。由软件置位和清零。置位后计数条件:GATE=0或者GATE=1,INT1脚为高电平。
IT1(IT0): 外部中断1触发方式控制位。IT1=0,外部中断INT1引脚为低电平触发。IT1=1,下降沿触发。
IE1(IE0): 外部中断1请求源标志。当引脚INT1低电平或者下降沿,置位IE1. 响应中断后,由硬件清零。
GATE:为0不影响。置1时,只有INT引脚为高时,TR才能有效。(可以测量INT脚高电平时间)
C/T: 清零为定时器(内部时钟输入),置1为计数器(从T引脚输入)。
M1、M0: 模式选择。00(13位)、01(16位)、10(8位自动重装载)、11(定时器0作为双8位)
#include <REGX52.H>
typedef unsigned char BYTE;
typedef unsigned int WORD;
#define FOSC 11059200L //晶振频率
#define TIMS (65536-FOSC/12*0.001) // 16位定时器,12T模式,1毫秒(0.001秒)中断一次
WORD count; //计数变量,对1ms计数1000次,即1s执行一次代码
void tm0_isr() interrupt 1
{
TL0=TIMS; //16位赋值给8位,自动取低8位
TH0=TIMS>>8;
if(count--==0)
{
count=1000;
//执行代码
}
}
void main()
{
TMOD &= 0xF0; //setp1:设置定时器模式1,16位模式
TMOD |= 0x01; //
TF0 = 0; //清除TF0标志
TL0=TIMS; //setp2:设置定时器初始值
TH0=TIMS>>8;
ET0=1; //setp3:打开中断
EA=1;
count=0;
TR0=1; //setp4:最后才启动定时器,以免启动早,进入中断
while(1);
}
定时器2相关寄存器表:
TF2:定时器2溢出标志,必须由软件清除。定时器2用作串口时(RCLK或TCLK=1),TF2将不会置位。
TR2: 定时器2启动/停止控制位,置1时启动定时器。
C/T2: 0=内部定时器(12T或者6T),1=外部事件计数器(T2引脚下降沿触发)
EXEN2:定时器2外部使能标志。置位时且定时器2未作为串行口时钟时,允许T2EX的负跳变产生捕获或重装。
EXF2:定时器2外部标志。当EXEN2=1且T2EX的负跳变产生捕获或者重装时,EXF2置位。必须软件清零。递增递减模式(DCEN=1)中,EXF2不会引起中断。
RCLK:接收时钟标志。置位后,定时器2作为串行口模式1和3的接收时钟。RCLK=0,串行口用定时器1。
TCLK:发送时钟标志。置位后,定时器2作为串行口模式1和3的发送时钟。TCLK=0,串行口用定时器1。
CP/RL2: 捕获/重装标志。置位且EXEN2=1时,T2EX的负跳变产生捕获。清零且EXEN2=0时,定时器溢出或者T2EX的负跳变都可使定时器自动重装。定时器2用作串行口时,此位无效,溢出自动重装。
T2OE:定时器2输出使能位
DECN:向下计数使能位。为0时,默认向上计数。为1时,T2EX引脚确定递增或者递减计数。
定时器2的捕获模式:
定时器2作为16位定时器或者计数器,溢出置位TF2。该模式中,无重新装载值。
当EXEN2=1时,T2EX的负跳变,将TL2、TH2的值捕获到RCAP2L、RCAP2H中,并且置位EXF2。
TF2、EXF2中断地址相同,中断程序中查询。
定时器2的自动重装模式(递增、递减计数器)
DCEN=0,自动重装模式:16位向上计数,溢出或者负跳变(EXEN2使能)时16位重新装载。
DCEN=1,递增递减模式:T2EX=1时,递增计数,溢出置位TF2,且把RCAP2值重新装载。
T2EX=0时,递减计数,当计数到等于RCAP时,中断,重装值为0XFFFF。
12 中断系统
中断允许位
EA: 总中断允许控制位。
ET2、ET1、ET0: 定时/计数器T2、T1、T0中断允许位。
ES: 串行口1中断允许位。
EX0、EX1、EX2、EX3 : 外部中断0、1、2、3中断允许位。
中断请求标志位
TF0、TF1、TF2、EXF2 :定时/计数器T0、T1、T2溢出标志位,定时器2外部标志位。
IE0、IE1、IE2、IE3:外部中断中断请求标志位。
RI、TI 串行口中断标志位。
外部中断源类型选择位
IT0、IT1、IT2、IT3 :为0,引脚低电平触发外部中断,为1,下降沿触发。
中断优先级(只设置IP,2级)
PX3H,PX3、PX2H,PX2、PX1H,PX1、PX0H,PX0: 外部中断。00-01-10-11对应优先级0-1-2-3
PT2H,PT2、PT1H,PT1、PT0H,PT0 :定时器。
PSH,PS : 串口1.
13 串行口通信
SM0 SM1(SMOD0=0时): 串行口工作方式,0 0 方式0,移位寄存器。0 1 方式1,8位UART,波特率可变。1 0 方式2,9位UART。1 1 方式3,9位,波特率可变. (SMOD0=1时,SM0用于帧错误检测,无效停止位时置位)
SM2:多机通信允许控制位。方式2、3时,如果SM2=1,REN=1,只有接收到RB8为1(地址帧)时,RI才为一。
REN: 允许、禁止串行接收控制位。为1,允许接收。为0,禁止接收。
TB8、RB8:方式2、3中是发送或接收到的第9位。
TI、RI:发送(接收)中断请求标志位。必须用软件复位。中断入口是 一个,中断处理程序中判断。
SMOD:置位时,方式1、2、3波特率加倍。
SMOD0:帧错误检测有效控制位。置位时,SM0用于帧错误检测,无效停止位时置位。
SADEN SADDR: 从机地址掩膜寄存器,从机地址寄存器。
标签:定时器,中断,51,unsigned,char,单片机,key,按键 From: https://www.cnblogs.com/sunwenping/p/16736637.html