244 lines
6.9 KiB
C
244 lines
6.9 KiB
C
/**
|
||
* 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; // 使能访问扩展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;
|
||
}
|
||
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秒清零
|
||
}
|
||
}
|