/** * 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) */ # ifndef CONFIG # define CONFIG # include "config.h" # endif #include "nrf24.h" #include "uart.h" // EEPROM操作 static CONFIG IAP_Read(); static void IAP_Write(uint8 addr, CONFIG dat); static void IAP_Erase(uint8 addr); static void LoadConfig(); static void SystemInit(); static void PWMBInit(); static void UpdatePWMB(); static void main(void) { /* 系统初始化 */ SystemInit(); PWMBInit(); /* 初始化NRF24L01 */ NRF24L01_Init(); // 从EEPROM加载配置并应用 */ InitSet(LoadConfig()); /* 检测NRF模块 */ if (!NRF24L01_Check()) { /* NRF模块未检测到, 设置硬件错误标志, LED双闪 */ SysStatus = NRF_ERROR; while (1) LED_Update(); } while (1) { LED_Update(); } } void SystemInit() { // System_Init() // 使用stc-isp下载时, 系统时钟已设置为内部IRC 40MHz, 可省略 P_SW2 |= 0x80; // 使能访问扩展SFR(XFR) // 选择内部高速IRC作为主时钟源(复位后默认即为内部IRC,可省略) CLKSEL = 0x00; // MCKSEL[1:0]=00 选择内部IRC // 主时钟不分频 CLKDIV = 0x00; // 系统时钟 = 主时钟 / 1 = 40MHz // GPIO_Init() /* 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; // Timer_Init() /* 16位自动重载模式, 1T */ TMOD &= 0xF0; /* 清除T0模式低4位 */ TMOD |= 0x00; /* T0: 16位定时器 */ AUXR |= 0x80; /* 不分频,T0x12=1,定时器0时钟 = SYSCLK */ /* 50us @ 40MHz */ TH0 = 0xFE; TL0 = 0x70; ET0 = 1; /* 使能T0中断 */ EA = 1; TR0 = 1; /* 启动T0 */ // UART_Init() /* 使用 Timer2 做波特率发生器 */ /* 115200 @ 24MHz: T2 reload = 65536 - 52 = 0xFFCC */ T2H = 0xFF; T2L = 0xCC; AUXR |= T2x12; /* T2: 1T模式 */ AUXR |= T2R; /* 启动T2 */ /* UART1 配置: 8位可变波特率, 模式1 */ SCON = 0x50; /* 模式1, REN=1 */ AUXR |= S1ST2; /* UART1使用T2做波特率 */ PCON &= ~SMOD0; /* SMOD0=0, 帧格式8位数据 */ /* 使能接收中断 */ ES = 1; RI = 0; /* SPI 初始化: 主模式, 模式0, 5MHz @ 40MHz */ // 1. 配置 SPI 时钟频率 (SPR[1:0] = 01) // 同时设置其他控制位: // SSIG = 1 (忽略SS引脚,由MSTR控制主从)[reference:7] // SPEN = 1 (使能SPI)[reference:8] // DORD = 0 (MSB先发)[reference:9] // MSTR = 1 (主机模式)[reference:10] // CPOL = 0 (时钟空闲时为低电平)[reference:11] // CPHA = 0 (第一个时钟沿采样)[reference:12] // 组合得到 SPCTL = 0b11010001 = 0xD1 SPCTL = 0xD1; // 2. 清空中断标志 SPSTAT = 0xC0; // 3. (可选) 使能SPI中断 // IE2 |= ESPI; // ADC_Init() ADCCFG = ADC_RESFMT; /* 右对齐, 12位结果 */ ADC_CONTR = ADC_POWER; /* 开启ADC电源 */ /* 选择通道11 (P3.3) */ ADC_CONTR = (ADC_CONTR & 0xF0) | 0x0B; } void PWMBInit() { // 1. 使能访问扩展特殊功能寄存器 (XFR) P_SW2 |= 0x80; // 2. 配置时基:1MHz 计数时钟,20ms 周期, 基于40MHz系统 PWMB_PSCR = 39; // 预分频器 PWMB_ARR = 19999; // 自动重装载值 // 3. 配置四个通道为 PWM 模式1 (PWM1),使能预装载 PWMB_CCMR1 = 0x68; // 通道5: PWM模式1, 预装载使能 PWMB_CCMR2 = 0x68; // 通道6 PWMB_CCMR3 = 0x68; // 通道7 PWMB_CCMR4 = 0x68; // 通道8 // 4. 设置初始占空比(例如 1.5ms,对应 90°) PWMB_CCR5 = 1500; PWMB_CCR6 = 1500; PWMB_CCR7 = 1500; PWMB_CCR8 = 1500; // 5. 配置 GPIO 为推挽输出 // P1.7 P1M0 |= 0x80; // P1M0.7 = 1 P1M1 &= ~0x80; // P1M1.7 = 0 // P5.4 P5M0 |= 0x10; // P5M0.4 = 1 P5M1 &= ~0x10; // P5M1.4 = 0 // P3.3, P3.4 P3M0 |= 0x18; // bit3 and bit4 P3M1 &= ~0x18; // 6. 引脚功能映射 (选择第二组引脚) PWMB_PS = PWMB_PS_VALUE; // 0x55 // 7. 使能输出 PWMB_ENO = 0x55; // bit0: ENO5P, bit2: ENO6P, bit4: ENO7P, bit6: ENO8P PWMB_CCER1 = 0x11; // CC5E=1, CC6E=1 PWMB_CCER2 = 0x11; // CC7E=1, CC8E=1 // 8. 主输出使能 PWMB_BKR |= 0x80; // MOE=1 // 9. 启动计数器 PWMB_CR1 |= 0x01; // CEN=1 } void LED_Update(void) { switch (led_mode) { case UNCONNECTED: // 未连接状态,1Hz闪烁 if (sys_tick < 500) P32 = 1; else P32 = 0; break; case NRF_BINDING: // 对频状态,2Hz闪烁 if (sys_tick % 500 < 250) P32 = 1; else P32 = 0; break; case NRF_ERROR: // NRF硬件错误状态,双闪 if (sys_tick < 100) P32 = 0; /* 亮 100ms */ else if (sys_tick < 200) P32 = 1; /* 灭 100ms */ else if (sys_tick < 300) P32 = 0; /* 亮 100ms */ else P32 = 1; /* 灭 700ms */ break; case CONNECTED: // 已连接状态,常亮 P32 = 1; break; default: P32 = 0; break; } } void Servo_SetAngle(uint16 CH_Value[4]) { // 角度范围 0 ~ 180,对应脉宽 0.5ms ~ 2.5ms,即500~2500 // CH_Value范围1000~2000,值为1500时中点 PWMB_CCR5 = CH_Value[0]*2-1500; PWMB_CCR6 = CH_Value[1]*2-1500; PWMB_CCR7 = CH_Value[2]*2-1500; PWMB_CCR8 = CH_Value[3]*2-1500; } // 中断 void tm0_isr(void) __interrupt(1) { // 40MHz系统下10us中断, 用于系统滴答 static idata uint8 sys_tick_cnt = 0; /* 系统滴答 (1ms) */ if (sys_tick_cnt++ >= 100) { sys_tick_cnt = 0; if (sys_tick++ >= 1000) sys_tick = 0; // 1秒清零 } } static CONFIG IAP_Read(){ static __xdata CONFIG dat; return dat; } static void IAP_Write(uint8 addr, CONFIG dat); static void IAP_Erase(uint8 addr);