Это руководство посвящено многоканальному режиму АЦП в микроконтроллере MSP430G2553 на плате Launchpad. В предыдущей части был рассмотрен одноканальный режим АЦП с прямым доступом к памяти DMA. Точно так же можно использовать АЦП и во многоканальном режиме. АЦП будет автоматически пересылать результаты преобразования со всех каналов на заданный адрес в памяти. В этом процессе ядро микроконтроллера не задействовано и может заниматься другими делами (например расчётами). Операции с АЦП выполняются как бы в отдельном потоке. Таким образом реализуется примитивная аппаратная многозадачность. Как это сделать рассмотрено под катом.
Чтобы перевести АЦП в повторяющийся многоканальный режим нужно установить биты CONSEQ_3 регистра ADC10CTL1 и задать количество каналов установив биты INCH_xx. В остальном процедура настройки АЦП не отличается от многоканального режима.
Если например установить биты INCH_7, то АЦП будет работать в восьмиканальном режиме. Микроконтроллер будет сканировать входы АЦП последовательно от седьмого до нулевого и каждый раз помещать результат преобразования в регистр ADC10MEM. Модуль DMA будет пересылать каждый раз результат из этого регистра на пользовательский адрес в памяти.
Значения регистра счётчика пересылок ADC10DTC1 нужно установить равным
ADC10CTL1 = число_каналов * количество_выборок
Результаты АЦП можно складывать в двухмерный массив, который следует объявить так
uint16_t adc[Количество_выборок][Число_каналов];
Теперь рассмотрим пример. Переведём АЦП в восьмиканальный режим, сделаем усреднение по четырём выборкам и выведем результат по запросу с UART. Для этого сперва модифицируем функцию инициализации АЦП из файла adc.cследующим образом:
#include "msp430.h"
#include "stdint.h"
#include "adc.h"
void ADC_init_intref_multichannel(uint16_t *buffer,uint16_t samples_cnt) // Init
ADC single channel oneshot with internal reference 2.5V
{
ADC10CTL0 &= ~ENC; // Стоп АЦП
ADC10CTL0 = MSC + // Разрешаем многократные выборки
SREF_1 + // Внутренний ИОН
ADC10SHT_2 + // Настраиваем скорость работы АЦП
REF2_5V + // Напряжение ИОН VREF=2.5В
REFON + // Включаем встроенный ИОН
ADC10ON + // Включаем АЦП
ADC10IE; // Разрешаем прерывание от АЦП
ADC10CTL1 = INCH_7 + // Сканировать 8 каналов АЦП начиная с A7 до A0
SHS_0 +
ADC10SSEL_0 + // Источник тактирования АЦП = SMCLK
ADC10DIV_0 + // без предделителя
CONSEQ_3; // Переводим АЦП во многоканльный режим
ADC10AE0 = BIT4+BIT5+BIT7; // Подключаем ножки P1.4, P1.5, P1.7
// как аналоговый вход
ADC10DTC0 = ADC10CT; // Разрешаем непрерывный перенос результатов
ADC10DTC1 = samples_cnt; // Число пересылок
//while (ADC10CTL1&BUSY);
ADC10SA = (int) buffer; // Стартовый адрес, куда отсылать результат АЦП
ADC10CTL0 |=ENC + ADC10SC; // Старт АЦП
}
В файле объявляем двухмерный массив, где будут храниться результаты АЦП:
#define SAMPLES_CNT 4 // Делаем 4 последовательных выборки #define CHANNELS_CNT 8 // По 8 каналам uint16_t adc[SAMPLES_CNT][CHANNELS_CNT];
В этом массиве нумерация каналов идёт с конца. То есть элемент adc[0][0] будет соответствовать 0-й выборке с 7-го канала, adc[0][1] - 0-й выборке с 6-го канала и так до adc[3][7], который соответствует 3-й выборке с нулевого канала.
И модифицируем обработчик прерывания от UART:
void uart_rx_isr(unsigned char c)
{
uint16_t mV=0; // Volatge in mV
uint32_t adc_avg=0; // ADC average
int i=0;
switch (c) {
case '1': P1OUT ^=LED_RED;
uart_puts("Red led toggled\n");
break;
case '2': P1OUT ^= LED_GREEN;
uart_puts("Green led toggled\n");
break;
case '3': adc_avg=0;
for (i=0;i < SAMPLES_CNT;i++) { // Вычисляем среднее напряжение
// по 5-му каналу
adc_avg += adc[i][((CHANNELS_CNT-1)-5)];
}
adc_avg /= SAMPLES_CNT;
mV = (adc_avg*2500)/1024;
printf("P1.5 pin voltage = %d mV\n",mV); // Печатаем напряжение
// на ножке P1.5
break;
default: uart_putc(c);
break;
}
}
Исходники для этого примера можно забрать с Гитхаба .
Комментариев нет:
Отправить комментарий