ILSF15 0 24 октября, 2020 Опубликовано 24 октября, 2020 (изменено) · Жалоба Уж извините, что снова прошу подсказки. Безуспешно пытаюсь понять, почему вот этот код в Arduino IDE не работает (писал по даташит): int main() { unsigned int result_l, result_h, result; unsigned char pin=7; while (1) { ADCSRA |= (1 << ADEN); // ADC Enable ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2); // ADC Prescaler CLK/128 ADMUX = pin; // ADC Source ADCSRA |= (1 << ADSC); // Start conversation while (bit_is_set(ADCSRA, ADSC)); // Wait for conversation end ADCSRA |= (0 << ADEN); // ADC Disable result_l = ADCL; // Get ADC LOW Byte result_h = ADCH; // Get ADC HIGH Byte result = result_l + result_h * 256; // Output Serial.begin(115200); Serial.print("Result = "); Serial.println(result); Serial.end(); for(volatile unsigned long int i=200000; i>0; i--); } return 0; } Ардуиновский отлично выводит на серийный порт данные с датчика влажности (с хорошей точностью: сухой - 1023, пальцами дотрагиваюсь - 1000-900, воду капаю - в зависимости от количества - 500-300): void setup() { Serial.begin(115200); } void loop() { int result = analogRead(A7); Serial.print("Result = "); Serial.println(result); delay(1000); } а нужный - выводит практически всё время 1023, пару раз удалось добиться хоть каких-то изменений в показаниях, при залитом датчике, но даже и это теперь не получается... Необходимо именно через регистры, мне в другую программу вставлять (а из main функция analogRead(); вообще нули выводит). Пробовал менять значение делителя и порядок операторов. Чего тут ещё не хватает-то? Изменено 24 октября, 2020 пользователем ILSF15 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 25 октября, 2020 Опубликовано 25 октября, 2020 · Жалоба 3 hours ago, ILSF15 said: ADMUX = pin; // ADC Source Судя по битам REFS1/REFS0, которые равны нулю, у вас опорное напряжение берётся с ножки AREF. Это правильно? У вас туда физически напряжение поступает? 3 hours ago, ILSF15 said: ADCSRA |= (0 << ADEN); // ADC Disable Эта строка бессмысленна, т.к. ничего не делает ровным счётом. Если хотите снять бит ADEN, то писать надо так ADCSRA &= ~(1 << ADEN); // ADC Disable Далее, вынесете всю инициализацию АЦП за цикл. Зачем вы каждый раз инициализируете его? Достаточно только запускать преобразование. Остальное по AVR я уже подзабыл, т.к. давно с ними не работаю. Но, рекомендую следующее: 1. На вход АЦП вместо датчика подключите потенциометр. Поглядите, меняются ли коды АЦП от нуля до значения полной шкалы - 1. 2. Убедитесь, что функция Serial.println способна выводить числа от нуля до 65535 (16 бит). Это можно сделать просто несколькими строками с записью этих чисел. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 (изменено) · Жалоба 7 hours ago, haker_fox said: Судя по битам REFS1/REFS0, которые равны нулю, у вас опорное напряжение берётся с ножки AREF. Это правильно? У вас туда физически напряжение поступает? Я сам не очень это дело понимаю, но судя по тому, что я прочёл про работу функции analogRead(); - там используется такое же опорное напряжение, и подключение одно и то же. 7 hours ago, haker_fox said: Если хотите снять бит ADEN, то писать надо так ADCSRA &= ~(1 << ADEN); // ADC Disable Спасибо за поправку. 7 hours ago, haker_fox said: вынесете всю инициализацию АЦП за цикл Там, где оно будет использоваться, оно будет включаться по требованию и выключаться для экономии. Написал так, потому что первое преобразование занимает больше циклов - для воспроизведения условий. Не принципиально. Вынес - результат тот же. 7 hours ago, haker_fox said: Остальное по AVR я уже подзабыл, т.к. давно с ними не работаю. Но, рекомендую следующее: 1. На вход АЦП вместо датчика подключите потенциометр. Поглядите, меняются ли коды АЦП от нуля до значения полной шкалы - 1. 2. Убедитесь, что функция Serial.println способна выводить числа от нуля до 65535 (16 бит). Это можно сделать просто несколькими строками с записью этих чисел. 1. Нет потенциометра. Скорее всего, меняться не будут: что-то не так в коде, если с ардуиновским датчик работает исправно, а с этим - нет. 2. Способна. И в ардуиновском коде, и в моём выводит 1023 и меньшие, а большего быть не может, преобразователь десятибитный. Видимо, чтобы понять, что не так, нужен кто-то, кто знает, что именно делает ардуиновский код, и чем это отличается от кода, который написал я (моих весьма скромных знаний не хватает, чтобы понять, что там в ардуиновских библиотеках написано, и воспроизвести это в С...) Изменено 25 октября, 2020 пользователем ILSF15 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 (изменено) · Жалоба Пока что сумел заставить работать вот такого монстрика, но это же уродское уродство... Хорошо бы всё-таки найти адекватное решение... unsigned char pin=20; unsigned int acd_r; int main() { ADCSRA |= (1 << ADEN); while (1) { setup(); Serial.begin(115200); Serial.print("Result = "); Serial.println(acd_r); Serial.end(); } return 0; } void setup() { acd_r=analogRead(pin); } Изменено 25 октября, 2020 пользователем ILSF15 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 119 25 октября, 2020 Опубликовано 25 октября, 2020 · Жалоба 2 часа назад, ILSF15 сказал: Я сам не очень это дело понимаю, но судя по тому, что я прочёл про работу функции analogRead(); - там используется такое же опорное напряжение, и подключение одно и то же. Судя по схеме, на вход AREF никакое напряжение не подается, поэтому вам в биты REFS1, REFS0 надо записать 01 (если ваш входной сигнал не превышает 1.1 В и вы хотите испльзовать внутренний источник опрного напряжения) или 11 (если вы хотите использовать в качестве опорного напряжения напряжение питания AVcc). То есть ваша строка выбора канала должна выглядеть так: ADMUX = (1 << REFS1) | (1 << REFS0) | pin; // AREF = VREFin (1.1 v) // или так: ADMUX = (0 << REFS1) | (1 << REFS0) | pin; // AREF = AVCC 13 часов назад, ILSF15 сказал: ADCSRA |= (1 << ADEN); // ADC Enable ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2); // ADC Prescaler CLK/128 У вас есть какая-то уверенность, что до выполнения этого кода в регистре не были выставлены единицы в каких-то ненужных вам битах? Тут не нужна операция |= типа "чтение-модификация-запись", в первой строке достаточно простой операции записи, которая точно выставит в нули все ненужные вам биты. ADCSRA = (1 < ADEN); Ну и напоследок можно смело объединить эти две строки в одну, уменьшив размер и время выполнения вашей программы (пусть и немного, но все же - копейка рубль бережет): ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); 13 часов назад, ILSF15 сказал: result_l = ADCL; // Get ADC LOW Byte result_h = ADCH; // Get ADC HIGH Byte result = result_l + result_h * 256; Это избыточно. В заголовочном файле описан регистр 16-битный регистр ADC, составленный из этих двух регистров и комилятор сам знает правильный порядок чтения таких регистров, так что совершенно безопасно писать result = ADC; И напоследок: если вы будете использовать в качестве входов АЦП ноги PC0...PC5, то будет очень полезно отключить от них цифровые сигналы при помощи регистра DIDR0. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 (изменено) · Жалоба 43 minutes ago, Сергей Борщ said: Судя по схеме, на вход ARef никакое напряжение не подается. Подключение вот такое: Spoiler (Правая сторона меня не касается, и контроллер не UNO, но не суть). К AREF ничего не подключено, подключено к 5V, GND и A7. Функция analogRead(); использует, если верить источникам, значения REFS1, REFS0 по умолчанию (0). Как я понимаю, любые другие значения требуют дополнительного подключения внешнего конденсатора и подключения датчика на AREF вместо 5V. В библиотеке wiring_analog.c от Arduino IDE код выглядит Spoiler uint8_t analog_reference = DEFAULT; // DEFAULT = 0 int analogRead(uint8_t pin) { uint8_t low, high; #if defined(analogPinToChannel) #if defined(__AVR_ATmega32U4__) if (pin >= 18) pin -= 18; // allow for channel or pin numbers #endif pin = analogPinToChannel(pin); #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) if (pin >= 54) pin -= 54; // allow for channel or pin numbers #elif defined(__AVR_ATmega32U4__) if (pin >= 18) pin -= 18; // allow for channel or pin numbers #elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) if (pin >= 24) pin -= 24; // allow for channel or pin numbers #else if (pin >= 14) pin -= 14; // allow for channel or pin numbers #endif #if defined(ADCSRB) && defined(MUX5) // the MUX5 bit of ADCSRB selects whether we're reading from channels // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high). ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5); #endif // set the analog reference (high two bits of ADMUX) and select the // channel (low 4 bits). this also sets ADLAR (left-adjust result) // to 0 (the default). #if defined(ADMUX) #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) ADMUX = (analog_reference << 4) | (pin & 0x07); #else ADMUX = (analog_reference << 6) | (pin & 0x07); #endif #endif // without a delay, we seem to read from the wrong channel //delay(1); #if defined(ADCSRA) && defined(ADCL) // start the conversion sbi(ADCSRA, ADSC); // ADSC is cleared when the conversion finishes while (bit_is_set(ADCSRA, ADSC)); // we have to read ADCL first; doing so locks both ADCL // and ADCH until ADCH is read. reading ADCL second would // cause the results of each conversion to be discarded, // as ADCL and ADCH would be locked when it completed. low = ADCL; high = ADCH; #else // we dont have an ADC, return 0 low = 0; high = 0; #endif // combine the two bytes return (high << 8) | low; } Судя по тому, что я понимаю оттуда - менять значения этих битов мне не следует (экспериментировать наугад точно не хочу - как бы чего не спалить). 43 minutes ago, Сергей Борщ said: У вас есть какая-то уверенность, что до выполнения этого кода в регистре не были выставлены единицы в каких-то ненужных вам битах? Надо признать, что я понятия не имею,что выставляет Arduino IDE, (видимо, нужные значения, что доказывается тем, что работает мой уродец с функцией setup), поэтому как раз и предпочитаю не трогать то, что трогать не надо. Делители перепробовал все - без результата. 43 minutes ago, Сергей Борщ said: Это избыточно. Опять же, опирался на datasheet и на описание функции analogRead, приведённое выше. 43 minutes ago, Сергей Борщ said: использовать в качестве входов АЦП ноги PC0...PC5 Только А6 и А7, остальные заюзаны, как цифровые входы/выходы (разумеется, не здесь, а в целевой программе). Возможно, стоит ещё раз подчеркнуть, что даже с тем уродцем, которого я написал через функцию setup(); (см. выше) датчик работает исправно Изменено 25 октября, 2020 пользователем ILSF15 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 (изменено) · Жалоба Воткнул в виде уродца в основную программу - судя по всему, работает (там нет вывода на серийный порт - в зависимости от данных датчика должно срабатывать или не срабатывать реле, частота опроса тоже зависит значения от последнего замера). Но хотелось бы всё-таки понять, что именно делает с регистрами функция analogRead (и/или сама среда Arduino IDE), и воспроизвести это в "пристойном" варианте. Изменено 25 октября, 2020 пользователем ILSF15 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 25 октября, 2020 Опубликовано 25 октября, 2020 · Жалоба 3 hours ago, ILSF15 said: Подключение вот такое: AREF-то здесь не виден. Вам нужно смотреть схему электрическую принципиальную, которую предоставил уважаемый @Сергей Борщ. 34 minutes ago, ILSF15 said: Но хотелось бы всё-таки понять, что именно делает с регистрами функция analogRead REFS1/0 точно равны нулю в момент преобразования АЦП? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 · Жалоба Хм... очень странно... В первую попытку приложенный ниже код не сработал - а теперь да (соответственно, вместо уродца вызов стандартной функции языка Ардуино - гораздо пристойней, возможно, на этом можно и остановиться на первый случай): int main() { while (1) { unsigned char pin=21; unsigned int acd_r; ADCSRA |= (1 << ADEN); acd_r=analogRead(pin); ADCSRA &= ~(1 << ADEN); Serial.begin(115200); Serial.print("Result = "); Serial.println(acd_r); Serial.end(); for (volatile unsigned int i=1000000; i>0; i--); } return 0; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 119 25 октября, 2020 Опубликовано 25 октября, 2020 · Жалоба 3 часа назад, ILSF15 сказал: Функция analogRead(); использует, если верить источникам, значения REFS1, REFS0 по умолчанию (0). [...] В библиотеке wiring_analog.c от Arduino IDE код выглядит Приведенный код противоречит вашему утверждению из начала цитаты. В приведенном коде в эти биты заносится значение analog_reference. 16 минут назад, haker_fox сказал: Вам нужно смотреть схему электрическую принципиальную, Я тоже не обратил внимания, что AREF выведен на разъем. Но раз ILSF15 утверждает, что к этому контакту ничего не подключенно - значит, АЦП нужно настраивать на работу от внутренней опоры или от AVcc. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 (изменено) · Жалоба 41 minutes ago, haker_fox said: AREF-то здесь не виден Правый верхний угол контроллера, сразу под джамперами. Никуда не подключен. Если об этом речь. Если речь о внутренней логике контроллера - простите, ничего не могу сказать. Я уже писал, что не очень понимаю на таком уровне. 41 minutes ago, haker_fox said: REFS1/0 точно равны нулю в момент преобразования АЦП? Для интересу набросал программку с выводом значений регистров на серийный порт. До вызова analogRead: ADCSRA = 0 ADMUX = 0 После (в бинарном представлении): ADCSRA = 10010000 ADMUX = 1000111 Интересно, что в datashit комбинация REFS1/0 = 10 указана, как зарезервированная 25 minutes ago, Сергей Борщ said: В приведенном коде в эти биты заносится значение analog_reference. В приведённом коде analog_refrence = DEFAULT, а DEFAULT (тоже где-то в библиотеках ардуино накопал) определена как 0. 25 minutes ago, Сергей Борщ said: АЦП нужно настраивать на работу от внутренней опоры или от AVcc 01 и 11 подразумевают наличие внешнего конденсатора на пине AREF, которым я вообще не пользуюсь. 10 зарезервирован (хотя, как я писал выше, ардуино выдаёт именно такое значение регистра). Что именно нужно? Изменено 25 октября, 2020 пользователем ILSF15 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 (изменено) · Жалоба Стоп. Тьфу. Он же срезал передний 0 с ADMUX! (ворона я, не посчитал биты). То есть, да, выходит, что нужно использовать REFS1/0 = 01! UPD: Да! Заработало, как надо (и 18 байт экономии после компиляции). Всем спасибо за подсказки! Изменено 25 октября, 2020 пользователем ILSF15 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ILSF15 0 25 октября, 2020 Опубликовано 25 октября, 2020 · Жалоба 2 hours ago, ILSF15 said: В первую попытку приложенный ниже код не сработал - а теперь да Тоже, кстати, догадываюсь, почему (проверять не охота, но звучит достоверно): в первую попытку я писал analogRead(A7), а он, видимо, не смог преобразовать это в 21... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться