Встраиваемые системы. Проектирование приложений на микроконтроллерах семейства 68HC12/HCS12 с применением языка С — страница 53 из 70

7.4.1. Описание проекта

Целью этого проекта является стабилизация скорости вращения электродвигателя.

Чтобы осуществить такую стабилизацию, используем управление с обратной связью. Мы будем постоянно контролировать скорость вращения двигателя в рабочем режиме и корректировать ее, изменяя напряжение питания, подводимое к двигателю. Мы также будем показывать текущую скорость вращения двигателя, выраженную в оборотах в минуту, на ЖК индикаторе.

В проекте используется двигатель постоянного тока с постоянным магнитом, выпускаемый компанией Electro-Craft Corporation и оптический кодер [11]. Двигатель имеет следующие характеристики:

• Постоянное питающее напряжение: 12 В;

• Скорость на холостом ходу: 2500 об/мин при 12 В;

• Пусковой ток: 2 А;

• Ток холостого хода: 370 мА;

• Ток при умеренной нагрузке: 600 мА;

• Ток останова: 4 А;

• Двигатель имеет один канал оптического кодера (Servo-Tek # PMBX-60-05).

Кодер формирует 60 ТТЛ-совместимых импульсов за один оборот двигателя.

После этого краткого описания проекта возникает ряд вопросов:

1. Как контролировать скорость двигателя?

2. Каким образом регулировать напряжение питания двигателя, чтобы изменять его скорость?

3. Микроконтроллер 68HC12 питается от постоянного напряжения в 5 В при очень малом выходном токе источника. Как управлять с его помощью двигателем с высоким рабочим током, питающимся от постоянного напряжения в 12 В?

4. Как можно совместить управление скоростью двигателя с выполнением других задач, стоящие перед системой?

5. Какие системы, встроенные в 68HC12, должны использоваться, чтобы выполнить эту задачу?

Как и прежде, мы обратимся сначала к нашему «сундучку инструментов», чтобы выяснить, какие инструментальные средства мы уже использовали в проектах и какие придется разрабатывать. Давайте сделаем это одновременно с рассмотрением каждого из сформулированных выше вопросов. При обсуждении воспользуемся схемой регулирования частоты вращения двигателя, представленной на рис. 7.15.

Рис. 7.15. Схема стабилизации скорости вращения двигателя


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

Подсчитав число импульсов на фиксированном временном интервале, можно определить скорость двигателя. Число импульсов, появляющихся внутри данного временного интервала, можно измерить, используя систему прерываний МК 68HC12 в режиме реального времени (RTI) и аккумулятор импульсов (PA). Обе эти системы обсуждались в главе 4. Как мы уже упоминали, система RTI генерирует прерывания через равные временные интервалы, определенные пользователем, чтобы «напоминать» МК о необходимости периодически выполнять задачу стабилизации, а аккумулятор PA может быть конфигурирован, чтобы считать импульсы. Объединив эти системы, мы сможем подсчитать число импульсов оптического кодера, появляющихся внутри данного временного интервала и, затем вычислить скорость вращения двигателя в оборотах в минуту.

Рассмотрим конкретный пример. Предположим, что мы конфигурировали RTI на прерывание через каждые 32.768 мс, и в течение этого интервала, PA зафиксировал появление 52 импульсов. Какова будет при этом скорость вращения двигателя в оборотах в минуту? Чтобы вычислить необходимо выполнить следующий перевод единиц измерения:

(52 импульса/32.768 мс) (1 об/60 импульсов) (1,000 мс/1 секунда) (60 с/1 мин) = 1,586 об/мин

2. Каким образом регулировать напряжение питания двигателя, чтобы изменять его скорость? В главе 4 мы обсуждали концепцию широтно-импульсной модуляции (ШИМ).

Было показано, что среднее значение напряжения на двигателе изменяется при изменении коэффициента заполнения питающего его импульсного напряжения. Коэффициент заполнения определяется как отношение времени включенного состояния ключа к периоду переключения. Если мы используем ШИМ, то сможем изменять питающее напряжение и, как следствие, скорость двигателя. Примем для простоты, что скорость двигателя линейно связана с питающим напряжением. Проверим это предположение экспериментально. Сигналы ШИМ могут быть сформированы за счет функции сравнения таймера (для всех моделей МК 68HC12) или с помощью широтно-импульсных модуляторов (только для B32 и для всех МК семейства HCS12). Используем модуль ШИМ микроконтролеров B32, который обсуждался в главе 4. Можно вспомнить также, что мы уже использовали ШИМ в настоящей главе, чтобы генерировать сигналы управления двигателем робота, проходящего через лабиринт.

3. Микроконтроллер 68HC12 питается от постоянного напряжения в 5 В при очень малом выходном токе источника. Как управлять с его помощью двигателем с высоким рабочим током, питающимся от постоянного напряжения в 12 В? В главе 5, мы обсуждали, как двигатель может управляться от 68HC12 с использованием силовых полупроводниковых ключей. Сигнал управления ШИМ подается на затвор МОП-транзистора. Двигатель подключен между полюсом напряжения питания и стоком МОП-транзистора, как показано на рис.7.15. Для этого конкретного проекта, мы используем мощный МОП-транзистор IRF530N HEXFET компании International Rectifier. Он выпускается в корпусе TO-220AB с тремя выводами и рассчитан на напряжения переключения в 100 В и рабочие токи в 17 А. Внимание!: Не забудьте включить обратный диод параллельно двигателю.

4. Как можно контролировать скорость двигателя и одновременно выполнять другие задачи, стоящие перед системой? Если мы будем использовать обычную последовательную обработку данных, наш процессор будет постоянно привязан к операциям контроля скорости двигателя, регулирования его питающего напряжения и отображения текущего значения скорости на ЖК индикаторе. Однако, как мы упомянули в ответе на вопрос 1, мы используем RTI систему, чтобы реализовать задачи, связанные с этим управлением, один раз в 33 мс. Между этими периодическими прерываниями, МК может выполнять другие задачи. Концепция периодических прерываний для управления двигателем иллюстрируется на рис. 7.16. Мы исследуем связанные с ней понятия в следующей главе.

Рис. 7.16. Программа управления двигателем в режиме прерываний


5. Какие подсистемы, встроенные в 68HC12, должны быть использованы, чтобы выполнить эту задачу? На этот вопрос мы в сущности уже ответили. Однако для завершенности обсуждения составим список подсистем необходимых для выполнения проекта:

• Модуль ШИМ;

• Модуль меток реального времени;

• Счетчик внешних событий (Аккумулятор импульсов);

• Силовой коммутатор для управления двигателем;

• Оптический кодер;

• Жидкокристаллический (ЖК) индикатор.

 7.4.2. Немного теории

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

Требования к двигателю. Специфический двигатель, который мы используем — это двигатель постоянного тока, выпускаемый компанией Electro-Craft Corporation. Мы уже рассмотрели основные характеристики двигателя. Однако, имеется ряд дополнительных характеристик, которые требуются для этого проекта, таких, например, как зависимость скорости двигателя от его тока. Так как они не приводятся в информационных данных, мы получим их экспериментально. Будем изменять питающее напряжение, поданное на двигатель, и соответствующую этому напряжению скорость. Для этого подключим частотомер на выход оптического кодера, чтобы определить частоту следования импульсов при заданном питающем напряжении двигателя, и затем вычислим скорость вращения. Одновременно будем измерять также токи двигателя. Результаты эксперимента показаны на рис. 7.17.

Рис. 7.17. Результаты испытаний двигателя


Оптический кодер.  Существует широкое разнообразие оптических кодеров для определения скорости вращения вала двигателя. Эти кодеры закрепляются непосредственно на валу, или могут быть связаны с валом какими-либо устройствами. При установке кодера на вращающийся вал, это устройство обеспечивает на выходе прямоугольное напряжение. Кодеры питаются от постоянного напряжения 5 В, и рассчитаны на максимальную скорость вращения в 12 000 об/мин. Мы используем оптические кодеры, чтобы обеспечить измерение скорости вращения двигателя, как описано в [11]. Установка для измерения частоты вращения показана на рис. 7.18.

Рис. 7.18. Установка для измерения скорости вращения двигателя


Прерывания в режиме реального времени. Мы используем в МК 68HC12 модуль меток реального времени (RTI), периодически прерывая работу 68HC12, чтобы измерить скорость двигателя и скорректировать коэффициент заполнения ШИМ, если это необходимо для стабилизации скорости вращения двигателя. Перед обсуждением программного кода для управления RTI, мы советуем Вам, возобновить в памяти информацию о сбросах и прерываниях. Ниже приведен краткий обзор действий, необходимых, чтобы инициализировать прерывание RTI:

• Инициализируют вектор прерывания по запросу RTI;

• Устанавливают масштабирующий коэффициент RTR[2:0];

• Устанавливают флаг RTIE разрешения прерываний от RTI в регистре RTICTL;

• Очищают флаг RTIF в регистре RTIFLG;

• Разрешают все маскируемые прерывания прерывания (команда CLI).

Приведенные ниже программный код поможет Вам ознакомиться с работой RTI. В этом примере, мы переходим к программе ISR обработки прерывания RTI, переключая флаг порта PORTP. Если вы исследуете возникающую в результате форму сигнала, показанного на рис. 7.19, с помощью осциллографа или логического анализатора, то сможете измерить период повторения системы RTI прерываний.

Рис. 7.19. Прерывания в режиме реального времени


//********************************************************************

//имя файла: RTI_test.с//

//Port P[0]: Конфигурируется как цифровой выходной порт, обеспечивающий TTL

// совместимые сигналы для управления затвором.

// авторы: Стив Барретт и Даниель Пак

//дата создания: Mar 10, 2003

//последняя редакция: Mar 10, 2004

//********************************************************************


#include <912b32.h>

#pragma interrupt_handler RTI_isr


//Функции-прототипы

//***************************************************

void initialize_ports(void); //инициализация портов

void RTI_isr(void);

void initialize_RTI(void);


//main program* *******************************************************

void main(void) {

 //инициализация вектора сброса для B32

 asm(".area vectors(abs)\n"

  " .org 0xFFF0\n" // инициализация вектора прерывания RTI

  " .word _RTI_isr\n"

  " .org 0xFFF8\n" // инициализация вектора сброса для B32

  " .word 0x8000, 0x8000, 0x8000, 0x8000\n"

  " .text");

 initialize_RTI(); //инициализация модуля RTI

 initialize_ports(); //инициализация портов

 PORTP = 0x01; // разрешение PORTP вывод 0

 asm("cli"); //разрешение всех маскируемых прерываний

 .

 .

 .

}


//********************************************************************

// определения функций

// ********************************************************************


//Initialize_ports: начальная конфигурация

//для портов входа/выхода

// ********************************************************************

void initialize_ports(void) {

 DDRP=0xFF; //порт PORTP устанавливается как выходной

}


// ********************************************************************

//RTI_isr: подпрограмма обслуживания прерываний по RTI

//*********************************************************************

void RTI_isr(void) {

 RTIFLG = 0x80; //сбрасывается флаг RTIF

 PORTP =~(PORTP); //переключить выходы PORTP

}

// ********************************************************************


// ********************************************************************

//initialize_RTI:конфигурирует модуль RTI

// ********************************************************************

void initialize_RTI(void) {

 RTICTL = 0x86; // устанавливается таймер RTI

 RTIFLG = 0x80; //сбрасывается флаг RTIF

}

// ********************************************************************

Аккумулятор импульсов. Число импульсов, поступающих от оптического кодера, подсчитывается аккумулятором импульсов (PA). Он инициализируется в начале программы и затем фиксирует текущее число импульсов за интервал каждого прерывания RTI. По известному интервалу времени между прерываниями RTI (32.768 мс) можно определить число импульсов, поступивших на PA между двумя прерываниями, и затем вывести на дисплей скорость вращения двигателя. Для конфигурации PA системы существует специальный код.

// **********************************************************************

//initialize_PA: инициализация модуля аккумулятора импульсов МК 68HC12

// **********************************************************************

void initialize_PA(void) {

 TIOS = 0x00; //Конфигурировать канал 7 для работы счетчика импульсов

 TCTL1 = 0x00; //кодера - 3 оператора

 OC7M = 0x00;

 TSCR = 0x80; //Установить бит разрешения таймера

 PACTL = 0x70; //Разрешить работу PA, режим счета событий, событие

               // по фронту импульса,

}

Объединим теперь эту программу с программой для RTI, чтобы подсчитать число импульсов кодера, появившихся за интервал прерывания. Получив, этот результат, преобразуем его в число об/мин и выведем полученное значение на ЖК индикатор.

// **********************************************************************

//имя файла: motor.с

//автор: Steve Barrett and Daniel Pack

//дата создания: Mar 10, 2003

//последняя редакция: Mar 25, 2004

// **********************************************************************

// включенные файлы *****************************************************

#include <912b32.h>

#pragma interrupt_handler RTI_isr //объявить подпрограмму прерывания по RTI


// функции-прототипы ****************************************************

void initialize_ports(void); //инициализировать порты

void RTI_isr(void); //подпрограмма обслуживания прерываний RTI

void initialize_RTI(void); // инициализировать систему RTI

void initialize_PA(void); //инициализировать аккумулятор импульсов (PA)

void initialize_LCD(void); // инициализировать ЖК индикатор

void display_count_LCD(unsigned int); //вывод текущего содержимого аккумулятора PA

                                      // на ЖК индикатор

void putchars(unsigned char); //функция поддержки ЖКИ - вывести символ

void putcommands(unsigned char);// функция поддержки ЖКИ вывести команду

void delay_5ms(void); //задержка 5 мс

void delay_100us(void); //задержка 100 мкс

unsigned int old_count; //последнее значение, записанное в (PA)

int RTI_int_count =0; //используется для подсчета RTI прерываний


//главная программа****************************************************

void main(void) {

 asm(" .area vectors(abs) \n" //inline assembly statement

  ".org 0xFFF0\n" //инициализация вектора прерываний RTI

  ".word _RTI_isr\n"

  " .org 0xFFF8\n" // инициализация вектора reset для 68HC12 B32

  " .word 0x8000, 0x8000, 0x8000, 0x8000\n"

  " .text");

 void initialize_ports(void); // инициализация портов

 initialize_LCD(); //инициализация ЖКИ

 initialize_RTI(); //инициализация модуля RTI

 initialize_PA(); //инициализация аккумулятора импульсов

 asm("cli"); //разрешение глобального маскирования

             //прерываний

 while(1) //продолжение цикла до следующего прерывания

 {

  ;

 }

}

//********************************************************************


//********************************************************************

//initialize_ports: определяет направление передачи портов

//********************************************************************

void initialize_ports(void) {

 DDRP = 0xFF; // порт PORTP устанавливается как выходной для ШИМ

 DDRT = 0x00; // PORTT устанавливается как входной, вывод PORTT[7]

              // в качестве входа аккумулятора импульсов PA

 DDRB = 0xFF; // PORTB устанавливается как выходной - порт

              // данных для ЖКД

 DDRDLC = 0xFF; // PORT DLC устанавливается как выходной - сигналы

                // управления для ЖКИ

}


//********************************************************************

//RTI_isr: подпрограмма прерывания по RTI

//********************************************************************

void RTI_isr(void) {

 unsigned int new_count;

 unsigned int pulse_count;

 float max_count = 65535.0;

 new_count = PACNT; //передается текущее число импульсов, записанное в PA

 if (new_count > old_count) //определяется приращение числа импульсов

  pulse_count = new_count - old_count;

 else pulse_count = (unsigned int)(max_count-(float)(old_count = new_count));

 RTI_int_count = RTI_int_count + 1;// изменяется показание счетчика

                                   //RTI-прерываний

 if (RTI_int_count == 10) // изменяется показание ЖКД через

                          // каждые 10 прерываний

 {

  display_count_LCD(pulse_count); //изменяется ЖКИ

  RTI_int_count = 0; //сбрасывается счетчик прерываний RTI

 }

 old_count = new_count;

 RTIFLG = 0x80; //сбрасывается RTI

}


// ********************************************************************

// initialize_RTI:конфигурирует регистры, связанные с RTI

// - регистр RTICTL

// -- разрешает работу модуля RTI установкой бита RTIE

// -- период RTI в 32.768 мс

// - сбрасывает бит RTIF в регистре флагов (RTIFLG)

// ********************************************************************

void initialize_RTI(void) {

 RTICTL = 0x86; // устанавливается период RTI на 32.768 мс

 RTIFLG = 0x80; //сбрасывается флаг RTIF

}


// ********************************************************************

//initialize_PA: инициализация модуля аккумулятора импульсов

// ********************************************************************

void initialize_PA(void) {

 TIOS = 0x00; // Конфигурирует канал 7 для работы

 TCTL1 = 0x00; // счетчика импульсов оптического кодера

 OC7M = 0x00;

 TSCR = 0x80; // устанавливает бит разрешения работы таймера

 PACTL = 0x70; // разрешает работу РА, режим счета событий,

               // по фронту импульса,

}


/********************************************************************/

/* initialize_LCD: инициализации ЖКИ                                */

/* передает на ЖКИ стартовую последовательность команд управления   */

/* - PORTDLC[3]: линия управления RS ЖКИ                            */

/* - PORTDLC[2]: линия управления E для ЖКИ                         */

/* - PORTB: двунаправленная магистраль данных для ЖКИ               */

/********************************************************************/

void initialize_LCD(void) {

 delay_5ms();

 delay_5ms();

 delay_5ms(); /*ожидание в течение 15 мс перед включением ЖКИ */

 putcommands(0x38); /*разрядность данных интерфейса 8 бит */

 delay_5ms(); /*задержка */

 putcommands(0x38); /*интерфейс в 8 бит */

 delay_100us(); /*задержка */

 putcommands(0x38); /* разрядность данных интерфейса 8 бит */

 putcommands(0x38); /* интерфейс в 8 бит */

 putcommands(0x0C); /*включение дисплея */

 putcommands(0x01); /*очистка дисплея */

 putcommands(0x06); /*установка режима инкремента адреса */

 putcommands(0x00);

 putcommands(0x00);

 putcommands(0xC0); /*курсор на линию 2 знакоместо 1 */

 putchars('R'); /* вывести "PRM" - скорость */

                /*в об/мин на строку 2 ЖКИ */

 putchars('P');

 putchars('M');

}


/********************************************************************/

/*putchars: функция посылает ASCII код для вывода на ЖКИ            */

/********************************************************************/

void putchars(unsigned char c) {

 PORTB = c; /*вывести на порт PORTB код символа */

 PORTDLC = PORTDLC|0x08; /*установить RS в 1 для передачи данных */

 PORTDLC = PORTDLC|0x04; /*установить E в 1 */

 PORTDLC = 0x00; /* установить E и RS в 0 */

 delay_100us(); delay_100us();

}


/********************************************************************/

/*putcommands: функция посылает команду управления ЖКИ              */

/********************************************************************/

void putcommands(unsigned char d) {

 PORTDLC = PORTDLC&0xF7; /*установить RS в 0 для передачи команды */

 PORTDLC = PORTDLC|0x04; /*установить E в 1 */

 PORTDLC = 0x00; /* установить E и RS в 0 */

 delay_100us();

 delay_100us();

}


/********************************************************************/

/* delay_5ms: программная задержка 5 мс                             */

/********************************************************************/

void delay_5ms(void) {

 int i;

 for (i=0; i<50; i++) {

  delay_100us();

 }

}


/********************************************************************/

/* delay_100us:программная задержка в 100 мс                        */

/********************************************************************/

void delay_100us(void) {

 int i;

 for(i=0; i<800; i++) {

  asm("nop");/*выполнение команды nор ассемблера занимает 1 период*/

 }

}


/********************************************************************/

/* display_count_LCD: преобразует целое число в ASCII символ        */

/* для вывода на ЖКИ                                                */

/********************************************************************/

void display_count_LCD(unsigned int count) {

 unsigned int thousands_int;

 unsigned int hundreds_int;

 unsigned int tens_int;

 unsigned int ones_int;

 char thousands;

 char hundreds;

 char tens;

 char ones;

 /*выбирает и преобразует наибольшую значащую цифру в десятичное */

 /* значение + 48, образуя ASCII код */

 thousands_int = count/1000;

 thousands = (char)(thousands_int + 48);

 /*выбирает и преобразует следующую наибольшую значащую цифру */

 /* в десятичное значение + 48, образуя ASCII код */

 hundreds_int = (count - thousands_int*1000)/100;

 hundreds = (char)(hundreds_int + 48);

 /*выбирает и преобразует следующую наибольшую значащую цифру */

 /* в десятичное значение + 48, образуя ASCII код */

 tens_int = (count - thousands_int*10 0 0 - hundreds_int*100)/10;

 tens = (char)(hundreds_int + 48);

 /*выбирает и преобразует следующую наибольшую значащую цифру */

 /* в десятичное значение + 48, образуя ASCII код */

 ones_int = (count-thousands_int*1000-hundreds_int*100-tens_int*10);

 ones = (char)(ones_int + 48);

 /*выводит результат на ЖКИ*/

 putcommands(0x80); /*курсор ЖКИ переводится на строку 1, позицию 1*/

 putchars(thousands);

 putchars(hundreds);

 putchars(tens);

 putchars(ones);

}

/********************************************************************/

/********************************************************************/

7.4.3. Анализ 

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

Стабилизируем скорость двигателя на уровне 1600 об/мин. Чтобы сделать это, мы должны определить требуемый коэффициент заполнения ШИМ для 1600 об/мин. Из полученного нами графика (рис. 7.17) мы видим, что для обеспечения скорости вращения приблизительно в 1600 об/мин, необходимо подать на двигатель напряжение в 8 В. При питании двигателя от источника в 12 В соответствующий коэффициент заполнения ШИМ составит 66.7 % (8 В/ 12 В).

Чтобы достичь коэффициента заполнения ШИМ в 66.7%, установим значение периода ШИМ на 256 единиц, а коэффициент заполнения ШИМ на 172 единицы.

Как мы уже указывали при предыдущем обсуждении системы ШИМ, эти значения устанавливаются в регистрах PWPER0 и PWDTY0, соответственно.

Мы используем сигнал управления ШИМ с частотой 976 Гц. Для этого частоту таймера ШИМ, равную 8 МГц, необходимо поделить на 32. При этом мы получим частоту в 250 кГц (период 4 мкс) и используем ее в качестве синхронизирующей для системы ШИМ. Поскольку мы используем период в 256 импульсов, частота управления ШИМ будет равна 976 Гц (период = 4 мкс/импульс×256 импульсов).

Ниже приведен исходный текст программы, позволяющей изменять коэффициент заполнения ШИМ, чтобы стабилизировать скорость на уровне 1600 об/мин.

Эта функция используется начальной установки скорости вращения двигателя.

/********************************************************************/

/*init_PWM(): инициализация модуля ШИМ контроллера 68HC12           */

/********************************************************************/

void init_PWM() {

 PWTST = 0x00; /*Установить порт ШИМ в нормальный режим */

 PWCTL = 0x00; /*установить режим фронтовой ШИМ */

 PWCLK = 0x28; /*без объединения каналов, ECLK/128*/

 PWPOL = 0x01; /*активный уровень 1 */

 DDRP = 0xFF; /*Порт P конфигурировать как выходной */

 PWEN = 0x01; /*установить выход ШИМ */

 PWPER0 = 255; /*установить для ШИМ период, соответствующий 976 Гц */

 PWDTY0 = 171; /*установить начальный коэффициент заполнения */

               /* на отсутствие движения */

}

/********************************************************************/

Если в процессе выполнения программы обработки прерывания RTI скорость вращения превышает 1600 об/мин, коэффициент заполнения ШИМ уменьшается на 1. С другой стороны, если скорость вращения меньше, чем 1600 об/мин, коэффициент заполнения ШИМ увеличивается на 1. Каждое приращение или снижения коэффициента заполнения ШИМ приводит к изменению скорости на 8.5 об/мин. Попробуйте вычислить это изменение в качестве домашней работы (задание 19).

Код, позволяющий сравнить опорную скорость с действительной и скорректировать ее обеспечивается в программе обработки RTI прерывания (RTLISR). В этой распечатке кода, функции поддержки ЖКД были помещены в файл для включения LCD.H с их функциями-прототипами.

7.4.4. Структура программы и блок-схема алгоритма 

На рис. 7.20 показаны блок-схема алгоритма и структура программы регулирования частоты вращения маломощного двигателя постоянного тока. В следующем параграфе приведен полный текст программы для этого проекта. Попробуйте разработать блок-схемы алгоритмов для каждой функции проекта в качестве домашней работы (задание 20).

а) Управление скоростью двигателя. Блок-схема алгоритма

