Многие вещи нам непонятны не потому, что наши понятия слабы, но потому, что сии вещи не входят в круг наших понятий.
Козьма Прутков

Меню навигации для мобильных

АЦП в AVR

Автор Shaman, 06 Июль, 2022, 23:25

« предыдущая - следующая »

Shaman

Поясните пожалуйста как правильно понимать ниже приведенный текст.
• Bit 6 – ADSC: ADC Start Conversion
In Single Conversion mode, write this bit to one to start each conversion. In Free Running mode,
write this bit to one to start the first conversion. The first conversion after ADSC has been written
after the ADC has been enabled, or if ADSC is written at the same time as the ADC is enabled,
will take 25 ADC clock cycles instead of the normal 13. This first conversion performs initializa-
tion of the ADC.
ADSC will read as one as long as a conversion is in progress. When the conversion is complete,
it returns to zero. Writing zero to this bit has no effect.

Как его понимаю я:

Данный бит запускает работу АЦП в выбраном режиме. И если режим одиночный после выполнения преобразования данный бит встаёт в 0 и АЦП выключается, а в режиме постоянного измерение выключить его уже невозможно судя по этому предложению: "Writing zero to this bit has no effect". Я прав или нет?

Slabovik

Ну, тут дословно если перевести, получается:
в режиме "Одиночное преобразование" необходимо устанавливать (записывать) бит в состояние "1" для запуска каждого преобразования.
в режиме "Постоянное преобразование" (Free running mode) установка бита в "1" запускает первое преобразование (после чего АЦП уже работает непрерывно цикл за циклом, Slabovik). Первое преобразование после запуска происходит за 25 тактов АЦП (не путать с тактами ядра процессора, Slabovik), все последующие происходят за 13 тактов.
Если работа АЦП уже предварительно разрешена, первое преобразование запускается по факту записи "1" в ADCS, либо это можно сделать одновременно с разрешением работы АЦП.

Регистр ADCS можно считывать в любое время работы АЦП. Пока АЦП не закончит цикл, из ADCS считывается "1". Когда АЦП закончит преобразование, он установит значение регистра ADCS в "0" (что означает, что данные в регистрах АЦП достоверны, т.е. готовы для считывания, Slabovik). Запись "0" в этот регистр со стороны программы, когда АЦП уже работает, не производит никаких действий (независимо от режима, но может привести к заблуждению о готовности данных а регистрах данных АЦП, Slabovik)

Т.е. в режиме одиночного преобразования нужно в регистр каждый раз забрасывать "1" для запуска каждого следующего АЦП (это удобно, когда нет нужды, чтобы АЦП молотил постоянно, он всё-таки электричество потребляет), в "0" он становится сам самостоятельно, когда АЦП закончит вычисления и АЦП при этом останавливается, ожидая следующей записи "1" в ADCS. Принудительная запись 0 в ADCS, как указано, никаких действий не произведёт - АЦП свой цикл всё-равно закончит и данные в регистры для считывания положит. Но по появлению "0" в ADCS программа следит за актуальностью данных в регистрах данных АЦП.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Shaman

Благодарю.
Т.е. получается что в режиме постоянных измерений после каждого цикла это бит становится 0, а потом снова 1 ?
А для чего т огда вот это бит:
↓ спойлер ↓
• Bit 4 – ADIF: ADC Interrupt Flag
This bit is set when an ADC conversion completes and the Data Registers are updated. The
ADC Conversion Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set.
ADIF is cleared by hardware when executing the corresponding interrupt Handling Vector. Alter-
natively, ADIF is cleared by writing a logical one to the flag. Beware that if doing a Read-Modify-
Write on ADCSRA, a pending interrupt can be disabled. This also applies if the SBI and CBI
instructions are used.
[свернуть]

Он тоже взводится по окончанию вычислений и сообщает, что данные в регистрах валидные. А сбрасываетя либо вручную, либо по выполнению прерывания.






Slabovik

Покурим?  ;)
Вот что пишут от этом бите, когда Single Mode
Цитата: 24.3 Single Conversion modeA single conversion is started by disabling the power reduction ADC bit, PRADC, (in Section 10.10 "Minimizing Power Consumption" on page 37) by writing a logical zero to it and writing a logical one to the ADC start conversion bit, ADSC. This bit stays high as long as the conversion is in progress and will be cleared by hardware when the conversion is completed.
То же самое в режиме AutoTriggering (это когда преобразование запускается либо от внешнего сигнала либо от таймера)
ЦитатаADSC can also be used to determine if a conversion is in progress. The ADSC bit will be read as one during a conversion, independently of how the conversion was started.
Ну т.е. этот бит читается как "1" тогда и только тогда, когда АЦП производит преобразование, ну т.е. работает.
Предполагаю, что в режиме Free Running этот бит всегда будет читаться как "1". Поищем в документации. И тут же ниже видны картинки

