☰
ATtiny13 8脚AVR单片机:资源边界、ISP烧录与裸机开发指南
2026/10/25 6:41:40 网站建设 项目流程

简介:面向嵌入式初学者与小型控制项目开发者的ATtiny13开发板资料包,围绕这款8引脚AVR超小型单片机(1KB闪存、64B SRAM、128B EEPROM、内置8MHz RC振荡器)组织内容,可用于智能家居节点、物联网终端与教学实验等低功耗微型控制场景。包内共237个文件,以C语言源码、汇编与工程构建文件(.c、.mak、.prj、.hex)、编译中间产物(.o、.lst、.lis、.cof)以及2份PDF为主,另有若干工程目录与压缩子包,涵盖LED闪烁、按键检测、定时器、PWM调光、ADC采样等实例,便于对照原理图逐项验证。其中开发板原理图说明电源管理、编程接口与扩展电路,中文数据手册覆盖寄存器、指令集与应用注意事项,ISP及ArduinoISP下载方式也有相应工程可供参考。已有280人学习下载,适合希望从点亮第一个LED到独立完成小型嵌入式设计的读者系统查阅。

1. 一块 1KB 闪存的 8 脚芯片,凭什么还值得单独打一版板子

去年帮朋友改一个小夜灯方案,要求是插在 5V 充电头上就能跑、BOM 成本压到一块多、板子塞进直径 25 毫米的圆壳里。选型绕了一圈,能同时满足「8 引脚封装」「内置振荡器不用外挂晶振」「5.5V 以内宽压供电」「单价几毛」这几条的,选项其实不多。ATtiny13 就是其中之一:1KB 闪存、64 字节 SRAM、64 字节 EEPROM,出厂默认 1.2MHz 内部 RC 时钟,8 个脚里 6 个能当 IO。

它的能力边界得先摆明白。没有硬件串口,没有硬件 SPI/I2C 主机,SRAM 少到连一个 64 字节的接收缓冲都放不下。所以拿它做通信网关、做屏幕驱动、做需要浮点运算的场合,基本是自找麻烦。它真正的甜区是替代 555 定时器、替代一堆分立逻辑门、做单键开关机、做 PWM 调光、做低占空比的传感器唤醒采集——一句话,做那些「逻辑简单但要极小极便宜」的活。

后面这条路径按「先看清资源边界 → 再搭烧录链路 → 写裸机代码 → 排掉那批反复出现的坑」铺开,新手上手能照着复现,熟手能对着参数和边界判断手上这活到底该不该用它。

2. 引脚、时钟和 64 字节 SRAM:ATtiny13 的资源到底紧到什么程度

很多人第一次拿到 ATtiny13 开发板,看原理图上标着 PB0 到 PB5 六个 IO,就默认这六个都能随便用。真跑起来才发现:有一个脚改完配置就再也烧不进去,有一个脚被 ISP 占着,还有两个脚做 ADC 的时候会跟别的东西打架。这一章先把这几个硬约束说死,后面写代码才不至于边写边返工。

2.1 8 个引脚里真正能给你用的有几个

DIP-8 封装的 ATtiny13 引脚排列是固定的:1 脚 PB5/RESET,2 脚 PB3,3 脚 PB4,4 脚 GND,5 脚 PB0/MOSI,6 脚 PB1/MISO,7 脚 PB2/SCK,8 脚 VCC。SOIC-8 封装编号一致,只是封装形式不同。

PB5 是第一个陷阱。它出厂就是复位脚,内部带上拉,正常状态下不能当普通 IO 用。想把它变成 PB5 数字 IO 或者 ADC0 通道,必须烧写RSTDISBL熔丝位。这个位一旦编程,常规的 ISP 烧录通道就彻底关闭了——因为 ISP 进编程模式靠的正是把 RESET 拉低到 12V 以下再发命令,复位功能没了,ISP 也就没了。恢复只能靠高压串行编程(HVSP)那套专门的时序和 12V 电源,普通 USBasp 干不了这活。所以除非板子上预留了 HVSP 排针,否则不建议第一次就动这个位。

