Пользователь
0,0
рейтинг
14 сентября 2011 в 11:03

Разработка → Работа с трехфазным синхронным двигателем из песочницы

Довелось мне, по роду своей деятельности, писать программу для разгона раскрутки и управления трехфазным синхронным двигателем. Далее я постараюсь подробно описать как я это реализовал.

График раскрутки двигателя выглядит таким образом:

image

Как видно, что необходимо не только увеличивать частоту, но и амплитуду, причем она растет не с нуля, а с 4-х вольт и при частоте в 80 Гц, должна достигать своего максимума. Ну и т.к. это трехфазный двигатель, то должно быть 3 синусоиды, причем сдвинутые относительно друг друга на 120 градусов. Также дополнительным условием было, чтобы на 80 Гц двигатель выходил не меньше чем через 30с, поэтому я решил использовать секунду на герц (100 Гц достигается за 100с).

В контроллере с которым я работаю имеется как раз 3 ШИМ’а и я решил использовать их, а не заморачиваться возиться с внешними ЦАП’ами.

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

image
image

А вот так при максимальной.

image
image

чтобы получить положительную полуволну:
DCH = sin[ptrA];
DCH = DCH + 128;

отрицательную:
DCH = sin[ptrA];
DCH = 128 - DCH;

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

image

Синусоида состоит из 128 точек, этого более чем достаточно. После низкочастотного фильтра получается практически идеально гладкая. Амплитуда растет тоже довольно плавно (таблица коэффициентов состоит из 80 значений), умножаем на значение из таблицы и отбрасываем младший байт:

ampS = amp[fctr];
DCH = DCH*ampS;
DCH = DCH>>8;

Для увеличения частоты я использовал 16 разрядный таймер TMR0, значение его периода берется из таблицы, которая состоит из 100 значений (от 1 до 100 Гц). Формула для вычисления выглядит следующим образом: 65535-sec/clk/smpl/Hz, где Hz меняется от 1 до 100.
  • 65535 — максимальное значение 16 разрядного таймера
  • sec — 1с в наносекундах, значение 1000000000
  • clk — длительность 1 такта в наносекундах, при кварце в 32 Мгц равна 125
  • smpl — количество точек на период, значение 128

Программа разрабатывалась для микроконтроллера PIC17 с использованием компилятора CC7A норвежской компании B Knudsen Data.

Вот, собственно, таблица значений для таймера.

    #define     CDATA_START  0x300
    #pragma     cdata[CDATA_START]
    #pragma     cdata[] = 0x153B, 0x8A9D, 0xB1BE, 0xC54E, 0xD10B, 0xD8DE, 0xDE75, 0xE2A7, 0xE5E9, 0xE885, 0xEAA7, 0xEC6F, 0xEDF0, 0xEF3A, 0xF058, 0xF153, 0xF230, 0xF2F4, 0xF3A4, 0xF442, 0xF4D1, 0xF553, 0xF5CA, 0xF637, 0xF69B, 0xF6F7, 0xF74D, 0xF79D, 0xF7E7, 0xF82C, 0xF86C, 0xF8A9, 0xF8E2, 0xF917, 0xF94A, 0xF97A, 0xF9A7, 0xF9D1, 0xF9FA, 0xFA21, 0xFA45, 0xFA68, 0xFA89, 0xFAA9, 0xFAC7, 0xFAE4, 0xFB00, 0xFB1B, 0xFB34, 0xFB4D, 0xFB65, 0xFB7B, 0xFB91, 0xFBA6, 0xFBBA, 0xFBCE, 0xFBE1, 0xFBF3, 0xFC04, 0xFC15, 0xFC26, 0xFC36, 0xFC45, 0xFC54, 0xFC62, 0xFC70, 0xFC7E, 0xFC8B, 0xFC98, 0xFCA4, 0xFCB1, 0xFCBC, 0xFCC8, 0xFCD3, 0xFCDE, 0xFCE8, 0xFCF2, 0xFCFC, 0xFD06, 0xFD10, 0xFD19, 0xFD22, 0xFD2B, 0xFD34, 0xFD3C, 0xFD44, 0xFD4C, 0xFD54, 0xFD5C, 0xFD63, 0xFD6B, 0xFD72, 0xFD79, 0xFD80, 0xFD86, 0xFD8D, 0xFD93, 0xFD9A, 0xFDA0, 0xFDA6


Еще 2 таблицы со значениями синуса и коэффициентами амплитуды соответственно.

