import os, pythoncom, win32com.client as win32
# ---------------------- 工具函数 ----------------------
def get_or_add_style(doc, name):
try:
return doc.Styles(name)
except:
return doc.Styles.Add(Name=name, Type=1) # 1=段落样式
# ---------------------- 主程序 ----------------------
pythoncom.CoInitialize()
try:
app = win32.Dispatch("Word.Application")
except:
app = win32.Dispatch("Kwps.Application")
app.Visible = True
doc = app.Documents.Add()
# Word 常数
wdCollapseEnd = 0
wdStory = 6
wdWord9TableBehavior = 1
wdAutoFitContent = 1
wdFormatDocumentDefault = 12
# ---------- 1. 建立样式 ----------
def make_styles():
# 实验标题
s = get_or_add_style(doc, "濮工_实验标题")
s.Font.NameFarEast = s.Font.Name = "黑体"
s.Font.Size = 18
s.Font.Bold = True
pf = s.ParagraphFormat
pf.Alignment = 1 # 居中
pf.SpaceBefore = 18
pf.SpaceAfter = 12
pf.LineSpacingRule = 0 # 单倍
# 一级标题
s = get_or_add_style(doc, "濮工_一级标题")
s.Font.NameFarEast = s.Font.Name = "黑体"
s.Font.Size = 14
s.Font.Bold = True
pf = s.ParagraphFormat
pf.Alignment = 0
pf.SpaceBefore = 12
pf.SpaceAfter = 6
pf.LineSpacingRule = 0
# 二级标题
s = get_or_add_style(doc, "濮工_二级标题")
s.Font.NameFarEast = s.Font.Name = "黑体"
s.Font.Size = 12
s.Font.Bold = True
pf = s.ParagraphFormat
pf.Alignment = 0
pf.SpaceBefore = 6
pf.SpaceAfter = 3
pf.LineSpacingRule = 0
# 正文
s = get_or_add_style(doc, "濮工_正文")
s.Font.NameFarEast = s.Font.Name = "宋体"
s.Font.Size = 10.5
pf = s.ParagraphFormat
pf.FirstLineIndent = app.CentimetersToPoints(0.74) # 2 字符
pf.LineSpacingRule = 3 # 多倍
pf.LineSpacing = 1.25 # 1.25 倍
pf.Alignment = 0 # 默认居左,避免继承公式的居中
# 图题注
s = get_or_add_style(doc, "濮工_图题注")
s.Font.NameFarEast = s.Font.Name = "宋体"
s.Font.Size = 9
pf = s.ParagraphFormat
pf.Alignment = 1
pf.SpaceAfter = 6
pf.LineSpacingRule = 0
# 表题注
s = get_or_add_style(doc, "濮工_表题注")
s.Font.NameFarEast = s.Font.Name = "宋体"
s.Font.Size = 9
pf = s.ParagraphFormat
pf.Alignment = 1
pf.SpaceBefore = 6
pf.LineSpacingRule = 0
make_styles()
# ---------- 2. 顺序写内容 ----------
rng = doc.Range()
rng.Collapse(wdCollapseEnd)
def write(style, text, newline=True):
rng.Style = style
rng.Text = text
if newline:
rng.InsertParagraphAfter()
rng.Collapse(wdCollapseEnd)
# 标题
write(doc.Styles("濮工_实验标题"), "实验13 单片机流水灯实验")
# 一、实验目的
write(doc.Styles("濮工_一级标题"), "一、实验目的")
for t in ["1. 掌握51系列单片机(如STC89C52)的基本工作原理及IO口控制方法;",
"2. 学会使用Keil C51软件编写简单的单片机控制程序,掌握程序下载流程;",
"3. 理解流水灯的实现逻辑,能通过硬件连接和软件调试实现LED灯的循环点亮效果;",
"4. 熟悉面包板、导线、电阻等元件的使用,提升硬件搭建与故障排查能力。"]:
write(doc.Styles("濮工_正文"), t)
# 二、实验仪器
write(doc.Styles("濮工_一级标题"), "二、实验仪器")
for t in ["1. 核心设备:STC89C52单片机开发板(或最小系统板)、USB下载线、Keil C51软件(V4.7及以上版本);",
"2. 电子元件:红色LED灯(8个)、1kΩ限流电阻(8个)、面包板(1块)、杜邦线(20根,含公对母、公对公);",
"3. 辅助工具:万用表(1台,用于检测电路通断)、镊子(1把);",
"4. 示意图:"]:
write(doc.Styles("濮工_正文"), t)
write(doc.Styles("濮工_图题注"), "图13-1 单片机流水灯实验硬件连接示意图")
# 三、实验原理
write(doc.Styles("濮工_一级标题"), "三、实验原理")
write(doc.Styles("濮工_二级标题"), "1. 单片机IO口控制原理")
write(doc.Styles("濮工_正文"), "51系列单片机的P1口为8位准双向IO口,可直接作为输出口使用。当IO口输出低电平时(接近0V),外接LED灯通过限流电阻形成回路,LED点亮;当IO口输出高电平时(接近5V),回路中电流极小,LED熄灭。通过控制P1口各引脚的高低电平变化,可实现LED灯的亮灭控制。")
write(doc.Styles("濮工_二级标题"), "2. 流水灯实现逻辑")
write(doc.Styles("濮工_正文"), "采用“循环移位+延时”的方式实现流水灯效果:通过程序让P1口的低电平信号按固定顺序(如从P1.0到P1.7,即左移;或从P1.7到P1.0,即右移)依次切换,每次切换后保持一定时间(由延时函数实现),从而形成“流水”视觉效果。")
write(doc.Styles("濮工_二级标题"), "3. 核心公式(延时时间计算)")
write(doc.Styles("濮工_正文"), "若单片机晶振频率为11.0592MHz(常用频率),机器周期T机 = 12/f晶振,其中f晶振为晶振频率。延时函数的延时时间t可近似表示为:")
# 核心修正:公式段处理(居中)
formula_rng = rng.Duplicate # 复制当前光标位置,专门用于公式
formula_rng.Style = doc.Styles("濮工_正文")
formula_rng.ParagraphFormat.Alignment = 1 # 公式段居中
formula_rng.Text = "t ≈ N × T机 "
formula_rng.InsertParagraphAfter()
# 光标移到公式下方,恢复居左
rng.MoveEnd(Unit=wdStory)
rng.Collapse(wdCollapseEnd)
rng.ParagraphFormat.Alignment = 0 # 确保后续文本居左
# 写入“式中N…”,此时已默认居左
write(doc.Styles("濮工_正文"),
"式中N为延时函数中指令执行的总周期数,本实验中通过Keil软件自动计算,设定单次延时时长为500ms,确保流水效果清晰可见。")
# 四、实验内容及步骤
write(doc.Styles("濮工_一级标题"), "四、实验内容及步骤")
write(doc.Styles("濮工_二级标题"), "1. 实验前准备")
for t in ["(1)硬件准备:检查单片机开发板、LED灯、电阻等元件是否完好;用万用表检测限流电阻阻值是否为1kΩ,排除损坏元件;",
"(2)软件准备:在电脑上安装Keil C51软件,完成软件注册与环境配置(如选择“Atmel”→“AT89C52”芯片型号);",
"(3)电路连接:将单片机P1口(P1.0~P1.7)通过杜邦线接面包板,LED正极经1kΩ电阻接5V电源,负极接对应P1口引脚,确保电源正负极无接反。"]:
write(doc.Styles("濮工_正文"), t)
# 五、数据及处理结果
write(doc.Styles("濮工_一级标题"), "五、数据及处理结果")
write(doc.Styles("濮工_表题注"), "表13-1 单片机流水灯实验数据记录表")
# 插入表格
table_rng = rng.Duplicate
table = doc.Tables.Add(table_rng, 4, 6, AutoFitBehavior=wdAutoFitContent)
table.Borders.Enable = True
# 表头
hdr = ["实验序号", "流水模式", "延时时长(ms)", "LED点亮顺序", "实验现象描述", "是否正常"]
for i, h in enumerate(hdr, 1):
table.Cell(1, i).Range.Text = h
# 数据
rows = [["1", "左移", "500", "P1.0→P1.1→…→P1.7", "8个LED从左到右依次循环点亮", ""],
["2", "左移(修改延时)", "200", "P1.0→P1.1→…→P1.7", "流水速度加快,点亮间隔缩短", ""],
["3", "右移(修改程序)", "500", "P1.7→P1.6→…→P1.0", "8个LED从右到左依次循环点亮", ""]]
for r, row_data in enumerate(rows, 2):
for c, txt in enumerate(row_data, 1):
table.Cell(r, c).Range.Text = txt
# 居中
for r in range(1, 5):
for c in range(1, 7):
table.Cell(r, c).Range.ParagraphFormat.Alignment = 1
# 光标移到表格下方
app.Selection.EndKey(Unit=wdStory)
app.Selection.InsertParagraphAfter()
rng = app.Selection.Range
write(doc.Styles("濮工_正文"), "数据处理与分析:")
write(doc.Styles("濮工_正文"), "1. 对比实验1和2:延时时长从500ms缩短至200ms,流水周期从4s(8×500ms)缩短至1.6s(8×200ms),验证“延时时长与流水速度成反比”;")
write(doc.Styles("濮工_正文"), "2. 实验3通过修改程序中“led << 1”为“led >> 1”,实现右移流水,说明单片机IO口控制的灵活性。")
# 六、思考题
write(doc.Styles("濮工_一级标题"), "六、思考题")
for t in ["1. 单片机IO口输出高电平时LED不亮,输出低电平时LED亮,其电路原理是什么?若将LED的正负极接反,会出现什么现象?",
"2. 若想实现“LED灯闪烁2次后再流水”的效果,需要在程序中添加哪些逻辑?",
"3. 实验中1kΩ限流电阻的作用是什么?若去掉限流电阻直接连接LED,可能会导致什么问题?"]:
write(doc.Styles("濮工_正文"), t)
# ---------- 保存 ----------
save_path = os.path.join(os.path.expanduser("~"), "Desktop", "单片机流水灯实验讲义2.docx")
doc.SaveAs(save_path, FileFormat=wdFormatDocumentDefault)
print("已生成:", save_path)