PB3 和 PB4 的隐藏身份是晶振脚。默认内部 RC 时钟配置下,它们就是普通 IO,可以接 LED、接按键、接继电器。但只要把熔丝位改成外部晶振模式,这两个脚立刻被 XTAL1/XTAL2 占用,再想当 IO 用就没门了。8 脚封装本来就缺脚,把仅剩的两个换成晶振,实际可用的 IO 会掉到 4 个,这个账要提前算。

PB0、PB1、PB2 是 ISP 三根线:MOSI、MISO、SCK。烧录的时候它们被编程器占着,烧完之后完全释放,可以正常当 IO。但要留个心眼——如果这三个脚上外接了强上拉或者强下拉(比如直接驱动继电器线圈),烧录时编程器可能拉不动,表现为「有时候能烧有时候烧不进」。

PB2 是唯一的外部中断脚(INT0)。做按键唤醒、做旋转编码器计数,基本只能落在它身上。PB1 和 PB0 分别是定时器 0 的 OC0B 和 OC0A 输出,做硬件 PWM 只能从这两个脚出。

把 ADC 通道也一起理清:ADC0 对应 PB5,ADC1 对应 PB2,ADC2 对应 PB4,ADC3 对应 PB3。注意 ADC0 和复位脚是同一个物理脚,ADC1 和 SCK 是同一个物理脚。所以「用 PB2 做 ADC 采集 + 同时用 PB2 做 ISP 烧录」这件事不冲突,但「一边烧录一边采」就别想了。

汇总成一张表:

物理脚端口默认功能复用能力使用代价
1PB5RESETADC0、数字 IO需烧 RSTDISBL,烧完 ISP 失效
2PB3数字 IOADC3、XTAL1改外部晶振后不可用
3PB4数字 IOADC2、XTAL2改外部晶振后不可用
4GND地——
5PB0数字 IOMOSI、OC0A烧录时被占用
6PB1数字 IOMISO、OC0B烧录时被占用
7PB2数字 IOSCK、ADC1、INT0烧录时被占用
8VCC电源——

实际做产品的时候,我一般把 PB5 保留成复位脚不动,PB3/PB4 当普通 IO 用,PB0/PB1 留给 PWM 输出,PB2 留给按键中断或者 ADC。这样六个脚里能用满五个,剩下一个当保险。

2.2 内部 RC 时钟与 CKDIV8:为什么延时总差 8 倍

ATtiny13A 出厂默认的低熔丝位是0x6A。这个值的含义是:时钟源选内部 9.6MHz RC 振荡器,同时CKDIV8分频器处于使能状态,9.6MHz 被除以 8,系统时钟实际跑在 1.2MHz。

这就解释了新手最常撞上的那个现象:代码里写_delay_ms(1000),拿秒表一掐,实际延迟了 8 秒。原因不是_delay_ms不准,而是编译时的F_CPU宏写的是 9600000,芯片实际只跑 1200000,所有基于时钟周期的延时、波特率、PWM 频率全部按 8 倍比例缩短或拉长。

要解决只有两条路。一条是把F_CPU老老实实写成 1200000UL,接受 1.2MHz 的低速运行;另一条是把低熔丝位改成0x7A,关掉CKDIV8,让系统时钟跑满 9.6MHz。改熔丝位之后必须断电重新上电才生效,这一点经常被忽略——改完直接烧程序,程序还是按旧时钟跑,人会以为是熔丝没写进去。

内部 RC 的精度得说清楚。出厂校准条件是 25℃、3V 供电,标称偏差在 ±10% 这个量级。温度每变化几十度、供电电压每变化 1V,频率都会跟着漂。所以:

  • 做按键消抖、做 PWM 调光、做几秒量级的定时,完全够用;
  • 做软件模拟串口、做红外接收解码这类对时序敏感的活,基本会翻车;
  • 做精确计时(比如做时钟),必须外挂 32.768kHz 晶振或者用外部时钟源。

有些人看到「支持最高 20MHz」就想超频。ATtiny13A 的速度等级是按供电电压分档的:1.8V 时最高 4MHz,2.7V 时最高 9.6MHz,4.5V 以上才能跑 20MHz。3.3V 供电下强行设 20MHz,芯片能起来,但高温或者电压跌落时会随机死机,这种玄学问题排查起来能耗掉一整天。

2.3 64 字节 SRAM 与 1KB 闪存的分配账

