ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

张毅刚单片机课后习题:Keil仿真验证定时器与串口初值

张毅刚单片机课后习题:Keil仿真验证定时器与串口初值 简介这份资料是《单片机原理及应用第三版》张毅刚教材的课后习题答案集面向高校电子信息、自动化、计算机等专业正在学习单片机课程的学生以及准备期末考试、考研复试或自学嵌入式入门的读者帮助解决课后练习无人批改、知识点零散难梳理的问题。资源包共1个PDF文件约662KB按章节组织填空、单选、判断对错、简答等题型的参考答案涵盖单片机基本概念、与普通微型计算机的差异、AT89S52特点、商用/工业/汽车/军用单片机温度分类、ISP与IAP编程方式、嵌入式系统及单片机与DSP、嵌入式微处理器的特点对比等核心考点并包含机器周期、位地址、堆栈与程序计数器等第2章的计算与寻址内容。目前已有2462人学习下载适合课后对答案、考前快速过重点、梳理章节框架时使用。1. 从一份对不上的课后答案说起很多人手里都有《单片机原理及应用第三版》张毅刚的电子版习题做完去搜答案出来的多半是别人整理的扫描件或者 Word题号对不上算出来的定时初值和波特率跟自己的还差一截。原因不复杂这本书从头到尾以 MCS-51 内核为主线习题里的结论高度依赖两个前提——晶振频率和机器周期。12MHz 和 11.0592MHz 下同一道定时题就是两套答案串口题更是如此。把课后习题当成可执行、可观测的验证题来做比对着答案抄一遍有用得多先判断题型再用 Keil 把程序跑起来最后拿寄存器状态反推计算过程。正在跟这门课、准备课程设计或者打蓝桥杯的人都能直接套这套流程。2. 单片机原理及应用(第三版)张毅刚课后习题的题型地图2.1 从目录倒推的六类考察点张毅刚这版教材的骨架大致是单片机概述与数制、MCS-51 硬件结构与引脚、指令系统与寻址方式、汇编语言程序设计、中断系统、定时器/计数器、串行口、并行扩展存储器与 I/O、A/D 与 D/A 接口、人机接口、应用系统设计。习题基本不会跳出这十来个模块只是换个包装。把题面里的动词认出来题型就定了问指出寻址方式是概念题给 A、R0、PSW 问执行结果是状态推演题问定时 50ms 初值是多少是计算题给 2764、6264 和 74LS373 的连线图问地址范围是硬件题。题型高频考点判分关键自查手段概念与填空引脚第二功能、寄存器位定义、寻址方式位编号和名称写全对照芯片手册的 SFR 表状态推演ADD/SUBB/DA 对 PSW 的影响CY、AC、OV、P 四个标志Keil 单步看 PSW计算题机器周期、定时初值、波特率、地址范围晶振频率与计数位数用脚本或表格复算程序阅读汇编片段的功能与执行次数循环次数与堆栈变化反汇编加断点计数硬件连接存储器扩展、数码管、按键、ADC0809片选逻辑与地址线位数画地址译码表综合设计交通灯、电子钟、温度采集主循环与中断的分工分模块单独调通概念题里最容易丢分的是引脚第二功能P3.0 和 P3.1 是 RXD 与 TXDP3.2 和 P3.3 是 INT0 与 INT1P3.4 和 P3.5 是 T0 与 T1P3.6 和 P3.7 是 WR 与 RD写的时候要和 P0、P2 的总线复用区分开。状态推演题则要记住加法看位 7 和位 3 的进位减法看借位方向DA A 只对压缩 BCD 码做调整而且只跟在 ADD 或 ADDC 之后用。2.2 同一道题答案对不上通常是这四处第一处是晶振频率。教材默认 12MHz 的地方不少但一到串口题就必须换成 11.0592MHz否则 9600bps 根本除不整。第二处是版本差异第三版对部分章节做过调整早期以 8031 为主讲外部扩展后来更多用 89C51、89S52 甚至 STC89C52RC 举例题号跟着变。第三处是计数位数定时器方式 0 是 13 位最大计数值 8192很多人顺手写成 65536整道题就废了。第四处是汇编伪指令写法不同教材在 ORG、DB、EQU 的摆放上不完全一致用 A51 汇编器直接编译会报错但逻辑本身没错。提示遇到答案和自己算的不一致先确认题目给的晶振频率和定时器工作方式这两项一定初值就唯一了。2.3 一段脚本把定时初值和波特率一次算清手算容易错在进制转换写个小脚本复算最省事。# 51 单片机定时初值与串口波特率复算 def timer_init(fosc_mhz, time_us, mode1): fosc_mhz: 晶振频率(MHz); time_us: 定时时间(us); mode: 0/1/2 machine_cycle 12 / fosc_mhz # 1 个机器周期的时间(us) counts int(round(time_us / machine_cycle)) # 需要的计数值 bits {0: 13, 1: 16, 2: 8}[mode] # 方式013位, 方式116位, 方式28位 max_count 1 bits if counts max_count: return None # 超出最大定时, 需要软件计数或降频 init max_count - counts if mode 2: return init, init # 方式2: TH 存重装值, TL 计数 if mode 0: return (init 8) 0x1F, init 0xFF # 13 位: 高 5 位进 TH, 低 8 位进 TL return (init 8) 0xFF, init 0xFF def baud_init(fosc_mhz, baud, smod0): 方式1/3 波特率 2^SMOD/32 * fosc/(12*(256-TH1)) th1 int(round(256 - fosc_mhz * 1e6 / (384 * (2 ** smod) * baud))) real fosc_mhz * 1e6 / (384 * (2 ** smod) * (256 - th1)) return hex(th1), round((real - baud) / baud * 100, 2) # 初值, 相对误差(%) print(timer_init(12, 50000, 1)) # (0x3C, 0xB0) 50ms12MHz 方式1 print(timer_init(12, 200, 2)) # (0x38, 0x38) 200us12MHz 方式2 print(baud_init(11.0592, 9600)) # (0xfd, 0.0) 误差为零 print(baud_init(12, 9600)) # (0xfd, 8.5) 12MHz 下不推荐timer_init里machine_cycle用的是 12 分频这是 MCS-51 的标准机器周期1 个机器周期等于 12 个振荡周期。counts max_count这个判断是定时题的分水岭方式 2 最大只能定时 256 个机器周期12MHz 下就是 256μs超过必须换方式或者用软件计数扩展。baud_init返回的第二个值是相对误差超过 2% 就要考虑换晶振或改 SMOD实测里 9600bps 配 12MHz 误差在 8% 以上通信会大量丢帧这也是串口题几乎都用 11.0592MHz 的原因。2.4 一套可复用的自查流程抄题时把已知量单独列出来尤其是晶振频率、工作方式、计数位数这三项。计算题先用上面的脚本复算一遍再和手算结果比对差在末位通常是四舍五入方向的差别。程序题把代码整段贴进 Keil编译通过后单步执行观察寄存器窗口里 A、R0、PSW 的变化。硬件题画出地址译码表把每个片选信号对应的地址区间列出来空白地址区也要标清楚。综合设计题拆成初始化—主循环—中断服务三段分段调通再合并。这套流程做出来的不是一份抄来的答案而是一份能跑、能对着波形核对的结果。后面几章全部围绕怎么把题跑起来。3. 用 Keil 把单片机原理及应用课后习题跑成可验证的代码3.1 建工程前必须定下的三个参数Keil μVision 里 Project 菜单新建工程器件选 Atmel 目录下的 AT89C52弹窗问是否复制 STARTUP.A51 选是。这一步做完别急着写代码先在 Options for Target 里把参数对齐否则仿真出来的时间和题目对不上。配置项位置推荐值对习题的影响器件Device 选择框AT89C52决定可用 SFR 与 Flash、RAM 容量晶振Target 选项卡 Xtal按题目给多为 12MHz影响仿真时间与定时初值HEX 输出Output 选项卡勾选 Create HEX File没有 HEX 就没法在仿真软件里加载内存模式Target 选项卡 Memory ModelSmall变量默认放内部 RAM与寻址方式题相关优化级别C51 选项卡默认 8 级过高优化可能改掉延时循环的循环次数Small 模式下变量默认放在内部 RAM用 MOV 指令直接寻址换成 Compact 会用 DPTR 间接寻址Large 则落到 xdata 区。习题里问变量存放在哪、用哪条指令访问和这个设置直接对应。3.2 定时器 50ms 那道典型题的完整代码#include reg52.h // 52 系列 SFR 定义, 89C51 用 reg51.h 亦可 void Timer0_Init(void) { TMOD 0xF0; // 清掉 T0 的 4 个位, 保留 T1 的设置 TMOD | 0x01; // T0 方式1, 16 位定时, 内部时钟 TH0 0x3C; // 初值高 8 位: 65536-50000 15536 0x3CB0 TL0 0xB0; // 初值低 8 位 EA 1; // 开总中断 ET0 1; // 开定时器 0 中断 TR0 1; // 启动计数 } void main(void) { Timer0_Init(); while (1) { } } void Timer0_ISR(void) interrupt 1 { TH0 0x3C; // 方式1 不自动重装, 必须手动补初值 TL0 0xB0; P1 ^ 0x01; // P1.0 翻转, 示波器上应是 20ms 高 20ms 低 }12MHz 下一个机器周期 1μs定时 50ms 需要 50000 个计数方式 1 是 16 位加计数从初值加到 65536 溢出所以初值 65536-5000015536即 0x3CB0。TMOD 0xF0先清低四位避免把 T1 的设置冲掉这是很多人漏掉的一步。方式 1 不自动重装中断里必须重新写 TH0、TL0否则第二次定时长度就错了。P1.0 翻转后的方波周期是 100ms、占空比 50%拿这个波形可以直接验证初值算得对不对。题目要求 1s 定时时方式 1 单次不够最大只能到 65536μs 左右需要在中断里加软件计数。static unsigned char cnt 0; // 软件计数, 累计 20 次得 1s void Timer0_ISR(void) interrupt 1 { TH0 0x3C; // 每次都要补初值, 顺序先高后低 TL0 0xB0; if (cnt 20) { cnt 0; P1 ^ 0x01; } }3.3 串口 9600bps 初始化与常用初值表void Uart_Init(void) // 11.0592MHz, 9600bps, 8 位数据, 允许接收 { TMOD 0x0F; TMOD | 0x20; // T1 方式2, 8 位自动重装, 作波特率发生器 TH1 0xFD; // 重装值, 见下表 TL1 0xFD; SCON 0x50; // 方式1(10位UART), REN1 允许接收 PCON 0x7F; // SMOD0, 波特率不加倍 TR1 1; // 启动 T1, 波特率时钟开始输出 ES 1; EA 1; } void Uart_SendByte(unsigned char dat) { SBUF dat; // 写入 SBUF 即启动发送 while (!TI); // 等发送完成 TI 0; // 发送标志必须软件清零 }晶振SMOD波特率TH1(TL1)相对误差11.0592MHz096000xFD011.0592MHz048000xFA011.0592MHz024000xF4011.0592MHz1192000xFD012MHz024000xF3约 0.16%12MHz096000xFD约 8.5%实际不可用SCON 的 0x50 拆开看是 SM0SM101方式 1、SM20、REN1、TB8 与 RB8 不用、TI 与 RI 初始为 0每一位在习题里都可能被单独提问。注意T1 作波特率发生器时只能用方式 2因为方式 2 自动重装不需要在中断里补初值换成方式 1 会让波特率在第一次溢出后就漂移。3.4 仿真里怎么把答案读回来Keil 里 Debug 菜单启动调试会话Peripherals 菜单下能打开 Timer、Serial、Interrupt 三个观察窗实时看 TH0、TL0、TR0、SCON、SBUF 的值。Watch 窗口里手动加 A、PSW、DPTR单步执行时逐条对照习题给的初始状态比背答案靠谱得多。要做波形验证就把编译出的 HEX 加载进仿真软件虚拟示波器接到 P1.0测出的周期除以 2 就是定时时间的实测值。4. 存储器扩展、中断优先级与串口通信三类难题的落地写法4.1 存储器扩展题的地址范围计算套路P0 口分时复用低 8 位地址需要靠 ALE 信号配 74LS373 锁存P2 口提供高 8 位地址片选可以走线选也可以走 74LS138 译码。以 62648K×813 根地址线 A0 到 A12为例用 P2.7 直接接 CE 时只要 A15 为 0 就选中A13、A14 与结果无关于是同一片芯片会映射到多个地址区这就是地址重叠改用 74LS138把 A15、A14、A13 接到 C、B、A 三个输入端8 个输出正好切分 64KB 空间。译码方式片选来源有效地址线地址范围线选P2.7 接 CE低有效A0–A120000H–1FFFH存在重叠区译码 Y0A15A14A13 000A0–A120000H–1FFFH译码 Y4A15A14A13 100A0–A128000H–9FFFH译码 Y7A15A14A13 111A0–A12E000H–FFFFHORG 0000H LJMP MAIN ORG 0030H MAIN: MOV DPTR, #0000H ; 指向片外 RAM 首地址 0000H MOV R6, #20H ; 外层循环 32 次 CLR A LOOP1: MOV R7, #00H ; 内层计 256 次(DJNZ 减到 0 退出) LOOP2: MOVX DPTR, A ; 写一个字节, WR 由硬件自动拉低 INC DPTR ; 16 位地址指针加一 DJNZ R7, LOOP2 DJNZ R6, LOOP1 SJMP $ ; 停在原地, 便于在仿真里查看结果 ENDMOVX DPTR,A 会触发 WR 有效MOVX A,DPTR 触发 RD 有效片选由外部译码电路负责软件只管地址和数据。32 乘 256 等于 8192正好把 8KB 空间清零。有人会问为什么不用 MOVX R0,A因为 R0 只能给低 8 位地址页外空间访问不了。4.2 IP 寄存器决定的中断嵌套顺序MCS-51 有 INT0、T0、INT1、T1、串口五个中断源同级内部优先级固定为 INT0 高于 T0T0 高于 INT1INT1 高于 T1T1 高于串口。IP 里把某一位置 1就把它提到高优先级高优先级可以打断低优先级的中断服务形成嵌套。中断源中断号标志位默认优先级IP 位INT00IE0最高PX0T01TF0次高PT0INT12IE1中PX1T13TF1次低PT1串口4RI/TI最低PSvoid main(void) { IP 0x04; // PS1, 串口提到高优先级, 可打断其他中断服务 EX0 1; IT0 1; // INT0 下降沿触发, 边沿标志由硬件自动清 EA 1; while (1) { } } void Int0_ISR(void) interrupt 0 { /* 低优先级服务程序, 执行时间尽量短 */ }标志位的清除方式是这类题的固定考点边沿触发的外部中断标志由硬件自动清电平触发的外部中断要靠外部电路拉高引脚来清定时器溢出标志 TF0、TF1 必须软件清串口的 RI、TI 也必须软件清。4.3 双机通信题的收发流程#define FRAME_HEAD 0xAA void Uart_ISR(void) interrupt 4 { unsigned char dat; if (RI) // 接收到一个字节 { RI 0; // 接收标志必须软件清零, 否则反复进入中断 dat SBUF; // 读走数据, 读取动作本身也会影响标志 if (dat FRAME_HEAD) { /* 帧头命中, 后续按约定解析长度与校验 */ } } if (TI) // 发送完成 { TI 0; } }收发共用一个中断号 4靠 RI 和 TI 两个标志位区分方向所以中断服务函数里两个 if 都要写。加上帧头是为了防止接收方把上电噪声当成有效数据帧头之后一般再跟长度字节和校验字节。5. 用寄存器窗口和汇编对照压掉最后那点误差Keil 调试会话里的 Peripherals 菜单是最容易被忽略的工具。Timer 观察窗能看到 TH0、TL0 随计数实时变化Timer0 打开时 TR0 位会亮起来Interrupt 窗口能直接看到 IE0、TF0、RI 这些标志位被硬件置位和软件清零的瞬间。把断点下在中断服务函数第一行用 Registers 窗口对比进入前后的 PSW 和 DPTR习题里那些执行后 A 等于多少的问题就有了实证。编译器优化是另一处暗坑。C 语言写的延时循环在 Options for Target 的 Listing 选项卡里勾上 Assembly Code编译后会生成 .lst 文件能看到循环到底保留了几条指令。优化级别调到 9 级时某些空循环会被整体删掉延时凭空消失这时要么降低优化级别要么在循环变量前加 volatile。现象常见原因排查动作P1.0 无输出漏了 EA1 或 TR01Watch 窗口看 EA、TR0定时时间翻倍中断里没重装 TH0、TL0检查 ISR 是否补初值串口乱码晶振与初值不匹配复算波特率误差超 2% 换晶振中断进不去触发方式不对或标志未清看 IT0 与 IE0 的状态数码管闪烁主循环里做了长延时把刷新挪进定时中断在真实板子上验证时STC89C52RC 用下载工具烧写要先选对串口号再断电重新上电触发冷启动握手直接点下载不动是常见现象。如果题目来自蓝桥杯那种板子注意板载晶振多为 12MHz数码管走 74HC138 加 74HC573 两级驱动写段码前必须先选通译码通道否则数据会锁到别的位上。每算出一个初值按晶振、工作方式、定时时间三列记进自己的参数表下一次遇到同类题或者接手新项目查表比重新推一遍快得多。本文还有配套的精品资源点击获取
返回列表