В этой части рассказывается о том как запустить АЦП на плате Launchpad и воспользоваться продвинутыми режимами работы АЦП: одноканальным многократным и многоканальным многократным и взаимодействием с контроллером прямого доступа в памяти --- DMA. Благодаря этим режимам АЦП можно автоматизировать процесс аналого-цифрового преобразования, в отличие от PIC и AVR где нужно каждый раз дёргать бит в управляющем регистре, потом ждать конца преобразования и извлекать результат. Достаточно настроить АЦП один раз и потом АЦП будет автоматически пересылать результаты на нужный адрес в памяти без вмешательства со стороны пользователя. Несмотря на то, что микроконтроллер MSP430G2553, который идёт в комплекте с платой Launchpad, относится к категории бюджетных, плата позволяет получать результаты недостижимые для МК семейств PIC и AVR. О подробностях см. под кат.
Пример работы с АЦП для платы Launchpad в однократном режиме можно найти тут . Здесь работа с АЦП почти ничем не отличается от PIC и AVR. Нужно сконфигурировать АЦП, подождать конца преобразования и забрать результат из регистра ADC10MEM. Мы же будем реализовывать автоматическую запись в циклическом режиме 16-ти выборок с пятого канала АЦП в массив. Как только мы доходи до конца массива, то переходим к записи в нулевой элемент. Потом мы будем брать значения с этого массива, делать усреднение и выдавать значение напряжения по запросу с UART. Будет использоваться модифицированный проект из предыдущего поста.
В микроконтроллере MSP430G2553 АЦП 10--разрядное. За него отвечает модуль ADC10. Работа с ADC10 отличается от работы с 12-разрядным АЦП в более продвинутых МК MSP430. Всего в MSP430 4 режима АЦП:
- Однократный одноканальный (CONSEQ 0)
- Повторяющийся одноканальный (CONSEQ 2)
- Однократный многоканальный (CONSEQ 1)
- Повторяющийся многоканальный (CONSEQ 3)
Для реализации нашей задачи нам нужен повторяющийся одноканальный режим. Чтобы перевести в него АЦП, нужно установить биты CONSEQ_2 в регистре ADC10CTL1. Ещё нужно установить бит MSC регистра ADC10CTL0, который разрешает многократные преобразования.
Модуль ADC10, в отличие от модуля ADC12. имеет встроенный контроллер прямого доступа к памяти (DMA). Он отвечает за пересылку результата преобразования из регистра ADC10MEM на нужный адрес в памяти. Контроллер DMA работает без участия человека. Достаточно проинициализировать его один раз. Работать с ним даже проще, чем с АЦП в однократном режиме. Нам понадобятся три регистра этого модуля:
- ADC10DTC0. В нём нужно установить бит ADC10CT, который разрешает последовательные переносы.
- ADC10SA (start address). Стартовый адрес, то есть адрес в памяти на который будет автоматически пересылаться содержимое регистра ADC10MEM после завершения преобразования. После каждого преобразования увеличивается на единицу, пока не дойдёт до значения ADC10DTC1. Потом снова сбрасывается к исходному значению. И так в цикле.
- ADC10DTC1 (data transfer count) --- количество пересылок данных. Должен быть равен длине массива, где будут храниться результаты АЦП.
Теперь рассмотрим пример. В предыдущий проект нужно добавить два файла adc.c и adc.h, которые содержат функции инициализации работы с АЦП. Исходный код можно забрать здесь.
Содержимое файла adc.c следующее
#include "msp430.h"
#include "stdint.h"
#include "adc.h"
void ADC_init_intref_repeating(uint16_t *buffer,uint16_t samples_cnt) 
// Эта функция инициализирует АЦП. Параметры --- адрес массива,
// где будут храниться результаты преобразования и его длина
{
    ADC10CTL0 &= ~ENC; // Остановим АЦП
    ADC10CTL0 = MSC + // Многократные выборки
                SREF_1 +  // АЦП работает от внутреннего источник 
                          // образцового напряжения (ИОН)
                ADC10SHT_2 + // параметры быстродействия АЦП
  REF2_5V + // Напряжение ИОН VREF=2.5V
  REFON + // Включим ИОН
  ADC10ON; // Включим АЦП
    ADC10CTL1 = INCH_5 + // Выбираем 5-й канал - ножку P1.5
                SHS_0 +
  ADC10SSEL_0 + // Источник тактирования АЦП = SMCLK
  ADC10DIV_0 + // Без предделителя
  CONSEQ_2; // Переводим АЦП в многократный одноканальный режим
    ADC10AE0 = BIT5; // Переводим ножку P1.5 в режим аналогового входа
    ADC10DTC0 = ADC10CT; // Настройка DMA
    ADC10DTC1 = samples_cnt; // Длина массива
    while (ADC10CTL1&BUSY);
    ADC10SA = (int) buffer; // Стартовый адрес
    ADC10CTL0 |=ENC + ADC10SC; // Запускаем АЦП
}
А это файл adc.h:
#ifndef ADC_H #define ADC_H #include "stdint.h" void ADC_init_intref_repeating(uint16_t *adc,uint16_t samples_cnt); #endifНе забываем добавить эти файлы в Makefile, иначе ничего не скомпилируется.
Теперь модифицируем файл main.c. Сначала добавлям #incude "adc.h". Потом добавляем глобальный массив, в который будут складываться результаты преобразования и необходимые дефайны.
#define SAMPLES_CNT 16 uint16_t adc[SAMPLES_CNT];
Перед уходом в спящий режим инициализируем АЦП.
ADC_init_intref_repeating(adc,SAMPLES_CNT);
И модифицируем обработчик прерывания от UART, чтобы он при получении символа "3" выдавал по UART значение напряжения на ножке P1.5 в милливольтах.
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++) { // Выполним усреднение
                 adc_avg += adc[i]; // Просуммируем все элементы массива
              }
       adc_avg /= SAMPLES_CNT; // И разделим сумму 
                                      // на количество элементов
              mV = (adc_avg*2500)/1024; // Рассчитаем напряжение
              printf("P1.5 pin voltage = %d mV\n",mV); 
            // Печатаем напряжение в UART
       break;
    default:  uart_putc(c);
              break;
    }
}
Исходный код для всего можно забрать с Гитхаба здесь. В следующей части будет рассказано о многоканальном режиме АЦП.
 
Комментариев нет:
Отправить комментарий