Симуляция генерации синусоидального шим и управление по модбас

note:
- модбас не моделируется,  в s-function просто передаются константы режимов.
- лишние файлы убраны в outdate.
- два канала одной фазы переключаются немного криво: на один такт симуляции проскакивает высокий уровень предыдущего канала и только потом включается текущий канал
This commit is contained in:
alexey
2024-08-21 12:58:52 +03:00
parent fcc3e72824
commit 0958cb80c7
682 changed files with 871653 additions and 0 deletions

238
Code/PWM/control.c Normal file
View File

@@ -0,0 +1,238 @@
#include "pwm.h"
TIM_SettingsTypeDef TIM_CTRL = {0};
// variables for filling arrays
int Numb_Of_Peroids = 2; // number of periods
int Samples_Per_Peroid = 0; // how many samples in one period
int Size_Of_Log = 0; // size of written data to log
int log_ind = 0; // index of log arrays
int cnt_to_cnt_log = 0; // counter for log_cnt
int sine_ind_prev = 0;
/**
* @brief Filling logs.
* @note Заполнение логов: синус, шим, пила.
* @note This called from TIM_CTRL_Handler
*/
void Fill_Logs_with_Data(void)
{
// calc pwm duty from timer
float PWM_Duty;
if(PWM_Get_Mode(&hpwm1, PWM_DC_MODE) == 0) // if sinus need to be written
{
if(PWM_Get_Mode(&hpwm1, PWM_CH_MODE)) // if its signed sine mode (two channels)
{
if(hpwm1.Duty_Table_Ind < hpwm1.Duty_Table_Size/2) // first half get from channel 1
PWM_Duty = (((float)PWM_Get_Compare1(&hpwm1))/(PWM_Get_Autoreload(&hpwm1)))+1;
else // second half get from channel 2
PWM_Duty = 1-(((float)PWM_Get_Compare2(&hpwm1))/(PWM_Get_Autoreload(&hpwm1)));
}
else // if its unsigned sine mode (single channel)
{ // just get current pwm duty
PWM_Duty = ((float)PWM_Get_Compare1(&hpwm1)/PWM_Get_Autoreload(&hpwm1));
}
}
else // if its dc pwm mode
{ // just get current pwm duty
if(PWM_Get_Mode(&hpwm1, PWM_CH_MODE)) // if its second channels mode
PWM_Duty = ((float)PWM_Get_Compare2(&hpwm1)/PWM_Get_Autoreload(&hpwm1));
else // if its first channel mode
PWM_Duty = ((float)PWM_Get_Compare1(&hpwm1)/PWM_Get_Autoreload(&hpwm1));
}
// WRITE SINUS TO WHOLE ARRAY
// sine_log[log_ind] = sin_val;
if(PWM_Get_Mode(&hpwm1,PWM_DC_MODE) == 0) // in table mode write PWM Duty (write sine) with scale 1/2 from sin table max value (0xFFFF/2)
sine_log[log_ind] = PWM_Duty*(0x8000-1);
else // in dc mode write PWM Duty (write sine)
sine_log[log_ind] = 0;
// WRITE PWM
if(PWM_Get_Mode(&hpwm1,PWM_DC_MODE)) // in DC mode
{
// write 1 - if log_ind < Size_Of_Period*PWM_Dury
// write 0 - otherwise
pwm_log[log_ind] = (log_ind%(Size_Of_Log/Numb_Of_Peroids) < (Size_Of_Log/Numb_Of_Peroids+1)*hpwm1.PWM_Value/100)? 1: 0;
}
else // in table mode
{
// write fill whole pwm array at one interrupt
int PWM_Period_End_Ind = (Size_Of_Log/Numb_Of_Peroids);
int PWM_Step_End_Ind;
if(PWM_Get_Mode(&hpwm1,PWM_CH_MODE))
PWM_Step_End_Ind = PWM_Period_End_Ind*fabs(PWM_Duty-1);
else
PWM_Step_End_Ind = PWM_Period_End_Ind*PWM_Duty;
for(int i = 0; i <= PWM_Step_End_Ind; i++)
{
for (int j = 0; j < Numb_Of_Peroids; j++)
pwm_log[i+j*PWM_Period_End_Ind] = 1;
}
for(int i = PWM_Step_End_Ind+1; i < PWM_Period_End_Ind; i++)
for (int j = 0; j < Numb_Of_Peroids; j++)
pwm_log[i+j*PWM_Period_End_Ind] = 0;
}
// WRITE COUNTER
cnt_log[log_ind] = cnt_to_cnt_log;
cnt_to_cnt_log++;
if(cnt_to_cnt_log>=Size_Of_Log/2)
cnt_to_cnt_log = 0;
// INCREMENT AND RESET COUNTER
log_ind++;
if(PWM_Get_Mode(&hpwm1,PWM_DC_MODE) == 0) // if its PWM table mode
{
// SYNCHRONIZE PERIOD OF SIN IN LOG
// (это надо, чтобы данные не съезжали из-за несинхронизированного периода)
// wait until period ended
if(log_ind>Size_Of_Log-1) // if logs are filled
{
if((unsigned)hpwm1.Duty_Table_Ind < sine_ind_prev) // and if new period started
{
log_ind = 0; // reset counter
sine_ind_prev = (unsigned)hpwm1.Duty_Table_Ind;
}
}
// update prev variable only if log currently writing
else
sine_ind_prev = (unsigned)hpwm1.Duty_Table_Ind;
}
else // if its PWM DC mode
{
// if logs are filled
if(log_ind>Size_Of_Log-1)
log_ind = 0;
}
// if its overflow log array size - reset log_ind
if(log_ind>LOG_SIZE-1)
{
log_ind = 0;
sine_ind_prev = (unsigned)hpwm1.Duty_Table_Ind;
}
}
/**
* @brief Update log parameters.
* @note Проверка надо ли обновлять параметры логов, и если надо - обновляет их.
* @note This called from TIM_CTRL_Handler
*/
void Update_Params_For_Log(void)
{
unsigned UpdateLog = 0;
// READ NUMB OF PERIOD IN LOGS
if(Numb_Of_Peroids != log_ctrl[R_LOG_CTRL_LOG_PWM_NUMB])
{
Numb_Of_Peroids = log_ctrl[R_LOG_CTRL_LOG_PWM_NUMB];
// update logs params
UpdateLog = 1;
}
// READ SIZE OF LOGS
if(Size_Of_Log != log_ctrl[R_LOG_CTRL_LOG_SIZE])
{
Size_Of_Log = log_ctrl[R_LOG_CTRL_LOG_SIZE];
// update logs params
UpdateLog = 1;
}
// UPDATE LOG PARAMS
if(UpdateLog)
{
// set logs params
Set_Log_Params();
}
}
/**
* @brief Set up log parameters.
* @note Устанавливает настройки логов и проверяет их на корректность.
*/
void Set_Log_Params(void)
{
// SET LOG PARAMS
log_ind = 0;
Samples_Per_Peroid = TIM_CTRL.sTimFreqHz/hpwm1.PWM_Value;
if(Size_Of_Log > LOG_SIZE) // if its too much data in log
{
Numb_Of_Peroids = (LOG_SIZE/Samples_Per_Peroid);
log_ctrl[R_LOG_CTRL_LOG_SIZE] = Numb_Of_Peroids;
Size_Of_Log = Numb_Of_Peroids*Samples_Per_Peroid;
}
// clear logs arrays
for(int i = Size_Of_Log; i < LOG_SIZE; i++)
{
sine_log[i] = 0;
pwm_log[i] = 0;
cnt_log[i] = 0;
}
}
/**
* @brief reInitialization of control timer.
* @note Перенастраивает таймер согласно принятным настройкам в log_ctrl.
* @note This called from main while
*/
void Control_Timer_ReInit(TIM_SettingsTypeDef *stim)
{
TIM_Base_MspDeInit(&stim->htim);
hpwm1.stim.sTickBaseMHz = TIMER_CTRL_TICKBASE;
TIM_Base_Init(stim);
HAL_TIM_Base_Start_IT(&stim->htim); // timer for sinus
HAL_NVIC_SetPriority(TIM8_BRK_TIM12_IRQn, 1, 1);
}
/**
* @brief First initialization of Control Timer.
* @note Первый управляющего таймера. Таймер записывает логи и обновляет параметры ШИМ.
* @note This called from main
*/
void Control_Timer_FirstInit(void)
{
//-------CONTROL TIMER INIT----------
// tim settings
TIM_CTRL.htim.Instance = TIM12;
TIM_CTRL.sTimMode = TIM_IT_MODE;
TIM_CTRL.sTickBaseMHz = TIMER_CTRL_TICKBASE;
TIM_CTRL.sTimAHBFreqMHz = TIMER_CTRL_AHB_FREQ;
TIM_CTRL.sTimFreqHz = HZ_TIMER_CTRL;
TIM_Base_Init(&TIM_CTRL);
HAL_NVIC_SetPriority(TIM8_BRK_TIM12_IRQn, 1, 1);
HAL_TIM_Base_Start_IT(&TIM_CTRL.htim); // timer for sinus
// FILL TIME ARRAY WITH TIME
for(int i = 0; i <= R_TIME_LOG_QNT; i++)
time_log[i] = i;
}
//-------------------------------------------------------------------
//------------------------HANDLERS FUNCTIONS-------------------------
//-------------CONTROL TIMER---------------
void TIM8_BRK_TIM12_IRQHandler(void)
{
Trace_CTRL_TIM_Enter();
HAL_TIM_IRQHandler(&TIM_CTRL.htim);
Fill_Logs_with_Data();
Update_Params_For_Log();
Update_Params_For_PWM(&hpwm1);
Trace_CTRL_TIM_Exit();
}

