Update RX

This commit is contained in:
2026-06-27 14:44:10 +08:00
parent fc0fd36861
commit b71a35dcd3
8 changed files with 823 additions and 467 deletions
+89
View File
@@ -0,0 +1,89 @@
#include "STC8_SDCC.H"
#include <string.h>
/* ========================================================================
* 系统常量
* ======================================================================== */
#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
/* 接收状态 */
#define RX_STATE_DISCONNECTED 0
#define RX_STATE_BINDING 1
#define RX_STATE_CONNECTED 2
/* 输出模式 */
#define OUT_MODE_PWM 0
#define OUT_MODE_SBUS 1
#define OUT_MODE_UART 2
#define OUT_MODE_CRFS 3
/* EEPROM 地址 (IAP) */
#define EEPROM_ADDR_BIND_FLAG 0x0000
#define EEPROM_ADDR_TX_ADDR 0x0001 /* 5字节 */
#define EEPROM_ADDR_RF_CH 0x0006
#define EEPROM_ADDR_BIND_PHRASE 0x0007 /* 6字节 */
#define EEPROM_BIND_MAGIC 0x5A /* 已对频标志 */
/* 数据包格式 (与发射机一致) */
#define RF_SYNC_BYTE0 0xAA
#define RF_SYNC_BYTE1 0x55
#define RF_PACKET_SIZE 32
#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硬件错误 */
/* ========================================================================
* 全局变量
* ======================================================================== */
/* PWM 通道脉宽值 (4us单位, 放在 xdata) */
static volatile uint16_t __xdata pwm_ch[CHANNEL_COUNT];
/* 接收状态 */
static uint8_t rx_state = RX_STATE_DISCONNECTED;
static uint8_t out_mode = OUT_MODE_PWM;
/* 对频信息 (放在 xdata) */
static uint8_t __xdata bind_phrase[RF_PHRASE_LEN] = "LOVE";
static uint8_t __xdata tx_addr[NRF_ADDR_WIDTH] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};
static uint8_t rf_channel = 40;
/* 信号丢失计数 */
static uint16_t lost_packet_cnt = 0;
#define LOST_PACKET_TIMEOUT 500 /* 500次接收超时后判丢失 (~5秒 @ 100Hz) */
/* NRF 硬件错误标志 */
static uint8_t nrf_hw_error = 0;
/* 电池电压 */
static volatile uint16_t bat_voltage_mv = 0;
/* 回传数据 */
static uint8_t telem_rssi = 0; /* 信号强度 (0-100) */
static uint8_t telem_packet_loss = 0; /* 丢包率 (0-100%) */
static uint8_t telem_rate_hz = 2; /* 回传频率 1-10Hz, 默认2Hz */
/* CRFS 模式下接收的飞控转发数据 */
static uint8_t __xdata crfs_forward_buf[24];
static uint8_t crfs_forward_len = 0;
/* UART 接收缓冲 (放在 xdata) */
static uint8_t __xdata uart_rx_buf[UART_BUF_SIZE];
static uint8_t uart_rx_idx = 0;
static volatile uint8_t uart_cmd_ready = 0;
/* 定时器计数 (用于LED闪烁) */
static volatile uint16_t sys_tick = 0;
+317
View File
@@ -0,0 +1,317 @@
/* ========================================================================
* NRF24L01 硬件 SPI 实现 (STC8H 内置 SPI)
* P1.3=MISO, P1.4=MOSI, P1.5=SCLK, P5.4=CSN(GPIO)
* ======================================================================== */
/* 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) {
SPDAT = dat;
/* 等待传输完成 */
while (!(SPSTAT & SPIF));
/* 清除 SPIF (读 SPSTAT 再读 SPDAT 自动清除, 但这里直接写1清除更可靠) */
SPSTAT = SPIF;
return SPDAT;
}
uint8_t NRF24L01_ReadReg(uint8_t reg) {
uint8_t val;
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_R_REGISTER | reg);
val = NRF_SPI_TransferByte(0xFF);
NRF_CSN = 1;
return val;
}
void NRF24L01_WriteReg(uint8_t reg, uint8_t value) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_W_REGISTER | reg);
NRF_SPI_TransferByte(value);
NRF_CSN = 1;
}
void NRF24L01_ReadBuf(uint8_t reg, uint8_t *buf, uint8_t len) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_R_REGISTER | reg);
for (uint8_t i = 0; i < len; i++)
buf[i] = NRF_SPI_TransferByte(0xFF);
NRF_CSN = 1;
}
void NRF24L01_WriteBuf(uint8_t reg, const uint8_t *buf, uint8_t len) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_W_REGISTER | reg);
for (uint8_t i = 0; i < len; i++)
NRF_SPI_TransferByte(buf[i]);
NRF_CSN = 1;
}
/* ========================================================================
* NRF24L01 初始化与操作
* ======================================================================== */
void NRF24L01_Init(void) {
NRF_CE = 0;
NRF_CSN = 1;
/* 初始化硬件 SPI */
NRF_SPI_Init();
/* 延时等待上电 */
for (volatile uint16_t d = 0; d < 5000; d++);
/* 配置寄存器 */
NRF24L01_WriteReg(NRF_CONFIG, 0x0E); /* EN_CRC, CRC0, PWR_UP, PTX */
NRF24L01_WriteReg(NRF_EN_AA, 0x01); /* Pipe0 auto ACK */
NRF24L01_WriteReg(NRF_EN_RXADDR, 0x01); /* Pipe0 enable */
NRF24L01_WriteReg(NRF_SETUP_AW, 0x03); /* 5字节地址 */
NRF24L01_WriteReg(NRF_SETUP_RETR, 0x1A); /* 500us + 15次重试 */
NRF24L01_WriteReg(NRF_RF_CH, rf_channel);
NRF24L01_WriteReg(NRF_RF_SETUP, NRF_RATE_1M | NRF_PA_MAX);
NRF24L01_WriteReg(NRF_RX_PW_P0, RF_PACKET_SIZE);
NRF24L01_WriteReg(NRF_DYNPD, 0x00); /* 关闭动态负载 */
NRF24L01_WriteReg(NRF_FEATURE, 0x00);
/* 设置地址 */
NRF24L01_WriteBuf(NRF_TX_ADDR, tx_addr, NRF_ADDR_WIDTH);
NRF24L01_WriteBuf(NRF_RX_ADDR_P0, tx_addr, NRF_ADDR_WIDTH);
/* 清空FIFO */
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_TX);
NRF_CSN = 1;
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_RX);
NRF_CSN = 1;
/* 清除状态 */
NRF24L01_WriteReg(NRF_STATUS, NRF_STATUS_RX_DR | NRF_STATUS_TX_DS | NRF_STATUS_MAX_RT);
NRF_CE = 0;
}
uint8_t NRF24L01_Check(void) {
uint8_t buf[5] = {0xA5, 0xA5, 0xA5, 0xA5, 0xA5};
uint8_t rx[5];
NRF24L01_WriteBuf(NRF_TX_ADDR, buf, 5);
NRF24L01_ReadBuf(NRF_TX_ADDR, rx, 5);
for (uint8_t i = 0; i < 5; i++)
if (rx[i] != 0xA5) return 0;
return 1;
}
void NRF24L01_SetChannel(uint8_t ch) {
if (ch > NRF_MAX_CHANNEL) ch = NRF_MAX_CHANNEL;
rf_channel = ch;
NRF24L01_WriteReg(NRF_RF_CH, ch);
}
void NRF24L01_SetRXAddr(uint8_t pipe, const uint8_t *addr) {
if (pipe > 5) return;
if (pipe < 2) {
NRF24L01_WriteBuf(NRF_RX_ADDR_P0 + pipe, addr, NRF_ADDR_WIDTH);
} else {
NRF24L01_WriteReg(NRF_RX_ADDR_P0 + pipe, addr[0]);
}
}
void NRF24L01_RXMode(void) {
NRF_CE = 0;
uint8_t cfg = NRF24L01_ReadReg(NRF_CONFIG);
cfg |= 0x01; /* PRIM_RX = 1 */
NRF24L01_WriteReg(NRF_CONFIG, cfg);
NRF_CE = 1;
}
void NRF24L01_TXMode(void) {
NRF_CE = 0;
uint8_t cfg = NRF24L01_ReadReg(NRF_CONFIG);
cfg &= ~0x01; /* PRIM_RX = 0 */
NRF24L01_WriteReg(NRF_CONFIG, cfg);
NRF_CE = 1;
}
uint8_t NRF24L01_IsDataReady(void) {
uint8_t status = NRF24L01_ReadReg(NRF_STATUS);
return (status & NRF_STATUS_RX_DR) ? 1 : 0;
}
uint8_t NRF24L01_RxPacket(uint8_t *data) {
uint8_t status = NRF24L01_ReadReg(NRF_STATUS);
if (!(status & NRF_STATUS_RX_DR)) return 0;
/* 读取payload */
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_R_RX_PAYLOAD);
for (uint8_t i = 0; i < RF_PACKET_SIZE; i++)
data[i] = NRF_SPI_TransferByte(0xFF);
NRF_CSN = 1;
/* 清除 RX_DR */
NRF24L01_WriteReg(NRF_STATUS, NRF_STATUS_RX_DR);
return 1;
}
void NRF24L01_FlushRX(void) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_RX);
NRF_CSN = 1;
}
/* ========================================================================
* NRF24L01 对频
* ======================================================================== */
void NRF24L01_EnterBindMode(void) {
/* 进入接收模式, 监听对频请求 */
NRF_CE = 0;
NRF24L01_WriteReg(NRF_CONFIG, 0x0F); /* PRX, PWR_UP, CRC */
NRF24L01_WriteReg(NRF_EN_RXADDR, 0x01);
NRF24L01_WriteReg(NRF_RX_PW_P0, RF_PACKET_SIZE);
NRF24L01_WriteBuf(NRF_RX_ADDR_P0, tx_addr, NRF_ADDR_WIDTH);
NRF24L01_WriteReg(NRF_STATUS, 0x70);
NRF_CE = 1;
}
void NRF_EnterBindMode(void) {
rx_state = RX_STATE_BINDING;
NRF24L01_EnterBindMode();
}
uint8_t NRF_DoBindScan(void) {
static uint8_t __xdata buf[RF_PACKET_SIZE];
/* 扫描所有通道 */
for (uint8_t ch = 0; ch <= NRF_MAX_CHANNEL; ch++) {
NRF24L01_SetChannel(ch);
for (volatile uint16_t d = 0; d < 500; d++); /* 延时 */
if (NRF24L01_RxPacket(buf)) {
/* 验证同步头 */
if (buf[0] != RF_SYNC_BYTE0 || buf[1] != RF_SYNC_BYTE1)
continue;
/* 验证对频短语 (6字节, 从偏移2开始) */
uint8_t match = 1;
for (uint8_t i = 0; i < RF_PHRASE_LEN; i++) {
if (buf[2 + i] != bind_phrase[i]) {
match = 0;
break;
}
}
if (!match) continue;
/* 对频成功! 从包中提取地址 (偏移8开始, 5字节) */
for (uint8_t i = 0; i < NRF_ADDR_WIDTH; i++) {
tx_addr[i] = buf[8 + i];
}
/* 保存配置到EEPROM */
EEPROM_SaveConfig();
/* 重新初始化NRF */
NRF24L01_Init();
NRF24L01_RXMode();
rx_state = RX_STATE_DISCONNECTED;
return 1;
}
}
return 0;
}
/* ========================================================================
* NRF24L01 数据包处理
* ======================================================================== */
void NRF_ProcessPacket(void) {
static uint8_t __xdata buf[RF_PACKET_SIZE];
if (!NRF24L01_RxPacket(buf))
return;
/* 验证同步头 */
if (buf[0] != RF_SYNC_BYTE0 || buf[1] != RF_SYNC_BYTE1)
return;
/* 验证对频短语 */
for (uint8_t i = 0; i < RF_PHRASE_LEN; i++) {
if (buf[2 + i] != bind_phrase[i])
return;
}
/* 验证校验和 (XOR) */
uint8_t checksum = 0;
for (uint8_t i = 0; i < RF_PACKET_SIZE - 1; i++)
checksum ^= buf[i];
if (checksum != buf[RF_PACKET_SIZE - 1])
return;
/* 提取通道数据 (8通道, 偏移8, 每通道2字节大端) */
for (uint8_t i = 0; i < CHANNEL_COUNT; i++) {
uint16_t raw = ((uint16_t)buf[8 + i * 2] << 8) | buf[8 + i * 2 + 1];
/* raw: 0~2000, 映射到 PWM_MIN~PWM_MAX (250~500) */
if (raw > 2000) raw = 2000;
pwm_ch[i] = (uint16_t)((uint32_t)raw * (PWM_MAX - PWM_MIN) / 2000 + PWM_MIN);
}
/* 更新 RSSI 估计: 从 NRF RPD 寄存器读取载波检测 */
if (NRF24L01_ReadReg(NRF_RPD) & 0x01) {
/* 有载波 = 信号强 */
if (telem_rssi < 100) telem_rssi += 5;
} else {
if (telem_rssi > 10) telem_rssi -= 1;
}
if (telem_rssi > 100) telem_rssi = 100;
/* 更新丢包率: 基于 lost_packet_cnt 估算 */
telem_packet_loss = 0; /* 收到包 = 当前不丢包, 累积值在外部计算 */
/* 更新状态 */
lost_packet_cnt = 0;
if (rx_state != RX_STATE_CONNECTED) {
rx_state = RX_STATE_CONNECTED;
}
}
/* ========================================================================
* NRF24L01 回传发送
* 包格式: 0xBB 0x44 + voltage(2B, mV) + rssi(1B) + loss(1B) + temp(2B)
* ======================================================================== */
static void NRF_SendTelemetry(void) {
static uint8_t __xdata tbuf[8];
tbuf[0] = 0xBB; /* sync0 */
tbuf[1] = 0x44; /* sync1 */
tbuf[2] = (uint8_t)(bat_voltage_mv >> 8); /* voltage high */
tbuf[3] = (uint8_t)(bat_voltage_mv & 0xFF); /* voltage low */
tbuf[4] = telem_rssi; /* RSSI */
tbuf[5] = telem_packet_loss; /* 丢包率 */
tbuf[6] = 0; /* temp high (未实现) */
tbuf[7] = 0; /* temp low */
/* 切到 TX 模式发送回传 */
NRF24L01_TXMode();
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_TX);
NRF_CSN = 1;
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_W_TX_PAYLOAD);
for (uint8_t i = 0; i < 8; i++)
NRF_SPI_TransferByte(tbuf[i]);
NRF_CSN = 1;
NRF_CE = 1;
for (volatile uint16_t d = 0; d < 100; d++);
NRF_CE = 0;
/* 切回 RX 模式 */
NRF24L01_RXMode();
NRF24L01_FlushRX();
}
+4 -4
View File
@@ -1,12 +1,12 @@
# ifndef CONFIG
# define CONFIG
# include "CONFIG.h"
# endif
/**
* NRF24L01+ 驱动头文件 (STC8H1K08 移植版, SDCC 兼容)
* SPI 引脚: SCK=P1.5, CE=P1.6, IRQ=P1.7, CSN=P5.4, MISO=P1.3, MOSI=P1.4
* 使用 STC8H 硬件 SPI (SPI 默认引脚)
*/
#ifndef __NRF24_H__
#define __NRF24_H__
#include "STC8_SDCC.H"
/* SDCC mcs51 模式下缺少的标准类型 */
typedef unsigned char uint8_t;
View File
+4
View File
@@ -0,0 +1,4 @@
# ifndef CONFIG
# define CONFIG
# include "CONFIG.h"
# endif
+356 -37
View File
@@ -1,58 +1,69 @@
#!/usr/bin/env python3
"""ZhaTianRX 接收机操作指南 PDF 生成脚本"""
"""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('Helvetica', 'I', 8)
self.cell(0, 8, 'ZhaTianRX 接收机操作指南', 0, 0, 'L')
self.cell(0, 8, f'{self.page_no()}', 0, 1, 'R')
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('Helvetica', 'I', 7)
self.cell(0, 10, f'ZhaTianRC 项目 - STC8H1K08 + NRF24L01', 0, 0, 'C')
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('Helvetica', 'B', 16)
self.set_font('CN', 'B', 16)
self.set_text_color(0x1A, 0x1A, 0x2E)
self.cell(0, 12, title, 0, 1)
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('Helvetica', 'B', 13)
self.set_font('CN', 'B', 13)
self.set_text_color(0x2C, 0x3E, 0x50)
self.cell(0, 10, title, 0, 1)
self.cell(0, 10, title, new_x="LMARGIN", new_y="NEXT")
self.ln(2)
elif level == 3:
self.set_font('Helvetica', 'B', 11)
self.set_font('CN', 'B', 11)
self.set_text_color(0x34, 0x49, 0x5E)
self.cell(0, 8, title, 0, 1)
self.cell(0, 8, title, new_x="LMARGIN", new_y="NEXT")
self.ln(1)
def body_text(self, text):
self.set_font('Helvetica', '', 10)
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('Helvetica', '', 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('Helvetica', '', 10)
self.cell(5, 5.5, chr(8226) + ' ')
self.set_font('CN', '', 10)
self.cell(5, 5.5, ' ')
self.multi_cell(0, 5.5, text)
self.ln(1)
@@ -62,15 +73,15 @@ class ZhaTianPDF(FPDF):
self.set_font('Courier', '', 9)
self.ln(1)
for line in text.split('\n'):
self.cell(0, 5, ' ' + line, 0, 1, '', True)
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('Helvetica', 'B', 10)
self.set_font('CN', 'B', 10)
self.set_text_color(0xC0, 0x39, 0x2B)
self.cell(0, 6, f'步骤 {num}: {title}', 0, 1)
self.set_font('Helvetica', '', 10)
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:
@@ -78,22 +89,22 @@ class ZhaTianPDF(FPDF):
self.ln(2)
def table_header(self, cols, widths):
self.set_font('Helvetica', 'B', 9)
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, 1, 0, 'C', True)
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('Helvetica', '', 9)
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, 1, 0, 'C', True)
self.cell(widths[i], 6, col, border=1, align='C', fill=True)
self.ln()
@@ -105,28 +116,29 @@ def build_pdf():
# ==================== 封面 ====================
pdf.add_page()
pdf.ln(40)
pdf.set_font('Helvetica', 'B', 28)
pdf.set_font('CN', 'B', 28)
pdf.set_text_color(0x1A, 0x1A, 0x2E)
pdf.cell(0, 15, 'ZhaTianRX', 0, 1, 'C')
pdf.set_font('Helvetica', '', 16)
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, '接收机操作指南', 0, 1, 'C')
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('Helvetica', '', 11)
pdf.set_font('CN', '', 11)
pdf.set_text_color(0x55, 0x55, 0x55)
pdf.cell(0, 7, 'MCU: STC8H1K08 | 射频: NRF24L01+ | 编译器: SDCC', 0, 1, 'C')
pdf.cell(0, 7, '固件版本: 1.0 | 项目: ZhaTianRC', 0, 1, 'C')
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('Helvetica', 'I', 9)
pdf.cell(0, 7, '本文档涵盖接收机的所有功能、操作步骤及串口命令参考', 0, 1, 'C')
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('Helvetica', '', 11)
pdf.set_font('CN', '', 11)
toc = [
'1. 概述',
'2. 硬件引脚分配',
@@ -137,9 +149,11 @@ def build_pdf():
'7. LED 状态指示',
'8. 固件烧录方法',
'9. 附录 - 技术规格',
'10. 完整功能清单(38 项)',
]
for item in toc:
pdf.cell(0, 8, item, 0, 1)
pdf.set_text_color(0x2C, 0x3E, 0x50)
pdf.cell(0, 8, item, new_x="LMARGIN", new_y="NEXT")
pdf.ln(1)
# ==================== 1. 概述 ====================
@@ -531,7 +545,7 @@ def build_pdf():
['主频', '24MHz 内部 IRC'],
['Flash', '8KB (固件占用 ~6.8KB)'],
['内部 RAM', '256 字节'],
['外部 RAM', '256 字节 (XDATA)'],
['外部 RAM (XDATA)', '256 字节'],
['射频芯片', 'NRF24L01+ 2.4GHz'],
['SPI 速率', '6MHz (硬件 SPI)'],
['RF 频道', '0~125 (默认 40)'],
@@ -612,7 +626,312 @@ def build_pdf():
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 舵机 PWMP3.4-P3.71.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 8E216 通道 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 8N1CRSF RC 通道帧 (type=0x16)CRC8 校验。')
pdf.step_block(1, '切换到 CRFS 模式', '', 'MODEL(CRFS)')
pdf.step_block(2, '连接飞控',
'TX(P3.1) 接飞控 CRSF RXRX(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}')
+40 -413
View File
@@ -12,99 +12,16 @@
* TX(P3.1) - UART1 TX / SBUS 输出 (经板上三极管反相)
* RX(P3.0) - UART1 RX / 命令输入
*
* NRF24L01 (件SPI):
* NRF24L01 (件SPI):
* SCK(P1.5) CE(P1.6) IRQ(P1.7) CSN(P5.4) MISO(P1.3) MOSI(P1.4)
*/
#include "STC8_SDCC.H"
# ifndef CONFIG
# define CONFIG
# include "CONFIG.h"
# endif
#include "NRF24.h"
#include <string.h>
/* ========================================================================
* 系统常量
* ======================================================================== */
#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
/* 接收状态 */
#define RX_STATE_DISCONNECTED 0
#define RX_STATE_BINDING 1
#define RX_STATE_CONNECTED 2
/* 输出模式 */
#define OUT_MODE_PWM 0
#define OUT_MODE_SBUS 1
#define OUT_MODE_UART 2
#define OUT_MODE_CRFS 3
/* EEPROM 地址 (IAP) */
#define EEPROM_ADDR_BIND_FLAG 0x0000
#define EEPROM_ADDR_TX_ADDR 0x0001 /* 5字节 */
#define EEPROM_ADDR_RF_CH 0x0006
#define EEPROM_ADDR_BIND_PHRASE 0x0007 /* 6字节 */
#define EEPROM_BIND_MAGIC 0x5A /* 已对频标志 */
/* 数据包格式 (与发射机一致) */
#define RF_SYNC_BYTE0 0xAA
#define RF_SYNC_BYTE1 0x55
#define RF_PACKET_SIZE 32
#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硬件错误 */
/* ========================================================================
* 全局变量
* ======================================================================== */
/* PWM 通道脉宽值 (4us单位, 放在 xdata) */
static volatile uint16_t __xdata pwm_ch[CHANNEL_COUNT];
/* 接收状态 */
static uint8_t rx_state = RX_STATE_DISCONNECTED;
static uint8_t out_mode = OUT_MODE_PWM;
/* 对频信息 (放在 xdata) */
static uint8_t __xdata bind_phrase[RF_PHRASE_LEN] = "LOVE";
static uint8_t __xdata tx_addr[NRF_ADDR_WIDTH] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};
static uint8_t rf_channel = 40;
/* 信号丢失计数 */
static uint16_t lost_packet_cnt = 0;
#define LOST_PACKET_TIMEOUT 500 /* 500次接收超时后判丢失 (~5秒 @ 100Hz) */
/* NRF 硬件错误标志 */
static uint8_t nrf_hw_error = 0;
/* 电池电压 */
static volatile uint16_t bat_voltage_mv = 0;
/* 回传数据 */
static uint8_t telem_rssi = 0; /* 信号强度 (0-100) */
static uint8_t telem_packet_loss = 0; /* 丢包率 (0-100%) */
static uint8_t telem_rate_hz = 2; /* 回传频率 1-10Hz, 默认2Hz */
/* CRFS 模式下接收的飞控转发数据 */
static uint8_t __xdata crfs_forward_buf[24];
static uint8_t crfs_forward_len = 0;
/* UART 接收缓冲 (放在 xdata) */
static uint8_t __xdata uart_rx_buf[UART_BUF_SIZE];
static uint8_t uart_rx_idx = 0;
static volatile uint8_t uart_cmd_ready = 0;
/* 定时器计数 (用于LED闪烁) */
static volatile uint16_t sys_tick = 0;
#include "UART.h"
/* ========================================================================
* 函数声明
@@ -513,13 +430,27 @@ static void ADC_Init(void) {
ADC_CONTR = (ADC_CONTR & 0xF0) | 0x0B;
}
static uint16_t ADC_Read(void) {
/* 读取指定ADC通道 */
static uint16_t ADC_ReadChannel(uint8_t ch) {
ADC_CONTR = (ADC_CONTR & 0xF0) | ch; /* 切换通道 */
__asm nop __endasm; /* 等待通道切换稳定 */
__asm nop __endasm;
ADC_CONTR |= ADC_START; /* 启动转换 */
while (!(ADC_CONTR & ADC_FLAG)); /* 等待完成 */
ADC_CONTR &= ~ADC_FLAG; /* 清除标志 */
return ((uint16_t)ADC_RES << 8) | ADC_RESL;
}
static uint16_t ADC_Read(void) {
return ADC_ReadChannel(0x0B); /* P3.3/ADC11 */
}
/* 读取内部带隙基准电压 (通道14), 用于反算VCC */
/* VCC(mV) = 1190 * 4096 / bandgap_adc */
static uint16_t ADC_ReadBandgap(void) {
return ADC_ReadChannel(0x0E); /* 内部带隙基准 */
}
/* ========================================================================
* IAP/EEPROM 操作
* ======================================================================== */
@@ -701,323 +632,6 @@ static void PWM_SetAllFailsafe(void) {
}
}
/* ========================================================================
* NRF24L01 硬件 SPI 实现 (STC8H 内置 SPI)
* P1.3=MISO, P1.4=MOSI, P1.5=SCLK, P5.4=CSN(GPIO)
* ======================================================================== */
/* 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) {
SPDAT = dat;
/* 等待传输完成 */
while (!(SPSTAT & SPIF));
/* 清除 SPIF (读 SPSTAT 再读 SPDAT 自动清除, 但这里直接写1清除更可靠) */
SPSTAT = SPIF;
return SPDAT;
}
uint8_t NRF24L01_ReadReg(uint8_t reg) {
uint8_t val;
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_R_REGISTER | reg);
val = NRF_SPI_TransferByte(0xFF);
NRF_CSN = 1;
return val;
}
void NRF24L01_WriteReg(uint8_t reg, uint8_t value) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_W_REGISTER | reg);
NRF_SPI_TransferByte(value);
NRF_CSN = 1;
}
void NRF24L01_ReadBuf(uint8_t reg, uint8_t *buf, uint8_t len) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_R_REGISTER | reg);
for (uint8_t i = 0; i < len; i++)
buf[i] = NRF_SPI_TransferByte(0xFF);
NRF_CSN = 1;
}
void NRF24L01_WriteBuf(uint8_t reg, const uint8_t *buf, uint8_t len) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_W_REGISTER | reg);
for (uint8_t i = 0; i < len; i++)
NRF_SPI_TransferByte(buf[i]);
NRF_CSN = 1;
}
/* ========================================================================
* NRF24L01 初始化与操作
* ======================================================================== */
void NRF24L01_Init(void) {
NRF_CE = 0;
NRF_CSN = 1;
/* 初始化硬件 SPI */
NRF_SPI_Init();
/* 延时等待上电 */
for (volatile uint16_t d = 0; d < 5000; d++);
/* 配置寄存器 */
NRF24L01_WriteReg(NRF_CONFIG, 0x0E); /* EN_CRC, CRC0, PWR_UP, PTX */
NRF24L01_WriteReg(NRF_EN_AA, 0x01); /* Pipe0 auto ACK */
NRF24L01_WriteReg(NRF_EN_RXADDR, 0x01); /* Pipe0 enable */
NRF24L01_WriteReg(NRF_SETUP_AW, 0x03); /* 5字节地址 */
NRF24L01_WriteReg(NRF_SETUP_RETR, 0x1A); /* 500us + 15次重试 */
NRF24L01_WriteReg(NRF_RF_CH, rf_channel);
NRF24L01_WriteReg(NRF_RF_SETUP, NRF_RATE_1M | NRF_PA_MAX);
NRF24L01_WriteReg(NRF_RX_PW_P0, RF_PACKET_SIZE);
NRF24L01_WriteReg(NRF_DYNPD, 0x00); /* 关闭动态负载 */
NRF24L01_WriteReg(NRF_FEATURE, 0x00);
/* 设置地址 */
NRF24L01_WriteBuf(NRF_TX_ADDR, tx_addr, NRF_ADDR_WIDTH);
NRF24L01_WriteBuf(NRF_RX_ADDR_P0, tx_addr, NRF_ADDR_WIDTH);
/* 清空FIFO */
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_TX);
NRF_CSN = 1;
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_RX);
NRF_CSN = 1;
/* 清除状态 */
NRF24L01_WriteReg(NRF_STATUS, NRF_STATUS_RX_DR | NRF_STATUS_TX_DS | NRF_STATUS_MAX_RT);
NRF_CE = 0;
}
uint8_t NRF24L01_Check(void) {
uint8_t buf[5] = {0xA5, 0xA5, 0xA5, 0xA5, 0xA5};
uint8_t rx[5];
NRF24L01_WriteBuf(NRF_TX_ADDR, buf, 5);
NRF24L01_ReadBuf(NRF_TX_ADDR, rx, 5);
for (uint8_t i = 0; i < 5; i++)
if (rx[i] != 0xA5) return 0;
return 1;
}
void NRF24L01_SetChannel(uint8_t ch) {
if (ch > NRF_MAX_CHANNEL) ch = NRF_MAX_CHANNEL;
rf_channel = ch;
NRF24L01_WriteReg(NRF_RF_CH, ch);
}
void NRF24L01_SetRXAddr(uint8_t pipe, const uint8_t *addr) {
if (pipe > 5) return;
if (pipe < 2) {
NRF24L01_WriteBuf(NRF_RX_ADDR_P0 + pipe, addr, NRF_ADDR_WIDTH);
} else {
NRF24L01_WriteReg(NRF_RX_ADDR_P0 + pipe, addr[0]);
}
}
void NRF24L01_RXMode(void) {
NRF_CE = 0;
uint8_t cfg = NRF24L01_ReadReg(NRF_CONFIG);
cfg |= 0x01; /* PRIM_RX = 1 */
NRF24L01_WriteReg(NRF_CONFIG, cfg);
NRF_CE = 1;
}
void NRF24L01_TXMode(void) {
NRF_CE = 0;
uint8_t cfg = NRF24L01_ReadReg(NRF_CONFIG);
cfg &= ~0x01; /* PRIM_RX = 0 */
NRF24L01_WriteReg(NRF_CONFIG, cfg);
NRF_CE = 1;
}
uint8_t NRF24L01_IsDataReady(void) {
uint8_t status = NRF24L01_ReadReg(NRF_STATUS);
return (status & NRF_STATUS_RX_DR) ? 1 : 0;
}
uint8_t NRF24L01_RxPacket(uint8_t *data) {
uint8_t status = NRF24L01_ReadReg(NRF_STATUS);
if (!(status & NRF_STATUS_RX_DR)) return 0;
/* 读取payload */
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_R_RX_PAYLOAD);
for (uint8_t i = 0; i < RF_PACKET_SIZE; i++)
data[i] = NRF_SPI_TransferByte(0xFF);
NRF_CSN = 1;
/* 清除 RX_DR */
NRF24L01_WriteReg(NRF_STATUS, NRF_STATUS_RX_DR);
return 1;
}
void NRF24L01_FlushRX(void) {
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_RX);
NRF_CSN = 1;
}
/* ========================================================================
* NRF24L01 对频
* ======================================================================== */
void NRF24L01_EnterBindMode(void) {
/* 进入接收模式, 监听对频请求 */
NRF_CE = 0;
NRF24L01_WriteReg(NRF_CONFIG, 0x0F); /* PRX, PWR_UP, CRC */
NRF24L01_WriteReg(NRF_EN_RXADDR, 0x01);
NRF24L01_WriteReg(NRF_RX_PW_P0, RF_PACKET_SIZE);
NRF24L01_WriteBuf(NRF_RX_ADDR_P0, tx_addr, NRF_ADDR_WIDTH);
NRF24L01_WriteReg(NRF_STATUS, 0x70);
NRF_CE = 1;
}
void NRF_EnterBindMode(void) {
rx_state = RX_STATE_BINDING;
NRF24L01_EnterBindMode();
}
uint8_t NRF_DoBindScan(void) {
static uint8_t __xdata buf[RF_PACKET_SIZE];
/* 扫描所有通道 */
for (uint8_t ch = 0; ch <= NRF_MAX_CHANNEL; ch++) {
NRF24L01_SetChannel(ch);
for (volatile uint16_t d = 0; d < 500; d++); /* 延时 */
if (NRF24L01_RxPacket(buf)) {
/* 验证同步头 */
if (buf[0] != RF_SYNC_BYTE0 || buf[1] != RF_SYNC_BYTE1)
continue;
/* 验证对频短语 (6字节, 从偏移2开始) */
uint8_t match = 1;
for (uint8_t i = 0; i < RF_PHRASE_LEN; i++) {
if (buf[2 + i] != bind_phrase[i]) {
match = 0;
break;
}
}
if (!match) continue;
/* 对频成功! 从包中提取地址 (偏移8开始, 5字节) */
for (uint8_t i = 0; i < NRF_ADDR_WIDTH; i++) {
tx_addr[i] = buf[8 + i];
}
/* 保存配置到EEPROM */
EEPROM_SaveConfig();
/* 重新初始化NRF */
NRF24L01_Init();
NRF24L01_RXMode();
rx_state = RX_STATE_DISCONNECTED;
return 1;
}
}
return 0;
}
/* ========================================================================
* NRF24L01 数据包处理
* ======================================================================== */
void NRF_ProcessPacket(void) {
static uint8_t __xdata buf[RF_PACKET_SIZE];
if (!NRF24L01_RxPacket(buf))
return;
/* 验证同步头 */
if (buf[0] != RF_SYNC_BYTE0 || buf[1] != RF_SYNC_BYTE1)
return;
/* 验证对频短语 */
for (uint8_t i = 0; i < RF_PHRASE_LEN; i++) {
if (buf[2 + i] != bind_phrase[i])
return;
}
/* 验证校验和 (XOR) */
uint8_t checksum = 0;
for (uint8_t i = 0; i < RF_PACKET_SIZE - 1; i++)
checksum ^= buf[i];
if (checksum != buf[RF_PACKET_SIZE - 1])
return;
/* 提取通道数据 (8通道, 偏移8, 每通道2字节大端) */
for (uint8_t i = 0; i < CHANNEL_COUNT; i++) {
uint16_t raw = ((uint16_t)buf[8 + i * 2] << 8) | buf[8 + i * 2 + 1];
/* raw: 0~2000, 映射到 PWM_MIN~PWM_MAX (250~500) */
if (raw > 2000) raw = 2000;
pwm_ch[i] = (uint16_t)((uint32_t)raw * (PWM_MAX - PWM_MIN) / 2000 + PWM_MIN);
}
/* 更新 RSSI 估计: 从 NRF RPD 寄存器读取载波检测 */
if (NRF24L01_ReadReg(NRF_RPD) & 0x01) {
/* 有载波 = 信号强 */
if (telem_rssi < 100) telem_rssi += 5;
} else {
if (telem_rssi > 10) telem_rssi -= 1;
}
if (telem_rssi > 100) telem_rssi = 100;
/* 更新丢包率: 基于 lost_packet_cnt 估算 */
telem_packet_loss = 0; /* 收到包 = 当前不丢包, 累积值在外部计算 */
/* 更新状态 */
lost_packet_cnt = 0;
if (rx_state != RX_STATE_CONNECTED) {
rx_state = RX_STATE_CONNECTED;
}
}
/* ========================================================================
* NRF24L01 回传发送
* 包格式: 0xBB 0x44 + voltage(2B) + rssi(1B) + loss(1B) + temp(2B)
* ======================================================================== */
static void NRF_SendTelemetry(void) {
static uint8_t __xdata tbuf[8];
tbuf[0] = 0xBB; /* sync0 */
tbuf[1] = 0x44; /* sync1 */
tbuf[2] = (uint8_t)(bat_voltage_mv >> 8); /* voltage high */
tbuf[3] = (uint8_t)(bat_voltage_mv & 0xFF); /* voltage low */
tbuf[4] = telem_rssi; /* RSSI */
tbuf[5] = telem_packet_loss; /* 丢包率 */
tbuf[6] = 0; /* temp high (未实现) */
tbuf[7] = 0; /* temp low */
/* 切到 TX 模式发送回传 */
NRF24L01_TXMode();
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_FLUSH_TX);
NRF_CSN = 1;
NRF_CSN = 0;
NRF_SPI_TransferByte(NRF_CMD_W_TX_PAYLOAD);
for (uint8_t i = 0; i < 8; i++)
NRF_SPI_TransferByte(tbuf[i]);
NRF_CSN = 1;
NRF_CE = 1;
for (volatile uint16_t d = 0; d < 100; d++);
NRF_CE = 0;
/* 切回 RX 模式 */
NRF24L01_RXMode();
NRF24L01_FlushRX();
}
/* ========================================================================
* 串口命令解析
* ======================================================================== */
@@ -1304,8 +918,8 @@ void main(void) {
telem_frame[0] = 0xEE;
telem_frame[1] = 10; /* len */
telem_frame[2] = 0x08; /* Battery sensor */
telem_frame[3] = (uint8_t)(bat_voltage_mv >> 8); /* voltage high (mV*10) */
telem_frame[4] = (uint8_t)(bat_voltage_mv & 0xFF); /* voltage low */
telem_frame[3] = (uint8_t)((bat_voltage_mv / 10) >> 8); /* voltage high (10mV单位) */
telem_frame[4] = (uint8_t)((bat_voltage_mv / 10) & 0xFF); /* voltage low */
telem_frame[5] = 0; /* current high */
telem_frame[6] = 0; /* current low */
telem_frame[7] = 0; /* capacity */
@@ -1370,9 +984,22 @@ void main(void) {
static uint16_t last_adc_tick = 0;
if (sys_tick - last_adc_tick >= 100) {
last_adc_tick = sys_tick;
bat_voltage_mv = ADC_Read();
/* 粗略转换: 12位ADC, Vref=VCC, 分压比取决于硬件 */
/* 这里保留原始ADC值, 上位机或飞控自行换算 */
/* 读取电池分压 ADC (P3.3/ADC11) */
uint16_t bat_adc = ADC_Read();
/* 读取内部带隙基准 (通道14), 反算 VCC */
uint16_t bg_adc = ADC_ReadBandgap();
/* VCC(mV) = 1190 * 4096 / bg_adc
* V_bat(mV) = bat_adc * VCC / 4096 * 11 (分压比 1K:10K = 1/11)
* 合并: V_bat(mV) = bat_adc * 1190 * 11 / bg_adc
* = bat_adc * 13090 / bg_adc
*/
if (bg_adc > 0) {
uint32_t tmp = (uint32_t)bat_adc * 13090UL / bg_adc;
bat_voltage_mv = (uint16_t)tmp;
}
}
/* 更新LED */
Binary file not shown.