Files
ZhaTianRC/rx/main.c
T
2026-06-28 23:50:53 +08:00

244 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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"
void LoadConfig();
void SystemInit();
void PWMBInit();
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; // 使能访问扩展SFRXFR
// 选择内部高速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;
}
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,即5002500
// CH_Value范围10002000,值为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秒清零
}
}