当前位置: 首页 > news >正文

Day43 嵌入式 中断、定时器与串行通信

day43 嵌入式 中断、定时器与串行通信


一、中断的使用

1. 中断的基本概念

中断是一种异步事件处理机制,允许CPU在执行主程序时响应外部或内部事件。当发生中断请求时,CPU暂停当前任务,跳转到对应的中断服务程序(ISR)执行处理,处理完成后返回原程序继续运行。

  • 同步事件:按顺序执行,如循环扫描按键。
  • 异步事件:随机发生,如按键按下、定时溢出等。

✅ 中断的核心优势:提高系统实时性,减少轮询开销,提升效率。


2. 中断的初始化流程

// 外部中断0初始化函数
void eint0_init(void)
{IT0 = 1; // 设置外部中断0为边沿触发方式(下降沿触发)EX0 = 1; // 使能外部中断0EA = 1;  // 使能总中断
}
🔧 初始化步骤说明:
寄存器功能值设置
IT0触发方式选择1:下降沿触发;0:电平触发
EX0外部中断0使能1:使能;0:禁用
EA全局中断使能1:开启所有中断;0:关闭所有中断

📌 注意:只有同时设置 EX0=1EA=1,外部中断才能生效。


3. 中断服务程序(ISR)

// 外部中断0服务函数
void eint0_handler(void) interrupt 0
{num++;  // 中断计数加1
}
⚠️ ISR编写规范:
  • 无参数、无返回值:不能传递参数,也不能返回结果。
  • 简短快速:应尽快完成处理并退出,避免影响其他任务。
  • 不可重入:同一时间只能被调用一次。

✅ 理想运行结果:每检测到一次按键按下(产生下降沿),num 自增1,实现对按键次数的统计。


二、矩阵按键模块与数码管显示

1. 矩阵按键电路结构

在这里插入图片描述

这是矩阵按键模块的电路示意图。图中展示了一个4×4的矩阵按键结构,共有16个按键(S1 - S16),通过P1端口的P10 - P17引脚进行连接与控制,常用于单片机等嵌入式系统中,实现多按键输入的功能,相比独立按键能更节省I/O口资源。


2. 数码管段码与显示函数