48
Code/PWM/control.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef __CONTROL_H_
#define __CONTROL_H_
#include "modbus_data.h"
#include "periph_general.h"
#include "math.h"
#include "settings.h"
#define M_PI 3.14159265358979323846 /* pi */
extern TIM_SettingsTypeDef TIM_CTRL;
//---------------------this called from TIM_CTRL_Handler()-----------------------
/**
* @brief Update log parameters.
* @note Проверка надо ли обновлять параметры логов, и если надо - обновляет их.
* @note This called from TIM_CTRL_Handler
*/
void Update_Params_For_Log(void);
/**
* @brief Filling logs.
* @note заполнение логов: синус, шим, пила.
* @note this called from TIM_CTRL_Handler
*/
void Fill_Logs_with_Data(void);
/**
* @brief Set up log parameters.
* @note Устанавливает настройки логов и проверяет их на корректность.
*/
void Set_Log_Params(void);
/**
* @brief First initialization of Control Timer.
* @note Первый управляющего таймера. Таймер записывает логи и обновляет параметры ШИМ.
* @note This called from main
*/
void Control_Timer_FirstInit(void);
// this called from main while(1)
/**
* @brief reInitialization of control timer.
* @param stim - указатель на настройки таймера.
* @note Перенастраивает таймер согласно принятным настройкам в log_ctrl.
* @note This called from main while
*/
void Control_Timer_ReInit(TIM_SettingsTypeDef *stim);
#endif // __CONTROL_H_

