diff --git a/rx/Makefile b/rx/Makefile new file mode 100644 index 0000000..debc04b --- /dev/null +++ b/rx/Makefile @@ -0,0 +1,49 @@ +# ZhaTianRX - STC8H1K08 接收机固件 Makefile +# 编译器: SDCC + +TARGET = ZhaTianRX +BUILD_DIR = build + +# SDCC 路径 +CC = sdcc +PACK = packihx + +# 源文件 +C_SOURCES = main.c + +# 编译标志 +CFLAGS = -mmcs51 --model-small --no-xinit-opt + +# 输出文件 +IHX = $(BUILD_DIR)/$(TARGET).ihx +HEX = $(BUILD_DIR)/$(TARGET).hex +BIN = $(BUILD_DIR)/$(TARGET).bin + +# 目标 +all: $(BUILD_DIR) $(HEX) $(BIN) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# 编译: SDCC 直接输出 .ihx +$(IHX): $(C_SOURCES) NRF24.h STC8_SDCC.H | $(BUILD_DIR) + $(CC) $(CFLAGS) -o $(IHX) $(C_SOURCES) + +# 转换为 Intel HEX +$(HEX): $(IHX) + $(PACK) $(IHX) > $(HEX) + +# 转换为二进制 +$(BIN): $(HEX) hex2bin.py + python3 hex2bin.py $(HEX) $(BIN) + +# 清理 +clean: + rm -rf $(BUILD_DIR) *.asm *.lst *.rel *.rst *.sym *.map *.mem *.lk + +# 烧录 (需要 stcgal) +flash: $(HEX) + @echo "烧录命令: stcgal -p /dev/ttyUSB0 -b 115200 $(HEX)" + @echo "或者使用 STC-ISP 工具手动烧录" + +.PHONY: all clean flash diff --git a/rx/NRF24.h b/rx/NRF24.h new file mode 100644 index 0000000..a86c360 --- /dev/null +++ b/rx/NRF24.h @@ -0,0 +1,109 @@ +/** + * 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; +typedef unsigned int uint16_t; +typedef unsigned long uint32_t; + +/* ========== 引脚定义 (SDCC 语法) ========== */ +__sbit __at(0x95) NRF_SCK; /* P1^5 */ +__sbit __at(0x96) NRF_CE; /* P1^6 */ +__sbit __at(0x97) NRF_IRQ; /* P1^7 */ +__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 + +/* ========== 命令 ========== */ +#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 + +/* ========== 函数声明 ========== */ + +/* 底层 SPI */ +uint8_t NRF_SPI_TransferByte(uint8_t 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); + +/* 初始化与检测 */ +void NRF24L01_Init(void); +uint8_t NRF24L01_Check(void); + +/* 配置 */ +void NRF24L01_SetChannel(uint8_t ch); +void NRF24L01_SetRXAddr(uint8_t pipe, const uint8_t *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); + +#endif /* __NRF24_H__ */ diff --git a/rx/README.md b/rx/README.md new file mode 100644 index 0000000..afec422 --- /dev/null +++ b/rx/README.md @@ -0,0 +1,79 @@ +# ZhaTianRX + +## 说明 + +本项目是ZhaTianRC下的接收机子项目,使用STC8H1K08系列单片机作为主控,NRF24L01作为射频IC,具有4路PWM输出(默认50Hz,驱动舵机),一路串口,一路SBUS(与串口共用TX),一路CRSF/Crossfire输出。 + +## 操作说明 + +* 使用内置eeprom保存数据,如果eeprom内没有数据则自动进入对频模式。 +* LED状态:1Hz慢闪未连接/断开连接,3Hz快闪对频模式,常亮连接正常,双闪nrf24l01连接异常 +* 默认工作模式:PWM输出 +* 默认回传频率:2Hz + +## 回传功能 + +接收机支持通过NRF24L01向发射机回传遥测数据: + +| 回传数据 | 说明 | +|---------|------| +| 电池电压 | 通过 BAT_ADC(P3.3) 采样,12位ADC原始值 | +| RSSI | 信号强度估计 (0-100),基于NRF RPD载波检测 | +| 丢包率 | 滑动窗口估算 (0-100%) | + +回传频率可通过串口命令 `TELEM(N)` 设置为 1~10Hz。 + +### 不同输出模式下的回传行为 + +| 输出模式 | 回传通道 | 说明 | +|---------|---------|------| +| PWM (默认) | NRF24L01 | 通过NRF回传电压、RSSI、丢包率 | +| SBUS | NRF24L01 | 同上 | +| CRFS | CRSF UART + NRF | 通过CRSF遥测帧回传电压/RSSI/丢包,同时将飞控发来的CRSF帧通过NRF转发给发射机 | + +## 引脚分配 + +### 功能引脚 + +| 功能 | 引脚 | 说明 | +|-----------|------------|---------------------------------------| +| 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 / 命令输入 | + +### NRF24L01连接引脚 +NRF24L01基于STC8系列的一路硬件SPI进行开发。由于IRQ连接到了不具备中断的引脚上,可以通过主函数轮询的方法检测引脚状态 +| SCK | P1.5 | +| CE | P1.6 | +| IRQ | P1.7 | +| CSN | P5.4 | +| MISO | P1.3 | +| MOSI | P1.4 | + +## 串口命令 + +通过 P3.0 (RX) 输入,115200 8N1,回车或换行结束命令。串口模式时也持续监听是否从串口接收到以下命令,未监听到不影响串口和飞控通信: + +| 命令 | 作用 | +|-------------------|----------------------------------------------| +| `MODEL(SBUS)` | 切换到 SBUS 反向 UART 输出 (100kbps 8N2) | +| `MODEL(PWM)` | 切换到 4 路舵机 PWM 输出 (50Hz) | +| `MODEL(UART)` | 切换到 UART 输出 (115200 8N1) | +| `MODEL(CRFS)` | 切换到 CRSF/Crossfire 输出 (420kbps 8N1) | +| `ADDR(0xHH HH HH HH HH)` | 切换 NRF24L01 地址 (5 字节十六进制) | +| `PHRASE(xxxxxx)` | 设置对频短语,1~6位,支持大小写字母和数字 | +| `TUN(N)` | 切换 RF 频道 (0..125 十进制) | +| `DEL(BIND)` | 清除EEPROM内的对频信息 | +| `TELEM(N)` | 设置回传频率 (1~10Hz, 默认2Hz) | + +每次命令有效都会应答: + * `OK\r\n` - 成功 + * `ERR\r\n` - 命令格式错 + * `ERR_ADDR\r\n` - 地址参数错 + * `ERR_RANGE\r\n` - 范围错 + * `UNK\r\n` - 未知命令 diff --git a/rx/STC8.H b/rx/STC8.H new file mode 100644 index 0000000..c54faa4 --- /dev/null +++ b/rx/STC8.H @@ -0,0 +1,590 @@ +#ifndef __STC8F_H_ +#define __STC8F_H_ + +///////////////////////////////////////////////// + +//包含本头文件后,不用另外再包含"REG51.H" + +//内核特殊功能寄存器 +sfr ACC = 0xe0; +sfr B = 0xf0; +sfr PSW = 0xd0; +sbit CY = PSW^7; +sbit AC = PSW^6; +sbit F0 = PSW^5; +sbit RS1 = PSW^4; +sbit RS0 = PSW^3; +sbit OV = PSW^2; +sbit P = PSW^0; +sfr SP = 0x81; +sfr DPL = 0x82; +sfr DPH = 0x83; +sfr TA = 0xae; +sfr DPS = 0xe3; +sfr DPL1 = 0xe4; +sfr DPH1 = 0xe5; + + +//I/O 口特殊功能寄存器 +sfr P0 = 0x80; +sfr P1 = 0x90; +sfr P2 = 0xa0; +sfr P3 = 0xb0; +sfr P4 = 0xc0; +sfr P5 = 0xc8; +sfr P6 = 0xe8; +sfr P7 = 0xf8; +sfr P0M0 = 0x94; +sfr P0M1 = 0x93; +sfr P1M0 = 0x92; +sfr P1M1 = 0x91; +sfr P2M0 = 0x96; +sfr P2M1 = 0x95; +sfr P3M0 = 0xb2; +sfr P3M1 = 0xb1; +sfr P4M0 = 0xb4; +sfr P4M1 = 0xb3; +sfr P5M0 = 0xca; +sfr P5M1 = 0xc9; +sfr P6M0 = 0xcc; +sfr P6M1 = 0xcb; +sfr P7M0 = 0xe2; +sfr P7M1 = 0xe1; + +sbit P00 = P0^0; +sbit P01 = P0^1; +sbit P02 = P0^2; +sbit P03 = P0^3; +sbit P04 = P0^4; +sbit P05 = P0^5; +sbit P06 = P0^6; +sbit P07 = P0^7; +sbit P10 = P1^0; +sbit P11 = P1^1; +sbit P12 = P1^2; +sbit P13 = P1^3; +sbit P14 = P1^4; +sbit P15 = P1^5; +sbit P16 = P1^6; +sbit P17 = P1^7; +sbit P20 = P2^0; +sbit P21 = P2^1; +sbit P22 = P2^2; +sbit P23 = P2^3; +sbit P24 = P2^4; +sbit P25 = P2^5; +sbit P26 = P2^6; +sbit P27 = P2^7; +sbit P30 = P3^0; +sbit P31 = P3^1; +sbit P32 = P3^2; +sbit P33 = P3^3; +sbit P34 = P3^4; +sbit P35 = P3^5; +sbit P36 = P3^6; +sbit P37 = P3^7; +sbit P40 = P4^0; +sbit P41 = P4^1; +sbit P42 = P4^2; +sbit P43 = P4^3; +sbit P44 = P4^4; +sbit P45 = P4^5; +sbit P46 = P4^6; +sbit P47 = P4^7; +sbit P50 = P5^0; +sbit P51 = P5^1; +sbit P52 = P5^2; +sbit P53 = P5^3; +sbit P54 = P5^4; +sbit P55 = P5^5; +sbit P56 = P5^6; +sbit P57 = P5^7; +sbit P60 = P6^0; +sbit P61 = P6^1; +sbit P62 = P6^2; +sbit P63 = P6^3; +sbit P64 = P6^4; +sbit P65 = P6^5; +sbit P66 = P6^6; +sbit P67 = P6^7; +sbit P70 = P7^0; +sbit P71 = P7^1; +sbit P72 = P7^2; +sbit P73 = P7^3; +sbit P74 = P7^4; +sbit P75 = P7^5; +sbit P76 = P7^6; +sbit P77 = P7^7; + +//如下特殊功能寄存器位于扩展RAM区域 +//访问这些寄存器,需先将P_SW2的BIT7设置为1,才可正常读写 +#define P0PU (*(unsigned char volatile xdata *)0xfe10) +#define P1PU (*(unsigned char volatile xdata *)0xfe11) +#define P2PU (*(unsigned char volatile xdata *)0xfe12) +#define P3PU (*(unsigned char volatile xdata *)0xfe13) +#define P4PU (*(unsigned char volatile xdata *)0xfe14) +#define P5PU (*(unsigned char volatile xdata *)0xfe15) +#define P6PU (*(unsigned char volatile xdata *)0xfe16) +#define P7PU (*(unsigned char volatile xdata *)0xfe17) +#define P0NCS (*(unsigned char volatile xdata *)0xfe18) +#define P1NCS (*(unsigned char volatile xdata *)0xfe19) +#define P2NCS (*(unsigned char volatile xdata *)0xfe1a) +#define P3NCS (*(unsigned char volatile xdata *)0xfe1b) +#define P4NCS (*(unsigned char volatile xdata *)0xfe1c) +#define P5NCS (*(unsigned char volatile xdata *)0xfe1d) +#define P6NCS (*(unsigned char volatile xdata *)0xfe1e) +#define P7NCS (*(unsigned char volatile xdata *)0xfe1f) + +//系统管理特殊功能寄存器 +sfr PCON = 0x87; +#define SMOD 0x80 +#define SMOD0 0x40 +#define LVDF 0x20 +#define POF 0x10 +#define GF1 0x08 +#define GF0 0x04 +#define PD 0x02 +#define IDL 0x01 +sfr AUXR = 0x8e; +#define T0x12 0x80 +#define T1x12 0x40 +#define UART_M0x6 0x20 +#define T2R 0x10 +#define T2_CT 0x08 +#define T2x12 0x04 +#define EXTRAM 0x02 +#define S1ST2 0x01 +sfr AUXR2 = 0x97; +#define TXLNRX 0x10 +sfr BUS_SPEED = 0xa1; +sfr P_SW1 = 0xa2; +sfr P_SW2 = 0xba; +#define EAXFR 0x80 +sfr VOCTRL = 0xbb; +sfr RSTCFG = 0xff; + +//如下特殊功能寄存器位于扩展RAM区域 +//访问这些寄存器,需先将P_SW2的BIT7设置为1,才可正常读写 +#define CKSEL (*(unsigned char volatile xdata *)0xfe00) +#define CLKDIV (*(unsigned char volatile xdata *)0xfe01) +#define IRC24MCR (*(unsigned char volatile xdata *)0xfe02) +#define XOSCCR (*(unsigned char volatile xdata *)0xfe03) +#define IRC32KCR (*(unsigned char volatile xdata *)0xfe04) + +//中断特殊功能寄存器 +sfr IE = 0xa8; +sbit EA = IE^7; +sbit ELVD = IE^6; +sbit EADC = IE^5; +sbit ES = IE^4; +sbit ET1 = IE^3; +sbit EX1 = IE^2; +sbit ET0 = IE^1; +sbit EX0 = IE^0; +sfr IE2 = 0xaf; +#define ET4 0x40 +#define ET3 0x20 +#define ES4 0x10 +#define ES3 0x08 +#define ET2 0x04 +#define ESPI 0x02 +#define ES2 0x01 +sfr IP = 0xb8; +sbit PPCA = IP^7; +sbit PLVD = IP^6; +sbit PADC = IP^5; +sbit PS = IP^4; +sbit PT1 = IP^3; +sbit PX1 = IP^2; +sbit PT0 = IP^1; +sbit PX0 = IP^0; +sfr IP2 = 0xb5; +#define PI2C 0x40 +#define PCMP 0x20 +#define PX4 0x10 +#define PPWMFD 0x08 +#define PPWM 0x04 +#define PSPI 0x02 +#define PS2 0x01 +sfr IPH = 0xb7; +#define PPCAH 0x80 +#define PLVDH 0x40 +#define PADCH 0x20 +#define PSH 0x10 +#define PT1H 0x08 +#define PX1H 0x04 +#define PT0H 0x02 +#define PX0H 0x01 +sfr IP2H = 0xb6; +#define PI2CH 0x40 +#define PCMPH 0x20 +#define PX4H 0x10 +#define PPWMFDH 0x08 +#define PPWMH 0x04 +#define PSPIH 0x02 +#define PS2H 0x01 +sfr INTCLKO = 0x8f; +#define EX4 0x40 +#define EX3 0x20 +#define EX2 0x10 +#define T2CLKO 0x04 +#define T1CLKO 0x02 +#define T0CLKO 0x01 +sfr AUXINTIF = 0xef; +#define INT4IF 0x40 +#define INT3IF 0x20 +#define INT2IF 0x10 +#define T4IF 0x04 +#define T3IF 0x02 +#define T2IF 0x01 + +//定时器特殊功能寄存器 +sfr TCON = 0x88; +sbit TF1 = TCON^7; +sbit TR1 = TCON^6; +sbit TF0 = TCON^5; +sbit TR0 = TCON^4; +sbit IE1 = TCON^3; +sbit IT1 = TCON^2; +sbit IE0 = TCON^1; +sbit IT0 = TCON^0; +sfr TMOD = 0x89; +#define T1_GATE 0x80 +#define T1_CT 0x40 +#define T1_M1 0x20 +#define T1_M0 0x10 +#define T0_GATE 0x08 +#define T0_CT 0x04 +#define T0_M1 0x02 +#define T0_M0 0x01 +sfr TL0 = 0x8a; +sfr TL1 = 0x8b; +sfr TH0 = 0x8c; +sfr TH1 = 0x8d; +sfr T4T3M = 0xd1; +#define T4R 0x80 +#define T4_CT 0x40 +#define T4x12 0x20 +#define T4CLKO 0x10 +#define T3R 0x08 +#define T3_CT 0x04 +#define T3x12 0x02 +#define T3CLKO 0x01 +sfr T4H = 0xd2; +sfr T4L = 0xd3; +sfr T3H = 0xd4; +sfr T3L = 0xd5; +sfr T2H = 0xd6; +sfr T2L = 0xd7; +sfr TH4 = 0xd2; +sfr TL4 = 0xd3; +sfr TH3 = 0xd4; +sfr TL3 = 0xd5; +sfr TH2 = 0xd6; +sfr TL2 = 0xd7; +sfr WKTCL = 0xaa; +sfr WKTCH = 0xab; +#define WKTEN 0x80 +sfr WDT_CONTR = 0xc1; +#define WDT_FLAG 0x80 +#define EN_WDT 0x20 +#define CLR_WDT 0x10 +#define IDL_WDT 0x08 + +//串行口特殊功能寄存器 +sfr SCON = 0x98; +sbit SM0 = SCON^7; +sbit SM1 = SCON^6; +sbit SM2 = SCON^5; +sbit REN = SCON^4; +sbit TB8 = SCON^3; +sbit RB8 = SCON^2; +sbit TI = SCON^1; +sbit RI = SCON^0; +sfr SBUF = 0x99; +sfr S2CON = 0x9a; +#define S2SM0 0x80 +#define S2ST4 0x40 +#define S2SM2 0x20 +#define S2REN 0x10 +#define S2TB8 0x08 +#define S2RB8 0x04 +#define S2TI 0x02 +#define S2RI 0x01 +sfr S2BUF = 0x9b; +sfr S3CON = 0xac; +#define S3SM0 0x80 +#define S3ST4 0x40 +#define S3SM2 0x20 +#define S3REN 0x10 +#define S3TB8 0x08 +#define S3RB8 0x04 +#define S3TI 0x02 +#define S3RI 0x01 +sfr S3BUF = 0xad; +sfr S4CON = 0x84; +#define S4SM0 0x80 +#define S4ST4 0x40 +#define S4SM2 0x20 +#define S4REN 0x10 +#define S4TB8 0x08 +#define S4RB8 0x04 +#define S4TI 0x02 +#define S4RI 0x01 +sfr S4BUF = 0x85; +sfr SADDR = 0xa9; +sfr SADEN = 0xb9; + +//ADC 特殊功能寄存器 +sfr ADC_CONTR = 0xbc; +#define ADC_POWER 0x80 +#define ADC_START 0x40 +#define ADC_FLAG 0x20 +sfr ADC_RES = 0xbd; +sfr ADC_RESL = 0xbe; +sfr ADCCFG = 0xde; +#define ADC_RESFMT 0x20 + +//SPI 特殊功能寄存器 +sfr SPSTAT = 0xcd; +#define SPIF 0x80 +#define WCOL 0x40 +sfr SPCTL = 0xce; +#define SSIG 0x80 +#define SPEN 0x40 +#define DORD 0x20 +#define MSTR 0x10 +#define CPOL 0x08 +#define CPHA 0x04 +sfr SPDAT = 0xcf; + +//IAP/ISP 特殊功能寄存器 +sfr IAP_DATA = 0xc2; +sfr IAP_ADDRH = 0xc3; +sfr IAP_ADDRL = 0xc4; +sfr IAP_CMD = 0xc5; +#define IAP_IDL 0x00 +#define IAP_READ 0x01 +#define IAP_WRITE 0x02 +#define IAP_ERASE 0x03 +sfr IAP_TRIG = 0xc6; +sfr IAP_CONTR = 0xc7; +#define IAPEN 0x80 +#define SWBS 0x40 +#define SWRST 0x20 +#define CMD_FAIL 0x10 +sfr ISP_DATA = 0xc2; +sfr ISP_ADDRH = 0xc3; +sfr ISP_ADDRL = 0xc4; +sfr ISP_CMD = 0xc5; +sfr ISP_TRIG = 0xc6; +sfr ISP_CONTR = 0xc7; + +//比较器特殊功能寄存器 +sfr CMPCR1 = 0xe6; +#define CMPEN 0x80 +#define CMPIF 0x40 +#define PIE 0x20 +#define NIE 0x10 +#define PIS 0x08 +#define NIS 0x04 +#define CMPOE 0x02 +#define CMPRES 0x01 +sfr CMPCR2 = 0xe7; +#define INVCMPO 0x80 +#define DISFLT 0x40 + +//PCA/PWM 特殊功能寄存器 +sfr CCON = 0xd8; +sbit CF = CCON^7; +sbit CR = CCON^6; +sbit CCF3 = CCON^3; +sbit CCF2 = CCON^2; +sbit CCF1 = CCON^1; +sbit CCF0 = CCON^0; +sfr CMOD = 0xd9; +#define CIDL 0x80 +#define ECF 0x01 +sfr CL = 0xe9; +sfr CH = 0xf9; +sfr CCAPM0 = 0xda; +#define ECOM0 0x40 +#define CCAPP0 0x20 +#define CCAPN0 0x10 +#define MAT0 0x08 +#define TOG0 0x04 +#define PWM0 0x02 +#define ECCF0 0x01 +sfr CCAPM1 = 0xdb; +#define ECOM1 0x40 +#define CCAPP1 0x20 +#define CCAPN1 0x10 +#define MAT1 0x08 +#define TOG1 0x04 +#define PWM1 0x02 +#define ECCF1 0x01 +sfr CCAPM2 = 0xdc; +#define ECOM2 0x40 +#define CCAPP2 0x20 +#define CCAPN2 0x10 +#define MAT2 0x08 +#define TOG2 0x04 +#define PWM2 0x02 +#define ECCF2 0x01 +sfr CCAPM3 = 0xdd; +#define ECOM3 0x40 +#define CCAPP3 0x20 +#define CCAPN3 0x10 +#define MAT3 0x08 +#define TOG3 0x04 +#define PWM3 0x02 +#define ECCF3 0x01 +sfr CCAP0L = 0xea; +sfr CCAP1L = 0xeb; +sfr CCAP2L = 0xec; +sfr CCAP3L = 0xed; +sfr CCAP0H = 0xfa; +sfr CCAP1H = 0xfb; +sfr CCAP2H = 0xfc; +sfr CCAP3H = 0xfd; +sfr PCA_PWM0 = 0xf2; +sfr PCA_PWM1 = 0xf3; +sfr PCA_PWM2 = 0xf4; +sfr PCA_PWM3 = 0xf5; + +//增强型PWM波形发生器特殊功能寄存器 +sfr PWMCFG = 0xf1; +#define CBIF 0x80 +#define ETADC 0x40 +sfr PWMIF = 0xf6; +#define C7IF 0x80 +#define C6IF 0x40 +#define C5IF 0x20 +#define C4IF 0x10 +#define C3IF 0x08 +#define C2IF 0x04 +#define C1IF 0x02 +#define C0IF 0x01 +sfr PWMFDCR = 0xf7; +#define INVCMP 0x80 +#define INVIO 0x40 +#define ENFD 0x20 +#define FLTFLIO 0x10 +#define EFDI 0x08 +#define FDCMP 0x04 +#define FDIO 0x02 +#define FDIF 0x01 +sfr PWMCR = 0xfe; +#define ENPWM 0x80 +#define ECBI 0x40 + +//如下特殊功能寄存器位于扩展RAM区域 +//访问这些寄存器,需先将P_SW2的BIT7设置为1,才可正常读写 +#define PWMC (*(unsigned int volatile xdata *)0xfff0) +#define PWMCH (*(unsigned char volatile xdata *)0xfff0) +#define PWMCL (*(unsigned char volatile xdata *)0xfff1) +#define PWMCKS (*(unsigned char volatile xdata *)0xfff2) +#define TADCP (*(unsigned char volatile xdata *)0xfff3) +#define TADCPH (*(unsigned char volatile xdata *)0xfff3) +#define TADCPL (*(unsigned char volatile xdata *)0xfff4) +#define PWM0T1 (*(unsigned int volatile xdata *)0xff00) +#define PWM0T1H (*(unsigned char volatile xdata *)0xff00) +#define PWM0T1L (*(unsigned char volatile xdata *)0xff01) +#define PWM0T2 (*(unsigned int volatile xdata *)0xff02) +#define PWM0T2H (*(unsigned char volatile xdata *)0xff02) +#define PWM0T2L (*(unsigned char volatile xdata *)0xff03) +#define PWM0CR (*(unsigned char volatile xdata *)0xff04) +#define PWM0HLD (*(unsigned char volatile xdata *)0xff05) +#define PWM1T1 (*(unsigned int volatile xdata *)0xff10) +#define PWM1T1H (*(unsigned char volatile xdata *)0xff10) +#define PWM1T1L (*(unsigned char volatile xdata *)0xff11) +#define PWM1T2 (*(unsigned int volatile xdata *)0xff12) +#define PWM1T2H (*(unsigned char volatile xdata *)0xff12) +#define PWM1T2L (*(unsigned char volatile xdata *)0xff13) +#define PWM1CR (*(unsigned char volatile xdata *)0xff14) +#define PWM1HLD (*(unsigned char volatile xdata *)0xff15) +#define PWM2T1 (*(unsigned int volatile xdata *)0xff20) +#define PWM2T1H (*(unsigned char volatile xdata *)0xff20) +#define PWM2T1L (*(unsigned char volatile xdata *)0xff21) +#define PWM2T2 (*(unsigned int volatile xdata *)0xff22) +#define PWM2T2H (*(unsigned char volatile xdata *)0xff22) +#define PWM2T2L (*(unsigned char volatile xdata *)0xff23) +#define PWM2CR (*(unsigned char volatile xdata *)0xff24) +#define PWM2HLD (*(unsigned char volatile xdata *)0xff25) +#define PWM3T1 (*(unsigned int volatile xdata *)0xff30) +#define PWM3T1H (*(unsigned char volatile xdata *)0xff30) +#define PWM3T1L (*(unsigned char volatile xdata *)0xff31) +#define PWM3T2 (*(unsigned int volatile xdata *)0xff32) +#define PWM3T2H (*(unsigned char volatile xdata *)0xff32) +#define PWM3T2L (*(unsigned char volatile xdata *)0xff33) +#define PWM3CR (*(unsigned char volatile xdata *)0xff34) +#define PWM3HLD (*(unsigned char volatile xdata *)0xff35) +#define PWM4T1 (*(unsigned int volatile xdata *)0xff40) +#define PWM4T1H (*(unsigned char volatile xdata *)0xff40) +#define PWM4T1L (*(unsigned char volatile xdata *)0xff41) +#define PWM4T2 (*(unsigned int volatile xdata *)0xff42) +#define PWM4T2H (*(unsigned char volatile xdata *)0xff42) +#define PWM4T2L (*(unsigned char volatile xdata *)0xff43) +#define PWM4CR (*(unsigned char volatile xdata *)0xff44) +#define PWM4HLD (*(unsigned char volatile xdata *)0xff45) +#define PWM5T1 (*(unsigned int volatile xdata *)0xff50) +#define PWM5T1H (*(unsigned char volatile xdata *)0xff50) +#define PWM5T1L (*(unsigned char volatile xdata *)0xff51) +#define PWM5T2 (*(unsigned int volatile xdata *)0xff52) +#define PWM5T2H (*(unsigned char volatile xdata *)0xff52) +#define PWM5T2L (*(unsigned char volatile xdata *)0xff53) +#define PWM5CR (*(unsigned char volatile xdata *)0xff54) +#define PWM5HLD (*(unsigned char volatile xdata *)0xff55) +#define PWM6T1 (*(unsigned int volatile xdata *)0xff60) +#define PWM6T1H (*(unsigned char volatile xdata *)0xff60) +#define PWM6T1L (*(unsigned char volatile xdata *)0xff61) +#define PWM6T2 (*(unsigned int volatile xdata *)0xff62) +#define PWM6T2H (*(unsigned char volatile xdata *)0xff62) +#define PWM6T2L (*(unsigned char volatile xdata *)0xff63) +#define PWM6CR (*(unsigned char volatile xdata *)0xff64) +#define PWM6HLD (*(unsigned char volatile xdata *)0xff65) +#define PWM7T1 (*(unsigned int volatile xdata *)0xff70) +#define PWM7T1H (*(unsigned char volatile xdata *)0xff70) +#define PWM7T1L (*(unsigned char volatile xdata *)0xff71) +#define PWM7T2 (*(unsigned int volatile xdata *)0xff72) +#define PWM7T2H (*(unsigned char volatile xdata *)0xff72) +#define PWM7T2L (*(unsigned char volatile xdata *)0xff73) +#define PWM7CR (*(unsigned char volatile xdata *)0xff74) +#define PWM7HLD (*(unsigned char volatile xdata *)0xff75) + +//I2C特殊功能寄存器 +//如下特殊功能寄存器位于扩展RAM区域 +//访问这些寄存器,需先将P_SW2的BIT7设置为1,才可正常读写 +#define I2CCFG (*(unsigned char volatile xdata *)0xfe80) +#define ENI2C 0x80 +#define MSSL 0x40 +#define I2CMSCR (*(unsigned char volatile xdata *)0xfe81) +#define EMSI 0x80 +#define I2CMSST (*(unsigned char volatile xdata *)0xfe82) +#define MSBUSY 0x80 +#define MSIF 0x40 +#define MSACKI 0x02 +#define MSACKO 0x01 +#define I2CSLCR (*(unsigned char volatile xdata *)0xfe83) +#define ESTAI 0x40 +#define ERXI 0x20 +#define ETXI 0x10 +#define ESTOI 0x08 +#define SLRST 0x01 +#define I2CSLST (*(unsigned char volatile xdata *)0xfe84) +#define SLBUSY 0x80 +#define STAIF 0x40 +#define RXIF 0x20 +#define TXIF 0x10 +#define STOIF 0x08 +#define TXING 0x04 +#define SLACKI 0x02 +#define SLACKO 0x01 +#define I2CSLADR (*(unsigned char volatile xdata *)0xfe85) +#define I2CTXD (*(unsigned char volatile xdata *)0xfe86) +#define I2CRXD (*(unsigned char volatile xdata *)0xfe87) + +///////////////////////////////////////////////// + +#endif + diff --git a/rx/STC8H-cn.pdf b/rx/STC8H-cn.pdf new file mode 100644 index 0000000..a7ba1b0 Binary files /dev/null and b/rx/STC8H-cn.pdf differ diff --git a/rx/STC8_SDCC.H b/rx/STC8_SDCC.H new file mode 100644 index 0000000..b830efe --- /dev/null +++ b/rx/STC8_SDCC.H @@ -0,0 +1,396 @@ +/*------------------------------------------------------------------------- + STC8_SDCC.H - Register definitions for STC8H1K08 series (SDCC compatible) + Based on STC8.H (Keil) converted to __sfr/__sbit syntax for SDCC +-------------------------------------------------------------------------*/ +#ifndef __STC8_SDCC_H__ +#define __STC8_SDCC_H__ + +/* ===== 内核 SFR ===== */ +__sfr __at(0xE0) ACC; +__sfr __at(0xF0) B; +__sfr __at(0xD0) PSW; +__sbit __at(0xD7) CY; +__sbit __at(0xD6) AC; +__sbit __at(0xD5) F0; +__sbit __at(0xD4) RS1; +__sbit __at(0xD3) RS0; +__sbit __at(0xD2) OV; +__sbit __at(0xD0) P; +__sfr __at(0x81) SP; +__sfr __at(0x82) DPL; +__sfr __at(0x83) DPH; +__sfr __at(0xAE) TA; /* 时序控制 (IAP 解锁) */ +__sfr __at(0xE3) DPS; +__sfr __at(0xE4) DPL1; +__sfr __at(0xE5) DPH1; + +/* ===== I/O 口 SFR ===== */ +__sfr __at(0x80) P0; +__sfr __at(0x90) P1; +__sfr __at(0xA0) P2; +__sfr __at(0xB0) P3; +__sfr __at(0xC0) P4; +__sfr __at(0xC8) P5; +__sfr __at(0xE8) P6; +__sfr __at(0xF8) P7; + +/* I/O 口模式寄存器 */ +__sfr __at(0x94) P0M0; +__sfr __at(0x93) P0M1; +__sfr __at(0x92) P1M0; +__sfr __at(0x91) P1M1; +__sfr __at(0x96) P2M0; +__sfr __at(0x95) P2M1; +__sfr __at(0xB2) P3M0; +__sfr __at(0xB1) P3M1; +__sfr __at(0xB4) P4M0; +__sfr __at(0xB3) P4M1; +__sfr __at(0xCA) P5M0; +__sfr __at(0xC9) P5M1; +__sfr __at(0xCC) P6M0; +__sfr __at(0xCB) P6M1; +__sfr __at(0xE2) P7M0; +__sfr __at(0xE1) P7M1; + +/* I/O 口位定义 */ +__sbit __at(0x80) P00; __sbit __at(0x81) P01; __sbit __at(0x82) P02; __sbit __at(0x83) P03; +__sbit __at(0x84) P04; __sbit __at(0x85) P05; __sbit __at(0x86) P06; __sbit __at(0x87) P07; +__sbit __at(0x90) P10; __sbit __at(0x91) P11; __sbit __at(0x92) P12; __sbit __at(0x93) P13; +__sbit __at(0x94) P14; __sbit __at(0x95) P15; __sbit __at(0x96) P16; __sbit __at(0x97) P17; +__sbit __at(0xA0) P20; __sbit __at(0xA1) P21; __sbit __at(0xA2) P22; __sbit __at(0xA3) P23; +__sbit __at(0xA4) P24; __sbit __at(0xA5) P25; __sbit __at(0xA6) P26; __sbit __at(0xA7) P27; +__sbit __at(0xB0) P30; __sbit __at(0xB1) P31; __sbit __at(0xB2) P32; __sbit __at(0xB3) P33; +__sbit __at(0xB4) P34; __sbit __at(0xB5) P35; __sbit __at(0xB6) P36; __sbit __at(0xB7) P37; +__sbit __at(0xC0) P40; __sbit __at(0xC1) P41; __sbit __at(0xC2) P42; __sbit __at(0xC3) P43; +__sbit __at(0xC4) P44; __sbit __at(0xC5) P45; __sbit __at(0xC6) P46; __sbit __at(0xC7) P47; +__sbit __at(0xC8) P50; __sbit __at(0xC9) P51; __sbit __at(0xCA) P52; __sbit __at(0xCB) P53; +__sbit __at(0xCC) P54; __sbit __at(0xCD) P55; __sbit __at(0xCE) P56; __sbit __at(0xCF) P57; + +/* ===== 系统管理 ===== */ +__sfr __at(0x87) PCON; +#define SMOD 0x80 +#define SMOD0 0x40 +#define LVDF 0x20 +#define POF 0x10 +#define PD 0x02 +#define IDL 0x01 + +__sfr __at(0x8E) AUXR; +#define T0x12 0x80 +#define T1x12 0x40 +#define UART_M0x6 0x20 +#define T2R 0x10 +#define T2_CT 0x08 +#define T2x12 0x04 +#define EXTRAM 0x02 +#define S1ST2 0x01 + +__sfr __at(0x97) AUXR2; +#define TXLNRX 0x10 + +__sfr __at(0xA1) BUS_SPEED; +__sfr __at(0xA2) P_SW1; /* 外设切换 1 */ +__sfr __at(0xBA) P_SW2; /* 外设切换 2 */ +#define EAXFR 0x80 +__sfr __at(0xBB) VOCTRL; +__sfr __at(0xFF) RSTCFG; + +/* 扩展 RAM 寄存器 (需 EAXFR=1) */ +#define CKSEL (*(volatile unsigned char __xdata *)0xFE00) +#define CLKDIV (*(volatile unsigned char __xdata *)0xFE01) +#define IRC24MCR (*(volatile unsigned char __xdata *)0xFE02) +#define XOSCCR (*(volatile unsigned char __xdata *)0xFE03) +#define IRC32KCR (*(volatile unsigned char __xdata *)0xFE04) + +/* ===== 中断 ===== */ +__sfr __at(0xA8) IE; +__sbit __at(0xAF) EA; +__sbit __at(0xAE) ELVD; +__sbit __at(0xAD) EADC; +__sbit __at(0xAC) ES; +__sbit __at(0xAB) ET1; +__sbit __at(0xAA) EX1; +__sbit __at(0xA9) ET0; +__sbit __at(0xA8) EX0; + +__sfr __at(0xAF) IE2; +#define ET4 0x40 +#define ET3 0x20 +#define ES4 0x10 +#define ES3 0x08 +#define ET2 0x04 +#define ESPI 0x02 +#define ES2 0x01 + +__sfr __at(0xB8) IP; +__sfr __at(0xB5) IP2; +__sfr __at(0xB7) IPH; +__sfr __at(0xB6) IP2H; + +__sfr __at(0x8F) INTCLKO; +__sfr __at(0xEF) AUXINTIF; + +/* ===== 定时器 ===== */ +__sfr __at(0x88) TCON; +__sbit __at(0x8F) TF1; +__sbit __at(0x8E) TR1; +__sbit __at(0x8D) TF0; +__sbit __at(0x8C) TR0; +__sbit __at(0x8B) IE1; +__sbit __at(0x8A) IT1; +__sbit __at(0x89) IE0; +__sbit __at(0x88) IT0; + +__sfr __at(0x89) TMOD; +#define T1_GATE 0x80 +#define T1_CT 0x40 +#define T1_M1 0x20 +#define T1_M0 0x10 +#define T0_GATE 0x08 +#define T0_CT 0x04 +#define T0_M1 0x02 +#define T0_M0 0x01 + +__sfr __at(0x8A) TL0; +__sfr __at(0x8B) TL1; +__sfr __at(0x8C) TH0; +__sfr __at(0x8D) TH1; + +__sfr __at(0xD1) T4T3M; +__sfr __at(0xD2) T4H; +__sfr __at(0xD3) T4L; +__sfr __at(0xD4) T3H; +__sfr __at(0xD5) T3L; +__sfr __at(0xD6) T2H; +__sfr __at(0xD7) T2L; + +__sfr __at(0xAA) WKTCL; +__sfr __at(0xAB) WKTCH; +#define WKTEN 0x80 + +__sfr __at(0xC1) WDT_CONTR; +#define WDT_FLAG 0x80 +#define EN_WDT 0x20 +#define CLR_WDT 0x10 +#define IDL_WDT 0x08 + +/* ===== 串口 ===== */ +__sfr __at(0x98) SCON; +__sbit __at(0x9F) SM0; +__sbit __at(0x9E) SM1; +__sbit __at(0x9D) SM2; +__sbit __at(0x9C) REN; +__sbit __at(0x9B) TB8; +__sbit __at(0x9A) RB8; +__sbit __at(0x99) TI; +__sbit __at(0x98) RI; +__sfr __at(0x99) SBUF; + +__sfr __at(0x9A) S2CON; +__sfr __at(0x9B) S2BUF; +__sfr __at(0xAC) S3CON; +__sfr __at(0xAD) S3BUF; +__sfr __at(0x84) S4CON; +__sfr __at(0x85) S4BUF; + +__sfr __at(0xA9) SADDR; +__sfr __at(0xB9) SADEN; + +/* ===== ADC ===== */ +__sfr __at(0xBC) ADC_CONTR; +#define ADC_POWER 0x80 +#define ADC_START 0x40 +#define ADC_FLAG 0x20 + +__sfr __at(0xBD) ADC_RES; +__sfr __at(0xBE) ADC_RESL; +__sfr __at(0xDE) ADCCFG; +#define ADC_RESFMT 0x20 + +/* ===== SPI ===== */ +__sfr __at(0xCD) SPSTAT; +#define SPIF 0x80 +#define WCOL 0x40 +__sfr __at(0xCE) SPCTL; +#define SSIG 0x80 +#define SPEN 0x40 +#define DORD 0x20 +#define MSTR 0x10 +#define CPOL 0x08 +#define CPHA 0x04 +__sfr __at(0xCF) SPDAT; + +/* ===== IAP/ISP ===== */ +__sfr __at(0xC2) IAP_DATA; +__sfr __at(0xC3) IAP_ADDRH; +__sfr __at(0xC4) IAP_ADDRL; +__sfr __at(0xC5) IAP_CMD; +#define IAP_IDL 0x00 +#define IAP_READ 0x01 +#define IAP_WRITE 0x02 +#define IAP_ERASE 0x03 +__sfr __at(0xC6) IAP_TRIG; +__sfr __at(0xC7) IAP_CONTR; +#define IAPEN 0x80 +#define SWBS 0x40 +#define SWRST 0x20 +#define CMD_FAIL 0x10 + +/* ===== PCA/PWM ===== */ +__sfr __at(0xD8) CCON; +__sbit __at(0xDF) CF; +__sbit __at(0xDE) CR; +__sbit __at(0xDB) CCF3; +__sbit __at(0xDA) CCF2; +__sbit __at(0xD9) CCF1; +__sbit __at(0xD8) CCF0; + +__sfr __at(0xD9) CMOD; +#define CIDL 0x80 +#define ECF 0x01 + +__sfr __at(0xE9) CL; +__sfr __at(0xF9) CH; +__sfr __at(0xDA) CCAPM0; +__sfr __at(0xDB) CCAPM1; +__sfr __at(0xDC) CCAPM2; +__sfr __at(0xDD) CCAPM3; +__sfr __at(0xEA) CCAP0L; +__sfr __at(0xEB) CCAP1L; +__sfr __at(0xEC) CCAP2L; +__sfr __at(0xED) CCAP3L; +__sfr __at(0xFA) CCAP0H; +__sfr __at(0xFB) CCAP1H; +__sfr __at(0xFC) CCAP2H; +__sfr __at(0xFD) CCAP3H; +__sfr __at(0xF2) PCA_PWM0; +__sfr __at(0xF3) PCA_PWM1; +__sfr __at(0xF4) PCA_PWM2; +__sfr __at(0xF5) PCA_PWM3; + +/* ===== 高级 PWM ===== */ +__sfr __at(0xF1) PWMCFG; +#define CBIF 0x80 +#define ETADC 0x40 +__sfr __at(0xF6) PWMIF; +__sfr __at(0xF7) PWMFDCR; +__sfr __at(0xFE) PWMCR; +#define ENPWM 0x80 +#define ECBI 0x40 + +/* 高级 PWM 扩展寄存器 (需 EAXFR=1) */ +#define PWMC (*(volatile unsigned int __xdata *)0xFFF0) +#define PWMCH (*(volatile unsigned char __xdata *)0xFFF0) +#define PWMCL (*(volatile unsigned char __xdata *)0xFFF1) +#define PWMCKS (*(volatile unsigned char __xdata *)0xFFF2) + +#define PWM0T1 (*(volatile unsigned int __xdata *)0xFF00) +#define PWM0T1H (*(volatile unsigned char __xdata *)0xFF00) +#define PWM0T1L (*(volatile unsigned char __xdata *)0xFF01) +#define PWM0T2 (*(volatile unsigned int __xdata *)0xFF02) +#define PWM0T2H (*(volatile unsigned char __xdata *)0xFF02) +#define PWM0T2L (*(volatile unsigned char __xdata *)0xFF03) +#define PWM0CR (*(volatile unsigned char __xdata *)0xFF04) +#define PWM0HLD (*(volatile unsigned char __xdata *)0xFF05) + +#define PWM1T1 (*(volatile unsigned int __xdata *)0xFF10) +#define PWM1T1H (*(volatile unsigned char __xdata *)0xFF10) +#define PWM1T1L (*(volatile unsigned char __xdata *)0xFF11) +#define PWM1T2 (*(volatile unsigned int __xdata *)0xFF12) +#define PWM1T2H (*(volatile unsigned char __xdata *)0xFF12) +#define PWM1T2L (*(volatile unsigned char __xdata *)0xFF13) +#define PWM1CR (*(volatile unsigned char __xdata *)0xFF14) +#define PWM1HLD (*(volatile unsigned char __xdata *)0xFF15) + +#define PWM2T1 (*(volatile unsigned int __xdata *)0xFF20) +#define PWM2T1H (*(volatile unsigned char __xdata *)0xFF20) +#define PWM2T1L (*(volatile unsigned char __xdata *)0xFF21) +#define PWM2T2 (*(volatile unsigned int __xdata *)0xFF22) +#define PWM2T2H (*(volatile unsigned char __xdata *)0xFF22) +#define PWM2T2L (*(volatile unsigned char __xdata *)0xFF23) +#define PWM2CR (*(volatile unsigned char __xdata *)0xFF24) +#define PWM2HLD (*(volatile unsigned char __xdata *)0xFF25) + +#define PWM3T1 (*(volatile unsigned int __xdata *)0xFF30) +#define PWM3T1H (*(volatile unsigned char __xdata *)0xFF30) +#define PWM3T1L (*(volatile unsigned char __xdata *)0xFF31) +#define PWM3T2 (*(volatile unsigned int __xdata *)0xFF32) +#define PWM3T2H (*(volatile unsigned char __xdata *)0xFF32) +#define PWM3T2L (*(volatile unsigned char __xdata *)0xFF33) +#define PWM3CR (*(volatile unsigned char __xdata *)0xFF34) +#define PWM3HLD (*(volatile unsigned char __xdata *)0xFF35) + +#define PWM4T1 (*(volatile unsigned int __xdata *)0xFF40) +#define PWM4T1H (*(volatile unsigned char __xdata *)0xFF40) +#define PWM4T1L (*(volatile unsigned char __xdata *)0xFF41) +#define PWM4T2 (*(volatile unsigned int __xdata *)0xFF42) +#define PWM4T2H (*(volatile unsigned char __xdata *)0xFF42) +#define PWM4T2L (*(volatile unsigned char __xdata *)0xFF43) +#define PWM4CR (*(volatile unsigned char __xdata *)0xFF44) +#define PWM4HLD (*(volatile unsigned char __xdata *)0xFF45) + +#define PWM5T1 (*(volatile unsigned int __xdata *)0xFF50) +#define PWM5T1H (*(volatile unsigned char __xdata *)0xFF50) +#define PWM5T1L (*(volatile unsigned char __xdata *)0xFF51) +#define PWM5T2 (*(volatile unsigned int __xdata *)0xFF52) +#define PWM5T2H (*(volatile unsigned char __xdata *)0xFF52) +#define PWM5T2L (*(volatile unsigned char __xdata *)0xFF53) +#define PWM5CR (*(volatile unsigned char __xdata *)0xFF54) +#define PWM5HLD (*(volatile unsigned char __xdata *)0xFF55) + +#define PWM6T1 (*(volatile unsigned int __xdata *)0xFF60) +#define PWM6T1H (*(volatile unsigned char __xdata *)0xFF60) +#define PWM6T1L (*(volatile unsigned char __xdata *)0xFF61) +#define PWM6T2 (*(volatile unsigned int __xdata *)0xFF62) +#define PWM6T2H (*(volatile unsigned char __xdata *)0xFF62) +#define PWM6T2L (*(volatile unsigned char __xdata *)0xFF63) +#define PWM6CR (*(volatile unsigned char __xdata *)0xFF64) +#define PWM6HLD (*(volatile unsigned char __xdata *)0xFF65) + +#define PWM7T1 (*(volatile unsigned int __xdata *)0xFF70) +#define PWM7T1H (*(volatile unsigned char __xdata *)0xFF70) +#define PWM7T1L (*(volatile unsigned char __xdata *)0xFF71) +#define PWM7T2 (*(volatile unsigned int __xdata *)0xFF72) +#define PWM7T2H (*(volatile unsigned char __xdata *)0xFF72) +#define PWM7T2L (*(volatile unsigned char __xdata *)0xFF73) +#define PWM7CR (*(volatile unsigned char __xdata *)0xFF74) +#define PWM7HLD (*(volatile unsigned char __xdata *)0xFF75) + +/* ===== 高级 PWM 端口切换 (扩展寄存器) ===== */ +#define PWMx_PS (*(volatile unsigned char __xdata *)0xFFF5) +#define PWMx_ETRPS (*(volatile unsigned char __xdata *)0xFFF6) + +/* ===== I2C ===== */ +#define I2CCFG (*(volatile unsigned char __xdata *)0xFE80) +#define I2CMSCR (*(volatile unsigned char __xdata *)0xFE81) +#define I2CMSST (*(volatile unsigned char __xdata *)0xFE82) +#define I2CSLCR (*(volatile unsigned char __xdata *)0xFE83) +#define I2CSLST (*(volatile unsigned char __xdata *)0xFE84) +#define I2CSLADR (*(volatile unsigned char __xdata *)0xFE85) +#define I2CTXD (*(volatile unsigned char __xdata *)0xFE86) +#define I2CRXD (*(volatile unsigned char __xdata *)0xFE87) + +/* ===== 扩展 I/O 上拉/下拉 ===== */ +#define P0PU (*(volatile unsigned char __xdata *)0xFE10) +#define P1PU (*(volatile unsigned char __xdata *)0xFE11) +#define P2PU (*(volatile unsigned char __xdata *)0xFE12) +#define P3PU (*(volatile unsigned char __xdata *)0xFE13) +#define P4PU (*(volatile unsigned char __xdata *)0xFE14) +#define P5PU (*(volatile unsigned char __xdata *)0xFE15) +#define P6PU (*(volatile unsigned char __xdata *)0xFE16) +#define P7PU (*(volatile unsigned char __xdata *)0xFE17) + +#define P0NCS (*(volatile unsigned char __xdata *)0xFE18) +#define P1NCS (*(volatile unsigned char __xdata *)0xFE19) +#define P2NCS (*(volatile unsigned char __xdata *)0xFE1A) +#define P3NCS (*(volatile unsigned char __xdata *)0xFE1B) +#define P4NCS (*(volatile unsigned char __xdata *)0xFE1C) +#define P5NCS (*(volatile unsigned char __xdata *)0xFE1D) +#define P6NCS (*(volatile unsigned char __xdata *)0xFE1E) +#define P7NCS (*(volatile unsigned char __xdata *)0xFE1F) + +/* ===== 比较器 ===== */ +__sfr __at(0xE6) CMPCR1; +__sfr __at(0xE7) CMPCR2; + +#endif /* __STC8_SDCC_H__ */ diff --git a/rx/gen_pdf.py b/rx/gen_pdf.py new file mode 100644 index 0000000..b3dfa0c --- /dev/null +++ b/rx/gen_pdf.py @@ -0,0 +1,623 @@ +#!/usr/bin/env python3 +"""ZhaTianRX 接收机操作指南 PDF 生成脚本""" + +from fpdf import FPDF +import os + +class ZhaTianPDF(FPDF): + def __init__(self): + super().__init__() + self.set_auto_page_break(auto=True, margin=15) + + 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.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') + + def chapter_title(self, title, level=1): + if level == 1: + self.set_font('Helvetica', 'B', 16) + self.set_text_color(0x1A, 0x1A, 0x2E) + self.cell(0, 12, title, 0, 1) + self.line(10, self.get_y(), 200, self.get_y()) + self.ln(4) + elif level == 2: + self.set_font('Helvetica', 'B', 13) + self.set_text_color(0x2C, 0x3E, 0x50) + self.cell(0, 10, title, 0, 1) + self.ln(2) + elif level == 3: + self.set_font('Helvetica', 'B', 11) + self.set_text_color(0x34, 0x49, 0x5E) + self.cell(0, 8, title, 0, 1) + self.ln(1) + + def body_text(self, text): + self.set_font('Helvetica', '', 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_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.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, 0, 1, '', True) + self.ln(2) + + def step_block(self, num, title, desc, code=None): + """操作步骤""" + self.set_font('Helvetica', 'B', 10) + self.set_text_color(0xC0, 0x39, 0x2B) + self.cell(0, 6, f'步骤 {num}: {title}', 0, 1) + self.set_font('Helvetica', '', 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('Helvetica', '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.ln() + + def table_row(self, cols, widths, fill=False): + self.set_font('Helvetica', '', 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.ln() + + +def build_pdf(): + pdf = ZhaTianPDF() + pdf.set_title('ZhaTianRX 接收机操作指南') + pdf.set_author('ZhaTianRC Project') + + # ==================== 封面 ==================== + pdf.add_page() + pdf.ln(40) + pdf.set_font('Helvetica', 'B', 28) + pdf.set_text_color(0x1A, 0x1A, 0x2E) + pdf.cell(0, 15, 'ZhaTianRX', 0, 1, 'C') + pdf.set_font('Helvetica', '', 16) + pdf.set_text_color(0x7F, 0x8C, 0x8D) + pdf.cell(0, 10, '接收机操作指南', 0, 1, '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_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.ln(20) + pdf.set_font('Helvetica', 'I', 9) + pdf.cell(0, 7, '本文档涵盖接收机的所有功能、操作步骤及串口命令参考', 0, 1, 'C') + + # ==================== 目录 ==================== + pdf.add_page() + pdf.chapter_title('目录') + pdf.set_font('Helvetica', '', 11) + toc = [ + '1. 概述', + '2. 硬件引脚分配', + '3. 首次使用 - 对频操作', + '4. 输出模式切换', + '5. 串口命令详解', + '6. 回传遥测功能', + '7. LED 状态指示', + '8. 固件烧录方法', + '9. 附录 - 技术规格', + ] + for item in toc: + pdf.cell(0, 8, item, 0, 1) + 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', '256 字节 (XDATA)'], + ['射频芯片', '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)) + + # 保存 + 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 new file mode 100644 index 0000000..2be1a1b --- /dev/null +++ b/rx/hex2bin.py @@ -0,0 +1,29 @@ +#!/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 new file mode 100644 index 0000000..2c08d11 --- /dev/null +++ b/rx/main.c @@ -0,0 +1,1381 @@ +/** + * ZhaTianRX - STC8H1K08 接收机固件 + * 主程序: NRF24L01 接收 + 4路舵机PWM + SBUS输出 + 串口命令 + * + * MCU: STC8H1K08 (SOP16/SOP20) + * 编译: SDCC -mmcs51 --model-small + * + * 引脚分配: + * CH1(P3.7) CH2(P3.6) CH3(P3.5) CH4(P3.4) - 舵机PWM输出 50Hz + * BAT_ADC(P3.3/ADC11) - 电池电压分压输入 + * LED(P3.2) - 状态指示灯 (低电平亮) + * TX(P3.1) - UART1 TX / SBUS 输出 (经板上三极管反相) + * RX(P3.0) - UART1 RX / 命令输入 + * + * NRF24L01 (软件SPI): + * SCK(P1.5) CE(P1.6) IRQ(P1.7) CSN(P5.4) MISO(P1.3) MOSI(P1.4) + */ +#include "STC8_SDCC.H" +#include "NRF24.h" +#include + +/* ======================================================================== + * 系统常量 + * ======================================================================== */ + +#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; + +/* ======================================================================== + * 函数声明 + * ======================================================================== */ + +static void System_Init(void); +static void GPIO_Init(void); +static void Timer0_Init(void); +static void UART1_Init(void); +static void ADC_Init(void); +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 void EEPROM_LoadConfig(void); +static void EEPROM_SaveConfig(void); +static uint8_t EEPROM_IsBound(void); + +static void LED_SetMode(uint8_t mode); +static void LED_Update(void); + +static void PWM_SetChannel(uint8_t ch, uint16_t pulse_us); +static void PWM_SetAllFailsafe(void); + +static void UART1_SendByte(uint8_t dat); +static void UART1_SendString(const char *s); +static void UART1_SendStringLen(const char *s, uint8_t len); + +static void SBUS_SendFrame(void); +static void SBUS_Init(void); + +static void CRFS_SendFrame(void); +static void CRFS_Init(void); + +static void Cmd_Parse(void); +static void Cmd_Respond(const char *resp); + +static void NRF_ProcessPacket(void); +static void NRF_EnterBindMode(void); +static uint8_t NRF_DoBindScan(void); +static void NRF_SendTelemetry(void); + +/* ======================================================================== + * 系统初始化 + * ======================================================================== */ + +static void System_Init(void) { + /* 切换到内部24MHz IRC */ + P_SW2 |= EAXFR; /* 使能访问扩展SFR */ + CLKDIV = 0x00; /* 主时钟不分频 */ + CKSEL = 0x00; /* 选择内部高速IRC */ + P_SW2 &= ~EAXFR; + + /* 设置内部IRC为24MHz */ + IRC24MCR = 0x80; /* 使能内部24MHz IRC */ + while (!(IRC24MCR & 0x01)); /* 等待IRC稳定 */ +} + +static void GPIO_Init(void) { + /* P3.4-P3.7: 推挽输出 (PWM通道) */ + P3M0 |= 0xF0; + P3M1 &= ~0xF0; + + /* P3.2: 推挽输出 (LED) */ + P3M0 |= 0x04; + P3M1 &= ~0x04; + + /* P3.1: 推挽输出 (UART TX) */ + P3M0 |= 0x02; + P3M1 &= ~0x02; + + /* P3.0: 高阻输入 (UART RX) */ + P3M0 &= ~0x01; + P3M1 |= 0x01; + + /* P3.3: 高阻输入 (ADC) */ + P3M0 &= ~0x08; + P3M1 |= 0x08; + + /* NRF24L01 SPI 引脚 */ + /* P1.5(SCK), P1.4(MOSI), P1.6(CE): 推挽输出 */ + P1M0 |= 0x70; + P1M1 &= ~0x70; + /* P1.3(MISO): 高阻输入 */ + P1M0 &= ~0x08; + P1M1 |= 0x08; + /* P1.7(IRQ): 高阻输入 (轮询) */ + P1M0 &= ~0x80; + P1M1 |= 0x80; + + /* P5.4(CSN): 推挽输出 */ + P5M0 |= 0x10; + P5M1 &= ~0x10; + + /* 初始电平 */ + P34 = 0; P35 = 0; P36 = 0; P37 = 0; /* PWM 初始低 */ + P32 = 1; /* LED 初始灭 (低电平亮) */ + P31 = 1; /* TX 空闲高 */ + + /* NRF 引脚初始 */ + NRF_CE = 0; + NRF_CSN = 1; + NRF_SCK = 0; + NRF_MOSI = 0; +} + +/* ======================================================================== + * 定时器0 - PWM 时基 (4us 中断) + * ======================================================================== */ + +static void Timer0_Init(void) { + /* 16位自动重载模式, 1T */ + TMOD &= ~0x0F; + TMOD |= 0x01; /* T0: 16位定时器 */ + AUXR |= T0x12; /* T0: 1T模式 */ + + /* 4us @ 24MHz: 96 ticks */ + TH0 = 0xFF; + TL0 = 0xA0; /* 65536 - 96 = 0xFFA0 */ + + ET0 = 1; /* 使能T0中断 */ + TR0 = 1; /* 启动T0 */ +} + +/* T0 中断服务 - 4us一次, 生成4路PWM */ +void tm0_isr(void) __interrupt(1) { + static uint16_t pwm_cnt = 0; + + pwm_cnt++; + if (pwm_cnt >= PWM_PERIOD) { + pwm_cnt = 0; + } + + /* 4路舵机PWM (P3.4-P3.7) */ + if (pwm_cnt < pwm_ch[0]) P37 = 1; else P37 = 0; + if (pwm_cnt < pwm_ch[1]) P36 = 1; else P36 = 0; + if (pwm_cnt < pwm_ch[2]) P35 = 1; else P35 = 0; + if (pwm_cnt < pwm_ch[3]) P34 = 1; else P34 = 0; + + /* 系统滴答 (每10ms) */ + if (pwm_cnt == 0) { + sys_tick++; + } +} + +/* ======================================================================== + * 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_isr(void) __interrupt(4) { + if (RI) { + RI = 0; + uint8_t dat = SBUF; + + if (out_mode == OUT_MODE_CRFS) { + /* CRFS 模式: 接收飞控发来的 CRSF 帧并缓存转发 */ + if (dat == 0xEE) { + /* CRSF 帧起始 */ + crfs_forward_len = 0; + crfs_forward_buf[crfs_forward_len++] = dat; + } else if (crfs_forward_len > 0 && crfs_forward_len < sizeof(crfs_forward_buf)) { + crfs_forward_buf[crfs_forward_len++] = dat; + /* 如果收到完整帧 (根据长度字段), 标记为待转发 */ + if (crfs_forward_len >= 3) { + uint8_t frame_len = crfs_forward_buf[1] + 2; /* type+payload+crc = length field */ + if (crfs_forward_len >= frame_len) { + /* 完整帧已收到, 保持缓存, 主循环会处理转发 */ + } + } + } + } else { + /* 命令接收: 回车/换行结束 */ + if (dat == '\r' || dat == '\n') { + if (uart_rx_idx > 0) { + uart_rx_buf[uart_rx_idx] = '\0'; + uart_cmd_ready = 1; + } + uart_rx_idx = 0; + } else { + if (uart_rx_idx < UART_BUF_SIZE - 1) { + uart_rx_buf[uart_rx_idx++] = dat; + } + } + } + } + if (TI) { + TI = 0; + } +} + +static void UART1_SendByte(uint8_t dat) { + SBUF = dat; + while (!TI); + TI = 0; +} + +static void UART1_SendString(const char *s) { + while (*s) { + UART1_SendByte((uint8_t)(*s++)); + } +} + +static void UART1_SendStringLen(const char *s, uint8_t len) { + for (uint8_t i = 0; i < len; i++) { + UART1_SendByte((uint8_t)s[i]); + } +} + +/* ======================================================================== + * SBUS 输出 (100kbps 8E2, 与UART1共用TX) + * ======================================================================== */ + +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; + } +} + +/* ======================================================================== + * 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) { + 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; + } +} + +/* ======================================================================== + * ADC - 电池电压检测 (P3.3/ADC11) + * ======================================================================== */ + +static void ADC_Init(void) { + ADCCFG = ADC_RESFMT; /* 右对齐, 12位结果 */ + ADC_CONTR = ADC_POWER; /* 开启ADC电源 */ + /* 选择通道11 (P3.3) */ + ADC_CONTR = (ADC_CONTR & 0xF0) | 0x0B; +} + +static uint16_t ADC_Read(void) { + ADC_CONTR |= ADC_START; /* 启动转换 */ + while (!(ADC_CONTR & ADC_FLAG)); /* 等待完成 */ + ADC_CONTR &= ~ADC_FLAG; /* 清除标志 */ + return ((uint16_t)ADC_RES << 8) | ADC_RESL; +} + +/* ======================================================================== + * IAP/EEPROM 操作 + * ======================================================================== */ + +static void IAP_Init(void) { + /* IAP 默认已就绪, 无需额外初始化 */ +} + +static uint8_t IAP_ReadByte(uint16_t addr) { + uint8_t dat; + IAP_CONTR = IAPEN; /* 使能IAP */ + IAP_CMD = IAP_READ; + IAP_ADDRH = (uint8_t)(addr >> 8); + IAP_ADDRL = (uint8_t)(addr & 0xFF); + /* 触发 */ + TA = 0x5A; + TA = 0xA5; + IAP_TRIG = 0x5A; + IAP_TRIG = 0xA5; + /* NOP 等待 */ + __asm nop __endasm; + __asm nop __endasm; + dat = IAP_DATA; + IAP_CONTR = 0x00; /* 关闭IAP */ + return dat; +} + +static void IAP_WriteByte(uint16_t addr, uint8_t dat) { + IAP_CONTR = IAPEN; /* 使能IAP */ + IAP_CMD = IAP_WRITE; + IAP_ADDRH = (uint8_t)(addr >> 8); + IAP_ADDRL = (uint8_t)(addr & 0xFF); + IAP_DATA = dat; + /* 触发 */ + TA = 0x5A; + TA = 0xA5; + IAP_TRIG = 0x5A; + IAP_TRIG = 0xA5; + __asm nop __endasm; + __asm nop __endasm; + IAP_CONTR = 0x00; /* 关闭IAP */ +} + +static void IAP_EraseSector(uint16_t addr) { + IAP_CONTR = IAPEN; /* 使能IAP */ + IAP_CMD = IAP_ERASE; + IAP_ADDRH = (uint8_t)(addr >> 8); + IAP_ADDRL = (uint8_t)(addr & 0xFF); + /* 触发 */ + TA = 0x5A; + TA = 0xA5; + IAP_TRIG = 0x5A; + IAP_TRIG = 0xA5; + __asm nop __endasm; + __asm nop __endasm; + IAP_CONTR = 0x00; /* 关闭IAP */ +} + +/* ======================================================================== + * EEPROM 配置存储 + * ======================================================================== */ + +static uint8_t EEPROM_IsBound(void) { + return (IAP_ReadByte(EEPROM_ADDR_BIND_FLAG) == EEPROM_BIND_MAGIC); +} + +static void EEPROM_LoadConfig(void) { + if (!EEPROM_IsBound()) { + /* 未对频, 进入对频模式 */ + rx_state = RX_STATE_BINDING; + return; + } + + /* 读取保存的配置 */ + for (uint8_t i = 0; i < NRF_ADDR_WIDTH; i++) { + tx_addr[i] = IAP_ReadByte(EEPROM_ADDR_TX_ADDR + i); + } + rf_channel = IAP_ReadByte(EEPROM_ADDR_RF_CH); + for (uint8_t i = 0; i < RF_PHRASE_LEN; i++) { + bind_phrase[i] = IAP_ReadByte(EEPROM_ADDR_BIND_PHRASE + i); + } + + rx_state = RX_STATE_DISCONNECTED; +} + +static void EEPROM_SaveConfig(void) { + /* 擦除扇区 (512字节, 从0x0000开始) */ + IAP_EraseSector(0x0000); + + /* 写入对频标志 */ + IAP_WriteByte(EEPROM_ADDR_BIND_FLAG, EEPROM_BIND_MAGIC); + + /* 写入TX地址 */ + for (uint8_t i = 0; i < NRF_ADDR_WIDTH; i++) { + IAP_WriteByte(EEPROM_ADDR_TX_ADDR + i, tx_addr[i]); + } + + /* 写入RF频道 */ + IAP_WriteByte(EEPROM_ADDR_RF_CH, rf_channel); + + /* 写入对频短语 */ + for (uint8_t i = 0; i < RF_PHRASE_LEN; i++) { + IAP_WriteByte(EEPROM_ADDR_BIND_PHRASE + i, bind_phrase[i]); + } +} + +/* ======================================================================== + * LED 控制 (P3.2, 低电平亮) + * ======================================================================== */ + +static void LED_SetMode(uint8_t mode) { + static uint8_t current_mode = 0xFF; + if (mode != current_mode) { + current_mode = mode; + switch (mode) { + case LED_ON: P32 = 0; break; /* 常亮 */ + case LED_OFF: P32 = 1; break; /* 熄灭 */ + default: break; /* 闪烁模式由 LED_Update 处理 */ + } + } +} + +static void LED_Update(void) { + uint8_t mode; + if (nrf_hw_error) { + mode = LED_BLINK_DOUBLE; + } else if (rx_state == RX_STATE_BINDING) { + mode = LED_BLINK_FAST; + } else if (rx_state == RX_STATE_CONNECTED) { + mode = LED_ON; + } else { + mode = LED_BLINK_SLOW; + } + + switch (mode) { + case LED_BLINK_SLOW: /* 1Hz: 500ms亮/500ms灭 */ + P32 = (sys_tick % 100 < 50) ? 0 : 1; + break; + case LED_BLINK_FAST: /* 3Hz: ~167ms亮/~167ms灭 */ + P32 = (sys_tick % 30 < 15) ? 0 : 1; + break; + case LED_BLINK_DOUBLE: /* 双闪: 100ms亮+100ms灭+100ms亮+700ms灭 */ + { + uint16_t t = sys_tick % 100; + if (t < 10) P32 = 0; /* 亮 100ms */ + else if (t < 20) P32 = 1; /* 灭 100ms */ + else if (t < 30) P32 = 0; /* 亮 100ms */ + else P32 = 1; /* 灭 700ms */ + } + break; + case LED_ON: + P32 = 0; + break; + default: + P32 = 1; + break; + } +} + +/* ======================================================================== + * PWM 通道设置 + * ======================================================================== */ + +static void PWM_SetChannel(uint8_t ch, uint16_t pulse_us) { + if (ch >= CHANNEL_COUNT) return; + + /* 限制脉宽范围: 1.0ms ~ 2.0ms */ + if (pulse_us < 1000) pulse_us = 1000; + if (pulse_us > 2000) pulse_us = 2000; + + /* 转换为4us步长 */ + pwm_ch[ch] = pulse_us / 4; +} + +static void PWM_SetAllFailsafe(void) { + /* 失控保护: 所有通道回到中位 */ + for (uint8_t i = 0; i < CHANNEL_COUNT; i++) { + pwm_ch[i] = PWM_MID; + } +} + +/* ======================================================================== + * 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(); +} + +/* ======================================================================== + * 串口命令解析 + * ======================================================================== */ + +static void Cmd_Respond(const char *resp) { + UART1_SendString(resp); + UART1_SendString("\r\n"); +} + +static void Cmd_Parse(void) { + if (!uart_cmd_ready) return; + uart_cmd_ready = 0; + + char *cmd = (char *)uart_rx_buf; + + /* MODEL(SBUS) - 切换到SBUS模式 */ + if (strcmp(cmd, "MODEL(SBUS)") == 0) { + out_mode = OUT_MODE_SBUS; + SBUS_Init(); + Cmd_Respond("OK"); + return; + } + + /* MODEL(PWM) - 切换到PWM模式 */ + if (strcmp(cmd, "MODEL(PWM)") == 0) { + out_mode = OUT_MODE_PWM; + /* 重新初始化UART1为115200 */ + UART1_Init(); + Cmd_Respond("OK"); + return; + } + + /* MODEL(UART) - 切换到UART模式 (占位) */ + if (strcmp(cmd, "MODEL(UART)") == 0) { + out_mode = OUT_MODE_UART; + UART1_Init(); + Cmd_Respond("OK"); + return; + } + + /* MODEL(CRFS) - 切换到CRFS/CRSF输出模式 */ + if (strcmp(cmd, "MODEL(CRFS)") == 0) { + out_mode = OUT_MODE_CRFS; + CRFS_Init(); + Cmd_Respond("OK"); + return; + } + + /* ADDR(0xHH HH HH HH HH) - 切换NRF24L01地址 */ + if (cmd[0] == 'A' && cmd[1] == 'D' && cmd[2] == 'D' && cmd[3] == 'R') { + /* 解析: ADDR(0xHH HH HH HH HH) */ + static uint8_t __xdata new_addr[NRF_ADDR_WIDTH]; + uint8_t ok = 1; + char *p = cmd + 5; /* 跳过 "ADDR(" */ + for (uint8_t i = 0; i < NRF_ADDR_WIDTH; i++) { + /* 跳过 "0x" */ + if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) p += 2; + /* 跳过空格 */ + while (*p == ' ') p++; + /* 解析两位十六进制 */ + uint8_t hi = 0, lo = 0; + if (*p >= '0' && *p <= '9') hi = *p - '0'; + else if (*p >= 'A' && *p <= 'F') hi = *p - 'A' + 10; + else if (*p >= 'a' && *p <= 'f') hi = *p - 'a' + 10; + else { ok = 0; break; } + p++; + if (*p >= '0' && *p <= '9') lo = *p - '0'; + else if (*p >= 'A' && *p <= 'F') lo = *p - 'A' + 10; + else if (*p >= 'a' && *p <= 'f') lo = *p - 'a' + 10; + else { ok = 0; break; } + p++; + new_addr[i] = (hi << 4) | lo; + } + if (ok) { + for (uint8_t i = 0; i < NRF_ADDR_WIDTH; i++) + tx_addr[i] = new_addr[i]; + NRF24L01_WriteBuf(NRF_TX_ADDR, tx_addr, NRF_ADDR_WIDTH); + NRF24L01_WriteBuf(NRF_RX_ADDR_P0, tx_addr, NRF_ADDR_WIDTH); + Cmd_Respond("OK"); + } else { + Cmd_Respond("ERR_ADDR"); + } + return; + } + + /* TUN(N) - 切换RF频道 */ + if (cmd[0] == 'T' && cmd[1] == 'U' && cmd[2] == 'N') { + uint8_t ch = 0; + char *p = cmd + 4; /* 跳过 "TUN(" */ + while (*p >= '0' && *p <= '9') { + ch = ch * 10 + (*p - '0'); + p++; + } + if (ch <= NRF_MAX_CHANNEL) { + NRF24L01_SetChannel(ch); + Cmd_Respond("OK"); + } else { + Cmd_Respond("ERR_RANGE"); + } + return; + } + + /* DEL(BIND) - 清除对频信息 */ + if (strcmp(cmd, "DEL(BIND)") == 0) { + /* 擦除EEPROM中的对频标志 */ + IAP_EraseSector(0x0000); + IAP_WriteByte(EEPROM_ADDR_BIND_FLAG, 0x00); + rx_state = RX_STATE_BINDING; + NRF_EnterBindMode(); + Cmd_Respond("OK"); + return; + } + + /* PHRASE(xxxxxx) - 设置对频短语, 1~6位字母数字 */ + if (cmd[0] == 'P' && cmd[1] == 'H' && cmd[2] == 'R' && cmd[3] == 'A' + && cmd[4] == 'S' && cmd[5] == 'E') { + uint8_t i; + char *p = cmd + 7; /* 跳过 "PHRASE(" */ + /* 计算短语长度 (直到 ')' 或结束) */ + uint8_t len = 0; + while (p[len] && p[len] != ')' && len < RF_PHRASE_LEN) { + char c = p[len]; + /* 只允许字母和数字 */ + if (!((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) { + Cmd_Respond("ERR"); + return; + } + len++; + } + if (len < 1 || len > RF_PHRASE_LEN) { + Cmd_Respond("ERR_RANGE"); + return; + } + /* 复制新短语 */ + for (i = 0; i < len; i++) + bind_phrase[i] = (uint8_t)p[i]; + /* 剩余部分用空格填充 */ + for (i = len; i < RF_PHRASE_LEN; i++) + bind_phrase[i] = ' '; + /* 保存到EEPROM */ + IAP_EraseSector(0x0000); + IAP_WriteByte(EEPROM_ADDR_BIND_FLAG, EEPROM_BIND_MAGIC); + for (i = 0; i < NRF_ADDR_WIDTH; i++) + IAP_WriteByte(EEPROM_ADDR_TX_ADDR + i, tx_addr[i]); + IAP_WriteByte(EEPROM_ADDR_RF_CH, rf_channel); + for (i = 0; i < RF_PHRASE_LEN; i++) + IAP_WriteByte(EEPROM_ADDR_BIND_PHRASE + i, bind_phrase[i]); + /* 重新初始化NRF以使用新短语 */ + NRF24L01_Init(); + NRF24L01_RXMode(); + Cmd_Respond("OK"); + return; + } + + /* TELEM(N) - 设置回传频率 1~10Hz */ + if (cmd[0] == 'T' && cmd[1] == 'E' && cmd[2] == 'L' && cmd[3] == 'E' + && cmd[4] == 'M') { + uint8_t hz = 0; + char *p = cmd + 6; /* 跳过 "TELEM(" */ + while (*p >= '0' && *p <= '9') { + hz = hz * 10 + (*p - '0'); + p++; + } + if (hz >= 1 && hz <= 10) { + telem_rate_hz = hz; + Cmd_Respond("OK"); + } else { + Cmd_Respond("ERR_RANGE"); + } + return; + } + + /* 未知命令 */ + Cmd_Respond("UNK"); +} + +/* ======================================================================== + * 主函数 + * ======================================================================== */ + +void main(void) { + /* 系统初始化 */ + System_Init(); + GPIO_Init(); + Timer0_Init(); + UART1_Init(); + ADC_Init(); + IAP_Init(); + + /* 初始PWM: 中位 */ + PWM_SetAllFailsafe(); + + /* 从EEPROM加载配置 */ + EEPROM_LoadConfig(); + + /* 初始化NRF24L01 */ + NRF24L01_Init(); + + /* 检测NRF模块 */ + if (!NRF24L01_Check()) { + /* NRF模块未检测到, 设置硬件错误标志, LED双闪 */ + nrf_hw_error = 1; + } + + /* 进入接收模式 */ + if (rx_state == RX_STATE_BINDING) { + NRF_EnterBindMode(); + } else { + NRF24L01_RXMode(); + } + + /* 主循环 */ + while (1) { + /* 处理串口命令 */ + Cmd_Parse(); + + /* NRF 硬件错误时定期重检 */ + if (nrf_hw_error) { + static uint16_t last_check_tick = 0; + if (sys_tick - last_check_tick >= 50) { /* 每 500ms 重检一次 */ + last_check_tick = sys_tick; + NRF24L01_Init(); + if (NRF24L01_Check()) { + nrf_hw_error = 0; + NRF24L01_RXMode(); + } + } + LED_Update(); + continue; + } + + /* 对频模式 */ + if (rx_state == RX_STATE_BINDING) { + if (NRF_DoBindScan()) { + /* 对频成功, 继续 */ + } + /* 更新LED */ + LED_Update(); + continue; + } + + /* 接收数据包 */ + if (NRF24L01_IsDataReady()) { + NRF_ProcessPacket(); + } else { + lost_packet_cnt++; + /* 每 100 个丢失包更新一次丢包率 (滑动窗口) */ + static uint16_t loss_window_cnt = 0; + static uint16_t loss_window_pkts = 0; + loss_window_cnt++; + loss_window_pkts++; + if (loss_window_cnt >= 100) { + /* 如果连续100次都没收到, 丢包率上升 */ + if (telem_packet_loss < 99) telem_packet_loss++; + loss_window_cnt = 0; + loss_window_pkts = 0; + } + if (lost_packet_cnt > LOST_PACKET_TIMEOUT) { + if (rx_state == RX_STATE_CONNECTED) { + rx_state = RX_STATE_DISCONNECTED; + PWM_SetAllFailsafe(); + } + } + } + + /* 回传发送 (PWM/SBUS 模式通过 NRF, CRFS 模式通过 UART) */ + if (rx_state == RX_STATE_CONNECTED) { + static uint16_t last_telem_tick = 0; + uint16_t telem_interval = 100 / telem_rate_hz; /* sys_tick=10ms单位 */ + if (telem_interval < 1) telem_interval = 1; + if (sys_tick - last_telem_tick >= telem_interval) { + last_telem_tick = sys_tick; + + if (out_mode == OUT_MODE_CRFS) { + /* CRFS 模式: 通过 CRSF 链路回传电压/RSSI/丢包 + * CRSF 遥测帧: type=0x02 (GPS), 或 type=0x08 (Battery sensor) + * 这里使用 Battery sensor type=0x08 + * 帧: 0xEE + len + 0x08 + voltage(2B)+current(2B)+capacity(3B)+remaining(1B) + crc + * 简化: 直接发一个精简遥测帧 + */ + static uint8_t __xdata telem_frame[14]; + 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[5] = 0; /* current high */ + telem_frame[6] = 0; /* current low */ + telem_frame[7] = 0; /* capacity */ + telem_frame[8] = 0; + telem_frame[9] = 0; + telem_frame[10] = telem_rssi; /* remaining = RSSI */ + telem_frame[11] = CRSF_CalcCRC(telem_frame + 2, 9); + for (uint8_t i = 0; i < 12; i++) { + SBUF = telem_frame[i]; + while (!TI); + TI = 0; + } + } else { + /* PWM/SBUS 模式: 通过 NRF 回传 */ + NRF_SendTelemetry(); + } + } + } + + /* SBUS 输出模式 (每10ms发送一帧) */ + if (out_mode == OUT_MODE_SBUS && rx_state == RX_STATE_CONNECTED) { + static uint16_t last_sbus_tick = 0; + if (sys_tick - last_sbus_tick >= 10) { + last_sbus_tick = sys_tick; + SBUS_SendFrame(); + } + } + + /* CRFS 输出模式 (每10ms发送一帧) */ + if (out_mode == OUT_MODE_CRFS && rx_state == RX_STATE_CONNECTED) { + static uint16_t last_crfs_tick = 0; + if (sys_tick - last_crfs_tick >= 10) { + last_crfs_tick = sys_tick; + CRFS_SendFrame(); + } + + /* 转发飞控发来的 CRSF 帧到 NRF 回传通道 */ + if (crfs_forward_len >= 3) { + uint8_t frame_len = crfs_forward_buf[1] + 2; + if (crfs_forward_len >= frame_len) { + /* 通过 NRF 转发给发射机 */ + 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 < frame_len && i < RF_PACKET_SIZE; i++) + NRF_SPI_TransferByte(crfs_forward_buf[i]); + NRF_CSN = 1; + NRF_CE = 1; + for (volatile uint16_t d = 0; d < 100; d++); + NRF_CE = 0; + NRF24L01_RXMode(); + NRF24L01_FlushRX(); + crfs_forward_len = 0; + } + } + } + + /* 读取电池电压 (每100ms) */ + 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值, 上位机或飞控自行换算 */ + } + + /* 更新LED */ + LED_Update(); + } +} diff --git a/rx/test.ihx b/rx/test.ihx new file mode 100644 index 0000000..31407cb --- /dev/null +++ b/rx/test.ihx @@ -0,0 +1,12 @@ +:03000000020006F5 +:03005F0002000399 +:0300030002006296 +:0700620075B20075B1002228 +:06003500E478FFF6D8FD9F +:200013007900E94400601B7A0090006D780175A000E493F2A308B8000205A0D9F4DAF27526 +:02003300A0FF2C +:20003B007800E84400600A790175A000E4F309D8FC7800E84400600C7900900001E4F0A3C3 +:04005B00D8FCD9FAFA +:0D000600758107120069E5826003020003A6 +:04006900758200227A +:00000001FF diff --git a/rx/test_compile.c b/rx/test_compile.c new file mode 100644 index 0000000..3175388 --- /dev/null +++ b/rx/test_compile.c @@ -0,0 +1,11 @@ +__sfr __at (0xb2) P3M0; +__sfr __at (0xb1) P3M1; +__sfr __at (0x90) P1; +__sfr __at (0xb0) P3; +__sfr __at (0xc8) P5; +__sbit __at (0x97) P15; +__sbit __at (0xb5) P35; +void main(void) { + P3M0 = 0x00; + P3M1 = 0x00; +}