1. 项目背景与M162单片机简介
最近整理工作室时,意外翻出一包20年前购买的ATMEGA162单片机。这款AVR系列的老将最吸引我的特点是它内置了双串口模块——在2000年代初,这可是相当奢侈的配置。记得当年为了做多机通信项目,不得不使用这款芯片,而现在随便一块STM32都能轻松实现更多串口。出于怀旧和技术验证的目的,我决定重新点亮这颗"古董"芯片。
M162采用经典的AVR RISC架构,工作频率0-16MHz,具有16KB闪存、1KB SRAM和512B EEPROM。其双USART模块(USART0和USART1)支持全双工通信,波特率可独立设置。与现在主流的ARM Cortex芯片相比,它的资源相当有限,但在当时已经属于中高端配置。特别值得一提的是它的熔丝位配置系统,这是AVR系列特有的功能,用于设置时钟源、启动时间等关键参数。
2. 测试电路设计与制作
2.1 最小系统设计
为了验证芯片功能,我设计了一个极简测试电路:
- 电源部分:采用AMS1117-5.0稳压芯片,将USB输入的5V转换为稳定的5V工作电压
- 时钟电路:使用8MHz陶瓷谐振器配合22pF电容(相比晶振更节省空间)
- 调试接口:标准的6针ISP接口,兼容USBASP下载器
- 指示电路:单个LED串联220Ω电阻连接到PB0引脚
- 串口接口:仅引出USART0的TXD/RXD引脚,通过CH340G芯片转换为USB信号
提示:虽然M162支持内部RC振荡器,但为了串口通信稳定,建议使用外部时钟源。8MHz时钟下,标准9600波特率的误差率仅为0.2%。
2.2 PCB快速制作
采用单面PCB设计,所有元件采用直插封装以便手工焊接:
- 使用KiCad绘制电路图,特别注意将ISP接口与芯片对应引脚直连
- 采用"飞线最少化"的布局策略,所有走线宽度设置为20mil
- 使用热转印法制作电路板:
- 激光打印机将布线图打印在光面纸上
- 熨斗加热将墨粉转印到覆铜板
- 三氯化铁溶液腐蚀(约3分钟)
- 钻孔使用0.8mm钻头,特别注意ISP接口和晶振安装孔位
焊接时的一个技巧:先焊接晶振和滤波电容,确保电源稳定后再焊接主芯片。使用松香芯焊锡丝,保持烙铁温度在300°C左右,每个引脚焊接时间不超过3秒。
3. 开发环境搭建与熔丝位配置
3.1 工具链准备
虽然现代IDE如Atmel Studio 7仍支持M162,但我选择了更轻量级的方案:
- 编译器:AVR-GCC 5.4 (与新版相比对老芯片支持更好)
- 下载工具:avrdude 6.3
- 编辑器:VS Code + AVR插件
Makefile关键配置:
makefile复制MCU = atmega162
F_CPU = 8000000UL
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os -Wall
3.2 熔丝位设置
这是最关键的步骤,错误的熔丝位可能导致芯片锁死:
- 使用USBASP下载器连接ISP接口
- 执行读取命令:
avrdude -p m162 -c usbasp -U lfuse:r:-:h -U hfuse:r:-:h - 必须修改的配置:
- 取消M161C兼容模式(hfusebit7=1)
- 时钟源选择外部全幅振荡器(lfuse=0xFF)
- 启动延时设为14CK + 65ms(lfusebit0=1)
警告:切勿设置RSTDISBL熔丝位,否则将失去编程能力!建议先用
-F参数绕过验证,确认配置正确后再正式写入。
4. 功能测试与问题排查
4.1 基础测试程序
编写简单的LED闪烁程序验证最小系统:
c复制#include <avr/io.h>
#include <util/delay.h>
int main(void) {
DDRB |= (1 << PB0); // 设置PB0为输出
while(1) {
PORTB ^= (1 << PB0); // 翻转PB0状态
_delay_ms(500);
}
}
编译下载后,观察到LED以1Hz频率闪烁,说明:
- 时钟系统工作正常
- GPIO功能完好
- 下载链路可靠
4.2 串口通信测试
启用USART0发送数据:
c复制#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
// 必须定义在main.c中才能被识别
int putchar(char c, FILE *stream) {
while (!(UCSR0A & (1<<UDRE0)));
UDR0 = c;
return 0;
}
void uart_init(void) {
UBRR0L = 51; // 9600bps @8MHz
UCSR0B = (1<<TXEN0);
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00); // 8N1
}
int main(void) {
uart_init();
while(1) {
printf("U"); // 发送0x55
_delay_ms(1000);
}
}
遇到的典型问题及解决方案:
-
putchar函数位置问题:
- 现象:函数定义在uart.c时链接报错
- 原因:老版本AVR-GCC对stdio重定向的实现方式不同
- 解决:必须将putchar定义在使用printf的同一源文件中
-
波特率误差大:
- 现象:接收端出现乱码
- 检查:UBRR计算公式应为F_CPU/(16*BAUD)-1
- 实测:使用逻辑分析仪校准,发现实际波特率偏高1.5%
- 调整:改用38400bps(UBRR=12)误差降至0.1%
-
熔丝位配置异常:
- 现象:程序下载后不运行
- 诊断:使用
avrdude -p m162 -c usbasp -U efuse:r:-:h读取验证 - 发现:M161C模式被意外启用
- 修复:重新编程hfusebit7=1
5. 双串口功能验证
5.1 硬件修改
为测试USART1,需要:
- 飞线连接PD2(TXD1)和PD3(RXD1)到CH340G
- 在PCB上增加第二组串口连接器
- 注意两组串口不能同时连接到同一USB转串口芯片
5.2 软件实现
双串口回环测试程序:
c复制void uart1_init(void) {
UBRR1L = 51; // 9600bps
UCSR1B = (1<<RXEN1)|(1<<TXEN1);
}
void uart1_echo(void) {
if (UCSR1A & (1<<RXC1)) {
char c = UDR1;
while (!(UCSR1A & (1<<UDRE1)));
UDR1 = c; // 回传接收到的字符
}
}
int main(void) {
uart0_init();
uart1_init();
while(1) {
uart0_echo();
uart1_echo();
}
}
测试结果:
- 两个串口可同时独立工作
- 最大稳定波特率实测为115200bps
- 全双工通信时无数据丢失现象
6. 经验总结与扩展应用
经过全面测试,这颗20年前的M162依然宝刀未老。以下是在项目中使用老款MCU的实用建议:
-
开发环境配置:
- 使用与芯片同期发布的工具链版本
- 避免依赖现代库函数,尽量直接操作寄存器
- 对于AVR系列,avr-libc的版本差异可能导致兼容性问题
-
硬件设计要点:
- 电源滤波电容不少于100nF+10μF组合
- 复位电路建议使用10kΩ上拉电阻+100nF电容
- 未用IO口设置为输出或内部上拉
-
性能优化技巧:
- 对于时间敏感操作,使用内联汇编
- 关闭未用外设以降低功耗
- 合理使用全局中断控制(cli()/sei())
-
扩展应用设想:
- 多机通信网关(利用双串口)
- 简易PLC控制器
- 老设备兼容性改造
这个项目让我重新认识到,在物联网时代追求高性能MCU的同时,这些经典芯片在特定场景下仍有其独特价值。特别是当需要与老系统保持兼容,或者对成本极其敏感时,它们往往是更务实的选择。