嵌入式【51单片机】基于普中科技HC6800-EM3 V3.0开发板的51单片机学习记录
LiQiWen普中科技HC6800-EM3
理论知识
周期
在51单片机中存在时钟周期、状态周期、机器周期、指令周期
- 时钟周期: 时钟周期也称振荡周期或晶振周期,即晶振在单位时间发出的脉冲数,在一般情况下使用外部晶振源,在本套单片机中提供了11.0592MHz、12MHz、24MHz三个晶振可供选择。
- 状态周期: 在8051单片机中将两个时钟周期定义为一个状态周期。
- 机器周期: 在计算机中往往将一条指令的执行过程划分成多个阶段,其中完成一个阶段的操作所需的时间成为机器周期,在8051系列单片机中一个机器周期 = 12个时钟周期。
- 指令周期: 完成一条指令所需的时间。在STC单片机下载程序中有12T和6T的选择,在12T模式下指令周期 = 12个时钟周期,在6T模式下指令周期 = 6个时钟周期。此选项会影响串口的波特率。
中断
中断简单的来说就是在主程序执行过程中突然有人叫CPU紧急去做另外一件事。

在基本型8051单片机中存在5个中断源分别是外部中断:INT0、INT1和定时计数器中断:T0、T1以及串口中断。
其中断的启用与否由内部特殊寄存器IE控制
| IE位(中断使能寄存器) |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
| 字节地址:A8H |
EA |
- |
- |
ES |
ET1 |
EX1 |
ET0 |
EX0 |
EX0: 外部中断0使能,EX0 = 1,允许外部中断0中断,EX0 = 0,禁止外部中断0中断。
ET0: T0溢出中断使能,ET0 = 1,允许T0中断,ET0 = 0,禁止T0中断。
EX1: 外部中断1使能,EX1 = 1,允许外部中断1中断,EX1 = 0,禁止外部中断1中断。
ET1: T1溢出中断使能,ET1 = 1 ,允许T1中断,ET1 = 0,禁止T1中断。
E S: 串行中断使能,ES = 1,允许串行口中断,ES = 0 ,禁止串行口中断。
E A: 中断允使能,EA = 1,允许CPU处理中断请求,EA = 0,禁止CPU处理中断请求。
| IP位(优先级寄存器) |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
| - |
- |
- |
- |
PS |
PT1 |
PX1 |
PT0 |
PX0 |
- P S: 串口优先级切换使能,PS = 1,将串口中断优先级设为高级,PS = 0,将串口中断优先级设为低级。
- PT1: T1优先级切换使能,PT1 = 1,将T1中断优先级设为高级,PT1 = 0,将T1中断优先级设为低级。
- PX1: 外部中断1优先级切换使能,PX1 = 1,将外部中断1中断优先级设为高级,PX1 = 0,将外部中断1中断优先级设为低级。
- PT0: T0优先级切换使能,PT0 = 1,将T0中断优先级设为高级,PT0 = 0,将T0中断优先级设为低级。
- PX0: 外部中断0优先级切换使能,PX0 = 1,将外部中断0中断优先级设为高级,PX0 = 0,将外部中断0中断优先级设为低级。
自然优先级:当未设置中断优先级(IP)时,程序处理中断按照自然优先级进行排序,自然优先级为 外部中断0 > T0 > 外部中断1 > T1 > 串口中断。当设置中断优先级(IP)时,按照高级/低级中存在的中断源再次按自然优先级进行。

