Attiny85 + SSD1306 = некорректная работа команд установки регистров (ADMUX |= (1<<REFS0))

Здравствуйте. Столкнулся с непонятной работой комманд установки значеной в регистрах. Attiny85 + SSD1306(i2C) + USBAsp

Проверял в 2х IDE - ArduinoIDE и Visual Studio Code+PlatformIO

  1. ADMUX |= (1<<REFS0);

    После попытке установки бита экран SSD1306(i2C) гаснет. Осцилографом вижу импульсы на SCK но на SDA 0 вольт. Проблема только с этим битом, причём команды

 ADMUX &= 0b00101111;                // Очиcтить REFS0(6) REFS1(7) REFS2(4)
  ADMUX |= 0b10010000;                // Установить REFS0(6)=0 REFS1(7)=1 REFS2(4)=1 что означает Internal 2.56V Voltage Reference without external bypass capacitor

или
ADMUX = 0b01000000;
работают нормально.

  1. ADCSRA |= (1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);
    Команда вообще ничего не делает, в регистре все 0. Причем
ADCSRA &= 0b11111000;               // Очичтить ADPS2(2) ADPS1(1) ADPS0(0)
  ADCSRA |= 0b00000101;               // Установить ADPS2(2)=1 ADPS1(1)=1 ADPS0(0)=0 что означает division factor between the system clock frequency and the input clock to the ADC = 64

работает нормально. Сстояния регистров вывожу на экран. Весь код тут

#include <Tiny4kOLED.h>

void setup() {
  // Setup OLED //
  oled.begin();              // Send the initialization sequence to the oled. This leaves the display turned off
  oled.enableChargePump();   // The default is off, but most boards need this.
  oled.setRotation(1);       // The default orientation is not the most commonly used.
  oled.clear();              // 
  oled.switchFrame();        // Clear ALL the memory before turning on the display
  oled.clear();              //
  oled.on();                 // Turn on the display
  oled.setFont(FONT8X16);
  oled.switchRenderFrame();  // Switch the half of RAM that we are writing to, to be the half that is non currently displayed

  // Настройка ADC
  ADCSRA |= (1<<ADEN); // Включить ADC
  //!!!Команда ADMUX |= (1<<REFS2)|(1<<REFS1)|(1<<REFS0); Вызывает ошибку экрана поэтому устанавливаем биты так!!!
  ADMUX &= 0b00101111;                // Очичтить REFS0(6) REFS1(7) REFS2(4)
  ADMUX |= 0b10010000;                // Установить REFS0(6)=0 REFS1(7)=1 REFS2(4)=1 что означает Internal 2.56V Voltage Reference without external bypass capacitor
  ADMUX |= (0<<MUX3)|(0<<MUX2)|(1<<MUX1)|(0<<MUX0); // Подключаем пин ADC2(PB4) к ADC
  //!!!Команда ADCSRA |= (1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0); ничего не делает поэтому устанавливаем биты так!!!
  ADCSRA &= 0b11111000;               // Очичтить ADPS2(2) ADPS1(1) ADPS0(0)
  ADCSRA |= 0b00000101;               // Установить ADPS2(2)=1 ADPS1(1)=1 ADPS0(0)=0 что означает division factor between the system clock frequency and the input clock to the ADC = 64

  ADMUX |= (1<<REFS0);

}

void read_reg(char port) {
  oled.setCursor(0, 0);
  for (int8_t i=7; i>=0; i--) {
    oled.print(bitRead(port, i));
  }
  oled.setCursor(0, 2);
  oled.print(F("0h"));
  oled.print(port, HEX);
}

void updateDisplay() {
  oled.setCursor(64+24, 0);
  oled.fillToEOL(0x00);
  oled.setCursor(64+24, 2);
  oled.fillToEOL(0x00);
  read_reg(ADCSRA);
  oled.switchFrame();
}

void loop() {  
updateDisplay();
}





а вас не смущает, что на тини AREF и SDA шины I2c - это один и тот же пин?

Неправильно очищаете. Записывайте полностью.
ADMUX = 0b10010000;
REFS0 должен быть в 0, MMM верно заметил.

Смущает, но я же устанавливаю значения которые подключают внутренний ИОН к компаратору, как я понял этот пин остается свободным. И почему тогда команда
ADMUX = 0b10010000; работает нормально, и экран отраматывает и замеры напряжения?

потому что она не ставит REFS0 в единицу