64 字节 SRAM 是什么概念?一个float占 4 字节,一个uint16_t占 2 字节,函数调用一次压栈要吃掉 2 到 4 字节(返回地址加保存的寄存器)。稍微写复杂一点,栈就能把全局变量区踩穿,表现是变量莫名其妙变成 0 或者随机值——这就是典型的栈溢出,而且不会报错,纯靠现象猜。

几条硬性习惯,照着做能少掉很多头发:

第一,不要用printf和任何浮点运算。printf光格式化那套代码就能吃掉几百字节 flash,运行时还要几十字节栈,1KB 闪存的芯片根本扛不住。需要输出调试信息的时候,用「点亮某个 LED 表示走到哪一步」这种最原始的办法。

第二,能用uint8_t就别用int。AVR 是 8 位机,int是 16 位,所有算术都要走两步。更重要的是 RAM 占用直接翻倍。

第三,缓冲区能省则省。想做一个 16 个采样的滑动平均?16 个uint8_t就是 16 字节,占掉 SRAM 的四分之一。要么改成 8 个采样,要么改成「指数加权平均」,只存一个累加值。

第四,函数调用层级不要太深。每层调用至少吃 2 字节栈,嵌套四五层就是十几个字节没了。

代码体积这边,给几个实测参考值(avr-gcc -Os编译,不含库):

功能组合大致 flash 占用
纯 GPIO 闪灯 +_delay_ms150 ~ 250 字节
加定时器 PWM 呼吸灯300 ~ 400 字节
再加一路 ADC 采样 + 简单滤波500 ~ 700 字节
再加掉电睡眠 + 看门狗唤醒700 ~ 900 字节
软件 I2C 从机 + 上述全部接近或超过 1024 字节

最后一行不是危言耸听。软件模拟 I2C 从机要处理起始条件、地址匹配、ACK、时钟拉伸,代码量很容易上 300 字节。1KB 闪存看着还有点余量,实际写完一编译就爆。

编译完顺手查一下占用,是个必做动作:

# 编译完立刻看 flash / RAM 占用,别等烧进去才发现爆了 avr-size --format=avr --mcu=attiny13a firmware.elf

输出里Program那一行是 flash,Data那一行是 RAM。经验值是 flash 用到 90% 以上就开始出现「加一行代码就编译不过」的诡异现象,RAM 用到 70% 以上就要警惕栈溢出。留 10% 余量是底线,不是浪费。

3. 搭一条可靠的 ATtiny13 烧录链路:ISP 接线、熔丝位与 avrdude 命令

资源账算清楚,接下来就是把这颗芯片跟电脑连起来。ATtiny13 没有 USB、没有 bootloader,唯一的入口就是 ISP。这条链路只要有一根线接反、一个时钟参数没设对,就会表现为「编程器识别不到芯片」,而且报错信息极其含糊,不给任何方向。这一章把接线、命令序列和 Arduino IDE 的挂载方式一次讲全。

3.1 用 USBasp 或另一块开发板当 ISP 的接线对照表

ISP 接口标准是 6 根线:MISO、MOSI、SCK、RESET、VCC、GND。USBasp 那一头是标准的 10 针或 6 针排针,ATtiny13 这一头要按物理引脚对应接过去。

对照表:

ISP 信号USBasp 6 针定义ATtiny13 物理脚端口
MOSI第 4 针5PB0
MISO第 1 针6PB1
SCK第 3 针7PB2
RESET第 5 针1PB5
VCC第 2 针8VCC
GND第 6 针4GND

最容易接反的是 MOSI 和 MISO。名字看起来对称,实际是一进一出,接反了编程器能识别到「有东西在线」,但每次读签名都读回0x000000。判断方法很简单:读签名读不到就先把这两根对调试一次。

供电方式要选一种,不要两边同时供。USBasp 板上通常有个跳线帽,短接表示由编程器给目标板供电(3.3V 或 5V),断开表示目标板自己供电。如果目标板已经有独立电源,跳线帽必须断开,否则两路电源顶在一起,轻则编程器保护,重则把芯片的 VCC 脚打穿。

