萝莉Loli双向有刷电调源代码 SDCC 版本

在git上找到这一份代码,感谢原作者的分享。原来应该是使用keil51编译的,我是macos, 没法安装,于是改了一下,让它可以使用免费的sdcc编译。 修改的过程也进行了阅读理解和注释,加上了makefile小工具。

把原来的keil的语法改成了sdcc的写法,可以使用sdcc编译。

## 编译方法

1. 安装sdcc

2. 安装make和srecord工具

3. 运行make命令

4. 如果不安装make 工具, 也可以使用命令行编译:

sdcc LoliDualBrusedBiEsc.c -o LoliDualBrusedBiEsc.ihx

使用的mcu是STC15 系列的应该都可以, 只有需要6个IO就可以了, 两个IO输出,4个IO输出.

如STC15W104系列, 使用sdcc编译出来大约是1.3K , 选用2K flash的型号应该就可以了, 使用dip8/dfn8 最少的脚的都行。

源代码比较小,直接贴出来,大家copy下来保存即可, 用到的朋友,请点个赞吧。

github的链接:GitHub - aerror2/LoliDualBrushedBiEsc: Loli Dual Brushed Motor bidirectional ESC firmware

#include <8052.h>

#define u8 unsigned char
#define u16 unsigned int
#define NOP() __asm NOP __endasm

//----------------------------------------------------------
//STC15寄存器声明

// 将这些行:
// sfr P3M1=0xb1;
// sfr P3M0=0xb2;
// sfr AUXR=0x8e;
// sfr T2H=0xd6;
// sfr T2L=0xd7;
// sfr IE2=0xaf;

//----------------------------------------------------------
//引脚定义
// sbit A1=P3^0;
// sbit A2=P3^1;
// sbit B1=P3^2;
// sbit B2=P3^3;
// sbit IN1=P3^5;
// sbit IN2=P3^4;


// 改为:
__sfr __at(0xB1) P3M1;    // P3M1 寄存器
__sfr __at(0xB2) P3M0;    // P3M0 寄存器
__sfr __at(0x8E) AUXR;    // AUXR 寄存器
__sfr __at(0xD6) T2H;     // Timer2 高字节
__sfr __at(0xD7) T2L;     // Timer2 低字节
__sfr __at(0xAF) IE2;     // 中断使能寄存器2



// 改为:
__sbit __at(0xB0) P30;  // P3.0  
__sbit __at(0xB1) P31;  // P3.1
__sbit __at(0xB2) P32;  // P3.2
__sbit __at(0xB3) P33;  // P3.3
__sbit __at(0xB5) P35;  // P3.5
__sbit __at(0xB4) P34;  // P3.4

#define A1 P30	//电机信号输出A
#define A2 P31  //电机信号输出A
#define B1 P32 //电机信号输出B
#define B2 P33  //电机信号输出B

#define IN1 P35   //pwm 输入1
#define IN2 P34	//pwm 输入2



//----------------------------------------------------------
//软件PWM

u8 PWM_count;
u8 PWM_duty_A;
u8 PWM_duty_B;

u8 PWM_value;

//----------------------------------------------------------
//控制

u16 timer1,timer2;

u16 pulse;

u16 IN1_H_time,IN2_H_time;
// 将这些行:
// bit IN1_last,IN2_last;
// bit get_pulse1,get_pulse2;
// bit get_new;
// bit Direction_A,Direction_B;

// 改为:
unsigned char status_bits;
#define IN1_last    (status_bits & 0x01)    // 位0
#define IN2_last    (status_bits & 0x02)    // 位1
#define get_pulse1  (status_bits & 0x04)    // 位2
#define get_pulse2  (status_bits & 0x08)    // 位3
#define get_new     (status_bits & 0x10)    // 位4
#define Direction_A (status_bits & 0x20)    // 位5
#define Direction_B (status_bits & 0x40)    // 位6

// 设置位的宏
#define SET_IN1_last()    (status_bits |= 0x01)
#define SET_IN2_last()    (status_bits |= 0x02)
#define SET_get_pulse1()  (status_bits |= 0x04)
#define SET_get_pulse2()  (status_bits |= 0x08)
#define SET_get_new()     (status_bits |= 0x10)
#define SET_Direction_A() (status_bits |= 0x20)
#define SET_Direction_B() (status_bits |= 0x40)