В режимах, где установлен бит REFS0, использовать пин AREF для чего-либо, кроме АДС - нельзя.
Вы номер бита не спутали, случаем?

Спасибо я баран получается там просто ошибка… ADMUX |= (1<<REFS2)|(1<<REFS1)|(0<<REFS0); должно быть… там эти биты не по порядку идут, поэтому запутался

А как понять почему ADCSRA |= (1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0); не работает? а

ADCSRA &= 0b11111000; ADCSRA |= 0b00000101; работает?

Может быть ADCSRA перед командой надо очистить?

1 лайк

Записи

и

абсолютно эквивалентны.

Попробуйте перед
ADCSRA |= (1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);
тоже давать

должно сработать

Спасибо работает. А не работало оно т.к. по уполчанию (ну или так в ядре прописано) после включения платы в ADCSRA 0x10000110, в эта штука ADCSRA |= (1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0); не может делать из единиц нули.

А подскажите я правильно понимаю что чем больше предделитель частоты ADC в REFS2 REFS1 REFS0, тем медленнее будет выполняться цикл преобразования, просто на практике он одинаковый что с делителем 2 что со 128 занимает 4us

  do { ADCSRA |= (1<<ADSC); }
  while ((ADCSRA & (1 << ADIF)) == 0);
  V_capture = (ADCL|ADCH << 8);

Что-то не то меряете, даже при прескалере 2 первое измерение должно занимать 6 us

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

Честно говоря не считал. В даташите написано для измерения с точностью 10бит, частота на которой работает АЦП должна быть 50-200 кГц. Я оставил 64 как по умолчанию, это получается 125кгц, с вольтметром замеры совпадают.

Частота считается как t2-t1, только вот она не меняется с изменением делителя, пока не пойму почему.

  t1=micros();
  do{ ADCSRA |= (1 << ADSC); }
  while ((ADCSRA & (1 << ADIF)) == 0);
  V_capture = (ADCL|ADCH << 8);
  t2=micros();
  updateDisplay();

А какая у Вас частота F_CPU?

В ардуино IDE стоит 16, в PlatformIO поставил тоже 16, но все замеры после компиляции и загрузки из него стали в 2 раза дольше, так что думаю что 8. Настройки есть на скриншотах. Как узнать реальную частоты работы сейчас погуглю.

Вы считаете, что здесь написан цикл ожидания пока измерит? Боюсь Вас огорчить, но это не так. Если Вы хотите использовать именно ADIF, то там нужна не &, а ^ Вообще, это вот так делается:

//
//	Запуск измерения
//	Проверка, что измерение ещё не закончилось
//	Проверка, что измерение уже закончилось
//
static inline void adcStartConversion(void) { ADCSRA |= bitMask(ADSC); }
static inline bool adcConversionInProgress(void) { return ADCSRA & bitMask(ADSC); }
//	так тоже можно static inline bool adcConversionInProgress(void) { return ADCSRA ^ bitMask(ADIF); }
static inline bool adcReady(void) { return ! adcConversionInProgress(); }

И вообще, если Вы намеряли 4uS – ищите ошибки ибо это бредовый бред. При частоте 125 кГц, время второго и последующих измерений - 104uS. Это элементарно посчитать: 13 тактов умножаем на время одного такта: 13 * 1/125000 = 0,000104

Чего там гуглить - фьюзы надо смотреть.

Если 8МГц, то делитель 64 - правильный, если 16 - то нужен 128

Вообще, у меня делитесь всегда сам считается по F_CPU (просто ищется минимальный, для которого частота АЦП будет в пределах 50-200 кГц Могу показать как.

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

Замерял так

  unsigned long ustime1, ustime2;
  int i, mhzFreq, iMax = 10000;
  ustime1 = micros();
  for(i=iMax; i>0; i--) __asm__("nop\n\t");
  ustime2 = micros();
  mhzFreq = 5 * (long)iMax / (ustime2 - ustime1) + 1;

Действительно Arduino IDE 16 а если прощивать из PlatformIO то 8

Я незнаю как определить частоту по фьюзам, там вообще пищут что максимальная 8, хотя у меня при прошивке из ArduinoISP и установкой


работает на 16.

AVRdude показывает одинаковые фьюзы хотя чипы точно работают на разной частоте

А чего там непонятного. Функция bitMask? Так возьмите файлик (в библиотеки добавьте) и забудьте про эти уродливые много этажные (1 << HRENZNET)