目标板 VCC 上如果有大电容,烧录时容易失败。USBasp 的供电能力有限(通常 100mA 以内),如果板子上挂了一颗 100μF 以上的电解电容,上电瞬间会把电压拉低,编程器还没进入编程模式就被判定为「目标无响应」。解决办法是烧录时把电容焊掉,或者用外部稳压电源单独供电,只把 GND 和 RESET 跟编程器共起来。

如果手边没有 USBasp,用一块 Arduino 板子刷成 ISP 也能干活。常见做法是把 Arduino 板烧上ArduinoISP示例程序,然后把它的 D11/D12/D13/D10 分别接到目标芯片的 MOSI/MISO/SCK/RESET。这种方案的好处是不用额外买件,坏处是 ISP 时钟比较慢,烧 1KB 的芯片要等几秒钟,而且有些核心包的烧录脚本对 ArduinoISP 的兼容性一般。

注意:不管用哪种编程器,目标芯片的 VCC 一定要先量一下。很多人接线接对了、命令敲对了,就是烧不进去,最后发现是目标板上的稳压芯片坏了,实际只有 1.2V。万用表这一步不能省。

3.2 avrdude 读芯片、改熔丝位、烧写 hex 的完整命令序列

avrdude是 AVR 生态里通用的烧录工具,绝大多数图形界面工具底层调的都是它。直接敲命令虽然原始,但每一步做了什么清清楚楚,出问题好定位。

第一步,先只读不写,确认链路通:

# -c 指定编程器型号,-p 指定芯片型号 # -B 8 把 ISP 时钟周期拉长到 8 微秒(约 125kHz) # -n 表示 dry run,只读不写,用来验证接线 avrdude -c usbasp -p t13 -B 8 -n

命令跑通会打印芯片签名1e 90 07,这是 ATtiny13 的固件签名。读不到签名就说明接线、供电或者 ISP 时钟三者有问题,先解决这个再往下走。

第二步,把当前熔丝位读出来并抄在纸上:

# -U 后面跟的是「存储区:操作:数据:格式」 # lfuse:r:-:h 表示读低熔丝位,输出到标准输出,十六进制格式 avrdude -c usbasp -p t13 -B 8 -U lfuse:r:-:h -U hfuse:r:-:h

出厂芯片应该读回0x6A和0xFF。这两个值必须记下来,因为改坏了要靠它恢复。

第三步,按需改时钟。把系统时钟从 1.2MHz 提到 9.6MHz:

# 把低熔丝位从 0x6A 改成 0x7A,即关闭 CKDIV8 分频 avrdude -c usbasp -p t13 -B 8 -U lfuse:w:0x7A:m

:w:表示写,:m表示立即模式(直接给数值)。写完必须断电再上电,新的时钟配置才生效。

如果要改成 4.8MHz 内部 RC,低熔丝位用0x79;如果要改成 128kHz 超低功耗内部 RC,用0x6B。这三个值是第三方核心包板级定义里最常见的三档。

第四步,烧写编译好的 hex 文件:

# -U flash:w:...:i 表示写 flash,格式是 Intel HEX avrdude -c usbasp -p t13 -B 8 -U flash:w:firmware.hex:i

烧完 avrdude 会自动回读校验,输出里出现avrdude done. Thank you.才算成功。如果出现verification error,说明读到的不等于写入的,通常是供电不稳或者 ISP 线太长(超过 20 厘米就开始不稳定)。

第五步,EEPROM 内容单独备份。ATtiny13 的 EEPROM 是独立存储区,烧 flash 的时候不会碰它:

# 把 EEPROM 全部内容读出来存成 hex,方便后面比对 avrdude -c usbasp -p t13 -B 8 -U eeprom:r:eeprom_backup.hex:i

参数说明汇总一下:

  • -c usbasp:编程器类型。用 ArduinoISP 就写-c arduino,用别的编程器要换成对应的名字。
  • -p t13:芯片型号。ATtiny13A 也写t13,avrdude 内部把它当attiny13a处理。
  • -B 8:ISP 时钟周期,单位微秒。数值越大时钟越慢。芯片主频 1.2MHz 时至少要-B 8,主频改到 9.6MHz 之后可以降到-B 2加快烧录速度。
  • -n:dry run,只执行不写入。
  • -U 区:操作:数据:格式:区可以是flash、eeprom、lfuse、hfuse、lock;操作是r(读)或w(写);格式是i(Intel HEX)、h(十六进制)、m(立即数)、b(二进制)。