38
Code/PWM/control_def.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef __CONTROL_H_
#define __CONTROL_H_
#include "modbus_data.h"
#include "periph_general.h"
#include "math.h"
#define M_PI 3.14159265358979323846 /* pi */
// defines for timers
#define HZ_TIMER_CTRL 400
#define HZ_TIMER_PWM 1000
// defines for PWMs
//#define INTERNAL_THREE_PHASE_PWM_ENABLE // pwm with timer in slave mode: time shift less than 1 period of PWM
//#define SINE_THREE_PHASE_PWM_ENABLE // pwm sine with time shift
#define SIN_TABLE_SIZE_MAX HZ_TIMER_PWM
// defines for filling arrays
#define CALC_SAMPLES_PER_PERIOD(_hztim_, _hztarget_) (_hztim_/_hztarget_)
//(TIM_CTRL.sTimFreq/PWM.PWM_Value)
// OR define samples
//#define SAMPLES_PER_PERIOD 10
//#define HZ_TIMER_CTRL (HZ_SINUS*SAMPLES_PER_PERIOD)
#define CALC_NUMB_OF_PERIODS(_arr_size_, _hztim_, _hztarget_) (_arr_size_/CALC_SAMPLES_PER_PERIOD(_hztim_, _hztarget_))
//#define NUMB_OF_PERIODS (5)
#define CALC_ARR_SIZE(_numb_of_periods_, _samples_per_period_) ((_numb_of_periods_)*(_samples_per_period_))
#endif // __CONTROL_H_

791
Code/PWM/pwm.c Normal file
View File

