单片机循环程序设计:从入门到精通,10步解锁程序设计大师级技能
发布时间: 2024-07-06 09:13:11 阅读量: 38 订阅数: 37
![单片机循环程序设计](https://img-blog.csdnimg.cn/20210923225002292.jpeg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAd2VuaGFpaWk=,size_20,color_FFFFFF,t_70,g_se,x_16)
# 1. 单片机循环程序设计的入门基础
单片机循环程序设计是单片机编程的基础,它通过重复执行一段代码来实现特定的功能。循环程序设计涉及到循环结构、循环控制语句和循环优化等内容。
### 1.1 循环结构的种类
单片机中常用的循环结构包括:
- **while循环:**当循环条件为真时,重复执行循环体。
- **do-while循环:**先执行循环体,然后判断循环条件是否为真,为真则继续执行。
- **for循环:**使用一个循环变量来控制循环次数,并可以指定循环变量的初始值、步长和结束条件。
# 2. 单片机循环程序设计的基本技巧
### 2.1 循环结构的种类和应用
循环结构是单片机程序设计中常用的控制结构,用于重复执行一段代码。单片机中常用的循环结构主要有 while 循环、do-while 循环和 for 循环。
#### 2.1.1 while 循环
while 循环是一种条件循环,当循环条件为真时,循环体中的代码将重复执行。while 循环的语法格式如下:
```c
while (条件) {
循环体
}
```
例如,以下代码使用 while 循环实现 LED 闪烁:
```c
while (1) {
// 打开 LED
LED_ON();
// 延时 1 秒
delay_ms(1000);
// 关闭 LED
LED_OFF();
// 延时 1 秒
delay_ms(1000);
}
```
#### 2.1.2 do-while 循环
do-while 循环也是一种条件循环,但与 while 循环不同的是,do-while 循环先执行循环体,然后再检查循环条件。do-while 循环的语法格式如下:
```c
do {
循环体
} while (条件);
```
例如,以下代码使用 do-while 循环实现 LED 闪烁:
```c
do {
// 打开 LED
LED_ON();
// 延时 1 秒
delay_ms(1000);
// 关闭 LED
LED_OFF();
// 延时 1 秒
delay_ms(1000);
} while (1);
```
#### 2.1.3 for 循环
for 循环是一种计数循环,用于重复执行一段代码一定次数。for 循环的语法格式如下:
```c
for (初始化; 条件; 增量) {
循环体
}
```
例如,以下代码使用 for 循环实现 LED 闪烁 10 次:
```c
for (int i = 0; i < 10; i++) {
// 打开 LED
LED_ON();
// 延时 1 秒
delay_ms(1000);
// 关闭 LED
LED_OFF();
// 延时 1 秒
delay_ms(1000);
}
```
### 2.2 循环控制语句
循环控制语句用于控制循环的执行流程,主要包括 break 语句、continue 语句和 return 语句。
#### 2.2.1 break 语句
break 语句用于提前终止循环,跳出循环体。break 语句的语法格式如下:
```c
break;
```
例如,以下代码使用 break 语句实现 LED 闪烁 5 次后终止循环:
```c
while (1) {
// 打开 LED
LED_ON();
// 延时 1 秒
delay_ms(1000);
// 关闭 LED
LED_OFF();
// 延时 1 秒
delay_ms(1000);
// 闪烁 5 次后终止循环
if (i == 5) {
break;
}
}
```
#### 2.2.2 continue 语句
continue 语句用于跳过当前循环体,继续执行下一次循环。continue 语句的语法格式如下:
```c
continue;
```
例如,以下代码使用 continue 语句实现 LED 闪烁时,跳过偶数次闪烁:
```c
while (1) {
// 打开 LED
LED_ON();
// 延时 1 秒
delay_ms(1000);
// 跳过偶数次闪烁
if (i % 2 == 0) {
continue;
}
// 关闭 LED
LED_OFF();
// 延时 1 秒
delay_ms(1000);
}
```
#### 2.2.3 return 语句
return 语句用于从函数中返回,也可以用于终止循环。return 语句的语法格式如下:
```c
return;
```
例如,以下代码使用 return 语句实现 LED 闪烁 5 次后终止循环:
```c
while (1) {
// 打开 LED
LED_ON();
// 延时 1 秒
delay_ms(1000);
// 关闭 LED
LED_OFF();
// 延时 1 秒
delay_ms(1000);
// 闪烁 5 次后终止循环
if (i == 5) {
return;
}
}
```
### 2.3 循环程序设计的优化
循环程序设计优化可以提高程序的执行效率和代码的可读性。循环程序设计的优化主要包括循环变量的优化和循环条件的优化。
#### 2.3.1 循环变量的优化
循环变量的优化主要包括使用局部变量和使用寄存器变量。局部变量存储在栈中,访问速度比全局变量快;寄存器变量存储在 CPU 的寄存器中,访问速度比局部变量快。因此,在循环中频繁使用的变量应该使用局部变量或寄存器变量。
#### 2.3.2 循环条件的优化
循环条件的优化主要包括提前计算循环条件和减少循环条件的复杂度。提前计算循环条件可以避免在每次循环中重新计算循环条件,减少程序的执行时间;减少循环条件的复杂度可以提高循环条件的执行效率。
# 3. 单片机循环程序设计的实践应用
### 3.1 LED闪烁程序
#### 3.1.1 程序设计思路
LED闪烁程序是单片机循环程序设计中最简单的应用之一。其基本思路是:
1. 初始化LED引脚为输出模式。
2. 进入循环,循环执行以下步骤:
- 将LED引脚置为高电平,点亮LED。
- 延时一段时间。
- 将LED引脚置为低电平,熄灭LED。
- 延时一段时间。
#### 3.1.2 程序代码实现
```c
#define LED_PIN PORTB.0
void main() {
// 初始化LED引脚为输出模式
DDRB |= (1 << LED_PIN);
while (1) {
// 点亮LED
PORTB |= (1 << LED_PIN);
_delay_ms(500); // 延时500ms
// 熄灭LED
PORTB &= ~(1 << LED_PIN);
_delay_ms(500); // 延时500ms
}
}
```
**代码逻辑分析:**
- `DDRB |= (1 << LED_PIN)`:将LED引脚(PORTB.0)配置为输出模式。
- `while (1)`:进入无限循环,循环执行LED闪烁程序。
- `PORTB |= (1 << LED_PIN)`:将LED引脚置为高电平,点亮LED。
- `_delay_ms(500)`:延时500ms,等待LED点亮。
- `PORTB &= ~(1 << LED_PIN)`:将LED引脚置为低电平,熄灭LED。
- `_delay_ms(500)`:延时500ms,等待LED熄灭。
### 3.2 按键检测程序
#### 3.2.1 程序设计思路
按键检测程序用于检测按键是否按下。其基本思路是:
1. 初始化按键引脚为输入模式,并启用上拉电阻。
2. 进入循环,循环执行以下步骤:
- 读取按键引脚电平。
- 如果按键按下,执行按键按下处理函数。
#### 3.2.2 程序代码实现
```c
#define KEY_PIN PORTB.0
void main() {
// 初始化按键引脚为输入模式,并启用上拉电阻
DDRB &= ~(1 << KEY_PIN);
PORTB |= (1 << KEY_PIN);
while (1) {
// 读取按键引脚电平
if (!(PINB & (1 << KEY_PIN))) {
// 按键按下,执行按键按下处理函数
key_pressed();
}
}
}
void key_pressed() {
// 按键按下处理函数
// ...
}
```
**代码逻辑分析:**
- `DDRB &= ~(1 << KEY_PIN)`:将按键引脚(PORTB.0)配置为输入模式。
- `PORTB |= (1 << KEY_PIN)`:启用按键引脚的上拉电阻。
- `while (1)`:进入无限循环,循环执行按键检测程序。
- `if (!(PINB & (1 << KEY_PIN)))`:读取按键引脚电平,如果按键按下,该条件为真。
- `key_pressed()`:如果按键按下,执行按键按下处理函数。
### 3.3 串口通信程序
#### 3.3.1 程序设计思路
串口通信程序用于通过串口与其他设备进行通信。其基本思路是:
1. 初始化串口,设置波特率、数据位、停止位等参数。
2. 进入循环,循环执行以下步骤:
- 接收串口数据。
- 如果收到数据,执行数据处理函数。
- 发送串口数据。
#### 3.3.2 程序代码实现
```c
#define BAUD_RATE 9600
void main() {
// 初始化串口
UBRR0H = (BAUD_RATE >> 8);
UBRR0L = BAUD_RATE;
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
while (1) {
// 接收串口数据
if (UCSR0A & (1 << RXC0)) {
// 收到数据,执行数据处理函数
data_received();
}
// 发送串口数据
// ...
}
}
void data_received() {
// 数据处理函数
// ...
}
```
**代码逻辑分析:**
- `UBRR0H = (BAUD_RATE >> 8); UBRR0L = BAUD_RATE;`:初始化串口波特率。
- `UCSR0B = (1 << RXEN0) | (1 << TXEN0)`:启用串口接收和发送。
- `while (1)`:进入无限循环,循环执行串口通信程序。
- `if (UCSR0A & (1 << RXC0))`:检查是否有数据接收。
- `data_received()`:如果收到数据,执行数据处理函数。
- `// 发送串口数据`:发送串口数据(此处省略代码)。
# 4. 单片机循环程序设计的进阶应用
### 4.1 中断程序设计
#### 4.1.1 中断的概念和分类
中断是一种硬件机制,当外部事件或内部事件发生时,可以暂停当前正在执行的程序,转而执行中断服务程序(ISR)。中断的分类包括:
- **外部中断:**由外部设备或信号触发,如按键按下、串口接收数据等。
- **内部中断:**由片内硬件模块触发,如定时器溢出、ADC转换完成等。
#### 4.1.2 中断程序的编写和管理
中断程序的编写需要遵循以下步骤:
1. **定义中断向量表:**在程序的开始处定义中断向量表,其中包含每个中断源对应的ISR入口地址。
2. **编写ISR:**为每个中断源编写ISR,ISR中包含对中断事件的处理代码。
3. **使能中断:**在程序中使能需要响应的中断源,通常通过设置相应的寄存器。
中断管理包括:
- **中断优先级:**不同的中断源可以设置不同的优先级,高优先级中断可以打断低优先级中断。
- **中断嵌套:**允许高优先级中断打断低优先级中断,但低优先级中断不能打断高优先级中断。
- **中断屏蔽:**可以临时屏蔽中断,防止中断打断当前正在执行的代码。
### 4.2 定时器程序设计
#### 4.2.1 定时器的种类和功能
单片机通常有多个定时器,其种类和功能包括:
- **通用定时器:**可用于产生脉冲、测量时间、产生PWM信号等。
- **看门狗定时器:**用于检测程序是否正常运行,如果程序长时间不更新看门狗寄存器,则看门狗定时器会复位单片机。
- **实时时钟定时器:**用于保持时间和日期信息,并提供闹钟功能。
#### 4.2.2 定时器程序的编写和应用
定时器程序的编写需要遵循以下步骤:
1. **选择定时器:**根据需要选择合适的定时器。
2. **配置定时器:**设置定时器的时钟源、计数模式、计数范围等参数。
3. **编写中断服务程序:**当定时器溢出或达到指定条件时,触发中断服务程序。
定时器程序的应用包括:
- **产生脉冲:**通过设置定时器的计数模式和计数范围,可以产生特定频率和占空比的脉冲。
- **测量时间:**通过记录定时器的计数值,可以测量时间间隔。
- **产生PWM信号:**通过设置定时器的比较值,可以产生特定频率和占空比的PWM信号。
### 4.3 PWM程序设计
#### 4.3.1 PWM的概念和原理
PWM(脉冲宽度调制)是一种通过改变脉冲宽度来控制输出电压或电流的技术。PWM信号的占空比(脉冲宽度与周期之比)决定了输出的平均值。
#### 4.3.2 PWM程序的编写和应用
PWM程序的编写需要遵循以下步骤:
1. **选择PWM模块:**根据需要选择合适的PWM模块。
2. **配置PWM模块:**设置PWM模块的时钟源、计数模式、计数范围、比较值等参数。
3. **编写中断服务程序:**当PWM模块更新比较值时,触发中断服务程序。
PWM程序的应用包括:
- **调光:**通过改变PWM信号的占空比,可以控制LED的亮度。
- **调速:**通过改变PWM信号的占空比,可以控制电机的转速。
- **产生音频信号:**通过改变PWM信号的频率和占空比,可以产生不同音调的音频信号。
# 5.1 嵌套循环程序设计
### 5.1.1 嵌套循环的原理和应用
嵌套循环是指在一个循环内部再嵌套一个或多个循环。这种结构可以实现多重循环嵌套,从而解决复杂的数据处理问题。
嵌套循环的原理是:外层循环控制内层循环的执行次数。当外层循环执行一次时,内层循环会执行完一个完整的循环。以此类推,外层循环执行完所有循环后,内层循环也会执行完所有循环。
嵌套循环的应用场景非常广泛,例如:
- 二维数组的遍历
- 多重条件的判断
- 复杂算法的实现
### 5.1.2 嵌套循环程序设计的注意事项
在设计嵌套循环程序时,需要注意以下事项:
- **循环嵌套层数不宜过多:**过多的嵌套层数会增加程序的复杂度和可读性。一般情况下,不建议超过3层嵌套。
- **循环变量的命名:**循环变量的命名应清晰易懂,避免使用混淆的变量名。
- **循环条件的优化:**循环条件应尽可能简洁高效,避免不必要的判断。
- **循环体内的代码优化:**循环体内的代码应尽量精简,避免冗余的代码。
## 5.2 循环链表程序设计
### 5.2.1 循环链表的概念和结构
循环链表是一种特殊的数据结构,它由一组节点组成,每个节点包含数据和指向下一个节点的指针。与单链表不同,循环链表的最后一个节点指向第一个节点,形成一个闭合的环形结构。
循环链表的优点在于:
- **插入和删除节点高效:**由于循环链表的闭合结构,插入和删除节点只需要修改指针即可,时间复杂度为 O(1)。
- **空间利用率高:**循环链表不需要额外的头结点和尾结点,空间利用率更高。
### 5.2.2 循环链表程序设计的实现
实现循环链表程序需要以下步骤:
1. 定义节点结构体:
```c
typedef struct Node {
int data;
struct Node *next;
} Node;
```
2. 创建循环链表:
```c
Node *create_circular_list() {
Node *head = NULL;
Node *new_node;
int data;
printf("Enter data (enter -1 to stop): ");
scanf("%d", &data);
while (data != -1) {
new_node = (Node *)malloc(sizeof(Node));
new_node->data = data;
if (head == NULL) {
head = new_node;
new_node->next = head;
} else {
Node *temp = head;
while (temp->next != head) {
temp = temp->next;
}
temp->next = new_node;
new_node->next = head;
}
printf("Enter data (enter -1 to stop): ");
scanf("%d", &data);
}
return head;
}
```
3. 遍历循环链表:
```c
void traverse_circular_list(Node *head) {
Node *temp = head;
if (head == NULL) {
printf("Circular list is empty.\n");
return;
}
printf("Circular list: ");
while (temp->next != head) {
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("%d\n", temp->data);
}
```
4. 插入节点:
```c
void insert_node(Node **head, int data, int position) {
Node *new_node = (Node *)malloc(sizeof(Node));
new_node->data = data;
if (*head == NULL) {
*head = new_node;
new_node->next = *head;
} else {
Node *temp = *head;
int count = 1;
while (count < position - 1) {
temp = temp->next;
count++;
}
new_node->next = temp->next;
temp->next = new_node;
}
}
```
5. 删除节点:
```c
void delete_node(Node **head, int position) {
if (*head == NULL) {
printf("Circular list is empty.\n");
return;
}
Node *temp = *head;
int count = 1;
if (position == 1) {
while (temp->next != *head) {
temp = temp->next;
}
temp->next = (*head)->next;
free(*head);
*head = temp->next;
} else {
while (count < position - 1) {
temp = temp->next;
count++;
}
Node *to_delete = temp->next;
temp->next = to_delete->next;
free(to_delete);
}
}
```
# 6. 单片机循环程序设计的综合案例
### 6.1 交通灯控制系统设计
#### 6.1.1 系统需求分析
交通灯控制系统是一个典型的循环程序设计应用,其主要功能是根据交通流量和时间要求,控制交通灯的红、黄、绿灯状态,实现有序的交通管理。
系统需求如下:
- 交通灯有三种状态:红灯、黄灯、绿灯。
- 红灯亮起后,持续时间为 30 秒。
- 黄灯亮起后,持续时间为 5 秒。
- 绿灯亮起后,持续时间为 40 秒。
- 系统循环运行,依次切换红、黄、绿灯状态。
#### 6.1.2 程序设计实现
```c
#include <reg51.h>
#define RED_LED P1_0
#define YELLOW_LED P1_1
#define GREEN_LED P1_2
unsigned char red_time = 30;
unsigned char yellow_time = 5;
unsigned char green_time = 40;
void delay(unsigned int time) {
while (time--) {
for (int i = 0; i < 1000; i++);
}
}
void main() {
while (1) {
// 红灯亮起
RED_LED = 1;
YELLOW_LED = 0;
GREEN_LED = 0;
delay(red_time);
// 黄灯亮起
RED_LED = 0;
YELLOW_LED = 1;
GREEN_LED = 0;
delay(yellow_time);
// 绿灯亮起
RED_LED = 0;
YELLOW_LED = 0;
GREEN_LED = 1;
delay(green_time);
}
}
```
0
0