Для одиночного преобразования
Single_conversion_ADC_timecycle.gif

Для непрерывного преобразования.
Free_running_ADC_timecycle.gif

Ну вот, становится видна разница.
Получается, что ADSC в режиме FreeRunning не получится использовать как индикатор готовности данных. Однако да, для этого есть специальный регистр ADIF
ЦитатаAfter the conversion is complete (ADIF is high), the conversion result can be found in the ADC result registers (ADCL, ADCH).
Но тут есть нюансы, ибо если используются прерывания для чтения из АЦП, то
ЦитатаThis bit is set when an ADC conversion completes and the data registers are updated. The ADC conversion complete interrupt is executed if the ADIE bit and the I-bit in SREG are set. ADIF is cleared by hardware when executing the corresponding interrupt handling vector.
Из сказанного выходит, что в режиме прерываний нет нужды специально следить за ADIF, т.к. при входе в прерывание он автоматически сбрасывается и задача программы просто быстренько прочитать данные из регистров, положив их куда надо.

Загадку, что надо делать, если прерывание не используется, а режим Free Running попробуешь сам разгадать? Хотя сразу скажу, что такой режим используется не часто, потому что в программном режиме всё-таки удобнее одиночное преобразование, где алгоритм прост: запустил - подождал - прочитал - пока вычисляю АЦП стоит, с если стало надо - снова запустил.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Shaman

Цитата: Slabovik от 07 Июль, 2022, 20:04Загадку, что надо делать, если прерывание не используется, а режим Free Running попробуешь сам разгадать?

Если правильно понимаю из даташита и вот этого примера, если прерывание не используется нужно вручную обнулять ADIF после того как программа прочитала данные из регистров.

Я всегда плохо читал эти  диаграммы. И вот здесь не понятно. В даташите написано, что сброшенное состояние ADIF это 1 т.е. как только он принимает значение 0 можно читать. А на рисунках в One conversion он 0, в Next conversion 1, а по моему пониманию 0 должен быть между ними, а во время, 1.
Поясните, пожалуйста, где я тут не правильно читаю?

Slabovik

Цитата: Shaman от 13 Июль, 2022, 14:46В даташите написано, что сброшенное состояние ADIF это 1 т.е. как только он принимает значение 0 можно читать.
Это в каком месте?

зы: читать диаграммы по-любому намного лучше, т.к. они не требуют траты 30+ минут на просмотр видео только ради того, чтобы в этом видео найти словесное описание их же...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Shaman

This bit is set when an ADC conversion completes and the Data Registers are updated. The ADC Conversion Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set.
ADIF is cleared by hardware when executing the corresponding interrupt Handling Vector. Alter-natively, ADIF is cleared by writing a logical one to the flag. Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt can be disabled. This also applies if the SBI and CBI
instructions are used.

Этот бит устанавливается, когда преобразование АЦП завершается и регистры данных обновляются. Прерывание завершения преобразования АЦП выполняется, если установлены бит ADIE и бит I в SREG. ADIF сбрасывается аппаратно при выполнении соответствующего вектора обработки прерывания. В качестве альтернативы ADIF очищается путем записи логической единицы во флаг. Имейте в виду, что при выполнении чтения-модификации-записи на ADCSRA ожидающее прерывание может быть отключено. Это также применяется, если SBI и CBI используются инструкции.


Slabovik

Погоди-погоди.... Ты только что утверждал, что в даташите написано, что
Цитата: Slabovik от 14 Июль, 2022, 10:03сброшенное состояние ADIF это 1
а из даташита цитируешь, что "для сброса ADIF запишите в него "1". Разве это одно и то же? Это совершенно разные вещи. Первое - это состояние. Второе - действие для изменения состояния.

Кстати, это не только ADC касается, у других прерываний похожий механизм сброса флага.

Хотя, лучшей практикой было бы конечно взять да попробовать, мало ли чего...

