单片机大作业

2022-6-12 单片机LCDDS18B20串口通信proteus

这学期学了单片机这门课,收获颇丰。相对于所学的其它课程来说,这门课“硬件”了很多,但学起来也相当有趣,在这里记录下大作业的一道题。题目如下:

使用proteus仿真,单片机甲采集ds18b20温度传感器的温度值,利用LCD显示该值。通过串口将温度值传给单片机乙,利用LCD显示该值。LCD都采用两行显示,第一行显示内容为"temperature:",第二行显示温度值,温度值显示的具体位数自行定义。


# 分析

这个大作业有3个功能需要实现:

  • 采集ds18b20温度传感器的温度值
  • 利用LCD显示温度值
  • 通过串口通信,将温度值发送给另一个单片机

在这里甲机需要完成3件事

  1. 采集DS18B20温度传感器的温度值
  2. 利用LCD显示温度值
  3. 通过串口将温度值传给乙机

而乙机则需要完成2件事

  1. 通过串口接收甲机传来的温度值
  2. 利用LCD显示温度值

# 仿真图

# 甲机

# 乙机


# 编程

# DS18B20相关函数

DS18B20_GetTemp()函数中,返回值是float类型,同时可以返回负数。

// 初始化
unsigned char DS18B20_INIT() {
	unsigned char presencePulse;
	DQ = 0;
	delay_us(240);
	DQ = 1;
	delay_us(30);
	presencePulse = DQ;
	delay_us(250);
	return presencePulse;
}

// 写指令
void DS18B20_WriteCmd(unsigned char Rbyte) {
	unsigned char i, k=0;
	for (i=0; i<8; i++) {
		k = Rbyte & 0x01;
		if (k == 1) {
			DQ = 0;
			delay_us(4);
			DQ = 1;
			delay_us(20);
			DQ = 1;
		} else {
			DQ = 0;
			delay_us(30);
			DQ = 1;
		}
		Rbyte = Rbyte >> 1;
	}
}

// 读数据
unsigned char DS18B20_ReadData() {
	unsigned char i, Data=0;
	for (i=0; i<8; i++) {
		DQ = 0;
		Data = Data >> 1;
		DQ = 1;
		if (DQ) {
			Data = Data | 0x80;
		}
		delay_us(28);
		DQ = 1;
		_nop_();
	}
	return Data;
}

// 获取温度
float DS18B20_GetTemp() {
	unsigned char valueLow = 0;
	unsigned char valueHigh = 0;
	short int temp = 0;
	DS18B20_INIT();
	DS18B20_WriteCmd(0xcc);
	DS18B20_WriteCmd(0x44);
	delay_750ms();
	DS18B20_INIT();
	DS18B20_WriteCmd(0xcc);
	DS18B20_WriteCmd(0xbe);
	valueLow = DS18B20_ReadData();
	valueHigh = DS18B20_ReadData();

	temp = valueHigh;
	temp = temp<<8;
	temp = temp|valueLow;
	return 0.0625*temp;
}
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

# LCD相关函数

LCD_INIT()函数中,对LCD分别做如下初始化:

  • 工作方式:8位数据位,2行显示,5*7点阵

  • 输入方式:光标右移,整个屏幕不移动

  • 光标设置:显示光标,但不闪烁

  • 显示设置:在第1行第1个位置开始显示

LCD_Float2CharArray()函数中,会对float类型的温度值转换为字符数组进行存储,以\0作为结束符

LCD_Display()函数中,会在LCD的第2行显示温度值,其中0xdfC是****

// 初始化
void LCD_INIT() {
	LCD_WriteCmd(0x38);
	LCD_WriteCmd(0x01);
	LCD_WriteCmd(0x06);
	LCD_WriteCmd(0x0e);
	LCD_WriteCmd(0x80);
}

// 读状态
unsigned char LCD_ReadState() {
	unsigned char s;
	RW=1; RS=0;
	E=1; s=P0; E=0;
	return s;
}

