PROGCONT.RU

Форма входа







Регистрпция Вход/

STM8S Простой частотник для трехфазного асинхронного двигателя.

Ссылка на видео в YouTubeНаписание данной статьи подтолкнуло просьба моего зрителя на YouTube и конечно личная нужда в трехфазном частотнике для асинхронного двигателя на фрезерный станок. Как говорится убьем два зайца сразу, напишу статью и будет работать станок.
 Для меня частотник должен быть максимально прост и дешевым по этому отсутствует защита от замыкания, система торможения и многие другие излишества которые предлагают нам промышленные, но присутствует защита диодного моста от больших токов во время включения устройства(заряд конденсаторной батареи) в сеть, в виде реле и ограничивающих резисторов.
 Тут выложу свою схему устройства и программу, но программа будет минимальной, простая индикация на TM1638, включение двигателя и смена вращения, для себя конечно я постараюсь сделать более продвинутое устройство которое продемонстрирую в другом видео.

Если заинтересуетесь созданием данного устройства то используйте мою программу для расчёта синуса ШИМ «Расчет таблицы SIN для управления асинхронным трехфазным двигателем» в разделе «Программы».

Начнем со схемы устройства, которая ниже.

нет рисунканет рисунка

 Особенности схемы, резисторы R14-R15 должны быть не менее 2W, конденсаторы C2-C3-C4 SMD 1206 не менее 25V, C12-C13 не менее 400V, вывод J4 для подключения конденсатора не менее 400V(емкость зависит от мощности двигателя, примерно 1 киловатт-1000 микрофарад), J2 питание может быть от 12-16V, логика питается через стабилизатор U2, в остальном используем компоненты с допустимым напряжением не ниже питающего. Транзисторы IGBT FGA25N120antd можно заменить на подходящие, лишь бы допустимое напряжение было не ниже 400V, хорошо подойдут мосфеты.

 Теперь как мы будем управлять через ШИМ трехфазным движком, происходить это будет с помощью комплементарных выводов TIM1, которые в свою очередь управляются через регистры Capture/Compare 1 Register, Capture/Compare 2 Register, Capture/Compare 3 Register далее как А-В-С, у каждого регистра есть пара выводов(верхний нижний ключ) которые связаны между собой, когда на одном высокий уровень на другом низкий, за исключением случая Deadtime(оба закрыты). Управление происходит с помощью изменения величины импульса через функции TIM1_SetCompare1, TIM1_SetCompare2, TIM1_SetCompare3. Уникальность заключается в создании искусственного нуля делается это просто, например у меня период TIM1 равен 1904 делим по полам, получаем 952 это и будет искусственный ноль, потому что если мы установим на А-В-С одновременно эту продолжительность импульса то все верхние и нижние ключи будут работать одинаково, токов в обмотках возникать не будет. Вот например давайте в А оставим импульс 952, в В 1776 и С 128 то ток будет течь из В в С 1776-128=1648 части периода, из В в А 1776-952=824 и из А в С 952-128=824 и последние из C токи не вытекают только в тикают потому что в основном открыт нижний ключ.

нет рисунка

 Вот так меняя импульс ШИМ для верхнего и нижнего ключа будут меняться токи и их направленность. Как вы наверное знаете а если нет то интернет в помощь, что фазы смещены на 120 градусов(это 360 деленная на 120 будет 3) так и у меня массив синуса в котором все фазы ШИМ смещены на 14 относительно друг друга(в массиве 42 значения, значит 42/3=14 настолько смещены фазы).

 Осталась сама программа, в которой настроен TIM1 для вращения с частотой 50Hz для этого устанавливаем частоту тактирования контроллера 16MHz, период TIM1 будет 1904 и пред делитель равный 4 и так как массив синуса состоит из 42 значения получаем 1904*4*42*(1/16000000)=0.019992 миллисекунд что и равно 1/50Hz. Ниже опишу две важные функции это установка мертвого времени и настройка комплементарных выводов.