зы: подозреваю, что есть некоторое заблуждение относительно того, что в реальности из регистра вовсе не обязано читаться то, что туда было записано. Ибо регистр записи и регистр чтения - это разные устройства.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Shaman

А вот тут я перестаю что-то понимать. Для меня это одно и тоже. Если написано что для сброса состояния нужно записать 1 значит в регистре это бит выставится в 1. А значит сброшенное состояние 1. И вот этот автор говорит то же самое.

Цитата: Slabovik от 14 Июль, 2022, 15:13зы: подозреваю, что есть некоторое заблуждение относительно того, что в реальности из регистра вовсе не обязано читаться то, что туда было записано. Ибо регистр записи и регистр чтения - это разные устройства.

Возможно, но это то регистр состояния если его разнести на 2 разных адреса то как сам контроллер будет понимать как он настроен если писаться будет по одному адресу. читаться из другого.

А проверить в живую пока не получается, код не работает в протеусе. Даже напрямую скопированный из видео выше.

Видео длинное, поэтому вот код
#define F_CPU 1000000UL
#include <avr/io.h>
int main(void)
{
    DDRB |= (1<<2) | (1<<1) | (1<<0);
    PORTB &= ~((1<<2) | (1<<1) | (1<<0));
    DDRC &= ~(1<<1);

    //Настраиваем АЦП
    ADCSRA |= (1<<ADEN);        //разрешаем работу АЦП,
    ADCSRA |= (1<<ADFR);       //разрешаем работу АЦП с тригера
    ADCSRA &= ~(1<<ADPS2);       
    ADCSRA |= (1<<ADPS1) | (1<<ADPS0); //частота дискретизации 125 кГц
    ADMUX |= (1<<REFS1) | (1<<REFS0); //используем внутреннее опорное напряжение
    ADMUX |= (1<<ADLAR);              //правостороннее выравнивание
    ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1)); //выставляем в качестве входа АЦП порт PC0
    ADMUX |= (1<<MUX0);
    ADCSRA |= (1<<ADSC);       //запускаем АЦП

    while (1)
    {
         if (ADCSRA & (1<<4))
             {
                if (ADC >= 600)     //в место 600 нужно подставить необходимое заначение для опорного напряжения 1,1 V
                {
                        PORTB |= (1<<0);
                        PORTB &= ~(1<<1);
                        PORTB &= ~(1<<2);
                }
                if (ADC >= 560 && ADC < 600)     //в место 600 нужно подставить необходимое заначение для опорного напряжения 1,1 V
                {
                        PORTB &= ~(1<<0);
                        PORTB |= (1<<1);
                        PORTB &= ~(1<<2);
                }
                if (ADC < 560)     //в место 600 нужно подставить необходимое заначение для опорного напряжения 1,1 V
                {
                        PORTB &= ~(1<<0);
                        PORTB &= ~(1<<1);
                        PORTB |= (1<<2);
                }
                
                ADCSRA |= (1<<4);
                
             }
    }
}
[свернуть]

У меня он почему-то работает как выключатель, т.е. если резистор стоит в минимуме то горит красный, стоит только повернуть сразу зелёный. И для кода из видео я естественно нарисовал схему с Атмега8 и компилил под неё. В живую собрать пока не могу, нет в наличии дросселя на 10uH, а купить не позволяет время.

Slabovik

А контроллер не понимает, контроллер - это суть набор логических элементов. И вот этот набо логических элементов работает так, что хардверный сигнал "RD/WR" выбирает, к какому набору элементов идёт обращение. И всегда (подчеркну: ВСЕГДА) это разные наборы. Их схемотехнически можно организовать так, что вход регистра, который для чтения, будет подключён к выходу регистра, который на запись, и тогда результат чтения будет идентичен записанному результату, это удобно в ряде случаев (например: память), но в общем случае это не так и по одному и тому же адресу, определяемому хардверными линиями адреса, чтение и запись производится в/из разных наборов логических ячеек, а арбитражем для этого как раз служит сигнал "RD/WR".
Следовательно вот это утверждение
ЦитатаЕсли написано что для сброса состояния нужно записать 1 значит в регистре это бит выставится в 1. А значит сброшенное состояние 1
не является верным. В даташите чётко написано, что сброшенное состояние "0", но для получения "0" при чтении записать надо "1". Это не только у Mega такой прикол, это и у Tiny аналогично, и на форумах тоже часть народ недопонимает это место. Но ещё раз скажу: лучше проверить, поскольку у меня, не часто практикующего последнее время, в голове набор приёмов для кучки разных процессоров и ядер, поэтому где-то могу и ошибиться. Но документация - наше всё, я её и толкую. Ладно, поехали дальше.