const unsigned char sin[128]=
     {
     0, 7, 13, 18, 24, 31, 37, 43, 50, 54, 60, 65, 71, 76, 80, 85, 90, 94, 99, 101, 105, 109, 112, 115, 118, 119, 121, 123, 125, 126, 126, 127, 127, 127, 126, 126, 125, 123, 121, 119, 117, 115, 112, 109, 105, 101, 99, 94, 90, 85, 80, 76, 71, 65, 60, 54, 48, 43, 37, 31, 24, 18, 13, 7, 0, 7, 13, 18, 24, 31, 37, 43, 50, 54, 60, 65, 71, 76, 80, 85, 90, 94, 99, 101, 105, 109, 112, 115, 118, 119, 121, 123, 125, 126, 126, 127, 127, 127, 126, 126, 125, 123, 121, 119, 117, 115, 112, 109, 105, 101, 99, 94, 90, 85, 80, 76, 71, 65, 60, 54, 48, 43, 37, 31, 24, 18, 13, 7
     };
     
const unsigned char amp[80]=
     {
     86, 88, 90, 93, 95, 97, 99, 101, 103, 105, 108, 110, 112, 114, 116, 118, 121, 123, 125, 127, 129, 131, 133, 136, 138, 140, 142, 144, 146, 148, 151, 153, 155, 157, 159, 161, 164, 166, 168, 170, 172, 174, 176, 179, 181, 183, 185, 187, 189, 192, 194, 196, 198, 200, 202, 204, 207, 209, 211, 213, 215, 217, 220, 222, 224, 226, 228, 230, 232, 235, 237, 239, 241, 243, 245, 247, 250, 252, 254, 255
     };


Обработчик прерывания таймера, выглядит следующим образом.

TMR0_service:
        {
        T0IF = 0;					// сброс флага прерывания
        
		sPRODL = PRODL;
		sPRODH = PRODH;
		sTBLPTRH = TBLPTRH;
		sTBLPTRL = TBLPTRL;
		sFSR0 = FSR0;
		sFSR1 = FSR1;
        
        if (herz < 100)
            {
            if (ptrA == 127)
               TMR0_inc();
            }

        TMR0_set();
        
        if (ptrA == 127)
            ptrA = 0;
        
        if (ptrB == 127)
            ptrB = 0;
           
        if (ptrC == 127)
            ptrC = 0;
            
        ptrA++;						// переход к след. точке 1-й синусоиды
        ptrB++;						// переход к след. точке 2-й синусоиды
        ptrC++;						// переход к след. точке 3-й синусоиды
        
        if (herz>=79)
            {
            if (ptrA < 64)
                {
                DCH = sin[ptrA];	// положительная полуволна
                DCH = DCH + 128;
                }
            else
                {
                DCH = sin[ptrA];	// отрицательная полуволна
                DCH = 128 - DCH;
                }
            PW1DCH = DCH;
        
            if (ptrB < 64)
                {
                DCH = sin[ptrB];	// положительная полуволна
                DCH = DCH + 128;
                }
            else
                {
                DCH = sin[ptrB];	// отрицательная полуволна
                DCH = 128 - DCH;
                }
            PW2DCH = DCH;
        
            if (ptrC < 64)
                {
                DCH = sin[ptrC];	// положительная полуволна
                DCH = DCH + 128;
                }
            else
                {
                DCH = sin[ptrC];	// отрицательная полуволна
                DCH = 128 - DCH;
                }
            PW3DCH = DCH;
            }
        else
            {
            fctr = herz-1;			// вычисление индекса
            ampS = amp[fctr];		// выборка коэффициента
            if (ptrA < 64)
                {
                DCH = sin[ptrA];	// положительная полуволна
                DCH = DCH*ampS;
                DCH = DCH>>8;
                DCH = DCH + 128;
                }
            else
                {
                DCH = sin[ptrA];	// отрицательная полуволна
                DCH = DCH*ampS;
                DCH = DCH>>8;
                DCH = 128 - DCH;
                }
            PW1DCH = DCH;
        
            if (ptrB < 64)
                {
                DCH = sin[ptrB];	// положительная полуволна
                DCH = DCH*ampS;
                DCH = DCH>>8;
                DCH = DCH + 128;
                }
            else
                {
                DCH = sin[ptrB];	// отрицательная полуволна
                DCH = DCH*ampS;
                DCH = DCH>>8;
                DCH = 128 - DCH;
                }
            PW2DCH = DCH;
        
            if (ptrC < 64)
                {
                DCH = sin[ptrC];	// положительная полуволна
                DCH = DCH*ampS;
                DCH = DCH>>8;
                DCH = DCH + 128;
                }
            else
                {
                DCH = sin[ptrC];	// отрицательная полуволна
                DCH = DCH*ampS;
                DCH = DCH>>8;
                DCH = 128 - DCH;
                }
            PW3DCH = DCH;
            }
        
		TBLPTRH = sTBLPTRH;
		TBLPTRL = sTBLPTRL;
		PRODL = sPRODL;
		PRODH = sPRODH;
		FSR0 = sFSR0;
		FSR1 = sFSR1;
        }

