Целью данной работы является программирование студентами на лабораторном стенде ШИМ-сигналов, сдвинутых на 120° [1], [2], [3]. Программирование осуществляется в среде CooCox CoIDE.
Для реализации этой цели необходимо решить следующие задачи:
‒ Формирование синусоидальных сигналов в цифровой форме с помощью задания углов, определяемых как 2π/192;
‒ Включение тактирования периферийных устройств;
‒ Ввод функции инициализации:
1) Назначение портов ввода-вывода (GPIO – General Ports Input/Outputs);
2) Инициализация таймера (TIM1);
3) Инициализация ШИМ с обязательным включением комплементарных каналов;
‒ Инициализация и настройка прямого доступа к памяти (DMA);
‒ Отправка значений массивов сигналов с помощью прямого доступа к памяти (DMA) в регистры сравнения таймера (TIM1).
Алгоритм набора кода в среде разработки CooCox CoIDE состоит в следующем:
- Запускаем среду программирования CooCox CoIDE.
- После запуска CooCox CoIDE в строке меню нажать: Project → New Project.
- В появившемся окне в поле «Project name» ввести имя своему проекту.
- Далее нужно выбрать поле с надписью «Chip».
- Появится окно с выпадающими списками различных фирм микроконтроллеров (рис. 1). Необходимо открыть список ST, затем из выпадающего списка открыть подсписок STM32F103x, после чего найти микроконтроллер STM32F103C8, выбрать его левым щелчком мыши и нажать Finish (рис. 2).
- После проделанных действий появится главное окно с репозиторием для выбора необходимых для проекта библиотек (рис. 3). Необходимо подключить следующие библиотеки:
‒ RCC – для управления тактовым генератором;
‒ GPIO – для управления портами ввода-вывода;
‒ TIM – для управления таймерами;
‒ DMA – для управления прямым доступом к памяти (DMA).
Рис. 1. Выбор фирмы микроконтроллера
Рис. 2. Выбор микроконтроллера
Рис. 3. Выбор библиотек в репозитории
- После выбора библиотек необходимо на панели инструментов выбрать «New file» и создать файлы «timer» и «sinDMA» с расширением «.с», а также аналогичные файлы с расширением «.h» (рис. 4).
Рис. 4. Создание файлов «timer» и «sinDMA»
- Следующим шагом необходимо в панели файлов открыть файл «timer.h», два раза щелкнув по нему левой кнопкой мыши, и с помощью директивы «#include <>» записать в нем все заголовочные файлы, необходимые для работы с таймером и портами ввода-вывода (рис. 5).
Рис. 5. Запись заголовочных файлов в «timer.h»
- Далее необходимо открыть файл «timer.c». В этом файле нужно создать функцию, в которой включить тактирование периферийных устройств, а также ввести структуры и заполнить их для инициализации портов ввода-вывода и таймера (TIM1). Содержание файла «timer.c» представлено в листинге 1.
Листинг 1. Содержание файла «timer.c»
#include
void timers(void)
{
//----------Включение тактирования-------//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
| RCC_APB2Periph_AFIO
| RCC_APB2Periph_TIM1, ENABLE);
//-----------Ввод структур--------------//
GPIO_InitTypeDef gpio;
GPIO_InitTypeDef gpio_1;
GPIO_InitTypeDef gpio_2;
GPIO_InitTypeDef gpio_3;
TIM_TimeBaseInitTypeDef TIM_Base_1;
TIM_OCInitTypeDef TIM_PWM;
//------Назначение GPIO-------------
GPIO_StructInit(&gpio);
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_8;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);
//------------------------------------
GPIO_StructInit(&gpio_1);
gpio_1.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_1.GPIO_Pin = GPIO_Pin_9;
gpio_1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_1);
//------------------------------------
GPIO_StructInit(&gpio_3);
gpio_3.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_3.GPIO_Pin = GPIO_Pin_10;
gpio_3.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_3);
//-----Назначение TIM1----------------
TIM_TimeBaseStructInit(&TIM_Base_1);
TIM_Base_1.TIM_Prescaler = 0;
TIM_Base_1.TIM_CounterMode = TIM_CounterMode_Up;
TIM_Base_1.TIM_Period = 2000;
TIM_Base_1.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM1,&TIM_Base_1);
//-------Назначение ШИМ-------------------
TIM_OCStructInit(&TIM_PWM);
TIM_PWM.TIM_OCMode = TIM_OCMode_PWM1;
TIM_PWM.TIM_OutputState = TIM_OutputState_Enable;
TIM_PWM.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OC1Init(TIM1, &TIM_PWM);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC2Init(TIM1, &TIM_PWM);
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC3Init(TIM1, &TIM_PWM);
TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_Cmd(TIM1, ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
- После этого в файле «timer.h» записать прототип функции «timers()» (рис. 6), а также с помощью директивы «#include<>» подключить заголовочный файл «timer.h» в файле «main.c».
Рис. 6. Запись прототипа функции «timers()»
- Следующим шагом нужно открыть файл «sinDMA.h» и подключить заголовочные файлы, необходимые для работы с DMA (рис. 7).
Рис. 7. Подключение заголовочных файлов в «sinDMA.h»
- Далее необходимо открыть файл «sinDMA.c» и создать массивы для трехфазной системы с синусоидальными напряжениями, а также создать функции для передачи элементов массива с помощью DMA в регистры сравнения таймера (TIM1). Содержание файла «sinDMA.c» представлено в листинге 2.
Листинг 2. Содержание файла «sinDMA.c»
#include
//---Фаза А*---//
uint16_t sinA[192] = {56,84,113,141,169,197,225,253,281,309,337,365,392,
420,447,474,502,529,556,583,609,636,662,688,714,740,766,791,817,842,867,
891,916,940,964,988,1011,1035,1058,1080,1103,1125,1147,1169,1190,1211,
1232,1252,1272,1292,1312,1331,1350,1368,1386,1404,1422,1439,1456,1472,
1488,1504,1519,1534,1549,1563,1577,1590,1603,1616,1628,1640,1651,1662,
1673,1683,1693,1702,1711,1720,1728,1736,1743,1750,1756,1762,1768,1773,
1777,1782,1785,1789,1792,1794,1796,1798,1799,1799,1800,1799,1799,1798,
1796,1794,1792,1789,1785,1782,1777,1773,1768,1762,1756,1750,1743,1736,
1728,1720,1711,1702,1693,1683,1673,1662,1651,1640,1628,1616,1603,1590,
1577,1563,1549,1534,1519,1504,1488,1472,1456,1439,1422,1404,1386,1368,
1350,1331,1312,1292,1272,1252,1232,1211,1190,1169,1147,1125,1103,1080,
1058,1035,1011,988,964,940,916,891,867,842,817,791,766,740,714,688,662,
636,609,583,556,529,502,474,447,420,392,365,337,309,281,253,225,56};
/*********************************************************************/
//---Фаза B (+2*pi/3)---//
uint16_t sinB[192] = {1529,1514,1499,1483,1467,1450,1433,1416,1398,1380,
1362,1343,1324,1305,1286,1266,1245,1225,1204,1183,1161,1140,1118,1095,
1073,1050,1027,1003,980,956,932,908,883,858,833,808,783,757,732,706,680,
653,627,600,574,547,520,493,465,438,411,383,355,328,300,272,244,216,188,
160,131,103,75,47,18,9,37,65,94,122,150,178,206,234,262,290,318,346,374,
401,429,456,484,511,538,565,591,618,645,671,697,723,749,774,800,825,850,
875,900,924,948,972,996,1019,1042,1065,1088,1110,1132,1154,1176,1197,
1218,1239,1259,1279,1299,1318,1337,1356,1374,1392,1410,1428,1445,1461,
1478,1494,1509,1524,1539,1568,1581,1595,1608,1620,1632,1644,1655,1666,
1677,1687,1696,1705,1714,1723,1731,1745,1752,1758,1764,1769,1774,1779,
1783,1786,1790,1792,1795,1797,1798,1799,1799,1799,1799,1798,1797,1795,
1793,1791,1788,1784,1780,1776,1771,1766,1760,1748,1741,1733,1725,1717,
1708,1699,1690,1680,1670,1659,1529};
/**********************************************************************/
//---Фаза C (-2*pi/3)---//
uint16_t sinC[192] = {1599,1612,1624,1636,1648,1659,1670,1680,1690,1699,
1708,1717,1725,1733,1741,1748,1754,1760,1766,1771,1776,1780,1784,1788,
1791,1793,1795,1797,1798,1799,1799,1799,1799,1798,1797,1795,1792,1790,
1786,1783,1779,1774,1769,1764,1758,1752,1745,1738,1731,1723,1714,1705,
1696,1687,1677,1666,1655,1644,1632,1620,1608,1595,1581,1568,1554,1539,
1524,1509,1494,1478,1461,1445,1428,1410,1392,1374,1356,1337,1318,1299,
1279,1259,1239,1218,1197,1176,1154,1132,1110,1088,1065,1042,1019,996,
972,948,924,900,875,850,825,800,774,749,723,697,671,645,618,591,565,538,
511,484,456,429,401,374,346,318,290,262,234,206,178,150,122,94,65,37,9,
18,47,75,103,131,160,188,216,244,272,300,328,355,383,411,438,465,493,
520,547,574,600,627,653,680,706,732,757,783,808,833,858,883,908,932,956,
980,1003,1027,1050,1073,1095,1118,1140,1161,1183,1204,1225,1245,1266,
1286,1305,1324,1343,1362,1380,1398,1416,1433,1586,1586};
/**********************************************************************/
void sinDMA_PhaseA(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_struct;
DMA_StructInit(&DMA_struct);
DMA_struct.DMA_PeripheralBaseAddr =(uint32_t)&TIM1->CCR1;
DMA_struct.DMA_MemoryBaseAddr = (uint32_t)&sinA[0];
DMA_struct.DMA_DIR=DMA_DIR_PeripheralDST;
DMA_struct.DMA_BufferSize=192;
DMA_struct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_struct.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_struct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_struct.DMA_Mode=DMA_Mode_Circular;
DMA_Init(DMA1_Channel2,&DMA_struct);
DMA_Cmd(DMA1_Channel2, ENABLE);
TIM_DMACmd(TIM1,TIM_DMA_CC1, ENABLE);
}
void sinDMA_PhaseB(void)
{
DMA_InitTypeDef DMA_struct1;
DMA_StructInit(&DMA_struct1);
DMA_struct1.DMA_PeripheralBaseAddr =(uint32_t)&TIM1->CCR2;
DMA_struct1.DMA_MemoryBaseAddr = (uint32_t)&sinB[0];
DMA_struct1.DMA_DIR=DMA_DIR_PeripheralDST;
DMA_struct1.DMA_BufferSize=192;
DMA_struct1.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_struct1.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_struct1.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_struct1.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_struct1.DMA_Mode=DMA_Mode_Circular;
DMA_Init(DMA1_Channel3,&DMA_struct1);
DMA_Cmd(DMA1_Channel3, ENABLE);
TIM_DMACmd(TIM1,TIM_DMA_CC2, ENABLE);
}
void sinDMA_PhaseC(void)
{
DMA_InitTypeDef DMA_struct2;
DMA_StructInit(&DMA_struct2);
DMA_struct2.DMA_PeripheralBaseAddr =(uint32_t)&TIM1->CCR3;
DMA_struct2.DMA_MemoryBaseAddr = (uint32_t)&sinC[0];
DMA_struct2.DMA_DIR=DMA_DIR_PeripheralDST;
DMA_struct2.DMA_BufferSize=192;
DMA_struct2.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_struct2.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_struct2.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_struct2.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_struct2.DMA_Mode=DMA_Mode_Circular;
DMA_Init(DMA1_Channel6,&DMA_struct2);
DMA_Cmd(DMA1_Channel6, ENABLE);
TIM_DMACmd(TIM1,TIM_DMA_CC3, ENABLE);
}
- Следующим шагом необходимо записать прототипы функций в файл «sinDMA.h» (рис. 8), а также подключить заголовочный файл «sinDMA.h» в «main.c».
Рис. 8. Запись прототипов функций для генерации синусоидальных напряжений
- После того как все файлы были заполнены и подключены к «main.c», необходимо записать функции в основную функцию «main()» (рис. 9). Цикл «while(1)» в данной программе остается пустым.
Рис. 9. Содержание функции «main()»
- После написания кода программы, его необходимо скомпилировать. Для этого в панели инструментов нужно нажать «Build». В случае успешной компиляции в консоли появится надпись «BUILD SUCCESSFUL», а также будет указан размер программы. Если же в коде присутствуют ошибки, то в консоли будет указано, где именно находятся эти ошибки, а также появится надпись «BUILD FAILED».
- После завершения компиляции последним этапом станет загрузка рабочей программы в микроконтроллер. Для этого нужно через специальный кабель (удлинитель USB) подключить программатор, расположенный на лабораторном стенде, к компьютеру. После подключения в панели инструментов нажать «Download Code to Flash» и дождаться окончания загрузки. В случае удачной загрузки в консоли появятся надписи: «Erase: Done»; «Program: Done»; «Verify: Done». Если существуют проблемы с подключением платы к компьютеру, то появится надпись «Error: Connect failed, check config and cable connection». Необходимо проверить кабель, к которому подключено устройство.
Результаты программирования трехфазного генератора с синусоидальными напряжениями со сдвигом 120° даны на рис. 10.
Рис. 10. Результаты программирования сигналов AB и BC
Литература:
- Анучин А. С. Системы управления электроприводов: учебник для вузов / А. С. Анучин. – М.: Изд. дом МЭИ, 2015. – 373 с.
- Огородников И. Н. Микропроцессорная техника: введение в Cortex-M3: учеб. пособие / И. Н. Огородников. – Екатеринбург: изд-во Урал. Ун-та, 2015. – 116 с.
- Джозеф Ю. Ядро Cortex-M3 компании ARM. Полное руководство / Ю. Джозеф; пер. с англ. А. В. Евстифеева. – М.: Додэка-XXI, 2012. – 552 с.