| TCON位(中断模式寄存器) |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
| 字节地址:88H |
TF1 |
TR1 |
TF0 |
TR0 |
IE1 |
IT1 |
IE0 |
IT0 |
| 所属模块 |
T1 |
T1 |
T0 |
T0 |
中断 |
中断 |
中断 |
中断 |
- IT0: 外部中断0触发模式控制位,IT0 = 1,外部中断0为下降沿触发,IT0 = 0,外部中断0为低电平触发。
- IE0: 外部中断0中断请求标志位,当外部中断0发出中断请求时,IE0置1。
- IT1: 外部中断1触发模式控制位,IT1 = 1,外部中断1为下降沿触发,IT1 = 1,外部中断1为低电平触发。
- IE1: 外部中断1中断请求标志位,当外部中断1发出中断请求时,IE1置1。
- TR0: 定时\计数器T0使能位,TR0 = 1,T0工作,TR0 = 0,T0不工作。
- TF0: 定时\计数器T0溢出标志位,T0溢出时,TF0置1,并发出中断请求。
- TR1: 定时\计数器T1使能位,TR1 = 1,T1工作,TR1 = 0,T1不工作。
- TF1: 定时\计数器T1溢出标志位,T1溢出时,TF1置1,并发出中断请求。
中断号
| 中断源符号 |
名称 |
中断原因 |
中断号 |
| INT0 |
外部中断0 |
P3^2引脚低电平或下降沿信号 |
0 |
| T0 |
定时器0中断 |
定时/计数器0计数回0溢出 |
1 |
| INT1 |
外部中断1 |
P3^3引脚低电平或下降沿信号 |
2 |
| T1 |
定时器1中断 |
定时/计数器1计数回0溢出 |
3 |
| TI/RI |
串行口中断 |
串行通讯完成一帧数据发送或接收引起中断 |
4 |
定时\计数器
在中断篇幅中有一个TCON寄存器其中高4位为定时/计数器部分。
| TMOD(定计器模式寄存器) |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
| 字节地址:89H |
GATE |
C/ T |
M1 |
M0 |
GATE |
C/T |
M1 |
M0 |

串口