Основная программа.

void main()
     {
     GLINTD = 1;				// запрещение прерываний
     
     // -- загрузка адреса таблицы -->
     TBLPTRL = 0;
     TBLPTRH = 3;				// 0x300
     #asm
	TABLRD		0,1,WREG		; пустое чтение из табличной защелки
	TLRD		1,sTMR0H		; чтение старшего байта из табличной защелки
	TABLRD		0,1,sTMR0L		; чтение младшего байта из табличной защелки
     #endasm
     // <--
    
     // -- настройка TMR0 -->
     T0IE = 1;					// разрешение прерывания по переполнению TMR0
     T0STA = 32;				// внутренняя тактовая частота, 1:1
     // <--
    
     // -- настройка ШИМ -->
     PR1 = 253;					// Частота ШИМ = 32000000 / 4 / 253 = 31620 Гц
     PW1DCL = PW2DCL = PW3DCL = 0;
     PW1DCH = 64;				// начальное значение 1-й синусоиды
     PW2DCH = 46;				// начальное значение 2-й синусоиды
     PW3DCH = 83;				// начальное значение 3-й синусоиды
     // <--
    
     // -- начальная инициализация -->
     rtc = 0;
     herz = 1;					// начинаем с 1 Гц
     ptrA = 0;					// стартовое смещение 1-й синусоиды
     ptrB = 85;					// стартовое смещение 2-й синусоиды
     ptrC = 42;					// стартовое смещение 3-й синусоиды
     
     TMR1ON = 1;				// запуск TMR1
     PWM1ON = PWM2ON = PWM3ON = 1;		// запуск ШИМ
     TMR0_set();				// запуск TMR0
     // <--
    
     GLINTD = 0;				// разрешение прерываний
     
     while(1);
     }


Две подпрограммы по работе с таймером.

void TMR0_set()
     {
     TMR0L = sTMR0L;
     TMR0H = sTMR0H;
     }

void TMR0_inc()
    {
    rtc++;						// счетчик реального времени
    if (rtc == herz)
       {
       rtc = 0;
       herz++;
       TBLPTRL = TBLPTRL - 1;
       #asm
	TABLRD		0,1,WREG	; пустое чтение из табличной защелки
	TLRD		1,sTMR0H	; чтение старшего байта из табличной защелки
	TABLRD		0,1,sTMR0L	; чтение младшего байта из табличной защелки
       #endasm
       sTBLPTRH = TBLPTRH;
       sTBLPTRL = TBLPTRL;
       }           
    }