// 清除位的宏
#define CLR_IN1_last()    (status_bits &= ~0x01)
#define CLR_IN2_last()    (status_bits &= ~0x02)
#define CLR_get_pulse1()  (status_bits &= ~0x04)
#define CLR_get_pulse2()  (status_bits &= ~0x08)
#define CLR_get_new()     (status_bits &= ~0x10)
#define CLR_Direction_A() (status_bits &= ~0x20)
#define CLR_Direction_B() (status_bits &= ~0x40)

int mix_x,mix_y;

//bit Direction_A,Direction_B;


int pulse1=150,pulse2=150;
u8 lose_A=20,lose_B=20;  //更新:上电默认处于失控状态,防止第一次信号检测错误


//----------------------------------------------------------
//混控设置
#define  mix_en 0  //关闭混控,两路独立
//bit mix_en=1;  //开启混控






void delay_ms(u16 ms)		//STC15 @12.000MHz
{
	u16 i;

	do{
	    i = 12000000 / 13022;
		
		NOP();NOP();NOP();
		
		while(--i)	;   
     }while(--ms);	
}

void delay_us(u8 us)		//@12.000MHz
{

	while(--us)
	{
		NOP();NOP();
	}
}

void shock( u8 n)    //震动发声
{
	u8 i;
	for(i=0;i<250;i++)
	{
		A1=0,A2=0;
		B1=0,B2=0;		
		delay_us(n);
		
		A1=1;B1=1;
		delay_us(n);
		
		A1=0;B1=0;		
		delay_us(n);
				
		A2=1;B2=1;
		delay_us(n);	
	}
	A1=0,A2=0;
	B1=0,B2=0;
	delay_us(n);
}