TIM1_BDTRConfig( TIM1_OSSISTATE_DISABLE, TIM1_LOCKLEVEL_OFF, 16, TIM1_BREAK_DISABLE, TIM1_BREAKPOLARITY_LOW, TIM1_AUTOMATICOUTPUT_DISABLE);

 Основное назначение этой функции установка состояния выводов при возникновении короткого замыкания и как возвращать их в рабочее состояние если есть защита, которой у нас нет по этому нас интересует только цифра 16 что значит задержка между переключением верхнего и нижнего ключа будет 1 микросекунда, в datasheet смотрите как его правильно установить Deadtime.
 Вторая функция.

TIM1_OC1Init( TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, 0, TIM1_OCPOLARITY_HIGH, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET, TIM1_OCNIDLESTATE_SET);

 Первый параметр TIM1_OCMODE_PWM1-режим включения, выключение верхнего и нижнего ключа(если импульс больше нуля включается верхний ключ после достижения счетчика размеру импульса отключается верхний ключ включается нижний и наоборот).
TIM1_OUTPUTSTATE_ENABLE-включаем выводы верхних ключей.
TIM1_OUTPUTNSTATE_ENABLE-включаем выводы нижних ключей.
Четвертый параметр размер импульса, тут у меня 0 по этому будут включены нижние ключи, пока не изменим этот параметр.
TIM1_OCPOLARITY_HIGH-полярность для верхнего ключа так как управляем через ir2101 должна быть положительна.
TIM1_OCNPOLARITY_HIGH-также полярность для нижнего ключа.
TIM1_OCIDLESTATE_SET-включаем Deadtime для верхних ключей.
TIM1_OCNIDLESTATE_SET-включаем Deadtime для нижних ключей.


Внимание! Данные примеры программ можно использовать на двигателях с мощностью не более 180 W, сопротивлением обмотки не меньше 40 Ом то есть пусковые токи не должны превышать параметры силовых ключей.


 Пример пуска аналогичного устройства с отключённой защитой по току на двигателе 2.2 kW и конечно плачевный результат, от большого пускового тока взорвались силовые транзисторы.

нет рисунка


Для пуска на более мощных двигателях вы должны ограничить пусковые токи уменьшив импульс ШИМ или дополнить код программы своей разработкой плавного пуска двигателя.