@@ -0,0 +1,791 @@
#include "pwm.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_PWM_INSTANCE;
hpwm1.stim.sTimMode = TIM_IT_MODE;
hpwm1.stim.sTickBaseMHz = TIMER_PWM_TICKBASE;
hpwm1.stim.sTimAHBFreqMHz = TIMER_PWM_AHB_FREQ;
hpwm1.stim.sTimFreqHz = HZ_TIMER_PWM;
hpwm1.GPIOx = TIMER_PWM_GPIOx;
hpwm1.GPIO_PIN_X1 = TIMER_PWM_GPIO_PIN_X1;
hpwm1.GPIO_PIN_X2 = TIMER_PWM_GPIO_PIN_X2;
hpwm1.PWM_Channel1 = TIMER_PWM_TIM_CHANNEL1;
hpwm1.PWM_Channel2 = TIMER_PWM_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 = TIMER_PWM2_INSTANCE;
hpwm2.GPIOx = TIMER_PWM2_GPIOx;
hpwm2.GPIO_PIN_X1 = TIMER_PWM2_GPIO_PIN_X1;
hpwm2.GPIO_PIN_X2 = TIMER_PWM2_GPIO_PIN_X2;
hpwm2.PWM_Channel1 = TIMER_PWM2_TIM_CHANNEL1;
hpwm2.PWM_Channel2 = TIMER_PWM2_TIM_CHANNEL2;
hpwm2.Duty_Shift_Ratio = (float)2/3;
hpwm3.hMasterPWM = &hpwm1;
hpwm3.stim = hpwm1.stim;
hpwm3.stim.htim.Instance = TIMER_PWM3_INSTANCE;
hpwm3.GPIOx = TIMER_PWM3_GPIOx;
hpwm3.GPIO_PIN_X1 = TIMER_PWM3_GPIO_PIN_X1;
hpwm3.GPIO_PIN_X2 = TIMER_PWM3_GPIO_PIN_X2;
hpwm3.PWM_Channel1 = TIMER_PWM3_TIM_CHANNEL1;
hpwm3.PWM_Channel2 = TIMER_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+PWM_Sine_Calc_Min_Duty(hpwm)); // 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)+PWM_Sine_Calc_Min_Duty(hpwm)); // 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
// 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
}
// if first channel enabled
else
{
PWM_Set_Duty_From_Percent(hpwm, hpwm->PWM_Channel1); // set first channel
PWM_Set_Compare2(hpwm, 0); // reset second channel
}
}
//-----CHECK CHANNELS FOR ERRORS-----
// Duty shoud be less or equeal than ARR
if (PWM_Get_Compare1(hpwm)>PWM_Get_Autoreload(hpwm))
PWM_Set_Compare1(hpwm, PWM_Get_Autoreload(hpwm));
// Duty shoud be less or equeal than ARR
if (PWM_Get_Compare2(hpwm)>PWM_Get_Autoreload(hpwm))
PWM_Set_Compare2(hpwm, PWM_Get_Autoreload(hpwm));
// Only one channel shoud be active
if((PWM_Get_Compare1(hpwm) != 0) && (PWM_Get_Compare2(hpwm) != 0))
{
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_Sine_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_Sine_Calc_Min_Duty(hspwm->hMasterPWM)); // set second channel
}
}
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))
{
// Duty shoud be less or equeal than ARR
if (PWM_Get_Compare1(hspwm)>PWM_Get_Autoreload(hspwm))
PWM_Set_Compare1(hspwm, PWM_Get_Autoreload(hspwm));
// Duty shoud be less or equeal than ARR
if (PWM_Get_Compare2(hspwm)>PWM_Get_Autoreload(hspwm))
PWM_Set_Compare2(hspwm, PWM_Get_Autoreload(hspwm));
// Only one channel shoud be active
if((PWM_Get_Compare1(hspwm) != 0) && (PWM_Get_Compare2(hspwm) != 0))
{ // reset channels
PWM_Set_Compare1(hspwm, 0); // reset first channel
PWM_Set_Compare2(hspwm, 0); // reset second channel
}
}
else // if three phase disabled
{
// 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-----------------
void TIM4_IRQHandler(void)
{
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_PWM_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

325
Code/PWM/pwm.h Normal file
View File

@@ -0,0 +1,325 @@
/********************************MODBUS*************************************
Данный файл содержит объявления базовых функции и дефайны для реализации
MODBUS.
Данный файл необходимо подключить в rs_message.h. После подключать rs_message.h
к основному проекту.
***************************************************************************/
#ifndef __PWM_H_
#define __PWM_H_
#include "control.h"
extern uint32_t sin_table[SIN_TABLE_SIZE_MAX];
#define int_to_percent(_int_) ((float)_int_/100)
/////////////////////////////////////////////////////////////////////
////////////////////////////---DEFINES---////////////////////////////
//----------------------------PWM HANDLE----------------------------//
/**
* @brief Calc duration of minimum pulse in ticks.
* @param _hpwm_ - указатель на хендл pwm.
* @return _val_ - количество тиков кратчайшего импульса.
*/
#define PWM_Sine_Calc_Min_Duty(_hpwm_) ((_hpwm_)->PWM_MinPulseDur/(_hpwm_)->stim.sTickBaseMHz)
/**
* @brief Calc Scale Koef for Table & AUTORELOAD REGISTER
* @param _hpwm_ - указатель на хендл pwm.
* @param _scale_ - верхняя граница диапазона значений.
* @return _koef_ - коэффициент для масштабирования.
* @note Данный макрос рассчитывает коэффициент для приведения значений с диапазоном [0,_scale_]
к регистру автозагрузки с диапазоном [0,ARR].
* @note Если задана минимальная длительность импульса в тактах n, она вычитается из ARR: [0, ARR-2*n]
И потом регистр ARR заполняется так, что диапазон его значений будет [n, ARR-n] @ref PWM_Get_Table_Element_Unsigned
*/
#define PWM_Calc_Duty_Scale(_hpwm_, _scale_) ((float)PWM_Get_Autoreload(_hpwm_)-2*PWM_Sine_Calc_Min_Duty(_hpwm_))/(_scale_)
/**
* @brief Get Table Element Scaled corresponding to TIM ARR register
* @param _hpwm_ - указатель на хендл pwm.
* @param _ind_ - номер элемента из таблицы скважностей.
* @return _val_ - масштабированный под регистры таймера значение.
* @note Если задана минимальная длительность импульса в тактах n,
то регистр ARR заполняется так, что диапазон его значений будет [n, ARR-n]
*/
#define PWM_Get_Table_Element_Unsigned(_hpwm_,_ind_) ((*((_hpwm_)->pDuty_Table_Origin+_ind_)*((_hpwm_)->Duty_Table_Scale))+PWM_Sine_Calc_Min_Duty(_hpwm_))
/**
* @brief Get Table Element Scaled and Shifted corresponding to TIM ARR register
* @param _hpwm_ - указатель на хендл pwm.
* @param _ind_ - номер элемента из таблицы скважностей.
* @return _val_ - масштабированный под регистры таймера значение.
* @note По сути такая же как PWM_Get_Table_Element_Unsigned но добавляется сдвиг на одну амплитуду для учитывания знака.
(если точнее, то сдвиг добавляется для компенсации сдвига, который имитирует знак)
* @note 0x8000*(_hpwm_)->Duty_Table_Scale - т.к. первая полуволна находится в диапазоне (0x8000-0xFFFF) вычитаем константу 0x8000 с масштабированием
*/
#define PWM_Get_Table_Element_Signed(_hpwm_,_ind_) ((int)(*((_hpwm_)->pDuty_Table_Origin+_ind_)*((_hpwm_)->Duty_Table_Scale))-0x8000*(_hpwm_)->Duty_Table_Scale)
/**
* @brief Create pointer to slave PWM from pointer to void in PWM_HandleTypeDef.
* @param _hpwm_ - указатель на хендл pwm.
* @param _slavepwm_ - имя слейв pwm.
* @return _pslavepwm_ - указатель на структуру PWM_SlaveHandleTypeDef.
*/
#define PWM_Set_pSlaveHandle(_hpwm_,_slavepwm_) ((PWM_SlaveHandleTypeDef *)_hpwm_->_slavepwm_)
/**
* @brief Copy setting from master TIM_SettingsTypeDef to slave TIM_SettingsTypeDef.
* @param _hpwm_ - указатель на хендл pwm.
* @return _set_ - имя настройки.
*/
#define PWM_Slave_CopyTimSetting(_hspwm_, _set_) ((_hspwm_)->stim._set_ = (_hspwm_)->hMasterPWM->stim._set_)
//---------------------------TIMER REGS----------------------------//
/**
* @brief Set PWM autoreload value (max duty value).
* @param _hpwm_ - указатель на хендл pwm.
* @param _val_ - значение, которое нужно записать в Compare.
*/
#define PWM_Get_Autoreload(_hpwm_) __HAL_TIM_GET_AUTORELOAD(&((_hpwm_)->stim.htim))
/**
* @brief Get PWM Duty on corresponding channel.
* @param _hpwm_ - указатель на хендл pwm.
* @param _val_ - значение, которое нужно записать в Compare.
*/
#define PWM_Get_Compare1(_hpwm_) __HAL_TIM_GET_COMPARE(&((_hpwm_)->stim.htim), (_hpwm_)->PWM_Channel1)
#define PWM_Get_Compare2(_hpwm_) __HAL_TIM_GET_COMPARE(&((_hpwm_)->stim.htim), (_hpwm_)->PWM_Channel2)
/**
* @brief Set PWM Duty on corresponding channel.
* @param _hpwm_ - указатель на хендл pwm.
* @param _val_ - значение, которое нужно записать в Compare.
*/
#define PWM_Set_Compare1(_hpwm_, _val_) __HAL_TIM_SET_COMPARE(&((_hpwm_)->stim.htim), (_hpwm_)->PWM_Channel1, (_val_))
#define PWM_Set_Compare2(_hpwm_, _val_) __HAL_TIM_SET_COMPARE(&((_hpwm_)->stim.htim), (_hpwm_)->PWM_Channel2, (_val_))
/**
* @brief Set PWM Duty From PWM_Value Percent
* @param _hpwm_ - указатель на хендл pwm.
* @param _channel_ - канал для выставления скважности.
* @param _ind_ - номер элемента из таблицы скважностей.
*/
#define PWM_Set_Duty_From_Percent(_hpwm_, _channel_) __HAL_TIM_SET_COMPARE(&((_hpwm_)->stim.htim), _channel_, ((_hpwm_)->PWM_Value/100)*(PWM_Get_Autoreload(_hpwm_)+1))
/**
* @brief Set PWM Duty From table
* @param _hpwm_ - указатель на хендл pwm.
* @param _channel_ - канал для выставления скважности.
* @param _ind_ - номер элемента из таблицы скважностей.
*/
#define PWM_Set_Duty_From_Table(_hpwm_, _ind_) (PWM_Set_Compare1(_hpwm_, (PWM_Get_Table_Element_Unsigned((_hpwm_), (_ind_))+1)))
/**
* @brief Set PWM Duty From table
* @param _hpwm_ - указатель на хендл pwm.
* @param _channel_ - канал для выставления скважности.
* @param _ind_ - номер элемента из таблицы скважностей.
*/
#define PWM_Set_SlaveDuty_From_Table(_hpwm_, _ind_) (PWM_Set_Compare1(_hpwm_, (PWM_Get_Table_Element_Unsigned((_hpwm_)->hMasterPWM, (_ind_))+1)))
// MODE DEFINES
#define PWM_DC_MODE_Pos (0)
#define PWM_CH_MODE_Pos (1)
#define PWM_PHASE_MODE_Pos (2)
#define PWM_DC_MODE (1<<(PWM_DC_MODE_Pos)) // 0 - set pwm duty from table with PWM_Value period, 1 - set pwm duty PWM_Value (in percent)
#define PWM_CH_MODE (1<<(PWM_CH_MODE_Pos))
// DC MODE: 0 - pwm on channel 1, 1 - pwm on channel 2
// TABLE MODE: 0 - signed mode, 1 - unsigned mode
#define PWM_PHASE_MODE (1<<(PWM_PHASE_MODE_Pos))
#define PWM_Get_Mode(_hpwm_, _mode_) ((_hpwm_)->sPWM_Mode&(_mode_))
/* Structure for PWM modes */
typedef enum
{
PWM_TABLE_UNSIGN = 0, /* set pwm duty from table with PWM_Value period */
PWM_TABLE_SIGN = PWM_CH_MODE, /* set pwm duty from table with PWM_Value period on two channels (positive and negative halfes) */
PWM_DC_POS = PWM_DC_MODE, /* set pwm duty PWM_Value (in percent) on first channel */
PWM_DC_NEG = PWM_DC_MODE|PWM_CH_MODE, /* set pwm duty PWM_Value (in percent) on second channel */
PWM_PHASE_UNSIGN = PWM_PHASE_MODE, /* set pwm table duty on three pins, with requested shift */
PWM_PHASE_SIGN = PWM_CH_MODE|PWM_PHASE_MODE, /* set pwm table duty on six pins (two pins = one phase (positive and negative halfes)) */
}PWM_ModeTypeDef;
/**
* @brief Handle for PWM.
* @note Prefixes: h - handle, s - settings, f - flag
*/
typedef struct // PWM_HandleTypeDef
{
/* PWM VARIABLES */
PWM_ModeTypeDef sPWM_Mode; /* PWM Mode: 0 - DC mode, 1 - Table mode */
float PWM_Value; /* DC mode: PWM duty, Table mode: frequency*/
uint32_t PWM_MinPulseDur; /* minimum pulse duration for PWM in us*/
uint32_t PWM_DeadTime; /* dead-Time between switches half waves (channels) in us */
/* SETTINGS FOR TIMER */
TIM_SettingsTypeDef stim; /* settings for TIM */
TIM_OC_InitTypeDef sConfigOC; /* settings for oc channel */
unsigned fActiveChannel; /* flag for active oc channel: 0 - first channel, 1 - second channel */
uint16_t PWM_Channel1; /* instance of first channel */
uint16_t PWM_Channel2; /* instance of second channel */
/* VARIABLES FOR TABLE DUTY PARAMETERS */
uint32_t *pDuty_Table_Origin; /* pointer to table of pwm duties */
uint32_t Duty_Table_Size; /* size of duty table */
float Duty_Table_Ind; /* current ind of duty table */
float Duty_Table_Scale; /* scale for TIM ARR register */
/* SETTIGNS FOR PWM OUTPUT */
GPIO_TypeDef *GPIOx; /* GPIO port for PWM output */
uint32_t GPIO_PIN_X1; /* GPIO pin for PWM output */
uint32_t GPIO_PIN_X2; /* GPIO pin for PWM output (second half wave) */
/* SLAVES PWM */
void *hpwm2;
void *hpwm3;
}PWM_HandleTypeDef;
extern PWM_HandleTypeDef hpwm1;
/**
* @brief Handle for Slave PWM.
* @note Prefixes: h - handle, s - settings, f - flag
*/
typedef struct // PWM_SlaveHandleTypeDef
{
/* MASTER PWM*/
PWM_HandleTypeDef *hMasterPWM; /* master pwm handle */
/* SETTINGS FOR TIMER */
TIM_SettingsTypeDef stim; /* slave tim handle */
unsigned fActiveChannel; /* flag for active oc channel: 0 - first channel, 1 - second channel */
uint16_t PWM_Channel1; /* instance of first channel */
uint16_t PWM_Channel2; /* instance of second channel */
/* VARIABLES FOR TABLE DUTY PARAMETERS */
float Duty_Table_Ind; /* current ind of duty table */
float Duty_Shift_Ratio; /* Ratio of table shift: 0.5 shift - shift = Table_Size/2 */
/* SETTIGNS FOR PWM OUTPUT */
GPIO_TypeDef *GPIOx; /* GPIO port for PWM output */
uint32_t GPIO_PIN_X1; /* GPIO pin for PWM output */
uint32_t GPIO_PIN_X2; /* GPIO pin for PWM output (second half wave) */
}PWM_SlaveHandleTypeDef;
extern PWM_SlaveHandleTypeDef hpwm2;
extern PWM_SlaveHandleTypeDef hpwm3;
//--------------------------------PWM FUNCTIONS----------------------------------
/**
* @brief reInitialization of PWM TIM.
* @param hpwm - указатель на хендл ШИМ.
* @note Перенастраивает таймер согласно принятным настройкам в pwm_ctrl.
*/
void PWM_Sine_ReInit(PWM_HandleTypeDef *hpwm);
/**
* @brief Initialization of Slave PWM TIM.
* @param hspwm - указатель на хендл слейв ШИМ.
* @note Вызывает функции инициализации и включения слейв ШИМ.
*/
void PWM_SlavePhase_Init(PWM_SlaveHandleTypeDef *hspwm);
/**
* @brief reInitialization of Slave PWM TIM.
* @param hspwm - указатель на хендл слейв ШИМ.
* @note Перенастраивает таймер согласно принятным настройкам в pwm_ctrl.
*/
void PWM_SlavePhase_reInit(PWM_SlaveHandleTypeDef *hspwm);
/**
* @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);
/**
* @brief Calc and update new Duty Table Scale.
* @param hpwm - указатель на хендл ШИМ.
* @note Используется, когда изменяется значение регистра ARR.
*/
void PWM_Update_DutyTableScale(PWM_HandleTypeDef *hpwm);
//---------------------this called from TIM_PWM_Handler()------------------------
// MASTER PWM FUNCTIONS
/**
* @brief PWM Handler.
* @param hpwm - указатель на хендл ШИМ.
* @note Управляет скважность ШИМ в режиме PWM_TABLE.
* @note This called from TIM_PWM_Handler
*/
void PWM_Handler(PWM_HandleTypeDef *hpwm);
/**
* @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);
/**
* @brief Create Dead Time when switches channels.
* @param hpwm - указатель на хендл ШИМ.
* @param LocalDeadTimeCnt - указатель на переменную для отсчитывания дедтайма.
* @param LocalActiveChannel - указатель на переменную для отслеживания смены канала.
*/
void PWM_CreateDeadTime(PWM_HandleTypeDef *hpwm, float *LocalDeadTimeCnt, unsigned *LocalActiveChannel);
// SLAVE PWM FUNCTIONS
/**
* @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);
/**
* @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);
/**
* @brief Check is all Slave channels works properly.
* @param hspwm - указатель на хендл слейв ШИМ.
* @note Проверка работает ли только один из каналов, и проверка чтобы CCRx <= ARR
* @note В мастере проверка происходит напрямую в PWM_Handler.
*/
void PWM_SlavePhase_Check_Channels(PWM_SlaveHandleTypeDef *hspwm);
/**
* @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);
//---------------------this called from TIM_CTRL_Handler()-----------------------
/**
* @brief Update PWM parameters.
* @param hpwm - указатель на хендл ШИМ.
* @note Проверка надо ли обновлять параметры ШИМ, и если надо - обновляет их.
* @note This called from TIM_CTRL_Handler
*/
void Update_Params_For_PWM(PWM_HandleTypeDef *hpwm);
//---------------------------this called from main()-----------------------------
/**
* @brief First set up of PWM Two Channel.
* @note Первый инит ШИМ. Заполняет структуры и инициализирует таймер для генерации синуоидального ШИМ.
* Скважность ШИМ меняется по закону синусоиды, каждый канал генерирует свой полупериод синуса (от -1 до 0 И от 0 до 1)
* ШИМ генерируется на одном канале.
* @note This called from main OR by setted coil
*/
void PWM_Sine_FirstInit(void);
#endif // __PWM_H_

42
Code/PWM/settings.h Normal file
View File

@@ -0,0 +1,42 @@
// settings defines
#define HZ_TIMER_CTRL 400
#define HZ_TIMER_PWM 1000
// TIM PWM1 SETTINGS
#define TIMER_PWM_INSTANCE TIM4
#define TIMER_PWM_TICKBASE TIM_TickBase_1US
#define TIMER_PWM_AHB_FREQ 72
#define TIMER_PWM_TIM_CHANNEL1 TIM_CHANNEL_1
#define TIMER_PWM_TIM_CHANNEL2 TIM_CHANNEL_2
#define TIMER_PWM_GPIOx GPIOD
#define TIMER_PWM_GPIO_PIN_X1 GPIO_PIN_12
#define TIMER_PWM_GPIO_PIN_X2 GPIO_PIN_13
// TIM PWM2 SETTINGS
#define TIMER_PWM2_INSTANCE TIM3
#define TIMER_PWM2_TIM_CHANNEL1 TIM_CHANNEL_3
#define TIMER_PWM2_TIM_CHANNEL2 TIM_CHANNEL_4
#define TIMER_PWM2_GPIOx GPIOB
#define TIMER_PWM2_GPIO_PIN_X1 GPIO_PIN_0
#define TIMER_PWM2_GPIO_PIN_X2 GPIO_PIN_1
// TIM PWM3 SETTINGS
#define TIMER_PWM3_INSTANCE TIM1
#define TIMER_PWM3_TIM_CHANNEL1 TIM_CHANNEL_1
#define TIMER_PWM3_TIM_CHANNEL2 TIM_CHANNEL_2
#define TIMER_PWM3_GPIOx GPIOE
#define TIMER_PWM3_GPIO_PIN_X1 GPIO_PIN_9
#define TIMER_PWM3_GPIO_PIN_X2 GPIO_PIN_11
// TIM CTRL SETTINGS
#define TIMER_CTRL_TICKBASE TIM_TickBase_1US
#define TIMER_CTRL_AHB_FREQ 72
// PWM SETTINGS
#define SIN_TABLE_ORIGIN sin_table
#define SIN_TABLE_SIZE_MAX 1000