void main()
{
	
 	P3=0xF0;	      //上电关闭输出
 
	P3M0=0x0F;      //IO推挽输出
	
	delay_ms(400);
	
	shock(200);	//新增 上电音乐,表明正常工作
	shock(150);
	shock(100);
	
	
	IE=0x80;
	IE2=0x04;
	T2L=0xF6;T2H=0xFF;  //开启软件PWM
	AUXR=0x10;
// 定时器初值:
// T2H = 0xFF, T2L = 0xF6
// 16位初值 = 0xFFF6 = 65526
// 计数次数 = 65536 - 65526 = 10
// 在12T模式下的中断周期:
// 系统时钟:12MHz
// 在12T模式下,定时器时钟 = 12MHz ÷ 12 = 1MHz
// 中断周期 = 10 × (1/1MHz) = 10μs
// AUXR = 0x10 = 0001 0000 (二进制)
// 第3位(T2x12) = 0  12分频
// 第4位(T2C/T) = 0   ,T2C/T=0,内部时钟
// 第5位(T2R)=1 , 允许T2 运行

	
	while(1)
	{
		
		if(get_pulse1)
		{
			CLR_get_pulse1();
			
			pulse =IN1_H_time;			
			IN1_H_time=0;
			
			if(pulse >85 && pulse <215) //只受理合理舵量范围
			{
				timer1=0;
				
				if(pulse <105)pulse =105;
				if(pulse >195)pulse =195; 
				
				if(lose_A)lose_A--;  //丢信号重连保护
				else { SET_get_new(); pulse1=pulse; }
				
			}
			
		}
		
		if(get_pulse2)
		{
			CLR_get_pulse2();
			
			pulse =IN2_H_time;			
			IN2_H_time=0;
			
			if(pulse >85 && pulse <215)
			{
				timer2=0;
				
				if(pulse <105)pulse =105;
				if(pulse >195)pulse =195;		//舵量限幅 1050~1950	

				if(lose_B)lose_B--;  //丢信号重连保护
				else { SET_get_new(); pulse2=pulse; }
			}			
			
		}
		
		
		if(get_new)
		{
			CLR_get_new();
			
			if(mix_en)  //引脚高电平 使能混控
			{
				if(pulse2>152 || pulse2<148) //通道2为差向输入,中位死区
				{
					mix_x=pulse1 + pulse2 -150;  
					mix_y=pulse1 - pulse2 +150;	
				}
				else
				{
					mix_x=pulse1;   //通道1为同向输入,
					mix_y=pulse1;
				}
							
			}
			else        //引脚低电平 两通道独立
			{
				mix_x=pulse1;
				mix_y=pulse2;				
			}
			
			
			if(mix_x<105)mix_x=105;
			if(mix_x>195)mix_x=195;
			
			if(mix_y<105)mix_y=105;
			if(mix_y>195)mix_y=195;
			
// 理想电机特性:
// PWM占空比    转速
// 0%    →     0 RPM
// 20%   →     20% 最大转速  
// 50%   →     50% 最大转速
// 100%  →     100% 最大转速			
// 实际电机特性:
// PWM占空比    转速
// 0%    →     0 RPM
// 20%   →     0 RPM (静摩擦力未克服)
// 30%   →     5% 最大转速 (刚好克服静摩擦)
// 50%   →     45% 最大转速
// 100%  →     100% 最大转速
//
			//===========================================================
			// A路电机控制量计算 - 摩擦力补偿算法
			// mix_x取值范围:105-195(对应1050-1950μs)
			// 核心思想:通过非线性映射克服电机静摩擦力,实现平滑可控
			//===========================================================
			if(mix_x>=153 )       // A路正转区间:>=1530μs
			{
				//=======================================================
				// 正转摩擦力补偿公式:(mix_x-151)*100/44
				//=======================================================
				// 关键参数解析:
				// • mix_x-151:起始点偏移补偿
				//   - 151而非150(中位):提供1单位预加载
				//   - 当mix_x=153时:153-151=2 → 最小输出≠0
				//   - 避免在静摩擦阈值附近震荡
				//
				// • 除以44而非45:量程优化补偿  
				//   - 理论量程:195-150=45
				//   - 实际量程:195-151=44
				//   - 效果:将更多分辨率分配给低速区间
				//
				// • 乘以100:转换为百分比PWM占空比
				//
				// 补偿效果对比:
				// 遥控量  |  线性映射  |  补偿映射  |  实际转速响应
				// 153     |    6.7%    |   4.5%    |  立即缓慢启动
				// 160     |   22.2%    |  20.5%    |  平滑低速运行  
				// 180     |   66.7%    |  65.9%    |  稳定中速运行
				// 195     |   100%     |   100%    |  最高速运行
				PWM_duty_A= (mix_x-151)*100/44; // 补偿摩擦力的非线性映射
				CLR_Direction_A();        // 设置A路正转方向(A2输出PWM,A1保持低)
			}
			else if(mix_x<=147 )  // A路反转区间:<=1470μs
			{
				//=======================================================
				// 反转摩擦力补偿公式:(149-mix_x)*100/44
				//=======================================================
				// 对称性设计原理:
				// • 149-mix_x:反向起始点偏移
				//   - 149而非150:提供反向1单位预加载
				//   - 当mix_x=147时:149-147=2 → 最小反向输出≠0
				//   - 与正转形成完美对称的控制特性
				//
				// • 反向量程:149-105=44(与正转量程相同)
				//   - 保证正反转响应的一致性
				//   - 用户操作感受完全对称
				//
				// 反转补偿效果:
				// 遥控量  |  反转深度  |  PWM占空比  |  实际转速响应
				// 147     |     2      |    4.5%     |  立即缓慢反转
				// 140     |     9      |   20.5%     |  平滑低速反转
				// 120     |    29      |   65.9%     |  稳定中速反转  
				// 105     |    44      |    100%     |  最高速反转
				PWM_duty_A= (149-mix_x)*100/44; // 反转摩擦力补偿
				SET_Direction_A();        // 设置A路反转方向(A1输出PWM,A2保持低)
			}
			else                  // A路停止死区:1470-1530μs(60μs宽度)
			{
				//=======================================================
				// 死区设计的工程意义:
				//=======================================================
				// • 防止误触发:遥控杆中位附近的微小抖动不会导致电机转动
				// • 明确停止:提供清晰的"停止"状态,用户操作更确定
				// • 保护电机:避免正反转快速切换导致的电流冲击
				// • 节能设计:中位时完全关闭PWM输出
				PWM_duty_A=0;        // 完全停止输出,电机进入制动状态
			}
			
			//===========================================================
			// B路电机控制量计算(基于mix_y的值)
			// mix_y取值范围:105-195(对应1050-1950μs)
			//===========================================================
			if(mix_y>=153 )       // B路正转判断:>=1530μs(中位150+死区3)
			{
				// 正转PWM占空比计算:
				// (mix_y-151)*100/44 
				// 当mix_y=153时:(153-151)*100/44 = 4.5%
				// 当mix_y=195时:(195-151)*100/44 = 100%
				// 151是正转起始点,44是满量程范围(195-151)
				PWM_duty_B= (mix_y-151)*100/44; // 补偿摩擦力的非线性映射
				CLR_Direction_B();        // 设置B路正转方向(B2输出PWM)
			}
			else if(mix_y<=147 )  // B路反转判断:<=1470μs(中位150-死区3)
			{
				// 反转PWM占空比计算:
				// (149-mix_y)*100/44
				// 当mix_y=147时:(149-147)*100/44 = 4.5%  
				// 当mix_y=105时:(149-105)*100/44 = 100%
				// 149是反转起始点,44是满量程范围(149-105)
				PWM_duty_B= (149-mix_y)*100/44; // 反转时占空比递增
				SET_Direction_B();        // 设置B路反转方向(B1输出PWM)
			}
			else                  // B路停止区间:1470-1530μs(60μs死区)
			{
				PWM_duty_B=0;        // 停止输出,电机制动
			}
			
		}
		
		
	}
	
}

