Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Разработка киберфизических систем на основе микроконтроллеров. Учебное пособие
.pdf
Лекция 2. Примеры решения типовых задач, решаемых при проектировании…
61
void setAnalogMux(ADC_modes mux); //Установить параметры
аналогового мультиплексора ( номер аналогового Pinа (ADC_A0- ADC_A7),
термодатчик (ADC_SENSOR)/ 1.1V (ADC_1V1)/ ADC_GND (по умолчанию
установлен A0)
void ADC_enable(void); // Включить АЦП
void ADC_disable(void); // Выключить АЦП (по умолчанию)
void ADC_setPrescaler(byte prescl); // Установка делителя часто-
ты АЦП из ряда (2, 4, 8, 16, 32, 64, 128) // (по умолчанию: 2)
void ADC_setReference(ADC_modes ref); // Выбор источника опор-
ного напряжения АЦП из ряда значений: (ADC_1V1, ADC_AREF,
ADC_VCC) // (по умолчанию установлен ADC_AREF)
void ADC_autoTriggerEnable(ADC_modes trig); // Разрешение ав-
тозапуска АЦП и выбор источника (сигнала автозапуска) (FREE_RUN,
ANALOG_COMP, ADC_INT0, TIMER0_COMPA, TIMER0_OVF, TIMER1_COMPB, TIMER1_OVF)
void ADC_autoTriggerDisable(void); // Запрещение автозапуска
АЦП // (по умолчанию этот режим запрещен)
void ADC_attachInterrupt(void (*isr)()); // Включить прерывание по
готовности АЦП и выбор функции, которая будет выполняться, когда
АЦП завершит преобразование
void ADC_detachInterrupt(void); // Выключить прерывание по
готовности АЦП // (по умолчанию оно выключено)
void ADC_startConvert(void); // Ручной запуск преобразования
unsigned int ADC_read(void); // Прочитать значение реги-
стров АЦП (Вызов до окончания преобразования возвращает неверный результат)
boolean ADC_available(void); // Проверить завершено ли текущее преобразование АЦП
unsigned int ADC_readWhenAvailable(void); // Дождаться окончания
текущего преобразования АЦП и вернуть результат
void ACOMP_attachInterrupt(void (*isr)(), ADC_modes source); // Разрешить прерывание от аналогового компаратора и выбрать при каком
типе событии оно будет вызвано (FALLING_TRIGGER, RISING_TRIGGER,
CHANGE_TRIGGER)
void ACOMP_detachInterrupt(void); // Выключить прерывание от
компаратора // (по умолчанию оно выключено)

2. Проектирование киберфизических систем
62
void ACOMP_enable(void); // Включить питание у компа-
ратора // (Внимание! По умолчанию оно включено)
void ACOMP_disable(void); // Принудительно выключить
питание компаратора
boolean ACOMP_read(void); // Прочитать значение выхода
компаратора
void ACOMP_setPositiveInput(ADC_modes in); // Настройка
подключения положительного входа компаратора (ADC_1V1, ADC_AIN0)
(по умолчанию: ADC_AIN0 - pin 6)
void ACOMP_setNegativeInput(ADC_modes in); // Настройка подклю-
чения отрицательного входа компаратора (ADC_AIN1, ANALOG_MUX)
(по умолчанию: ADC_AIN1 - pin 7)
// делитель 2: 3.04 мкс (частота оцифровки 329 000 кГц)
// делитель 4: 4.72 мкс (частота оцифровки 210 000 кГц)
// делитель 8: 8.04 мкс (частота оцифровки 125 000 кГц)
// делитель 16: 15.12 мкс (частота оцифровки 66 100 кГц)
// делитель 32: 28.04 мкс (частота оцифровки 35 600 кГц)
// делитель 64: 56.04 мкс (частота оцифровки 17 800 кГц)
// делитель 128: 112 мкс (частота оцифровки 8 900 Гц)
Функции библиотеки DirectTimers скачиваем, подключаем по ссылке
[11] https://github.com/GyverLibs/GyverTimers.
// Демонстрация всех функций библиотеки
#include <GyverTimers.h>
void setup() {
// Перенастроить таймер и задать ему период или частоту
// Все функции возвращают реальный период / частоту, которые мо-
гут отличаться от введенных
Timer2.setPeriod(1000); // Задать конкретный период 1000 мкс
(~ 1000 гц), вернет реальный период в мкс
Timer0.setFrequency(250); // Задать частоту прерываний таймера в
Гц, вернет реальную частоту в герцах
Timer1.setFrequencyFloat(50.20); // Задать частоту более точно, в
дробных числах, актуально для низких частот и таймера 1
// С этого момента таймер уже перенастроен и гоняет с выбранной
частотой / периодом

Лекция 2. Примеры решения типовых задач, решаемых при проектировании…
63
// Подключить прерывание таймера, с этого момента прерывания
начнут вызываться
Timer0.enableISR(); // Подключить стандартное прерывание,
канал А, без сдвига фаз
Timer2.enableISR(CHANNEL_B); // Подключить прерывание таймера 2, канал B
Timer1.enableISR(CHANNEL_A); // Подключить прерывание канала А
Timer1.enableISR(CHANNEL_B); // Подключить второе прерывание
таймера 1
// Прерывание уже начнет вызываться
// Если вдруг прерывание нужно отключить, не останавливая тай-
мер
Timer1.disableISR(CHANNEL_B);
// С этого момента прерывание B больше не будет вызываться
// Если нужно приостановить таймер ПОЛНОСТЬЮ, аппаратно
Timer2.pause();
// С этого момента таймер стоит на месте, содержимое счетчика
остается нетронутым
// Теперь таймер можно вернуть в строй
Timer2.resume();
// Таймер продолжил считать с того же места
// Если нужно полностью остановить таймер и сбросить содержи-
мое счетчика
Timer1.stop();
// Таймер стоит, счетчик сброшен
// Возвращаем таймер в строй
Timer1.restart();
// Таймер перезапущен, начал считать с начала
// Если нужно вернуть стандартные Arduino – настройки таймера
Timer0.setDefault();
// Теперь таймер работает в стандартном режиме
}
// подпрограммы обработки прерываний
ISR(TIMER1_A) {
}
ISR(TIMER1_B) {

2. Проектирование киберфизических систем
64
}
ISR(TIMER2_B) {
}
ISR(TIMER0_A) {
}
void loop() {
}
Пример № 1. Работа АЦП с ожиданием
#include <directADC.h>
#define PIN_RED 5
#define PIN_YELLOW 4
/* пример простой работы с АЦП с ожиданием*/
long TT,TOld;
void setup() {
// Настройки PINов для отображения на осциллографе
pinMode(PIN_YELLOW,OUTPUT);
digitalWrite(PIN_YELLOW,LOW);
pinMode(PIN_RED,OUTPUT);
digitalWrite(PIN_RED,LOW);
// Настройки АЦП
ADC_enable(); // вызывается обязательно
ADC_setPrescaler(2); // без вызова – делитель 2
ADC_setReference(ADC_VCC); // без вызова – ADC_AREF
setAnalogMux(ADC_A4); // выбрать ADC_A0-ADC_A7 /
ADC_SENSOR – термометр / ADC_1V1 / ADC_GND // Без вызова - ADC_A0
}
void loop() {
TT=micros();
if ((TT-TOld)>=100){
TOld=TT;
digitalWrite(PIN_RED,LOW);
digitalWrite(PIN_YELLOW,HIGH);
ADC_startConvert(); // ручной старт преобразования
while (!ADC_available()); // пока преобразование не готово – ждем
digitalWrite(PIN_YELLOW,LOW);

Лекция 2. Примеры решения типовых задач, решаемых при проектировании…
65
digitalWrite(PIN_RED,HIGH);
}
}
Из рис. 15 понятно, что 94 % времени процессор ожидает начала нового цикла преобразования, а 6 % времени ожидает конца преобразования от
АЦП. В данном примере процессор занят 100 % времени только задачей
оцифровки одного канала АЦП. При работе программы картинка на экране
осциллографа «дрожит», что говорит о невысокой стабильности частоты
дискретизации. В таблице измерений видно, что среднее значение периода
дискретизации 93,7 мкс (7 % от заданной), но при этом среднее квадратичное отклонение периода дискретизации 23, 6 мкс., т.е. относительная погрешность порядка 23 % – весьма невысокая величина. Это связано с тем,
что организация цикла опроса АЦП, ожидание результата преобразования
организованы чисто программным путем, причем в этот цикл могут вклиниваться прерывания от системного таймера.
Рис. 15. Цикл работы 100 мкс, АЦП выполняет
преобразование за 6,01 мкс (желтый луч)

2. Проектирование киберфизических систем
66
Пример № 2. Работа АЦП с использованием прерываний
Для наглядности при обработке прерывания от таймера выставляем в
высокий уровень цифровой выход 4, а при обработке прерывания от АЦП
выставляем в высокий уровень цифровой выход 5.
#include <GyverTimers.h>
#include <directADC.h>
#define PIN_RED 5
#define PIN_YELLOW 4
/* пример работы с АЦП по прерываниям от таймера*/
void setup() {
// Настройки PINов для отображения на осциллографе
pinMode(PIN_YELLOW,OUTPUT);
digitalWrite(PIN_YELLOW,LOW);
pinMode(PIN_RED,OUTPUT);
digitalWrite(PIN_RED,LOW);
// Настройки таймера-счетчика 1
Timer1.setFrequency(10 000); // Высокоточный таймер 1 для пер-
вого прерывания, частота – 3 000 Герц
Timer1.enableISR(); // Запускаем прерывание (по умолчанию канал А)
// Настройки АЦП
ADC_enable(); // вызывается обязательно
ADC_setPrescaler(16); // без вызова - делитель 2
ADC_setReference(ADC_VCC); // без вызова – ADC_AREF
ADC_attachInterrupt(adcReady); // добавим прерывание по готовности
setAnalogMux(ADC_A4); // выбрать ADC_A0-ADC_A7 /
ADC_SENSOR – термометр / ADC_1V1 / ADC_GND // Без вызова – ADC_A0
}
void loop() {
// Здесь можно выполнять любую полезную работу!
}
// Прерывание А таймера 1
ISR(TIMER1_A) { //
digitalWrite(PIN_YELLOW,HIGH);
ADC_startConvert(); // ручной старт преобразования
digitalWrite(PIN_YELLOW,LOW);
}

Лекция 2. Примеры решения типовых задач, решаемых при проектировании…
67
// Прерывание по готовности АЦП
void adcReady() {
digitalWrite(PIN_RED,HIGH);
int rr=ADC_read(); // забираем результат
digitalWrite(PIN_RED,LOW);
}
Рис. 16. Демонстрация времени работы прерывания от таймера желтым цветом,
красным цветом – время работы прерывания от АЦП (в сумме 8,67 мкс)
Изображение на экране не дрожит, это видно и по значению среднеквадратичного отклонения периода дискретизации – 438 нс, да и период
дискретизации выдерживается гораздо точнее (100, 094 мкс при нужных
100!). Процессор занят оцифровкой сигнала всего 8,5 % своего времени!
Это удалось достигнуть, переложив на специализированные аппаратные
блоки микроконтроллера (таймер и АЦП) функции организации цикла
опроса и аналого-цифрового преобразования параллельно с работой про-

2. Проектирование киберфизических систем
68
цессора. Процессор отвлекается только в начале цикла преобразования на
запуск АЦП (в прерывании таймера) и на считывание данных (в прерывании от АЦП).
Пример № 3. Работа с использованием аппаратного запуска АЦП
от таймера
Посмотрим, можно ли еще эффективнее использовать аппаратуру
микроконтроллера. АЦП имеет возможность аппаратно стартовать от события по каналу B таймера 1, тогда можно не использовать прерывание от
таймера 1A, в программе останется только прерывание от АЦП.
#include <GyverTimers.h>
#include <directADC.h>
#define PIN_RED 5
#define PIN_YELLOW 4
/* пример работы с АЦП по прерываниям от таймера*/
void setup() {
// Настройки PINов для отображения на осциллографе
pinMode(PIN_YELLOW,OUTPUT);
digitalWrite(PIN_YELLOW,LOW);
pinMode(PIN_RED,OUTPUT);
digitalWrite(PIN_RED,LOW);
// Настройки таймера-счетчика 1
Timer1.setFrequency(10 000); // Высокоточный таймер 1 для пер-
вого прерывания, частота – 3 000 Герц
// Timer1.enableISR(); // Запускаем прерывание (по умолч. канал А)
Timer1.enableISR(CHANNEL_B); // Подключить второе прерывание
таймера 1
// Настройки АЦП
ADC_enable(); // вызывается обязательно
ADC_setPrescaler(2); // без вызова – делитель 2
ADC_setReference(ADC_VCC); // без вызова – ADC_AREF
ADC_attachInterrupt(adcReady); // добавим прерывание по готовности
setAnalogMux(ADC_A4); // выбрать ADC_A0-ADC_A7 /
ADC_SENSOR – термометр / ADC_1V1 / ADC_GND // Без вызова – ADC_A0
ADC_autoTriggerEnable(TIMER1_COMPB); // Включить автозапуск
АЦП и выбрать событие (FREE_RUN, ANALOG_COMP, ADC_INT0, TIM-
ER0_COMPA, TIMER0_OVF, TIMER1_COMPB, TIMER1_OVF)

Лекция 2. Примеры решения типовых задач, решаемых при проектировании…
69
ADC_startConvert(); // ручной старт преобразования
}
void loop() {
// Здесь можно выполнять любую полезную работу!
}
// Прерывание B таймера 1
ISR(TIMER1_B) {
}
// Прерывание по готовности АЦП
void adcReady() {
digitalWrite(PIN_RED,HIGH);
int rr=ADC_read(); // забираем результат
digitalWrite(PIN_RED,LOW);
}
Рис. 17. Прерывания от АЦП, в которых выполняются действия по чтению данных

2. Проектирование киберфизических систем
70
Остались прерывания от АЦП (в которых выполняются действия по
чтению данных), прерывание от таймера пустое (нужен только его факт).
Длительность прерывания – 4,56 мкс (по сравнению со вторым примером
загрузка процессора уменьшилась вдвое).
Пример № 4. Работа с использованием аппаратного запуска АЦП
от таймера и библиотекой быстрого ввода-вывода дискретных сигналов
Рассмотрим и используем еще оставшиеся резервы. Дело в том, что
функция digitalWrite() в Arduino IDE (точнее в микроконтроллерном ядре)
очень медленная, занимает около 3–5 мкс. Можно воспользоваться библиотекой GyverIO [12] https://github.com/GyverLibs/GyverIO.
#include <Arduino.h>
#include "GyverIO.h"
#include <GyverTimers.h>
#include <directADC.h>
#define PIN_RED 5
#define PIN_YELLOW 4
/* пример работы с АЦП по прерываниям от таймера*/
void setup() {
// Настройки PINов для отображения на осциллографе
pinMode(PIN_YELLOW,OUTPUT);
digitalWrite(PIN_YELLOW,LOW);
pinMode(PIN_RED,OUTPUT);
digitalWrite(PIN_RED,LOW);
// Настройки таймера-счетчика 1
Timer1.setFrequency(10 000); // Высокоточный таймер 1 для первого
прерывания, частота – 3 000 Герц
// Timer1.enableISR(); // Запускаем прерывание (по умолч. канал А)
Timer1.enableISR(CHANNEL_B); // Подключить второе прерывание
таймера 1
// Настройки АЦП
ADC_enable(); // вызывается обязательно
ADC_setPrescaler(2); // без вызова – делитель 2
ADC_setReference(ADC_VCC); // без вызова – ADC_AREF
ADC_attachInterrupt(adcReady); // добавим прерывание по готовности
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
