В этой части рассказывается о том как запустить АЦП на плате 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; } }
Исходный код для всего можно забрать с Гитхаба здесь. В следующей части будет рассказано о многоканальном режиме АЦП.
Комментариев нет:
Отправить комментарий