#include "stm8s.h"
void send_command(uint8_t com);
void send_data(uint8_t *send_data_display);
void display_data(uint32_t multiplier);
uint8_t scan_button(void);
void pause(uint32_t p);
uint8_t result=0;
//Наша таблица синуса, 42 результата.
uint16_t arr_pwm[42]={952 ,1094 ,1233 ,1365 ,1488 ,1600 ,1696 ,1776 ,1838 ,1880 ,
1901 ,1901 ,1880 ,1838 ,1776 ,1696 ,1600 ,1488 ,1365 ,1233 ,
1094 ,952 ,810 ,671 ,539 ,416 ,304 ,208 ,128 ,66 ,
24 ,3 ,3 ,24 ,66 ,128 ,208 ,304 ,416 ,539 ,671 ,810};
//Счетчик значения фаз, А В С.
uint8_t count_A=0;
uint8_t count_B=14;
uint8_t count_C=28;
//Статус режима работы 0-стоим 1 и 2-направление вращения.
uint8_t status_pwm=0;
uint8_t send_data_display[16];
uint8_t symbol[10]={ 63, 6, 91, 79, 102, 109, 125, 7, 127, 111};
int main( void ){
CLK_HSIPrescalerConfig( CLK_PRESCALER_HSIDIV1);
//Настраиваем порты для SPI.
GPIO_Init( GPIOC, GPIO_PIN_5, GPIO_MODE_OUT_OD_HIZ_FAST);
GPIO_Init( GPIOC, GPIO_PIN_7, GPIO_MODE_OUT_OD_HIZ_FAST);
SPI_Init( SPI_FIRSTBIT_LSB, SPI_BAUDRATEPRESCALER_8, SPI_MODE_MASTER, SPI_CLOCKPOLARITY_LOW, SPI_CLOCKPHASE_1EDGE, SPI_DATADIRECTION_1LINE_TX, SPI_NSS_SOFT, 0x07);
SPI_Cmd( ENABLE);
//Настраиваем выводы для ШИМ и TIM1 для управления двигателем.
GPIO_Init( GPIOC, GPIO_PIN_1, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init( GPIOC, GPIO_PIN_2, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init( GPIOC, GPIO_PIN_3, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init( GPIOB, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init( GPIOB, GPIO_PIN_1, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init( GPIOB, GPIO_PIN_2, GPIO_MODE_OUT_PP_LOW_FAST);
TIM1_TimeBaseInit( 3, TIM1_COUNTERMODE_UP, 1904, TIM1_OPMODE_REPETITIVE);
TIM1_BDTRConfig( TIM1_OSSISTATE_DISABLE, TIM1_LOCKLEVEL_OFF, 16, TIM1_BREAK_DISABLE, TIM1_BREAKPOLARITY_LOW, TIM1_AUTOMATICOUTPUT_DISABLE);
TIM1_OC1Init( TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, 0, TIM1_OCPOLARITY_HIGH, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET, TIM1_OCNIDLESTATE_SET);
TIM1_OC2Init( TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, 0, TIM1_OCPOLARITY_HIGH, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET, TIM1_OCNIDLESTATE_SET);
TIM1_OC3Init( TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, 0, TIM1_OCPOLARITY_HIGH, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET, TIM1_OCNIDLESTATE_SET);
TIM1_ClearFlag( TIM1_FLAG_UPDATE);
TIM1_ITConfig( TIM1_IT_UPDATE , ENABLE);
TIM1_OC1PreloadConfig( ENABLE);
TIM1_OC2PreloadConfig( ENABLE);
TIM1_OC3PreloadConfig( ENABLE);
TIM1_CtrlPWMOutputs( ENABLE);
enableInterrupts();
TIM1_Cmd( ENABLE);
//Ожидаем когда зарядятся конденсаторы через ограничительные резисторы, включаем реле.
pause(200000);
GPIO_Init( GPIOA, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
while(1){
send_command(140);
send_command(64);
//Отображаем статус работы.
display_data(50+status_pwm);
pause(100);
result=scan_button();
pause(100);
if( result!=scan_button()){ result=0;} //Проверяем нажатие кнопок, меняем статус.
if( result!=0){
if( result==1){ status_pwm=0;}
if( result==2){ status_pwm=1;}
if( result==4){ status_pwm=2;}
}
}
}
//Тут происходит основное действие, проверяем статус если не ноль то двигатель крутится.
 INTERRUPT_HANDLER( TIM1_UPD_OVF_TRG_BRK_IRQHandler, 11)
 {
 if(status_pwm>0){
//Выбераем вращение.
   if( status_pwm==1){
    TIM1_SetCompare1( arr_pwm[count_A]);
    TIM1_SetCompare2( arr_pwm[count_B]);
    TIM1_SetCompare3( arr_pwm[count_C]);
    }
   if( status_pwm==2){
    TIM1_SetCompare1( arr_pwm[count_A]);
    TIM1_SetCompare2( arr_pwm[count_C]);
    TIM1_SetCompare3( arr_pwm[count_B]);
   }
//Прибавляем счетчики фаз.
   count_A++;
   count_B++;
   count_C++;
//Если счетчик больше массива устанавливаем ноль.
    if( count_A==42){ count_A=0;}
    if( count_B==42){ count_B=0;}
    if( count_C==42){ count_C=0;}
//Если статус равен нулю то выключаем двигатель.
    }else{ TIM1_SetCompare1(0); TIM1_SetCompare2(0); TIM1_SetCompare3(0);}
   TIM1_ClearFlag( TIM1_FLAG_UPDATE);
 }

void send_command( uint8_t com){
GPIO_WriteLow( GPIOC, GPIO_PIN_7);
SPI_SendData( com);
while( ( SPI->SR & SPI_FLAG_BSY) );
GPIO_WriteHigh( GPIOC, GPIO_PIN_7);
}
uint8_t scan_button(void){
uint8_t scan_result;
GPIO_WriteLow( GPIOC, GPIO_PIN_7);
SPI_SendData( 66);
while( ( SPI->SR & SPI_FLAG_BSY) );
SPI_BiDirectionalLineConfig( SPI_DIRECTION_RX);
while( !( SPI->SR & SPI_FLAG_RXNE) );
scan_result=SPI->DR;
while( !( SPI->SR & SPI_FLAG_RXNE) );
scan_result|=( SPI->DR<<1);
while( !( SPI->SR & SPI_FLAG_RXNE) );
scan_result|=( SPI->DR<<2);
while( !( SPI->SR & SPI_FLAG_RXNE) );
scan_result|=( SPI->DR<<3);
GPIO_WriteHigh( GPIOC, GPIO_PIN_7);
SPI_BiDirectionalLineConfig( SPI_DIRECTION_TX);
return scan_result;
}
void send_data( uint8_t *send_data_display){
GPIO_WriteLow( GPIOC, GPIO_PIN_7);
SPI_SendData( 192);
while( ( SPI->SR & SPI_FLAG_BSY) );
for( uint8_t i=0;i<16; i++){
SPI_SendData( send_data_display[i]);
while( !( SPI->SR & SPI_FLAG_TXE) );
}
while( ( SPI->SR & SPI_FLAG_BSY) );
GPIO_WriteHigh( GPIOC, GPIO_PIN_7);
} void display_data( uint32_t multiplier){
multiplier= multiplier-(multiplier/10000000*10000000);
multiplier=multiplier-(multiplier/1000000*1000000);
multiplier= multiplier-(multiplier/100000*100000);
multiplier= multiplier-(multiplier/10000*10000);
multiplier= multiplier-(multiplier/1000*1000);
multiplier= (multiplier-multiplier/100*100);
send_data_display[12]= symbol[multiplier/10]|128;
multiplier= multiplier-(multiplier/10*10);
send_data_display[14]= symbol[multiplier];
send_data(send_data_display);
}
void pause( uint32_t p){
for( uint32_t i=0; i<p; i++){}
}
#ifdef USE_FULL_ASSERT
void assert_failed( uint8_t* file, uint32_t line)
{
while (1){}
}
#endif

 И еще программа из которой я убрал все лишнее, работает просто, включаем в розетку устройство и двигатель начинает вращаться с частотой 50Hz.

#include "stm8s.h"
void pause(uint32_t p){for(uint32_t i=0; i<p; i++){}}
uint16_t arr_pwm[42]={952 ,1094 ,1233 ,1365 ,1488 ,1600 ,1696 ,1776 ,1838 ,1880 ,
1901 ,1901 ,1880 ,1838 ,1776 ,1696 ,1600 ,1488 ,1365 ,1233 ,
1094 ,952 ,810 ,671 ,539 ,416 ,304 ,208 ,128 ,66 ,
24 ,3 ,3 ,24 ,66 ,128 ,208 ,304 ,416 ,539 ,671 ,810};
uint8_t count_A=0;
uint8_t count_B=14;
uint8_t count_C=28;
int main( void ){
CLK_HSIPrescalerConfig( CLK_PRESCALER_HSIDIV1);
GPIO_Init(GPIOC, GPIO_PIN_1, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOC, GPIO_PIN_2, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOC, GPIO_PIN_3, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOB, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOB, GPIO_PIN_1, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOB, GPIO_PIN_2, GPIO_MODE_OUT_PP_LOW_FAST);
TIM1_TimeBaseInit( 3, TIM1_COUNTERMODE_UP, 1904, TIM1_OPMODE_REPETITIVE);
TIM1_BDTRConfig( TIM1_OSSISTATE_DISABLE, TIM1_LOCKLEVEL_OFF, 16, TIM1_BREAK_DISABLE, TIM1_BREAKPOLARITY_LOW, TIM1_AUTOMATICOUTPUT_DISABLE);
TIM1_OC1Init( TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, 0, TIM1_OCPOLARITY_HIGH, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET, TIM1_OCNIDLESTATE_SET);
TIM1_OC2Init( TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, 0, TIM1_OCPOLARITY_HIGH, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET, TIM1_OCNIDLESTATE_SET);
TIM1_OC3Init( TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, 0, TIM1_OCPOLARITY_HIGH, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET, TIM1_OCNIDLESTATE_SET);
TIM1_ClearFlag( TIM1_FLAG_UPDATE);
TIM1_ITConfig( TIM1_IT_UPDATE , ENABLE);
TIM1_OC1PreloadConfig( ENABLE);
TIM1_OC2PreloadConfig( ENABLE);
TIM1_OC3PreloadConfig( ENABLE);
TIM1_CtrlPWMOutputs( ENABLE);
enableInterrupts();
TIM1_Cmd( ENABLE);
pause( 200000);
GPIO_Init( GPIOA, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
while(1){
//Тут ваша программа.
}
}
INTERRUPT_HANDLER( TIM1_UPD_OVF_TRG_BRK_IRQHandler, 11)
{
TIM1_SetCompare1( arr_pwm[count_A]);
TIM1_SetCompare2( arr_pwm[count_B]);
TIM1_SetCompare3( arr_pwm[count_C]);
count_A++;
count_B++;
count_C++;
if(count_A==42){ count_A=0;}
if(count_B==42){ count_B=0;}
if(count_C==42){ count_C=0;}
TIM1_ClearFlag( TIM1_FLAG_UPDATE);
}
#ifdef USE_FULL_ASSERT
void assert_failed( uint8_t* file, uint32_t line)
{
while (1){}
}
#endif
ВНИМАНИЕ ОПАСНОЕ НАПРЯЖЕНИЕ!!!
Для пробного пуска используйте блок питания около 40 вольт и маломощный двигатель, если все пройдет удачно подключайте к 220в, очень не приятно когда взрываются транзисторы, удачи.

Комментарии  Только зарегистрированные пользователи могут оставлять комментарии!

NOteseiaaw   2019-02-27 18:56:27
50 таблиц многовато, что бы помочь насчет ошибки нужен код программы!

NOOleg   2019-02-28 11:56:25
Решил записывать массив динамически То есть на ходу.

NOOleg   2019-03-02 12:59:38
Ни где не могу найти какое количество точек оптимально ? Сейчас использую 510.Пробовал 1020 но синусоида красивее не стала . Как считаете сколько оптимально ?

NOteseiaaw   2019-03-03 23:34:15
Что такое точки, если имеете в виду период то он зависит от максимальной скорости вращения, все надо высчитывать а не пробывать разные значения.

NOOleg   2019-03-04 12:20:49
Вот это uint16_t arr_pwm[42] у меня 510. 3, TIM1_COUNTERMODE_UP, 1904, Таймер 1020. Как я понял главное чтоб делилось на три без остатка .

NOteseiaaw   2019-03-04 21:50:35
Олег какой у вас контроллер? Должно делится на три и быть четным обязательно.

NOOleg   2019-03-05 09:28:17
Отлажеваю на stm32 f411 но планирую собирать на stm32f103 . С синусом разобрался все отлично но тут почитал что нужен векторный шим , не подскажите нужен ли он ? И как его реализовать на stm32?

NOteseiaaw   2019-03-05 23:41:21
Здесь в примере скалярное управление, векторное управление сложней из за обратной связи с помощью которой контроллер должен на ходу вычислять токи в обмотка и их направление.

NOOleg   2019-03-06 08:40:19
С этим всем разобрался все огонь. Вопрос в следующем я изменяю частоту с помощью таймера так вот она меняется не линейно к примеру если период таймера 1000 частота 5 герц если 800 то 10 но чем быстрее частота тем меньше значений таймера на герц например между 45 и 46 герц всего 3 значения. Я забил 50 значений в таблицу и подставляю их но шаг получается в один герц а хотелось хотя бы в пол . Можно таблицу конечно переписать но мне кажется есть вариант получше. Что скажите ?

NOOleg   2019-03-18 04:26:10
Дак что есть идеи ? ? ?

←Пред.12345След.→
Всего:6