最近在学习AM32电调的2.18版本的源码,我用的硬件是AT32F421,整理了部分流程处理,内容的颗粒度是按自己的需要整理的,发出来给有需要的人参考。按自己的理解整理的,技术能力有限,可能理解有误,欢迎纠正。
注:lida2003博主是个大牛。写的无刷电调的理论和AM32相关知识点介绍的比较系统,介绍的很详细,有需要的同学可以去参考。我对电调这块是外行,只是刚入门学习。这里重点是代码理解的整理分析,他哪里写了很多原理性的知识。
https://blog.csdn.net/lida2003/category_12753961.html?spm=1001.2014.3001.5482
这是第三篇,介绍AM32过零点判断处理。
1、过零点判断方法。
AM32的过零点判断使用的是比较器输出判断,下图是6步换相单片机6个管脚和比较器的输出。
A1和A2间相位是AH->BL,也就是AB相,此时比较器切换到C相和中性点。
比较器的波形从低电平切换到高电平的点就是过零点。AM32判断过零点的方法是:读比较器输出连续ilter_level次都是符合预期的值,则认为是过零点。
其中filter_level的值是在main函数中根据平均换相时间计算的
//过零点门限filter_level计算:
if (zero_crosses < 100 && commutation_interval > 500)
{
filter_level = 12;
}
else
{
filter_level = map(average_interval, 100, 500, 3, 12);
}
if (commutation_interval < 50)
{
filter_level = 2;
}
2、无感驱动比较器中断函数ADC1_CMP_IRQHandler
void ADC1_CMP_IRQHandler(void)
{
if ((INTERVAL_TIMER->cval) > ((average_interval >> 1)))//平均换相时间一半后才会出现过零点
{
EXINT->intsts = EXTI_LINE;
interruptRoutine();
}
else //这个else代码分支感觉没意义,我把代码屏蔽了还能正常运行
{
if (getCompOutputLevel() == rising)
{
EXINT->intsts = EXTI_LINE;
}
}
}
3、无感驱动interruptRoutine函数
void interruptRoutine()
{
if (average_interval > 125)
{ //启动初期低油门转速突变是不可能的,直接忽略
if ((INTERVAL_TIMER_COUNT < 125) && (duty_cycle < 600) && (zero_crosses < 500))
{ // should be impossible, desync?exit anyway
return;
}
//main函数卡死判断保护
stuckcounter++; // stuck at 100 interrupts before the main loop happens
// again.
if (stuckcounter > 100)
{
maskPhaseInterrupts();
zero_crosses = 0;
return;
}
}
//连续读取比较器输出符合预期filter_level次则判断为过零点
for (int i = 0; i < filter_level; i++)
{
#if defined(MCU_F031) || defined(MCU_G031)
if (((current_GPIO_PORT->IDR & current_GPIO_PIN) == ! (rising)))
{
#else
if (getCompOutputLevel() == rising)
{
#endif
return;//连续读取正确次数不符合预期,不是过零点,跳过
}
}
__disable_irq();
maskPhaseInterrupts(); //关闭比较器中断
lastzctime = thiszctime;
thiszctime = INTERVAL_TIMER_COUNT; //更新本次换相时间
SET_INTERVAL_TIMER_COUNT(0); //换相时间计数器清零
SET_AND_ENABLE_COM_INT(waitTime + 1); // enable COM_TIMER interrupt 设置换相定时器等待时间并且使能定时器
__enable_irq();
}
4、启动阶段过零点判断getBemfState函数
AM32 有两种启动方式,正弦启动、同步驱动启动。正弦启动阶段不需要过零点判断,同步驱动阶段需要过零点判断,过零点判断使用getBemfState函数
void getBemfState()
{
uint8_t current_state = 0;
#if defined(MCU_F031) || defined(MCU_G031)
if (step == 1 || step == 4)
{
current_state = PHASE_C_EXTI_PORT->IDR & PHASE_C_EXTI_PIN;
}
if (step == 2 || step == 5)
{ // in phase two or 5 read from phase A Pf1
current_state = PHASE_A_EXTI_PORT->IDR & PHASE_A_EXTI_PIN;
}
if (step == 3 || step == 6)
{ // phase B pf0
current_state = PHASE_B_EXTI_PORT->IDR & PHASE_B_EXTI_PIN;
}
#else
current_state = !getCompOutputLevel(); // polarity reversed //读取比较器的值
#endif
if (rising)
{
if (current_state)
{
bemfcounter++; //比较器输出符合预期
}
else
{
bad_count++; //比较器读数不符合预期
if (bad_count > bad_count_threshold) //比较器读数不符合预期数累计大于门限值,则bemfcounter清零
{
bemfcounter = 0;
}
}
}
else
{
if (!current_state)
{
bemfcounter++;
}
else
{
bad_count++;
if (bad_count > bad_count_threshold)
{
bemfcounter = 0;
}
}
}
}
5、启动阶段过零点判断处理
启动阶段过零点和无感驱动阶段的过零点判断规则不一样,是定时50us调用一次
if (old_routine && running)//在tenKhzRoutine函数中50us定时调用
{
// send_LED_RGB(255, 0, 0);
maskPhaseInterrupts();//启动阶段不使用比较器中断判断过零点,所以需要禁用比较器中断
getBemfState(); //读取比较器输出计算bemfcounter
if (!zcfound)
{
if (rising)
{
if (bemfcounter > min_bemf_counts_up) //bemfcounter满足门限,认为是过零点
{
zcfound = 1; //找到过零点了,可以换相了
zcfoundroutine(); //换相操作
}
}
else
{
if (bemfcounter > min_bemf_counts_down)//bemfcounter满足门限,认为是过零点
{
zcfound = 1;//找到过零点了,可以换相了
zcfoundroutine(); //换相操作
}
}
}
}