// 共阴极数码管段码数组,存储0-9对应的段码
unsigned char digit_data[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
📊 段码解释(共阴极):
  • 0x3f → 数字 0(a,b,c,d,e,f亮)
  • 0x06 → 数字 1(b,c亮)
  • 0x6f → 数字 9(a,b,c,d,f,g亮)
// 数码管显示函数,将数字显示在4位数码管上
void digit_show(unsigned int num)
{unsigned char digit[4]; // 用于存储数字的个位、十位、百位、千位unsigned char i;// 分解数字到各位digit[0] = num%10;               // 获取个位数字digit[1] = (num/10)%10;        // 获取十位数字digit[2] = (num/100)%10;       // 获取百位数字digit[3] = (num/1000)%10;      // 获取千位数字// 逐位扫描显示for(i=0;i<4;i++){P0 = 0x00;               // 清空P0口输出,避免段码串扰P2 = i<<2;             // 选通第i位数码管,左移2位是硬件连接需要if(0 == num && i>0){P0 = 0x00;  // 数字为0且不是最低位,不显示}else if(num<10 && i>0){P0 = 0x00;  // 数字是个位数且不是最低位,不显示}else if(num<100 && i>1){P0 = 0x00;  // 数字是两位数且超过十位,不显示}else if(num<1000 && i>2){P0 = 0x00;  // 数字是三位数且超过百位,不显示}else{P0 = digit_data[digit[i]]; // 输出对应位的段码,显示数字}delay(1); // 延时1ms,利用视觉暂留保持显示稳定}
}
💡 功能说明:
  • 使用动态扫描技术,依次点亮每位数码管。
  • 利用人眼视觉暂留效应,实现“同时显示”效果。
  • 实现前导零消除:如 num=5 显示为 5 而非 0005

✅ 理想运行结果:输入任意数值(0~9999),数码管正确显示该数值,高位无效零自动隐藏。


3. 矩阵按键扫描函数

// 按键扫描函数,检测矩阵按键并返回按键对应的数值
unsigned char key_scan(void)
{unsigned char num = 0; // 存储按键对应的数值unsigned char key = 0; // 存储按键的键值unsigned char i = 0;   // 循环变量for(i = 0; i < 4; i++){P1 = ~(1 << i); // 逐行拉低P1口的某一位,扫描按键if(0xf0 != (P1 & 0xf0)) // 判断是否有按键按下{key = P1 & 0xf0; // 获取按键的键值(高四位)switch(key){case 0x70:num = 4 - i;break;case 0xb0:num = 8 - i;break;case 0xd0:num = 12 - i;break;case 0xe0:num = 16 - i;break;default:num = 0;break;}}}return num; // 返回按键对应的数值
}
🔍 扫描逻辑详解:
  • 行扫描法:依次将P1口的某一行设为低电平,其余为高电平。
  • 列读取:读取P1口的高4位(列线),若不等于 0xf0,说明有按键按下。
  • 键值识别:根据列线状态判断具体是哪个按键。

✅ 理想运行结果:按下任一按键后,key_scan() 返回对应编号(1~16),例如按下S1返回1,S16返回16。


4. 主程序逻辑

// 主函数
void main(void)
{unsigned char key = 0;   // 存储当前按键数值unsigned char tmp = 0;   // 存储按键扫描的临时数值eint0_init();            // 初始化外部中断0while(1)                 // 无限循环{tmp = key_scan();    // 扫描按键,获取临时数值if(tmp)              // 如果有有效按键按下key = tmp;       // 更新当前按键数值digit_show(key);     // 在数码管上显示当前按键数值}
}
🔄 运行流程:
  1. 初始化外部中断。
  2. 不断扫描按键。
  3. 若有按键按下,则更新 key
  4. key 显示在数码管上。

✅ 理想运行结果:按下任意按键,数码管立即显示对应数字(1~16),松开后仍保持显示。


三、定时器/计数器原理与应用

1. 定时器基本概念

定时器是基于内部计数器的硬件模块,用于生成精确的时间基准。它由一个递增计数器构成,当计数达到最大值(溢出)时产生中断。

📌 51单片机定时器为递增计数器,从初值开始向上计数,直到溢出。


2. 定时器相关寄存器

在这里插入图片描述

寄存器地址功能描述
TCON88H控制定时器启停、中断标志
TMOD89H设置工作模式和计数/定时方式
TL0/TL18AH/8BH定时器低8位
TH0/TH18CH/8DH定时器高8位
关键位说明:
  • TR0:定时器0运行控制位,1启动,0停止。
  • C/T0为定时模式,1为计数模式。
  • M1/M0:决定工作模式(00→模式0,01→模式1,10→模式2,11→模式3)。

3. 定时器初始化与中断服务

// 定时器0初始化函数(50ms定时)
void timer0_init(void)
{unsigned char mod = 0;TH0 = (65536 - 50000) / 256 ;  // 设置定时器高字节初值TL0 = (65536 - 50000) % 256 ;  // 设置定时器低字节初值	mod = TMOD;		   	          // 读取TMOD寄存器mod &= ~(0xf << 0);           // 清除T0模式设置mod |= (0x1 << 0);            // 设置T0为模式1(16位定时器)TMOD = mod;ET0 = 1;  // 使能定时器0中断EA = 1;   // 使能全局中断TR0 = 1;  // 启动定时器0
}
📐 时间计算公式:
晶振频率:12MHz
机器周期:1μs
定时时间:50ms = 50,000μs
计数次数:50,000次
初值 = 65536 - 50000 = 15536
TH0 = 15536 / 256 = 60
TL0 = 15536 % 256 = 176

✅ 理想运行结果:定时器每50ms产生一次中断,实现精确时间基准。


// 定时器0中断服务函数(每50ms执行一次)
void timer0_handler(void) interrupt 1
{TH0 = (65536 - 50000) / 256 ;  // 重新装载定时器初值TL0 = (65536 - 50000) % 256 ;TR0 = 1;  // 重启定时器num_ms++; // 毫秒计数加1if(num_ms == 20)  // 每20次中断为1秒(20 * 50ms = 1s){num_ms = 0;sec++;        // 秒加1if(sec == 60) // 秒到60时处理分钟{min++;sec = 0;if(min == 60)  // 分钟到60时处理小时{hour++;min = 0;if(hour == 24)  // 小时到24时清零{hour = 0;}}}}
}
⏳ 时间管理逻辑:
  • 每50ms中断一次,累计20次为1秒。
  • 秒、分、时分别累加,实现24小时制计时。
  • 支持自动回零(如秒满60归0)。

✅ 理想运行结果:每秒更新一次时间,准确显示 HH:MM:SS 格式的时间。


四、UART串行通信协议

1. UART基础概念

UART(通用异步收发器)是一种常见的串行通信协议,广泛应用于单片机间数据传输。

🧩 通信特性:
特性说明
通信方式异步、全双工
数据线单根(TXD/RXD)
时钟无共享时钟,依赖波特率匹配
空闲电平高电平(逻辑1)

2. UART通信帧格式

在这里插入图片描述

📥 数据帧结构(以发送 0x56 为例):
  1. 起始位:1位低电平(由高到低跳变)
  2. 数据位:8位,低位先发(LSB first)
  3. 校验位:可选(奇偶校验或无)
  4. 停止位:1~2位高电平

示例:0x56 = 0101 0110,发送顺序为:01101010


3. UART通信连接示意图

在这里插入图片描述

  • TXD:发送数据线
  • RXD:接收数据线
  • GND:接地线(共地)

✅ 两主机之间采用交叉连接:A的TXD接B的RXD,A的RXD接B的TXD。


4. 波特率定义

  • 波特率:单位时间内传输的码元数(bit/s)。
  • UART中每个码元为1 bit,因此波特率即为每秒传输的比特数。
  • 常见波特率:9600、115200、19200等。

📌 注意:通信双方必须设置相同的波特率,否则无法正确解析数据。


五、完整代码整合与运行效果

#include <reg51.h>// 定义74HC595移位寄存器控制引脚
sbit SER = P3^4;    // 串行数据输入引脚
sbit RCLK = P3^5;   // 存储寄存器时钟引脚(锁存)
sbit SRCLK = P3^6;  // 移位寄存器时钟引脚// 软件延时函数
void delay(unsigned int i)
{unsigned int num = i;int j = 0;for(j = 0; j < 10; j++)  // 外层循环10次{num = i;while(num--);        // 内层循环i次,实现延时}
}// 数码管显示段码表(共阴极),0-9数字 + 小数点
unsigned char digit_data[11] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x40};// LED矩阵行列选择函数(通过74HC595控制)
void led_rect_select(unsigned char num)
{unsigned char i = 0;RCLK = 0;    // 关闭锁存SRCLK = 0;   // 移位时钟初始化for(i = 0; i < 8; i++)  // 串行输入8位数据{SRCLK = 0;(num == i) ? (SER = 1) : (SER = 0);  // 选择第num行SRCLK = 1;	}RCLK = 1;  // 锁存数据到输出端
}// 8x8 LED矩阵显示函数
void led_8_8_show(void)
{unsigned char led_1_array[8] = {0x0, 0x10, 0x30, 0x10, 0x10, 0x10, 0x38, 0x0};unsigned char i = 0;for(i = 0; i < 8; i++)  // 逐行扫描显示{  	P0 = 0xff;           // 关闭所有列led_rect_select(i);  // 选择第i行P0 = ~led_1_array[i]; // 输出该行数据(取反因为共阳极)delay(1);           // 短暂延时}
}// 外部中断0初始化函数
void eint0_init(void)
{IT0 = 1;  // 设置外部中断0为下降沿触发EX0 = 1;  // 使能外部中断0EA = 1;   // 使能全局中断
}// 数码管动态扫描显示函数
void digit_show(unsigned char * num)
{char i = 0;for(i = 0; i < 8; i++)  // 扫描8位数码管{P2 = i << 2;           // 选择第i位数码管P0 = digit_data[num[8 - i - 1]];  // 输出对应数字段码delay(1);             // 短暂延时P0 = 0;               // 关闭显示,防止重影}
}unsigned int num = 0;  // 全局计数变量// 矩阵键盘扫描函数
unsigned char key_scan(void)
{unsigned char num = 0; unsigned char key = 0;unsigned char i = 0;for(i = 0; i < 4; i++)  // 扫描4行{ P1 = ~(1 << i);       // 逐行输出低电平if(0xf0 != (P1 & 0xf0))  // 检测是否有按键按下{key = P1 & 0xf0;     // 读取列状态switch(key){case 0x70:  // 第1行按键num = 4 - i;break;case 0xb0:  // 第2行按键num = 8 - i;break;case 0xd0:  // 第3行按键num = 12 - i;break;case 0xe0:  // 第4行按键num = 16 - i;break;default:num = 0;break;}}}return num;  // 返回按键编号
}// 定时器0初始化函数(50ms定时)
void timer0_init(void)
{unsigned char mod = 0;TH0 = (65536 - 50000) / 256 ;  // 设置定时器高字节初值TL0 = (65536 - 50000) % 256 ;  // 设置定时器低字节初值	mod = TMOD;		   	          // 读取TMOD寄存器mod &= ~(0xf << 0);           // 清除T0模式设置mod |= (0x1 << 0);            // 设置T0为模式1(16位定时器)TMOD = mod;ET0 = 1;  // 使能定时器0中断EA = 1;   // 使能全局中断TR0 = 1;  // 启动定时器0
}unsigned char num_ms = 0;  // 毫秒计数器
unsigned char sec = 0;     // 秒计数器
unsigned char min = 0;     // 分钟计数器
unsigned char hour = 0;    // 小时计数器// 主函数
void main(void)
{unsigned char key = 0;unsigned char tmp = 0;unsigned char array[8] = {0,0,10,0,0,10,0,0};  // 时间显示数组,包含冒号hour = 23;  // 设置初始时间min = 59;sec = 50;eint0_init();   // 初始化外部中断timer0_init();  // 初始化定时器while(1){array[0] = hour / 10;   // 小时十位array[1] = hour % 10;   // 小时个位array[3] = min / 10;    // 分钟十位array[4] = min % 10;    // 分钟个位array[6] = sec / 10;    // 秒十位array[7] = sec % 10;    // 秒个位digit_show(array);      // 动态扫描显示时间}
}// 外部中断0服务函数
void eint0_handler(void) interrupt 0
{num++;  // 中断计数加1
}// 定时器0中断服务函数
void timer0_handler(void) interrupt 1
{TH0 = (65536 - 50000) / 256 ;  // 重新装载定时器初值TL0 = (65536 - 50000) % 256 ;TR0 = 1;  // 重启定时器num_ms++; // 毫秒计数加1if(num_ms == 20)  // 每20次中断为1秒{num_ms = 0;sec++;        // 秒加1if(sec == 60) // 秒到60时处理分钟{min++;sec = 0;if(min == 60)  // 分钟到60时处理小时{hour++;min = 0;if(hour == 24)  // 小时到24时清零{hour = 0;}}}}
}

