【单片机C语言编程指南】:10个入门必备知识点,揭开单片机编程的神秘面纱
发布时间: 2024-07-07 17:30:26 阅读量: 158 订阅数: 32
![【单片机C语言编程指南】:10个入门必备知识点,揭开单片机编程的神秘面纱](https://img-blog.csdnimg.cn/509823d7be834421a341f28adb5146bf.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aW955qEX-a1qeWQjOWtpg==,size_20,color_FFFFFF,t_70,g_se,x_16)
# 1. 单片机C语言基础**
单片机C语言是一种嵌入式系统编程语言,专门用于控制单片机(MCU)的硬件和软件。它是一种紧凑、高效且易于使用的语言,非常适合资源受限的嵌入式系统。
单片机C语言基于标准C语言,但包含了一些额外的特性和功能,以适应单片机的特殊要求。这些特性包括:
- **位操作:**单片机C语言支持对单个位的操作,这对于控制硬件设备非常有用。
- **内存管理:**单片机C语言提供对单片机内存的直接访问,允许程序员优化内存使用和提高性能。
- **中断处理:**单片机C语言支持中断处理,允许程序响应外部事件。
# 2. 单片机C语言编程技巧
### 2.1 数据类型和变量
#### 2.1.1 数据类型概述
单片机C语言中,数据类型用于定义变量可以存储的数据类型。常见的单片机C语言数据类型包括:
| 数据类型 | 描述 |
|---|---|
| char | 8位有符号字符类型 |
| int | 16位有符号整数类型 |
| long | 32位有符号整数类型 |
| float | 32位浮点数类型 |
| double | 64位浮点数类型 |
#### 2.1.2 变量的定义和使用
变量用于存储数据。要定义一个变量,需要指定其数据类型和名称。例如:
```c
int age;
```
这将创建一个名为`age`的变量,该变量可以存储一个16位有符号整数。
要使用变量,可以使用其名称。例如:
```c
age = 25;
```
这将把值25存储在`age`变量中。
### 2.2 流程控制
#### 2.2.1 条件语句
条件语句用于根据条件执行不同的代码块。最常见的条件语句是`if-else`语句:
```c
if (condition) {
// 如果条件为真,执行此代码块
} else {
// 如果条件为假,执行此代码块
}
```
例如:
```c
if (age >= 18) {
// 如果年龄大于或等于18岁,执行此代码块
} else {
// 如果年龄小于18岁,执行此代码块
}
```
#### 2.2.2 循环语句
循环语句用于重复执行一段代码。最常见的循环语句是`for`循环和`while`循环:
```c
// for循环
for (int i = 0; i < 10; i++) {
// 执行此代码块10次
}
// while循环
while (condition) {
// 如果条件为真,执行此代码块
}
```
例如:
```c
// 打印数字1到10
for (int i = 1; i <= 10; i++) {
printf("%d ", i);
}
```
#### 2.2.3 函数和参数
函数是可重用的代码块,可以接收参数并返回一个值。要定义一个函数,需要指定其返回类型、名称和参数列表。例如:
```c
int sum(int a, int b) {
return a + b;
}
```
这将创建一个名为`sum`的函数,该函数接收两个整数参数并返回它们的和。
要调用函数,可以使用其名称和参数。例如:
```c
int result = sum(10, 20);
```
这将调用`sum`函数,并将结果存储在`result`变量中。
### 2.3 存储器管理
#### 2.3.1 存储器类型
单片机中有多种类型的存储器,包括:
| 存储器类型 | 描述 |
|---|---|
| ROM | 只读存储器,存储程序代码 |
| RAM | 随机存取存储器,存储数据和变量 |
| EEPROM | 电可擦除可编程只读存储器,可以多次擦除和编程 |
| Flash | 闪存,可以擦除和编程,但比EEPROM速度更快 |
#### 2.3.2 存储器访问方式
单片机访问存储器的方式有两种:
| 访问方式 | 描述 |
|---|---|
| 直接寻址 | 使用地址直接访问存储器位置 |
| 间接寻址 | 使用指针间接访问存储器位置 |
例如:
```c
// 直接寻址
int data = *(int *)0x1000;
// 间接寻址
int *ptr = &data;
int data = *ptr;
```
# 3.1 输入输出操作
#### 3.1.1 端口配置
**端口配置概述**
单片机通过端口与外部设备进行数据交换。端口配置是指设置端口的电气特性和功能,以满足特定应用需求。端口配置通常包括以下步骤:
* **设置端口方向:**指定端口引脚是输入还是输出。
* **设置端口模式:**指定端口引脚的电气特性,例如推挽输出、开漏输出或输入。
* **设置端口拉电阻:**为输入端口引脚配置上拉或下拉电阻,以防止浮空状态。
**端口配置代码示例**
```c
// 设置端口B的第5个引脚为输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
```
**参数说明:**
* `GPIO_InitTypeDef GPIO_InitStructure;`:GPIO初始化结构体。
* `GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;`:设置端口B的第5个引脚。
* `GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;`:设置端口模式为推挽输出。
* `GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;`:设置端口速度为50MHz。
* `GPIO_Init(GPIOB, &GPIO_InitStructure);`:初始化端口B。
#### 3.1.2 数据传输
**数据传输概述**
数据传输是指单片机与外部设备之间的数据交换。单片机通过端口引脚进行数据传输,可以使用以下两种方式:
* **读操作:**从外部设备读取数据到单片机。
* **写操作:**从单片机向外部设备写入数据。
**数据传输代码示例**
```c
// 从端口B的第5个引脚读取数据
uint8_t data = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5);
```
**参数说明:**
* `uint8_t data = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5);`:读取端口B的第5个引脚的数据。
```c
// 向端口B的第5个引脚写入数据
GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_SET);
```
**参数说明:**
* `GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_SET);`:向端口B的第5个引脚写入高电平。
# 4. 单片机C语言进阶应用
### 4.1 通信接口
#### 4.1.1 串口通信
**概念**
串口通信是一种异步通信方式,用于在两台设备之间传输数据。它使用两个引脚:一个用于发送数据(TX),另一个用于接收数据(RX)。
**硬件连接**
连接两个设备进行串口通信时,需要使用交叉电缆或串口转接器。交叉电缆将发送引脚连接到接收引脚,反之亦然。
**软件配置**
在单片机中,串口通信通常使用UART(通用异步收发器)模块进行配置。UART模块负责生成波特率、数据位、停止位和奇偶校验等通信参数。
**代码示例**
```c
#include <avr/io.h>
// 初始化串口
void uart_init(uint32_t baud_rate) {
// 设置波特率
UBRR0H = (uint8_t)(baud_rate >> 8);
UBRR0L = (uint8_t)baud_rate;
// 设置数据位、停止位和奇偶校验
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8 数据位
UCSR0C |= (1 << USBS0); // 1 停止位
UCSR0C |= (1 << UPM01); // 无奇偶校验
// 启用串口
UCSR0B |= (1 << RXEN0) | (1 << TXEN0);
}
// 发送一个字符
void uart_putc(char c) {
// 等待发送缓冲区空闲
while (!(UCSR0A & (1 << UDRE0)));
// 发送字符
UDR0 = c;
}
// 接收一个字符
char uart_getc() {
// 等待接收缓冲区有数据
while (!(UCSR0A & (1 << RXC0)));
// 接收字符
return UDR0;
}
```
**逻辑分析**
* `uart_init()` 函数初始化串口,设置波特率、数据位、停止位和奇偶校验。
* `uart_putc()` 函数发送一个字符。它等待发送缓冲区空闲,然后将字符写入 UDR0 寄存器。
* `uart_getc()` 函数接收一个字符。它等待接收缓冲区有数据,然后从 UDR0 寄存器读取字符。
#### 4.1.2 I2C通信
**概念**
I2C(Inter-Integrated Circuit)是一种同步通信方式,用于在多台设备之间传输数据。它使用两条线:一条数据线(SDA)和一条时钟线(SCL)。
**硬件连接**
连接多个设备进行 I2C 通信时,需要使用 I2C 总线。I2C 总线将所有设备的 SDA 和 SCL 引脚连接在一起。
**软件配置**
在单片机中,I2C 通信通常使用 TWI(两线接口)模块进行配置。TWI 模块负责生成时钟信号、数据传输和应答信号。
**代码示例**
```c
#include <avr/io.h>
#include <util/twi.h>
// 初始化 I2C
void i2c_init() {
// 设置时钟频率
TWSR = 0; // 预分频器为 1
TWBR = 12; // 波特率为 100kHz
// 启用 I2C
TWCR |= (1 << TWEN);
}
// 发送一个字节
void i2c_write(uint8_t data) {
// 等待总线空闲
while (TWCR & (1 << TWINT));
// 发送起始信号
TWCR = (1 << TWINT) | (1 << TWSTA);
// 等待起始信号发送完成
while (!(TWCR & (1 << TWINT)));
// 发送设备地址和写标志
TWDR = (0x50 << 1) | 0; // 0x50 是设备地址,0 表示写操作
// 等待地址发送完成
while (!(TWCR & (1 << TWINT)));
// 发送数据
TWDR = data;
// 等待数据发送完成
while (!(TWCR & (1 << TWINT)));
// 发送停止信号
TWCR = (1 << TWINT) | (1 << TWSTO);
}
// 接收一个字节
uint8_t i2c_read() {
// 等待总线空闲
while (TWCR & (1 << TWINT));
// 发送起始信号
TWCR = (1 << TWINT) | (1 << TWSTA);
// 等待起始信号发送完成
while (!(TWCR & (1 << TWINT)));
// 发送设备地址和读标志
TWDR = (0x50 << 1) | 1; // 0x50 是设备地址,1 表示读操作
// 等待地址发送完成
while (!(TWCR & (1 << TWINT)));
// 接收数据
TWCR = (1 << TWINT) | (1 << TWEA); // 发送应答信号
// 等待数据接收完成
while (!(TWCR & (1 << TWINT)));
// 发送停止信号
TWCR = (1 << TWINT) | (1 << TWSTO);
// 返回接收到的数据
return TWDR;
}
```
**逻辑分析**
* `i2c_init()` 函数初始化 I2C,设置时钟频率和启用 I2C。
* `i2c_write()` 函数发送一个字节。它等待总线空闲,发送起始信号、设备地址和写标志,然后发送数据。
* `i2c_read()` 函数接收一个字节。它等待总线空闲,发送起始信号、设备地址和读标志,然后接收数据。
# 5. 单片机C语言项目实战**
**5.1 LED闪烁程序**
LED闪烁程序是单片机C语言最基本的项目之一,通过控制LED的亮灭实现闪烁效果。
**代码:**
```c
#include <reg51.h>
void main()
{
while (1)
{
P1 = 0x01; // LED1亮
delay(500); // 延时500ms
P1 = 0x00; // LED1灭
delay(500); // 延时500ms
}
}
```
**参数说明:**
* P1:LED1的端口地址
* 0x01:LED1亮
* 0x00:LED1灭
* delay(500):延时500ms的函数
**执行逻辑:**
1. 初始化P1端口为输出模式。
2. 进入无限循环。
3. 将P1端口置为0x01,LED1亮。
4. 延时500ms。
5. 将P1端口置为0x00,LED1灭。
6. 延时500ms。
7. 重复步骤3-6,实现LED1闪烁效果。
**5.2 按键检测程序**
按键检测程序用于检测按键的按下和释放状态,可以实现按键控制等功能。
**代码:**
```c
#include <reg51.h>
void main()
{
while (1)
{
if (P1_0 == 0) // 按键按下
{
// 按键按下处理代码
}
else // 按键释放
{
// 按键释放处理代码
}
}
}
```
**参数说明:**
* P1_0:按键的端口地址
* 0:按键按下
* 1:按键释放
**执行逻辑:**
1. 初始化P1_0端口为输入模式。
2. 进入无限循环。
3. 检测P1_0端口的状态,如果为0表示按键按下,否则表示按键释放。
4. 根据按键状态执行相应的处理代码。
5. 重复步骤3-4,实现按键检测功能。
**5.3 串口通信程序**
串口通信程序用于通过串口与其他设备进行数据交换,可以实现数据传输、调试等功能。
**代码:**
```c
#include <reg51.h>
void main()
{
// 初始化串口
SCON = 0x50; // 串口模式1,8位数据,1位停止位
TMOD = 0x20; // 定时器1为串口模式
TH1 = 0xFD; // 波特率9600
TR1 = 1; // 启动定时器1
while (1)
{
if (RI == 1) // 接收到数据
{
// 数据接收处理代码
RI = 0; // 清除接收标志位
}
if (TI == 1) // 可以发送数据
{
// 数据发送处理代码
TI = 0; // 清除发送标志位
}
}
}
```
**参数说明:**
* SCON:串口控制寄存器
* TMOD:定时器模式寄存器
* TH1:定时器1重装值寄存器
* TR1:定时器1运行控制位
* RI:接收标志位
* TI:发送标志位
**执行逻辑:**
1. 初始化串口。
2. 进入无限循环。
3. 检测接收标志位RI,如果为1表示接收到数据,执行数据接收处理代码。
4. 检测发送标志位TI,如果为1表示可以发送数据,执行数据发送处理代码。
5. 重复步骤3-4,实现串口通信功能。
0
0