上个帖子【基于NUCLEO-F746ZG电机开发应用】6.程序框架中说到系统在初始化完成后,进入到主循环中,主函数中没有任何任务执行,所有的任务都在中断中。主要在两个中断中:Systick中断和ADC中断。Systick中断执行安全任务和中频任务,ADC中断主要执行高频任务。本贴将详细介绍一下:
1.Systick中断
Systick系统定时器是属于CM7内核中的一个外设,内嵌在NVIC中。系统定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK。当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复。因为SysTick是属于CM7内核的外设,所以所有基于CM7内核的单片机都具有这个系统定时器,使得软件在CM7单片机中可以很容易的移植。系统定时器专用于实时操作系统,用于产生时基,维持操作系统的心跳。
本程序中Systick中断默认为1ms的定时,其中安全任务执行在这个中断中,中频任务(速度环)也执行在这个中断中。
Systick中断初始化代码如下:
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/* Configure the SysTick to have interrupt in 1ms time basis*/
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
{
return HAL_ERROR;
}
/* Configure the SysTick IRQ priority */
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
return HAL_ERROR;
}
/* Return function status */
return HAL_OK;
}
中断函数:
void SysTick_Handler(void)
{
MC_RunMotorControlTasks();
}
其中MC_RunMotorControlTasks()函数中,执行的是安全任务和中频任务:
__weak void MC_RunMotorControlTasks(void)
{
if ( bMCBootCompleted ) {
/* ** Medium Frequency Tasks ** */
MC_Scheduler(); //中频任务
/* Safety task is run after Medium Frequency task so that
* it can overcome actions they initiated if needed. */
TSK_SafetyTask(); //安全任务
/* ** User Interface Task ** */
UI_Scheduler(); //用户界面任务
}
}
2.ADC中断
ADC中断完成高频任务,ADC采样开始由TIM1硬件触发。在中断中执行 FOC坐标变换 , SVPWM的执行 和 TIM1的PWM占空比调整输出。
本程序中使用的是ADC1,完成对电流、母线电压、温度的采集。主要流程如下:
ADC1初始化代码如下:
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_InjectionConfTypeDef sConfigInjected = {0};
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //ADC1时钟4分频
hadc1.Init.Resolution = ADC_RESOLUTION_12B; //分辨率为12位
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; //扫描模式开启
hadc1.Init.ContinuousConvMode = DISABLE; //连续采样模式关闭
hadc1.Init.DiscontinuousConvMode = DISABLE;//非连续采样模式关闭
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //禁止触发检测,使用软件触发
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
hadc1.Init.DataAlign = ADC_DATAALIGN_LEFT; //数据左对齐
hadc1.Init.NbrOfConversion = 2; //配置位2通道
hadc1.Init.DMAContinuousRequests = DISABLE; //关闭DMA传输数据
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;//使能EOC标志位
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_0; //ADC1通道0
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_1;
sConfigInjected.InjectedNbrOfConversion = 2;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_15CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_CC4;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = ENABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
{
Error_Handler();
}
/** Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_11;
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_2;
if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = ADC_REGULAR_RANK_2;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
ADC中断函数:
void ADC_IRQHandler(void)
{
/* USER CODE BEGIN ADC_IRQn 0 */
/* USER CODE END ADC_IRQn 0 */
if ( LL_ADC_IsActiveFlag_JEOS( ADC1 ) )//检查ADC转化标志
{
LL_ADC_ClearFlag_JEOS( ADC1 );//清除标志
}
// Highfrequency task Single or M1
UI_DACUpdate(TSK_HighFrequencyTask()); //高频任务
/* USER CODE BEGIN ADC_IRQn 1 */
/* USER CODE END ADC_IRQn 1 */
}
从程序中可以看出,ADC中就只有一个任务,高频任务。为什么要把高频任务放在这里,是因为ADC要及时准确把电流电压的值采集完成后,给到FOC进行计算,然后将计算的值通过定时器TIM1转化为输出波形,使电机运行起来。