б) Управление скоростью двигателя. Структура программы

Рис. 7.20. К программе управления скоростью двигателя

7.4.5. Программный код

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

//********************************************************************

//имя файла: motor.с

//авторы: Стив Барретт и Даниэль Пак

//дата создания: 10 марта 2003

//последняя редакция: 2 марта 2004

//********************************************************************

//включенные файлы

#include <912b32.h>

#include  //Функции поддержки ЖКИ

#pragma interrupt_handler RTI_isr//Объявляет подпрограмму обработки

                                 // прерывания по запросу (RTI)


//используемые подпрограммы

void initialize_ports(void); // Инициализация портов

void RTI_isr(void); //программа обработки прерывания по RTI

void initialize_RTI(void); // Инициализация модуля RTI

void initialize_PA(void); // Инициализация модуля счетчика событий PA

void initialize_PWM(void); // Инициализация модуля ШИМ


//глобальные переменные

unsigned int old_count; // последнее содержимое PA

int RTI_int_count = 0; // Используется для подсчета числа

                       // прерывания RTI

unsigned char PWM_duty_cycle=172; // Начальный коэффициент заполнения ШИМ

unsigned int desired_motor_RPM = 1600; //эталон скорости двигателя


//********************************************************************

void main(void) {

 asm(".area vectors(abs)\n" // команда встроенного ассемблерного кода

  ".org 0xFFF0\n" // Инициализировать вектор прерывания RTI

  ".word _RTI_isr\n"

  ".org 0xFFF8\n"

  ".word 0x8000, 0x8000, 0x8000, 0x8000\n"

  ".text");

 initialize_ports(); // Инициализация портов

 initialize_LCD(); // Инициализация ЖКИ

 initialize_RTI(); // Инициализация модуля RTI

 initialize_PA(); // Инициализация модуля PA

 initialize_PWM(); //Инициализация модуля ШИМ

 asm("cli"); // Разрешить все маскируемые прерывания

 while(1) // Непрерывный цикл в ожидании прерывания

 {

  ;

 }

}


