АЦП через регистры
- Войдите на сайт для отправки комментариев
Пт, 19/02/2016 - 22:54
Задумка сделать что то вроде быстрого вольтметра, использование analogRead не устраивает - сожрет слишком много быстродействия на ожидание ответа АЦП. Накарябал код с управлением АЦП через регистры. Опыта с регистрами АЦП ноль - пожайлуста гляньте на предмет явных косяков.
// Version 0.0 // Попытка опрашивать 5 каналов АЦП на прерываниях // Идея такая - цепляемся к таймеру Timer0 его все равно уже используется millis() // обрабатываем промежуточную переменную, програмируем АЦП на измерение следующего канала // АЦП измеряет, генерирует прерывание, подпрограмма читает данные и присваивает их промежуточной // переменной. И так по кругу... // Весь этот цирк для того чтобы микроконтролер не простаивал дожидаясь данных с АЦП // По идее 976 в секунду / на 5 каналов = 200 на канал // Если фильтровать по 16 значениям получается 12 измерений в секунду на каждый канал // Масив данных для програмирования входа мультиплексора АЦП unsigned char pin_AD [5] = {0x47,0x46,0x45,0x44,0x43}; // Если не лоханулся каналы 7, 6, 5, 4, 3 void setup() { //Нпастройка прерывания, используется Timer0 чтобы потом не накосячить с генерацией ШИП OCR0A = 0xAF; // Timer0 уже используется millis() - прерываемся где-то TIMSK0 |= _BV(OCIE0A); // посередине и вызываем ниже функцию "Compare A" Serial.begin(9600); // Настройка последовательного порта для отправки данных на компьютер // Програмирование АЦП DIDR0 = 0x3F; // отключаем цифровые входы, по идее только для порта А ADCSRA = 0xAF; // включаем АЦП, разрешаем прерывания, делитель = 128 ADCSRB = 0x40; // Включаем каналы MUX АЦП, режим постоянной выборки sei(); // устанавливаем флаг разрешающий прерывания ///////////////////////////////////////////////////////////// byte i=0; // Переменная для чтения масива, заодно и для разнесения данных с одного АЦП // на 5 переменных int analogValue = 0; // значение аналогового сигнала АЦП } /////////////////////////////////////////////////////////////// SIGNAL(TIMER0_COMPA_vect) // Прерывание вызывается 976.5625 в секунду { // действия при прерываниях от таймера // Тут будет присваивание данных переменной конкретного канала for (int i=0;i<5;i++) {ADMUX=pin_AD[i];} // Если не накосячил то работает так: // Берется байт с масива и пишется в регистр - задаем какой канал АЦП читать bitWrite(ADCSRA, 6, 1);// Запускаем преобразование установкой бита 6 (=ADSC) в ADCSRA } /*** Процедура обработки прерывания АЦП ***////////////////////// ISR(ADC_vect) { analogValue = ADCL; // сохраняем младший байт результата АЦП analogValue += ADCH << 8; // сохраняем старший байт АЦП bitWrite(ADCSRA, 6, 0); // Останавливаем АЦП, высокая частота чтения не нужна, запустим от таймера } void loop() { //Тут буду обрабатывать }
Использованны статьи, лучшее из найденного по теме:
http://robotosha.ru/arduino/analog-measurements-arduino.html
http://www.junradio.com/index/analogovye_vkhody_arduino/0-284
есть два комментария
1. для повышения точности и стабильности измерений рекомендую делать серию измерений и потом усреднять. Так как АЦП 10 бит, то можно делать от 2 до 64 измерений в 16-ти битный аккумулятор и потом усреднять.
2. если стоит цель делать измерения часто и при этом не тратить времени на обработку то лучше применять режим Free running в таймере. В этом случае запуск измерения будет делать автоматически как только завершилось предыдущее
Бред какой-то. Вы зачем крутите вчодной MUX?
А на форуме нельзя файл приатачить? У меня есть проверенный код с таймером 1
https://drive.google.com/file/d/0Bw4tXXvyWtFVUGw5T280QnZlb1U/view?usp=sharing
AS31979 Вы сравнивали, ваш метод быстрее библиотеки CyberLib.h?
MagicianT в даташите явно сказано, что при использовании тригеров для запуска преобразования или free running изменение admux без остановки преобразования приведет к ситуации когда атмел не гарантирует какой канал был преобразован - в ADMUX может быть одно значение, а преобразование совершено с другим
поэтому если нужно делать преобразование по разным каналам перед исзменением ADMUX нужно останавливать преобразование
Не обязательно. Вы всегда можете проверить с какого входа приходят самплы. Не останавливая АЦП скорость преобразования 14 тактов, а с остановкой 25. Почти вдвое. В примере когда АЦП clock 1 МГц, частота до 76 кГц. Там бывает что MUX не успевает переключиться и отстанет на один такт, но если скорост самплирования не менять на лету, выставив один раз и проверив с какого входа идут измерения, потом можно быть увереным что это не изменится
Primer:
user is thus advised not to write new channel or reference selection values to ADMUX until one ADC clock cycle
after ADSC is written.
Правильно, 1 clock , в примере:
Да, но при частоте АЦП выше 250кгц атмел не рекомендует использовать 10бит преобразования, а только 8
для 10бит частота должна быть менее 250кгц
Не могу найти, в сети был график снижения точности АЦП от частоты, наверное удалили. Не всё там так резко, и тот-же дата шит говорит что исходная точность на 200 кГц +- 2LSB.
Дааа..., я вроде на пояснения в скече не поскупился.
Строка 7, 8, 9 там и про фильтрацию, и про частоту преобразования. Задача выдавить из АЦП максимум не стоит - результаты измерений будет обрабатывать сам микроконтролер, куча данных которые микроконтролер не успеет обработать мне даром не нужна, 10-12 измерений по 5 каналам хватит.
Все извращения в коде направленны на то чтобы избежать простоя ядра микроконтролера при выполнении analogRead, меня больше всего волнует не накосячил ли я с битами при програмировании регистров АЦП.
Из всего написанного мне оказались полезны ответы:
MagicianT - протупил я, данные с АЦП можно при прерывании по таймеру сначала забрать измеренные, а потом уже запустить АЦП на однократное измерение следующего канала.
axill - "в даташите явно сказано, что..." никуда не спешу, лучше пару лишних команд всталю чем потом косяк словлю.
MagicianT - если не затруднит, сделайте пожайлуста руские коментарии к свроему примеру, я так понял что АЦП програмируется побитно называя компилятору нужную функцию. Но не уверен точно что именно делается. Не знал что в параметры АЦП можно задавать просто через названия функций.
Данные по снижению точности АЦП от частоты преобразования можно найти в первой ссылке, там в конце описанно понятнее чем в даташите.
Перевод имел бы смысл если даташит был бы на русском. А так не целесообразно. Дата шит скачали? Первое время постоянно приходится сверяться в каком регистре какой бит находится/прописать.
В общем первый блин как и ожидалось - комом, оптимизировал/отрихтовал.
За последующую обработку не беспокоюсь, всегда можно на монитор COM порта послать переменную для отладки. Сильно беспокоит програмирование АЦП - есть соображения где я облажался?
Там всё неправильно. Перечислять косяки нет смысла, Вы вообще ответы, что выше, читали?
AS31979, я не сразу понял что вы пытались изобразить. Этот режим называется Auto trigged mode using Timer в принципе штатный режим АЦП, вот так он будет по-человечески выглядеть:
MagicianT - "Там все неправильно..." и т.д. это конечно ОЧЕНЬ проясняет ситуацию, но новичку вроде меня проще перичитать даташит с нуля чем извлечь пользу с вашего ответа.
dimax - спасибо, ваш пример подходит идеально.
К сожалению с коментариями в примере не густо, прошу глянуть правильно ли я разшифровал действия. Есть подозрение что 14 строка примера изменяет нустройки опорного напряжения сделанные в 7 строке!?
Непонятно зачем использовалась нестандартная библиотека формирования задержек, в коде вроде нет ничего что бы нарушало работу millis, у меня заработало с стандартной задержкой.
Фактически тот же скечь только с моими коментариями:
Если кто будет повторять:
Входы выбираются в строках 34-36, но в отличии от analogRead пины выбираются довольно извращенным образом, сначала выбрали канал, при следующем прерывании дали задание АЦП мерять этот канал и только при третьем прерывании забрали результат.
Лично у меня при delay(100) COM порт лег менее чем за минуту.
Без конденсаторов на входах АЦП и AREF даные будут дико прыгать, на маленьких напряжениях контакты макетной платы могут внести такую погрешность что показания покажутся бредом!!!
AS31979,
- внешняя библа delay.h была взята на всякий случай, если я в процессе эксперементов собью настроки таймера0. Не потребовалась.
- в 8 строке корректнее написать так ADMUX=(1<<REFS0)|(1<<REFS1);
- в 22 строке старшие 5 бит регистра ADMUX остаются неизменны, точно как были заданы в сетапе. Обнуляются три младших бита, затем складываются с переменной n.
Не могу найти, в сети был график снижения точности АЦП от частоты, наверное удалили. Не всё там так резко, и тот-же дата шит говорит что исходная точность на 200 кГц +- 2LSB.
Не график, но для сравнения http://www.gammon.com.au/adc
dimax - 7 и 14 строку я имел в виду из вашего скеча, они у вас по идее конфликтуют. В своей версии я это учел, кроме того есть подозрение что 22 строка моего скеча делеет необязательной 8 строку.
Pyotr - по точности АЦП есть ссылка в самом первом сообщении.
dimax - 7 и 14 строку я имел в виду из вашего скеча, они у вас по идее конфликтуют. В своей версии я это учел, кроме того есть подозрение что 22 строка моего скеча делеет необязательной 8 строку.
Я же вам написал про это. Повторю ещё раз в команда ADMUX = ADMUX & 0b11111000 обнуляет только три младших бита. Старшие какие были такие и остаются.
Пытаюсь измерять ток, что-то как-то не заладилось...
Библиотека отсюда
ua6em - вы так "подробно и емко" описали проблему, что единственный ответ который я могу вам дать:
"Пытаюсь помочь, что-то как-то с помощью не заладилось..."
И думаю НИКТО НЕ СМОЖЕТ!!!!
ua6em - вы так "подробно и емко" описали проблему, что единственный ответ который я могу вам дать:
"Пытаюсь помочь, что-то как-то с помощью не заладилось..."
И думаю НИКТО НЕ СМОЖЕТ!!!!
думаю никто не поможет, с долей вероятности близкой к 100% чипы поддельные
PS надеялся, что в коде есть косяки, сделал скетч с чтением через analogRead(), ничего не изменилось...
Пины-то нагрузил? У них выход токовый
Пины-то нагрузил? У них выход токовый
1 ком там стандартная нагрузка
Может надо вынести из обработчика static uint8_t n = 0;?
А то case 1 никогда не происходит
P.S. Возможно это сделано специально, для проверки...догадался Штирлиц))
Ну тогда остается немыслимое - измерить мультиметром.
ua6em - вы так "подробно и емко" описали проблему, что единственный ответ который я могу вам дать:
"Пытаюсь помочь, что-то как-то с помощью не заладилось..."
И думаю НИКТО НЕ СМОЖЕТ!!!!
а как перенести измерение на пины A6 и A7?
Входы задаются этой строкой
Только произвольно в моем примере их изменять нельзя - переменная n одновременно переменная цикла.
Хорошо описана адресация входов АЦП тут: http://www.junradio.com/index/analogovye_vkhody_arduino/0-284
так? Если измеряем A5 A6 A7