/** * 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(); } }