AM32电调学习解读四:输入信号处理

发布于:2025-05-20 ⋅ 阅读:(12) ⋅ 点赞:(0)

        最近在学习AM32电调的2.18版本的源码,我用的硬件是AT32F421,整理了部分流程处理,内容的颗粒度是按自己的需要整理的,发出来给有需要的人参考。按自己的理解整理的,技术能力有限,可能理解有误,欢迎纠正。


注:lida2003博主是个大牛。写的无刷电调的理论和AM32相关知识点介绍的比较系统,介绍的很详细,有需要的同学可以去参考。我对电调这块是外行,只是刚入门学习。这里重点是代码理解的整理分析,他哪里写了很多原理性的知识。

https://blog.csdn.net/lida2003/category_12753961.html?spm=1001.2014.3001.5482
 

        这是第四篇,介绍AM32输入信号处理。本篇比较长,信号控制整个流程,部分内容展开的比较细,不是太重要的简写了,要了解细节最终还是要看代码。

1、输入信号的宏定义

  input signal types
 */
enum inputType {
    AUTO_IN = 0,//自动模式
    DSHOT_IN = 1,//dshot信号
    SERVO_IN = 2,//伺服信号,也就是PWM信号
    SERIAL_IN = 3,//串口信号,
    EDTARM_IN = 4,//dshot自检,arm这个单词不知道用啥中文好(英文不好),电调低油门确认后arm=1,会发低油门确认音,就像自检通过。所以我这里就称为自检。
    DRONECAN_IN = 5,//DroneCan输入
};

2、输入信号配置参数的初始化

		if (eepromBuffer.input_type < 10)
		{ //输入信号类型可以配置,可以是自适应

			switch (eepromBuffer.input_type)
			{
				case AUTO_IN://自动模式
					dshot = 0;
					servoPwm = 0;
					EDT_ARMED = 1;
					break;

				case DSHOT_IN://dshot信号
					dshot = 1;
					EDT_ARMED = 1;
					break;

				case SERVO_IN://伺服信号,也就是PWM信号
					servoPwm = 1;
					break;

				case SERIAL_IN://串口信号,目前还未实现
					break;

				case EDTARM_IN://dshot自检
					EDT_ARM_ENABLE = 1;
					EDT_ARMED = 0;
					dshot = 1;
					break;
				//1、这里没有DroneCan输入的处理,这部分后续再研究,先挂起
				//2、这里其实可以添加个ADC输入的处理,AM32的代码定义了USE_ADC_INPUT宏,就是ADC信号输入,也是常用的一种信号输入
			};
		}
		else 
		{
			dshot				= 0;
			servoPwm			= 0;
			EDT_ARMED			= 1;
		}

Esc_Config_Tool_1_82_WIN的配置工具还没支持这个参数的配置。可以使用如下工具修改参数

【免费】AM32ESC配置上位机_am32资源-CSDN文库

EDT_ARM_ENABLE   //dshot自检使能。这个配置我没想到具体啥应用场景。我验证的结果是:有dshot信号可以收到低油门确认音,但是不能启动,需要发送13   DSHOT_EXTENDED_TELEMETRY_ENABLE指令把EDT_ARMED置1后才能启动。

dshot可以参考:

DShot | Betaflight

3、定时器外设初始化

void UN_TIM_Init(void)
{
#ifdef USE_TIMER_3_CHANNEL_1
	crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
	crm_periph_clock_enable(CRM_TMR3_PERIPH_CLOCK, TRUE);
	gpio_mode_QUICK(INPUT_PIN_PORT, GPIO_MODE_MUX, GPIO_PULL_NONE, INPUT_PIN);
	gpio_pin_mux_config(INPUT_PIN_PORT, INPUT_PIN_SOURCE, GPIO_MUX_1);
#endif

#ifdef USE_TIMER_15_CHANNEL_1
	crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);//信号输入管脚GPIO时钟使能
	crm_periph_clock_enable(CRM_TMR15_PERIPH_CLOCK, TRUE);//信号输入定时器时钟初始化
	gpio_mode_QUICK(INPUT_PIN_PORT, GPIO_MODE_MUX, GPIO_PULL_NONE, INPUT_PIN);//信号输入管脚GPIO初始化
#endif

	//	RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_DMA1,ENABLE);
	crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);//信号输入DMA时钟使能
	INPUT_DMA_CHANNEL->ctrl = 0X98a;				//	PERIPHERAL HALF WORD, MEMROY WORD ,//信号输入DMA时钟初始化

	//	MEMORY INC ENABLE , TC AND ERROR INTS
	NVIC_SetPriority(IC_DMA_IRQ_NAME, 1);
	NVIC_EnableIRQ(IC_DMA_IRQ_NAME);
	IC_TIMER_REGISTER->pr = 0xFFFF;		//信号输入定时器初始化
	IC_TIMER_REGISTER->div = 16;
	IC_TIMER_REGISTER->ctrl1_bit.prben = TRUE;
	IC_TIMER_REGISTER->ctrl1_bit.tmren = TRUE;
}

初始化函数中开启了数据传输错误中断,但是中断函数中并没有相关处理,目前还未用上

4、信号输入DMA中断处理函数DMA1_Channel5_4_IRQHandler