3.3 在 Arduino IDE 里挂第三方核心,写第一段闪灯程序

不想碰命令行的人可以走 Arduino 这条路。官方 IDE 本身不带 ATtiny13 的板级定义,需要挂第三方核心。

流程是:打开 IDE 的「首选项」,在「附加开发板管理器网址」里加一条第三方 ATtiny 核心的索引地址(具体地址按你选的核心包来,不同维护者各有一份),然后在「开发板管理器」里搜 ATtiny 安装。装完之后在「工具 → 开发板」里能选到 ATtiny13/13A。

关键设置有三项:

  • Chip:选 ATtiny13A。
  • Clock:选 9.6MHz internal osc.(前提是熔丝位已经改成 0x7A;如果没改就选 1.2MHz internal osc.)。
  • Programmer:选 USBasp 或 Arduino as ISP。

这三项必须跟芯片实际状态一致。选错了不会报错,只会表现为「程序烧进去了但不跑」。

第一段程序,物理脚 6(PB1)接一颗 LED 到 GND:

// 目标:ATtiny13A,9.6MHz 内部 RC,PB1 驱动 LED 闪烁 // 核心包里的引脚编号 0~5 对应 PB0~PB5,所以这里的 1 就是 PB1 void setup() { pinMode(1, OUTPUT); // 把 PB1 配置为推挽输出 } void loop() { digitalWrite(1, HIGH); // PB1 输出高电平,LED 点亮 delay(500); digitalWrite(1, LOW); // PB1 输出低电平,LED 熄灭 delay(500); }

逻辑说明:pinMode内部操作的是DDRB寄存器,把对应位置 1;digitalWrite操作的是PORTB寄存器。delay(500)依赖核心包根据你选的时钟自动定义的F_CPU宏,所以时钟选项必须选对,否则 500 毫秒会变成 4 秒。

烧录时点「项目 → 使用编程器上传」,IDE 会调用 avrdude 完成擦除、写入、校验三步。第一次烧录如果报program enable失败,把 IDE 的「烧录器」设置里那个「上传速度」调慢一点(对应命令行就是加大-B值)。

LED 串联电阻取值:5V 供电、普通红色 LED 压降约 1.8V、目标电流 5mA,算下来是(5 - 1.8) / 0.005 = 640Ω,取 680Ω 或者 1kΩ 都行。ATtiny13 单个 IO 的最大灌电流和拉电流都是 40mA,但整颗芯片所有 IO 加起来不能超过 200mA,所以不要同时驱动太多负载。

4. 在 1KB 里写裸机代码:GPIO、PWM、ADC 与掉电睡眠

Arduino 核心包省事,但它本身要吃掉一部分 flash 和 RAM。1KB 的芯片上,核心包占掉 200 到 400 字节之后,留给业务逻辑的空间就很紧张了。真要做产品,直接写裸机寄存器代码更划算——代码小、时序可控、出了问题能直接对着数据手册定位。这一章给三段可以直接抄的裸机代码。

4.1 定时器 0 做 PWM 呼吸灯:OCR0A 与预分频怎么定

ATtiny13 只有一个 8 位定时器(定时器 0),它同时承担延时、PWM 输出、溢出中断三个职责。PWM 频率由三个量决定:系统时钟、预分频系数、计数上限。

计算公式是:f_pwm = f_cpu / (预分频 × 256)(快速 PWM 模式下计数上限固定 255)。

9.6MHz 系统时钟,预分频选 8:9.6e6 / (8 × 256) = 4687.5Hz。这个频率人耳听不到,LED 上看不到闪烁,是比较理想的取值。如果预分频选 1,频率是 37.5kHz,对 LED 来说没必要,反而增加开关损耗;选 64 的话频率掉到 586Hz,摄像头拍出来会有横纹。

#include <avr/io.h> #include <util/delay.h> int main(void) { // PB0 配置为输出(它同时是 OC0A,硬件 PWM 输出脚) DDRB |= (1 << PB0); // TCCR0A: COM0A1 <p> <a href="https://download.csdn.net/download/jkd_405/9943455" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询