//********************************************************************

//initialize_ports: обеспечивает начальную конфигурацию портов

//ввода - вывода

//********************************************************************

void initialize_ports(void) {

 DDRP = 0xFF; // Установить PORTP как выходной для ШИМ

 DDRT = 0x00; //Установить PORTT как входной, а вывод PORTT[7]

              // как вход аккумулятора PA

 DDRB = 0xFF; //PORTB на вывод - двунаправленная

              //магистраль данных для ЖКИ

 DDRDLC = 0xFF; //порт PORT DLC на вывод - линии управления ЖКИ

}


//********************************************************************

//RTI_isr: Программа обработки прерывания по меткам реального времени

//********************************************************************

void RTI_isr(void) {

 unsigned int new_count;

 unsigned int pulse_count;

 unsigned int current_RPM;

 float max_count = 65535.0;

 new_count = PACNT; // Получить текущее число импульсов в аккумуляторе

 if (new_count > old_count) // Определить текущее число импульсов

  pulse_count = new_count - old_count;

 else pulse_count = (unsigned int)(max_count-(float)(old_count+new_count));

 // Преобразовать число импульсов кодера в об/мин

 current_RPM = (unsigned int)(pulse_count/0.032768);

 // Изменить число прерываний RTI в счетчике

 RTI_int_count = RTI_int_count + 1;

 if (RTI_int_count == 10) // Изменить на ЖКИ значение после 10 прерываний RTI

 {

  display_count_LCD(current_RPM); // Изменить показания ЖКИ

  RTI_int_count=0; // Сбросить счетчик прерываний RTI

 }

 // Изменить значение скорости двигателя

 if (current_RPM < desired_motor_RPM) PWM_duty_cycle = PWM_duty_cycle + 1; // Ускорить двигатель

 else PWM_duty_cycle = PWM_duty_cycle - 1; // Замедлить двигатель

 // Изменить скорость двигателя с помощью ШИМ

 PWDTY0 = PWM_duty_cycle; //коэффициент заполнения

 old_count = new_count;

 RTIFLG = 0x80; //сбросить флаг прерывания RTI

}


