1. 项目概述:M162单片机与双串口设计
第一次接触M162这款AVR单片机是在2012年的一次工业设备维修中。那台老旧的纺织机控制板上,一颗28脚DIP封装的芯片在众多现代元件中显得格外醒目——正是Atmel M162。作为早期AVR家族中少有的配备双串口的8位MCU,它在当时通信需求复杂的场景下展现了独特价值。
M162属于Atmel的megaAVR系列,采用先进的RISC架构,最高支持16MHz主频,具备16KB可编程Flash和1KB SRAM。与常见的ATmega16/32相比,其最大特色在于配备了两个完全独立的USART模块(USART0和USART1),这在需要同时连接多个串行设备的场合(如工业HMI+传感器采集)能大幅简化电路设计。我曾用它在自动化产线上实现PLC与条码枪的并行通信,省去了额外的串口扩展芯片。
注意:M162的PD2引脚同时作为USART1的RX和外部中断0输入,设计中断服务程序时需要特别注意信号冲突问题。
2. 硬件架构深度解析
2.1 双串口硬件实现原理
M162的两个串口采用分时复用IO的设计思路:
- USART0固定使用PD0(RXD)和PD1(TXD)
- USART1复用PE0(RXD)和PE1(TXD)
这种设计使得在28引脚封装中实现双串口成为可能。实际使用中需要注意:
- 波特率发生器共享系统时钟,两个串口需使用相同晶振频率
- 每个USART有独立的中断向量(USART0_RX_vect/USART1_RX_vect)
- 数据寄存器UDR0/UDR1物理隔离,可同时收发数据
c复制// 典型初始化代码示例
void uart_init(uint8_t channel, uint16_t baud) {
if(channel == 0) {
UBRR0H = (uint8_t)(baud>>8);
UBRR0L = (uint8_t)baud;
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00); // 8N1
} else {
UBRR1H = (uint8_t)(baud>>8);
UBRR1L = (uint8_t)baud;
UCSR1B = (1<<RXEN1)|(1<<TXEN1);
UCSR1C = (1<<UCSZ11)|(1<<UCSZ10);
}
}
2.2 外设资源分配策略
M162的资源管理需要精打细算:
- 定时器:1个16位(Timer1)和2个8位(Timer0/2)
- ADC:8通道10位精度
- 比较器:1个模拟比较器
- 中断源:21个中断向量
在同时使用双串口的系统中,建议:
- 将高优先级通信任务分配给USART0(中断向量号更小)
- 使用Timer1作为串口超时计数器
- 避免在USART1中断服务程序中执行耗时操作(与外部中断0冲突风险)
3. 开发环境搭建实战
3.1 工具链配置
虽然现代IDE如Atmel Studio 7已不再原生支持M162,但通过以下方法仍可开发:
- 安装WinAVR-20100110(最后支持M162的稳定版本)
- 手动添加器件定义:
- 修改avr/io.h添加
#define __AVR_ATmega162__ - 复制mega32的io_*.h文件重命名为m162版本
- 修改avr/io.h添加
makefile复制# 示例Makefile关键配置
MCU = atmega162
F_CPU = 16000000UL
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os
3.2 编程技巧与避坑指南
-
熔丝位配置:
- 务必设置CKOPT=1(全幅振荡输出)
- SPIEN保持编程状态(否则无法再次烧录)
- 典型配置:LFUSE=0xCF HFUSE=0x99
-
内存优化:
- 使用
PROGMEM存储常量字符串 - 关键变量加上
__attribute__((section(".noinit")))避免启动清零
c复制const char menuText[] PROGMEM = "System Menu"; __attribute__((section(".noinit"))) uint8_t backupReg; - 使用
-
双串口DMA模拟:
通过环形缓冲区+定时器中断实现伪DMA:c复制#define BUF_SIZE 64 typedef struct { uint8_t head, tail; char data[BUF_SIZE]; } UART_Buffer; volatile UART_Buffer uart0_buf, uart1_buf; ISR(USART0_RX_vect) { uart0_buf.data[uart0_buf.head++] = UDR0; uart0_buf.head %= BUF_SIZE; }
4. 典型应用场景剖析
4.1 工业控制网关设计
在某纺织机械改造项目中,M162作为通信枢纽:
- USART0连接Modbus RTU总线(波特率19200)
- USART1对接触摸屏(ASCII协议,115200bps)
- 硬件电路关键点:
- 两路串口均添加MAX3485电平转换器
- 在RX线上并联120Ω终端电阻
- TX线路串联33Ω阻抗匹配电阻
实测发现:当两个串口同时全速工作时,需将系统时钟提升至16MHz以上,否则可能出现数据丢失。
4.2 多传感器采集系统
构建环境监测节点时:
- USART0以9600bps轮询4个DS18B20温度传感器
- USART1通过MAVLink协议上传数据到飞控
- 利用Timer2产生1ms时基进行数据打包
c复制void sensor_task(void) {
static uint8_t idx;
int16_t temp = ds18b20_read(idx);
mavlink_msg_scaled_pack(
0xFF, 0xBE,
&msg,
MAV_SENSOR_TEMP,
temp/10.0f
);
uart_send_mavlink(1, &msg);
idx = (idx+1)%4;
}
5. 疑难问题解决方案
5.1 串口数据错位问题
现象:USART1接收数据时偶发首字节丢失
排查过程:
- 用逻辑分析仪捕获波形,确认发送端数据完整
- 检查发现PE0引脚同时连接了按键电路
- 添加Schmitt触发器整形后问题解决
5.2 功耗异常分析
某电池供电设备待机电流达8mA(预期应<1mA):
- 逐步关闭外设测试:
- 禁用ADC后降至6.5mA
- 关闭比较器后到5.2mA
- 最终发现未使用的PC6引脚浮空
- 配置为输出低电平后电流降至0.8mA
c复制void power_save(void) {
PRR = (1<<PRTIM1)|(1<<PRUSART1); // 关闭未用外设
DIDR = 0xFF; // 禁用数字输入缓冲
ACSR |= (1<<ACD); // 关闭模拟比较器
DDRC |= (1<<PC6); PORTC &= ~(1<<PC6);
}
6. 现代化改造方案
6.1 固件升级路径
虽然M162已停产,但可通过以下方式延续使用:
- 硬件兼容设计:
- 保持引脚兼容ATmega32A
- 在PCB上预留两种封装焊盘
- 代码迁移:
- 将USART1相关代码替换为软串口
- 使用Timer0模拟第二个UART
6.2 替代型号对比
| 特性 | M162 | ATmega32A | ATmega324PA |
|---|---|---|---|
| 串口数量 | 2 | 1 | 2 |
| Flash大小 | 16KB | 32KB | 32KB |
| 工作电压 | 4.5-5.5V | 2.7-5.5V | 1.8-5.5V |
| 价格(2023) | 停产 | $3.2 | $4.8 |
对于新设计,建议考虑ATmega324PA:
- 保留双串口特性
- 支持更宽电压范围
- 提供QFP和QFN封装选项
7. 实战经验总结
在最近的一次电梯状态监测系统改造中,M162再次证明了其价值。通过USART0连接Modbus电力仪表,USART1对接4G DTU模块,实现了:
- 实时电流电压采集(500ms间隔)
- 异常数据本地缓存(利用内部EEPROM)
- 断网自动重连机制
关键优化点:
- 修改USART波特率寄存器写入顺序避免毛刺:
c复制void uart_update_baud(uint8_t ch, uint16_t baud) { if(ch == 0) { UCSR0B &= ~(1<<RXEN0); // 先禁用接收 UBRR0L = baud; UBRR0H = baud>>8; UCSR0B |= (1<<RXEN0); } // 类似处理USART1... } - 利用片内EEPROM存储设备ID和校准参数,避免每次上电配置
经过十余个项目验证,这颗"古老"的芯片在双串口场景下仍能稳定运行。其设计精髓——简单可靠的硬件架构和高效的指令集,值得当代嵌入式开发者借鉴。对于存量设备维护,掌握M162的独特性能够显著提高维修效率;在新设计中,理解其双串口实现方案也能为现代MCU的外设使用提供参考思路。