简介:本资源是面向业余无线电爱好者与嵌入式开发者的APRS对讲机功能实现源码包,聚焦于在资源受限的AVR微控制器(如ATmega8)上构建轻量级APRS通信系统,特别适配对讲机硬件平台并基于优化协议px2.5进行数据传输。压缩包共14个文件,含5个原理图符号(.sym,用于KiCad设计)、3份关键芯片手册(PDF格式,涵盖ATmega8、MAX232及APRS基础协议)、1份原理图(.sch)、1份Makefile构建脚本、1份README说明文档及1个核心C语言源文件(main.c),整体大小为4.29MB,结构清晰,软硬协同特征突出。已有433人学习下载,读者可直接获取完整可编译工程框架、硬件接口驱动范例、AX.25兼容的APRS协议栈雏形、GPS位置数据打包逻辑及串口调制解调配置方案,是深入理解业余无线电数字通信与低功耗嵌入式协议栈开发的实用参考。 APRS这套东西,圈内人叫它“业余无线电的短信+定位”,老外玩得风生水起,国内这几年也越来越多人入坑。前阵子我翻到一个名为 aprs-avr-master 的老工程,项目标题写得很直白:“对讲机 + AVR + APRS源码”。乍一看是个老掉牙的项目,但仔细扒完代码和电路,发现这套思路不仅没过时,反而是理解APRS数据链路最干净的入口之一。这篇文章我就把这套源码的工程架构、硬件接线、对讲机改装要点、编译烧录流程以及我实际调试中踩过的坑,全部捋一遍。
这篇文章适合谁?如果你是刚接触APRS、手里有闲置对讲机和AVR开发板、想亲手DIY一个带GPS定位和短信收发功能的APRS终端的玩家,这篇文章能帮你省下大量翻论坛、啃数据手册的时间。如果你已经在用成品电台和手机App玩APRS,这篇文章也能让你对自己手里那套系统“底下到底怎么跑起来”有个更清晰的认识。
1. 整体设计与方案选型:为什么是AVR+对讲机,而不是直接用现成模块
先聊项目设计层面的东西。这套源码的核心理念,是用最廉价的硬件组合,实现完整的APRS收发链路。所谓“完整链路”,指的是:从GPS拿到定位数据,按APRS协议组帧,用音频调制方式(AFSK)把数据变成声音信号,通过对讲机发射出去;同时也能接收对讲机解调出的音频,从声音里把APRS帧解出来,还原成位置信息或文本消息。
1.1 核心链路拆解:AVR在整套系统里扮演什么角色
AVR单片机在这套系统里干的是“全栈”的活:
- 从GPS模块的串口读取NMEA语句(
$GPGGA、$GPRMC),解析出经纬度、高度、时间。 - 根据APRS协议构造数据帧,包括源呼号、目标呼号、路径(如
WIDE1-1,WIDE2-1)、经度纬度格式转换、注释信息等。 - 用软件方式生成1200bps的Bell 202 AFSK音频信号,从某个IO引脚输出方波音频给对讲机麦克风。
- 从对讲机扬声器/耳机接口接收音频信号,通过模拟比较器或ADC采样,用软件解调出0和1的数据流,再按AX.25协议解析出完整帧。
这个方案的精妙之处在于,它没有用任何专用的APRS调制解调芯片(比如以前常见的MX614),也没有用TNC(Terminal Node Controller,终端节点控制器)设备,纯靠一颗8位单片机的运算能力,用定时器中断模拟出1200波特率的频移键控信号。这种“软件无线电”的思路放在今天看很朴素,但在AVR这种主频只有8MHz甚至更低的芯片上跑起来,需要对时序有极其精确的控制。
1.2 为什么选AVR而非STM32、ESP32
现在很多人一上手就是ESP32,自带Wi-Fi和蓝牙,性能吊打AVR。但在这个特定的APRS场景里,AVR有两个不会被替代的理由:
第一, AVR的定时器外设足够精确 。APRS AFSK调制要求位时间误差控制在微秒级别,AVR的8位定时器配合CTC模式,在8MHz晶振下可以精确到0.5微秒级别,完全够用。而且AVR的IO翻转速度极快,适合做音频信号的边沿输出。
第二, AVR的裸机编程模型简单可靠 。这套源码是纯C写的,没有操作系统依赖,中断优先级简单明确,调试起来非常直观。相比之下,ESP32虽然性能强,但它的Wi-Fi栈和RTOS对于纯模拟信号处理反而不友好,中断延迟不可控,容易造成音频信号抖动。
当然,如果你希望APRS终端同时支持Wi-Fi上报位置到APRS-IS网络,那ESP32确实是更合适的选择。但如果是做纯射频链路、野外无网络环境下的位置共享,AVR这套方案又省电又稳定,用两节18650电池能跑一整天。
2. 硬件核心细节解析:从引脚分配到模拟前端设计
硬件层面是这项目里最需要耐心的地方。源码本身只处理逻辑,但APRS能不能正常工作,90%取决于你的音频通路和PTT控制电路做得是否干净。
2.1 引脚分配与最小系统构成
以源码注释里常用的 ATmega128 (部分版本支持 ATmega328P )为例,核心引脚分配如下:
| 功能模块 | 引脚 | 说明 |
|---|---|---|
| GPS串口接收 | PD0 (RXD0) | 连接GPS模块TXD,波特率9600 |
| GPS串口发送 | PD1 (TXD0) | 连接GPS模块RXD,用于发送配置指令 |
| 调制音频输出 | OC1A (PB5) | 通过定时器1比较匹配翻转,输出1200/2200Hz方波 |
| 解调音频输入 | ADC0 (PF0) | 对讲机音频信号经放大整形后进入ADC采样 |
| PTT控制 | PD2 | 高电平(或低电平,取决于电路)驱动PTT三极管/光耦 |
| 状态LED | PB0 | 心跳/发射状态指示 |
| 配置按键 | PD3 | 长按进入信标发送模式,短按切换状态 |
注意引脚分配因项目分支而异,有些版本用模拟比较器(Analog Comparator)的AIN0/AIN1引脚做解调输入,而不是ADC。用模拟比较器的好处是硬件上直接对音频信号做门限判决,软件只需要在比较器中断里记录边沿时间即可,CPU占用率极低。用ADC的好处是能对信号幅度做数字化判断,抗干扰能力更强,但对采样时序要求更高。
2.2 音频链路设计:这是整个项目成败的分水岭
APRS的音频信号是1200bps的Bell 202标准,具体来说:
- 逻辑“0”(Space)对应 2200Hz
- 逻辑“1”(Mark)对应 1200Hz
- 调制方式是二进制频移键控,频偏约 ±600Hz,和业余无线电窄带FM的2.5kHz高频偏相比,属于“sub-audio”级别的低频范围。
发射方向上,AVR输出的1200/2200Hz方波不能直接怼到对讲机的麦克风。原因是:
- 方波含有大量谐波,直接输入会严重过调制,导致对讲机发射的射频信号严重失真,对方解调失败。
- 对讲机麦克风的输入灵敏度很高,通常几毫伏到几十毫伏就能满调制。AVR引脚输出的5V方波要是直接接上去,能把对讲机的射频功放直接推到过载。
所以发射电路必须经过衰减和滤波。常规做法是:AVR输出引脚 → 2kΩ+2kΩ分压 → 1μF隔直电容 → 470Ω限流电阻 → 对讲机麦克风输入端(MIC)和地。
接收方向上,对讲机扬声器或耳机接口输出的音频信号幅度通常是几百毫伏到几伏(取决于音量设置),但这个信号带有很多噪声和语音残余,直接用ADC采样效果很差。需要经过一个带通滤波器(通常中心频率1500-2000Hz,带宽约1kHz),再经过一级运算放大器放大到适合ADC采样的范围(0-5V),最后才进入单片机。
我这里推荐一个简单可靠的前端方案:用双运放LM358或TL072搭一个二阶带通滤波器,把1200Hz和2200Hz信号保留,把300-3000Hz以外的杂波滤干净。实测效果最好的参数是:中心频率1700Hz,Q值约1.5。这组参数下解调误码率最低。
2.3 PTT控制与对讲机改装的关键思考
PTT(Push To Talk,按讲)控制是整个对讲机改装里最容易翻车的环节。直接说结论: 不要试图直接操作对讲机内部的PTT开关线路 ,而是用光电耦合器隔离控制。
我之前第一次做的时候,图省事,直接把AVR的IO口接到了对讲机PTT键的两端焊盘上,结果一开机对讲机就自动发射。原因是对讲机内部PTT线路的电位和MCU的GND不共地,IO口的默认电平直接触发了PTT。
正确做法是:
- 打开对讲机,找到麦克风(MIC)和扬声器(SPK)的焊盘,以及地线焊盘。
- 从这三个焊盘引线出来,接一个4芯航空插头(MIC、SPK、GND、PTT-GND)。
- 在单片机端,用一个TLP521或PC817光耦,输入侧接AVR的PTT控制引脚(限流电阻1kΩ),输出侧的集电极和发射极分别接对讲机PTT焊盘引出的两线。
- 这样AVR启功PTT时,光耦导通,等效于按下对讲机的PTT键,对讲机进入发射状态。反之则完全隔离,对讲机平时处于接收状态。
改装完成后务必用万用表测试:光耦未导通时,PTT两端电阻应为无穷大;光耦导通时,电阻应接近0。
3. 源码结构与关键算法解析
源码部分我相信很多朋友拿到的版本不一定完全一致,但核心文件通常是这几个: main.c 、 ax25.c 、 afsk.c 、 gps.c 、 beacon.c 。下面逐个拆解。
3.1 软件AFSK调制:用定时器精确生成1200/2200Hz
AVR实现的AFSK调制原理很直观,就是通过定时器比较匹配翻转IO来输出方波。这里的关键问题是:在8MHz系统时钟下,生成1200Hz和2200Hz的方波,定时器的计数初值怎么算?
8MHz主频下,定时器1工作在CTC模式,输出比较寄存器的值决定方波频率:
- 对1200Hz:半个周期为 1/1200/2 ≈ 416.7μs,计数器需要计数 8MHz × 416.7μs ≈ 3333 个时钟周期。
- 对2200Hz:半个周期为 1/2200/2 ≈ 227.3μs,计数器需要计数 8MHz × 227.3μs ≈ 1818 个时钟周期。
代码里会有类似这样的宏定义:
#define FREQ_MARK 1200UL
#define FREQ_SPACE 2200UL
#define OCR_MARK (F_CPU / FREQ_MARK / 2 - 1)
#define OCR_SPACE (F_CPU / FREQ_SPACE / 2 - 1)
注意 -1 是因为比较匹配是从0开始的,实际翻转周期要减一个时钟。如果主频不是8MHz整,比如用16MHz晶振,对应值会翻倍,但公式一致。
在定时器比较匹配中断里,直接把OCR寄存器值切换成上下两个频率对应的值,就完成了FSK调制。整个调制过程不占用CPU处理时间,这是这套方案能跑得顺的关键。
3.2 AX.25协议封装:APRS帧的“集装箱”
APRS数据帧在物理上走的是AX.25链路层协议,这是一个基于HDLC(高级数据链路控制)的变种。一个完整的AX.25帧长这样:
标志位(0x7E) | 目标地址 | 源地址 | 路径地址(可省略) | 控制字段(0x03) | PID(0xF0) | 信息字段 | FCS校验 | 标志位
每个地址段16进制表示,需要左移一位。比如呼号 BG5ABC-7 ,每个字符ASCII码左移一位,最后一个字节还带上SSID标识位。
在APRS中,信息字段是纯文本,格式通常是:
!3856.22N/11559.30W- 这是注释信息
这是一个典型的APRS定位帧格式。以 ! 开头,表示无时间戳的定位帧,后面直接跟纬度、经度数据。
源码里 ax25.c 的核心工作就是把这堆字节按位流转成HDLC帧格式,并且在数据流里做“位填充(bit stuffing)”:遇到连续5个1就插入一个0,避免在帧中间出现和标志位 0x7E 相同的位模式。
3.3 汉明码与状态帧:APRS能发的不只是位置
APRS不只是定位系统,它还支持短消息。消息帧格式通常以 : 开头,比如:
BG5ABC-9>T2U0P7,WIDE1-1,WIDE2-1::BG5XYZ :Hello, this is a test message
这一段里,发送方 BG5ABC-9 给 BG5XYZ 发了一条消息,消息正文是 Hello, this is a test message 。这套源码实现了消息的发送缓冲区和接收显示功能,收到消息后可以通过串口或1602/12864液晶屏显示出来,实际上就是一个简陋的“无线电短信终端”。
部分版本源码还实现了Beacon(信标)自动定时发送功能。GPS模块上电后每秒钟输出一帧NMEA数据,程序在首次锁定有效定位后,每隔N分钟(配置项,通常5分钟)自动发射一次位置帧,这样你在APRS地图上就能看到一个移动的轨迹了。
4. 实操过程:从编译到烧录的完整流程
这次我用了 avr-gcc 工具链在命令行编译这套源码,顺便把Makefile的细节和环境配置也过一遍。如果你用的是Atmel Studio或者PlatformIO,思路类似,但命令行版本能让你更清楚地看到每一步到底在干什么。
4.1 工具链安装与编译
在Ubuntu/Debian系统下,安装编译工具:
sudo apt update
sudo apt install gcc-avr avr-libc avrdude make
然后进入源码目录,先看Makefile里的核心配置:
MCU = atmega128
F_CPU = 8000000UL
BAUD = 9600
PROGRAMMER = usbasp
注意 F_CPU 要和你的实际晶振频率严格一致。如果这里写8MHz但你板子上焊的是16MHz晶振,串口波特率会偏一倍,GPS解析必然出错。这个我踩过坑,排查了整整一个晚上,最后用示波器量了一下时钟信号才发现是晶振型号搞错。
编译:
make clean
make hex
如果一切顺利,会在目录下生成 .hex 文件。如果有报错,大概率是头文件路径不对,检查是否安装了 avr-libc ,以及 PORT 宏定义是否在 config.h 里正确配置。
烧录:
sudo avrdude -p m128 -c usbasp -U flash:w:aprs.hex
如果你用的是Arduino Uno板子,可以把MCU换成 atmega328p ,编程器选 arduino ,STK500协议,烧录前记得把Uno板上的复位电容去掉,否则可能出现烧录过程中同步失败。
4.2 实物接线与上电顺序
拿到一个全新的AVR板子,我建议按以下顺序接线,避免一上电就把芯片烧了:
- 先接GPS模块的VCC和GND,接好串口TX/RX,用USB-TTL在电脑上看是否有NMEA语句输出。
- 确认GPS能定位后,再接AVR的音频输出和PTT控制,先不接对讲机,用示波器看AVR的音频引脚是否有1200/2200Hz方波。
- 最后才实现对讲机侧的所有接线,并先用假负载(50Ω电阻)代替天线测试,避免信号辐射。
上电顺序也有讲究:先给AVR板供电,等程序跑起来后再给GPS模块供电,最后再打开对讲机电源。原因是GPS模块启动瞬间电流很大,如果AVR和对讲机共用一个电源,容易导致AVR复位。
4.3 参数配置与GPS数据格式解析
APRS的位置格式和GPS原始NMEA格式是有区别的。NMEA里的 $GPRMC 输出类似:
$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
其中 4807.038,N 表示北纬48度07.038分。而APRS位置帧需要的格式是:
!4807.038N/01131.000E-
即去掉逗号、去掉度分标识,合并成连续字符串。源码里 gps.c 做的工作就是从NMEA字符串中提取度分数据,然后把度分转换成APRS需要的“度分格式”文本。具体转换逻辑是:
int lat_deg = (int)(lat_raw / 100);
float lat_min = lat_raw - lat_deg * 100;
sprintf(buffer, "%02d%05.2f%c", lat_deg, lat_min, lat_hemi);
意思是将 4807.038 拆成度(48)和分(07.038),对应APRS位置帧中的 4807.038N 。注意这里不需要转换成十进制度数,直接保留度分格式,这也是新手容易误解的地方。
5. 常见问题排查与实战避坑记录
这部分我结合自己的实操经历和踩坑记录,整理成几张速查表,方便你现场排查问题。
5.1 发射方向问题排查
| 现象 | 可能原因 | 排查步骤与解决 |
|---|---|---|
| 按下PTT发射但对讲机不转发射 | 光耦驱动电流不足 | 检查PTT控制引脚是否为推挽输出,限流电阻是否过大,实测1kΩ限流配PC817,5V驱动电流约5mA足够导通 |
| 对方收到信号但解不出APRS帧 | 音频电平过高/过低,调制频偏不对 | 用示波器观察对讲机MIC引脚音频幅度,APRS标准调制频偏约3kHz,对应音频幅度约几十毫伏。适当调整分压电阻,使对方接收到的音频电平在200-500mV RMS之间 |
| 自己听到的音频是刺耳的“嘟——哔——”但还是解不出来 | 方波谐波分量过重 | 硬件滤波没有做好。在AVR输出后加一个RC低通滤波器,截止频率约4kHz,可大幅改善频谱纯净度 |
| 发射偏频/口齿不清 | 对讲机麦克风灵敏度太高、音频过调制 | 调分压电阻让音频幅度降低,或者在音频线上串1k电阻再并一个10μF电容到地,形成一个衰减网络 |
5.2 接收方向问题排查
解调不出信号是最让人崩溃的,因为问题可能出在任何一环:对讲机音频输出电平、滤波器参数、软件解码器阈值。
| 现象 | 可能原因 | 排查步骤与解决 |
|---|---|---|
| 完全解不出信号,LED无反应 | 音频未进入ADC/比较器 | 用示波器测量ADC引脚是否有明显音频波形。如果太微弱,放大倍数不够;如果波形是直流平台,说明比较器阈值设置错误 |
| 偶尔解出帧头但总是CRC错误 | 位同步不稳,采样点偏移 | 检查解码器的PLL(锁相环)时间常数,适当增加定时器中断频率(如改成2倍过采样),能显著提高纠错能力 |
| 听筒声音正常但收不到 | 模拟比较器阀值过高 | 把减压二极管或1N4148并联在地和输入间,限制音频摆幅在安全范围,同时用可调电阻调整比较器参考电压 |
| 收到自己发射信号能解码,但收不到别人 | 频率偏差/对讲机亚音设置 | 确认对讲机是否设置了CTCSS亚音静噪,APRS通联要求关闭亚音或者接收方使用相同亚音,否则音频被静噪电路阻断。这是一个非常隐蔽的坑 |
| 连着发3台同频中继都是CRC错误 | 本机解码器对多普勒频移没容忍度 | 提高解码器带宽范围,或者调整ADC采样窗口的容错范围 |
5.3 组网与接入APRS-IS的一些经验
纯单片机做APRS终端,更多时候是作为移动台“发射信标”,位置信息会被附近的中继台或iGate接收并转发到互联网APRS服务器。这时候你可以在 aprs.fi 网站上看到自己的呼号轨迹。
我在实际使用中发现几个很影响体验的点:
第一, 路径设置要谨慎 。如果用 WIDE1-1,WIDE2-1 路径,一次信标会被中继台重复转发多次,浪费频率资源。正确做法是:在城市多中继环境下,用 WIDE1-1 就够了;在人烟稀少的山区,才需要 WIDE2-2 这种更长的传播链。
第二, GPS模块冷启动时间要预留 。程序上电后可能30秒到2分钟才能定位成功,在这期间如果你按下“手动信标发射”按键,代码应该做保护,拒绝发送无效坐标。这源码里有一个标志位,只有 $GPGGA 语句中定位状态为有效时才允许发射。
第三, 如果你打算做iGate(把射频收到的APRS数据通过互联网转发出去) ,AVR这套方案就不太合适了。iGate要求设备同时接对讲机和网络,现在主流方案是树莓派+USB声卡,配合direwolf这类软件解码器,效率和稳定性远超单片机方案。但用AVR做成的APRS终端作为“无线节点”,本身是有独立价值的——在没有蜂窝网络也没有Wi-Fi的地方,它依然能正常传递位置信息。
5.4 硬件优化与后续扩展思路
项目跑通之后,可玩性还很多。我试过几个简单又有效的扩展:
- 增加OLED显示屏 。在I2C接口上挂一个SSD1306 0.96寸OLED,实时显示当前位置经纬度和最后收到的APRS消息。这个扩展只需要把源码里
lcd.c的接口替换掉,代码量不大,但整机可读性提升非常大。 - 加入遥测数据 。AVR有几路ADC输入,可以用来监测电池电压和温度,然后在信标帧的注释字段里附加
Batt=12.8V Temp=35C这样的信息。玩家在APRS地图上就能看到你的设备状态。 - 改用LoRa模块做超远距离数据链 。有人直接把对讲机替换成E22-400M30S这种LoRa模块,同时用AVR做数据源。LoRa相比对讲机FM体制,接收灵敏度更高,但APRS标准是1200bps AFSK音频调制,不能直接跑在LoRa上。如果你想让位置数据到更远的地方,可以考虑在APRS-IS之外再架一条LoRa链路做私有协议中继,这块就属于更高阶的玩法了。
6. 关于源码获取与二次开发的建议
aprs-avr-master 这类开源工程在GitHub上能搜到多个分支,建议优先选择更新时间较近、issue区有活跃讨论的fork版本。老版本的代码虽然也能跑,但AVR工具链更新后部分宏定义已经不兼容,需要手动改编译选项。
二次开发时,我建议重点看 afsk.c 和 ax25.c 这两个文件。前者是软件调制解调算法,理解了它,你就掌握了所有基于音频的低速数据通信的底层原理(比如也能自己改出RTTY、SSTV的调制器);后者是帧协议处理,理解它,你再去看LoRa、NB-IoT的数据帧格式,会发现都是同一套“组帧-校验-填充-收发”的模型,一通百通。
另外,有几个接口问题特别提醒:
- 发射音频和PTT的延时需要配置。AVR程序确认PTT电平稳定后,需要延迟约50-100ms再开始发送音频,否则对讲机发射机还没进入稳定状态,前几个字节会被切掉。
- GPS模块的物理连接线建议用双绞线并加磁环,因为AVR对讲机发射时产生的射频能量可能通过串口线倒灌进GPS模块,导致GPS掉星或者数据乱码。这个我在野外实测时遇到过,GPS在发射瞬间位置跳变几十米,非常诡异,后来加了一个100Ω磁珠和100nF电容组合的EMI滤波才解决。
- 天线系统不能省。如果你直接把对讲机原配天线插上去,APRS的压缩数据流对信号的完整性要求比语音高得多。语音听不清还能猜,数据解不出来就是零。实际通联成功率和信号质量最相关的就是天线和馈线,花在馈线上的每一分钱都值得。
7. 最后再分享一点实操中的心得
断断续续玩了几年APRS终端,我最大的感受是:这套东西真正的门槛不是代码,而是“频率使用规则”和“硬件射频基础”。频率资源怎么申请和合规使用、发射功率怎么控制、天线怎么架才不影响其他通信业务,这些才是决定你最终能不能越玩越顺的关键。AVR和APRS只是工具,是载体。
还有一件事,就是去学它的时候,别急着拿成品比性能。现在一个普通手机配上APRS App,在网络上发位置的能力比任何自制设备都强。但自制APRS终端的乐趣恰恰在于,它让数据通信变得看得见摸得着。你在没有信号覆盖的山顶按下发射键,三秒后几百公里外的朋友在Aprs.fi上看到你的位置弹出来,这种感觉,和用手机发一条微信定位,是完全不一样的。这套源码就是打开这扇门的第一把钥匙,我建议你把它走通一次,哪怕只是室内试验,射频技术中的“数据调制到射频发射再到解调”这条完整链路,你会比啃十本教材理解得都深。
613




被折叠的 条评论
为什么被折叠?