Настоятельно рекомендую вписывать в условия более явные признаки.
Вот это: if (ADCSRA & (1<<4)) хорошо бы писать хотя бы так: if ((ADCSRA & (1<<ADIF))>0) потому что компилятор бывает игнорирует такие неявно заданные условия, либо интерпретирует их не так, как видит человек (уже ведь были примеры из практики).

Далее, сразу, как только вошли в это условие (бит=1), надо сделать две вещи. Порядок не особо принципиален, т.к. это произойдёт быстро.
Первое: сбросить бит: ADCSRA=ADCSRA |(1<<ADIF)
Второе: прочитать один раз регистры ADC в двубайтную переменную, с которой дальше и работать.
Если такого чтения не сделать, программа каждый раз, когда у ней будет появляться ADC, будет читать эти регистры, а по прошествии некоторого времени они могут стать неактуальными (часики-то тикают, несмотря на то, что 13 тактов при 125 кГц - это довольно много, но это и не много, когда начинают выполняться какие-либо действия).

Да, в конфигурации обозначен запуск от триггера. А что служит триггером? ADTS не вижу, где задан... По-идее для FreeRunning надо его в 0 поставить (там три бита). Считаем, что FreeRunning стоит.

PortB используется для контрольки?

Для некоторого ускорения работы программы, IF'ы, которые проверяют прочитанные данные с ADC (пусть это будет переменная ADCresult), лучше вложить друг в друга. Будучи записанными просто последовательно, они каждый раз выполняются все. Будучи вложенными, внутренние будут выполняться только при совпадении условий. Например
if (ADCresult <= LowLevel) { Signal = Warning_LOW; }
  else { if (ADCresult >= NormLevel) { Signal = Warning_OK;}
            else { Signal = Warning_NearLOW;}
        }

Ну и здесь, записать в PortB состояние Signal (да, меня практика научила меньше порты дёргать)
Думаю, мелочи, которые я не конкретизировал, вполне понятны и реализация их не составит большого труда. :)

И, да... Надо помнить, что резистор - это очень-очень шумный элемент. Поэтому после резистора надо бы сигнал пофильтровать. Между средним выводом и ногой входа АЦП воткнуть, скажем, 10 кОм, а между входом АЦП и землёй какой-нибудь конденсатор на микрофарад (с SMD керамикой нынче нет проблем, их как грязи). И ещё... Дроссель в питании секции АЦП на этапе макета - элемент вовсе не обязательный. без него всё будет работать не хуже. На этапе реализации можно уже и поставить.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Shaman

Ёкарный бабай  :)  Благодарю за потраченное на ответ время.
Помимо понимания он породил ещё больше вопросов. Попробую разложить комментарии и новые вопросы по степени важности относительно изначального поста.

1.
Цитата: Slabovik от 14 Июль, 2022, 18:21В даташите чётко написано, что сброшенное состояние "0", но для получения "0" при чтении записать надо "1".

Если не сложно киньте, пожалуйста, выдержки из мануала. Я пока не нашел.

2.
Цитата: Slabovik от 14 Июль, 2022, 18:21Вот это: if (ADCSRA & (1<<4))

Я тоже склоняюсь к тому, что компилятор gcc может это неверно интерпретировать в примере сработало скорее всего потому, что использовалась среда разработки для атмеги.

3.
Цитата: Slabovik от 14 Июль, 2022, 18:21ADTS не вижу, где задан...
Пример писался для Атмеги8, в ней нет регистра ADCSRB а соответственно и бита ADTS. А также 5 бит в регистре ADCSRА называется ADFR вместо ADATE и просто включает режим FreeRunning. Комментарий остался от кода для 328-й, забыл исправить  :)

4.
Цитата: Slabovik от 14 Июль, 2022, 18:21И всегда (подчеркну: ВСЕГДА) это разные наборы.
Ок, очень ценная информация. А сигнал RD/WR чисто хардверный или может управляться тем кто пишет программу? Если нет, то тогда интересно зачем сделана такая реализация?

5.
Цитата: Slabovik от 14 Июль, 2022, 18:21PortB используется для контрольки?
В основной программе для отладки, потом удалю, а в примере из видео для вывода индикации.