相关寄存器:
| SCON(串口控制寄存器) |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
| 字节地址:98H |
SM0/FE |
SM1 |
SM2 |
REN |
TB8 |
RB8 |
T1 |
R1 |
SM2: 允许方式2或方式3多机通讯控制位。
REN: 允许/禁止串行接受控制位,REN = 1 ,串行接受状态,串行接收器RxD启用,开始接收信息,REN = 0 ,串行禁止状态,停止接收数据。
TB8: 在方式2/方式3,他是要发送的第9位数据。
TR8: 在方式2/方式3,他是接收到的地9位数据。
TI: 发送中断请求标志位,当串行数据发送结束时,硬件自动置为,即TI = 1,响应中断后必须软件复位。
RI: 接收中断请求标志位,当串行数据接收结束时,硬件自动置为,即RI = 1,响应中断后必须软件复位。
| SM0 |
SM1 |
工作方式 |
功能说明 |
波特率 |
| 0 |
0 |
方式0 |
同步移位寄存器方式,8 位数据。数据通过 RxD(输入)和 TxD(输出)移位,类似 SPI |
波特率 = fosc / 12 |
| 0 |
1 |
方式1 |
10 位异步串行通信:1 起始位、8 数据位、1 停止位。 |
波特率可变,通常由定时器 1 决定(常用于标准 UART 通讯) |
| 1 |
0 |
方式2 |
11 位异步串行通信:1 起始位、9 数据位(包括可编程的第 9 位)、1 停止位。 |
波特率为固定的 fosc / 64 或 fosc / 32(SMOD=1 时) |
| 1 |
1 |
方式3 |
11 位异步串行通信,跟模式 2 类似。 |
区别是波特率可变,通常由定时器 1 决定。 |
| PCON(电源控制寄存器) |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
| 字节地址:87H |
SMOD |
SMOD0 |
- |
POF |
GF1 |
GF0 |
PD |
IDL |
- SMOD: 波特率选择位,SOMD = 1 ,方式1、2、3的波特率加倍,SMOD = 0,波特率不加倍。
- SMOD0: 帧错误检测有效控制位,SMOD0 = 1,SCON寄存器中SM0/EF位用于EF(帧错误检测)功能,SMOD0 = 0,SCON寄存器中SM0/EF位用于SM0。
51单片机串口通讯
1 2 3 4 5 6 7 8 9 10 11
| void UsartInit() { SCON=0X50; TMOD=0X20; PCON=0X80; TH1=0XF3; TL1=0XF3; ES=1; EA=1; TR1=1; }
|
1 2 3 4 5 6 7 8 9
| void Usart() interrupt 4 { u8 receiveData; receiveData=SBUF; RI = 0; SBUF=receiveData; while(!TI); TI=0; }
|
}
SPI协议
在标准SPI (Serial Peripheral interface) 通讯中,往往使用4根数据线进行通讯,其分别为SSEL(片选信号)、SCKL(时钟信号)、MOSI(主机to从机数据信号)、MISO( 从机to主机数据信号)。
别称
SSEL:NSS、CE、CS
SCKL:SCK
MOSI:SOMI、DIN、DI、SDI
MISO:SIMO、DOUT、DO、SDO
使用51单片机模拟SPI通讯
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
| void SPI_write(u8 dat){ u8 i; CLK = 0; for(i = 0 ; i < 8 ; i ++){ DIN = dat & 0x80 ? 1 : 0 ; CLK = 0; CLK = 1; dat <<= 1; } }
u16 SPI_read(){ u8 j; u16 dat_read = 0x0000; CLK = 0; for(j = 0 ; j < 12 ; j ++){ dat_read <<= 1; CLK = 1 ; CLK = 0; dat_read |= DOUT; } return dat_read; }
u16 SPI_read_value(u8 a){ u8 i = 0; u16 value = 0 ; CS = 0; CLK = 0; SPI_write(a); for(i=6; i>0; i--); CLK = 1; _npo_(); CLK = 0; value = SPI_read(); CS = 1; return value; }
|
实验1 :关闭和开启一个LED灯
编程
引用51单片机特殊寄存器
1 2
| sbit led1 = P0^0; sbit led2 = P0^1;
|
定义引脚P0.0
令P0.0引脚为低电平,P0.1引脚为高电平,开发板单片机引脚有上拉电阻。
1 2 3 4 5 6 7 8 9 10 11
| #include "reg52.h" sbit led1=P0^0; sbit led2=P0^1; void main() { while(1) { led1=0; led2=1; } }
|
while(1) 使单片机循环持续执行,在希望程序一直运行应该加上。
实验
将开发板的 JP10.1和 SIP8.1、JP10.2和 SIP8.2进行跳线连接,将 J21的1、2叫使用跳线帽进行短接。
编译下载程序。
发现D18关闭,D17开启。
实验2:LED灯闪烁
编程
1
| typedef unsigned int u16;
|
将u16定义为无符号整数类型。
1 2 3 4
| void delay(u16 i) { while(i--); }
|
定义了一个延时函数,通过单片机执行代码的延迟进行延时,在当前平台下 while 每次循环耗时约10u。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include "reg52.h" typedef unsigned int u16; sbit led=P0^0; void delay(u16 i) { while(i--); } void main() { while(1) { led=0; delay(50000); led=1; delay(50000); } }
|
delay(50000) 约能提供450ms的延时。
实验
将开发板的 JP10.1和 SIP8.1进行跳线连接,将 J21的1、2叫使用跳线帽进行短接。
编译下载程序。
发现D18闪烁。
实验3:LDE流水灯
编程
[intrins.h](函数库 | LiQiWen) 是特定于 Keil C51 的扩展库,无法跨平台使用。
定义led为P0.0-P0.7引脚。
0x01为16进制数据其转换为二进制数为0b00000001,相应的也是就是使P0.0为高电平,P0.1-P0.7为低电平。
将变量led左移一位。
将变量led右移一位。
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
| #include "reg52.h" #include<intrins.h> typedef unsigned int u16; typedef unsigned char u8; #define led P0 void delay(u16 i) { while(i--); } void main() { u8 i; led=0x01; delay(50000); while(1) { for(i=0;i<7;i++) { led=_crol_(led,1); delay(50000); } for(i=0;i<7;i++) { led=_cror_(led,1); delay(50000); } } }
|
实验
实验4:蜂鸣器
编程
定义引脚P1.5。
将beep变量进行取反。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include "reg52.h" #include<intrins.h> typedef unsigned int u16; typedef unsigned char u8; sbit beep=P1^5; void delay(u16 i) { while(i--); } void main() { while(1) { beep=~beep; delay(10); } }
|
实验
实验5:继电器
编程
定义引脚P1.4。
当while(1);没有包裹函数单独放在主函数后面时,当单片机运行到此处时,会进入死循环,可以实现函数只执行一次的效果。
1 2 3 4 5 6 7 8 9
| #include "reg52.h" typedef unsigned int u16; typedef unsigned char u8; sbit relay=P1^4; void main() { relay=0; while(1); }
|
实验6:多个数码管0-9循环
编程
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
| #include "reg52.h" typedef unsigned int u16; typedef unsigned char u8; #define LED P1 sbit L_A = P0^2 ; sbit L_B = P0^1 ; sbit L_C = P0^0 ;
int NUM[10] = {0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x3F}; void dealy(){ int d_i = 105000; while(d_i){ d_i --; } } void main() { while(1){ int i = 0; int j = 0; for(j = 0 ; j < 8 ; j ++){ switch(j){ case 0: L_A = 0 ; L_B = 0 ; L_C = 0 ; break ; case 1: L_A = 0 ; L_B = 0 ; L_C = 1 ; break ; case 2: L_A = 0 ; L_B = 1 ; L_C = 0 ; break ; case 3: L_A = 0 ; L_B = 1 ; L_C = 1 ; break ; case 4: L_A = 1 ; L_B = 0 ; L_C = 0 ; break ; case 5: L_A = 1 ; L_B = 0 ; L_C = 1 ; break ; case 6: L_A = 1 ; L_B = 1 ; L_C = 0 ; break ; case 7: L_A = 1 ; L_B = 1 ; L_C = 1 ; break ; } for(i = 0 ; i < 10 ; i ++){ P1 = NUM[i]; dealy(); }; } }; }
|
通过控制单片机P1通道使其输出0-9对应的二进制值,并通过实验箱内的138译码器实现不同数码管之间的循环。
实验
将J12和JP8进行跳线连接,将P0.0-P0.2和138译码器区域内的ABC进行连接。
下载程序。

