单片机学习 记录51单片机学习中的笔记
一. 开发板选取及环境配置 开发板选取 开发板采用普中51单片机开发板,如下:
环境配置步骤
CH340驱动安装.
程序烧录.
keil5的安装及破解.
二. 51单片机介绍 单片机简介 普通计算机其由CPU(运算,控制),RAM(数据存储),ROM(程序存储),I/O设备构成,在普通计算机上被分成若干芯片,安装在一块主板上。而在单片机中,这些部件全部被做到一块集成电路芯片中,所以称之为单片机也叫MCU(微控制器).
51单片机 51单片机是对所有兼容Intel 8031指令系统的单片机的统称. 80C51芯片管脚图如下:
STC89CXX芯片介绍 常见PDIP封装(直插封装)的51单片机芯片实物图如下:
芯片表面印有STC89C52RC 40IPDIP40 1947HOXN52.X9CC丝印,丝印含义如下:
STC - 表示芯片生产公司,其他有AT,I,SST.
8 - 表示芯片为8051内核芯片.
9 - 表示内部含有 Flash EEPROM 存储器,还有如:
80C51中 0 表内部含有MASKROM(掩模 ROM)存储器;
如87C51中 7 表示内部含有EPROM(紫外线可擦除ROM)存储器.
C - 表示该器件为CMOS产品.
5 - 固定不变.
2 - 表示内部程序存储(FLASH)空间大小,1为4KB,2为8,3为12.
RC - 内部RAM为512B,还有如RD+表示RAM为1280B.
40 - 表示芯片外部晶振最高可接入40MHz.
I - 产品级别.
C - 表示商业及,温度范围0~70.
I - 表示工业级,温度范围-40~85.
A - 表示汽车级,温度范围-40~125.
M - 表示军用级,温度范围-55~150.
PDIP40 - 产品封装型号. PDIP表示双列直插式.
1946 - 生产日期为19年第47周.
HOXN52.X90C - 不详,可能表示为芯片制造工艺或处理工艺
三. 51单片机中语法 C51基本数据类型
C51扩充数据类型 用于精准控制寄存器.
1 2 3 4 sfr SCON = 0X98 ; sfr16 T2 = 0XCC ; sbit TI = SCON^1 ;
reentrant修饰符 用于把函数定义为可重入函数.
interrupt x 修饰符 这个比较重要,将函数转化为中断函数,x的数值对应不同的中断.
0 - 外部中断0.
1 - 定时/计数器T0.
2 - 外部中断1.
3 - 定时/计数器T1.
4 - 串行口中断.
5 - 定时/计时器T2.
四. 51单片机最小系统 单片机需要工作,不仅仅需要芯片,还需要相应的外围电路,能满足51单片机最简单基础的电路,称为51单片机的最小系统.
51单片机最小系统构成 最小系统由以下几部分组成:
晶振电路: 提供时钟给单片机工作,如人的心脏.
复位电路: 提供系统复位操作,通过复位按键重启系统.
电源电路: 选择合适稳定的电源电路,提供适合的电压.
下载电路: 烧录程序.
51单片机的P0口是**漏极开路 ,即输出高电平会导致高阻态,要让它输出高电平就必须外接 上拉电阻 **,如下:
晶振电路 单片机正常工作需要一个时钟,因此需要在其晶振引脚上外界晶振 (stc89cxx晶振引脚为18和19) . 我学习的开发板为11.0592M,51可采取的范围在0-40MHz.
晶振的电路如下: 若直接将晶振接入单片机引脚,系统工作不稳定,原因是晶振起振的一瞬间会产生一些**电感 ,为了消除这个电感,在两端加上一个 电容 ,电容的选取需要 无极性 **,另一端需要共地,只有保证晶振电路稳定,单片机才能继续工作.
复位电路 单片机引脚中有一个RST复位引脚,条件是高电平复位,所以只需要让这个引脚保持一段时间的高电平即可.
此功能有两种方式:
通过按键进行手动复位,电路如下: 按下后VCC直接进入RST引脚,松开后VCC断开,RST被电阻拉为低电平.
电源开启自动复位: 利用RC的充放电功能,电源一开启,由于电容隔直 ,VCC直接进入RST,然后电容开始慢慢充电,直到充电完成,此时RST被电阻拉低
电源电路 开发板电源电路如下:
下载电路 使用USB转TTL串口电平芯片,建立PC机和单片机数据传输通路. 通常使用CH340G或CH340C芯片来完成电平转换. CH340G需外接12M晶振,CH340C内部自带. 下载电路如下:
看不懂该电路目前.
五. KEIL C51软件安装 b站随便搜搜就有,也就是对KEIL软件的破解.
六. KEIL单片机工程模板创建 51单片机工程模板创建
project -> New vision project.
选择CPU型号,该开发板AT89C52即可.
给工程添加文件.
点击魔术棒选项Output选项卡,勾选Create HEX file.
头文件内容
该头文件定义了52系列单片机内部所有的功能寄存器.
七. 点亮第一个LED 单片机GPIO介绍
P0端口:
P1端口:
P2端口:
P3端口:
重点 :
P0口是漏极开路,要使其输出高电平,必须外接上拉电阻,通常选择4.7~10k阻值.
P0,P1,P2几乎都用作普通I/O口使用,即可作为输入,又可作为输出.
P3口即可用作普通I/O口,也可作为第二功能使用,比如外部中断,串口.
电路中一些重要门电路:
三态门:
EN使能端,1即无效时,Y=高阻态,反之Y=A‘.
D触发器: D数据输入,控制下一个状态值;CLK控制状态改变发生的时间.
P0中的两个MOS管,场效应管输出驱动: 没了解.
上拉电阻.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <REGX52.H> sbit LED = P2^0 ; void main () { LED = 0 ; while (1 ) { } }
e.g.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <REGX52.H> #include <intrins.h> sbit LED = P2^0 ; void Delay100ms (void ) ;void main () { LED = 0 ; while (1 ) { Delay100ms(); LED = 1 ; Delay100ms(); LED = 0 ; } } void Delay100ms (void ) { unsigned char data i, j, k; _nop_(); _nop_(); i = 5 ; j = 52 ; k = 195 ; do { do { while (--k); } while (--j); } while (--i); }
e.g.3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <REGX52.H> #include <intrins.h> unsigned char i;sbit LED = P2^0 ; void Delay100ms (void ) ;void main () { while (1 ) { for (i=0 ; i<8 ; i++) { P2 = ~((0x80 >> i)); Delay100ms(); } } } void Delay100ms (void ) { unsigned char data i, j, k; _nop_(); _nop_(); i = 5 ; j = 52 ; k = 195 ; do { do { while (--k); } while (--j); } while (--i); }
e.g.4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <REGX52.H> #include <intrins.h> #define LED_PORT P2 unsigned char i;sbit LED = P2^0 ; void Delay100ms (void ) ;void main () { LED_PORT = ~0x01 ; Delay100ms(); while (1 ) { for (i=0 ; i<7 ; i++) { LED_PORT = _crol_(LED_PORT, 1 ); Delay100ms(); } for (i=0 ; i<7 ; i++) { LED_PORT = _cror_(LED_PORT, 1 ); Delay100ms(); } } } void Delay100ms (void ) { unsigned char data i, j, k; _nop_(); _nop_(); i = 5 ; j = 52 ; k = 195 ; do { do { while (--k); } while (--j); } while (--i); }
八. 蜂鸣器实验 九. 静态数码管实验 数码管硬件介绍 数码管相当于多个led组合起来的,大体分为共阴极和共阳极两类,即将所有发光二极管的阴/阳级接在一起. 电路图如下:
所以如何显示不同的数字,即将对应引脚点亮,组合出来.
开发板采取的为共阴极接法,由于P0为漏极开路,输出高电平能力弱,所以需要接上拉电阻. 同时为了节省I/O口,将八个数码管的引脚并联,其次用38译码器选择准确的数码管.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <REGX52.H> #include "Delay.h" #define SMG_A_DP_PORT P0 unsigned char gsmod_code[17 ] = {0x3f ,0x06 ,0x5b ,0x4f ,0x66 ,0x6d ,0x7d ,0x07 , 0x7f ,0x6f ,0x77 ,0x7c ,0x39 ,0x5e ,0x79 ,0x71 }; unsigned char i;void main () { while (1 ) { for (i=0 ; i<17 ; i++) { SMG_A_DP_PORT = gsmod_code[i]; Delay(100 ); } } }
十. 动态数码管实验 动态数码管原理 动态指的是多位数码管同时显示,显示具体数值和静态同理,通过段选码控制. 但为了节约I/O口,将多位数码管的段选引脚并接;然后通过一个38译码器进行位选,即同一时间只有一个数码管显示,但在短时间内不断进行位选,在不同数码管上显示不同数字.
74HC138芯片 I/O扩展芯片,类似的还有74HC164、74HC595,74HC245,用很少的I/O口扩展出8个控制口. 此开发板采用74HC138芯片,简单来说可以看作CBA组成一个二进制数,输出0~8即为扩展出来的控制位.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #ifndef __Nixie_H__ #define __Nixie_H__ void Nixie (unsigned char location, number) ;#endif #include <regx52.h> #include "Delay.h" #define SMG_A_DP_PORT P0 typedef unsigned char u8;unsigned char mode_code[10 ] = {0x3f ,0x06 ,0x5b ,0x4f ,0x66 ,0x6d ,0x7d ,0x07 , 0x7f ,0x6f , }; void Nixie (u8 location, u8 number) { switch (location) { case 1 : P2_4=1 , P2_3=1 , P2_2=1 ; break ; case 2 : P2_4=1 , P2_3=1 , P2_2=0 ; break ; case 3 : P2_4=1 , P2_3=0 , P2_2=1 ; break ; case 4 : P2_4=1 , P2_3=0 , P2_2=0 ; break ; case 5 : P2_4=0 , P2_3=1 , P2_2=1 ; break ; case 6 : P2_4=0 , P2_3=1 , P2_2=0 ; break ; case 7 : P2_4=0 , P2_3=0 , P2_2=1 ; break ; case 8 : P2_4=0 , P2_3=0 , P2_2=0 ; break ; } SMG_A_DP_PORT = mode_code[number]; Delay(1 ); SMG_A_DP_PORT = 0x00 ; } #include <REGX52.H> #include "Nixie.h" void main () { while (1 ) { Nixie(1 ,1 ); Nixie(2 ,2 ); Nixie(3 ,3 ); } }
十一. 独立按键实验 独立按键介绍 值得注意的是,由于按键的机械弹性,按键开关在按下释放的过程中,不会马上稳定的接通,电压信号如下:
所以需要对其进行按键消抖,分为软硬件两种方式. 这里我们采用软件消抖,即在按键按下之后延时10ms,在按键释放之后再次延时10ms.
开发板上独立按键模块电路如下:
判断按键是否按下,读取P3.0-P3.3引脚的状态即可.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #ifndef __KEY_H__ #define __KEY_H__ unsigned char Key () ;#endif #include <REGX52.H> #include "Delay.h" sbit k1 = P3^1 ; sbit k2 = P3^0 ; sbit k3 = P3^2 ; sbit k4 = P3^3 ; unsigned char key_number;unsigned char Key () { key_number = 0 ; if (k1==0 ) {Delay(20 );while (k1==0 );Delay(20 );key_number =1 ;} if (k2==0 ) {Delay(20 );while (k2==0 );Delay(20 );key_number =2 ;} if (k3==0 ) {Delay(20 );while (k3==0 );Delay(20 );key_number =3 ;} if (k4==0 ) {Delay(20 );while (k4==0 );Delay(20 );key_number =4 ;} return key_number; } #include <REGX52.H> #include "Key.h" unsigned char number;void main () { while (1 ) { number = Key(); if (number==1 ) {P2_0 = !P2_0;} if (number==2 ) {P2_1 = !P2_1;} if (number==3 ) {P2_2 = !P2_2;} if (number==4 ) {P2_3 = !P2_3;} } }
十二. 矩阵按键实验 矩阵按键介绍 硬件电路如下:
通过并联节省I/O,操作思路类似与动态数码管,逐行扫描.
代码部分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #ifndef __MatrixKey_H__ #define __MatrixKey_H__ unsigned char Matrix_Key () ;#endif #include <REGX52.H> #include "Delay.h" unsigned char key_number1;unsigned char Matrix_Key () { P1=0xFF ; P1_7 = 0 ; if (P1_3 == 0 ) {Delay(20 );while (P1_3==0 );Delay(20 );key_number1=1 ;} if (P1_2 == 0 ) {Delay(20 );while (P1_2==0 );Delay(20 );key_number1=2 ;} if (P1_1 == 0 ) {Delay(20 );while (P1_1==0 );Delay(20 );key_number1=3 ;} if (P1_0 == 0 ) {Delay(20 );while (P1_0==0 );Delay(20 );key_number1=4 ;} P1=0xFF ; P1_6 = 0 ; if (P1_3 == 0 ) {Delay(20 );while (P1_3==0 );Delay(20 );key_number1=5 ;} if (P1_2 == 0 ) {Delay(20 );while (P1_2==0 );Delay(20 );key_number1=6 ;} if (P1_1 == 0 ) {Delay(20 );while (P1_1==0 );Delay(20 );key_number1=7 ;} if (P1_0 == 0 ) {Delay(20 );while (P1_0==0 );Delay(20 );key_number1=8 ;} P1=0xFF ; P1_5 = 0 ; if (P1_3 == 0 ) {Delay(20 );while (P1_3==0 );Delay(20 );key_number1=9 ;} if (P1_2 == 0 ) {Delay(20 );while (P1_2==0 );Delay(20 );key_number1=10 ;} if (P1_1 == 0 ) {Delay(20 );while (P1_1==0 );Delay(20 );key_number1=11 ;} if (P1_0 == 0 ) {Delay(20 );while (P1_0==0 );Delay(20 );key_number1=12 ;} P1=0xFF ; P1_4 = 0 ; if (P1_3 == 0 ) {Delay(20 );while (P1_3==0 );Delay(20 );key_number1=13 ;} if (P1_2 == 0 ) {Delay(20 );while (P1_2==0 );Delay(20 );key_number1=14 ;} if (P1_1 == 0 ) {Delay(20 );while (P1_1==0 );Delay(20 );key_number1=15 ;} if (P1_0 == 0 ) {Delay(20 );while (P1_0==0 );Delay(20 );key_number1=16 ;} return key_number1; } #include <REGX52.H> #include "MatrixKey.h" #include "LCD1602.h" unsigned char number;void main () { LCD_Init(); LCD_ShowString(2 ,1 ,"hello" ); while (1 ) { number = Matrix_Key(); LCD_ShowNum(1 ,1 ,number,2 ); } }
e.g.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 unsigned char Matrix_flip_scan () { P1 = 0x0f ; if (P1 != 0x0f ) { Delay(20 ); switch (P1) { case 0x07 : key_number1 = 1 ; break ; case 0x0b : key_number1 = 2 ; break ; case 0x0d : key_number1 = 3 ; break ; case 0x0e : key_number1 = 4 ; break ; } P1 = 0xf0 ; switch (P1) { case 0x70 : key_number1 = key_number1; break ; case 0xb0 : key_number1 = key_number1+4 ; break ; case 0xd0 : key_number1 = key_number1+8 ; break ; case 0xe0 : key_number1 = key_number1+12 ; break ; } while (P1 != 0xf0 ); Delay(20 ); } return key_number1; }
十三. 74HC595芯片 芯片介绍 8位串行输入,并行输出的位移缓存器,其中并行输出为三态输出,高电平,低电平和高阻态. 芯片管脚如下:
13输出使能,低电平有效.
11SCK移位寄存器时钟输入,上升沿有效,即每个上升沿将数据移动一位.
12存储寄存器时钟输入,上升沿有效,即上升沿输出到并行端口.
14串行数据输入.
15和1-7并行数据输出.
开发板上74HC595芯片用于LED点阵,以便节约I/O口,如下:
注意: J24将OE接到GND上
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #ifndef __LED_H__ #define __LED_H__ void HC595_Write_Data (unsigned char Data) ;#endif #include <REGX52.H> #include "Delay.h" sbit SCK = P3^6 ; sbit RCK = P3^5 ; sbit SER = P3^4 ; void HC595_Write_Data (unsigned char Data) { unsigned char i; for (i=0 ; i<8 ; i++) { SER = Data&(0x80 >>i); SCK = 0 ; SCK = 1 ; } RCK = 0 ; RCK = 1 ; } #include <REGX52.H> #include "Delay.h" #include "Led.h" #define LED_PORT P0 unsigned char ghc595_buf[8 ] = {0x01 ,0x02 ,0x04 ,0x08 ,0x10 ,0x20 ,0x40 ,0x80 };unsigned char i;void main () { LED_PORT = 0x00 ; while (1 ) { for (i=0 ; i<8 ; i++) { HC595_Write_Data(0x00 ); HC595_Write_Data(ghc595_buf[i]); Delay(500 ); } } }
十四. LED点阵实验
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <REGX52.H> #include "Led.h" #define LED_PORT P0 void main () { LED_PORT = ~0x80 ; HC595_Write_Data(0x80 ); while (1 ) { } }
e.g.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <REGX52.H> #include "Delay.h" #include "Led.h" #define LED_PORT P0 unsigned char led_buf[8 ] = {0x00 ,0x7E ,0x81 ,0x81 ,0x81 ,0x81 ,0x7E ,0x00 };unsigned char led_col[8 ] ={0x7f ,0xbf ,0xdf ,0xef ,0xf7 ,0xfb ,0xfd ,0xfe };unsigned char led_heart[8 ]={0x38 ,0x7C ,0x7E ,0x3F ,0x3F ,0x7E ,0x7C ,0x38 }; void main () { unsigned char i = 0 ; while (1 ) { for (i=0 ; i<8 ; i++) { LED_PORT = led_col[i]; HC595_Write_Data(led_buf[i]); HC595_Write_Data(0x00 ); } } }
原理即从左到右依次点亮,由于LED的余晖,肉眼看上去同时显示,和动态数码管原理一致.
当不使用LED点阵时,将J24黄色跳线帽短接到VCC端. 因为LED点阵和数码管都使用到了P0口,为了避免干扰.
十五. 直流电机实验 直流电机介绍 实物图如下:
作用将电能转换为机械能(直流电动机),或将机械能转化为电能(直流发电机). 由定子和转子两大部分组成. 用法简单,两端加上直流电就能工作,没有正负之分,正反接改变转动方向.
ULN2003芯片介绍 51单片机用来控制而非驱动,如果直接用芯片的GPIO管脚去驱动大功率芯片,要么将芯片烧坏,要么驱动不起来. ULN2003作为一个驱动芯片, 来驱动直流电机.
逻辑框图:
数电没学好看不懂捏XD,不过内部可以看出相当于非门电路,输入高为低. 由于输出为集电极开路,ULN2003要输出高电平,必须在输出口外接上拉电阻. 这也是为什么后面连接直流电机时不能直接将ULN2003的2个输出口接电机线,而必须一根线接电源,另一根接ULN2003输出口.
开发板电路如下:
输入口与单片机的P1.0-P1.3引脚连接,P2.5应该是提供给蜂鸣器使用的,J47即提供给外部使用的接口,可以支持直流电机,五线四相步进电机,本实验使用直流电机,即一根线连接在VCC上,一根线连接在OUT1上.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <REGX52.H> #include "Delay.h" sbit motor = P1^0 ; void main () { motor = 1 ; Delay(1000 ); motor = 0 ; while (1 ) { } }
十六. 步进电机实验 没有硬件跳过.
十七. 中断系统 中断介绍 中断概念 为了使单片机具有对外部或内部随机发生的事件实时处理能力而设置的。拿一个生活中的例子举例:打开热水壶烧了一壶水,然后去洗衣,在洗衣的过程中,热水烧好了,发出警报声,此时就相当于发生了中断,你停下洗衣,将热水灌入热水壶,相当于执行中断处理程序,然后继续洗衣,即中断返回,继续执行主程序.
中断嵌套 如果CPU能够暂停原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完后,再回到原低级中断服务程序,这样的过程称为中断嵌套. 这样的中断系统称为多级中断系统,没有中断嵌套的称为单级中断系统.
中断的开启与关闭,设置启动哪一个中断等都是由单片机内部的一些特殊功能寄存器决定的。
中断结构及相关寄存器 中断结构 此开发板提供了8个中断源,分别为外部中断0,定时器0,外部中断1,定时器1,串口中断,定时器2,外部中断2,外部中断3. 中断号如下:
不同的单片机型号资源可能不同,需要查看芯片手册,51系列一定有基本的5个,即INT0,INT1,定时器0/1,串口中断. 基本型的单片机只有2个中断优先级,此开发板的51芯片具有4个中断优先级,中断查询次序表如下: 中断查询次序即为中断号.
以传统51单片机的中断系统图来介绍,如下:
INT0 对应的是 P3.2 口的附加功能,可由 IT0(TCON.0)选择其为低电平有效还是下降沿有效。当 CPU 检测到 P3.2 引脚上出现有效的中断信号时,中断标志 IE0(TCON.1)置 1,向 CPU 申请中断.
INT1 对应的是 P3.3 口的附加功能,可由 IT1(TCON.2)选择其为低电平有效还是下降沿有效。当 CPU 检测到 P3.3 引脚上出现有效的中断信号时,中断标志 IE1(TCON.3)置 1,向 CPU 申请中断.
T0 对应的是 P3.4 口的附加功能,TF0(TCON.5),片内定时/计数器 T0 溢出中断请求标志。当定时/计数器 T0 发生溢出时,置位 TF0,并向 CPU 申请中断.
T1 对应的是 P3.5 口的附加功能,TF1(TCON.7),片内定时/计数器 T1溢出中断请求标志。当定时/计数器 T1 发生溢出时,置位 TF1,并向 CPU 申请中断.
RXD 和 TXD 对应的是 P3.0 和 P3.1 口的附加功能,RI(SCON.0)或TI (SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位 RI或当串行口发送完一帧串行数据时置位 TI,向 CPU 申请中断.
中断相关寄存器 中断允许控制 (寄存器IE)
位
7
6
5
4
3
2
1
0
字节地址: A8H
EA
ES
ET1
EX1
ET0
EX0
EX0:外部中断0允许位;ET0:定时器0中断允许位;EX1:外部中断1允许位;ET1:定时器1中断允许位;ES:串口中断允许位;EA:中断总允许位.
中断请求标志(寄存器TCON)
位
7
6
5
4
3
2
1
0
字节地址:88H
TF1
TR1
TF0
TR0
IE1
IT1
IE0
IT0
IT0:外部中断0触发方式控制位,为0时电平触发,为1时边沿触发(下降沿有效);IE0:外部中断0中断请求标志位;IT1:外部中断1触发方式控制位;IE1:外部中断1中断请求标志位;TF0:定时器0溢出中断请求标志位;TF1:定时器1溢出中断请求标志位.
中断响应条件
中断源有中断请求.
此中断源的中断允许位为1.
中断总允许位(EA)为1.
使用中断参考
1 2 3 4 5 6 7 8 9 10 11 12 13 void Int0_init () { EA = 1 ; EX0 = 1 ; IT0 = 0 /1 ; } void int0_routine () interrupt 0{ ... }
十八. 外部中断实验 代码部分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <REGX52.H> #include "Delay.h" sbit k3 = P3^2 ; sbit led = P2^0 ; void Int0_Init () ;void main () { Int0_Init(); while (1 ) { } } void Int0_Init () { EA = 1 ; EX0 = 1 ; IT0 = 1 ; } void Int0_Routine () interrupt 0{ Delay(10 ); if (k3==0 ) led = !led; }
十九. 定时器中断 定时器介绍
CPU时序有关知识:
振荡周期:为单片机提供定时信号的振荡源的周期.
状态周期:2个振荡周期为1个状态周期,又称为S周期或时钟周期.
机器周期:1个机器周期含6个状态周期,12个振荡周期.
指令周期:完成1条指令所占用的全部时间,它以机器周期为单位.
以外接晶振12Mhz举例,振荡周期=1/12us,状态周期=1/6us,机器周期=1us,指令周期=1-4us.
工作方式(寄存器TMOD) 设置定时/计数器的工作方式,低四位用于T0,高四位用于T1.
位
7
6
5
4
3
2
1
0
字节地址:89H
GATE
C/T’
M1
M0
GATE
C/T’
M1
M0
GATE:门控位,用于控制定时器的启动是否受外部中断源信号的影响,GATE=1时,要用软件使TR0为1,同时外部中断引脚INT0也为高电平时,才能启动;C/T’:定时/计数模式选择位,C/T=0为定时,C/T=1为计数;M1M0:工作方式设置位,如下:
M1M0
工作方式
说明
00
方式0
13位定时/计数器
01
方式1
16位定时/计数器
10
方式2
8位自动重装定时/计数器
11
方式3
T0分成两个独立的8位定时/计数器;T1停止计时
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #ifndef __TIMER0_H__ #define __TIMER0_H__ void Timer0_Init () ;#endif #include <REGX52.H> void Timer0_Init () { TMOD |= 0x01 ; TL0 = 0x18 ; TH0 = 0xFC ; TR0 = 1 ; TF0 = 0 ; ET0 = 1 ; EA = 1 ; PT0 = 1 ; } #include <REGX52.H> #include "Timer0.h" sbit led = P2^0 ; void main () { led = 0 ; Timer0_Init(); while (1 ) { } } void Timer0_Routine () interrupt 1{ static unsigned int counter = 0 ; TL0 = 0x66 ; TH0 = 0xfc ; counter++; if (counter>1000 ) { counter = 0 ; led = !led; } }
二十. 串口通信实验 通信概念 波特率: 表示每秒钟传输了多少个码元. 简单来说,对于数据接收时的时间要求做出一个规定.
串口内部结构
通过对SBUF的写入和读取,实现数据的收发. 收到数据与发送数据时将RI/TI置1,前往串口中断. TI计时器用于获取波特率
串口相关寄存器 串口控制(寄存器SCON)
位
7
6
5
4
3
2
1
0
字节地址:98H
SM0
SM1
SM2
REN
TB8
RB8
TI
RI
SM0SM1为工作方式选择位,如下:
SM0
SM1
方式
说明
波特率
0
0
0
移位寄存器
FOSC/12
0
1
1
10位异步收发器(8位数据)
可变
1
0
2
11位异步收发器(9位数据)
FOSC/64或FOSC/32
1
1
3
11位异步收发器(9位数据)
可变
SM2:多机通信控制位,用于方式2和方式3;REN:接受使能,允许串行接受位;TB8:在方式2或方式3中,是发送数据的第9位,在方式0或方式1中未用到;RB8:在方式2或方式3中,是接受数据的第九位,方式1中,若SM2=0,RB8是接收到的停止位;TI:发送中断标志位,由内部硬件使TI置1,像CPU发送中断申请;RI:接受中断标志位,由内部硬件使RI置1,像CPU发送中断申请.
电源控制(寄存器PCON)
位
7
6
5
4
3
2
1
0
字节地址:97H
SMOD
SMOD:波特率额倍增位,当SMOD=1时,波特率提高一倍.
代码部分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #ifndef __UART_H__ #define __UART_H__ void Uart_Init () ;void Uart_Send_Byte (unsigned char Data) ;#endif #include <REGX52.H> void Uart_Init () { SCON = 0x50 ; PCON |= 0x80 ; TMOD &= 0x0f ; TMOD |= 0x20 ; TL1 = 0xfa ; TH1 = 0xfa ; ES = 1 ; TR1 = 1 ; ET1 = 0 ; EA = 1 ; } void Uart_Send_Byte (unsigned char Data) { SBUF = Data; while (TI==0 ); TI = 0 ; } #include <REGX52.H> #include "Uart.h" unsigned char buffer;void main () { Uart_Init(); while (1 ) { P2 = ~buffer; } } void Uart_Routine () interrupt 4{ if (RI==1 ) { RI = 0 ; buffer = SBUF; Uart_Send_Byte(buffer); } }
二十一. I2C-EEPROM实验 I2C协议 通过两根线完成数据传输,SCL时钟线和SDA数据线.
I2C电路规范
所有I2C设备的SCL连在一起,SDA连在一起.
设备的SCL和SDA均要配置成开漏输出模式.
SCL和SDA各添加一个上拉电阻,阻值为4.7KΩ左右.
开漏输出和上拉电阻的共同作用实现了线与,用以解决多机通信的互相干扰.
时序结构 起始条件 :SCL高电平期间,SDA从高电平切换到低电平;终止条件 :SCL高电平期间,SDA从低电平切换到高电平.
发送一个字节 :SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节.
接收一个字节 :SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),然后拉高SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA).
发送应答 :在接收完一个字节之后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答;接收应答 :在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA).
然后由上文时序结构,根据I2C协议要求组成协议帧即可.
I2C数据帧 发送一帧数据.
接收一帧数据.
AT24C02芯片 一种可以实现掉电不丢失数据的存储器,可用于保存单片机运行时想要永久保存的数据信息. 芯片管脚如下:
引脚
功能
VCC/GND
电源(1.8~5.5v)
WP
写保护(高电平有效)
SCL/SDA
I2C接口
A0,A1,A2
I2C地址
数据帧 字节写:
随机读:
操作方式,即根据AT24C02需要的协议帧,根据I2C时序结构写入即可.
代码部分 e.g.1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 #ifndef __AT24C02_H__ #define __AT24C02_H__ void AT24C02_Send (unsigned char Word_Addr, Data) ;unsigned char AT24C02_Recv (unsigned char Word_Addr) ;#endif #include <REGX52.H> #include "I2C.h" #define AT24C02_ADDRESS 0xA0 void AT24C02_Send (unsigned char Word_Addr, Data) { I2C_Start(); I2C_Send_Byte(AT24C02_ADDRESS); I2C_RACK(); I2C_Send_Byte(Word_Addr); I2C_RACK(); I2C_Send_Byte(Data); I2C_RACK(); I2C_Stop(); } unsigned char AT24C02_Recv (unsigned char Word_Addr) { unsigned char Data; I2C_Start(); I2C_Send_Byte(AT24C02_ADDRESS); I2C_RACK(); I2C_Send_Byte(Word_Addr); I2C_RACK(); I2C_Start(); I2C_Send_Byte(AT24C02_ADDRESS|0x01 ); I2C_RACK(); Data = I2C_Recv_Byte(); I2C_SACK(1 ); I2C_Stop(); return Data; } #ifndef __I2C_H__ #define __I2C_H__ void I2C_Start () ;void I2C_Stop () ;void I2C_SACK (unsigned char ackbit) ;unsigned char I2C_RACK () ;void I2C_Send_Byte (unsigned char Data) ;unsigned char I2C_Recv_Byte () ;#endif #include <REGX52.H> sbit SCL = P2^1 ; sbit SDA = P2^0 ; void I2C_Start () { SDA = 1 ; SCL = 1 ; SDA = 0 ; SCL = 0 ; } void I2C_Stop () { SDA = 0 ; SCL = 1 ; SDA = 1 ; } void I2C_SACK (unsigned char ackbit) { SDA = ackbit; SCL = 1 ; SCL = 0 ; } unsigned char I2C_RACK () { unsigned char ackbit; SDA = 1 ; ackbit = SDA; SCL = 1 ; SCL = 0 ; return ackbit; } void I2C_Send_Byte (unsigned char Data) { unsigned char i; for (i=0 ; i<8 ; i++) { SDA = Data&(0x80 >>i); SCL = 1 ; SCL = 0 ; } } unsigned char I2C_Recv_Byte () { unsigned char Data = 0x00 ; unsigned char i; SDA = 1 ; for (i=0 ; i<8 ; i++) { SCL = 1 ; if (SDA){Data|=(0x80 >>i);} SCL = 0 ; } return Data; } #include <REGX52.H> #include <Nixie.h> #include <Key.h> #include <AT24C02.h> unsigned char number;unsigned char key_num;void main () { while (1 ) { key_num = Key(); Nixie(1 , number/100 ); Nixie(2 , number%100 /10 ); Nixie(3 , number%10 ); if (key_num == 1 ) { AT24C02_Send(0 ,number); } if (key_num == 2 ) { number = AT24C02_Recv(0 ); } if (key_num == 3 ) { number++; } if (key_num == 4 ) { number=0 ; } } }
二十二. DS18B20温度传感器实验 芯片介绍 一种常见的数字温度传感器,采用单总线协议传输数据,引脚及应用电路如下:
内部结构框图如下:
64-BIT ROM:器件地址,用于总线通信的寻址.
SCRATCHPAD(暂存器):用于总线的数据相互.
EEPROM:用于保存温度触发阈值和配置参数.
DS18B20操作流程
初始化:从机复位,主机判断从机是否响应.
ROM操作:ROM指令+本指令需要的读写操作.
功能操作:功能指令+本指令需要的读写操作.
ROM指令
功能指令
SEARCH ROM[F0H]
CONVERT T[44H]
READ ROM[33H]
WRITE SCRATCHPAD[4EH]
MATCH ROM[55H]
READ SCRATCHPAD[BEH]
SKIP ROM[CCH]
COPY SCRATCHPAD[48H]
ALARM SEARCH[ECH]
RECALL E2[B8H]
READ POWER SUPPLY[B4H]
单总线协议 类似I2C那样对通信时的一些规范.
初始化时序结构 主机将总线拉低至少480us,然后释放总线,等待1560us后,存在的从机会拉低总线60240us以响应主机,之后从机将释放总线.
发送一位时序结构 主机将总线拉低60120us,然后释放总线,表示发送0;主机将总线拉低115us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us.
接收一位时序结构 主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 #ifndef __DS18B20_H__ #define __DS18B20__ unsigned char DS18B20_Convert () ;float DS18B20_Read () ;#endif #include "OneWire.h" #define SKIP_ROM 0xcc #define CONVERT_T 0x44 #define READ_SCRATCH 0xbe unsigned char DS18B20_Convert () { unsigned char ackbit; ackbit = OW_Init(); OW_Send(SKIP_ROM); OW_Send(CONVERT_T); return ackbit; } float DS18B20_Read () { float value; unsigned int temp=0 ; unsigned char lsb=0 , msb=0 ; OW_Init(); OW_Send(SKIP_ROM); OW_Send(READ_SCRATCH); lsb = OW_Recv(); msb = OW_Recv(); temp = (msb<<8 ) | lsb; if ((temp&0xf800 )==0xf800 ) { temp = (~temp)+1 ; value = temp*(-0.0625 ); } else { value = temp*0.0625 ; } return value; } #ifndef __ONEWIRE_H__ #define __ONEWIRE_H__ unsigned char OW_Init () ;void Send_Bit (unsigned char Bit) ;unsigned char Recv_Bit () ;unsigned char OW_Recv () ;void OW_Send (unsigned char Data) ;#endif #include <intrins.h> #include <REGX52.H> sbit DQ = P3^7 ; void Delay10us (void ) { unsigned char data i; i = 2 ; while (--i); } void Delay5us (void ) { } void Delay50us (void ) { unsigned char data i; _nop_(); i = 20 ; while (--i); } void Delay500us (void ) { unsigned char data i; _nop_(); i = 227 ; while (--i); } unsigned char OW_Init () { unsigned char ackbit; DQ = 1 ; DQ = 0 ; Delay500us(); DQ = 1 ; Delay50us(); ackbit = DQ; Delay500us(); return ackbit; } void Send_Bit (unsigned char Bit) { DQ = 0 ; Delay10us(); DQ = Bit; Delay50us(); DQ = 1 ; } unsigned char Recv_Bit () { unsigned char Bit; DQ = 0 ; Delay5us(); DQ = 1 ; Delay5us(); Bit = DQ; Delay50us(); return Bit; } unsigned char OW_Recv () { unsigned char i, Data=0x00 ; for (i=0 ; i<8 ; i++) { if (Recv_Bit()) { Data |= (0x01 <<i); } } return Data; } void OW_Send (unsigned char Data) { unsigned char i; for (i=0 ; i<8 ; i++) { Send_Bit(Data&(0x01 <<i)); } } #include <REGX52.H> #include <DS18B20.h> #include <Delay.h> #include <LCD1602.h> #include <Nixie.h> sbit led = P2^0 ; float t;void main () { unsigned char ackbit; ackbit = DS18B20_Convert(); if (!ackbit) { led = 0 ; } Delay(1000 ); LCD_Init(); LCD_ShowString(1 ,1 ,"Temperature:" ); while (1 ) { DS18B20_Convert(); t = DS18B20_Read(); if (t<0 ) { LCD_ShowChar(2 ,1 ,'-' ); t = -t; } else { LCD_ShowChar(2 ,1 ,'+' ); } LCD_ShowNum(2 ,2 ,t,3 ); LCD_ShowChar(2 ,5 ,'.' ); LCD_ShowNum(2 ,6 ,(unsigned long )(t*10000 )%10000 ,4 ); } }
二十三. DS1302时钟芯片介绍 芯片介绍 SPI接口.
芯片管脚如下:
vcc2:主电源引脚;X1,X2:外部晶振引脚;GND:电源地;CE:使能引脚;IO:串行数据引脚;SCLK:串行时钟引脚;VCC1:备用电源.
寄存器 控制寄存器 存放DS1302的控制命令字,DS1302的CE引脚回到高电平后写入的第一个字节就为控制命令,它用于对DS1302的读写过程进行控制,格式如下:
寄存器名称
7
6
5
4
3
2
1
0
1
RAM/CK
A4
A3
A2
A1
A0
RD
秒寄存器
1
0
0
0
0
0
0
0/1
分寄存器
1
0
0
0
0
0
1
0/1
时寄存器
1
0
0
0
0
1
0
0/1
日寄存器
1
0
0
0
0
1
1
0/1
月寄存器
1
0
0
0
1
0
0
0/1
周寄存器
1
0
0
0
1
0
1
0/1
年寄存器
1
0
0
0
1
1
0
0/1
写保护寄存器
1
0
0
0
1
1
1
0/1
…
…
…
…
…
…
…
…
…
日历/时钟寄存器 存放数据的寄存器,采用BCD码形式.
读写时序
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 #ifndef __DS1302_H__ #define __DS1302_H__ void DS1302_Init () ;unsigned char DS1302_Recv (unsigned char command) ;void DS1302_Send (unsigned char command, Data) ;void DS1302_Init_Time () ;void DS1302_Get_Time () ;void show () ;#endif #include <REGX52.H> #include "LCD1602.h" #define SEC_COM 0x80 #define MIN_COM 0x82 #define HOUR_COM 0x84 #define DAY_COM 0x86 #define MONTH_COM 0x88 #define YEAR_COM 0x8c #define WEEK_COM 0x8a #define WP_COM 0x8e sbit SCL = P3^6 ; sbit IO = P3^4 ; sbit CE = P3^5 ; unsigned char Write_Data[7 ] = {0x80 ,0x82 ,0x84 ,0x86 ,0x88 ,0x8c ,0x8a };unsigned char Read_Data[7 ] = {0x81 ,0x83 ,0x85 ,0x87 ,0x89 ,0x8d ,0x8b };extern unsigned char Init_Data[7 ] = {0x50 , 0x20 , 0x10 , 0x24 , 0x10 , 0x23 , 0x02 };void DS1302_Init () { SCL = 0 ; CE = 0 ; } unsigned char DS1302_Recv (unsigned char command) { unsigned char i, Data = 0x00 ; CE = 1 ; for (i=0 ; i<8 ; i++) { IO = command&(0x01 <<i); SCL = 0 ; SCL = 1 ; } IO = 0 ; for (i=0 ; i<8 ; i++) { SCL = 0 ; if (IO) {Data|=(0x01 <<i);} SCL = 1 ; } SCL = 0 ; CE = 0 ; return Data; } void DS1302_Send (unsigned char command, Data) { unsigned char i; CE = 1 ; for (i=0 ; i<8 ; i++) { IO = command&(0x01 <<i); SCL = 1 ; SCL = 0 ; } for (i=0 ; i<8 ; i++) { IO = Data&(0x01 <<i); SCL = 1 ; SCL = 0 ; } CE = 0 ; } void DS1302_Init_Time () { unsigned char i; DS1302_Send(WP_COM, 0x00 ); for (i=0 ; i<8 ; i++) { DS1302_Send(Write_Data[i],Init_Data[i]); } DS1302_Send(WP_COM, 0x80 ); } void DS1302_Get_Time () { unsigned char i; for (i=0 ; i<8 ; i++) { Init_Data[i] = DS1302_Recv(Read_Data[i]); } } void show () { LCD_ShowHexNum(2 ,3 ,Init_Data[2 ],2 ); LCD_ShowHexNum(2 ,7 ,Init_Data[1 ],2 ); LCD_ShowHexNum(2 ,11 ,Init_Data[0 ],2 ); } #include <REGX52.H> #include "LCD1602.h" #include "DS1302.h" void main () { LCD_Init(); LCD_ShowString(1 ,1 ,"HOUR:MIN:SEC" ); LCD_ShowString(2 ,1 ," : : " ); DS1302_Init(); DS1302_Init_Time(); while (1 ) { DS1302_Get_Time(); show(); } }
二十四. 红外遥控实验 简介 红光的波长范围为0.620.76μm,比红光波长场的叫红外线. 红外线遥控就是利用波长为0.761.5μm之间的近红外线来传送控制信号.
红外遥控是一种无线、非接触控制技术,具有抗干扰强,信心传输可靠,成本低,易实现.
红外发射装置由键盘电路,红外编码电路,电源电路和红外发射电路组成,主要元件为红外发光二极管.
通常红外遥控为了提高抗干扰性和降低电源消耗,常用载波的方式传送二进制编码,常用的载波频率为38KHz,这由发射端使用的455KHz晶振来决定的.
基本发送与接收
空闲状态:红外LED不亮,接收头输出高电平.
发送低电平:红外LED以38KHz频率闪烁发光,接收头输出低电平.
发送高电平:红外LED不亮,接收头输出高电平.
NEC编码协议
二进制脉冲码的形式有多种,常用的是NEC Protocol的PWM码(脉冲宽度调制)和Philips RC-5 Protocol的PPM码(脉冲位置调制码),我们使用的采用NEC协议.
即以一个START信号为始,随后跟的是DATA,DATA由地址码,地址反码,命令码,命令反码构成.
连发码:如果在一帧数据发送完毕后,红外遥控器仍没有放开,则发射连发码,可以通过统计连发码的次数来标记按键按下的长短和次数.
硬件电路
操作流程 由于红外接收头在没有脉冲的时候是高电平,当接收到脉冲的时候为低电平,所以可以通过外部中断的下降沿触发中断,在中断函数内通过计算高电平的时间来判断接收的数据是0还是1,是START还是REPEAT码.
时间计算 四个关键时间间隔,START(13.5ms),0(1120us),1(2250us),REPEAT(11.25ms).
所以START利用定时器接收13-14ms 之间的时间,REPEAT接收11-12 之间的时间,0接收1100-1200us之间的时间,1接收2200-2300之间的时间.
注意时间范围给小了的话,会导致接收出现问题,上面的01范围给小了,无法接收信号,后调整为0接收663-1400us 之间的时间,1接收1704-2441us 之间的时间即可.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 #ifndef __REDTIMER_H__ #define __REDTIMER_H__ void RTimer_Init () ;void RTimer_Run (unsigned char flag) ;void RTimer_Set (unsigned char xms) ;unsigned int RTimer_Get () ;#endif #include <REGX52.H> void RTimer_Init () { TMOD &= 0xf0 ; TMOD |= 0x01 ; TL0 = 0 ; TH0 = 0 ; TR0 = 0 ; TF0 = 0 ; } void RTimer_Run (unsigned char flag) { TR0 = flag; } void RTimer_Set (unsigned char xms) { TH0 = xms/256 ; TL0 = xms%256 ; } unsigned int RTimer_Get () { return (TH0<<8 )|TL0; } #ifndef __RED_H__ #define __RED_H__ void Red_Init () ;unsigned char Get_Address () ;unsigned char Get_Command () ;unsigned char Get_RepeateFlag () ;unsigned char Get_DataFlag () ;#endif #include <REGX52.H> #include "RedTimer.h" unsigned int time;unsigned char state;unsigned char Data[4 ]; unsigned char count; unsigned char RepeatFlag;unsigned char Address;unsigned char Command;unsigned char DataFlag;void Red_Init () { IT0 = 1 ; EX0 = 1 ; EA = 1 ; IE0 = 0 ; PX0 = 1 ; RTimer_Init(); } unsigned char Get_Address () { return Address; } unsigned char Get_Command () { return Command; } unsigned char Get_RepeateFlag () { return RepeatFlag; } unsigned char Get_DataFlag () { return DataFlag; } void Int0_Routine () interrupt 0{ if (state == 0 ) { RTimer_Set(0 ); RTimer_Run(1 ); state = 1 ; } else if (state == 1 ) { time = RTimer_Get(); if (time>11980 && time<12901 ) { RTimer_Set(0 ); state = 2 ; } else if (time>9369 && time<11058 ) { RTimer_Run(0 ); RepeatFlag = 1 ; state = 0 ; } else { RTimer_Run(0 ); state = 0 ; } } else if (state == 2 ) { time = RTimer_Get(); RTimer_Run(0 ); RTimer_Set(0 ); if (time>1704 && time<2441 ) { Data[count/8 ] |= 0x01 <<(count%8 ); count++; RTimer_Run(1 ); } else if (time>663 && time<1400 ) { Data[count/8 ] &= ~(0x01 <<(count%8 )); count++; RTimer_Run(1 ); } else { RTimer_Run(0 ); count = 0 ; state = 0 ; } if (count>=32 ) { count = 0 ; if (Data[0 ]==~Data[1 ] && Data[2 ]==~Data[3 ]) { DataFlag = 1 ; Address = Data[0 ]; Command = Data[2 ]; } RTimer_Run(0 ); state = 0 ; } } } #include <REGX52.H> #include "LCD1602.h" #include "Red.h" unsigned char Comand_Data;void main () { Red_Init(); LCD_Init(); LCD_ShowString(1 ,1 ,"RED" ); while (1 ) { if (Get_DataFlag() | Get_RepeateFlag()) { Comand_Data = Get_Command(); LCD_ShowHexNum(2 ,1 ,Comand_Data,2 ); } } }
二十五. ADC模数转换实验 ADC介绍 ADC(analog to digital converter)即模数转换器,指将一个模拟信号转变为数字信号. 主要指标如下:
分辨率: 指指AD/DA数字量的精细程度,通常用位数表示。例如,对于5V电源系统来说,8位的AD可将5V等分为256份,即数字量变化最小一个单位时,模拟量变化5V/256=0.01953125V,所以8位AD的电压分辨率0.01953125V,AD/DA的位数越高,分辨率就越高.
转换速度: 表示AD/DA的最大采样/建立频率,通常用转换频率或者转换时间来表示,对于采样/输出高速信号,应注意AD/DA的转换速度.
转换误差: 它表示A/D 转换器实际输出的数字量和理论上的输出数字量之间的差别.
ADC转换原理 XPT2046芯片 芯片管脚如下
X、Y、Z、VBAT、TEMP和AUX模拟信号经过片内的控制寄存器选择后进入ADC,ADC可配置为单端或差分模式. 作为触摸屏应用时,应该配置为差分模式,有效消除由于驱动开关的寄生电阻及外部的干扰带来的测量误差,提高转换精度. 单端和差分模式输入配置如下图所示:
工作时序 一次完整的转换需要24个串行同步时钟来完成. 前8个用来通过DIN引脚输入控制字节,随后16个字节为转换结果.
控制字各位如下:
7
6
5
4
3
2
1
0
S
A2
A1
A0
MODE
SER/DFR’
PD1
PD0
控制字各位描述如下:
位
名称
功能描述
7
S
开始位,为1表示1个新的控制字节到来,为0则忽略PIN引脚上数据
6-4
A2-A0
通道选择位,见表3、表4
3
MODE
12位/8位分辨率选择位,1为8位,0为12位
2
SER/DFR’
1为单端输入方式,0为差分输入方式
1-0
PD1-PD0
低功率模式选择位,00为低功率模式,11为器件总处于供电状态
硬件电路
P3.4-P3.7引脚接到该芯片的控制管脚上. XP接到电位器(可调节电阻/AD1),YP接到热敏传感器(NTC1),VBAT接到光敏传感器(GR1),AUX接到外部通道J52,供外部模拟信号检测
操作流程 直接根据时序读取芯片上经转换后得到的数字信号即可.
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #ifndef __XPT2046_H__ #define __XPT2046_H__ #define XP_COM 0x9C #define YP_COM 0xdc #define VBAT_COM 0xac #define AUX_COM 0xec unsigned char XPT2046_Recv (unsigned char Command) ;#endif #include <REGX52.H> sbit DIN = P3^4 ; sbit CS = P3^5 ; sbit CLK = P3^6 ; sbit DOUT = P3^7 ; unsigned char XPT2046_Recv (unsigned char Command) { unsigned char i, Data = 0x00 ; CS = 0 ; CLK = 0 ; for (i=0 ; i<8 ; i++) { DIN = Command&(0x80 >>i); CLK = 1 ; CLK = 0 ; } for (i=0 ; i<8 ; i++) { CLK = 1 ; CLK = 0 ; if (DOUT) {Data|=(0x80 >>i);} } for (i=0 ; i<8 ; i++) { CLK = 1 ; CLK = 0 ; } CS = 1 ; return Data; } #include <REGX52.H> #include "LCD1602.h" #include "XPT2046.h" unsigned char AD, NTC, GR;void main () { LCD_Init(); LCD_ShowString(1 ,1 ,"AD NTC GR " ); while (1 ) { AD = XPT2046_Recv(XP_COM); LCD_ShowNum(2 ,1 ,AD,3 ); NTC = XPT2046_Recv(YP_COM); LCD_ShowNum(2 ,5 ,NTC,3 ); GR = XPT2046_Recv(VBAT_COM); LCD_ShowNum(2 ,9 ,GR,3 ); } }
二十六. DAC数模转换实验 DAC介绍 DAC(Digital to analog converter)即数字模拟转换器. 主要指标如下:
分辨率: 和ADC的分辨率同理.
线性度: 线性度(也称非线性误差)是实际转换特性曲线与理想直线特性之间的最大偏差.
绝对精度和相对精度: 绝对精度是指在整个刻度范围内,任一输入数码所对应的模拟量实际输出值与理论值之间的最大误差;相对精度与绝对精度表示同一含义,用最大误差相对于满刻度的百分比表示.
建立时间: 指输入的数字量发生满刻度变化时,输出模拟信号达到满刻度值的+-1/2LSB所需的时间.
DAC工作原理 T型电阻网络DAC
由数字寄存器,模拟电子开关,位权网络,求和运算放大器和基准电压组成.
大概原理就是,各位电子开关根据输入的数据的二进制每一位决定是否开启或关闭,由此把基准电压分压引入输出电路中.
PWM模拟输出
PWM(Pulse Width Modulation)即脉冲宽度调制,在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常用于电机控速,开关电源等领域,重要参数如下:
频率 = 1/Ts
占空比 = Ton/Ts
精度 = 占空比变化步距
拿电机举个例子,电机启动正负极接通即可,如果想要控制它的转动速度,可以在一段时间内,比如100的时间单位内,接通40s,关闭60s,由于惯性的存在,他仍然会转动,不过会以一个稍慢的速度转动,40/100即它的占空比,PWM的原理大概就是这样.
PWM型DA转换器
关键地方只有PWM输入,其他电路好像是放大电压啥的. 输出电压 V0 = (PWM占空比)×VH
开发板上的DA电路如下
代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #ifndef __DAPWM__ #define __DAPWM_H__ void Timer_Init () ;void Set_Pwm (unsigned char x) ;#endif #include <REGX52.H> sbit PWM = P2^1 ; unsigned char Scale;void Timer_Init () { TMOD &= 0xF0 ; TMOD |= 0x01 ; TL0 = 0x9C ; TH0 = 0xFF ; TF0 = 0 ; TR0 = 1 ; EA = 1 ; ET0 = 1 ; } void Set_Pwm (unsigned char x) { Scale = x; } void Timer_Routine () interrupt 1{ static unsigned char count = 0 ; TL0 = 0x9C ; TH0 = 0xFF ; count++; count%=100 ; if (count<Scale) { PWM = 1 ; } else { PWM = 0 ; } } #include <REGX52.H> #include "Delay.h" #include "DAPWM.h" void main () { unsigned char i; P2_4 = 0 ; Timer_Init(); while (1 ) { for (i=0 ; i<100 ; i++) { Set_Pwm(i); Delay(1 ); } for (i=100 ; i>0 ; i--) { Set_Pwm(i); Delay(1 ); } } }
二十七. LCD1602液晶显示实验 LCD1602介绍 它是一种专门用来显示字母、数字、符号的点阵型液晶模块. 它能显示2行字符信息,每行又能显示16个字符. 实物如下,有16个引脚.
硬件电路如下
引脚
功能
VSS
地
VDD
电源正极(4.5~5.5v)
VO
对比度调节电压
RS
数据/指令选择,1为数据,0为指令
RW
读/写选择,1为读,0为写
E
使能,1为数据有效,下降沿执行命令
D0~D7
数据输入/输出
A
背光灯电源正极
K
背光灯电源负极
DDRAM LCD1602内部含有80个字节的DDRAM,它是用来寄存显示字符的. 地址和屏幕的对应关系如下表:
也就是说虽然LCD1602每行只显示16个字符,但实际上每行能存储40个字符.
LCD1602常用指令 1) 清屏指令
功能:
清除液晶显示器,即将DDRAM的内容全部填入”空白”的ASCII码20H.
光标归位,即将光标撤回液晶显示器的左上方.
将地址计数器的值设为0.
2) 模式设置指令
功能:
设定每次写入1位数据后光标的移位方向,并且设定每次写入的一个字符是否移动.
I/D:0为写入新数据后光标左移,1为写入新数据后光标右移.
S:0为写入新数据后显示屏不移动,1为写入新数据后显示屏整体右移1个字符.
3) 显示开关控制指令
功能:
控制显示器开/关、光标显示/关闭以及光标是否闪烁.
D:0为显示功能关,1为显示功能开.
C:0为无光标,1为有光标.
B:0为光标闪烁,1为光标不闪烁.
4) 功能设定指令
功能:
设定数据总线位数、显示的行数及字符.
DL:0为数据总线为4位,1为数据总线为8位.
N:0为显示1行,1为显示2行.
F:0为5×7点阵/每字符,1为5×10点阵/每字符.
LCD1602使用 代码部分 e.g.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 #ifndef __LCD_H__ #define __LCD_H__ void LCD1602_Init () ;void ShowChar (unsigned char row, col, Data) ;void ShowString (unsigned char row, col, char * Data) ;void ShowNum (unsigned char row, col, unsigned int num, unsigned char length) ;#endif #include <REGX52.H> #define IO P0 sbit RW = P2^5 ; sbit RS = P2^6 ; sbit EN = P2^7 ; void delay () { unsigned char i, j; i = 2 ; j = 239 ; do { while (--j); } while (--i); } void Send_Command (unsigned char Command) { RS = 0 ; RW = 0 ; IO = Command; EN = 1 ; delay(); EN = 0 ; delay(); } void Send_Data (unsigned char Data) { RS = 1 ; RW = 0 ; IO = Data; EN = 1 ; delay(); EN = 0 ; delay(); } void Set_Cursor (unsigned char row, unsigned char col) { unsigned char i = col-1 ; if (row == 1 ) { Send_Command(0x80 |(0x00 +i)); } else if (row == 2 ) { Send_Command(0x80 |(0x40 +i)); } } void LCD1602_Init () { Send_Command(0x38 ); Send_Command(0x0c ); Send_Command(0x06 ); Send_Command(0x01 ); } void ShowChar (unsigned char row, col, Data) { Set_Cursor(row, col); Send_Data(Data); } void ShowString (unsigned char row, col, char * Data) { unsigned char i; Set_Cursor(row, col); for (i=0 ; *(Data+i)!='\0' ; i++) { Send_Data(*(Data+i)); } } void ShowNum (unsigned char row, col, unsigned int num, unsigned char length) { unsigned char Data[16 ]; unsigned char i=0 ; Set_Cursor(row, col); while (num) { Data[i++] = num%10 ; num/=10 ; } i = i-length; while (i--) { Send_Data(Data[i]+'0' ); } } #include <REGX52.H> #include "LCD.h" void main () { LCD1602_Init(); ShowNum(1 ,1 ,1234 ,2 ); while (1 ) { } }