воскресенье, 17 августа 2014 г.

Дополнительные режимы работы АЦП в MSP430. Часть 2.

Это руководство посвящено многоканальному режиму АЦП в микроконтроллере 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;
    }
}

Исходники для этого примера можно забрать с Гитхаба .

Комментариев нет:

Отправить комментарий