//10us 触发一次调用
//IN1_H_time 取值 0-255, 即0-2550us
//IN2_H_time 取值 0-255, 即0-2550us
//PWM_count 是0-100, 即每1000us 重置一次
//PWM_duty_A 和PWM_duty_B, 取值都是0-100,当PWM_count 小于PWM_duty_A/B时,管脚高电平,否则低
//软件PWM 1Khz
void T2_isr() __interrupt(12)
{

	
	if(IN1)					//通道1脉宽检测
	{
		SET_IN1_last();
		IN1_H_time++;
	}
	else 
	{
		if(IN1_last)  SET_get_pulse1();
		CLR_IN1_last();
	}
	
	if(IN2)					//通道2脉宽检测
	{
		SET_IN2_last();
		IN2_H_time++;
	}
	else 
	{
		if(IN2_last) SET_get_pulse2();
		CLR_IN2_last();
	}	
	
	
	PWM_count++;
	if(PWM_count==100) //100级分辨率
	{
		PWM_count=0;
		
		timer1++;
		if(timer1>500)//0.5S无信号保护
		{
			timer1=0; lose_A=30;
			
			pulse1=150; SET_get_new();		
		}
		
		timer2++;
		if(timer2>500)//0.5S无信号保护
		{
			timer2=0; lose_B=30;
			
			pulse2=150; SET_get_new();		
		}
	}
	
	
	//=======================================================================
	// PWM输出控制部分 - 每10μs执行一次(中断频率100kHz)
	// PWM周期:1ms(PWM_count从0计数到100)
	// PWM分辨率:100级(占空比精度1%)
	//=======================================================================
	
	// A路电机H桥PWM控制
	if(PWM_duty_A > PWM_count)    // PWM有效期间(高电平期间)
	{
		if(Direction_A)           // 方向控制:1=反转,0=正转
			A1=1;                 // 反转:A1高,A2保持低(由else分支控制)
		else 
			A2=1;                 // 正转:A2高,A1保持低(由else分支控制)
	}
	else A1=0,A2=0;              // PWM无效期间:A1、A2都为低电平(电机停止/刹车)
	
	// B路电机H桥PWM控制(逻辑与A路相同)
	if(PWM_duty_B > PWM_count)    // PWM有效期间(高电平期间)
	{
		if(Direction_B)           // 方向控制:1=反转,0=正转  
			B1=1;                 // 反转:B1高,B2保持低
		else 
			B2=1;                 // 正转:B2高,B1保持低
	}
	else B1=0,B2=0;              // PWM无效期间:B1、B2都为低电平(电机停止/刹车)

	
}

Makefile

CC = sdcc
stctool = stcgal
protocol = stc15
upload_port = /dev/tty.wchusbserial*

all: build

build:
	@mkdir -p build
	$(CC) LoliDualBrusedBiEsc.c -o build/LoliDualBrusedBiEsc.ihx
upload:
	$(stctool) -P $(protocol) -p $(upload_port) LoliDualBrusedBiEsc.ihx

clean:
	rm -f *.asm *.lst *.map *.mem *.rel *.rst *.sym *.lk *.hex *.ihx
	rm -rf build

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值