①74HC138
是一个译码芯片,CBA二进制对应的十进制数值与Y0~Y7位置相符,Y输出端低电平有效
②CON3口
J13跳帽当1、2短接即存储器控制方式,2、3短接即IO口控制方式,习惯使用IO控制方式
③M74HC574M1R八路D型触发器
当LE为高电平时候Q输出与D输入一致,当LE为低电平,Q输出不随D输入改变,能够起到锁存数据的功能,因为P0口分时复用,所以为了不影响驱动蜂鸣器、继电器等器件,开发板电路中采用触发器进行数据锁存
④ULN2003达林顿管
起到一个加大驱动电流且取反的作用,若N_BUZZ为低电平为蜂鸣器响。因此IN7为高电平,Q7位高电平,D7位高电平,P06为高电平且Y5C为高电平,Y5C要为高电平,则需要Y5为低电平(WR与GND短接,74HC02为或非门),也就是CBA对应101
//第一种方法
#include "reg52.h"
sbit buzzer = P0^6;
void main()
{
P2 = 0xa0; //选通触发器
buzzer = 0; //蜂鸣器不叫
P2 = 0x00 //不选通触发器
}
&:与。作用是将某位置0
|:或。作用是将某位置1,且不影响其他位
//第二种方法
#include "reg52.h"
void main()
{
P2 = ((P2&0x1f)|0xa0);
P0 &= ~(0x01<<6);
//P0 |= (0x01<<6);
P2 &= 0x1f;
}
XBYTE[0XA000] = 0x00
把数据0x00送到外部存储器0xA000这个地址单元
①单片机把该地址的高8位送到P2口,P2=0xA0
②地址的低8位送到P0口,P0=0x00
③单片机地址锁存信号有效
④单片机把数据0x00送到P0口,P0=0x00
⑤地址锁存无效
tips:51单片机中P0口数据地址分时复用,P2口是地址高8位控制
#include "reg52.h"
#include "absacc.h"
void main()
{
XBYTE[0xA000] = 0x00; //关蜂鸣器
while(1)
{
//...
}
}
P2口操作完后一定要关闭(P2 = 0x00),不然单片机掉电后蜂鸣器会一直响
使用软件延时需要加上头文件intrins.h,因为使用到了_nop()_
=======================================================================
同蜂鸣器,详细见电路图
#include "reg52.h"
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
void main()
{
int i=0;
while(1)
{
P2 = 0x80; //使能Y4C
P0 = ~(0x01<
=======================================================================
J5按键功能选择。
KBD:1接2,矩阵键盘 BTN:2接3,独立键盘
当一个按键两端分别接一个IO口,一个IO口置高电平另一个置低电平,当按下按键时高电平IO口电平被拉低,另一端还是为低电平,这时检测IO口值就是两个低电平
把上述方法应用到矩阵键盘上,如果把4个行引脚接P3前4位IO口(P3.0 ~ P3.3),都置低电平,把4个列引脚接P3后4位IO口(P3.4~P3.7),都置高电平,这时P3=0xf0, 如果按下第一列第一行按键,那么第一列的IO口电平变低,其他IO口电平不变,这时保存P3值,确定了列值。同样,用这种方法确定行值,就是在保存列值后把4个行引脚置高电平,4个列引脚置低电平,P3=0x0f(这时按键还处于按下的状态),确定行值,最后把列值行值进行或运算得出一个保存了行值和列值的数(每个按键都有一个对应的数值)。
简单的说,要确定某行是否有按键按下,只要在列上置低电平在行上置高电平,若某行有被按下的,则该行会被拉低,列检测同理
方法一:延时去消抖
不推荐,这样会使得整个系统时钟推迟
方法二:状态机消抖
//此代码是独立按键下的消抖
#define key_input P3
#define key_state_0 0 //判断按键是否按下
#define key_state_1 1 //判断按键是否抖动
#define key_state_2 2 //判断按键是否弹起
#define key_mask 0x0f //屏蔽不需要的IO,因为独立按键P3^4~P3^7接地是低电平
char read_key(void)
{
static char key_state = 0;
char key_press, key_return = 0;
key_press = key_input&key_mask;
switch (key_state)
{
case key_state_0:
if (key_press!=key_mask) //若为真说明有按键按下
{
key_state = key_state_1;
}
break;
case key_state_1:
if (key_press!=key_mask)
{
if(key_press==0x0e) key_return = 1; //S7
if(key_press==0x0d) key_return = 2; //S6
if(key_press==0x0b) key_return = 3; //S5
if(key_press==0x07) key_return = 4; //S4
key_state = key_state_2;
}
else
key_state = key_state_0;
break;
case key_state_2:
if (key_press==0x0f) key_state = key_state_0;
break;
}
return key_return;
}
方法三:三行代码
unsigned char Trg; //trigger触发值(按下的瞬间会变化,然后会清楚)
unsigned char Cont; //continue持续触发值(长按)
void Key_Read( void )
{
unsigned char ReadData = KEYPORT^0xff; //KEYPORT就是P口
Trg = ReadData & (ReadData ^ Cont);
Cont = ReadData;
}
//详细参见博客
//https://blog.csdn.net/yhguo2008/article/details/51327524/
#include "reg52.h"
#include "intrins.h"
#define key_input P3
#define key_state_0 0 //按键按下
#define key_state_1 1 //按键消抖
#define key_state_2 2 //按键弹起
#define key_mask 0x0f
#define ON 1
#define OFF 0
/*
#define KEYPORT P3
unsigned char Trg;
unsigned char Cont;
void Key_Read( void )
{
unsigned char ReadData = KEYPORT^0xff;
Trg = ReadData & (ReadData ^ Cont);
Cont = ReadData;
}
*/
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
char key_read(void)
{
char key_press;
char key_return = 0;
static char key_state = key_state_0;
key_press = key_input&key_mask;
switch(key_state)
{
case key_state_0:
if(key_press != 0x0f)
{
key_state = key_state_1; //初步确定按下按键后要进入消抖环节
}
break;
case key_state_1:
if(key_press != 0x0f)
{
if(key_press == 0x0e)key_return = 7;//S7
if(key_press == 0x0d)key_return = 6;//S6
if(key_press == 0x0b)key_return = 5;//S5
if(key_press == 0x07)key_return = 4;//S4
key_state = key_state_2;
}
else
key_state = key_state_0;
break;
case key_state_2:
if(key_press == 0x0f) //有次写错成0xff,键盘按键就只能使用一次
{
key_state = key_state_0;
}
break;
}
return key_return;
}
void control_buzzer(int two_value_switch)
{
P2 = 0xa0;
if(two_value_switch == ON)
{
P0 = 0x40; //开蜂鸣器
}
else
{
P0 = 0x00; //关蜂鸣器
}
P2 = 0x00;
}
void control_relay(int two_value_switch)
{
P2 = 0xa0;
if(two_value_switch == ON)
{
P0 = 0x10; //开继电器
}
else
{
P0 = 0x00; //关继电器
}
P2 = 0x00;
}
void control_led(int two_value_switch)
{
P2 = 0x80;
if(two_value_switch == ON)
{
P0 = 0x00; //打开所有灯
}
else
{
P0 = 0xff; //关闭所有灯
}
P2 = 0x00;
}
void main()
{
unsigned char key_value;
while(1)
{
key_value = key_read();
if(key_value == 7)control_led(ON);//control_buzzer(ON)//control_relay(ON)
if(key_value == 6)control_led(OFF);//control_buzzer(OFF)//control_relay(OFF)
delay10ms();//未定义,之后再定时器一节再讲述
//三行代码实验程序
// Key_Read();
// if(Trg & 0x08)//S4
// {
// P2=0xa0;buzzer=1;P2=0x00;
// }
// if(Trg & 0x04)//S5
// {
// P2=0xa0;buzzer=0;P2=0x00;
// }
}
}
#include "reg52.h"
#define KEY P3
#define key_state_0 0 //按键按下
#define key_state_1 1 //按键消抖
#define key_state_2 2 //按键弹起
char read_KBD(void)
{
static char key_state = 0;
unsigned char key_return=0, key_press;
unsigned char key1,key2;
KEY=0xf0;
key1=KEY&0xf0;
KEY=0x0f;
key2=KEY&0x0f;
key_press =key1|key2;
switch (key_state)
{
case key_state_0: //
if (key_press!=0xff) key_state = key_state_1;
break;
case key_state_1:
if (key_press !=0xff)
{
if(key_press==0xde) key_return = 1; //S15
if(key_press==0xdd) key_return = 2; //S14
key_state = key_state_2;
}
else
key_state = key_state_0;
break;
case key_state_2:
if (key_press==0xff) key_state = key_state_0;
break;
}
return key_return;
}
sbit buzzer = P0^6;
void main()
{
unsigned char key_val;
while(1)
{
key_val=read_KBD();
if(key_val==1)//S15
{
P2=0xa0;buzzer=1;P2=0x00;
}
if(key_val==2)//S14
{
P2=0xa0;buzzer=0;P2=0x00;
}
}
}
1、独立按键实现蜂鸣器、继电器控制、LED亮灭
//注意短接口J5不要接错
/*
* 出错的地方:
* ①key_state 没有定义为static变量
* ②只能一个开关起作用,若要另一个开关也起作用,则需要重启:说明出错在按键弹起来,果然问题在判断语句写成了0xff,独立按键应该写0x0f
* ③检测独立函数写错也是因为判断是否按下写成了0xff,应该写成0x0f
*/
#include "reg52.h"
#define key_input P3
#define key_state_0 0 //检查是否按下
#define key_state_1 1 //检查是否抖动
#define key_state_2 2 //检查是否弹起
#define ON 1
#define OFF 0
typedef unsigned char BYTE;
typedef unsigned int WORD;
unsigned char temp_key_compare;
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1ms timer calculation method in 12T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
/*
* 蜂鸣器驱动函数
*/
void control_buzzer(unsigned char bi_val_control)
{
P2 = 0xa0;
if(bi_val_control == ON)
{
P0 = 0x40; //开蜂鸣器
}
else
{
P0 = 0x00; //关蜂鸣器
}
P2 = 0x00;
}
/*
* 独立按键驱动函数
*/
unsigned char read_control_key(void)
{
unsigned char return_key_val=0;
unsigned char key_press;
static char key_state = key_state_0; //错过这里,未定义成静态变量
key_press = P3&0x0f;
switch(key_state)
{
case key_state_0:
{
if(key_press != 0x0f) //错过这里,错写0xff,误当为矩阵键盘
{
key_state = key_state_1;
}
}
break;
case key_state_1:
{
if(key_press != 0x0f)//错过这里,错写0xff,误当为矩阵键盘
{
if(key_press == 0x0e)return_key_val = 7;//S7
if(key_press == 0x0d)return_key_val = 6;//S6
if(key_press == 0x0b)return_key_val = 5;//S5
if(key_press == 0x07)return_key_val = 4;//S4
key_state = key_state_2;
}
else
key_state = key_state_0;
}
break;
case key_state_2:
{
if(key_press == 0x0f)//错过这里,错写0xff,误当为矩阵键盘
{
key_state = key_state_0;
}
}
break;
}
return return_key_val;
}
void main()
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
while (1); //loop
}
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 10 -> 10ms
{
count = 10; //reset counter
temp_key_compare = key_read();
if(temp_key_compare==7)
{
control_buzzer(ON);
}
if(temp_key_compare==5)
{
control_buzzer(OFF);
}
}
}
2、矩阵键盘控制蜂鸣器
//注意短接口J
/*
* 出错的地方:
* ①key_state 没有定义为static变量
* ②只能一个开关起作用,若要另一个开关也起作用,则需要重启:说明出错在按键弹起来,果然问题在判断语句写成了0xff,独立按键应该写0x0f
* ③检测独立函数写错也是因为判断是否按下写成了0xff,应该写成0x0f
*/
#include "reg52.h"
#define KEY P3
#define key_input P3
#define key_state_0 0 //检查是否按下
#define key_state_1 1 //检查是否抖动
#define key_state_2 2 //检查是否弹起
#define ON 1
#define OFF 0
typedef unsigned char BYTE;
typedef unsigned int WORD;
unsigned char temp_key_compare;
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1ms timer calculation method in 12T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
/*
* 蜂鸣器驱动函数
*/
void control_buzzer(unsigned char bi_val_control)
{
P2 = 0xa0;
if(bi_val_control == ON)
{
P0 = 0x40; //开蜂鸣器
}
else
{
P0 = 0x00; //关蜂鸣器
}
P2 = 0x00;
}
/*
* 矩阵按键驱动函数
*/
unsigned char matrix_read_control_key(void)
{
unsigned char return_key_val=0;
unsigned char key_press;
static char key_state = key_state_0; //错过这里,未定义成静态变量
unsigned char key1,key2;
KEY=0xf0;
key1=KEY&0xf0;
KEY=0x0f;
key2=KEY&0x0f;
key_press =key1|key2;
switch(key_state)
{
case key_state_0:
{
if(key_press != 0xff) //错过这里,错写0xff,误当为矩阵键盘
{
key_state = key_state_1;
}
}
break;
case key_state_1:
{
if(key_press != 0xff)//错过这里,错写0xff,误当为矩阵键盘
{
if(key_press == 0x7e)return_key_val = 7;//S7
if(key_press == 0xbe)return_key_val = 11;//S11
if(key_press == 0xdd)return_key_val = 14;//S14
if(key_press == 0xde)return_key_val = 15;//S15
if(key_press == 0xee)return_key_val = 19;//S19
key_state = key_state_2;
}
else
key_state = key_state_0;
}
break;
case key_state_2:
{
if(key_press == 0xff)//错过这里,错写0xff,误当为矩阵键盘
{
key_state = key_state_0;
}
}
break;
}
return return_key_val;
}
void main()
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
while (1); //loop
}
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 10 -> 10ms
{
count = 10; //reset counter
temp_key_compare = matrix_read_control_key();
if(temp_key_compare==14)
{
control_buzzer(ON);
}
if(temp_key_compare==15)
{
control_buzzer(OFF);
}
}
}
=======================================================================
共阳接法,高电平是暗低电平是亮
根据CT107D的原理图我们可以看出,数码管显示同样涉及到74HC573、74HC138和74HC02,这与上一节中的LED灯、继电器和蜂鸣器的控制大同小异。如下图,蓝色标注为位选控制端口,由锁存器U6来控制,需配置红色标注的Y6C来控制锁存;黄色标注为段选控制端口,由锁存器U7来控制,需配置红色标注的Y7C来控制锁存。而控制Y6C与Y7C则需要通过74HC18和74HC02来进行控制,如下图,Y6C由Y6和WR共同控制,74HC02是一个4路2输入或非门功能,WR为低电平,要使Y6C为高,则Y6必须为低电平,即74HC138译码器的三个输入端应该为100,即P2^ 7=1,P2^ 6=0,P2^5=0。又只需要操作P2口的这三位,不需要配置其他的五位,所以P2端口应该配置为:P2 = ((P2&0x1f)|0xC0)。同理,控制Y7C时P2端口应该置为:P2 = ((P2&0x1f)|0xE0)。
//软件延时是stc89c51的,IAP不一样
#include "reg52.h"
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
void main()
{
unsigned char i;
P2=0x80;P0=0xaa;P2=0x00;
while(1)
{
P2=0x80;P0=~(0x80>>i);P2=0x00;
i++;
if(i==8) i=0;
Delay100ms();
}
}
#include "reg51.h"
#include "intrins.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/1000) //1ms timer calculation method in 1T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
//-----------------------------------------------
unsigned char code T_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
static char i;
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 1000 -> 1s
{
count = 1; //reset counter
P2=0xe0;P0=~T_display[i];P2=0;
P2=0xc0;P0=T_COM[i];P2=0;
i++;
if(i==8) i=0;
}
}
//-----------------------------------------------
/* main program */
void main()
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
while(1);
}
void 函数名() interrupt 中断号 [using 工作寄存器组号]
中断号 | 中断源 |
---|---|
0 | 外部中断0 |
1 | 定时器0 |
2 | 外部中断1 |
3 | 定时器1中断 |
4 | 串行口中断 |
寄存器组号 | 工作寄存器组 |
---|---|
0 | 工作寄存器组0 |
1 | 工作寄存器组1 |
2 | 工作寄存器组2 |
3 | 工作寄存器组3 |
STC12内核是1T的,没有像51单片机进行12分频,晶振为11.0592MHZ
T机器周期 = T时钟周期 = 1/11.0592=0.09042us
假设定时时间为x毫秒
定时次数=x*1000/0.09042
初值=2^(定时器位数)-定时次数
例如定时器0方式1(16位计数)
定时时间=1毫秒
定时次数=1x1000/0.09042=11059.5001(C语言向下取整,实际定时是微长一点)
初值=2^16-11059=65536-11059=54477
TMOD寄存器
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|
GATE | C/T’ | M1 | M0 | GATE | C/T’ | M1 | M0 |
IE寄存器
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|
EA | – | – | ES | ET1 | EX1 | ET0 | EX0 |
1、矩阵键盘控制数码管显示不同的数字
/*
* 关于如何实现根据按键显示相应数字,思路如下。
* 设置一个指定长度的数组,在中断函数里将读取的键值
* 赋给数组,然后在显示
* 提示点如下:
* ①将想显示的不同数设为数组,这样多个数就有多个数组,这样是不可以的,由于按键读取判断会有逻辑问题
* ②程序中的i可以改变显示位数
* ③静态变量i初始值应该赋为0,不然会受到上一次烧写程序的影响
* ④code定义的变量是写入ROM里的不能被修改
*/
#include "reg51.h"
#include "intrins.h"
#define KEY P3
#define key_state_0 0 //按键按下
#define key_state_1 1 //按键消抖
#define key_state_2 2 //按键弹起
typedef unsigned char BYTE;
typedef unsigned int WORD;
char read_KBD(void)
{
static char key_state = 0;
unsigned char key_return=0, key_press;
unsigned char key1,key2;
KEY=0xf0;
key1=KEY&0xf0;
KEY=0x0f;
key2=KEY&0x0f;
key_press =key1|key2;
switch (key_state)
{
case key_state_0: //
if (key_press!=0xff) key_state = key_state_1;
break;
case key_state_1:
if (key_press !=0xff)
{
if(key_press==0xde) key_return = 15; //S15
if(key_press==0xdd) key_return = 14; //S14
key_state = key_state_2;
}
else
key_state = key_state_0;
break;
case key_state_2:
if (key_press==0xff) key_state = key_state_0;
break;
}
return key_return;
}
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1ms timer calculation method in 1T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
//-----------------------------------------------
unsigned char code T_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char T_number1[8]={0}; //code定义的变量是写入ROM里的不能被修改
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
static char i=0; //初始化时应该赋值,不然还保留着上一次烧写的值
unsigned char temp=read_KBD();
if(temp != 0)
{
T_number1[0]=temp/10;
T_number1[1]=temp%10;
}
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 1000 -> 1s
{
count = 1; //reset counter
P2=0xe0;P0=~T_display[T_number1[i]];P2=0;
P2=0xc0;P0=T_COM[i];P2=0;
i++;
if(i==8) i=0; //显示位数
}
}
//-----------------------------------------------
/* main program */
void main()
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
while(1);
}
2、两个独立按键控制数码管数字的增减
#include "reg51.h"
#include "intrins.h"
#define key_input P3
#define key_state_0 0 //按键按下
#define key_state_1 1 //按键消抖
#define key_state_2 2 //按键弹起
#define key_mask 0x0f
typedef unsigned char BYTE;
typedef unsigned int WORD;
char read_KBD(void)
{
static char key_state = 0;
char key_press, key_return = 0;
key_press = key_input&key_mask;
switch (key_state)
{
case key_state_0:
if (key_press!=key_mask) //若为真说明有按键按下
{
key_state = key_state_1;
}
break;
case key_state_1:
if (key_press!=key_mask)
{
if(key_press==0x0e) key_return = 1; //S7
if(key_press==0x0d) key_return = 2; //S6
if(key_press==0x0b) key_return = 3; //S5
if(key_press==0x07) key_return = 4; //S4
key_state = key_state_2;
}
else
key_state = key_state_0;
break;
case key_state_2:
if (key_press==0x0f) key_state = key_state_0;
break;
}
return key_return;
}
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1ms timer calculation method in 1T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
//-----------------------------------------------
unsigned char code T_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char T_number1[8]={0}; //code定义的变量是写入ROM里的不能被修改
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
static char i=0; //初始化时应该赋值,不然还保留着上一次烧写的值
static unsigned char display_temp=0;
unsigned char temp=read_KBD();
if(temp == 3)
{
display_temp++;
}
if(temp == 4)
{
display_temp--;
}
if(temp >= 0)
{
T_number1[0]=display_temp/10;
T_number1[1]=display_temp%10;
}
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 1000 -> 1s
{
count = 1; //reset counter
P2=0xe0;P0=~T_display[T_number1[i]];P2=0;
P2=0xc0;P0=T_COM[i];P2=0;
i++;
if(i==8) i=0; //显示位数
}
}
//-----------------------------------------------
/* main program */
void main()
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
while(1);
}
=======================================================================
电阻的作用:上拉电阻
采用单根信号线,既可传输时钟,又能传输数据,而且数据传输是双向的(单片机<->18B20芯片)
此项中以下除了 rd_temperature_f程序比赛都会提供
#检测初始化是否成功
bit Init_DS18B20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
//给18B20写数据
//先写低位,低电平写入0高电平写入1
//dat是要写入的数据
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1; //dat右移一位
}
Delay_OneWire(5);
}
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//这个函数不会提供
float rd_temperature_f(void)
{
unsigned int temp;
float temperature;
unsigned char low,high;
Init_DS18B20();
Write_DS18B20(0xCC); //挂很多18B20通过ID号区分,板子只有一个18B20,故忽略
Write_DS18B20(0x44); //启动温度转换
Delay_OneWire(200);
Init_DS18B20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE); //读取寄存器
low = Read_DS18B20(); //低字节
high = Read_DS18B20(); //高字节
/** 精度为0.0625摄氏度 */
temp = high;
temp <<= 8;
temp |= low;
temperature = temp*0.0625;
return temperature;
}
自行定义一个上限(如20摄氏度),超过上限L1亮,继电器打开;自行定义一个下限(如15摄氏度),低于下限,L2亮,蜂鸣器响
①出错在工程文件未导入onewire.c
②while(1)后加了一个:
#include "reg52.h"
#include "intrins.h"
#include "onewire.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
unsigned char temperature;
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1ms timer calculation method in 12T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
//-----------------------------------------------
unsigned char code T_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char temp_table[2];
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
static char i;
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 1000 -> 1s
{
count = 1; //reset counter
P2=0xe0;P0=~T_display[temp_table[i]];P2=0;
P2=0xc0;P0=T_COM[i];P2=0;
i++;
if(i==2) i=0;
}
}
//-----------------------------------------------
void Delay2ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
i = 4;
j = 146;
do
{
while (--j);
} while (--i);
}
/* main program */
sbit relay = P0^4;
sbit buzzer = P0^6;
void main()
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
P2=0xa0;P0=0x00;P2=0x00;
while(1)
{
EA=0;
temperature= (unsigned char)rd_temperature_f();
EA=1;
if(temperature>25)
{
P2=0x80;P0=~0x01;P2=0x00;
P2=0xa0;buzzer=0;relay=1;P2=0x00;
}
if(temperature<21)
{
P2=0x80;P0=~0x02;P2=0x00;
P2=0xa0;buzzer=1;relay=0;P2=0x00;
}
temp_table[0]=temperature/10;
temp_table[1]=temperature%10;
Delay2ms();
}
}
=======================================================================
开头左上角Y3及U19
比赛包会提供驱动
SPI是串行外设接口的缩写
指令表格倒数第二行0x8E:写 丨 0x8F:读
这样对应数据传输协议中对位电平的限制
官方给的驱动函数写入的数据都要是BCD码,例如写入37s就是写入0x37h
但是我们自己定义的set_sfm()函数不是
void set_sfm(unsigned char shi,unsigned char fen,unsigned char miao)
{
Ds1302_Single_Byte_Write(0x8e,0); //关掉写保护。不然无法写入
Ds1302_Single_Byte_Write(0x80,(miao/10)*16+miao%10);
Ds1302_Single_Byte_Write(0x82,(fen/10)*16+fen%10);
Ds1302_Single_Byte_Write(0x84,(shi/10)*16+shi%10);
Ds1302_Single_Byte_Write(0x8e,0x80); //开启写保护
}
//读
shi = Read_Ds1302(0x85); //BCD码
miao = Read_Ds1302(0x81);
fen = Read_Ds1302(0x83);
1、编程实现DS1302实时时钟读写并用数码管显示
2、用两个独立按键分别控制时的增减;用两个独立按键分别控制分的增减
TIPS:
①独立按键程序若放在中断服务函数里,则中断函数执行时间增长,会导致主函数里的函数执行会到影响,所以尽量放在main函数里,通过设定全局变量bit flag;在中断服务函数里只进行加减数及标志位的改变,而具体执行程序放在main中,if flag==1 则执行
②注意时分秒都是16进制的BCD码,如果仅是++会出错,应该先将BCD码转换为10十进制数
=======================================================================
PCF8591是一个8位的AD/DA芯片,四路AD口和一路AD输出(15脚),主要用到部分的电路。比赛用到AIN1和AIN3两个AD输入,P20和P21用来通讯,
只需要两根线P20/P21,A0A1A2(电路中接地)是决定通讯地址的(24C02和8591的A0A1A2都接地但是内部本身的地址是一样,故编程可以区分)
AIN1是通过光敏电阻来调节输入1,AIN3是通过旋钮电阻来调节输入3的,
SCL:SCLK时钟线
SDA:SDATA数据线
数据传送时候SCL需保持高电平
PCF8591:add是AD通道,通道0~通道4分别对应0x00 ~ 0x03
AT24C02:add是地址,0x00~0xff
void write_adc(unsigned char add)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
}
第一个图是地址
第二图是写函数定义方式:
S - 启动
ADDRESS - 地址(由第一个图写则是0x90,读则是0x91)
0 - ADDRESS是7位,最后一位写的话就是0
A - 等待回应IIC_WaitAck()
add - 通道值,例如通道0(0x00),通道3(0x03)
unsigned char read_adc(unsigned char add)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_WaitAck();
IIC_Stop();
return temp;
}
要读的话需要先写控制字,告诉ADC采样哪一个通道然后再根据读时序写
void write_24c02(unsigned char add,unsigned char data1)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add); //存储单元地址0x00-0xFF
IIC_WaitAck();
IIC_SendByte(data1); //数据
IIC_WaitAck();
IIC_Stop();
}
unsigned char read_24c02(unsigned char add)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_WaitAck();
IIC_Stop();
return temp;
}
1、编程实现PCF8591读取通道1和通道3的电压值
/*
* 显示小数点的数据,则写两套数模,一套带小数点一套不带
* 如果要显示小数则实现小数乘10^x(x是小数的位数)这样就可以除不同值求出每一位的数值
* 读数据调用函数时怕中断影响程序,可以先关闭中断,待会再打开
*/
#include "reg52.h"
#include "intrins.h"
#include "iic.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1ms timer calculation method in 1T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
//-----------------------------------------------
unsigned char code T_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char code T_display_dian[]={0xbF,0x86,0xdB,0xcF,0xe6,0xeD,0xfD,0x87,0xfF,0xeF};
unsigned char code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char T_number1[8]={0}; //code定义的变量是写入ROM里的不能被修改
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
static char i=0; //初始化时应该赋值,不然还保留着上一次烧写的值
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 1000 -> 1s
{
count = 1; //reset counter
if(i==0)
{
P2=0xe0;P0=~T_display_dian[T_number1[i]];P2=0;
}
else
{
P2=0xe0;P0=~T_display[T_number1[i]];P2=0;
}
P2=0xc0;P0=T_COM[i];P2=0;
i++;
if(i==8) i=0; //显示位数
}
}
//-----------------------------------------------
/* main program */
void main()
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
while(1)
{
unsigned char temp=read_adc(0x01);
EA=0;
temp = temp*50/255;
EA=1;
if(temp != 0)
{
T_number1[0]=temp/10;
T_number1[1]=temp%10;
}
}
}
2、编程实现AT24C02的读写,让0x55单元的数值每次上电自加1
/*
* 本项目需要是上电一次,数字加一,所以在未断电时这个数字不用改变,所以只要把读取和写入AT2402的部分写在while循环外,每一次上电只要执行一次
*/
#include "reg52.h"
#include "intrins.h"
#include "iic.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
//-----------------------------------------------
/* define constants */
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1ms timer calculation method in 1T mode
/* define SFR */
sbit TEST_LED = P1^0; //work LED, flash once per second
/* define variables */
WORD count; //1000 times counter
//-----------------------------------------------
unsigned char code T_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char code T_display_dian[]={0xbF,0x86,0xdB,0xcF,0xe6,0xeD,0xfD,0x87,0xfF,0xeF};
unsigned char code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char T_number1[8]={0}; //code定义的变量是写入ROM里的不能被修改
unsigned char count_number=0;
/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 using 1
{
static char i=0; //初始化时应该赋值,不然还保留着上一次烧写的值
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if (count-- == 0) //1ms * 1000 -> 1s
{
count = 1; //reset counter
P2=0xe0;P0=~T_display[T_number1[i]];P2=0;
P2=0xc0;P0=T_COM[i];P2=0;
i++;
if(i==8) i=0; //显示位数
}
}
//-----------------------------------------------
/* main program */
void main()
{
unsigned char temp;
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1; //open global interrupt switch
count = 0; //initial counter
EA=0;
count_number =read_24c02(0x55);
temp =count_number;
write_24c02(0x55,++count_number);
EA=1;
T_number1[0]=temp/10;
T_number1[1]=temp%10;
while(1)
{
}
}
=======================================================================
C语言里面没有bool类型,C++里面才有。C99标准里面,定义了bool类型变量,只需要引入头文件
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
详细见博客https://blog.csdn.net/u011974987/article/details/52305364
如果一个变量只有几种可能的值,则可以定义为“枚举类型”;所谓“枚举”就是把可能的值一一的列举出来,变量的值只限于列举出来的值的范围
语法:
enum 枚举类型{枚举成员列表};
//其中的枚举成员列表是以逗号“,”相分隔
如:
enum Spectrum{red,black,yellow,blue,white};
-------------------------------------------
enum Spectrum{red,balck,yellow,blue,white};
0 1 2 3 4
默认情况下:该枚举列表中的常量值分别为:0,1,2,3,4
宏定义有弱点:其定义的只是预处理阶段的名字,在编译器的预处理阶段会进行简单的名字替换,而且不会进行类型的安全检查,其作用域是全局的,因此若程序中有变量true、false,则会被替换。为了避免这样的情况,采用将全部的宏用大写来命名,以区别于正常的代码。
P4.2替代P3.6
P4.4替代P3.7
P4.1接
P4.5接 控制存储器编程比如锁存
P5.5接 中断使能
P4.1/P4.5/P5.5在比赛中一般不用
STC89C52RC为12T单片机而IAP15为1T单片机
特殊功能IO
内部RC振荡器(自带晶振)
参考教程“IAP15F2K61S2单片机仿真使用说明.pdf”19页
1、在stc-isp点击“Keil仿真设置”->“添加型号和头文件到Keil中”,将文件添加到目录中后,创建项目选择MCU型号为“STC15F2K60S2”(差不多)
2、在option里点击“Debug”设置仿真器选择呢“STC Monitor-51 Driver”,COM口选择相应的口,波特率选选最高或默认即可
3、创建仿真芯片选择“将IAP15F2K61S2设置为仿真芯片(5.0V版本)”
【注意】
1、P3.0和P3.1作为仿真使用的串口,如果操作P3.0和P3.1会导致仿真失败
2、软件延时指令集选择STC-Y5系统
3、断点是停止在此语句执行之前的
1、硬件PWM
2、ADC
3、定时器(数量更多功能更多)
4、串口(数量更多功能更多)
1、熟悉如何使用IAP15单片机进行仿真
2、尝试用STC-ISP软件生成IAP15单片机的软件延时
=======================================================================
1、定时器0:1ms中断一次
2、状态机消抖矩阵键盘函数
3、按键10ms中断
4、数码管3ms中断
#include "STC15F2K60S2.h" //不用再包含reg51.h
#define u8 unsigned char
u8 code smg_du[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00}; //0-9
u8 code smg_wei[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
#define KEY P3
#define NO_KEY 0xff //无按键按下
#define KEY_STATE0 0 //判断按键按下
#define KEY_STATE1 1 //确认按键按下
#define KEY_STATE2 2 //释放
unsigned char Key_Scan()
{
static unsigned char key_state=KEY_STATE0;
u8 key_value=0,key_temp;
u8 key1,key2;
P30=0;P31=0;P32=0;P33=0;P34=1;P35=1;P42=1;P44=1;
if(P44==0) key1=0x70;
if(P42==0) key1=0xb0;
if(P35==0) key1=0xd0;
if(P34==0) key1=0xe0;
if((P34==1)&&(P35==1)&&(P42==1)&&(P44==1)) key1=0xf0;//没有按键
P30=1;P31=1;P32=1;P33=1;P34=0;P35=0;P42=0;P44=0;
if(P30==0) key2=0x0e;
if(P31==0) key2=0x0d;
if(P32==0) key2=0x0b;
if(P33==0) key2=0x07;
if((P30==1)&&(P31==1)&&(P32==1)&&(P33==1)) key2=0x0f;
key_temp=key1|key2;
switch(key_state)
{
case KEY_STATE0:
if(key_temp!=NO_KEY)
{
key_state=KEY_STATE1; //第一次确定有按键按下时后,给key_state赋值进入下一检查阶段
}
break;
case KEY_STATE1:
if(key_temp==NO_KEY)
{
key_state=KEY_STATE0;//第二次检测没按下
}
else
{
switch(key_temp)
{
case 0x77: key_value=4;break;
case 0x7b: key_value=5;break;
case 0x7d: key_value=6;break;
case 0x7e: key_value=7;break;
case 0xb7: key_value=8;break;
case 0xbb: key_value=9;break;
case 0xbd: key_value=10;break;
case 0xbe: key_value=11;break;
case 0xd7: key_value=12;break;
case 0xdb: key_value=13;break;
case 0xdd: key_value=14;break;
case 0xde: key_value=15;break;
case 0xe7: key_value=16;break;
case 0xeb: key_value=17;break;
case 0xed: key_value=18;break;
case 0xee: key_value=19;break;
}
key_state=KEY_STATE2;
}
break;
case KEY_STATE2:
if(key_temp==NO_KEY)
{
key_state=KEY_STATE0;
}
break;
}
return key_value;
}
void Timer_Init(void) //1ms
{
AUXR |= 0x80; //1T timer
TMOD &= 0xF0; // 16bit
TL0 = 0xCD;
TH0 = 0xD4;
TF0 = 0;
TR0 = 1;
ET0 = 1;
EA=1;
}
bit key_flag;
void main(void)
{
u8 key_val=NO_KEY; //检测的按键值
P2=0xa0;P0=0x00;P2=0x00; // close buzzer and relay
Timer_Init(); //1ms
while(1)
{
if(key_flag) //10ms,每进一次中断key_flag加一,1ms中断进10次就是10ms
{
key_flag=0;
key_val=Key_Scan();
switch(key_val)
{
case 4: break;
case 5: break;
case 6: break;
case 7: break;
case 8: break;
case 9: break;
case 10: break;
case 11: break;
case 12: break;
case 13: break;
case 14: break;
case 15: break;
case 16: break;
case 17: break;
case 18: break;
case 19: break;
}
}
}
}
void timer0() interrupt 1 using 1
{
static int key_count=0,smg_count=0,i=0;
key_count++;smg_count++;
if(key_count==10) //10ms
{
key_count=0;
key_flag=1;
}
if(smg_count==3) //3ms
{
smg_count=0;
P2=0xc0;P0=0;P2=0; //消影
P2=0xe0;P0=~smg_du[i];P2=0;
P2=0xc0;P0=smg_wei[i];P2=0;
i++;
if(i==8) i=0;
}
}
1、头文件包含不同了。#include “STC15F2K6052.h”
2、使用STC-ISP软件中的定时器定时,语句中还要加上中断使能,例如ET0=1;EA=1;
1、熟悉蓝桥杯模板的程序结构和函数
2、尝试自己编写类似的模板程序
1、熟悉CT107板子上的功能模块
2、熟悉8051系统单片机(微机原理)
3、熟悉SPI、IIC、Onewire、UART协议
4、熟悉运放基本原理和计算方法