//********************************************************************

// Initialize_RTI: конфигурирует регистры связанные с RTI

//- Регистр управления RTI (RTICTL):

// - разрешение RTI, флаг RTIE

// - установка периода RTI на 32.768 мс

// - сброс RTI, бит RTIF регистре Флагов RTI (RTIFLG)

//********************************************************************

void initialize_RTI(void) {

 RTICTL = 0x86; // Установить период RTI 32.768 мс

 RTIFLG = 0x80; // сброс RTI

}


//********************************************************************

// Initialize_RTI_PA: инициализация 68HC12 аккумулятора импульсов PA

//********************************************************************

void initialize_PA(void) {

 TIOS = 0x00; // конфигурировать канал 7 для работы в качестве счетчика

 TCTL1 = 0x00; // импульсов оптического кодера

 OC7M = 0x00; // Включить бит разрешения таймера

 TSCR = 0x80; // Установить разрешение для аккумулятора по фронту

              //импульса,

 PACTL = 0x50; // Режим счета событий

}


/********************************************************************/

//initialize_PWM: генерировать сигнал ШИМ 976 Гц с */

// коэффициентом заполнения 67.2% */

/********************************************************************/

void initialize_PWM(void) {

 PWTST =0x00; /*установить ШИМ в режим нормальной работы */

 PWCTL =0x00; /*установить выравнивание по левой границ */

 PWCLK = 0x28; /*без конкатенации, разделить clk на 32 */

 PWPOL = 0x01; /*состояние : высокое затем переход к низкому */

 DDRP = 0xFF; /*установить порт PORTP как выходной */

 PWEN = 0x01; /*разрешение на ШИМ для канала 0 */

 PWPER0= 0xFF; /* установить период равным 256 */

 PWDTY0= PWM duty_cycle; /* установить коэффициент заполнения равным 172 */

}

/********************************************************************/

7.4.6. Испытания

Установка для испытания системы стабилизации скорости вращения была показана на рис. 7.18. Это — очень сложная система, содержащая целый ряд компонентов и микроконтроллерную систему 68HC12. Чтобы гарантировать успешную работу системы, настоятельно рекомендуется применять обсужденный в главе 2 метод измерений. Собрав и запустив всю систему, можно включить двигатель на умеренную нагрузку, и убедиться, что система стабилизирует скорость вращения на уровне в 1600 об/мин.

7.5. Парящий робот