6.
Цитата: Slabovik от 14 Июль, 2022, 18:21Для некоторого ускорения работы программы,
Да, благодарю, я потом так и делаю просто в СИ я новичок и так мне проще запустить незнакомый код и отследить где он не работает и уж потом упрощать.

Я знаю что контроллер не понимает. Просто так короче писать чем: "С помощью какого набора логических элементов и связей между ними происходит определение как, когда и в какой регистр записывать?"  :)

Slabovik

Цитата: Shaman от 15 Июль, 2022, 17:27выдержки из мануала. Я пока не нашел.
Как это не нашёл, если мне же эту фразу и цитировал?  :o Описание флага ADIF...  :-\
Цитата: Shaman от 15 Июль, 2022, 17:27А сигнал RD/WR чисто хардверный или может управляться тем кто пишет программу?
Это сигнал арбитража шины, и он хардверный (и не единственный). Но косвенно программист на него влияет, например, когда пишет ReadPort или ReadRAM (команды чисто условные). Формируется же непосредственно шинным контроллером, который у ATmega спрятан где-то внутри, как и сама шина. Вообще, хорошо бы поизучать построение вычислительных систем, начиная с i8080 или с той же Моторолы (правда, у нас она мало распространена, зато i8080 как грязи), там шинный контроллер - устройство частично внешнее (частично всё-таки в чипе CPU, который и задаёт правила его работы), но зато сама шина полностью открыта для фантазий конструктора. Другое дело, что по скорости та же Mega кроет его как... но это другой вопрос.

Код для Mega8 в общем-то такой же, как для других "мег", но да, есть некоторые отличия в управляющих регистрах.

Тут, как говорится, твой ход - реализовать изменения в тестовой программе и посмотреть, как оно отзовётся. Ну не расчехлять же мне свой макетатор...  ::)

зы: Кстати, вот здесь интересная ситуация разбирается. Может пригодится...

Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Shaman

Цитата: Shaman от 14 Июль, 2022, 14:38This bit is set when an ADC conversion completes and the Data Registers are updated. The ADC Conversion Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set.
ADIF is cleared by hardware when executing the corresponding interrupt Handling Vector. Alter-natively, ADIF is cleared by writing a logical one to the flag. Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt can be disabled. This also applies if the SBI and CBI
instructions are used.

Этот бит устанавливается, когда преобразование АЦП завершается и регистры данных обновляются. Прерывание завершения преобразования АЦП выполняется, если установлены бит ADIE и бит I в SREG. ADIF сбрасывается аппаратно при выполнении соответствующего вектора обработки прерывания. В качестве альтернативы ADIF очищается путем записи логической единицы во флаг. Имейте в виду, что при выполнении чтения-модификации-записи на ADCSRA ожидающее прерывание может быть отключено. Это также применяется, если SBI и CBI используются инструкции.

Господи, да где!?  :o З десь сказано, что он очищается записью 1, но где написано, что очищенное состояние это 0

Slabovik

Просто хотя бы совмести первые восемь слов с картинкой, которую я уже показывал. Даташит - это всё-таки цельный документ, а не просто обрывки фраз...  :-\  Плюсом надо помнить правила логики, что если об инверсии флага нигде не упоминается, следовательно прочитанное состояние "1" означает "Set", а "0" ~ "Clear". Об это говорит любой фрагмент текста, где описывается его работа, и также диаграммы.

зы: можно было уже с полдюжины раз попробовать на макете ( я же знаю, он у тебя есть), чем впустую перепихиваться по поводу "а вот там этого не написано"...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Shaman

Всё, ушел в долгое переваривание полученной информации и отпуск во время которого расстояние от меня до макетки буде около 3000 км поэтому проверить в железе пока не вариант.  :)
Резюмирую то что понял.
1. 1 регистр всегда равно 2 независимых ячейки памяти на чтение и запись (если есть где инфа на русском зачем так сделано буду признателен если поделитесь)

2. Непонятно по какой причине запись 1 в ячейку/регистр? (даже не знаю как это назвать) записи приводит к установке 0 в ячейке чтения. Опять же непонятно как и зачем так сделано. Если знаете, расскажите, пожалуйста.

А тем кто так пишет документацию, желаю самим же её и читать до конца дней.  :)

Slabovik

1. Это логическое следствие схемотехники на транзисторах (и лампах тоже). И сделано оно так не `зачем', а 'потому что'
2. По причине, что так организовано схемотехнически. А за логическим объяснением смысла - это лучше к производителю.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.