Довелось мне, по роду своей деятельности, писать программу для разгона раскрутки и управления трехфазным синхронным двигателем. Далее я постараюсь подробно описать как я это реализовал.
График раскрутки двигателя выглядит таким образом:
Как видно, что необходимо не только увеличивать частоту, но и амплитуду, причем она растет не с нуля, а с 4-х вольт и при частоте в 80 Гц, должна достигать своего максимума. Ну и т.к. это трехфазный двигатель, то должно быть 3 синусоиды, причем сдвинутые относительно друг друга на 120 градусов. Также дополнительным условием было, чтобы на 80 Гц двигатель выходил не меньше чем через 30с, поэтому я решил использовать секунду на герц (100 Гц достигается за 100с).
В контроллере с которым я работаю имеется как раз 3 ШИМ’а и я решил использовать их, а незаморачиваться возиться с внешними ЦАП’ами.
Первая проблема с которой я столкнулся это то, что сигнал с выхода ШИМ будет только положительный, т.е. синусоида будет выше нуля, для этого пришлось разбить ее на полуволны и строить их отдельно. Вот так будет выглядеть синусоида при минимальной амплитуде.
А вот так при максимальной.
чтобы получить положительную полуволну:
отрицательную:
Как видно из следующего рисунка теперь у меня синусоида крутится возле значения 128, теперь достаточно аппаратно сдвинуть сигнал и установить ноль на значении 128 и она станет биполярной.
Синусоида состоит из 128 точек, этого более чем достаточно. После низкочастотного фильтра получается практически идеально гладкая. Амплитуда растет тоже довольно плавно (таблица коэффициентов состоит из 80 значений), умножаем на значение из таблицы и отбрасываем младший байт:
Для увеличения частоты я использовал 16 разрядный таймер TMR0, значение его периода берется из таблицы, которая состоит из 100 значений (от 1 до 100 Гц). Формула для вычисления выглядит следующим образом: 65535-sec/clk/smpl/Hz, где Hz меняется от 1 до 100.
Программа разрабатывалась для микроконтроллера PIC17 с использованием компилятора CC7A норвежской компании B Knudsen Data.
Вот, собственно, таблица значений для таймера.
Еще 2 таблицы со значениями синуса и коэффициентами амплитуды соответственно.
Обработчик прерывания таймера, выглядит следующим образом.
Основная программа.
Две подпрограммы по работе с таймером.
График раскрутки двигателя выглядит таким образом:
Как видно, что необходимо не только увеличивать частоту, но и амплитуду, причем она растет не с нуля, а с 4-х вольт и при частоте в 80 Гц, должна достигать своего максимума. Ну и т.к. это трехфазный двигатель, то должно быть 3 синусоиды, причем сдвинутые относительно друг друга на 120 градусов. Также дополнительным условием было, чтобы на 80 Гц двигатель выходил не меньше чем через 30с, поэтому я решил использовать секунду на герц (100 Гц достигается за 100с).
В контроллере с которым я работаю имеется как раз 3 ШИМ’а и я решил использовать их, а не
Первая проблема с которой я столкнулся это то, что сигнал с выхода ШИМ будет только положительный, т.е. синусоида будет выше нуля, для этого пришлось разбить ее на полуволны и строить их отдельно. Вот так будет выглядеть синусоида при минимальной амплитуде.
А вот так при максимальной.
чтобы получить положительную полуволну:
DCH = sin[ptrA];
DCH = DCH + 128;
отрицательную:
DCH = sin[ptrA];
DCH = 128 - DCH;
Как видно из следующего рисунка теперь у меня синусоида крутится возле значения 128, теперь достаточно аппаратно сдвинуть сигнал и установить ноль на значении 128 и она станет биполярной.
Синусоида состоит из 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;
}
}