最近在学习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可以参考:
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()
设置为输出模式