实验7:点阵屏显示图案(X 逐行扫描,Y IO直出)
本实验实现了在点阵屏上输出一个图案,但是因为点阵屏的Y轴直接连接单片机IO,故造成单片机IO大量占用,并非最优解。
编程
写一个小程序将图片转换为一个16进制二维数组
小程序使用python编写。
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
| from PIL import Image
def process_image_to_decimal(image_path, output_path): image = Image.open(image_path).convert('RGB') with open(output_path, "w", encoding="utf-8") as file: for y in range(16): binary_arr = [0] * 16 for x in range(16): r, g, b = image.getpixel((x, y)) if r == 0 and g == 0 and b == 0: binary_arr[x] = 1 else: binary_arr[x] = 0 first_byte = binary_arr[:8] second_byte = binary_arr[8:] decimal1 = int("".join(map(str, first_byte)), 2) decimal2 = int("".join(map(str, second_byte)), 2) hex1 = f"{decimal1:02X}" hex2 = f"{decimal2:02X}" file.write(f"{{0x{hex1},0x{hex2}}},\n")
if __name__ == "__main__": input_image = r"C:\Users\22979\Desktop\img.png" output_file = r"C:\Users\22979\Desktop\example.txt" try: process_image_to_decimal(input_image, output_file) print("文件转换完成") except Exception as e: print(f"错误发生: {str(e)}")
|
将生成文件中的数组,粘贴到 unsigned char code SER_XIN[16][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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include "reg52.h"
#define J17 P1 #define J18 P0
const unsigned char code SER_XIN[16][2] = { {0x10,0x08}, {0x38,0x1C}, {0x7C,0x3E}, {0xEE,0x7F}, {0xEF,0xF7}, {0xEF,0xF7}, {0xE0,0x07}, {0xEF,0xE7}, {0x6F,0xEE}, {0x3F,0xDC}, {0x1F,0xF8}, {0x0F,0xF0}, {0x07,0xE0}, {0x03,0xC0}, {0x01,0x80}, {0x00,0x00} };
sbit SER = P3^4 ; sbit RCK = P3^5 ; sbit SCK = P3^6 ;
int m_i = 0;
void VGA(int a){ J18 = SER_XIN[a][0]; J17 = SER_XIN[a][1]; }
void main(){ SER = 0; RCK = 0; SCK = 0; while(1){ for(m_i = 0 ; m_i < 32 ; m_i ++){ if((m_i%32) == 0 || (m_i%32) == 31){ SER = 0; }else{ SER = 1; }
if((m_i%2) == 0){ VGA(m_i/2); SCK = 1; RCK = 0; }else{ SCK = 0; RCK = 1; } } } }
|
实验
将J18和P0、J17和P1相连,将JP595使用跳线帽短接,下载程序
观察到点阵屏出现图案
实验8:点阵屏显示图案(X 逐行扫描,XY 595输出)
本实验和试验7相同均是实现点阵屏图案显示,但实验8仅使用3个IO,相比实验7使用了11个IO可以算是极其节省了。
编程
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
| #include "reg51.h" #include <intrins.h>
sbit SER = P3^4 ; sbit RCK = P3^5 ; sbit SCK = P3^6 ;
char move = 0 ;
short int list[16] = { {0xAAAA}, {0xCCCC}, {0xF0F0}, {0xFF00}, {0xDB6C}, {0xE38E}, {0x5B55}, {0x8800}, {0x4894}, {0x3E3C}, {0x4F72}, {0x05A6}, {0x31C0}, {0x473C}, {0x7CDE}, {0xC400}, };
short int row[16] = { {0xFFFE}, {0xFFFD}, {0xFFFB}, {0xFFF7}, {0xFFEF}, {0xFFDF}, {0xFFBF}, {0xFF7F}, {0xFEFF}, {0xFDFF}, {0xFBFF}, {0xF7FF}, {0xEFFF}, {0xDFFF}, {0xBFFF}, {0x7FFF}, };
void Serial_OUT(int dat){ int j = 0; for( ; j < 16 ; j ++){ SCK = 0; SER = dat & 0x8000 ? 1 : 0; SCK = 1; dat <<= 1; }
}
void VGA(short int Y[16] , short int X[16] ){ for(move = 0 ; move < 16 ; move ++){ Serial_OUT(X[move]); Serial_OUT(Y[move]); RCK = 0; RCK = 1; } }
void main(){ int move = 0; SER = 0; RCK = 0; SCK = 0; while(1){ VGA(list,row); } }
|
实验
本次实验完全不需要使用跳线,仅需将JP595使用跳线帽进行短接即可。
下载程序。
实验9:ADC模数转换
本开发板使用XPT2046,其使用了SPI进行数据通讯,并配有4路12bitADC电路。
编程
此次程序设计使用了多文件工程。
xpt2046的通讯程序
首先定义头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #ifndef __xpt2046_H__ #define __xpt2046_H__
#include "reg51.h"
typedef unsigned char u8; typedef unsigned short int u16; typedef unsigned int u32;
sbit DOUT = P3^7; sbit DIN = P3^6; sbit CS = P3^5; sbit CLK = P3^4;
void xpt2046_delay(u16 sleep); void xpt2046_write(u8 dat); u16 xpt2046_read(void); u16 xpt2046_read_adc(u8 a);
#endif
|
其次编写xpt2046的通讯函数。
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
| #include "xpt2046.h"
void xpt2046_delay(u16 sleep){ u16 x,y; for(x = sleep ; x > 0 ; x --){ for(y = 18 ; y > 0 ; y --){
} } }
void xpt2046_write(u8 dat){ u8 i; CLK = 0; for(i = 0 ; i < 8 ; i ++){ DIN = dat & 0x80 ? 1 : 0 ; CLK = 0; CLK = 1; dat <<= 1; } }
u16 xpt2046_read(){ u8 j; u16 dat_read = 0x0000; CLK = 0; for(j = 0 ; j < 12 ; j ++){ dat_read <<= 1; CLK = 1 ; CLK = 0; dat_read |= DOUT; } return dat_read; }
u16 xpt2046_read_adc(u8 a){ u16 adc = 0 ; CS = 0; CLK = 0; xpt2046_write(a); xpt2046_delay(1); CLK = 1; xpt2046_delay(1); CLK = 0; adc = xpt2046_read(); CS = 1; return adc; }
|
最后编写主函数。
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 "reg51.h" #include "xpt2046.h"
sbit hc138_a = P1^0; sbit hc138_b = P1^1; sbit hc138_c = P1^2;
typedef unsigned int uint; typedef unsigned char uchar;
#define LED P0
u8 NUM[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void GMS_display(u8 a[] , u8 sum){ u8 GMS_i = 0; for(GMS_i = 0 ; GMS_i < 4 ; GMS_i ++){ switch(GMS_i){ case 0:hc138_a = 0 ; hc138_b = 0 ; hc138_c = 0; break; case 1:hc138_a = 0 ; hc138_b = 0 ; hc138_c = 1; break; case 2:hc138_a = 0 ; hc138_b = 1 ; hc138_c = 0; break; case 3:hc138_a = 0 ; hc138_b = 1 ; hc138_c = 1; break; } LED = NUM[a[GMS_i]]; xpt2046_delay(100); LED = 0x00; } } void main(){ u16 adc = 0; u8 i = 0; u8 sum[4]; while (1) { if(i == 20){ adc = xpt2046_read_adc(0xD4); i = 0; } i ++; sum[0] = adc / 1000; sum[1] = adc % 1000 /100; sum[2] = adc % 1000 %100 /10; sum[3] = adc % 1000 %100 %10; GMS_display(sum,4); } }
|
实验
将单片机P1.0-P1.2使用跳线连接至74HC138的ABC端口,将P0口与J12使用跳线连接,将P3.4-P3.7与AD/DA区域内的CLK、CS、DIN、DOUT使用跳线进进行连接。
下载程序。

实验10:ADC读取可调电阻电压并使用LCD显示电压,并使用串口向上位机传输电压书记
编程
XPT2046的程序在实验9已经编写过,可以直接使用,在此处不再编写。
定义LCD头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #ifndef __LCD_H__ #define __LCD_H__
#include "reg51.h"
#define BD P0
sbit RS = P2^6; sbit RW = P2^5; sbit EN = P2^7;
void lcd_dealy(u16 sleep); u8 lcd_read(); u8 lcd_busy(); void lcd_write_cmd(u8 cmd); void lcd_write_data(u8 dat); void lcd_inti(); void lcd_display(u8 dat[],u8 sum);
#endif
|
编写LCD函数
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
| #include "LCD.h"
void lcd_dealy(u16 sleep){ u16 x,y; for(x = sleep ; x > 0 ; x --){ for(y = 18 ; y > 0 ; y --){ } } }
u8 lcd_read(){ u8 dat = 0x00; RS = 0; RW = 1; EN = 0; lcd_dealy(1); EN = 1; dat = BD; return dat; }
u8 lcd_busy(){ u8 temp = 0x01 & lcd_read(); return temp; }
void lcd_write_cmd(u8 cmd){ RS = 0; RW = 0; EN = 0; BD = cmd; lcd_dealy(2); EN = 1; lcd_dealy(2); EN = 0; }
void lcd_write_data(u8 dat){ RS = 1; RW = 0; EN = 0; BD = dat; lcd_dealy(2); EN = 1; lcd_dealy(5); EN = 0; }
void lcd_inti(){ lcd_write_cmd(0x38); lcd_write_cmd(0x0c); lcd_write_cmd(0x60); lcd_write_cmd(0x01); }
void lcd_display(u8 dat[],u8 sum){ u8 i; for(i = 0 ; i < sum ; i ++){ lcd_write_cmd(0x80+i); lcd_write_data(dat[i]); } }
|
编写主函数同样可以在实验9的主函数上略加修改即可
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
| #include "reg51.h" #include "xpt2046.h" #include "LCD.h"
sbit hc138_a = P1^0; sbit hc138_b = P1^1; sbit hc138_c = P1^2;
void UsartInit(){ SCON = 0X50; TMOD &= 0X0F; TMOD |= 0X20; PCON &= 0X7F; TH1 = 0XF3; TL1 = 0XF3; TR1 = 1; }
void Usart_white(u8 dat[],u8 sum){ u8 i; for(i = 0 ; i < sum ; i ++){ SBUF = dat[i]; while(!TI); TI = 0; xpt2046_delay(10); } xpt2046_delay(200); }
void main(){ u16 adc = 0; u8 i = 0; u8 sum[4]; u8 HEX_H[6]; UsartInit(); lcd_inti(); while (1) { if(i == 20){ adc = xpt2046_read_adc(0x94); i = 0; adc = adc*12/10; sum[0] = adc / 1000; sum[1] = adc % 1000 /100; sum[2] = adc % 1000 %100 /10; sum[3] = adc % 1000 %100 %10; } i ++; HEX_H[0] = '0' + sum[0] ; HEX_H[1] = '.' ; HEX_H[2] = '0' + sum[1] ; HEX_H[3] = '0' + sum[2] ; HEX_H[4] = '0' + sum[3] ; HEX_H[5] = 'V' ; lcd_display(HEX_H,6); Usart_white(HEX_H,6); xpt2046_delay(100); }
}
|
实验
将P3.4-P3.7与AD/DA区域内的CLK、CS、DIN、DOUT使用跳线进进行连接;将LCD1602液晶屏幕,插入J1602。
下载程序。


使用node-red写一个小程序,将单片机获取到的电压数据使用图形化的方式显示出来。

实验11:使用定时器中断产生PWM波形
编程
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 "reg51.h"
sbit pwm = P1^0;
u16 i = 0;
void Timer0Init(void) { TCON |= 0X10; TMOD &= 0xF0; TMOD |= 0x02; TL0 = 0xFE; TH0 = 0xFE; TF0 = 0; TR0 = 1; EA = 1; ET0 = 1; }
void User() interrupt 1{ i = i < 100 ? i : 0 ; pwm = i < 42 ? 1 : 0; i ++; }
void main(){ Timer0Init(); while (1) { } }
|
实验
下载程序,使用示波器测试P1.0口输出波形。

观察到占空比为42%的波形。