// 写指令
void LCD_WriteCmd(unsigned char cmd) {
	unsigned char i;
	do {
		i = LCD_ReadState();
		i &= 0x80;
	} while(i!=0);

	RW=0; RS=0;
	E=1; P0=cmd; E=0;
	delay(200);
}

// 写数据
void LCD_WriteData(unsigned char info) {
	unsigned char i;
	do {
		i = LCD_ReadState();
		i &= 0x80;
	} while(i!=0);

	RW=0; RS=1;
	E=1; P0=info; E=0;
	delay(200);
}

// 温度:float转为字符数组
void LCD_Float2CharArray(float tempp) {
	if (tempp >= 0) {
		temp[0] = '+';
	} else {
		temp[0] = '-';
		tempp = tempp * (-1);
	}
	temp[1] = ' ';
	if (tempp >= 10) {
		temp[2] = tempp / 10 + 48;
		temp[3] = (int)tempp % 10 + 48;
		temp[4] = '.';
		temp[5] = (tempp - (int)tempp) * 10 + 48;;
		temp[6] = '\0';
	} else {
		temp[2] = (int)tempp % 10 + 48;
		temp[3] = '.';
		temp[4] = (tempp - (int)tempp) * 10 + 48;;
		temp[5] = '\0';
	}
}

// 显示温度
void LCD_Display(unsigned char *tempp) {
	LCD_WriteCmd(0xC0);
	while(*tempp != '\0') {
		LCD_WriteData(*tempp++);
		delay(200);
	}
	LCD_WriteData(0xdf);	delay(200);
	LCD_WriteData('C');		delay(200);
	LCD_WriteData(' ');
}
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

# 串口通信相关函数

甲机在发送数据时,会首先计算存储温度的字符数组的实际长度,然后将长度发送给乙机,之后在发送字符数组。

乙机在接收数据时,会首先接收字符数组的长度,然后通过for循环再接收温度值,最后添加上\0

// 发送数据
void SERIAL_Send(unsigned char *tempp) {
	unsigned char i, len=0;
	while(*tempp++ != '\0') {
		len += 1;
	}
	// 发送数据长度
	SBUF = len;
	while(TI == 0);
	TI = 0;
	// 发送数据
	for (i=0; i<len; i++) {
		SBUF = temp[i];
		while(TI == 0);
		TI = 0;
	}
}

