Программирование LaunchPad от TI. Осваиваем аналого-цифровое преобразование и управляем широтно-импульсной модуляцией

Недавно на нашем сайте стартовала серия статей по электроприводу постоянного тока. В них будут описаны основные принципы управления двигателями постоянного тока, а финалом станет создание лабораторного стенда привода постоянного тока с системой подчинённого регулирования. Регулирование тока и напряжения будет осуществляться с помощью ШИМ. Статейку о ШИМ на MSP430G2553 я совсем недавно выложил в нашей рубрике.
Как вы понимаете, для того чтобы регулировать ток и напряжения, необходимо знать параметры системы. То есть нужно измерять скорость вращения, момент, текущее значение тока и напряжения. А так как мы давно используем везде цифровую вычислительную технику, то и перечисленные величины должны быть представлены в цифровом виде для того, чтобы мы могли произвести вычисления.
Функции преобразования аналогового сигнала в цифровой выполняет аналогово-цифровой преобразователь (далее АЦП). Он так же есть в контроллере MSP430G2553, который установлен в нашем LaunchPad от TI.
Именно АЦП и будет посвящена тема данной статьи.
Итак, микроконтроллер имеет в своем составе периферийный модуль ADC10, который представляет из себя 10-битный аналого-цифровой преобразователь (АЦП). Для работы АЦП необходимо опорное напряжение. MSP430 дает возможность использовать один из двух внутренних источников опорного напряжения (на 1.5 В и на 2.5 В) или внешний источник опорного напряжения. Вообще ADC10 может работать с двумя источниками опорного напряжения, верхнее напряжение может принимать значения от 1.4 В до значения напряжения питания, а нижнее — от нуля до 1.2 В.
АЦП в MSP430 может работать в одном из четырех режимов:
• однократное преобразование сигнала с одного из каналов
• однократное преобразование сигналов с нескольких входов
• повторяющиеся преобразования сигнала с одного из входов
• и повторяющиеся преобразования с нескольких каналов
Как и для любой другой периферии, для аналого-цифрового преобразователя необходимо задать источник тактирования — все это настраивается в регистрах ADC10.

ADC10AE0 — Регистр разрешает АЦП использовать выводы порта как входы.

ADC10CTL0

SREFx (биты 15-13): Выбор одной из 8 различных конфигураций верхнего и нижнего ИОН (источника опорного напряжения).
SHTx (биты 12-11): Продолжительность выборки. Напряжение входящего сигнала, поддерживается на одном уровне, за счёт зарядки конденсатора. Данный регистр контролирует длину времени зарядки. Понятно, что чем больше времени идет измерение, тем оно точнее, но частота выборки микроконтроллера ограничена, и есть опасность, что уровень входящего сигнала может успеть измениться до конца измерения. Можно установить время выборки в течение 4, 8, 16 или 64 тактов ADC10CLK.
REF2_5V, REFON (биты 6,5): Выбор между встроенным ИОН на 1.5 В и 2.5 В. Отключение/включение встроенного ИОН.
ADC10ON, ENC, ADC10SC (биты 4,1,0): Включение АЦП, разрешение преобразования и запуск преобразования, соответственно.
ADC10IE, ADC10IFG (биты 3,2): Разрешение прерывания и флаг прерывания.

ADC10CTL1

INCHx (биты 15-12): В одноканальном режиме, выбирает канал, с которого ведется преобразование. В режиме последовательных преобразований (многоканальном), выбирает старший канал (с максимальным номером).
ADC10DF (бит 9): Выбор формата результата: обычный двоичный код или дополнение.
SSELx, DIVx (биты 4-3,7-5): Выбор источника тактового сигнала и делитель частоты тактового сигнала на 1-8.
CONSEQx (биты 2-1): Выбор режима преобразования.
BUSY (бит 0): Флаг, только для чтения, сообщает о занятости АЦП преобразованием, в данный момент.

ADC10MEM

Когда преобразование заканчивается, в нём сохраняется результат, который мы можем прочесть оттуда. Если бит формата результата ADC10DF в регистре ADC10CTL0, сброшен (= 0), мы можем считать результат, как есть, и 0x00 будет эквивалентно значению нижнего ИОН, а 0xFF будет эквивалентно значению верхнего ИОН. Все средние значения, распределены в промежутке между этими двумя точками. Если бит формата ADC10DF установлен (= 1), результат сохраняется как дополнение. Это может использоваться, в некоторых конфигурациях, для передачи данных.
Попробуем сконфигурировать АЦП.

ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;

Так будет выглядеть строка конфигурации регистра ADC10CTL0. В ней :
1) SREF_0 — Используем Vcc/Vss(аналоговая земля) для верхнего/нижнего ИОН;
2) ADC10SHT_2 — 16 x ADC10CLKs (выборка за 16 тактов), включаем АЦП;
3) ADC10ON – включаем АЦП;

Таким же образом настраиваем регистр ADC10CTL1.

ADC10CTL1 = INCH_4 + SHS_0 + ADC10SSEL_0 + ADC10DIV_0 + CONSEQ_0;

1) INCH_4 + SHS_0 — выбираем входы для АЦП которые необходимо сканировать с 4 по 0.
2) ADC10SSEL_0 — Источник тактирования АЦП = SMCLK
3) ADC10DIV_0 – без предделителя
4) CONSEQ_0 – одноканальный режим АЦП

Так же нужно указать с какого порта необходимо считывать аналоговый сигнал.

ADC10AE0 = BIT4; // подключаем 4 бит порта Р1 к АЦП. (всего их 8, с Р1.0 по Р1.7)

Для того чтоб АЦП начал преобразование необходимо дать ему команду, для этого после всех настроек запишем в регистр ADC10CTL0 следующее:

