Невозможно установить низкий уровень на выводе MOSI даже после завершения SPI

interrupt spi pins atmega

Я собираю устройство с батарейным питанием и должен отключать вывод MOSI во время сна, потому что в противном случае он пропускает ток через SD-карту (около 400 мкА).

Проблема в том, что он не отключается. Я просмотрел документацию по SPI и, насколько я знаю, мне просто нужно вызвать spi.end(), затем установить вывод pinmode и, наконец, digitalWrite LOW. Я наблюдаю с помощью осциллографа, что он действительно становится низким, когда я его запрашиваю, но только примерно на 1 мкс (на самом деле он становится низким дважды в течение периода времени 5 мкс).

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

spi.end();  

//pinMode(pin_MOSI, OUTPUT);
// цифровая запись (pin_MOSI, LOW);

// Как выше (закомментировано), так и ниже приводит к одному и тому же поведению - вывод не остается НИЗКИМ

DDRB |= B00100000;            // устанавливаем вывод 5 в качестве выхода
PORTB &= B11011111;           // устанавливаем низкий уровень выхода для контакта 5

delay(5000);

...Как вы можете видеть, есть задержка, чтобы убедиться, что она не установлена высокой каким-то другим кодом.

Есть ли какое-то прерывание SPI, которое автоматически снова устанавливает на выводе высокий уровень?

Сведения об оборудовании:

MCU: ATMEGA1284P; Программист: Atmel-ICE


ОБНОВЛЕНИЕ (13 марта 2019 г.):

Я не знал, что SPI реализуется Atmel напрямую в аппаратном обеспечении (см. техническое описание на стр. 105):

MOSI/PCINT13 — порт B, бит 5 – MOSI: вывод основных данных SPI, ввод подчиненных данных для канала SPI. Когда SPI0 включен в качестве ведомого этот вывод настраивается как вход независимо от настройки DDB5. Когда СПИ включен в качестве ведущего, направление данных этого вывода контролируется DDB5. Когда вывод вынужден быть входом, подтяжкой по-прежнему можно управлять с помощью бита PORTB5.

В настоящее время я изучаю последнюю информацию и, надеюсь, скоро приду к решению. Я буду публиковать любой прогресс.

, 👍3

Обсуждение

это модуль SD с преобразованием логического уровня?, @Juraj

@Juraj: Без переключения уровня, прямо 3,3 вольта на SD-карту., @DatuPuti

Я думал, вы используете SPI.begin() и SPI.end() для использования встроенного оборудования из Arduino IDE. Вместо этого вы немного стучите?, @CrossRoads

@CrossRoads: я использую SPIClass из <spi.h>. Я предполагаю, что это часть ядра Arduino, но я не уверен. Я успешно заставил его работать, очистив бит включения SPI (SPE) из регистра SPCR, поэтому я знаю, что библиотека делала это не для меня., @DatuPuti

@Juraj: я использую ATmega1284P, который представляет собой микроконтроллер на 3,3 вольта, а не 5 вольт. Спасибо!, @DatuPuti

мой работает на 5V. по умолчанию здесь 5V., @Juraj

@Juraj: Хороший вопрос! Я ошибочно предположил, что ATmega1284P рассчитан на 3,3 вольта, но он может достигать 5,5 вольт — мой работает на 3,3 вольта, так как это мой расчетный уровень VCC., @DatuPuti

Вы же не работаете на частоте 16 МГц? Это может быть нестабильно. Классы скорости: 0–4 МГц при 1,8–5,5 В, 0–10 МГц при 2,7–5,5 В, 0–20 МГц при 4,5–5,5 В согласно техническому описанию., @CrossRoads

@CrossRoads: я думал, что работаю на частоте 8 МГц, но при попытке проверить обнаружил, что действительно работаю на частоте 16 МГц при напряжении 3,3 вольта! Он работал нормально, но нет гарантии, что не будет проблем! Используя внешний генератор на частоте 16 МГц — я понял, как добавить предварительный делитель для замедления микроконтроллера до 8 МГц, а затем изменил поле F_CPU в файле boards.txt, чтобы синхронизация не была перепутана в коде Arduino. Я думаю, что все, наконец, работает. Большое спасибо за комментарий - это еще не было проблемой, но я уверен, что это могло бы стать серьезной проблемой при определенных условиях (например, более теплые температуры)!, @DatuPuti


1 ответ


Лучший ответ:

2

Действительно, SPI реализован аппаратно. Мне удалось заставить вывод MOSI работать должным образом, очистив бит включения SPI в регистре SPCR следующим образом:

SPCR &= 0B10111111;  //отключаем SPI

Чтобы снова включить после сна, я предполагаю, что просто сделаю SPCR |= 0B01000000; ...почти уверен, что это сработает, но я обновлю здесь, если мне нужно изменить мое решение.

.

Возможно, также необходимая информация:

Я использовал объект SPIClass в SPI.h из основных библиотек Arduino — по какой-то причине он не отключал SPI аппаратно при вызове метод spi.end(), возможно, потому, что был какой-то другой объект SPI, инициализированный какой-то другой библиотекой. Вот исходный код из SPI.cpp для метода end:

void SPIClass::end() {
  uint8_t sreg = SREG;
  noInterrupts(); // Защитить от планировщика и предотвратить начало транзакции
  // Уменьшаем счетчик ссылок
  if (initialized)
    initialized--;
  // Если ссылок больше нет, отключаем SPI
  if (!initialized) {
    SPCR &= ~_BV(SPE);
    interruptMode = 0;
    #ifdef SPI_TRANSACTION_MISMATCH_LED
    inTransactionFlag = 0;
    #endif
  }
  SREG = sreg;
}

Строка SPCR &= ~_BV(SPE); должна была выполнить эту работу за меня, но я предполагаю, что она так и не попала туда, потому что initialized, вероятно, все еще ненулевой.

,

Если у вас не было #include <SPI.h> вверху скетча, как вы могли использовать SPI.begin(), SPI.transfer(); и т. д?, @CrossRoads

@CrossRoads: у меня был включен SPI.h - извините, если это было неясно., @DatuPuti

Я думал, что читал что-то о неиспользовании библиотеки, но сейчас я этого не вижу. Возможно, я перепутал две темы. Рад, что ты разобрался., @CrossRoads

Возможно, библиотека SPI была улучшена за годы, прошедшие с момента написания этого ответа. spi.end() теперь имеет желаемый эффект. Однако установка/очистка бита в SPCR выполняется быстрее., @Dmitry Grigoryev

@DmitryGrigoryev: Взгляните на метод end(), который я скопировал из основных исходных файлов (выше в конце моего ответа). Если переменная-член 'initialized' (которая определена как uint8_t) не равна нулю, то очистка бита включения SPI в SPCR никогда не происходит. Я думаю, проблема в том, что одна из библиотек, которые я использовал, называлась SPI.begin(), но никогда не вызывала SPI.end(), что означает, что SPI никогда не могла деактивироваться. В итоге я переписал код без других библиотек, и проблема была решена., @DatuPuti