// 接收数据
void SERIAL_Receive() {
	unsigned char i, len;
    // 接收数据长度
	REN = 1;
	while(RI == 0);
	len = SBUF;
	RI = 0;
	for (i=0; i<len; i++) {
		REN = 1;
		while(RI == 0);
		temp[i] = SBUF;
		RI = 0;
	}
	temp[len] = '\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

# 延迟函数

// 2微秒
void delay_us(unsigned char i) {
	while(--i);
}

// 750毫秒
void delay_750ms() {
	unsigned char i,j,k;
	for (i=23; i>0; i--) {
		for (j=202; j>0; j--) {
			for (k=81; k>0; k--);
		}
	}
}

// 自定义延迟
void delay(unsigned char t) {
	unsigned char i, j;
	for (i=0; i<t; i++) {
		for (j=0; j<50; j++);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 甲机主函数

甲机首先会在LCD的第1行第1个位置开始,显示temperature:,之后通过DS18B20_GetTemp()获取DS18B20的温度值,然后转为字符数组并从LCD的第2行第1个位置开始显示。

然后通过串口通信,将字符数组传给乙机,最后时刻监视温度值是否有变化,如果变化了则重新显示温度值并将新的温度值发送给乙机。

void main() {
	unsigned char i;
	float tempp;
	unsigned char title[]="temperature:";
	P0 = 0xff;
	LCD_INIT();
	delay(255);
	LCD_WriteCmd(0x80);
	for(i=0; i<strlen(title); i++) {
		LCD_WriteData(title[i]);
		delay(200);
	}
	
	temperature = DS18B20_GetTemp();
	LCD_Float2CharArray(temperature);
	LCD_Display(temp);

	TMOD = 0x20;
	TL1 = 0xf4;
	TH1 = 0xf4;
	TR1 = 1;
	SCON = 0x40;
	SERIAL_Send(temp);
	
	while(1) {
		tempp = DS18B20_GetTemp();
		// 温度有改变
		if (abs(tempp*100 - temperature*100) > 0.001) {
			temperature = tempp;
			LCD_Float2CharArray(temperature);
			LCD_Display(temp);
			SERIAL_Send(temp);
		}
	}

}
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

# 乙机主函数

乙机首先会在LCD的第1行第1个位置开始,显示temperature:,之后通过串口通信接收来自甲机传来的温度值并从LCD的第2行第1个位置开始显示,最后通过while(1)死循环,一直等待并接收来自甲机的数据并重新显示到LCD上。

void main() {
	unsigned char i;
	unsigned char title[]="temperature:";
	P0 = 0xff;
	LCD_INIT();
	delay(255);
	LCD_WriteCmd(0x80);
	for(i=0; i<strlen(title); i++) {
		LCD_WriteData(title[i]);
		delay(200);
	}

	TMOD = 0x20;
	TL1 = 0xf4;
	TH1 = 0xf4;
	TR1 = 1;
	SCON = 0x40;
	SERIAL_Receive();
	LCD_Display(temp);

	while(1) {
		SERIAL_Receive();
		LCD_Display(temp);
	}
}
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

# 源码

# 甲机

/*
甲机需要完成3件事
1. 采集DS18B20温度传感器的温度值
2. 利用LCD显示温度值
3. 通过串口将温度值传给乙机
*/

#include <reg52.h>
#include <intrins.h>
#include <string.h>
#include <math.h>

sbit RS = P1^0;
sbit RW = P1^1;
sbit E  = P2^5;
sbit DQ = P2^0;

float temperature = 0;                      // 温度
unsigned char temp[16];                     // 温度

// DS18B20相关函数
unsigned char DS18B20_INIT();               // 初始化
void DS18B20_WriteCmd(unsigned char Rbyte); // 写指令
unsigned char DS18B20_ReadData();           // 读数据
float DS18B20_GetTemp();                    // 获取温度

// LCD相关函数
void LCD_INIT();                            // 初始化
unsigned char LCD_ReadState();              // 读状态
void LCD_WriteCmd(unsigned char cmd);       // 写指令
void LCD_WriteData(unsigned char info);     // 写数据
void LCD_Float2CharArray(float tempp);      // 温度:float转为字符数组
void LCD_Display(unsigned char *tempp);     // 显示温度

// 串口通信相关函数
void SERIAL_Send(unsigned char *tempp);     // 发送数据

// 延迟函数
void delay_us(unsigned char i);             // 2微秒
void delay_750ms();                         // 750毫秒
void delay(unsigned char t);                // 自定义延迟


void main() {
	unsigned char i;
	float tempp;
	unsigned char title[]="temperature:";
	P0 = 0xff;
	LCD_INIT();
	delay(255);
	LCD_WriteCmd(0x80);
	for(i=0; i<strlen(title); i++) {
		LCD_WriteData(title[i]);
		delay(200);
	}
	
	temperature = DS18B20_GetTemp();
	LCD_Float2CharArray(temperature);
	LCD_Display(temp);

	TMOD = 0x20;
	TL1 = 0xf4;
	TH1 = 0xf4;
	TR1 = 1;
	SCON = 0x40;
	SERIAL_Send(temp);
	
	while(1) {
		tempp = DS18B20_GetTemp();
		// 温度有改变
		if (abs(tempp*100 - temperature*100) > 0.001) {
			temperature = tempp;
			LCD_Float2CharArray(temperature);
			LCD_Display(temp);
			SERIAL_Send(temp);
		}
	}

}


// ----------------DS18B20相关函数--------------------
// 初始化
unsigned char DS18B20_INIT() {
	unsigned char presencePulse;
	DQ = 0;
	delay_us(240);
	DQ = 1;
	delay_us(30);
	presencePulse = DQ;
	delay_us(250);
	return presencePulse;
}

// 写指令
void DS18B20_WriteCmd(unsigned char Rbyte) {
	unsigned char i, k=0;
	for (i=0; i<8; i++) {
		k = Rbyte & 0x01;
		if (k == 1) {
			DQ = 0;
			delay_us(4);
			DQ = 1;
			delay_us(20);
			DQ = 1;
		} else {
			DQ = 0;
			delay_us(30);
			DQ = 1;
		}
		Rbyte = Rbyte >> 1;
	}
}

// 读数据
unsigned char DS18B20_ReadData() {
	unsigned char i, Data=0;
	for (i=0; i<8; i++) {
		DQ = 0;
		Data = Data >> 1;
		DQ = 1;
		if (DQ) {
			Data = Data | 0x80;
		}
		delay_us(28);
		DQ = 1;
		_nop_();
	}
	return Data;
}

// 获取温度
float DS18B20_GetTemp() {
	unsigned char valueLow = 0;
	unsigned char valueHigh = 0;
	short int temp = 0;
	DS18B20_INIT();
	DS18B20_WriteCmd(0xcc);
	DS18B20_WriteCmd(0x44);
	delay_750ms();
	DS18B20_INIT();
	DS18B20_WriteCmd(0xcc);
	DS18B20_WriteCmd(0xbe);
	valueLow = DS18B20_ReadData();
	valueHigh = DS18B20_ReadData();

	temp = valueHigh;
	temp = temp<<8;
	temp = temp|valueLow;
	return 0.0625*temp;
}


// ----------------LCD相关函数--------------------
// 初始化
void LCD_INIT() {
	LCD_WriteCmd(0x38);
	LCD_WriteCmd(0x01);
	LCD_WriteCmd(0x06);
	LCD_WriteCmd(0x0e);
	LCD_WriteCmd(0x80);
}

// 读状态
unsigned char LCD_ReadState() {
	unsigned char s;
	RW=1; RS=0;
	E=1; s=P0; E=0;
	return s;
}

// 写指令
void LCD_WriteCmd(unsigned char cmd) {
	unsigned char i;
	do {
		i = LCD_ReadState();
		i &= 0x80;
	} while(i!=0);

	RW=0; RS=0;
	E=1; P0=cmd; E=0;
	delay(200);
}

// 写数据
void LCD_WriteData(unsigned char info) {
	unsigned char i;
	do {
		i = LCD_ReadState();
		i &= 0x80;
	} while(i!=0);

	RW=0; RS=1;
	E=1; P0=info; E=0;
	delay(200);
}

// 温度:float转为字符数组
void LCD_Float2CharArray(float tempp) {
	if (tempp >= 0) {
		temp[0] = '+';
	} else {
		temp[0] = '-';
		tempp = tempp * (-1);
	}
	temp[1] = ' ';
	if (tempp >= 10) {
		temp[2] = tempp / 10 + 48;
		temp[3] = (int)tempp % 10 + 48;
		temp[4] = '.';
		temp[5] = (tempp - (int)tempp) * 10 + 48;;
		temp[6] = '\0';
	} else {
		temp[2] = (int)tempp % 10 + 48;
		temp[3] = '.';
		temp[4] = (tempp - (int)tempp) * 10 + 48;;
		temp[5] = '\0';
	}
}

// 显示温度
void LCD_Display(unsigned char *tempp) {
	LCD_WriteCmd(0xC0);
	while(*tempp != '\0') {
		LCD_WriteData(*tempp++);
		delay(200);
	}
	LCD_WriteData(0xdf);	delay(200);
	LCD_WriteData('C');		delay(200);
	LCD_WriteData(' ');
}


// ----------------串口通信相关函数--------------------
// 发送数据
void SERIAL_Send(unsigned char *tempp) {
	unsigned char i, len=0;
	while(*tempp++ != '\0') {
		len += 1;
	}
	// 发送数据长度
	SBUF = len;
	while(TI == 0);
	TI = 0;
	// 发送数据
	for (i=0; i<len; i++) {
		SBUF = temp[i];
		while(TI == 0);
		TI = 0;
	}
}


// ----------------延迟函数--------------------
// 2微秒
void delay_us(unsigned char i) {
	while(--i);
}

// 750毫秒
void delay_750ms() {
	unsigned char i,j,k;
	for (i=23; i>0; i--) {
		for (j=202; j>0; j--) {
			for (k=81; k>0; k--);
		}
	}
}

// 自定义延迟
void delay(unsigned char t) {
	unsigned char i, j;
	for (i=0; i<t; i++) {
		for (j=0; j<50; j++);
	}
}
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276

# 乙机

/*
乙机需要完成2件事
1. 通过串口接收甲机传来的温度值
2. 利用LCD显示温度值
*/

#include <reg52.h>
#include <intrins.h>
#include <string.h>
#include <math.h>

sbit RS = P1^0;
sbit RW = P1^1;
sbit E  = P2^5;

unsigned char temp[16];                     // 温度

// LCD相关函数
void LCD_INIT();                            // 初始化
unsigned char LCD_ReadState();              // 读状态
void LCD_WriteCmd(unsigned char cmd);       // 写指令
void LCD_WriteData(unsigned char info);     // 写数据
void LCD_Display(unsigned char *tempp);     // 显示温度

// 串口通信相关函数
void SERIAL_Receive();                      // 接收数据

// 延迟函数
void delay(unsigned char t);                // 自定义延迟


void main() {
	unsigned char i;
	unsigned char title[]="temperature:";
	P0 = 0xff;
	LCD_INIT();
	delay(255);
	LCD_WriteCmd(0x80);
	for(i=0; i<strlen(title); i++) {
		LCD_WriteData(title[i]);
		delay(200);
	}

	TMOD = 0x20;
	TL1 = 0xf4;
	TH1 = 0xf4;
	TR1 = 1;
	SCON = 0x40;
	SERIAL_Receive();
	LCD_Display(temp);

	while(1) {
		SERIAL_Receive();
		LCD_Display(temp);
	}
}


// ----------------LCD相关函数--------------------
// 初始化
void LCD_INIT() {
	LCD_WriteCmd(0x38);
	LCD_WriteCmd(0x01);
	LCD_WriteCmd(0x06);
	LCD_WriteCmd(0x0e);
	LCD_WriteCmd(0x80);
}

// 读状态
unsigned char LCD_ReadState() {
	unsigned char s;
	RW=1; RS=0;
	E=1; s=P0; E=0;
	return s;
}

// 写指令
void LCD_WriteCmd(unsigned char cmd) {
	unsigned char i;
	do {
		i = LCD_ReadState();
		i &= 0x80;
	} while(i!=0);

	RW=0; RS=0;
	E=1; P0=cmd; E=0;
	delay(200);
}

// 写数据
void LCD_WriteData(unsigned char info) {
	unsigned char i;
	do {
		i = LCD_ReadState();
		i &= 0x80;
	} while(i!=0);

	RW=0; RS=1;
	E=1; P0=info; E=0;
	delay(200);
}

// 显示温度
void LCD_Display(unsigned char *tempp) {
	LCD_WriteCmd(0xC0);
	while(*tempp != '\0') {
		LCD_WriteData(*tempp++);
		delay(200);
	}
	LCD_WriteData(0xdf);	delay(200);
	LCD_WriteData('C');		delay(200);
	LCD_WriteData(' ');
}


// ----------------串口通信相关函数--------------------
// 接收数据
void SERIAL_Receive() {
	unsigned char i, len;
    // 接收数据长度
	REN = 1;
	while(RI == 0);
	len = SBUF;
	RI = 0;
	for (i=0; i<len; i++) {
		REN = 1;
		while(RI == 0);
		temp[i] = SBUF;
		RI = 0;
	}
	temp[len] = '\0';
}


// ----------------延迟函数--------------------
// 自定义延迟
void delay(unsigned char t) {
	unsigned char i, j;
	for (i=0; i<t; i++) {
		for (j=0; j<50; j++);
	}
}
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

# 运行结果

  • 设置DS18B20
  • 运行结果
  • 调节温度值
  • 当温度值>10
  • 当温度值为负数