ADC10CTL0 |= ENC; // Разрешаем преобразования.

Целиком это выглядит вот так:

void ADC_init(void)
 {
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;

ADC10CTL1 = INCH_4 + SHS_0 + ADC10SSEL_0 + ADC10DIV_0 + CONSEQ_0;

ADC10AE0 = BIT4; 

ADC10CTL0 |= ENC;
}

После таких действий, нам остаётся только вызвать функцию void ADC_init(void) в основном теле программы и в дальнейшем лишь считывать значения, которые будут записываться в ADC10MEM.

ADC_init();

for (;;) 
{
ADC10CTL0 |= ADC10SC; // начинаем новое преобразование

while ((ADC10CTL1 & ADC10BUSY) == 0x01); // ждем, когда преобразование закончится

TACCR1 = ADC10MEM;// загружаем в CCR1 значение ADC10MEM
}

Для того, чтобы сделать наглядный пример работы АЦП, давайте воспользуемся предыдущей статьёй про ШИМ и вместо того, чтобы в регистре ТАCCR1 прибавлять или отнимать значения, будем записывать их с АЦП. То есть значении регистра ТАCCR1 мы будем менять во время работы программы вручную, путём изменения напряжения на входах АЦП. Именно поэтому выше в цикле for (;;) записано TACCR1 = ADC10MEM, таким образом после каждого преобразования АЦП, в регистр таймера будет записываться новое значение, и если напряжении на входе АЦП измениться то и коэффициент заполнения ШИМ тоже. А так, как в прошлой статье с помощью ШИМ мы регулировали яркость свечения светодиода, то в этот раз будет происходить тоже самое. Максимальное значение которое может выдать АЦП будет 1024, а значит и значение ТАCCR1 (время импульса) тоже будет варьироваться от 0 до 1024. Поэтому сделаем период ШИМ немножечко больше, например 1100, запишем ТАCCR0=1100.

Целиком программа будет иметь следующий вид:

#include <msp430G2553.h> 

/* Обьявление функций */

void ADC_init(void);

void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // останавливаем сторожевой таймер

P1DIR |= 0x61;// настройка портов 1,5,6 на выход, а остальных на вход.

P1OUT = 0x00; // устанавливаем все каналы в ноль.

BCSCTL1 = CALBC1_1MHZ; // Устанавливаем частоту DCO

DCOCTL = CALDCO_1MHZ;

TACCR0 = 1100; // Период

TACCR1 = 1; // Время импульса

TACCTL0 = CCIE; // Разрешаем прерывание таймера по достижению CCR0.

TACCTL1 = CCIE; // Разрешаем прерывание таймера по достижению CCR1.

TACTL = TASSEL_2 + ID_2 + MC_1 + TACLR; //Настройка режима работы Timer_A:

// TASSEL_2 - источник так тов SMCLK (SubMainCLocK),
// по умолчанию настроенны х на работу от DCO
// ID_3 - делитель частоты на 8, от 1MHz это будет 125kHz
// MC_1 - режим прямого счёта (до TACCR0)
// TACLR - начальное обнуление таймера

_enable_interrupt();

ADC_init();

for (;;)
 {
ADC10CTL0 |= ADC10SC; // начинаем новое преобразование

while ((ADC10CTL1 & ADC10BUSY) == 0x01); // ждем, когда преобразование закончится

TACCR1 = ADC10MEM;// загружаем с CCR1 значение ADC10MEM
}
}
void ADC_init(void) 
{
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;

// Используем Vcc/Vss(аналоговая земля) для верхнего/нижнего ИОН,
// 16 x ADC10CLKs (выборка за 16 тактов), включаем АЦП.

ADC10CTL1 = INCH_4 + SHS_0 + ADC10SSEL_0 + ADC10DIV_0 + CONSEQ_0;

// Вход A4, делитель ADC10CLK на 1, одноканальный режим.

ADC10AE0 = BIT4; // Разрешаем вход АЦП на порту P1.4
ADC10CTL0 |= ENC; // Разрешаем преобразования.
}
/* Обработчики прерываний */

#pragma vector = TIMER0_A0_VECTOR

__interrupt void CCR0_ISR(void)
{
P1OUT |= BIT0;

P1OUT &= ~ BIT6;

P1OUT &= ~ BIT5;
}
#pragma vector=TIMER0_A1_VECTOR

__interrupt void CCR1_ISR(void)
{
P1OUT &= ~BIT0;

P1OUT |= BIT6;

P1OUT |= BIT5;

TAIV &= ~TA0IV_TACCR1; //сбрасываем все флаги прерывания от таймера
}

Казалось бы все, программа готова, прошивай контроллер и наслаждайся её работой. Но в этот раз придётся немного поработать и паяльником, ну или хотя бы очень аккуратно скрутить несколько проводов. Так как мы работаем с АЦП, то на её входы необходимо подать аналоговый сигнал, который мы сможем изменять путём каких либо действий. Самым простым будет сделать резистивный делитель. Для этого понадобиться всего один переменный резистор, кусочек макетной платы, разъём и кусочек провода.

delitel22

Один крайний контакт резистора подключаем к ножке питания Vcc, второй крайний контакт к ножке минуса питания GND, а средний на канал АЦП, в нашем случае это порт Р1.4.

DSC_0934

DSC_0935

Вот так вот выглядит небольшое дополнение к LaunchPad, для работы с АЦП. На фотографии вы увидите еще пару резисторов и светодиодов. Это я продублировал уже имеющиеся на LaunchPad светодиоды , делать это не обязательно, достаточно только переменного резистора.
На видео ниже продемонстрирована работа данной программки. Как я уже говорил, данные навыки можно применить для управления электродвигателем, нагревателем, освещением и т.д.

You may also like...

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>