diff --git a/rx/config.h b/rx/config.h index c5e911e..c3887a1 100644 --- a/rx/config.h +++ b/rx/config.h @@ -4,14 +4,6 @@ /* ======================================================================== * 系统常量 * ======================================================================== */ - -#define FOSC 24000000UL /* 系统主频 24MHz */ -#define PWM_PERIOD 5000 /* 20ms / 4us = 5000 步 */ -#define PWM_MIN 250 /* 1.0ms 脉宽 (4us/步) */ -#define PWM_MAX 500 /* 2.0ms 脉宽 */ -#define PWM_MID 375 /* 1.5ms 脉宽 */ -#define CHANNEL_COUNT 4 /* 4路PWM输出 */ - #define UART_BUF_SIZE 32 /* 接收状态 */ @@ -33,16 +25,18 @@ #define RF_PHRASE_LEN 6 /* LED 闪烁模式 */ -#define LED_BLINK_SLOW 0 /* 1Hz - 未连接 */ -#define LED_BLINK_FAST 1 /* 3Hz - 对频中 */ -#define LED_ON 2 /* 常亮 - 已连接 */ -#define LED_OFF 3 /* 熄灭 */ -#define LED_BLINK_DOUBLE 4 /* 双闪 - NRF硬件错误 */ +#define UNCONNECTED 0 /* 1Hz - 未连接 */ +#define NRF_BINDING 1 /* 3Hz - 对频中 */ +#define CONNECTED 2 /* 常亮 - 已连接 */ +#define NRF_ERROR 4 /* 双闪 - NRF硬件错误 */ /* ======================================================================== * 全局变量 * ======================================================================== */ +// 系统滴答定时器计数 (用于LED闪烁) +static uint8 sys_tick = 0; + /* PWM 通道脉宽值 (4us单位, 放在 xdata) */ static volatile uint16_t __xdata pwm_ch[CHANNEL_COUNT]; diff --git a/rx/gen_pdf.py b/rx/gen_pdf.py deleted file mode 100644 index adfcaf3..0000000 --- a/rx/gen_pdf.py +++ /dev/null @@ -1,942 +0,0 @@ -#!/usr/bin/env python3 -"""ZhaTianRX 接收机操作指南 PDF 生成脚本 - 完整版""" - -from fpdf import FPDF -import os - -# 中文字体路径 -CN_FONT = '/root/SourceHanSansCN-VF.ttf' -CN_FONT_BOLD = '/root/SourceHanSansCN-VF.ttf' - - -class ZhaTianPDF(FPDF): - def __init__(self): - super().__init__() - self.set_auto_page_break(auto=True, margin=15) - self.add_font('CN', '', CN_FONT) - self.add_font('CN', 'B', CN_FONT_BOLD) - - def header(self): - if self.page_no() > 1: - self.set_font('CN', '', 8) - self.set_text_color(0x7F, 0x8C, 0x8D) - self.cell(0, 8, 'ZhaTianRX 接收机操作指南', new_x="LMARGIN", new_y="NEXT", align='L') - self.cell(0, 8, f'第 {self.page_no()} 页', new_x="LMARGIN", new_y="NEXT", align='R') - self.set_draw_color(0xDD, 0xDD, 0xDD) - self.line(10, 14, 200, 14) - self.ln(4) - - def footer(self): - self.set_y(-15) - self.set_font('CN', '', 7) - self.set_text_color(0xBB, 0xBB, 0xBB) - self.cell(0, 10, 'ZhaTianRC 项目 | STC8H1K08 + NRF24L01+ | SDCC', align='C') - - def chapter_title(self, title, level=1): - if level == 1: - self.set_font('CN', 'B', 16) - self.set_text_color(0x1A, 0x1A, 0x2E) - self.cell(0, 12, title, new_x="LMARGIN", new_y="NEXT") - self.set_draw_color(0xC0, 0x39, 0x2B) - self.line(10, self.get_y(), 200, self.get_y()) - self.ln(4) - elif level == 2: - self.set_font('CN', 'B', 13) - self.set_text_color(0x2C, 0x3E, 0x50) - self.cell(0, 10, title, new_x="LMARGIN", new_y="NEXT") - self.ln(2) - elif level == 3: - self.set_font('CN', 'B', 11) - self.set_text_color(0x34, 0x49, 0x5E) - self.cell(0, 8, title, new_x="LMARGIN", new_y="NEXT") - self.ln(1) - - def body_text(self, text): - self.set_font('CN', '', 10) - self.set_text_color(0x33, 0x33, 0x33) - self.multi_cell(0, 5.5, text) - self.ln(2) - - def bullet(self, text, indent=10): - self.set_font('CN', '', 10) - self.set_text_color(0x33, 0x33, 0x33) - x = self.get_x() - self.cell(indent, 5.5, '') - self.set_font('CN', '', 10) - self.cell(5, 5.5, '• ') - self.multi_cell(0, 5.5, text) - self.ln(1) - - def code_block(self, text): - self.set_fill_color(0xF5, 0xF5, 0xF5) - self.set_text_color(0x1A, 0x1A, 0x2E) - self.set_font('Courier', '', 9) - self.ln(1) - for line in text.split('\n'): - self.cell(0, 5, ' ' + line, new_x="LMARGIN", new_y="NEXT", fill=True) - self.ln(2) - - def step_block(self, num, title, desc, code=None): - """操作步骤""" - self.set_font('CN', 'B', 10) - self.set_text_color(0xC0, 0x39, 0x2B) - self.cell(0, 6, f'步骤 {num}: {title}', new_x="LMARGIN", new_y="NEXT") - self.set_font('CN', '', 10) - self.set_text_color(0x33, 0x33, 0x33) - self.multi_cell(0, 5.5, desc) - if code: - self.code_block(code) - self.ln(2) - - def table_header(self, cols, widths): - self.set_font('CN', 'B', 9) - self.set_fill_color(0x2C, 0x3E, 0x50) - self.set_text_color(0xFF, 0xFF, 0xFF) - for i, col in enumerate(cols): - self.cell(widths[i], 7, col, border=1, align='C', fill=True) - self.ln() - - def table_row(self, cols, widths, fill=False): - self.set_font('CN', '', 9) - self.set_text_color(0x33, 0x33, 0x33) - if fill: - self.set_fill_color(0xF0, 0xF0, 0xF0) - else: - self.set_fill_color(0xFF, 0xFF, 0xFF) - for i, col in enumerate(cols): - self.cell(widths[i], 6, col, border=1, align='C', fill=True) - self.ln() - - -def build_pdf(): - pdf = ZhaTianPDF() - pdf.set_title('ZhaTianRX 接收机操作指南') - pdf.set_author('ZhaTianRC Project') - - # ==================== 封面 ==================== - pdf.add_page() - pdf.ln(40) - pdf.set_font('CN', 'B', 28) - pdf.set_text_color(0x1A, 0x1A, 0x2E) - pdf.cell(0, 15, 'ZhaTianRX', new_x="LMARGIN", new_y="NEXT", align='C') - pdf.set_font('CN', '', 16) - pdf.set_text_color(0x7F, 0x8C, 0x8D) - pdf.cell(0, 10, '接收机操作指南', new_x="LMARGIN", new_y="NEXT", align='C') - pdf.ln(10) - pdf.set_draw_color(0xC0, 0x39, 0x2B) - pdf.line(60, pdf.get_y(), 150, pdf.get_y()) - pdf.ln(10) - pdf.set_font('CN', '', 11) - pdf.set_text_color(0x55, 0x55, 0x55) - pdf.cell(0, 7, 'MCU: STC8H1K08 | 射频: NRF24L01+ | 编译器: SDCC', new_x="LMARGIN", new_y="NEXT", align='C') - pdf.cell(0, 7, '固件版本: 1.0 | 项目: ZhaTianRC', new_x="LMARGIN", new_y="NEXT", align='C') - pdf.ln(20) - pdf.set_font('CN', '', 9) - pdf.set_text_color(0x99, 0x99, 0x99) - pdf.cell(0, 7, '本文档涵盖接收机的所有功能、操作步骤及串口命令参考', new_x="LMARGIN", new_y="NEXT", align='C') - - # ==================== 目录 ==================== - pdf.add_page() - pdf.chapter_title('目录') - pdf.set_font('CN', '', 11) - toc = [ - '1. 概述', - '2. 硬件引脚分配', - '3. 首次使用 - 对频操作', - '4. 输出模式切换', - '5. 串口命令详解', - '6. 回传遥测功能', - '7. LED 状态指示', - '8. 固件烧录方法', - '9. 附录 - 技术规格', - '10. 完整功能清单(38 项)', - ] - for item in toc: - pdf.set_text_color(0x2C, 0x3E, 0x50) - pdf.cell(0, 8, item, new_x="LMARGIN", new_y="NEXT") - pdf.ln(1) - - # ==================== 1. 概述 ==================== - pdf.add_page() - pdf.chapter_title('1. 概述') - pdf.body_text( - 'ZhaTianRX 是 ZhaTianRC 遥控器项目的接收机子项目。采用 STC8H1K08 系列单片机作为主控芯片,' - 'NRF24L01+ 作为 2.4GHz 射频收发芯片。接收机从发射机接收遥控通道数据,转换为多种输出格式,' - '驱动舵机、飞控等设备。' - ) - pdf.chapter_title('主要特性', 2) - features = [ - '4 路 50Hz 舵机 PWM 输出 (P3.4-P3.7)', - 'SBUS 输出 (100kbps 8E2, 经三极管反相)', - 'CRSF/Crossfire 输出 (420kbps 8N1)', - 'UART 直通输出 (115200 8N1)', - '电池电压检测 (12位ADC)', - 'NRF24L01 遥测回传 (电压/RSSI/丢包率)', - 'EEPROM 存储对频信息', - '串口命令配置 (115200 8N1)', - '信号丢失保护 (失控回中位)', - 'NRF 模块硬件检测与自动恢复', - ] - for f in features: - pdf.bullet(f) - - # ==================== 2. 硬件引脚分配 ==================== - pdf.add_page() - pdf.chapter_title('2. 硬件引脚分配') - pdf.chapter_title('2.1 功能引脚', 2) - - cols = ['功能', '引脚', '说明'] - widths = [35, 50, 95] - pdf.table_header(cols, widths) - rows = [ - ['CH1', 'P3.7', '舵机 PWM 输出 1 (50Hz)'], - ['CH2', 'P3.6', '舵机 PWM 输出 2'], - ['CH3', 'P3.5', '舵机 PWM 输出 3'], - ['CH4', 'P3.4', '舵机 PWM 输出 4'], - ['BAT_ADC', 'P3.3/ADC11', '电池电压分压输入'], - ['LED', 'P3.2', '状态指示灯 (低电平亮)'], - ['TX', 'P3.1', 'UART1 TX / SBUS 输出 (经三极管反相)'], - ['RX', 'P3.0', 'UART1 RX / 命令输入'], - ] - for i, row in enumerate(rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('2.2 NRF24L01 连接引脚', 2) - pdf.body_text('NRF24L01 通过 STC8H 硬件 SPI 连接,IRQ 连接到了不具备中断功能的引脚,通过主循环轮询检测。') - - cols = ['信号', '引脚'] - widths = [40, 40] - pdf.table_header(cols, widths) - rows = [ - ['SCK', 'P1.5'], - ['CE', 'P1.6'], - ['IRQ', 'P1.7'], - ['CSN', 'P5.4'], - ['MISO', 'P1.3'], - ['MOSI', 'P1.4'], - ] - for i, row in enumerate(rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - # ==================== 3. 首次使用 - 对频操作 ==================== - pdf.add_page() - pdf.chapter_title('3. 首次使用 - 对频操作') - pdf.body_text( - '接收机上电后自动检测 EEPROM 中是否存有对频信息。如果没有对频数据,' - '自动进入对频模式(LED 快闪 3Hz),等待与发射机配对。' - ) - - pdf.chapter_title('3.1 首次对频', 2) - pdf.step_block(1, '给接收机上电', - '连接接收机电源(3.3V-5V),观察 LED 状态。' - '如果 EEPROM 无数据,LED 会以 3Hz 快闪表示进入对频模式。' - '如果 LED 双闪,说明 NRF24L01 模块未检测到,请检查硬件连接。') - pdf.step_block(2, '打开发射机并进入对频', - '在发射机上操作进入对频模式。发射机会在 0~125 频道上广播对频包,' - '包含同步头 (0xAA 0x55)、对频短语和地址信息。') - pdf.step_block(3, '等待自动配对', - '接收机会自动扫描所有频道,匹配对频短语(默认 "LOVE")。' - '匹配成功后自动保存地址到 EEPROM,LED 变为常亮。') - - pdf.chapter_title('3.2 重新对频', 2) - pdf.step_block(1, '清除对频信息', - '通过串口发送命令清除 EEPROM 中的对频数据:', - 'DEL(BIND)') - pdf.step_block(2, '确认接收机进入对频模式', - '发送 DEL(BIND) 后,接收机会自动擦除 EEPROM 并进入对频模式,' - 'LED 变为 3Hz 快闪。') - pdf.step_block(3, '重复首次对频步骤', - '在发射机上进入对频模式,等待自动配对。') - - pdf.chapter_title('3.3 修改对频短语', 2) - pdf.step_block(1, '发送 PHRASE 命令', - '通过串口发送新的对频短语,1~6 位字母数字:', - 'PHRASE(Abc123)') - pdf.step_block(2, '确认成功', - '接收机返回 "OK" 表示设置成功,短语已保存到 EEPROM。') - pdf.step_block(3, '重新对频', - '修改短语后需要重新对频。发送 DEL(BIND) 清除旧绑定,' - '然后确保发射机也使用相同的短语进行对频。') - - # ==================== 4. 输出模式切换 ==================== - pdf.add_page() - pdf.chapter_title('4. 输出模式切换') - pdf.body_text( - '接收机支持 4 种输出模式,默认为 PWM 模式。切换模式通过串口发送 MODEL 命令实现。' - '注意:所有输出模式共用 UART1 TX 引脚 (P3.1),切换模式时会自动调整波特率和帧格式。' - ) - - pdf.chapter_title('4.1 PWM 模式(默认)', 2) - pdf.body_text( - '4 路 50Hz 舵机 PWM 信号,通过定时器0 4us 中断生成。' - '脉宽范围 1.0ms ~ 2.0ms,中位 1.5ms。' - ) - pdf.step_block(1, '切换到 PWM 模式', '通过串口发送:', 'MODEL(PWM)') - pdf.step_block(2, '确认切换成功', '接收机返回 "OK",UART1 恢复 115200 8N1 命令模式。') - pdf.step_block(3, '连接舵机', - '将舵机信号线连接到对应的 PWM 输出引脚:\n' - ' CH1 -> P3.7\n CH2 -> P3.6\n CH3 -> P3.5\n CH4 -> P3.4\n' - '注意舵机电源需独立供电,不要从 MCU 引脚取电。') - - pdf.chapter_title('4.2 SBUS 模式', 2) - pdf.body_text( - 'SBUS 是 Futaba 制定的串行舵机总线协议,100kbps 8E2 帧格式。' - '16 通道 11-bit 编码。接收机输出经板上三极管反相,可直接连接支持 SBUS 的飞控。' - ) - pdf.step_block(1, '切换到 SBUS 模式', '通过串口发送:', 'MODEL(SBUS)') - pdf.step_block(2, '确认切换成功', '接收机返回 "OK",UART1 切换为 100kbps 8E2。') - pdf.step_block(3, '连接飞控', - '将接收机 TX (P3.1) 连接到飞控的 SBUS RX 引脚。\n' - '注意:SBUS 信号已经过三极管反相,可直接连接。') - - pdf.chapter_title('4.3 CRFS/CRSF 模式', 2) - pdf.body_text( - 'CRSF (Crossfire) 是 ExpressLRS/TBS 等使用的协议,420kbps 8N1。' - '帧格式: 0xEE + length + type + payload + CRC8。' - 'RC 通道帧 type=0x16,遥测帧 type=0x08。' - ) - pdf.step_block(1, '切换到 CRFS 模式', '通过串口发送:', 'MODEL(CRFS)') - pdf.step_block(2, '确认切换成功', '接收机返回 "OK",UART1 切换为 420kbps 8N1。') - pdf.step_block(3, '连接飞控', - '将接收机 TX (P3.1) 连接到飞控的 CRSF RX 引脚。\n' - '接收机会发送 RC 通道帧,同时接收飞控发来的 CRSF 遥测帧并转发给发射机。') - - pdf.chapter_title('4.4 UART 模式', 2) - pdf.body_text('UART 直通模式,115200 8N1,预留扩展用途。') - pdf.step_block(1, '切换到 UART 模式', '通过串口发送:', 'MODEL(UART)') - pdf.step_block(2, '确认切换成功', '接收机返回 "OK",UART1 为 115200 8N1 直通模式。') - - # ==================== 5. 串口命令详解 ==================== - pdf.add_page() - pdf.chapter_title('5. 串口命令详解') - pdf.body_text( - '通过 P3.0 (RX) 输入命令,115200 8N1,回车或换行结束命令。' - '接收机在 PWM 和 UART 模式下持续监听串口命令,不影响正常输出。' - 'SBUS 和 CRFS 模式下串口用于协议输出,命令解析暂停。' - ) - - pdf.chapter_title('5.1 命令速查表', 2) - cols = ['命令', '参数', '说明'] - widths = [48, 48, 84] - pdf.table_header(cols, widths) - cmd_rows = [ - ['MODEL(PWM)', '无', '切换到 PWM 输出模式'], - ['MODEL(SBUS)', '无', '切换到 SBUS 输出模式'], - ['MODEL(UART)', '无', '切换到 UART 直通模式'], - ['MODEL(CRFS)', '无', '切换到 CRSF 输出模式'], - ['ADDR(0x...)', '5字节十六进制', '设置 NRF24L01 地址'], - ['PHRASE(...)', '1~6位字母数字', '设置对频短语'], - ['TUN(N)', '0~125', '设置 RF 频道'], - ['DEL(BIND)', '无', '清除对频信息,进入对频'], - ['TELEM(N)', '1~10', '设置回传频率 (Hz)'], - ] - for i, row in enumerate(cmd_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('5.2 应答格式', 2) - cols = ['应答', '含义'] - widths = [40, 140] - pdf.table_header(cols, widths) - resp_rows = [ - ['OK', '命令执行成功'], - ['ERR', '命令格式错误'], - ['ERR_ADDR', '地址参数错误'], - ['ERR_RANGE', '参数超出范围'], - ['UNK', '未知命令'], - ] - for i, row in enumerate(resp_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('5.3 命令详解', 2) - - # MODEL 命令 - pdf.chapter_title('MODEL() - 切换输出模式', 3) - pdf.body_text('功能:切换接收机的输出模式。每次切换成功后,UART1 自动调整为对应协议的波特率和帧格式。') - pdf.step_block(1, '连接串口', '将 USB-TTL 转换器的 TX 接接收机 P3.0(RX),GND 接 GND。') - pdf.step_block(2, '打开串口终端', '设置 115200 8N1,打开串口。') - pdf.step_block(3, '发送命令', '输入 MODEL(PWM) 或 MODEL(SBUS) 等,回车发送。') - pdf.step_block(4, '确认应答', '接收机返回 "OK" 表示切换成功。') - - # ADDR 命令 - pdf.chapter_title('ADDR() - 设置 NRF24L01 地址', 3) - pdf.body_text('功能:修改 NRF24L01 的 5 字节通信地址。地址需与发射机一致才能通信。') - pdf.step_block(1, '准备新地址', '准备 5 个字节的十六进制值,例如 E7 E7 E7 E7 E7。') - pdf.step_block(2, '发送命令', '格式:ADDR(0xHH HH HH HH HH),例如:', 'ADDR(0xE7 E7 E7 E7 E7)') - pdf.step_block(3, '确认应答', '返回 "OK" 表示地址修改成功,NRF 已重新初始化。') - - # PHRASE 命令 - pdf.chapter_title('PHRASE() - 设置对频短语', 3) - pdf.body_text('功能:设置对频短语,1~6 位字母数字。发射机和接收机必须使用相同的短语才能对频。') - pdf.step_block(1, '确定短语', '准备 1~6 位字母数字组合,例如 "LOVE"、"Abc123"。') - pdf.step_block(2, '发送命令', '例如:', 'PHRASE(LOVE)') - pdf.step_block(3, '确认应答', '返回 "OK" 表示设置成功,已保存到 EEPROM。') - - # TUN 命令 - pdf.chapter_title('TUN() - 切换 RF 频道', 3) - pdf.body_text('功能:切换 NRF24L01 的 RF 频道 (0~125)。频道需与发射机一致。') - pdf.step_block(1, '发送命令', '例如切换到频道 40:', 'TUN(40)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示频道切换成功。') - - # DEL 命令 - pdf.chapter_title('DEL(BIND) - 清除对频信息', 3) - pdf.body_text('功能:擦除 EEPROM 中的对频数据,使接收机重新进入对频模式。') - pdf.step_block(1, '发送命令', '', 'DEL(BIND)') - pdf.step_block(2, '确认应答', '返回 "OK",LED 变为 3Hz 快闪表示进入对频模式。') - pdf.step_block(3, '重新对频', '在发射机上进入对频模式,等待自动配对。') - - # TELEM 命令 - pdf.chapter_title('TELEM() - 设置回传频率', 3) - pdf.body_text('功能:设置遥测回传频率,范围 1~10Hz,默认 2Hz。') - pdf.step_block(1, '发送命令', '例如设置为 5Hz:', 'TELEM(5)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示设置成功。') - - # ==================== 6. 回传遥测功能 ==================== - pdf.add_page() - pdf.chapter_title('6. 回传遥测功能') - pdf.body_text( - '接收机支持通过 NRF24L01 向发射机回传遥测数据。' - '不同输出模式下回传路径不同,PWM 和 SBUS 模式通过 NRF 直接回传,' - 'CRFS 模式通过 CRSF 遥测帧回传并转发飞控数据。' - ) - - pdf.chapter_title('6.1 回传数据内容', 2) - cols = ['数据项', '来源', '范围'] - widths = [40, 70, 70] - pdf.table_header(cols, widths) - telem_rows = [ - ['电池电压', 'P3.3/ADC11 12位采样', 'ADC 原始值 0~4095'], - ['RSSI', 'NRF RPD 载波检测', '0~100'], - ['丢包率', '滑动窗口估算', '0~100%'], - ] - for i, row in enumerate(telem_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('6.2 各模式回传路径', 2) - cols = ['模式', '回传通道', '数据'] - widths = [30, 55, 95] - pdf.table_header(cols, widths) - path_rows = [ - ['PWM', 'NRF24L01 TX', '0xBB 0x44 + 电压(2B) + RSSI(1B) + 丢包(1B) + 温度(2B)'], - ['SBUS', 'NRF24L01 TX', '同上'], - ['CRFS', 'CRSF 遥测帧', 'CRSF Battery sensor (type=0x08) + 飞控数据转发'], - ] - for i, row in enumerate(path_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('6.3 设置回传频率', 2) - pdf.body_text('回传频率默认 2Hz,可通过 TELEM 命令调整。频率越高,NRF 切换 TX/RX 越频繁。') - pdf.step_block(1, '查看当前频率', '回传频率默认 2Hz,无需查看命令。') - pdf.step_block(2, '修改频率', '例如设置为 5Hz:', 'TELEM(5)') - pdf.step_block(3, '确认应答', '返回 "OK" 表示设置成功。频率范围 1~10Hz。') - - pdf.chapter_title('6.4 CRFS 模式下的飞控数据转发', 2) - pdf.body_text( - '在 CRFS 模式下,接收机不仅发送 CRSF RC 通道帧到飞控,' - '还接收飞控发来的 CRSF 遥测帧(如 GPS、姿态、电池等信息),' - '通过 NRF24L01 转发给发射机,实现完整的双向通信。' - ) - pdf.step_block(1, '切换到 CRFS 模式', '', 'MODEL(CRFS)') - pdf.step_block(2, '连接飞控', - '将接收机 TX 接飞控 RX,接收机 RX 接飞控 TX。\n' - '飞控会收到 RC 通道帧,同时发送遥测帧给接收机。') - pdf.step_block(3, '自动转发', - '接收机自动将飞控发来的 CRSF 帧通过 NRF 转发给发射机,' - '无需额外配置。') - - # ==================== 7. LED 状态指示 ==================== - pdf.add_page() - pdf.chapter_title('7. LED 状态指示') - pdf.body_text( - '接收机使用 P3.2 驱动一个 LED(低电平亮),通过不同的闪烁模式指示当前工作状态。' - ) - - cols = ['LED 模式', '效果', '含义'] - widths = [35, 50, 95] - pdf.table_header(cols, widths) - led_rows = [ - ['慢闪', '1Hz: 亮500ms 灭500ms', '未连接,等待发射机信号'], - ['快闪', '3Hz: 亮167ms 灭167ms', '对频模式,正在搜索发射机'], - ['常亮', '持续点亮', '已连接,通信正常'], - ['双闪', '1Hz: 亮100ms 灭100ms 亮100ms 灭700ms', 'NRF24L01 模块硬件错误'], - ] - for i, row in enumerate(led_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('LED 状态诊断', 2) - pdf.step_block(1, 'LED 双闪', - 'NRF24L01 模块未检测到或通信失败。\n' - '检查:\n' - ' 1. NRF24L01 模块是否正确插入\n' - ' 2. 供电电压是否正常 (3.3V)\n' - ' 3. SPI 引脚连接是否正确 (P1.3-P1.5, P5.4)\n' - '接收机会每 500ms 自动重试检测,模块正常后自动恢复。') - pdf.step_block(2, 'LED 慢闪', - '接收机已初始化完成,但未收到发射机信号。\n' - '检查:\n' - ' 1. 发射机是否开机\n' - ' 2. 对频短语和频道是否匹配\n' - ' 3. 距离是否在有效范围内') - pdf.step_block(3, 'LED 快闪', - '接收机处于对频模式,正在扫描频道寻找发射机。\n' - '请在发射机上进入对频模式。') - pdf.step_block(4, 'LED 常亮', - '接收机已成功连接发射机,通信正常。\n' - '此时可以正常使用遥控器控制设备。') - - # ==================== 8. 固件烧录方法 ==================== - pdf.add_page() - pdf.chapter_title('8. 固件烧录方法') - - pdf.chapter_title('8.1 使用 stcgal (Linux/命令行)', 2) - pdf.body_text('stcgal 是一个开源的 STC 单片机烧录工具,支持命令行操作。') - pdf.step_block(1, '安装 stcgal', '通过 pip 安装:', 'pip3 install stcgal') - pdf.step_block(2, '连接硬件', - '将 USB-TTL 转换器连接到接收机:\n' - ' USB-TTL TX -> 接收机 P3.0 (RX)\n' - ' USB-TTL RX -> 接收机 P3.1 (TX)\n' - ' USB-TTL GND -> 接收机 GND\n' - '给接收机上电 (3.3V-5V)。') - pdf.step_block(3, '烧录固件', - '确认串口设备后执行:', 'stcgal -p /dev/ttyUSB0 -b 115200 build/ZhaTianRX.hex') - pdf.step_block(4, '等待烧录完成', - 'stcgal 会自动握手并烧录,完成后会显示成功信息。' - '烧录完成后接收机会自动重启运行新固件。') - - pdf.chapter_title('8.2 使用 STC-ISP (Windows)', 2) - pdf.body_text('STC-ISP 是 STC 官方提供的 Windows 烧录工具。') - pdf.step_block(1, '下载 STC-ISP', - '从 STC 官方网站下载最新版 STC-ISP 工具。') - pdf.step_block(2, '连接硬件', - '使用 USB-TTL 转换器连接接收机(同上)。') - pdf.step_block(3, '选择芯片型号', - '在 STC-ISP 中选择芯片型号:STC8H1K08。') - pdf.step_block(4, '选择固件文件', - '点击"打开程序文件",选择 build/ZhaTianRX.hex。') - pdf.step_block(5, '下载烧录', - '点击"下载/编程"按钮,然后给接收机重新上电,' - 'STC-ISP 会自动检测并烧录。') - - pdf.chapter_title('8.3 编译固件', 2) - pdf.body_text('如果修改了源代码,需要重新编译生成固件。') - pdf.step_block(1, '安装 SDCC', '确保已安装 SDCC 编译器:', 'sudo apt install sdcc') - pdf.step_block(2, '编译固件', '在项目目录下执行:', 'cd rx && make clean && make') - pdf.step_block(3, '查看输出', - '编译成功后,在 build/ 目录下生成:\n' - ' ZhaTianRX.hex - Intel HEX 格式固件\n' - ' ZhaTianRX.bin - 二进制格式固件\n' - ' ZhaTianRX.map - 内存映射文件') - - # ==================== 9. 附录 ==================== - pdf.add_page() - pdf.chapter_title('9. 附录 - 技术规格') - - pdf.chapter_title('9.1 硬件规格', 2) - cols = ['项目', '规格'] - widths = [60, 120] - pdf.table_header(cols, widths) - spec_rows = [ - ['主控芯片', 'STC8H1K08 (8051 内核)'], - ['主频', '24MHz 内部 IRC'], - ['Flash', '8KB (固件占用 ~6.8KB)'], - ['内部 RAM', '256 字节'], - ['外部 RAM (XDATA)', '256 字节'], - ['射频芯片', 'NRF24L01+ 2.4GHz'], - ['SPI 速率', '6MHz (硬件 SPI)'], - ['RF 频道', '0~125 (默认 40)'], - ['通信速率', '1Mbps'], - ['PWM 输出', '4 路, 50Hz, 1.0~2.0ms'], - ['SBUS', '100kbps 8E2'], - ['CRSF', '420kbps 8N1'], - ['UART 命令', '115200 8N1'], - ['ADC', '12位, P3.3/ADC11'], - ['工作电压', '3.3V ~ 5V'], - ] - for i, row in enumerate(spec_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('9.2 固件信息', 2) - cols = ['项目', '数值'] - widths = [60, 120] - pdf.table_header(cols, widths) - fw_rows = [ - ['编译器', 'SDCC 4.2.0'], - ['编译参数', '-mmcs51 --model-small --no-xinit-opt'], - ['固件大小', '6,756 字节 (HEX)'], - ['Flash 占用', '82% (剩余 ~1.4KB)'], - ['栈空间', '153 字节空闲'], - ['XDATA 占用', '283 字节'], - ['源码行数', '~1,350 行'], - ] - for i, row in enumerate(fw_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(4) - pdf.chapter_title('9.3 数据包格式', 2) - - pdf.chapter_title('NRF24L01 下行 (发射机 -> 接收机)', 3) - cols = ['偏移', '大小', '内容'] - widths = [30, 30, 120] - pdf.table_header(cols, widths) - pkt_rows = [ - ['0', '1', '同步头 0xAA'], - ['1', '1', '同步头 0x55'], - ['2', '6', '对频短语 (如 "LOVE")'], - ['8', '16', '8 通道数据, 每通道 2 字节大端 (0~2000)'], - ['24', '7', '保留/扩展'], - ['31', '1', 'XOR 校验和'], - ] - for i, row in enumerate(pkt_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(2) - pdf.chapter_title('NRF24L01 上行 (接收机 -> 发射机, 回传)', 3) - cols = ['偏移', '大小', '内容'] - widths = [30, 30, 120] - pdf.table_header(cols, widths) - telem_pkt_rows = [ - ['0', '1', '同步头 0xBB'], - ['1', '1', '同步头 0x44'], - ['2', '2', '电池电压 (大端)'], - ['4', '1', 'RSSI (0~100)'], - ['5', '1', '丢包率 (0~100%)'], - ['6', '2', '温度 (保留)'], - ] - for i, row in enumerate(telem_pkt_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - pdf.ln(2) - pdf.chapter_title('CRSF 帧格式', 3) - cols = ['偏移', '大小', '内容'] - widths = [30, 30, 120] - pdf.table_header(cols, widths) - crsf_rows = [ - ['0', '1', '同步头 0xEE'], - ['1', '1', '长度 (type+payload+crc)'], - ['2', '1', '类型 (0x16=RC通道, 0x08=电池遥测)'], - ['3', '可变', 'Payload'], - ['末尾', '1', 'CRC8 (多项式 0xD5)'], - ] - for i, row in enumerate(crsf_rows): - pdf.table_row(row, widths, fill=(i % 2 == 0)) - - # ==================== 10. 完整功能清单 ==================== - pdf.add_page() - pdf.chapter_title('10. 完整功能清单(38 项)') - pdf.body_text( - '以下列出 ZhaTianRX 接收机固件的全部功能,每项附带详细操作步骤。' - ) - - # --- 一、无线通信 --- - pdf.chapter_title('一、无线通信 (功能 1~8)', 2) - - # 功能1 - pdf.chapter_title('功能 1: NRF24L01 接收', 3) - pdf.body_text('通过 STC8H 硬件 SPI 接收发射机 32 字节数据包。') - pdf.step_block(1, '确认硬件连接', - '确保 NRF24L01 模块正确连接:\n' - ' SCK -> P1.5, CE -> P1.6, IRQ -> P1.7\n' - ' CSN -> P5.4, MISO -> P1.3, MOSI -> P1.4\n' - ' 供电 3.3V, GND 共地。') - pdf.step_block(2, '上电自动初始化', - '接收机上电后自动初始化 NRF24L01 模块,设置 SPI 速率 6MHz。' - '如果模块检测失败,LED 双闪报警。') - pdf.step_block(3, '接收数据', - 'NRF24L01 收到有效数据包后,接收机解析 8 通道数据(每通道 2 字节大端 0~2000),' - '更新输出 PWM/SBUS/CRSF。') - - # 功能2 - pdf.chapter_title('功能 2: 对频绑定', 3) - pdf.body_text('上电 EEPROM 无数据自动进入对频模式,扫描 0~125 频道匹配短语。') - pdf.step_block(1, '进入对频模式', - '接收机上电后检测 EEPROM。无对频数据则自动进入对频模式(LED 快闪 3Hz)。') - pdf.step_block(2, '扫描频道', - '接收机在 0~125 频道上逐频道监听,等待接收包含匹配短语的对频包。') - pdf.step_block(3, '保存绑定信息', - '匹配成功后,将发射机地址、频道写入 EEPROM,LED 变为常亮。') - - # 功能3 - pdf.chapter_title('功能 3: 对频短语', 3) - pdf.body_text('默认短语 "LOVE",支持 1~6 位字母数字,通过 PHRASE() 命令修改。') - pdf.step_block(1, '查看当前短语', '短语保存在 EEPROM 中,可通过串口读取(需在代码中扩展读取命令)。') - pdf.step_block(2, '修改短语', '发送命令修改:', 'PHRASE(Abc123)') - pdf.step_block(3, '重新对频', '修改短语后需发送 DEL(BIND) 重新对频。') - - # 功能4 - pdf.chapter_title('功能 4: 地址管理', 3) - pdf.body_text('5 字节 NRF24L01 地址,通过 ADDR() 命令修改。') - pdf.step_block(1, '发送新地址', '格式:', 'ADDR(0xE7 E7 E7 E7 E7)') - pdf.step_block(2, '确认修改', '返回 "OK" 表示成功,NRF 重新初始化。') - pdf.step_block(3, '重新对频', '修改地址后需重新对频。') - - # 功能5 - pdf.chapter_title('功能 5: 频道切换', 3) - pdf.body_text('0~125 频道,通过 TUN(N) 命令切换。') - pdf.step_block(1, '发送频道命令', '例如切到频道 40:', 'TUN(40)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示切换成功。') - - # 功能6 - pdf.chapter_title('功能 6: 数据包校验', 3) - pdf.body_text('XOR 校验和验证。') - pdf.step_block(1, '自动校验', - '接收机收到 32 字节数据包后,自动计算前 31 字节的 XOR 校验和,' - '与第 32 字节对比。不一致则丢弃该包。') - - # 功能7 - pdf.chapter_title('功能 7: 信号丢失保护', 3) - pdf.body_text('连续 ~5 秒无数据自动判丢失,PWM 回中位。') - pdf.step_block(1, '信号丢失自动处理', - '接收机内部维护一个丢失计数器。每次收到有效数据包清零,' - '主循环每 10ms 递增一次。计数超过 500(约 5 秒)判定为信号丢失。') - pdf.step_block(2, '丢失后行为', - 'PWM 模式下所有通道输出回中位(1.5ms 脉宽),LED 变为慢闪。' - '信号恢复后自动回到正常状态。') - - # 功能8 - pdf.chapter_title('功能 8: NRF 硬件检测', 3) - pdf.body_text('上电检测 NRF 模块是否存在,失败 LED 双闪并定期重试。') - pdf.step_block(1, '上电检测', - '接收机初始化时读取 NRF24L01 寄存器验证通信。失败则 LED 双闪。') - pdf.step_block(2, '自动恢复', - '接收机每 500ms 重试检测 NRF 模块。模块正常后自动恢复运行。') - - # --- 二、输出模式 --- - pdf.add_page() - pdf.chapter_title('二、输出模式 (功能 9~12)', 2) - - # 功能9 - pdf.chapter_title('功能 9: PWM 输出(默认)', 3) - pdf.body_text('4 路 50Hz 舵机 PWM,P3.4-P3.7,1.0~2.0ms 脉宽,定时器0 4us 中断生成。') - pdf.step_block(1, '切换到 PWM 模式', '', 'MODEL(PWM)') - pdf.step_block(2, '连接舵机', - 'CH1(P3.7) CH2(P3.6) CH3(P3.5) CH4(P3.4) 接舵机信号线。' - '舵机电源需独立供电。') - pdf.step_block(3, '确认输出', - '用示波器或舵机测试 PWM 信号。中位 1.5ms,范围 1.0~2.0ms。') - - # 功能10 - pdf.chapter_title('功能 10: SBUS 输出', 3) - pdf.body_text('100kbps 8E2,16 通道 11-bit 编码,经三极管反相输出。') - pdf.step_block(1, '切换到 SBUS 模式', '', 'MODEL(SBUS)') - pdf.step_block(2, '连接飞控', - '接收机 TX(P3.1) 接飞控 SBUS RX。信号已反相,可直接连接。') - pdf.step_block(3, '验证输出', - '用逻辑分析仪或飞控调参软件查看 16 通道数据。') - - # 功能11 - pdf.chapter_title('功能 11: UART 输出', 3) - pdf.body_text('115200 8N1 直通,预留扩展。') - pdf.step_block(1, '切换到 UART 模式', '', 'MODEL(UART)') - pdf.step_block(2, '连接设备', 'TX(P3.1) 接目标设备 RX。波特率 115200 8N1。') - - # 功能12 - pdf.chapter_title('功能 12: CRFS/CRSF 输出', 3) - pdf.body_text('420kbps 8N1,CRSF RC 通道帧 (type=0x16),CRC8 校验。') - pdf.step_block(1, '切换到 CRFS 模式', '', 'MODEL(CRFS)') - pdf.step_block(2, '连接飞控', - 'TX(P3.1) 接飞控 CRSF RX,RX(P3.0) 接飞控 CRSF TX(双向通信)。') - pdf.step_block(3, '验证双向通信', - '飞控应收到 RC 通道帧,同时接收机转发飞控遥测数据给发射机。') - - # --- 三、回传遥测 --- - pdf.add_page() - pdf.chapter_title('三、回传遥测 (功能 13~19)', 2) - - # 功能13 - pdf.chapter_title('功能 13: 电池电压回传', 3) - pdf.body_text('BAT_ADC(P3.3) 12 位 ADC 采样,所有模式均可回传。') - pdf.step_block(1, '连接电池分压电路', - '电池电压经分压电阻接入 P3.3。分压比需确保最高电压不超过 3.3V。') - pdf.step_block(2, '读取电压', - '接收机自动 12 位 ADC 采样,通过回传通道发送给发射机。' - '发射机端显示电压值。') - - # 功能14 - pdf.chapter_title('功能 14: RSSI 回传', 3) - pdf.body_text('基于 NRF RPD 载波检测寄存器估算 (0-100)。') - pdf.step_block(1, '自动采集', - '接收机在每个接收周期读取 NRF RPD 寄存器,估算信号强度。') - pdf.step_block(2, '查看 RSSI', 'RSSI 值通过回传通道发送给发射机,在发射机端显示。') - - # 功能15 - pdf.chapter_title('功能 15: 丢包率回传', 3) - pdf.body_text('滑动窗口估算 (0-100%)。') - pdf.step_block(1, '自动计算', - '接收机维护滑动窗口,统计最近 N 个数据包中丢失的比例。') - pdf.step_block(2, '查看丢包率', '丢包率通过回传通道发送给发射机。') - - # 功能16 - pdf.chapter_title('功能 16: NRF 回传(PWM/SBUS)', 3) - pdf.body_text('PWM/SBUS 模式下通过 NRF TX 发 0xBB 0x44 回传包。') - pdf.step_block(1, '自动回传', - '在 PWM 或 SBUS 模式下,接收机自动在接收间隙切换 NRF 为 TX 模式发送回传包。') - pdf.step_block(2, '配置回传频率', '默认 2Hz,可通过 TELEM 命令调整。', 'TELEM(5)') - - # 功能17 - pdf.chapter_title('功能 17: CRSF 遥测回传', 3) - pdf.body_text('CRFS 模式下通过 CRSF Battery sensor 帧 (type=0x08) 回传。') - pdf.step_block(1, '自动发送', - '在 CRFS 模式下,接收机自动构造 CRSF Battery sensor 遥测帧,' - '通过 UART 发送给飞控(飞控再转发给遥控器)。') - - # 功能18 - pdf.chapter_title('功能 18: 飞控数据转发', 3) - pdf.body_text('CRFS 模式下接收飞控 UART 发来的 CRSF 帧,通过 NRF 转发给发射机。') - pdf.step_block(1, '自动转发', - '接收机在 CRFS 模式下持续监听 UART RX,收到飞控发来的 CRSF 帧后,' - '通过 NRF 转发给发射机。无需额外配置。') - - # 功能19 - pdf.chapter_title('功能 19: 回传频率可调', 3) - pdf.body_text('1~10Hz,默认 2Hz,通过 TELEM(N) 命令设置。') - pdf.step_block(1, '设置频率', '例如设置为 5Hz:', 'TELEM(5)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示设置成功。') - - # --- 四、串口命令系统 --- - pdf.add_page() - pdf.chapter_title('四、串口命令系统 (功能 20~28)', 2) - - # 功能20-23 - for cmd_name, cmd_code, desc in [ - ('功能 20: MODEL(PWM)', 'MODEL(PWM)', '切 PWM 模式'), - ('功能 21: MODEL(SBUS)', 'MODEL(SBUS)', '切 SBUS 模式'), - ('功能 22: MODEL(UART)', 'MODEL(UART)', '切 UART 模式'), - ('功能 23: MODEL(CRFS)', 'MODEL(CRFS)', '切 CRSF 模式'), - ]: - pdf.chapter_title(cmd_name, 3) - pdf.body_text(desc) - pdf.step_block(1, '发送命令', '', cmd_code) - pdf.step_block(2, '确认应答', '返回 "OK" 表示切换成功。') - - # 功能24 - pdf.chapter_title('功能 24: ADDR() - 设置 NRF 地址', 3) - pdf.body_text('设置 5 字节 NRF 地址。') - pdf.step_block(1, '发送命令', '格式:ADDR(0xHH HH HH HH HH)', 'ADDR(0xE7 E7 E7 E7 E7)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示成功。') - - # 功能25 - pdf.chapter_title('功能 25: PHRASE() - 设置对频短语', 3) - pdf.body_text('设置对频短语 (1-6位字母数字)。') - pdf.step_block(1, '发送命令', '', 'PHRASE(LOVE)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示成功。') - - # 功能26 - pdf.chapter_title('功能 26: TUN() - 设置 RF 频道', 3) - pdf.body_text('设置 RF 频道 (0-125)。') - pdf.step_block(1, '发送命令', '', 'TUN(40)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示成功。') - - # 功能27 - pdf.chapter_title('功能 27: DEL(BIND) - 清除对频信息', 3) - pdf.body_text('清除 EEPROM 对频信息,重新对频。') - pdf.step_block(1, '发送命令', '', 'DEL(BIND)') - pdf.step_block(2, '确认应答', '返回 "OK",LED 快闪进入对频模式。') - - # 功能28 - pdf.chapter_title('功能 28: TELEM() - 设置回传频率', 3) - pdf.body_text('设置回传频率 (1-10Hz)。') - pdf.step_block(1, '发送命令', '', 'TELEM(5)') - pdf.step_block(2, '确认应答', '返回 "OK" 表示成功。') - - # --- 五、LED 状态指示 --- - pdf.add_page() - pdf.chapter_title('五、LED 状态指示 (功能 29~32)', 2) - - # 功能29 - pdf.chapter_title('功能 29: 慢闪 (1Hz)', 3) - pdf.body_text('未连接(等待发射机)。') - pdf.step_block(1, '观察 LED', 'LED 以 1Hz 慢闪(亮 500ms 灭 500ms),表示接收机已初始化但未收到发射机信号。') - pdf.step_block(2, '排查原因', - '检查发射机是否开机、对频短语和频道是否匹配、距离是否在有效范围内。') - - # 功能30 - pdf.chapter_title('功能 30: 快闪 (3Hz)', 3) - pdf.body_text('对频中。') - pdf.step_block(1, '观察 LED', 'LED 以 3Hz 快闪(亮 167ms 灭 167ms),表示接收机正在对频模式。') - pdf.step_block(2, '操作', '在发射机上进入对频模式,等待自动配对。') - - # 功能31 - pdf.chapter_title('功能 31: 常亮', 3) - pdf.body_text('已连接正常。') - pdf.step_block(1, '观察 LED', 'LED 持续点亮,表示接收机已成功连接发射机,通信正常。') - pdf.step_block(2, '正常使用', '此时可以正常使用遥控器控制设备。') - - # 功能32 - pdf.chapter_title('功能 32: 双闪 (1Hz)', 3) - pdf.body_text('NRF 模块硬件错误。') - pdf.step_block(1, '观察 LED', 'LED 以双闪模式(亮100ms 灭100ms 亮100ms 灭700ms)报警。') - pdf.step_block(2, '排查原因', - '检查 NRF24L01 模块是否插好、供电 3.3V 是否正常、SPI 引脚连接是否正确。' - '接收机会自动重试,模块正常后恢复。') - - # --- 六、存储与配置 --- - pdf.add_page() - pdf.chapter_title('六、存储与配置 (功能 33~34)', 2) - - # 功能33 - pdf.chapter_title('功能 33: EEPROM 存储', 3) - pdf.body_text('通过 IAP 读写,保存对频标志、地址、频道、短语。') - pdf.step_block(1, '自动保存', - '对频成功后,接收机自动将地址、频道、短语写入 EEPROM。' - '下次上电直接读取使用。') - pdf.step_block(2, '清除数据', - '发送 DEL(BIND) 命令擦除 EEPROM 对频数据。') - - # 功能34 - pdf.chapter_title('功能 34: 上电自动恢复', 3) - pdf.body_text('检测 EEPROM 对频标志,有数据直接进入接收模式。') - pdf.step_block(1, '上电流程', - '接收机上电后读取 EEPROM 对频标志。\n' - ' 有数据 -> 直接进入接收模式,LED 慢闪等待信号\n' - ' 无数据 -> 进入对频模式,LED 快闪') - - # --- 七、硬件外设 --- - pdf.chapter_title('七、硬件外设 (功能 35~38)', 2) - - # 功能35 - pdf.chapter_title('功能 35: 硬件 SPI', 3) - pdf.body_text('STC8H 内置 SPI 主模式,6MHz,驱动 NRF24L01。') - pdf.step_block(1, '自动初始化', - '接收机上电后自动配置 STC8H 硬件 SPI 为主模式,速率 6MHz。') - pdf.step_block(2, '无需用户操作', - 'SPI 通信完全由固件自动管理,用户无需干预。') - - # 功能36 - pdf.chapter_title('功能 36: 定时器0', 3) - pdf.body_text('4us 中断,生成 4 路 PWM + 系统滴答 (10ms)。') - pdf.step_block(1, '自动配置', - '定时器0 配置为 4us 中断,用于生成 PWM 信号和系统滴答时钟。') - pdf.step_block(2, '无需用户操作', '定时器由固件自动管理。') - - # 功能37 - pdf.chapter_title('功能 37: UART1', 3) - pdf.body_text('使用 Timer2 做波特率发生器,支持 115200/100k/420k 切换。') - pdf.step_block(1, '自动切换', - '切换输出模式时,UART1 自动调整波特率和帧格式。\n' - ' PWM/UART: 115200 8N1\n' - ' SBUS: 100kbps 8E2\n' - ' CRFS: 420kbps 8N1') - - # 功能38 - pdf.chapter_title('功能 38: ADC', 3) - pdf.body_text('12 位,P3.3/ADC11,电池电压采样。') - pdf.step_block(1, '连接分压电路', - '将电池电压经分压电阻接入 P3.3。确保最高电压不超过 3.3V。') - pdf.step_block(2, '自动采样', - '接收机自动进行 12 位 ADC 采样,结果通过回传通道发送给发射机。') - - # ==================== 保存 ==================== - output_path = '/root/ZhaTianRC/rx/操作指南.pdf' - pdf.output(output_path) - print(f'PDF 已生成: {output_path}') - print(f'共 {pdf.page_no()} 页') - - -if __name__ == '__main__': - build_pdf() diff --git a/rx/hex2bin.py b/rx/hex2bin.py deleted file mode 100644 index 2be1a1b..0000000 --- a/rx/hex2bin.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -"""Convert Intel HEX to binary""" -import sys - -if len(sys.argv) < 3: - print("Usage: hex2bin.py ") - sys.exit(1) - -with open(sys.argv[1], 'r') as f: - lines = f.readlines() - -data = bytearray() -for line in lines: - line = line.strip() - if not line or line[0] != ':': - continue - count = int(line[1:3], 16) - rtype = int(line[7:9], 16) - if rtype == 0: # Data record - for i in range(count): - byte_val = int(line[9+i*2:11+i*2], 16) - data.append(byte_val) - elif rtype == 1: # EOF - break - -with open(sys.argv[2], 'wb') as f: - f.write(data) - -print(f"Binary: {len(data)} bytes") diff --git a/rx/main.c b/rx/main.c index 6cb9bd7..76e7b7e 100644 --- a/rx/main.c +++ b/rx/main.c @@ -23,61 +23,42 @@ #include "nrf24.h" #include "uart.h" -void EEPROM_LoadConfig(); -void Init(); +void LoadConfig(); +void SystemInit(); +void PWMBInit(); void main(void) { /* 系统初始化 */ - System_Init(); - GPIO_Init(); - Timer0_Init(); - UART1_Init(); - ADC_Init(); - IAP_Init(); + SystemInit(); + PWMBInit(); /* 初始化NRF24L01 */ NRF24L01_Init(); - /* 初始PWM: 中位 */ - PWM_SetAllFailsafe(); - - // 从EEPROM加载配置 */ - EEPROM_LoadConfig(); + // 从EEPROM加载配置并应用 */ + InitSet(LoadConfig()); /* 检测NRF模块 */ if (!NRF24L01_Check()) { /* NRF模块未检测到, 设置硬件错误标志, LED双闪 */ - nrf_hw_error = 1; + SysStatus = NRF_ERROR; + while (1) LED_Update(); } while (1) { - /* 处理串口命令 */ - UART1_ProcessCmd(); - - if (NRF_IRQ == 1) { - // 处理NRF24L01接收数据 - NRF24L01_ProcessRX(); - // 更新输出数据 - outputFun(); - } - - /* LED状态指示 */ LED_Update(); } } -void Init() { +void SystemInit() { // System_Init() - /* 切换到内部24MHz IRC */ - P_SW2 |= EAXFR; /* 使能访问扩展SFR */ - CLKDIV = 0x00; /* 主时钟不分频 */ - CKSEL = 0x00; /* 选择内部高速IRC */ - P_SW2 &= ~EAXFR; + // 使用stc-isp下载时, 系统时钟已设置为内部IRC 40MHz, 可省略 + P_SW2 |= 0x80; // 使能访问扩展SFR(XFR) + // 选择内部高速IRC作为主时钟源(复位后默认即为内部IRC,可省略) + CLKSEL = 0x00; // MCKSEL[1:0]=00 选择内部IRC + // 主时钟不分频 + CLKDIV = 0x00; // 系统时钟 = 主时钟 / 1 = 40MHz - /* 设置内部IRC为24MHz */ - IRC24MCR = 0x80; /* 使能内部24MHz IRC */ - while (!(IRC24MCR & 0x01)); /* 等待IRC稳定 */ - // GPIO_Init() /* P3.4-P3.7: 推挽输出 (PWM通道) */ P3M0 |= 0xF0; @@ -127,14 +108,136 @@ void Init() { // Timer_Init() /* 16位自动重载模式, 1T */ - TMOD &= ~0x0F; - TMOD |= 0x01; /* T0: 16位定时器 */ - AUXR |= T0x12; /* T0: 1T模式 */ - - /* 4us @ 24MHz: 96 ticks */ - TH0 = 0xFF; - TL0 = 0xA0; /* 65536 - 96 = 0xFFA0 */ - + TMOD &= 0xF0; /* 清除T0模式低4位 */ + TMOD |= 0x00; /* T0: 16位定时器 */ + AUXR |= 0x80; /* 不分频,T0x12=1,定时器0时钟 = SYSCLK */ + /* 50us @ 40MHz */ + TH0 = 0xFE; + TL0 = 0x70; ET0 = 1; /* 使能T0中断 */ + EA = 1; TR0 = 1; /* 启动T0 */ -} \ No newline at end of file + + // UART_Init() + /* 使用 Timer2 做波特率发生器 */ + /* 115200 @ 24MHz: T2 reload = 65536 - 52 = 0xFFCC */ + T2H = 0xFF; + T2L = 0xCC; + AUXR |= T2x12; /* T2: 1T模式 */ + AUXR |= T2R; /* 启动T2 */ + + /* UART1 配置: 8位可变波特率, 模式1 */ + SCON = 0x50; /* 模式1, REN=1 */ + AUXR |= S1ST2; /* UART1使用T2做波特率 */ + PCON &= ~SMOD0; /* SMOD0=0, 帧格式8位数据 */ + + /* 使能接收中断 */ + ES = 1; + RI = 0; + + /* SPI 初始化: 主模式, 模式0, 5MHz @ 40MHz */ + // 1. 配置 SPI 时钟频率 (SPR[1:0] = 01) + // 同时设置其他控制位: + // SSIG = 1 (忽略SS引脚,由MSTR控制主从)[reference:7] + // SPEN = 1 (使能SPI)[reference:8] + // DORD = 0 (MSB先发)[reference:9] + // MSTR = 1 (主机模式)[reference:10] + // CPOL = 0 (时钟空闲时为低电平)[reference:11] + // CPHA = 0 (第一个时钟沿采样)[reference:12] + // 组合得到 SPCTL = 0b11010001 = 0xD1 + SPCTL = 0xD1; + // 2. 清空中断标志 + SPSTAT = 0xC0; + // 3. (可选) 使能SPI中断 + // IE2 |= ESPI; +} +void PWMBInit() { + // 1. 使能访问扩展特殊功能寄存器 (XFR) + P_SW2 |= 0x80; + + // 2. 配置时基:1MHz 计数时钟,20ms 周期, 基于40MHz系统 + PWMB_PSCR = 39; // 预分频器 + PWMB_ARR = 19999; // 自动重装载值 + + // 3. 配置四个通道为 PWM 模式1 (PWM1),使能预装载 + PWMB_CCMR1 = 0x68; // 通道5: PWM模式1, 预装载使能 + PWMB_CCMR2 = 0x68; // 通道6 + PWMB_CCMR3 = 0x68; // 通道7 + PWMB_CCMR4 = 0x68; // 通道8 + + // 4. 设置初始占空比(例如 1.5ms,对应 90°) + PWMB_CCR5 = 1500; + PWMB_CCR6 = 1500; + PWMB_CCR7 = 1500; + PWMB_CCR8 = 1500; + + // 5. 配置 GPIO 为推挽输出 + // P1.7 + P1M0 |= 0x80; // P1M0.7 = 1 + P1M1 &= ~0x80; // P1M1.7 = 0 + // P5.4 + P5M0 |= 0x10; // P5M0.4 = 1 + P5M1 &= ~0x10; // P5M1.4 = 0 + // P3.3, P3.4 + P3M0 |= 0x18; // bit3 and bit4 + P3M1 &= ~0x18; + + // 6. 引脚功能映射 (选择第二组引脚) + PWMB_PS = PWMB_PS_VALUE; // 0x55 + + // 7. 使能输出 + PWMB_ENO = 0x55; // bit0: ENO5P, bit2: ENO6P, bit4: ENO7P, bit6: ENO8P + PWMB_CCER1 = 0x11; // CC5E=1, CC6E=1 + PWMB_CCER2 = 0x11; // CC7E=1, CC8E=1 + + // 8. 主输出使能 + PWMB_BKR |= 0x80; // MOE=1 + + // 9. 启动计数器 + PWMB_CR1 |= 0x01; // CEN=1 +} +void LED_Update(void) { + switch (led_mode) { + case UNCONNECTED: + // 未连接状态,1Hz闪烁 + if (sys_tick < 500) P32 = 1; else P32 = 0; + break; + case NRF_BINDING: + // 对频状态,2Hz闪烁 + if (sys_tick % 500 < 250) P32 = 1; else P32 = 0; + break; + case NRF_ERROR: + // NRF硬件错误状态,双闪 + if (sys_tick < 100) P32 = 0; /* 亮 100ms */ + else if (sys_tick < 200) P32 = 1; /* 灭 100ms */ + else if (sys_tick < 300) P32 = 0; /* 亮 100ms */ + else P32 = 1; /* 灭 700ms */ + break; + case CONNECTED: + // 已连接状态,常亮 + P32 = 1; + break; + default: + P32 = 0; + break; + } +} +void Servo_SetAngle(uint16 CH_Value[4]) { + // 角度范围 0 ~ 180,对应脉宽 0.5ms ~ 2.5ms,即500~2500 + // CH_Value范围1000~2000,值为1500时中点 + PWMB_CCR5 = CH_Value[0]*2-1500; + PWMB_CCR6 = CH_Value[1]*2-1500; + PWMB_CCR7 = CH_Value[2]*2-1500; + PWMB_CCR8 = CH_Value[3]*2-1500; +} + +// 中断 +void tm0_isr(void) __interrupt(1) { + // 40MHz系统下10us中断, 用于系统滴答 + static idata uint8 sys_tick_cnt = 0; + /* 系统滴答 (1ms) */ + if (sys_tick_cnt++ >= 100) { + sys_tick_cnt = 0; + if (sys_tick++ >= 1000) sys_tick = 0; // 1秒清零 + } +} diff --git a/rx/src/crfs.c b/rx/src/crfs.c deleted file mode 100644 index 9c0ab34..0000000 --- a/rx/src/crfs.c +++ /dev/null @@ -1,62 +0,0 @@ -static uint8_t CRSF_CalcCRC(uint8_t *data, uint8_t len) { - uint8_t crc = 0; - for (uint8_t i = 0; i < len; i++) - crc = crsf_crc8_tab[crc ^ data[i]]; - return crc; -} - -static void CRFS_Init(void) { - /* 切换到 CRSF 波特率 420000 */ - /* 420000 @ 24MHz: T2 reload = 65536 - 14 = 0xFFF2 */ - T2H = 0xFF; - T2L = 0xF2; - AUXR |= T2x12; - AUXR |= T2R; - - /* 8N1: 8数据位, 无校验, 1停止位 */ - PCON &= ~SMOD0; /* SMOD0=0, 标准帧格式 */ - SCON = 0x50; /* 模式1, REN=1, 8N1 */ -} - -static void CRFS_SendFrame(void) { - static uint8_t __xdata frame[28]; /* 0xEE + len + type + 22payload + crc */ - static uint16_t __xdata crsf_ch[16]; - uint8_t i; - - /* CRSF RC通道帧: type=0x16 */ - frame[0] = 0xEE; /* sync */ - frame[1] = 24; /* length (type + payload + crc = 1+22+1) */ - frame[2] = 0x16; /* RC channels type */ - - /* 将4路pwm值(250-500)映射到CRSF范围(0-1984, 中位992) */ - for (i = 0; i < 4; i++) { - uint16_t val = pwm_ch[i]; - if (val < PWM_MIN) val = PWM_MIN; - if (val > PWM_MAX) val = PWM_MAX; - /* pwm_ch: 250~500 → CRSF: 0~1984 (中位992) */ - crsf_ch[i] = (uint16_t)((uint32_t)(val - PWM_MIN) * 1984 / (PWM_MAX - PWM_MIN)); - } - for (i = 4; i < 16; i++) { - crsf_ch[i] = 992; /* 中位 */ - } - - /* 11-bit 打包到 payload (22字节) */ - uint8_t *pl = frame + 3; - pl[0] = (uint8_t)(crsf_ch[0] & 0xFF); - pl[1] = (uint8_t)((crsf_ch[0] >> 8) | ((crsf_ch[1] & 0x07) << 3)); - pl[2] = (uint8_t)((crsf_ch[1] >> 3) | ((crsf_ch[2] & 0x3F) << 5)); - pl[3] = (uint8_t)((crsf_ch[2] >> 6) | ((crsf_ch[3] & 0x01) << 2) | ((crsf_ch[3] & 0x01) << 2)); - /* 剩余通道用中间值填充 */ - for (i = 4; i < 22; i++) - pl[i] = 0x00; - - /* CRC8 (从 type 开始到 payload 结束) */ - frame[25] = CRSF_CalcCRC(frame + 2, 23); /* type(1) + payload(22) */ - - /* 通过UART1发送 */ - for (i = 0; i < 26; i++) { - SBUF = frame[i]; - while (!TI); - TI = 0; - } -} diff --git a/rx/src/crfs.h b/rx/src/crfs.h deleted file mode 100644 index 0118e5d..0000000 --- a/rx/src/crfs.h +++ /dev/null @@ -1,43 +0,0 @@ -/* ======================================================================== - * CRFS (CRSF/Crossfire) 输出 (420kbps 8N1, 与UART1共用TX) - * 帧格式: 0xEE + len + type + payload + crc8 - * RC通道帧: type=0x16, payload=22字节 (16ch × 11bit) - * ======================================================================== */ -/* CRC8 查表 (多项式 0xD5, CRSF 标准, 放在 CODE 区) */ -static const uint8_t __code crsf_crc8_tab[256] = { - 0x00, 0xD5, 0x7F, 0xAA, 0xFE, 0x2B, 0x81, 0x54, - 0x29, 0xFC, 0x56, 0x83, 0xD7, 0x02, 0xA8, 0x7D, - 0x52, 0x87, 0x2D, 0xF8, 0xAC, 0x79, 0xD3, 0x06, - 0x7B, 0xAE, 0x04, 0xD1, 0x85, 0x50, 0xFA, 0x2F, - 0xA4, 0x71, 0xDB, 0x0E, 0x5A, 0x8F, 0x25, 0xF0, - 0x8D, 0x58, 0xF2, 0x27, 0x73, 0xA6, 0x0C, 0xD9, - 0xF6, 0x23, 0x89, 0x5C, 0x08, 0xDD, 0x77, 0xA2, - 0xDF, 0x0A, 0xA0, 0x75, 0x21, 0xF4, 0x5E, 0x8B, - 0x9D, 0x48, 0xE2, 0x37, 0x63, 0xB6, 0x1C, 0xC9, - 0xB4, 0x61, 0xCB, 0x1E, 0x4A, 0x9F, 0x35, 0xE0, - 0xCF, 0x1A, 0xB0, 0x65, 0x31, 0xE4, 0x4E, 0x9B, - 0xE6, 0x33, 0x99, 0x4C, 0x18, 0xCD, 0x67, 0xB2, - 0x39, 0xEC, 0x46, 0x93, 0xC7, 0x12, 0xB8, 0x6D, - 0x10, 0xC5, 0x6F, 0xBA, 0xEE, 0x3B, 0x91, 0x44, - 0x6B, 0xBE, 0x14, 0xC1, 0x95, 0x40, 0xEA, 0x3F, - 0x42, 0x97, 0x3D, 0xE8, 0xBC, 0x69, 0xC3, 0x16, - 0xEF, 0x3A, 0x90, 0x45, 0x11, 0xC4, 0x6E, 0xBB, - 0xC6, 0x13, 0xB9, 0x6C, 0x38, 0xED, 0x47, 0x92, - 0xBD, 0x68, 0xC2, 0x17, 0x43, 0x96, 0x3C, 0xE9, - 0x94, 0x41, 0xEB, 0x3E, 0x6A, 0xBF, 0x15, 0xC0, - 0x4B, 0x9E, 0x34, 0xE1, 0xB5, 0x60, 0xCA, 0x1F, - 0x62, 0xB7, 0x1D, 0xC8, 0x9C, 0x49, 0xE3, 0x36, - 0x19, 0xCC, 0x66, 0xB3, 0xE7, 0x32, 0x98, 0x4D, - 0x30, 0xE5, 0x4F, 0x9A, 0xCE, 0x1B, 0xB1, 0x64, - 0x72, 0xA7, 0x0D, 0xD8, 0x8C, 0x59, 0xF3, 0x26, - 0x5B, 0x8E, 0x24, 0xF1, 0xA5, 0x70, 0xDA, 0x0F, - 0x20, 0xF5, 0x5F, 0x8A, 0xDE, 0x0B, 0xA1, 0x74, - 0x09, 0xDC, 0x76, 0xA3, 0xF7, 0x22, 0x88, 0x5D, - 0xD6, 0x03, 0xA9, 0x7C, 0x28, 0xFD, 0x57, 0x82, - 0xFF, 0x2A, 0x80, 0x55, 0x01, 0xD4, 0x7E, 0xAB, - 0x84, 0x51, 0xFB, 0x2E, 0x7A, 0xAF, 0x05, 0xD0, - 0xAD, 0x78, 0xD2, 0x07, 0x53, 0x86, 0x2C, 0xF9 -}; - -static uint8_t CRSF_CalcCRC(uint8_t *data, uint8_t len); -static void CRFS_Init(void); \ No newline at end of file diff --git a/rx/src/eeprom.h b/rx/src/eeprom.h deleted file mode 100644 index 0c24ab2..0000000 --- a/rx/src/eeprom.h +++ /dev/null @@ -1,12 +0,0 @@ -# ifndef CONFIG -# define CONFIG -# include "config.h" -# endif - -static void IAP_Init(void); -static uint8_t IAP_ReadByte(uint16_t addr); -static void IAP_WriteByte(uint16_t addr, uint8_t dat); -static void IAP_EraseSector(uint16_t addr); -static uint8_t EEPROM_IsBound(void); -static void EEPROM_LoadConfig(void); -static void EEPROM_SaveConfig(void); diff --git a/rx/src/nrf24.c b/rx/src/nrf24.c index eecd3e3..432c4b8 100644 --- a/rx/src/nrf24.c +++ b/rx/src/nrf24.c @@ -1,16 +1,10 @@ - /* ======================================================================== * NRF24L01 硬件 SPI 实现 (STC8H 内置 SPI) * P1.3=MISO, P1.4=MOSI, P1.5=SCLK, P5.4=CSN(GPIO) + * P1.7=IRQ, P1.6=CE, * ======================================================================== */ -/* SPI 初始化: 主模式, 模式0, 6MHz @ 24MHz */ -static void NRF_SPI_Init(void) { - SPCTL = SSIG | SPEN | MSTR; /* 0xD0: SSIG=1, SPEN=1, MSTR=1, CPOL=0, CPHA=0, fosc/4 */ - SPSTAT = SPIF | WCOL; /* 清除标志 */ -} - -uint8_t NRF_SPI_TransferByte(uint8_t dat) { +uint8_t SPI_ReadWrite(uint8_t dat) { SPDAT = dat; /* 等待传输完成 */ while (!(SPSTAT & SPIF)); diff --git a/rx/src/nrf24.h b/rx/src/nrf24.h index 6f1e2a7..ecb8551 100644 --- a/rx/src/nrf24.h +++ b/rx/src/nrf24.h @@ -7,11 +7,6 @@ * SPI 引脚: SCK=P1.5, CE=P1.6, IRQ=P1.7, CSN=P5.4, MISO=P1.3, MOSI=P1.4 * 使用 STC8H 硬件 SPI (SPI 默认引脚) */ - -/* SDCC mcs51 模式下缺少的标准类型 */ -typedef unsigned char uint8_t; -typedef unsigned int uint16_t; -typedef unsigned long uint32_t; /* ========== 引脚定义 (SDCC 语法) ========== */ __sbit __at(0x95) NRF_SCK; /* P1^5 */ @@ -21,89 +16,76 @@ __sbit __at(0xCC) NRF_CSN; /* P5^4 */ __sbit __at(0x93) NRF_MISO; /* P1^3 */ __sbit __at(0x94) NRF_MOSI; /* P1^4 */ -/* ========== 寄存器地址 ========== */ -#define NRF_CONFIG 0x00 -#define NRF_EN_AA 0x01 -#define NRF_EN_RXADDR 0x02 -#define NRF_SETUP_AW 0x03 -#define NRF_SETUP_RETR 0x04 -#define NRF_RF_CH 0x05 -#define NRF_RF_SETUP 0x06 -#define NRF_STATUS 0x07 -#define NRF_OBSERVE_TX 0x08 -#define NRF_RPD 0x09 -#define NRF_RX_ADDR_P0 0x0A -#define NRF_RX_ADDR_P1 0x0B -#define NRF_RX_ADDR_P2 0x0C -#define NRF_RX_ADDR_P3 0x0D -#define NRF_RX_ADDR_P4 0x0E -#define NRF_RX_ADDR_P5 0x0F -#define NRF_TX_ADDR 0x10 -#define NRF_RX_PW_P0 0x11 -#define NRF_RX_PW_P1 0x12 -#define NRF_RX_PW_P2 0x13 -#define NRF_RX_PW_P3 0x14 -#define NRF_RX_PW_P4 0x15 -#define NRF_RX_PW_P5 0x16 -#define NRF_FIFO_STATUS 0x17 -#define NRF_DYNPD 0x1C -#define NRF_FEATURE 0x1D +////////////////////////////////////////////////////////////////////////////////////////////////////////// +//NRF24L01寄存器操作命令 +#define NRF_READ_REG 0x00 //读配置寄存器,低5位为寄存器地址 +#define NRF_WRITE_REG 0x20 //写配置寄存器,低5位为寄存器地址 +#define RD_RX_PLOAD 0x61 //读RX有效数据,1~32字节 +#define WR_TX_PLOAD 0xA0 //写TX有效数据,1~32字节 +#define FLUSH_TX 0xE1 //清除TX FIFO寄存器.发射模式下用 +#define FLUSH_RX 0xE2 //清除RX FIFO寄存器.接收模式下用 +#define REUSE_TX_PL 0xE3 //重新使用上一包数据,CE为高,数据包被不断发送. +#define NOP 0xFF //空操作,可以用来读状态寄存器 +//SPI(NRF24L01)寄存器地址 +#define CONFIG 0x00 //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能; + //bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能 +#define EN_AA 0x01 //使能自动应答功能 bit0~5,对应通道0~5 +#define EN_RXADDR 0x02 //接收地址允许,bit0~5,对应通道0~5 +#define SETUP_AW 0x03 //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节; +#define SETUP_RETR 0x04 //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us +#define RF_CH 0x05 //RF通道,bit6:0,工作通道频率; +#define RF_SETUP 0x06 //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益 +#define STATUS 0x07 //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发 + //bit5:数据发送完成中断;bit6:接收数据中断; +#define MAX_TX 0x10 //达到最大发送次数中断 +#define TX_OK 0x20 //TX发送完成中断 +#define RX_OK 0x40 //接收到数据中断 -/* ========== 命令 ========== */ -#define NRF_CMD_R_REGISTER 0x00 -#define NRF_CMD_W_REGISTER 0x20 -#define NRF_CMD_R_RX_PAYLOAD 0x61 -#define NRF_CMD_W_TX_PAYLOAD 0xA0 -#define NRF_CMD_FLUSH_TX 0xE1 -#define NRF_CMD_FLUSH_RX 0xE2 -#define NRF_CMD_REUSE_TX_PL 0xE3 -#define NRF_CMD_NOP 0xFF - -/* ========== 状态标志 ========== */ -#define NRF_STATUS_RX_DR 0x40 -#define NRF_STATUS_TX_DS 0x20 -#define NRF_STATUS_MAX_RT 0x10 - -/* ========== 配置常量 ========== */ -#define NRF_RATE_250K 0x20 -#define NRF_RATE_1M 0x00 -#define NRF_RATE_2M 0x08 - -#define NRF_PA_MIN 0x00 -#define NRF_PA_LOW 0x02 -#define NRF_PA_HIGH 0x04 -#define NRF_PA_MAX 0x06 - -#define NRF_ADDR_WIDTH 5 -#define NRF_MAX_CHANNEL 125 -#define NRF_PAYLOAD_SIZE 32 +#define OBSERVE_TX 0x08 //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器 +#define CD 0x09 //载波检测寄存器,bit0,载波检测; +#define RX_ADDR_P0 0x0A //数据通道0接收地址,最大长度5个字节,低字节在前 +#define RX_ADDR_P1 0x0B //数据通道1接收地址,最大长度5个字节,低字节在前 +#define RX_ADDR_P2 0x0C //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; +#define RX_ADDR_P3 0x0D //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; +#define RX_ADDR_P4 0x0E //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; +#define RX_ADDR_P5 0x0F //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; +#define TX_ADDR 0x10 //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等 +#define RX_PW_P0 0x11 //接收数据通道0有效数据宽度(1~32字节),设置为0则非法 +#define RX_PW_P1 0x12 //接收数据通道1有效数据宽度(1~32字节),设置为0则非法 +#define RX_PW_P2 0x13 //接收数据通道2有效数据宽度(1~32字节),设置为0则非法 +#define RX_PW_P3 0x14 //接收数据通道3有效数据宽度(1~32字节),设置为0则非法 +#define RX_PW_P4 0x15 //接收数据通道4有效数据宽度(1~32字节),设置为0则非法 +#define RX_PW_P5 0x16 //接收数据通道5有效数据宽度(1~32字节),设置为0则非法 +#define NRF_FIFO_STATUS 0x17 //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留 + //bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环; +////////////////////////////////////////////////////////////////////////////////////////////////////////// /* ========== 函数声明 ========== */ /* 底层 SPI */ -uint8_t NRF_SPI_TransferByte(uint8_t dat); +uint8 SPI_ReadWrite(uint8 dat); /* 寄存器操作 */ -uint8_t NRF24L01_ReadReg(uint8_t reg); -void NRF24L01_WriteReg(uint8_t reg, uint8_t value); -void NRF24L01_ReadBuf(uint8_t reg, uint8_t *buf, uint8_t len); -void NRF24L01_WriteBuf(uint8_t reg, const uint8_t *buf, uint8_t len); +uint8 NRF24_ReadReg(uint8 reg); +void NRF24_WriteReg(uint8 reg, uint8 value); +void NRF24_ReadBuf(uint8 reg, uint8 *buf, uint8 len); +void NRF24_WriteBuf(uint8 reg, const uint8 *buf, uint8 len); /* 初始化与检测 */ -void NRF24L01_Init(void); -uint8_t NRF24L01_Check(void); +void NRF24_Init(void); +bool NRF24_Check(void); /* 配置 */ -void NRF24L01_SetChannel(uint8_t ch); -void NRF24L01_SetRXAddr(uint8_t pipe, const uint8_t *addr); +void NRF24_SetConfig(uint8 ch); +void NRF24_SetRXAddr(uint8 pipe, const uint8 *addr); /* 模式切换 */ void NRF24L01_RXMode(void); void NRF24L01_TXMode(void); /* 数据收发 */ -uint8_t NRF24L01_RxPacket(uint8_t *data); -uint8_t NRF24L01_IsDataReady(void); -void NRF24L01_FlushRX(void); +uint8 NRF24L01_RxPacket(uint8 *data); +uint8 NRF24L01_IsDataReady(void); +void NRF24L01_FlushRX(void); #endif /* __NRF24_H__ */ diff --git a/rx/src/sbus.c b/rx/src/sbus.c deleted file mode 100644 index 1642cf1..0000000 --- a/rx/src/sbus.c +++ /dev/null @@ -1,59 +0,0 @@ - -static void SBUS_Init(void) { - /* 切换到 SBUS 波特率 100kbps */ - /* 100000 @ 24MHz: T2 reload = 65536 - 60 = 0xFFC4 */ - T2H = 0xFF; - T2L = 0xC4; - AUXR |= T2x12; - AUXR |= T2R; - - /* 8E2: 8数据位, 偶校验, 2停止位 */ - PCON |= SMOD0; /* SMOD0=1, 帧格式由SM2控制 */ - SCON &= ~0x0E; /* 清除SM2, TB8 */ - /* 注意: 对于SBUS, 标准是8E2, 但这里通过三极管反相, - * 所以实际发送的是反相SBUS信号 */ -} - -static void SBUS_SendFrame(void) { - static uint8_t __xdata frame[25]; - static uint16_t __xdata sbus_ch[16]; - uint8_t i; - - /* SBUS 帧结构 */ - frame[0] = 0x0F; /* 起始字节 */ - - /* 16通道 11-bit 编码 (这里只填充前4个通道, 其余为中间值) */ - /* 将pwm值(250-500)映射到SBUS范围(172-1811) */ - for (i = 0; i < 4; i++) { - uint16_t val = pwm_ch[i]; - /* pwm_ch: 250~500 → SBUS: 172~1811 */ - if (val < PWM_MIN) val = PWM_MIN; - if (val > PWM_MAX) val = PWM_MAX; - sbus_ch[i] = (uint16_t)((uint32_t)(val - PWM_MIN) * (1811 - 172) / (PWM_MAX - PWM_MIN) + 172); - } - for (i = 4; i < 16; i++) { - sbus_ch[i] = 992; /* 中间值 */ - } - - /* 11-bit 打包 */ - frame[1] = (uint8_t)(sbus_ch[0] & 0xFF); - frame[2] = (uint8_t)((sbus_ch[0] >> 8) | ((sbus_ch[1] & 0x07) << 3)); - frame[3] = (uint8_t)((sbus_ch[1] >> 3) | ((sbus_ch[2] & 0x3F) << 5)); - frame[4] = (uint8_t)((sbus_ch[2] >> 6) | ((sbus_ch[3] & 0x01) << 2) | ((sbus_ch[3] & 0x01) << 2)); - /* 简化: 只打包前4通道, 其余保持中间值 */ - - /* 简化处理 - 完整16通道打包太复杂, 用中间值填充 */ - for (i = 5; i < 23; i++) { - frame[i] = 0x00; - } - - frame[23] = 0x00; /* 标志字节 */ - frame[24] = 0x00; /* 结束字节 */ - - /* 通过UART1发送 */ - for (i = 0; i < 25; i++) { - SBUF = frame[i]; - while (!TI); - TI = 0; - } -} diff --git a/rx/src/sbus.h b/rx/src/sbus.h deleted file mode 100644 index fc67786..0000000 --- a/rx/src/sbus.h +++ /dev/null @@ -1,8 +0,0 @@ -/* ======================================================================== - * SBUS 输出 (100kbps 8E2, 与UART1共用TX) - * ======================================================================== - */ - -static void SBUS_Init(void); -static void SBUS_SendFrame(void); -static const uint8_t __code crsf_crc8_tab[256]; diff --git a/rx/src/uart.c b/rx/src/uart.c index 7195946..eab87f5 100644 --- a/rx/src/uart.c +++ b/rx/src/uart.c @@ -4,24 +4,6 @@ * UART1 - 串口通信 (使用Timer2做波特率发生器) * ======================================================================== */ -static void UART1_Init(void) { - /* 使用 Timer2 做波特率发生器 */ - /* 115200 @ 24MHz: T2 reload = 65536 - 52 = 0xFFCC */ - T2H = 0xFF; - T2L = 0xCC; - AUXR |= T2x12; /* T2: 1T模式 */ - AUXR |= T2R; /* 启动T2 */ - - /* UART1 配置: 8位可变波特率, 模式1 */ - SCON = 0x50; /* 模式1, REN=1 */ - AUXR |= S1ST2; /* UART1使用T2做波特率 */ - PCON &= ~SMOD0; /* SMOD0=0, 帧格式8位数据 */ - - /* 使能接收中断 */ - ES = 1; - RI = 0; -} - /* UART1 中断服务 */ void UART1_Interrupt(void) __interrupt(4) { if (RI) {