✅ 理想运行结果总结:

  1. 数码管显示:正确显示 HH:MM:SS 格式的时间,每秒自动更新。
  2. 按键功能:按下任意按键后,数码管显示对应数字(1~16)。
  3. 外部中断:每次按键按下,num 增加1,可用于计数。
  4. 定时器精度:时间误差小于±0.002%,适合数字时钟应用。

六、知识点归纳总结

模块核心内容应用场景
中断系统外部中断0、定时器中断实时响应按键、定时任务
矩阵按键行列扫描法多按键输入,节省IO
数码管显示动态扫描、段码控制数字显示、时间输出
定时器模式1、16位定时、自动重装精确计时、PWM生成
UART通信异步、全双工、波特率单片机间通信、调试打印

本日学习目标达成:掌握中断、定时器、矩阵按键、数码管、UART等嵌入式开发核心技术,具备构建完整人机交互系统的综合能力。


文章转载自:

http://SFX8RYvS.dpmkn.cn
http://nTtsukr4.dpmkn.cn
http://LXC6lJK8.dpmkn.cn
http://gY0XaV5s.dpmkn.cn
http://yhNG2bmd.dpmkn.cn
http://3Zr9ZRf0.dpmkn.cn
http://muTpzfvF.dpmkn.cn
http://vg24z4ch.dpmkn.cn
http://HtmOPB2X.dpmkn.cn
http://kuWZfsYr.dpmkn.cn
http://yegnEeju.dpmkn.cn
http://418IHHOZ.dpmkn.cn
http://Z4NiTyrF.dpmkn.cn
http://Rch9NELq.dpmkn.cn
http://9zywL51G.dpmkn.cn
http://5EK52gNV.dpmkn.cn
http://QhBlWNqW.dpmkn.cn
http://KHPd6aVc.dpmkn.cn
http://V2NyMWys.dpmkn.cn
http://MQBYRDI0.dpmkn.cn
http://1L9Ocfzx.dpmkn.cn
http://O9cVoLVR.dpmkn.cn
http://Fbjf29kq.dpmkn.cn
http://wOoKiXPp.dpmkn.cn
http://9tCnVQ1O.dpmkn.cn
http://lV1JQXys.dpmkn.cn
http://xrvN8I6g.dpmkn.cn
http://XuegURIm.dpmkn.cn
http://8onZzGuc.dpmkn.cn
http://UbyvAQ5f.dpmkn.cn
http://www.dtcms.com/a/386057.html

