diplom/научка/code/pwm_motor_control/Interface/interface.c
2025-05-09 21:26:59 +03:00

518 lines
14 KiB
C

#include "interface.h"
#include "pwm.h"
TIM_SettingsTypeDef TIM_ENCODER = {0};
extern I2C_HandleTypeDef hi2c1;
LCDI2C_HandleTypeDef hlcd1;
TIM_EncoderTypeDef henc1;
extern PWM_HandleTypeDef hpwm1;
Interface_HandleTypeDef hinterface;
/**
* @brief Read encoder function.
* @param henc - хендл энкодера.
* @param hlcd - хендл LCD-дисплея.
* @note This called from freertos MainTask thread.
*/
void ReadEncoder(TIM_EncoderTypeDef *henc, LCDI2C_HandleTypeDef *hlcd)
{
uint8_t *menu = (uint8_t *)&hinterface.MenuItems;
uint8_t *disp_menu = (uint8_t *)&hinterface.DisplayingItems;
//-----------READ SWITCH-------------
if(hinterface.FineChanging == 0) // если кнопка энкодера не в режиме точной настройки
{
if((~henc->GPIOx->IDR)&henc->GPIO_PIN_SW) // ожидание нажатия кнопки
{
osDelay(50);
if(((~henc->GPIOx->IDR)&henc->GPIO_PIN_SW )&& // если кнопка действительно нажата
(HAL_GetTick() - hinterface.Switch_prevTick > hinterface.DoubleClick_Timeout) ) // и это не второй клик даблклика (слишком большая задержка после предыдущего нажатия)
{
hinterface.Encoder_Shdw = henc->htim->Instance->CNT;
while((~henc->GPIOx->IDR)&henc->GPIO_PIN_SW) // ожидаем её отпускания
{
//------------FINE TUNE--------------
if (hinterface.Encoder_Shdw != henc->htim->Instance->CNT) // если кнопка не отпускается, и енкодер начинает регулироваться
{
hinterface.FineChanging = 1; // переходим в режим точной настройки
break; // выход из цикла
}
osDelay(1);
}
//-----------SWITCH PARAM------------
if(hinterface.FineChanging == 0) // если энкодер не перешел в режим точной настройки
{
hinterface.Switch_prevTick = HAL_GetTick(); // сохраняем время отжатия для регистрации даблклика если он будет
hinterface.CurrentSelection++; // переключаем режим
// skip item if it isnt displayin, until run out of items
while((*disp_menu&(1<<(hinterface.CurrentSelection-1))) == 0)
{
hinterface.CurrentSelection++;
if(hinterface.MenuNumber == 0) // if menu for params (registers)
{
if(hinterface.CurrentSelection > Menu_Size)
{
hinterface.CurrentSelection = NOTHING_SELECTED;
break;
}
}
else // if menu for modes (coils)
{
if(hinterface.CurrentSelection - NOTHING_COIL_SELECTED > Menu_Coil_Size)
{
hinterface.CurrentSelection = NOTHING_COIL_SELECTED;
break;
}
}
}
if(hinterface.MenuNumber == 0)
{
if(hinterface.CurrentSelection > (Menu_Size))
hinterface.CurrentSelection = NOTHING_SELECTED;
}
else
{
if((hinterface.CurrentSelection - NOTHING_COIL_SELECTED) > (Menu_Coil_Size))
hinterface.CurrentSelection = NOTHING_COIL_SELECTED;
}
}
}
else if(((~henc->GPIOx->IDR)&henc->GPIO_PIN_SW )&& // если кнопка действительно нажата
(HAL_GetTick() - hinterface.Switch_prevTick < hinterface.DoubleClick_Timeout) ) // и это не второй клик даблклика (слишком большая задержка после предыдущего нажатия)
{
hinterface.MenuNumber++;
if(hinterface.MenuNumber >= 2)
{
hinterface.MenuNumber = 0;
}
if (hinterface.MenuNumber == 0)
hinterface.CurrentSelection = NOTHING_SELECTED;
else if (hinterface.MenuNumber == 1)
hinterface.CurrentSelection = NOTHING_COIL_SELECTED;
}
}
}
static uint16_t coef_value;
if(hinterface.FineChanging == 0) // если енкодер не регулируется
coef_value = 100;
else
coef_value = 2;
int16_t encoder_diff = (int32_t)henc->htim->Instance->CNT - hinterface.Encoder_Shdw;
int32_t tmpreg;
switch(hinterface.CurrentSelection)
{
case PWM_VALUE_SELECTED:
tmpreg = pwm_ctrl[R_PWM_CTRL_PWM_VALUE];
tmpreg += ((encoder_diff*coef_value)/2);
if (tmpreg >= 0)
pwm_ctrl[R_PWM_CTRL_PWM_VALUE] = tmpreg;
else
pwm_ctrl[R_PWM_CTRL_PWM_VALUE] = 0;
break;
case PWM_HZ_SELECTED:
tmpreg = pwm_ctrl[R_PWM_CTRL_PWM_HZ];
tmpreg += (encoder_diff*coef_value)/2;
if (tmpreg >= 0)
pwm_ctrl[R_PWM_CTRL_PWM_HZ] = tmpreg;
else
pwm_ctrl[R_PWM_CTRL_PWM_HZ] = 0;
break;
case PWM_DUTYBRIDGE_SELECTED:
tmpreg = pwm_ctrl[R_PWM_CTRL_DUTY_BRIDGE];
tmpreg += ((encoder_diff*coef_value)/2);
if (tmpreg < 0)
pwm_ctrl[R_PWM_CTRL_DUTY_BRIDGE] = 0;
else if (tmpreg > 10000)
pwm_ctrl[R_PWM_CTRL_DUTY_BRIDGE] = 10000;
else
pwm_ctrl[R_PWM_CTRL_DUTY_BRIDGE] = tmpreg;
break;
case PWM_COIL_DC_SELECTED:
if(encoder_diff)
hpwm1.sPWM_Config.PWM_Mode->DC = ~hpwm1.sPWM_Config.PWM_Mode->DC;
break;
case PWM_COIL_BRIDGE_SELECTED:
if(encoder_diff)
hpwm1.sPWM_Config.PWM_Mode->BRIDGE = ~hpwm1.sPWM_Config.PWM_Mode->BRIDGE;
break;
case PWM_COIL_PHASE_SELECTED:
if(encoder_diff)
hpwm1.sPWM_Config.PWM_Mode->PHASE = ~hpwm1.sPWM_Config.PWM_Mode->PHASE;
break;
case PWM_COIL_POLARITY_SELECTED:
if(encoder_diff)
hpwm1.sPWM_Config.PWM_Mode->POLARITY = ~hpwm1.sPWM_Config.PWM_Mode->POLARITY;
break;
case NOTHING_SELECTED:
if(encoder_diff > 0)
hinterface.StartMenuItem += 1;
else if(encoder_diff < 0)
hinterface.StartMenuItem -= 1;
if(hinterface.StartMenuItem < 0)
hinterface.StartMenuItem = 0;
break;
case NOTHING_COIL_SELECTED:
if(encoder_diff > 0)
hinterface.StartMenuItem += 1;
else if(encoder_diff < 0)
hinterface.StartMenuItem -= 1;
if(hinterface.StartMenuItem < 0)
hinterface.StartMenuItem = 0;
break;
default: break;
}
hinterface.Encoder_Shdw = henc->htim->Instance->CNT;
if(hinterface.FineChanging == 1) // если некодер в режиме точной настройки)
{
if(((~henc->GPIOx->IDR)&henc->GPIO_PIN_SW) == 0) // ожидание отпускания кнопки
{
osDelay(50);
if(((~henc->GPIOx->IDR)&henc->GPIO_PIN_SW) == 0) // если кнопка действительно нажата
{
hinterface.FineChanging = 0; // отключение режима точной настройки
}
}
}
}
void UpdateInterfaceStruct(void)
{
if(hinterface.MenuNumber == 0)
{
if(hpwm1.sPWM_Config.PWM_Mode->BRIDGE && hpwm1.sPWM_Config.PWM_Mode->DC)
hinterface.MenuItems.PWM_DutyBridge = 1;
else
hinterface.MenuItems.PWM_DutyBridge = 0;
hinterface.MenuItems.PWM_Hz = 1;
hinterface.MenuItems.PWM_Value = 1;
}
else
{
hinterface.MenuItems.PWM_DC = 1;
hinterface.MenuItems.PWM_BRIDGE = 1;
hinterface.MenuItems.PWM_PHASE = 1;
hinterface.MenuItems.PWM_POLARITY = 1;
}
}
void UpdateLCDDisplay(LCDI2C_HandleTypeDef *hlcd)
{
LCD_Check(hlcd);
// clear display
LCD_Send_CMD(hlcd, 0x01);
osDelay(hlcd->ClearDelay);
uint8_t *menu = (uint8_t *)&hinterface.MenuItems;
int8_t disp_cnt = 0;
disp_cnt -= hinterface.StartMenuItem;
hinterface.DisplayingItems = (const Menu_HandleTypeDef){0};
if(hinterface.MenuNumber == 0)
disp_cnt = LCD_DisplayMenuRegisters(hlcd, menu, disp_cnt);
else
disp_cnt = LCD_DisplayMenuCoils(hlcd, menu, disp_cnt);
// if less than 2 item displayed - decrease start menu item
if(disp_cnt <= 1) hinterface.StartMenuItem--;
osDelay(hlcd->DisplayDelay);
}
uint8_t LCD_DisplayMenuCoils(LCDI2C_HandleTypeDef *hlcd, uint8_t *menu, int8_t disp_cnt)
{
for(int i = 0; i < Menu_Coil_Size; i++)
{
// if first sring is printed
if (disp_cnt%2 == 0)
LCD_Send_CMD(hlcd, 0x80); // set cursor to second string
// if first sring is printed
if (disp_cnt%2 == 1)
LCD_Send_CMD(hlcd, 0xC0); // set cursor to second string
switch(*menu&(1<<(i+NOTHING_COIL_SELECTED)))
{
case Menu_PWM_DC:
disp_cnt++;
if(disp_cnt > 0)
{
hinterface.DisplayingItems.PWM_DC = 1;
if(hinterface.CurrentSelection == NOTHING_COIL_SELECTED)
Display_PWM_DC(hlcd);
else if (hinterface.CurrentSelection == PWM_COIL_DC_SELECTED)
Display_PWM_DC(hlcd);
}
break;
case Menu_PWM_BRIDGE:
disp_cnt++;
if(disp_cnt > 0)
{
hinterface.DisplayingItems.PWM_BRIDGE = 1;
if(hinterface.CurrentSelection == NOTHING_COIL_SELECTED)
Display_PWM_BRIDGE(hlcd);
else if (hinterface.CurrentSelection == PWM_COIL_BRIDGE_SELECTED)
Display_PWM_BRIDGE(hlcd);
}
break;
case Menu_PWM_PHASE:
disp_cnt++;
if(disp_cnt > 0)
{
hinterface.DisplayingItems.PWM_PHASE = 1;
if(hinterface.CurrentSelection == NOTHING_COIL_SELECTED)
Display_PWM_PHASE(hlcd);
else if (hinterface.CurrentSelection == PWM_COIL_PHASE_SELECTED)
Display_PWM_PHASE(hlcd);
}
break;
case Menu_PWM_POLARITY:
disp_cnt++;
if(disp_cnt > 0)
{
hinterface.DisplayingItems.PWM_POLARITY = 1;
if(hinterface.CurrentSelection == NOTHING_COIL_SELECTED)
Display_PWM_POLARITY(hlcd);
else if (hinterface.CurrentSelection == PWM_COIL_POLARITY_SELECTED)
Display_PWM_POLARITY(hlcd);
}
break;
}
// if second sring is printed
if (disp_cnt >= 2)
break; // stop "displaying"
}
return disp_cnt;
}
uint8_t LCD_DisplayMenuRegisters(LCDI2C_HandleTypeDef *hlcd, uint8_t *menu, int8_t disp_cnt)
{
for(int i = 0; i < Menu_Size; i++)
{
// if first sring is printed
if (disp_cnt%2 == 0)
LCD_Send_CMD(hlcd, 0x80); // set cursor to second string
// if first sring is printed
if (disp_cnt%2 == 1)
LCD_Send_CMD(hlcd, 0xC0); // set cursor to second string
switch(*menu&(1<<i))
{
case Menu_PWM_Value:
disp_cnt++;
if(disp_cnt > 0)
{
hinterface.DisplayingItems.PWM_Value = 1;
if(hinterface.CurrentSelection == NOTHING_SELECTED)
Display_PWM_Value(hlcd);
else if (hinterface.CurrentSelection == PWM_VALUE_SELECTED)
Display_PWM_Value(hlcd);
}
break;
case Menu_PWM_Hz:
disp_cnt++;
if(disp_cnt > 0)
{
hinterface.DisplayingItems.PWM_Hz = 1;
if(hinterface.CurrentSelection == NOTHING_SELECTED)
Display_PWM_Hz(hlcd);
else if (hinterface.CurrentSelection == PWM_HZ_SELECTED)
Display_PWM_Hz(hlcd);
}
break;
case Menu_PWM_DutyBridge:
disp_cnt++;
if(disp_cnt > 0)
{
hinterface.DisplayingItems.PWM_DutyBridge = 1;
if(hinterface.CurrentSelection == NOTHING_SELECTED)
Display_PWM_DutyBridge(hlcd);
else if (hinterface.CurrentSelection == PWM_DUTYBRIDGE_SELECTED)
Display_PWM_DutyBridge(hlcd);
}
break;
}
// if second sring is printed
if (disp_cnt >= 2)
break; // stop "displaying"
}
return disp_cnt;
}
void Display_PWM_Value(LCDI2C_HandleTypeDef *hlcd)
{
if(hpwm1.sPWM_Mode&PWM_DC_MODE && ((hpwm1.sPWM_Mode&PWM_BRIDGE_MODE) == 0)) // in dc mode disp pwm duty
{
LCD_Send_STRING(hlcd, "Duty: ");
LCD_Send_INT(hlcd, pwm_ctrl[R_PWM_CTRL_PWM_VALUE]/100, 0);
LCD_Send_STRING(hlcd, ".");
LCD_Send_INT(hlcd, pwm_ctrl[R_PWM_CTRL_PWM_VALUE]%100, 2);
LCD_Send_STRING(hlcd, "%");
}
else // in dc mode disp sine freq
{
LCD_Send_STRING(hlcd, "Sine: ");
LCD_Send_INT(hlcd, pwm_ctrl[R_PWM_CTRL_PWM_VALUE]/100, 0);
LCD_Send_STRING(hlcd, ".");
LCD_Send_INT(hlcd, pwm_ctrl[R_PWM_CTRL_PWM_VALUE]%100, 2);
LCD_Send_STRING(hlcd, " Hz");
}
}
void Display_PWM_Hz(LCDI2C_HandleTypeDef *hlcd)
{
LCD_Send_STRING(hlcd, "PWM: ");
LCD_Send_INT(hlcd, pwm_ctrl[R_PWM_CTRL_PWM_HZ], 0);
LCD_Send_STRING(hlcd, " Hz");
}
void Display_PWM_DutyBridge(LCDI2C_HandleTypeDef *hlcd)
{
LCD_Send_STRING(hlcd, "Duty: ");
LCD_Send_INT(hlcd, pwm_ctrl[R_PWM_CTRL_DUTY_BRIDGE]/100, 0);
LCD_Send_STRING(hlcd, ".");
LCD_Send_INT(hlcd, pwm_ctrl[R_PWM_CTRL_DUTY_BRIDGE]%100, 2);
LCD_Send_STRING(hlcd, "%");
}
void Display_PWM_DC(LCDI2C_HandleTypeDef *hlcd)
{
LCD_Send_STRING(hlcd, "DC: ");
if(hpwm1.sPWM_Config.PWM_Mode->DC)
LCD_Send_STRING(hlcd, "DC");
else
LCD_Send_STRING(hlcd, "Sine");
}
void Display_PWM_BRIDGE(LCDI2C_HandleTypeDef *hlcd)
{
LCD_Send_STRING(hlcd, "Bridge: ");
if(hpwm1.sPWM_Config.PWM_Mode->BRIDGE)
LCD_Send_STRING(hlcd, "Bridge");
else
LCD_Send_STRING(hlcd, "Single");
}
void Display_PWM_PHASE(LCDI2C_HandleTypeDef *hlcd)
{
LCD_Send_STRING(hlcd, "Phase: ");
if(hpwm1.sPWM_Config.PWM_Mode->PHASE)
LCD_Send_STRING(hlcd, "3-Phase");
else
LCD_Send_STRING(hlcd, "One Phase");
}
void Display_PWM_POLARITY(LCDI2C_HandleTypeDef *hlcd)
{
LCD_Send_STRING(hlcd, "Polarity: ");
if(hpwm1.sPWM_Config.PWM_Mode->POLARITY)
LCD_Send_STRING(hlcd, "Neg");
else
LCD_Send_STRING(hlcd, "Pos");
}
/**
* @brief First initialization of Encoder Timer.
* @note This called from main
*/
void EncoderFirstInit(void)
{
hinterface.DoubleClick_Timeout = 200;
// tim settings
TIM_ENCODER.htim.Instance = TIMER_ENCODER_INSTANCE;
TIM_ENCODER.htim.Init.Period = 0xFFFF;
TIM_ENCODER.htim.Init.Prescaler = 0;
TIM_ENCODER.sTimMode = TIM_IT_MODE;
// TIM_ENCODER.sTickBaseMHz = TIMER_ENCODER_TICKBASE;
// TIM_ENCODER.sTimAHBFreqMHz = TIMER_ENCODER_AHB_FREQ;
// TIM_ENCODER.sTimFreqHz = 0;
TIM_Base_Init(&TIM_ENCODER);
henc1.sConfig.EncoderMode = TIM_ENCODERMODE_TI2;
henc1.sConfig.IC1Polarity = TIM_INPUTCHANNELPOLARITY_FALLING;
henc1.sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
henc1.sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
henc1.sConfig.IC1Filter = 5;
henc1.sConfig.IC2Polarity = TIM_INPUTCHANNELPOLARITY_FALLING;
henc1.sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
henc1.sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
henc1.sConfig.IC2Filter = 5;
henc1.GPIOx = TIMER_ENCODER_PORT;
henc1.GPIO_PIN_TI1 = TIMER_ENCODER_PIN1;
henc1.GPIO_PIN_TI2 = TIMER_ENCODER_PIN2;
henc1.GPIO_PIN_SW = TIMER_ENCODER_PIN_SW;
TIM_Encoder_Init(&henc1, &TIM_ENCODER.htim);
HAL_TIM_Base_Start(&TIM_ENCODER.htim);
}
/**
* @brief First initialization of LCD.
* @note This called from main
*/
void LCD_FirstInit(void)
{
hlcd1.hi2c = &hi2c1;
hlcd1.DisplayDelay = 250;
hlcd1.ClearDelay = 10;
LCD_Init(&hlcd1);
}