matlab_stm_emulate/Code/PWM/pwm.c
alexey b82faa62fe Симуляция генерации синусоидального шим и управление по модбас
note:
- модбас не моделируется,  в s-function просто передаются константы режимов.
- лишние файлы убраны в outdate.
- два канала одной фазы переключаются немного криво: на один такт симуляции проскакивает высокий уровень предыдущего канала и только потом включается текущий канал
2024-08-21 13:06:16 +03:00

856 lines
28 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "pwm.h"
//#include "rng.h"
PWM_HandleTypeDef hpwm1;
PWM_SlaveHandleTypeDef hpwm2;
PWM_SlaveHandleTypeDef hpwm3;
uint32_t sin_table[SIN_TABLE_SIZE_MAX];
unsigned ActiveChannelSHDW_Master;
float DeadTimeCnt_Master;
unsigned ActiveChannelSHDW_Slave2;
float DeadTimeCnt_Slave2;
unsigned ActiveChannelSHDW_Slave3;
float DeadTimeCnt_Slave3;
/**
* @brief First set up of PWM.
* @note Первый инит ШИМ. Заполняет структуры и инициализирует таймер для генерации синуоидального ШИМ.
* Скважность ШИМ меняется по закону синусоиды, каждый канал генерирует свой полупериод синуса (от -1 до 0 И от 0 до 1)
* ШИМ генерируется на одном канале.
* @note This called from main
*/
void PWM_Sine_FirstInit(void)
{
hpwm1.pDuty_Table_Origin = SIN_TABLE_ORIGIN;
//---------PWM TIMER1 INIT------------
// channels settings
hpwm1.sConfigOC.OCMode = TIM_OCMODE_PWM1;
hpwm1.sConfigOC.Pulse = 0;
hpwm1.sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
hpwm1.sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
// tim1 settings
hpwm1.stim.htim.Instance = TIMER_PWM1_INSTANCE;
hpwm1.stim.sTimMode = TIM_IT_MODE;
hpwm1.stim.sTimFreqHz = HZ_TIMER_PWM;
hpwm1.stim.sTickBaseMHz = PROJSET.TIM_PWM_TICKBASE;
hpwm1.stim.sTimAHBFreqMHz = PROJSET.TIM_PWM_AHB_FREQ;
hpwm1.GPIOx = TIMER_PWM1_GPIOx;
hpwm1.GPIO_PIN_X1 = PROJSET.TIM_PWM1_GPIO_PIN_X1;
hpwm1.GPIO_PIN_X2 = PROJSET.TIM_PWM1_GPIO_PIN_X2;
hpwm1.PWM_Channel1 = PROJSET.TIM_PWM1_TIM_CHANNEL1;
hpwm1.PWM_Channel2 = PROJSET.TIM_PWM1_TIM_CHANNEL2;
hpwm1.hpwm2 = (void *)&hpwm2;
hpwm1.hpwm3 = (void *)&hpwm3;
TIM_Base_Init(&hpwm1.stim);
TIM_Output_PWM_Init(&hpwm1.stim.htim, &hpwm1.sConfigOC, hpwm1.PWM_Channel1, hpwm1.GPIOx, hpwm1.GPIO_PIN_X1);
TIM_Output_PWM_Init(&hpwm1.stim.htim, &hpwm1.sConfigOC, hpwm1.PWM_Channel2, hpwm1.GPIOx, hpwm1.GPIO_PIN_X2);
// PWM SLAVES INIT
hpwm2.hMasterPWM = &hpwm1;
hpwm2.stim = hpwm1.stim;
hpwm2.stim.htim.Instance = (TIM_TypeDef *)PROJSET.TIM_PWM2_INSTANCE;
hpwm2.GPIOx = (GPIO_TypeDef *)PROJSET.TIM_PWM2_GPIOx;
hpwm2.GPIO_PIN_X1 = PROJSET.TIM_PWM2_GPIO_PIN_X1;
hpwm2.GPIO_PIN_X2 = PROJSET.TIM_PWM2_GPIO_PIN_X2;
hpwm2.PWM_Channel1 = PROJSET.TIM_PWM2_TIM_CHANNEL1;
hpwm2.PWM_Channel2 = PROJSET.TIM_PWM2_TIM_CHANNEL2;
hpwm2.Duty_Shift_Ratio = (float)2/3;
hpwm3.hMasterPWM = &hpwm1;
hpwm3.stim = hpwm1.stim;
hpwm3.stim.htim.Instance = (TIM_TypeDef *)PROJSET.TIM_PWM3_INSTANCE;
hpwm3.GPIOx = (GPIO_TypeDef *)PROJSET.TIM_PWM3_GPIOx;
hpwm3.GPIO_PIN_X1 = PROJSET.TIM_PWM3_GPIO_PIN_X1;
hpwm3.GPIO_PIN_X2 = PROJSET.TIM_PWM3_GPIO_PIN_X2;
hpwm3.PWM_Channel1 = PROJSET.TIM_PWM3_TIM_CHANNEL1;
hpwm3.PWM_Channel2 = PROJSET.TIM_PWM3_TIM_CHANNEL2;
hpwm3.Duty_Shift_Ratio = (float)-2/3;
PWM_SlavePhase_Init(&hpwm2);
PWM_SlavePhase_Init(&hpwm3);
//----------TIMERS START-------------
HAL_TIM_Base_Start_IT(&hpwm1.stim.htim); // timer for PWM
HAL_TIM_PWM_Start(&hpwm1.stim.htim, hpwm1.PWM_Channel1); // PWM channel 1
HAL_TIM_PWM_Start(&hpwm1.stim.htim, hpwm1.PWM_Channel2); // PWM channel 2
}
/**
* @brief PWM Handler.
* @param hpwm - указатель на хендл ШИМ.
* @note Управляет скважностью ШИМ.
* @note This called from TIM_PWM_Handler
*/
void PWM_Handler(PWM_HandleTypeDef *hpwm)
{
//------------SINUS MODE-------------
if(PWM_Get_Mode(&hpwm1,PWM_DC_MODE) == 0)
{
if(hpwm->PWM_Value != 0) // if there some frequency
{
unsigned sin_ind = PWM_Get_Duty_Table_Ind(hpwm, hpwm->stim.sTimFreqHz);
// overflow check
if(sin_ind >= hpwm->Duty_Table_Size)
sin_ind -= hpwm->Duty_Table_Size;
if(sin_ind >= hpwm->Duty_Table_Size) // if its still overflow reset it
sin_ind = 0;
// if unsigned sine enabled
if(PWM_Get_Mode(hpwm, PWM_CH_MODE) == 0)
{
// set pwm duty
PWM_Set_Duty_From_Table(hpwm, sin_ind); // set first channel
PWM_SlavePhase_Set_DutyTable_Unsigned(PWM_Set_pSlaveHandle(hpwm,hpwm2), sin_ind);
PWM_SlavePhase_Set_DutyTable_Unsigned(PWM_Set_pSlaveHandle(hpwm,hpwm3), sin_ind);
}
// if signed sine enabled
else
{
int Duty = PWM_Get_Table_Element_Signed(hpwm, sin_ind);
if(Duty >= 0)
{
PWM_Set_Compare1(hpwm, Duty); // set first channel
PWM_Set_Compare2(hpwm, 0); // reset second channel
}
else // если это вторая полуволна
{
PWM_Set_Compare1(hpwm, 0); // reset first channel
PWM_Set_Compare2(hpwm, -Duty); // set second channel
}
PWM_SlavePhase_Set_DutyTable_Signed(PWM_Set_pSlaveHandle(hpwm,hpwm2), sin_ind);
PWM_SlavePhase_Set_DutyTable_Signed(PWM_Set_pSlaveHandle(hpwm,hpwm3), sin_ind);
}
}
else // if freq = 0 reset all channels
{
PWM_Set_Compare1(hpwm, 0); // reset first channel
PWM_Set_Compare2(hpwm, 0); // reset second channel
PWM_Set_Compare1(PWM_Set_pSlaveHandle(hpwm,hpwm2), 0); // reset first channel
PWM_Set_Compare2(PWM_Set_pSlaveHandle(hpwm,hpwm2), 0); // reset second channel
PWM_Set_Compare1(PWM_Set_pSlaveHandle(hpwm,hpwm3), 0); // reset first channel
PWM_Set_Compare2(PWM_Set_pSlaveHandle(hpwm,hpwm3), 0); // reset second channel
}
}
//-----------PWM DC MODE-------------
else
{
PWM_Set_Compare1(PWM_Set_pSlaveHandle(hpwm,hpwm2), 0); // reset first channel
PWM_Set_Compare2(PWM_Set_pSlaveHandle(hpwm,hpwm2), 0); // reset second channel
PWM_Set_Compare1(PWM_Set_pSlaveHandle(hpwm,hpwm3), 0); // reset first channel
PWM_Set_Compare2(PWM_Set_pSlaveHandle(hpwm,hpwm3), 0); // reset second channel
// uint32_t pwm_rng = 0;
// HAL_RNG_GenerateRandomNumber(&hrng, &pwm_rng);
// pwm_rng = ((pwm_rng&0xFFFF)/(0xFFFF/PWM_Get_Autoreload(hpwm)))/((float)100/hpwm->PWM_Value);
// if (pwm_rng < PWM_Calc_Min_Duty(hpwm))
// pwm_rng = PWM_Calc_Min_Duty(hpwm);
// if second channel enabled
if(PWM_Get_Mode(hpwm, PWM_CH_MODE))
{
PWM_Set_Compare1(hpwm, 0); // reset first channel
PWM_Set_Duty_From_Percent(hpwm, hpwm->PWM_Channel2); // set second channel
// __HAL_TIM_SET_COMPARE(&(hpwm->stim.htim), TIM_CHANNEL_2, pwm_rng); // set second channel
}
// if first channel enabled
else
{
// __HAL_TIM_SET_COMPARE(&(hpwm->stim.htim), TIM_CHANNEL_1, pwm_rng); // set second channel
PWM_Set_Duty_From_Percent(hpwm, hpwm->PWM_Channel1); // set first channel
PWM_Set_Compare2(hpwm, 0); // reset second channel
}
}
//-----CHECK CHANNELS FOR ERRORS-----
uint16_t min_duty = PWM_Calc_Min_Duty(hpwm);
// IF FIRST CHANNEL IS ACRIVE
if(PWM_Get_Compare1(hpwm) != 0)
{
// Duty shoud be bigger or equeal than min duration
if (PWM_Get_Compare1(hpwm)<min_duty)
PWM_Set_Compare1(hpwm, min_duty);
// Duty shoud be less or equeal than ARR-min duration
if (PWM_Get_Compare1(hpwm)>PWM_Get_Autoreload(hpwm)-min_duty)
PWM_Set_Compare1(hpwm, PWM_Get_Autoreload(hpwm)-min_duty);
}
// IF SECOND CHANNEL IS ACRIVE
else if(PWM_Get_Compare2(hpwm) != 0)
{
// Duty shoud be bigger or equeal than min duration
if (PWM_Get_Compare2(hpwm)<min_duty)
PWM_Set_Compare2(hpwm, min_duty);
// Duty shoud be less or equeal than ARR
if (PWM_Get_Compare2(hpwm)>PWM_Get_Autoreload(hpwm)-min_duty)
PWM_Set_Compare2(hpwm, PWM_Get_Autoreload(hpwm)-min_duty);
}
// IF BOTH CHANNEL IS ACRIVE
if((PWM_Get_Compare1(hpwm) != 0) && (PWM_Get_Compare2(hpwm) != 0))
{
// Only one channel shoud be active so disable all
PWM_Set_Compare1(hpwm, 0);
PWM_Set_Compare2(hpwm, 0);
}
PWM_SlavePhase_Check_Channels(PWM_Set_pSlaveHandle(hpwm,hpwm2));
PWM_SlavePhase_Check_Channels(PWM_Set_pSlaveHandle(hpwm,hpwm3));
if(hpwm->PWM_DeadTime)
{
PWM_CreateDeadTime(hpwm, &DeadTimeCnt_Master, &ActiveChannelSHDW_Master);
PWM_SlavePhase_CreateDeadTime(PWM_Set_pSlaveHandle(hpwm,hpwm2), &DeadTimeCnt_Slave2, &ActiveChannelSHDW_Slave2);
PWM_SlavePhase_CreateDeadTime(PWM_Set_pSlaveHandle(hpwm,hpwm3), &DeadTimeCnt_Slave3, &ActiveChannelSHDW_Slave3);
}
}
/**
* @brief Update PWM parameters.
* @note Проверка надо ли обновлять параметры ШИМ, и если надо - обновляет их.
* @note This called from TIM_CTRL_Handler
*/
void Update_Params_For_PWM(PWM_HandleTypeDef *hpwm)
{
unsigned UpdateModeParams = 0;
unsigned UpdateLog = 0;
// READ PWM_DC_MODE
if(PWM_Get_Mode(hpwm, PWM_DC_MODE) != (MB_Read_Coil_Local(&coils_regs[0], COIL_PWM_DC_MODE) << PWM_DC_MODE_Pos))
{
if(MB_Read_Coil_Local(&coils_regs[0], COIL_PWM_DC_MODE))
{
hpwm->sPWM_Mode |= PWM_DC_MODE;
}
else
{
hpwm->sPWM_Mode &= ~PWM_DC_MODE;
}
// update mode params
UpdateModeParams = 1;
// update logs params
UpdateLog = 1;
}
// READ PWM_CH_MODE
if(PWM_Get_Mode(hpwm, PWM_CH_MODE) != (MB_Read_Coil_Local(&coils_regs[0], COIL_PWM_CH_MODE) << PWM_CH_MODE_Pos))
{
if(MB_Read_Coil_Local(&coils_regs[0], COIL_PWM_CH_MODE))
{
hpwm->sPWM_Mode |= PWM_CH_MODE;
}
else
{
hpwm->sPWM_Mode &= ~PWM_CH_MODE;
}
// update mode params
UpdateModeParams = 1;
// update logs params
UpdateLog = 1;
}
// READ PWM_CH_MODE
if(PWM_Get_Mode(hpwm, PWM_PHASE_MODE) != (MB_Read_Coil_Local(&coils_regs[0], COIL_PWM_PHASE_MODE) << PWM_PHASE_MODE_Pos))
{
if(MB_Read_Coil_Local(&coils_regs[0], COIL_PWM_PHASE_MODE))
{
hpwm->sPWM_Mode |= PWM_PHASE_MODE;
}
else
{
hpwm->sPWM_Mode &= ~PWM_PHASE_MODE;
}
// update mode params
UpdateModeParams = 1;
// update logs params
UpdateLog = 1;
}
// READ PWM_VALUE
if(hpwm->PWM_Value != int_to_percent(pwm_ctrl[R_PWM_CTRL_PWM_VALUE]))
{
hpwm->PWM_Value = int_to_percent(pwm_ctrl[R_PWM_CTRL_PWM_VALUE]);
// update logs params
UpdateLog = 1;
}
// READ TABLE_SIZE
if(hpwm->Duty_Table_Size != pwm_ctrl[R_PWM_CTRL_SIN_TABLE_SIZE])
{
hpwm->Duty_Table_Size = PWM_Fill_Sine_Table(&hpwm1, pwm_ctrl[R_PWM_CTRL_SIN_TABLE_SIZE]);
pwm_ctrl[R_PWM_CTRL_SIN_TABLE_SIZE] = hpwm->Duty_Table_Size;
}
// READ MIN PULSE DURATION
if(hpwm->PWM_MinPulseDur != pwm_ctrl[R_PWM_CTRL_MIN_PULSE_DUR])
{
hpwm->PWM_MinPulseDur = pwm_ctrl[R_PWM_CTRL_MIN_PULSE_DUR];
// update mode params
UpdateModeParams = 1;
// update logs params
UpdateLog = 1;
}
// READ DEAD TIME
if(hpwm->PWM_DeadTime != pwm_ctrl[R_PWM_CTRL_DEAD_TIME])
{
hpwm->PWM_DeadTime = pwm_ctrl[R_PWM_CTRL_DEAD_TIME];
}
// UPDATE PWM PARAMS
if(UpdateModeParams)
{
// UPDATE DUTY TABLE SCALE
PWM_Update_DutyTableScale(hpwm);
// update logs params
UpdateLog = 1;
}
// UPDATE LOG PARAMS
if(UpdateLog)
{
// set logs params
Set_Log_Params();
}
}
/**
* @brief reInitialization of PWM TIM.
* @param hpwm - указатель на хендл ШИМ.
* @note Перенастраивает таймер согласно принятным настройкам в pwm_ctrl
* ШИМ генерируется на одном канале.
*/
void PWM_Sine_ReInit(PWM_HandleTypeDef *hpwm)
{
Trace_PWM_reInit_Enter();
TIM_Base_MspDeInit(&hpwm->stim.htim);
hpwm1.stim.sTickBaseMHz = TIMER_PWM_TICKBASE;
TIM_Base_Init(&hpwm->stim);
TIM_Output_PWM_Init(&hpwm->stim.htim, &hpwm->sConfigOC, hpwm->PWM_Channel1, hpwm->GPIOx, hpwm->GPIO_PIN_X1);
TIM_Output_PWM_Init(&hpwm->stim.htim, &hpwm->sConfigOC, hpwm->PWM_Channel2, hpwm->GPIOx, hpwm->GPIO_PIN_X2);
PWM_Update_DutyTableScale(hpwm);
//----------TIMERS START-------------
HAL_TIM_Base_Start_IT(&hpwm1.stim.htim); // timer for PWM
HAL_TIM_PWM_Start(&hpwm1.stim.htim, hpwm->PWM_Channel1); // PWM channel 1
HAL_TIM_PWM_Start(&hpwm1.stim.htim, hpwm->PWM_Channel2); // PWM channel 2
Trace_PWM_reInit_Exit();
}
/**
* @brief Getting ind for Duty Table.
* @param hpwm - указатель на хендл ШИМ.
* @param FreqTIM - частота таймера ШИМ.
* @note Рассчитывает индекс для таблицы скважностей.
* PWM_Value в hpwm - частота с которой эта таблица должна выводиться на ШИМ
* @note This called from TIM_PWM_Handler
*/
uint32_t PWM_Get_Duty_Table_Ind(PWM_HandleTypeDef *hpwm, float FreqTIM)
{
float sine_ind_step;
uint32_t sine_ind;
// calc ind for sin table
sine_ind_step = hpwm->Duty_Table_Size/(FreqTIM/hpwm->PWM_Value);
hpwm->Duty_Table_Ind += sine_ind_step;
if(hpwm->Duty_Table_Ind >= hpwm->Duty_Table_Size)
hpwm->Duty_Table_Ind -= hpwm->Duty_Table_Size;
// if its too big (e.g. inf)
if(hpwm->Duty_Table_Ind >= 0xFFFF)
hpwm->Duty_Table_Ind = 0;
return hpwm->Duty_Table_Ind;
}
/**
* @brief Create Dead Time when switches channels.
* @param hpwm - указатель на хендл ШИМ.
*/
void PWM_CreateDeadTime(PWM_HandleTypeDef *hpwm, float *LocalDeadTimeCnt, unsigned *LocalActiveChannel)
{
// get current active channel
hpwm->fActiveChannel = (PWM_Get_Compare2(hpwm) != 0); // if channel two is active - write 1, otherwise - 0
// when channels are swithed and no dead time currently active
if(*LocalActiveChannel != hpwm->fActiveChannel)
{ // update active channel
*LocalActiveChannel = hpwm->fActiveChannel;
// set deadtime
*LocalDeadTimeCnt = hpwm->PWM_DeadTime;
Trace_PWM_DeadTime_Enter();
}
// decrement dead time
*LocalDeadTimeCnt -= (PWM_Get_Autoreload(hpwm)+1)*hpwm->stim.sTickBaseMHz;
if(*LocalDeadTimeCnt > 0) // if dead time is still active
{ // reset all channels
// reset channels
PWM_Set_Compare1(hpwm, 0);
PWM_Set_Compare2(hpwm, 0);
}
else // if dead time is done
{ // set it to zero
*LocalDeadTimeCnt = 0;
Trace_PWM_DeadTime_Exit();
}
}
/**
* @brief Filling table with one period of sinus values.
* @param hpwm - указатель на хендл ШИМ.
* @param table_size - размер таблицы.
* @note Формирует таблицу синусов размером table_size.
*/
uint32_t PWM_Fill_Sine_Table(PWM_HandleTypeDef *hpwm, uint32_t table_size)
{
if((hpwm == NULL) || (hpwm->pDuty_Table_Origin == NULL) || (table_size == 0))
{
return 0;
}
if (table_size > SIN_TABLE_SIZE_MAX)
table_size = SIN_TABLE_SIZE_MAX;
hpwm->Duty_Table_Size = table_size;
float pi_step = 2*M_PI/(hpwm->Duty_Table_Size);
float pi_val = 0;
float sin_koef = 0;
uint32_t sin_val = 0;
// fill table with sinus
for(int i = 0; i < hpwm->Duty_Table_Size; i++)
{
// rotate pi
pi_val += pi_step;
// calc sin value
sin_koef = (float)0xFFFF;
sin_val = (sin(pi_val)+1)*sin_koef/2;
sin_table[i] = sin_val;
}
// fill rest of table with zeros
for(int i = hpwm->Duty_Table_Size; i < SIN_TABLE_SIZE_MAX; i++)
sin_table[i] = 0;
// if second channel is enabled
PWM_Update_DutyTableScale(hpwm);
return hpwm->Duty_Table_Size;
}
/**
* @brief Calc and update new Duty Table Scale.
* @param hpwm - указатель на хендл ШИМ.
* @note Используется, когда изменяется значение регистра ARR.
*/
void PWM_Update_DutyTableScale(PWM_HandleTypeDef *hpwm)
{
// UPDATE DUTY TABLE SCALE
if(PWM_Get_Mode(hpwm, PWM_CH_MODE)) // if second channel is enabled
{
hpwm->Duty_Table_Scale = PWM_Calc_Duty_Scale(&hpwm1, 0x8000);
}
else
{
hpwm->Duty_Table_Scale = PWM_Calc_Duty_Scale(&hpwm1, 0xFFFF);
}
// for case if min pulse dur is too big and scale is negative
if (hpwm->Duty_Table_Scale < 0)
hpwm->Duty_Table_Scale = 1;
}
//-------------------------------------------------------------------
//-----------------------THREEPHASE FUNCTIONS------------------------
/**
* @brief Initialization of Slave PWM TIM.
* @param hspwm - указатель на хендл слейв ШИМ.
* @note Вызывает функции инициализации и включения слейв ШИМ.
*/
void PWM_SlavePhase_Init(PWM_SlaveHandleTypeDef *hspwm)
{
TIM_Base_Init(&hspwm->stim);
TIM_Output_PWM_Init(&hspwm->stim.htim, &hspwm->hMasterPWM->sConfigOC, hspwm->PWM_Channel1, hspwm->GPIOx, hspwm->GPIO_PIN_X1);
TIM_Output_PWM_Init(&hspwm->stim.htim, &hspwm->hMasterPWM->sConfigOC, hspwm->PWM_Channel2, hspwm->GPIOx, hspwm->GPIO_PIN_X2);
// if three phase enables
//----------TIMERS START-------------
HAL_TIM_Base_Start(&hspwm->stim.htim);
HAL_TIM_PWM_Start(&hspwm->stim.htim, hspwm->PWM_Channel1); // PWM channel 1
HAL_TIM_PWM_Start(&hspwm->stim.htim, hspwm->PWM_Channel2); // PWM channel 2
if(PWM_Get_Mode(hspwm->hMasterPWM, PWM_PHASE_MODE) == 0) // if three phase disabled
{
PWM_Set_Compare1(hspwm, 0); // reset first channel
PWM_Set_Compare2(hspwm, 0); // reset second channel
}
}
/**
* @brief reInitialization of Slave PWM TIM.
* @param hspwm - указатель на хендл слейв ШИМ.
* @note Перенастраивает таймер согласно принятным настройкам в pwm_ctrl.
*/
void PWM_SlavePhase_reInit(PWM_SlaveHandleTypeDef *hspwm)
{
PWM_Slave_CopyTimSetting(hspwm, sTimFreqHz);
TIM_Base_MspDeInit(&hspwm->stim.htim);
PWM_SlavePhase_Init(hspwm);
}
/**
* @brief Set Duty from table on Slave PWM at one channel by sin_ind of the Master PWM.
* @param hspwm - указатель на хендл слейв ШИМ.
* @param sin_ind - индекс таблицы для Мастер ШИМ.
* @note Индекс для свейл ШИМ расчитывается в самой функции.
*/
void PWM_SlavePhase_Set_DutyTable_Unsigned(PWM_SlaveHandleTypeDef *hspwm, uint16_t sin_ind)
{
// if three phase enables
if (PWM_Get_Mode(hspwm->hMasterPWM, PWM_PHASE_MODE))
{
if(hspwm->Duty_Shift_Ratio > 0)
sin_ind += hspwm->hMasterPWM->Duty_Table_Size*hspwm->Duty_Shift_Ratio;
else
sin_ind += hspwm->hMasterPWM->Duty_Table_Size*(1+hspwm->Duty_Shift_Ratio);
// overflow check
if(sin_ind > hspwm->hMasterPWM->Duty_Table_Size)
sin_ind -= hspwm->hMasterPWM->Duty_Table_Size;
PWM_Set_SlaveDuty_From_Table(hspwm, sin_ind); // set first channel
}
}
/**
* @brief Set Duty from table on Slave PWM at two channel by sin_ind of the Master PWM.
* @param hspwm - указатель на хендл слейв ШИМ.
* @param sin_ind - индекс таблицы для Мастер ШИМ.
* @note Индекс для свейл ШИМ расчитывается в самой функции.
*/
void PWM_SlavePhase_Set_DutyTable_Signed(PWM_SlaveHandleTypeDef *hspwm, uint16_t sin_ind)
{
int Duty;
// if three phase enables
if (PWM_Get_Mode(hspwm->hMasterPWM, PWM_PHASE_MODE))
{
if(hspwm->Duty_Shift_Ratio > 0)
sin_ind += hspwm->hMasterPWM->Duty_Table_Size*hspwm->Duty_Shift_Ratio;
else
sin_ind += hspwm->hMasterPWM->Duty_Table_Size*(1+hspwm->Duty_Shift_Ratio);
// overflow check
if(sin_ind >= hspwm->hMasterPWM->Duty_Table_Size)
sin_ind -= hspwm->hMasterPWM->Duty_Table_Size;
Duty = PWM_Get_Table_Element_Signed(hspwm->hMasterPWM, sin_ind);
// если это первая полуволна
if(Duty > 0)
{
PWM_Set_Compare1(hspwm, Duty+PWM_Calc_Min_Duty(hspwm->hMasterPWM)); // set first channel
PWM_Set_Compare2(hspwm, 0); // reset second channel
}
else // если это вторая полуволна
{
PWM_Set_Compare1(hspwm, 0); // reset first channel
PWM_Set_Compare2(hspwm, (-Duty)+PWM_Calc_Min_Duty(hspwm->hMasterPWM)); // set second channel
}
//if(hspwm == &hpwm2)
//__ASM("");
}
else // if three phase disabled
{
PWM_Set_Compare1(hspwm, 0); // reset first channel
PWM_Set_Compare2(hspwm, 0); // reset second channel
}
}
/**
* @brief Check is all Slave channels works properly.
* @param hspwm - указатель на хендл слейв ШИМ.
* @note Проверка работает ли только один из каналов, и проверка чтобы CCRx <= ARR
* @note В мастере проверка происходит напрямую в PWM_Handler.
*/
void PWM_SlavePhase_Check_Channels(PWM_SlaveHandleTypeDef *hspwm)
{
// if three phase enables
if (PWM_Get_Mode(hspwm->hMasterPWM, PWM_PHASE_MODE))
{
uint16_t min_duty = PWM_Calc_Min_Duty(hspwm->hMasterPWM);
// IF FIRST CHANNEL IS ACRIVE
if(PWM_Get_Compare1(hspwm) != 0)
{
// Duty shoud be bigger or equeal than min duration
if (PWM_Get_Compare1(hspwm)<min_duty)
PWM_Set_Compare1(hspwm, min_duty);
// Duty shoud be less or equeal than ARR-min duration
if (PWM_Get_Compare1(hspwm)>PWM_Get_Autoreload(hspwm)-min_duty)
PWM_Set_Compare1(hspwm, PWM_Get_Autoreload(hspwm)-min_duty);
}
// IF SECOND CHANNEL IS ACRIVE
else if(PWM_Get_Compare2(hspwm) != 0)
// Duty shoud be bigger or equeal than min duration
if (PWM_Get_Compare2(hspwm)<min_duty)
PWM_Set_Compare2(hspwm, min_duty);
// Duty shoud be less or equeal than ARR
if (PWM_Get_Compare2(hspwm)>PWM_Get_Autoreload(hspwm)-min_duty)
PWM_Set_Compare2(hspwm, PWM_Get_Autoreload(hspwm)-min_duty);
// IF BOTH CHANNEL IS ACRIVE
if((PWM_Get_Compare1(hspwm) != 0) && (PWM_Get_Compare2(hspwm) != 0))
{
// Only one channel shoud be active so disable all
PWM_Set_Compare1(hspwm, 0);
PWM_Set_Compare2(hspwm, 0);
}
}
else
{
// reset channels
PWM_Set_Compare1(hspwm, 0); // reset first channel
PWM_Set_Compare2(hspwm, 0); // reset second channel
}
}
/**
* @brief Create Dead Time for Slave PWM when switches channels.
* @param hspwm - указатель на хендл слейв ШИМ.
* @param LocalDeadTimeCnt - указатель на переменную для отсчитывания дедтайма.
* @param LocalActiveChannel - указатель на переменную для отслеживания смены канала.
* @note Аналог функции PWM_CreateDeadTime но для слейв ШИМов.
*/
void PWM_SlavePhase_CreateDeadTime(PWM_SlaveHandleTypeDef *hspwm, float *LocalDeadTimeCnt, unsigned *LocalActiveChannel)
{
// get current active channel
hspwm->fActiveChannel = (PWM_Get_Compare2(hspwm) != 0); // if channel two is active - write 1, otherwise - 0
// when channels are swithed and no dead time currently active
if(*LocalActiveChannel != hspwm->fActiveChannel)
{ // update active channel
*LocalActiveChannel = hspwm->fActiveChannel;
// set deadtime
*LocalDeadTimeCnt = hspwm->hMasterPWM->PWM_DeadTime;
Trace_PWM_DeadTime_Enter();
}
// decrement dead time
*LocalDeadTimeCnt -= (PWM_Get_Autoreload(hspwm)+1)*hspwm->hMasterPWM->stim.sTickBaseMHz;
if(*LocalDeadTimeCnt > 0) // if dead time is still active
{ // reset all channels
// reset channels
PWM_Set_Compare1(hspwm, 0);
PWM_Set_Compare2(hspwm, 0);
}
else // if dead time is done
{ // set it to zero
*LocalDeadTimeCnt = 0;
Trace_PWM_DeadTime_Exit();
}
}
//-------------------------------------------------------------------
//------------------------HANDLERS FUNCTIONS-------------------------
//---------------PWM TIMER-----------------
#if (PWM_MASTER_TIM_NUMB == 1) || (PWM_MASTER_TIM_NUMB == 10) // choose handler for TIM
void TIM1_UP_TIM10_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 2)
void TIM2_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 3)
void TIM3_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 4)
void TIM4_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 5)
void TIM5_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 6)
void TIM6_DAC_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 7)
void TIM7_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 8) || (PWM_MASTER_TIM_NUMB == 13)
void TIM8_UP_TIM13_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 1) || (PWM_MASTER_TIM_NUMB == 9)
void TIM1_BRK_TIM9_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 1) || (PWM_MASTER_TIM_NUMB == 11)
void TIM1_TRG_COM_TIM11_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 8) || (PWM_MASTER_TIM_NUMB == 12)
void TIM8_BRK_TIM12_IRQHandler(void)
#elif (PWM_MASTER_TIM_NUMB == 8) || (PWM_MASTER_TIM_NUMB == 14)
void TIM8_TRG_COM_TIM14_IRQHandler(void)
#endif
{
Trace_PWM_TIM_Enter();
HAL_TIM_IRQHandler(&hpwm1.stim.htim);
PWM_Handler(&hpwm1);
Trace_PWM_TIM_Exit();
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//-----------------------------OUTDATE-------------------------------
#ifdef OUTDATE
/**
* @brief First set up of PWM Single Channel.
* @note Первый инит ШИМ. Заполняет структуры и инициализирует таймер для генерации синуоидального ШИМ.
* Скважность ШИМ меняется по закону синусоиды, сдвинутой в положительную область (от 0 до 2)
* ШИМ генерируется на одном канале.
* @note This called from main
*/
void PWM_SineSingChannel_FirstInit(void)
{
hpwm1.pDuty_Table_Origin = SIN_TABLE_ORIGIN;
//---------PWM TIMER1 INIT------------
// channel settings
hpwm1.sConfigOC.OCMode = TIM_OCMODE_PWM1;
hpwm1.sConfigOC.Pulse = 0;
hpwm1.sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
hpwm1.sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
// tim1 settings
hpwm1.stim.htim.Instance = TIMER_PWM1_INSTANCE;
hpwm1.stim.sTimMode = TIM_IT_MODE;
hpwm1.stim.sTickBaseMHz = TIM_TickBase_1US;
hpwm1.stim.sTimAHBFreqMHz = 72;
hpwm1.stim.sTimFreqHz = HZ_TIMER_PWM;
hpwm1.GPIOx = GPIOD;
hpwm1.GPIO_PIN_X1 = GPIO_PIN_12;
TIM_Base_Init(&hpwm1.stim);
TIM_Output_PWM_Init(&hpwm1.stim.htim, &hpwm1.sConfigOC, hpwm->PWM_Channel1, hpwm1.GPIOx, hpwm1.GPIO_PIN_X1);
//----------TIMERS START-------------
HAL_TIM_PWM_Start_IT(&hpwm1.stim.htim, hpwm->PWM_Channel1); // timer for PWM
}
#ifdef SINE_THREE_PHASE_PWM_ENABLE
//---------PWM TIMER2 INIT------------
// tim2 settings
hpwm2 = hpwm1;
hpwm2.stim.htim.Instance = TIM5;
hpwm2.GPIOx = GPIOA;
hpwm2.GPIO_PIN_X = GPIO_PIN_0;
TIM_Base_Init(&hpwm2.stim);
TIM_Output_PWM_Init(&hpwm2.stim.htim, &hpwm2.sConfigOC, TIM_CHANNEL_1, hpwm2.GPIOx, hpwm2.GPIO_PIN_X);
//---------PWM TIMER3 INIT------------
// tim3 settings
hpwm3 = hpwm2;
hpwm3.stim.htim.Instance = TIM8;
hpwm3.GPIOx = GPIOC;
hpwm3.GPIO_PIN_X = GPIO_PIN_6;
TIM_Base_Init(&hpwm3.stim);
TIM_Output_PWM_Init(&hpwm3.stim.htim, &hpwm3.sConfigOC, TIM_CHANNEL_1, hpwm3.GPIOx, hpwm3.GPIO_PIN_X);
HAL_TIM_PWM_Start(&hpwm2.stim.htim, TIM_CHANNEL_1); // timer for PWM
HAL_TIM_PWM_Start(&hpwm3.stim.htim, TIM_CHANNEL_1); // timer for PWM
#endif // SINE_THREE_PHASE_PWM_ENABLE
void PWM_Threephase_Init(void)
{
#ifdef INTERNAL_THREE_PHASE_PWM_ENABLE
TIM_OC_InitTypeDef sPWMConfigOC = {0};
TIM_OC_InitTypeDef sOCConfigOC = {0};
int us100Time = 10000/TIM_CTRL.sTimFreqHz; // 1/TIM_CTRL.sTimFreqHz * 10^6 - Sample time in us
// PWM CHANNEL SETTINGS
sPWMConfigOC.OCMode = TIM_OCMODE_PWM1;
sPWMConfigOC.Pulse = us100Time/2;
sPWMConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sPWMConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
// CC CHANNEL SETTINGS
sOCConfigOC.OCMode = TIM_OCMODE_ACTIVE;
sOCConfigOC.Pulse = (2*us100Time-1) / 3;
sOCConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
// TIMER1 PWM MASTER INIT
TIM_3PWM1.htim = &tim_3pwm1;
TIM_3PWM1.htim->Instance = TIM1;
TIM_3PWM1.htim->Init.Prescaler = 7200-1; // 1 us
TIM_3PWM1.htim->Init.Period = us100Time-1; // period in us = Sample time in us
TIM_3PWM1.sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC2REF;
TIM_3PWM1.sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
TIM_3PWM1.sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
TIM_Base_Init(&TIM_3PWM1);
TIM_Output_PWM_Init(TIM_3PWM1.htim, &sPWMConfigOC, TIM_CHANNEL_1, GPIOE, GPIO_PIN_9);
HAL_TIM_OC_ConfigChannel(TIM_3PWM1.htim, &sOCConfigOC, TIM_CHANNEL_2);
// TIMER2 PWM SLAVE INIT
TIM_3PWM2 = TIM_3PWM1;
TIM_3PWM2.htim = &tim_3pwm2;
*TIM_3PWM2.htim = *TIM_3PWM1.htim;
TIM_3PWM2.htim->Instance = TIM2;
TIM_3PWM1.TIM_MODE = TIM_DEFAULT;
TIM_3PWM2.sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
TIM_3PWM2.sSlaveConfig.InputTrigger = TIM_TS_ITR0;
TIM_Base_Init(&TIM_3PWM2);
TIM_Output_PWM_Init(TIM_3PWM2.htim, &sPWMConfigOC, TIM_CHANNEL_1, GPIOA, GPIO_PIN_5);
HAL_TIM_OC_ConfigChannel(TIM_3PWM2.htim, &sOCConfigOC, TIM_CHANNEL_2);
// TIMER3 PWM SLAVE INIT
TIM_3PWM3 = TIM_3PWM2;
TIM_3PWM3.htim = &tim_3pwm3;
*TIM_3PWM3.htim = *TIM_3PWM2.htim;
TIM_3PWM3.htim->Instance = TIM3;
TIM_3PWM3.sSlaveConfig.InputTrigger = TIM_TS_ITR1;
TIM_Base_Init(&TIM_3PWM3);
TIM_Output_PWM_Init(TIM_3PWM3.htim, &sPWMConfigOC, TIM_CHANNEL_1, GPIOA, GPIO_PIN_6);
hpwm1.Duty_Table_Size = PWM_Fill_Sine_Table(&sin_table, SIN_TABLE_SIZE_MAX);
// TIMERS START
HAL_TIM_OC_Start(TIM_3PWM3.htim, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(TIM_3PWM3.htim, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(TIM_3PWM2.htim, TIM_CHANNEL_1);
HAL_TIM_OC_Start(TIM_3PWM2.htim, TIM_CHANNEL_2);
HAL_TIM_OC_Start(TIM_3PWM1.htim, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(TIM_3PWM1.htim, TIM_CHANNEL_1);
#endif // INTERNAL_THREE_PHASE_PWM_ENABLE
}
#endif