В этой части рассказывается о том как запустить АЦП на плате 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 следующее
А это файл adc.h:
- #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; // Запускаем АЦП
- }
Не забываем добавить эти файлы в Makefile, иначе ничего не скомпилируется.
- #ifndef ADC_H
- #define ADC_H
- #include "stdint.h"
- void ADC_init_intref_repeating(uint16_t *adc,uint16_t samples_cnt);
- #endif
Теперь модифицируем файл 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;
- }
- }
Исходный код для всего можно забрать с Гитхаба здесь. В следующей части будет рассказано о многоканальном режиме АЦП.
Комментариев нет:
Отправить комментарий