The previous post [Development and application based on NUCLEO-F746ZG motor] 6. The program framework mentioned that after the system is initialized, it enters the main loop. There is no task execution in the main function, and all tasks are in interrupts. There are mainly two interrupts: Systick interrupt and ADC interrupt. Systick interrupt executes safety tasks and medium frequency tasks, and ADC interrupt mainly executes high frequency tasks. This post will introduce in detail:
1. Systick interruption
Systick system timer is a peripheral in CM7 core, embedded in NVIC. System timer is a 24-bit down-counting counter, and the time for each count of the counter is 1/SYSCLK. When the value of the reload value register decreases to 0, the system timer generates an interrupt, and the cycle repeats. Because SysTick is a peripheral of CM7 core, all microcontrollers based on CM7 core have this system timer, so that the software can be easily ported in CM7 microcontrollers. System timer is dedicated to real-time operating system, used to generate time base and maintain the heartbeat of operating system.
In this program, the Systick interrupt defaults to a 1ms timing, in which the safety task is executed in this interrupt, and the intermediate frequency task (speed loop) is also executed in this interrupt.
Systick interrupt initialization code is as follows:
__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;
}
Interrupt function:
void SysTick_Handler(void)
{
MC_RunMotorControlTasks();
}
In the MC_RunMotorControlTasks() function, the safety task and the intermediate frequency task are executed:
__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 interrupt
ADC interrupt completes high frequency task, ADC sampling starts triggered by TIM1 hardware. FOC coordinate transformation, SVPWM execution and PWM duty cycle adjustment output of TIM1 are executed in the interrupt.
This program uses ADC1 to collect current, bus voltage, and temperature. The main process is as follows:
ADC1 initialization code is as follows:
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 interrupt function:
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 */
}
As can be seen from the program, there is only one task in ADC, the high-frequency task. Why is the high-frequency task placed here? Because ADC needs to collect the current and voltage values in a timely and accurate manner, give them to FOC for calculation, and then convert the calculated values into output waveforms through timer TIM1 to make the motor run.