void DMA1_Channel5_4_IRQHandler(void)
{
#ifdef USE_TIMER_15_CHANNEL_1

	if (dshot)//这段代码和后面的重复了,这个if语句删除了功能也正常
	{
		DMA1->clr			= DMA1_GL5_FLAG;
		INPUT_DMA_CHANNEL->ctrl_bit.chen = FALSE;
		transfercomplete();
		EXINT->swtrg		= EXINT_LINE_15;
		return;
	}

	//	  if (dma_flag_get(DMA1_HDT5_FLAG) == SET) {
	//		  if (servoPwm) {
	//			  IC_TIMER_REGISTER->cctrl_bit.c1p = TMR_INPUT_FALLING_EDGE;
	//			  DMA1->clr = DMA1_HDT5_FLAG;
	//		  }
	//	  }
	if (dma_flag_get(DMA1_FDT5_FLAG) == SET)
	{
		DMA1->clr			= DMA1_GL5_FLAG;
		INPUT_DMA_CHANNEL->ctrl_bit.chen = FALSE;
		transfercomplete();
		EXINT->swtrg		= EXINT_LINE_15;
	}

	if (dma_flag_get(DMA1_DTERR5_FLAG) == SET)//并没有相关处理,目前还未用上,只是清除标志位
	{
		DMA1->clr			= DMA1_GL5_FLAG;
	}

#endif

#ifdef USE_TIMER_3_CHANNEL_1

	if (dshot)
	{
		DMA1->clr			= DMA1_GL4_FLAG;
		INPUT_DMA_CHANNEL->ctrl_bit.chen = FALSE;
		transfercomplete();
		EXINT->swtrg		= EXINT_LINE_15;
		return;
	}

	if (dma_flag_get(DMA1_HDT4_FLAG) == SET)
	{
		if (servoPwm)
		{
			IC_TIMER_REGISTER->cctrl_bit.c1p = TMR_INPUT_FALLING_EDGE;
			DMA1->clr			= DMA1_HDT4_FLAG;
		}
	}

	if (dma_flag_get(DMA1_FDT4_FLAG) == SET)
	{
		DMA1->clr			= DMA1_GL4_FLAG;
		INPUT_DMA_CHANNEL->ctrl_bit.chen = FALSE;
		transfercomplete();
		EXINT->swtrg		= EXINT_LINE_15;
	}

	if (dma_flag_get(DMA1_DTERR4_FLAG) == SET)
	{
		DMA1->clr			= DMA1_GL4_FLAG;
	}

#endif
}

5、transfercomplete函数

0:初始状态 

inputSet=0 油门输入在位状态

dshot=0        是否是dshot信号

servoPwm=0 是否是PWM信号

armed=0        自检是否通过、解锁、低油门确认状态(不同叫法,同一件事)

1:检测油门在位状态,区分是dshot还是servoPwm

2:检测到是dshot信号

computeDshotDMA函数中会判断是否是双向dshot:dshot_telemetry=?

3:检测到是servoPwm信号

4:如果双向dshot周这个分支

5:低油门确认处理

低油门确认前可能触发油门校准(servoPwm信号才需要),电调一上电就是高油门会触发油门校准

6:低油门确认后双向dshot的处理

用更低优先级的中断触发dshot信息,以提升固件所支持的最高转速

7:processDshot中处理双向dshot

8:油门信息newinput应用处理

6、processDshot函数

7、setInput函数

setInput函数分为3个部分:

adjusted_input计算、 input计算duty_cycle_setpoint计算    

adjusted_input计算    

input计算    

duty_cycle_setpoint计算    

8、油门控制变量赋值关系

//servorawinput      [0,2047] 根据PWM信号输入原始值结合高油门、低油门值计算出的原始读数
//newinput         [0,2047] 根据servorawinput、tocheck(DSHOT读数)、ADC_smoothed_input读数
//adjusted_input  [0,2047] 根据newinput计算
//input_override    [0,20470000] PID调速使用
//input            [0,2047] 根据adjusted_input或者input_override计算
//stall_protection_adjust [0,20470000] 失速调速使用
//duty_cycle_setpoint [0,2000] 根据input计算
//duty_cycle            [0,2000] 根据duty_cycle_setpoint计算
//last_duty_cycle        [0,2000] last_duty_cycle = duty_cycle;
//adjusted_duty_cycle [0,tim1_arr]
//SET_DUTY_CYCLE_ALL(adjusted_duty_cycle);

9、其他函数功能简要说明

detectInput()

计算输入波形的时长信息:smallestnumber和average_signal_pulse。然后判断是dshot信号还是servo信号

checkDshot()

根据smallestnumber和average_signal_pulse判断是否是dshot并且调整输入捕获参数

dshot = 1;

inputSet  = 1;

checkServo()

根据smallestnumber判断是否是servoPwm,并且调整输入捕获预分频参数

servoPwm = 1;

inputSet  = 1;

computeDshotDMA()

解析dshot数据得到tocheck、

crc校验

dshotcommand命令处理

计算出newinput

make_dshot_package(uint16_t com_time)

构建双向dshot返回数据

computeServoInput()   

 ServoPwm计算油门值和油门校准油门校准处理.

双向旋转油门映射处理

单向旋转油门映射处理

油门平滑变化处理

最终计算出newinput

computeMSInput()        

这个函数未使用

receiveDshotDma()
设置为输入捕获模式

void sendDshotDma()

设置为输出模式


网站公告

今日签到

点亮在社区的每一天
去签到