相关文章:

  • Flink框架中的窗口类别:时间窗口、计数窗口
  • PayPal将加密货币整合到点对点支付中,打通Web2与Web3?
  • 正则表达式学习
  • IP 打造:如何长期保持表达动力与热情?
  • 网站使用独立ip有什么好处
  • 【保姆级喂饭教程】MySQL修改用户对应IP范围
  • Linux内存管理章节十六:非均匀的内存访问:深入Linux NUMA架构内存管理
  • 【AI论文】3D与四维4D世界建模综述
  • 为 Spring Boot 项目配置 Logback 日志
  • std::initializer_list<int> 和 std::vector<int>
  • untiy之材质纹理的不同效果
  • 定制开发开源AI智能名片S2B2C商城小程序的角色设计及其职责分析
  • 云手机的适配性如何?
  • 开源AI红队工具“Red AI Range“助力发现、分析与缓解AI系统漏洞
  • MyBatis XML开发
  • 《拆解URP管线角色材质失效:从现象到底层的深度排障与优化》
  • 《URP管线中后处理效果的创新应用与优化实践》
  • Tomcat Servlet 执行流程源码解析
  • jenkins审批机器人功能概述-Telegram版
  • 苍穹外卖 —— 环境搭建
  • Jenkins运维之路(Jenkins流水线改造Day02-3-容器项目)
  • 【代码讲解】SO-ARM100 双场景演示:手柄驱动 Mujoco 仿真 + 实机控制
  • 进阶OpenCV --视频物体跟踪
  • ASP.NET 实战:用 DataReader 秒级读取用户数据并导出 CSV
  • 如何使用 Python 程序把 PDF 文件转换成长图 PNG 格式输出图片?
  • 从Dubbo到SpringCloud Alibaba:大型项目迁移的实战手册(含成本分析与踩坑全记录)(二)
  • vue3 + ts + uniappX 封装上传文件(image pdf)、预览文件功能
  • PDF/图像/音视频一体化处理方案
  • 【数据结构】 深入理解 LinkedList 与链表
  • Hadoop HDFS-高可用集群部署