@heid
карма
5,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (25)

  • +1
    А китайцы делают без шима и ЦАП, а просто дискретные уровни +V, 0, -V и не парятся.
    • +1
      и да, PIC17 это как-то вообще не мейнстрим, даже среди ПИКов. Я уж думал они все вымерли…
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        любой китайский ESC
    • 0
      Не путайте BLDC с трехфазным движком.

      BLDC это движок аля коллекторный, только коллектор у него электронный, а сам он вывернут наизнанку. Он действительно управляется уровнями.
  • +1
    Расскажите, что стоит между выходами контроллера и двигателем.
    1. Чем отфильтровывали НЧ?
    2. Как сдвигали нулевой уровень?
    3. Чем усиливали сигнал?
    4. Как обеспечивали гальваническую развязку (если обеспечивали вообще)?
  • +1
    А у вас есть такая же статья, но только с перламутровыми пуговицами трехфазными асинхронными двигателями и AVR?
    • +1
      Помнится я делал в институте диплом по тиристорному управлению асинхронным двигателем. Собирал лабораторный стенд. Управлялся двигатель 1,5 кВт.
      • 0
        У меня почему-то ничего не получается. Вроде нашел инфу о том, как управлять такими движками, примеры кода под AVR, собрал драйвер на мощных полевиках, перепроверил схему 10 раз, а результат — нулевой, либо редкие подергивания вала мотора, если с кодом играться. (
        • 0
          у меня было управление не на транзисторах, а на тиристорах. Преподаватель говорил, что не заработает, а оно крутилось :-) Было это все, правда, 13 лет назад, и после этого я электроприводом не занимался. А у меня тоже вал сначала только подергивался, пока я не убрал шунтирующие конденсаторы в цепи управления тиристорами. Почему это помогло я н успел разобраться.
        • +3
          Одной из проблем состыковки MOSFET транзистора и микроконтроллера (или цифровой схемы) является то, что для полноценного открытия до полного насыщения этому транзистору надо вкатить на затвор довольно больше напряжение. Обычно это около 10 вольт, а МК может выдать максимум 5.

          Проверьте даташит на транзисторы.
          Проверьте не спалили ли вы выводы мк и что вобще происходит на выводах.
          Если нет осциллографа, можно взять светодиод с подключенным последовательно резистором ом на 300-400 и посмотреть, идут ли управляющие импульсы на полевики. А еще лучше подключить по светодиоду на каждую фазу.

          Уменьшите в алгоритме число оборотов двигателя так, чтобы на глаз можно было увидеть порядок включения фаз при помощи подключенных светодиодов. Мультимером проверьте напряжение на фазах двигателя (в режиме измерения напряжения переменного тока).
          • +1
            Не напряжение, а заряд. Затвор полевика — конденсатор по сути, то есть ему надо вкатить заряд, зависящий в том числе и от напряжения. А время, за которое это надо сделать — зависит от тока.

            Пока оно не зарядится, полевик будет работать в почти линейном режиме, то есть иметь сопротивление больше, чем указано в даташите. То есть — сильно греться будет.

            А чтобы так не было, надо драйвера ставить, от IR2101 и далее во всеми остановками, ну или на рассыпухе.
    • 0
      читайте аппноуты атмела — там все все все по полочкам
  • +1
    На графике не хватает единиц измерения.
  • +6
    В статье описано простейшее малоэффективное управление фазами без обратной связи, у такого принципа генерации фаз слишком мал КПД и крутящий момент. Намного более эффективен метод генерации фаз с отслеживанием положения ротора — по датчикам Холла или по ЭДС самоиндукции, причем необязательно для фаз генерировать синусоиду — достаточно просто применить простое ключевое управление напряжением на обмотках.

    В профессиональных массовых приложениях, когда нужно получить максимальный КПД и наиболее дешевое железо — например, в жестких дисках или в моторчиках авиамоделей — генерируют фазы грубой аппроксимацией синусоиды с отслеживанием положения ротора по ЭДС самоиндукции. В жестких дисках для этой цели не изобретают велосипед и применяют специализированные контроллеры. В контроллерах авиамоделей применяют микроконтроллер AVR ATmega8 или ATmega16. Есть реализации таких контроллеров с открытой принципиальной схемой и исходным кодом, см. например http://www.mikrokopter.com.
    • 0
      Зато синус!
      • +2
        Да хоть тангенс с котангенсом — все равно используется без толку. Почти вся энергия уходит на обогрев окружающего воздуха.
        • 0
          Обычные промышленные частотники на АД без обратной связи (энкодер или тахогенератор не в счет) как раз на таком синусе и работают. Вы привели пример для BLDC, а статья про асинхронник (или синхронник).
  • 0
    И к предыдущему добавлю — читайте аппноуты на сайте микрочипа.
    • 0
      Да, апноут у Microchip очень хороший по управлению моторами BLDC (Brusheless DC) — принцип описан очень понятно и наглядно. Но почти все контроллеры делают все равно почему-то на Atmel.
      • 0
        off-top. Часы-то я сделал. А потом, в попытках перевода с макеток на ПП сломал все. Когда переводил, обнаружил кучу забавных вещей, типа светодиоды подключены через резисторы с обозначением «390» — я аж прибалдел, как у меня МК столько прожил с таким театром.

        А спалив три самодельных драйвера двигателей в итоге сделал крутилку на микрочиповской микросхеме — на вход ШИМ, на выход двигатель, обороты задаются скважностью. Вот такая история :-)
        • 0
          Почитать интересно про Ваши эксперименты и результаты. Может, статью напишете?
          • +1
            Да у меня нет моральных сил уже даже восстановить их :-) Код, например, два раза переписывал с нуля и сейчас понимаю, что надо переписывать третий раз, ну просто потому что ужас.

            Вот как они перед смертью выглядели:
            www.youtube.com/watch?v=xixJDPJwafA

            Хотя обещал статью, обещал, помню…
            • 0
              Жаль, что поломали, конечно.
              • +1
                Зато в голове не жужжит :-) Ничего, наступит следующий кризис среднего возраста, сделаю полный рефакторинг железа и кода :-)
                Спасибо еще раз за вдохновение и науку!

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.