揭秘单片机语言C51程序设计:从基础到实战,全面解析
发布时间: 2024-07-07 16:25:52 阅读量: 253 订阅数: 36
基于C51单片机的 程序设计 包括流水灯 蜂鸣器 温度传感器 串口通讯+源代码+文档说明
# 1. 单片机C51语言简介**
C51语言是一种专为英特尔8051系列单片机设计的汇编语言。它具有简洁、高效、易于理解等特点,广泛应用于嵌入式系统开发中。
C51语言基于8位数据总线,支持多种数据类型,包括整数、浮点数、字符和字符串。它提供了丰富的指令集,包括算术运算、逻辑运算、控制转移和输入/输出操作等。
C51语言采用模块化设计,程序可以分为多个模块,每个模块负责特定的功能。这使得程序易于维护和扩展。
# 2.1 数据类型和变量
### 2.1.1 数据类型概述
C51语言支持多种数据类型,用于表示不同类型的数据。常见的数据类型包括:
- **整型:**用于表示整数,包括有符号和无符号类型。
- **浮点型:**用于表示小数或实数。
- **字符型:**用于表示单个字符。
- **布尔型:**用于表示真或假。
### 2.1.2 变量定义和使用
变量用于存储数据,变量的定义需要指定数据类型和变量名。变量定义的语法如下:
```c
<数据类型> <变量名>;
```
例如:
```c
int num;
char ch;
```
变量的使用需要通过变量名进行访问,例如:
```c
num = 10;
ch = 'a';
```
## 2.2 运算符和表达式
### 2.2.1 算术运算符
算术运算符用于执行算术运算,包括加(+)、减(-)、乘(*)、除(/)、取模(%)等。
```c
int a = 10, b = 5;
int sum = a + b; // sum = 15
int diff = a - b; // diff = 5
int prod = a * b; // prod = 50
int quot = a / b; // quot = 2
int rem = a % b; // rem = 0
```
### 2.2.2 逻辑运算符
逻辑运算符用于执行逻辑运算,包括与(&&)、或(||)、非(!)等。
```c
int a = 1, b = 0;
int result = a && b; // result = 0
result = a || b; // result = 1
result = !a; // result = 0
```
## 2.3 控制语句
### 2.3.1 条件语句
条件语句用于根据条件执行不同的代码块,包括 if-else 语句和 switch-case 语句。
```c
int num = 10;
if (num > 5) {
// 执行代码块 1
} else {
// 执行代码块 2
}
```
```c
int num = 3;
switch (num) {
case 1:
// 执行代码块 1
break;
case 2:
// 执行代码块 2
break;
default:
// 执行默认代码块
break;
}
```
### 2.3.2 循环语句
循环语句用于重复执行一段代码块,包括 for 循环、while 循环和 do-while 循环。
```c
for (int i = 0; i < 10; i++) {
// 执行代码块
}
```
```c
int i = 0;
while (i < 10) {
// 执行代码块
i++;
}
```
```c
int i = 0;
do {
// 执行代码块
i++;
} while (i < 10);
```
# 3.1 函数和数组
#### 3.1.1 函数定义和调用
函数是将代码块封装成一个独立的单元,可以被程序中的其他部分调用。在 C51 中,函数的定义如下:
```c
returnType functionName(parameterList) {
// 函数体
}
```
其中:
* `returnType` 是函数的返回值类型,可以是 `void`(无返回值)或其他数据类型。
* `functionName` 是函数的名称。
* `parameterList` 是函数的参数列表,可以为空或包含多个参数。
* `函数体` 是函数的代码块,包含要执行的语句。
函数的调用使用以下语法:
```c
functionName(argumentList);
```
其中:
* `functionName` 是要调用的函数的名称。
* `argumentList` 是传递给函数的参数列表,可以为空或包含多个参数。
**代码块:**
```c
// 定义一个计算两个数之和的函数
int sum(int a, int b) {
return a + b;
}
// 调用 sum 函数
int result = sum(10, 20);
```
**逻辑分析:**
* `sum` 函数定义了一个计算两个整数之和并返回结果的函数。
* `result` 变量存储了 `sum` 函数的返回值,即 10 + 20 = 30。
#### 3.1.2 数组定义和使用
数组是一种数据结构,它允许存储相同数据类型的多个元素。在 C51 中,数组的定义如下:
```c
dataType arrayName[size];
```
其中:
* `dataType` 是数组元素的数据类型。
* `arrayName` 是数组的名称。
* `size` 是数组的大小,即元素的数量。
数组元素可以使用索引访问,索引从 0 开始。
**代码块:**
```c
// 定义一个存储 5 个整数的数组
int numbers[5];
// 访问数组的第一个元素
int firstNumber = numbers[0];
```
**逻辑分析:**
* `numbers` 数组定义了一个可以存储 5 个整数的数组。
* `firstNumber` 变量存储了 `numbers` 数组的第一个元素,即索引为 0 的元素。
# 4. C51语言实战应用
### 4.1 LED灯控制
#### 4.1.1 硬件连接
* 将LED灯的正极连接到单片机的P1.0端口
* 将LED灯的负极连接到地线
#### 4.1.2 程序设计
```c
#include <reg51.h>
void main() {
while (1) {
P1 = 0x01; // 点亮LED灯
delay(1000); // 延时1秒
P1 = 0x00; // 熄灭LED灯
delay(1000); // 延时1秒
}
}
void delay(unsigned int ms) {
unsigned int i, j;
for (i = 0; i < ms; i++) {
for (j = 0; j < 120; j++) {
; // 空操作,延时
}
}
}
```
**代码逻辑分析:**
* 主函数`main()`中,通过循环不断点亮和熄灭LED灯。
* `delay()`函数用于延时,参数`ms`表示延时时间(单位:毫秒)。
### 4.2 数码管显示
#### 4.2.1 硬件连接
* 将数码管的正极连接到单片机的P0端口
* 将数码管的负极连接到地线
#### 4.2.2 程序设计
```c
#include <reg51.h>
const unsigned char code seg_code[] = {
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
};
void main() {
unsigned char i;
while (1) {
for (i = 0; i < 10; i++) {
P0 = seg_code[i]; // 显示数字i
delay(1000); // 延时1秒
}
}
}
void delay(unsigned int ms) {
unsigned int i, j;
for (i = 0; i < ms; i++) {
for (j = 0; j < 120; j++) {
; // 空操作,延时
}
}
}
```
**代码逻辑分析:**
* `seg_code`数组存储了0-9十个数字的数码管显示码。
* 主函数`main()`中,通过循环依次显示数字0-9。
* `delay()`函数用于延时,参数`ms`表示延时时间(单位:毫秒)。
### 4.3 串口通信
#### 4.3.1 硬件连接
* 将单片机的P3.0端口连接到串口接收端(RXD)
* 将单片机的P3.1端口连接到串口发送端(TXD)
#### 4.3.2 程序设计
```c
#include <reg51.h>
void main() {
SCON = 0x50; // 设置串口控制寄存器
TMOD = 0x20; // 设置定时器1为8位自动重装模式
TH1 = 0xFD; // 设置定时器1重装值
TR1 = 1; // 启动定时器1
while (1) {
if (RI == 1) { // 接收到数据
SBUF = SBUF; // 清除接收标志位
P0 = SBUF; // 将接收到的数据显示到P0端口
}
}
}
```
**代码逻辑分析:**
* 设置串口控制寄存器`SCON`为0x50,开启串口通信。
* 设置定时器1为8位自动重装模式,并设置重装值`TH1`为0xFD,波特率为9600。
* 主函数`main()`中,不断轮询接收标志位`RI`。当接收到数据时,清除接收标志位并显示接收到的数据到P0端口。
# 5.1 I2C总线通信
### 5.1.1 I2C总线概述
I2C(Inter-Integrated Circuit)总线是一种串行通信协议,用于连接微控制器和其他设备。它是一种双向、半双工通信协议,使用两根信号线:串行数据线(SDA)和串行时钟线(SCL)。
I2C总线具有以下特点:
- 主从模式:总线上只有一个主设备和多个从设备。
- 多主设备:允许在同一总线上连接多个主设备,但一次只能有一个主设备处于活动状态。
- 地址寻址:每个从设备都有一个唯一的地址,主设备通过发送地址来选择要通信的从设备。
- 数据传输:数据以字节为单位传输,主设备和从设备都可以发送和接收数据。
- 冲突检测:I2C总线使用开漏输出,如果两个设备同时尝试发送数据,则会发生冲突,总线上的电压会下降,设备会检测到冲突并停止传输。
### 5.1.2 C51 I2C库使用
C51语言提供了I2C库,用于简化I2C总线通信。该库包含以下函数:
```c
void I2C_Init(unsigned char baudrate);
void I2C_Start(void);
void I2C_Stop(void);
unsigned char I2C_WriteByte(unsigned char data);
unsigned char I2C_ReadByte(void);
```
**函数参数说明:**
- `baudrate`:I2C总线波特率,单位为kbps。
- `data`:要发送的数据字节。
**函数逻辑分析:**
- `I2C_Init()`:初始化I2C总线,设置波特率和引脚配置。
- `I2C_Start()`:启动I2C通信,发送起始信号。
- `I2C_Stop()`:停止I2C通信,发送停止信号。
- `I2C_WriteByte()`:向I2C总线发送一个数据字节。
- `I2C_ReadByte()`:从I2C总线读取一个数据字节。
**代码示例:**
```c
#include <reg51.h>
#include <i2c.h>
void main()
{
I2C_Init(100); // 初始化I2C总线,波特率为100kbps
I2C_Start(); // 发送起始信号
I2C_WriteByte(0x50); // 发送从设备地址(0x50)
I2C_WriteByte(0x00); // 发送寄存器地址(0x00)
I2C_Stop(); // 发送停止信号
I2C_Start(); // 发送起始信号
I2C_WriteByte(0x50); // 发送从设备地址(0x50)
I2C_WriteByte(0x01); // 发送读命令(0x01)
unsigned char data = I2C_ReadByte(); // 读取数据字节
I2C_Stop(); // 发送停止信号
}
```
**代码逻辑分析:**
该代码示例使用I2C库向从设备(地址为0x50)写入数据,然后读取从设备中指定寄存器(地址为0x00)的数据。
# 6. C51语言项目实战**
**6.1 温湿度监测系统**
**6.1.1 系统设计**
温湿度监测系统是一个基于C51单片机的嵌入式系统,用于测量和显示环境中的温度和湿度。系统由以下硬件模块组成:
* C51单片机
* 温湿度传感器
* LCD显示器
* 电源模块
系统的工作原理如下:
1. C51单片机从温湿度传感器获取温度和湿度数据。
2. 单片机将数据处理成可读格式。
3. 单片机将数据显示在LCD显示器上。
**6.1.2 程序实现**
```c
// 温湿度监测系统程序
#include <reg51.h>
#include <stdio.h>
// 温湿度传感器地址
#define DHT11_ADDR 0x5C
// 温湿度传感器命令
#define DHT11_CMD_READ_DATA 0x03
// LCD显示器地址
#define LCD_ADDR 0x27
// LCD显示器命令
#define LCD_CMD_CLEAR_DISPLAY 0x01
#define LCD_CMD_SET_CURSOR_HOME 0x02
#define LCD_CMD_WRITE_DATA 0x40
// 初始化LCD显示器
void init_lcd() {
P0 = LCD_CMD_CLEAR_DISPLAY;
P0 = LCD_CMD_SET_CURSOR_HOME;
}
// 向LCD显示器写入数据
void write_lcd(char data) {
P0 = LCD_CMD_WRITE_DATA;
P1 = data;
}
// 从温湿度传感器读取数据
void read_dht11(unsigned char *temp, unsigned char *humi) {
// 发送读取数据命令
P0 = DHT11_CMD_READ_DATA;
// 等待传感器响应
while (P0 != 0x80);
// 读取温度数据
*temp = P0;
// 读取湿度数据
*humi = P0;
}
// 主函数
void main() {
unsigned char temp, humi;
// 初始化LCD显示器
init_lcd();
// 循环读取温湿度数据
while (1) {
// 读取温湿度数据
read_dht11(&temp, &humi);
// 将数据显示在LCD显示器上
write_lcd(temp);
write_lcd(' ');
write_lcd('C');
write_lcd(' ');
write_lcd(humi);
write_lcd(' ');
write_lcd('%');
}
}
```
0
0