Увеличение памяти ардуино уно
Согласно документации Arduino, ATmega328 имеет 32 КБ флэш-памяти для загрузчика + загруженный эскиз и только 2 КБ SRAM для данных времени выполнения. ATmega2560 имеет немного больше, общий объем 256 КБ и 8 КБ соответственно.
В любом случае эти ограничения кажутся довольно небольшими, особенно по сравнению с потребительскими устройствами аналогичного размера, такими как смартфоны.
Что вы можете сделать, если у вас кончились? Например, если ваш эскиз слишком большой или вам нужно обрабатывать много данных (например, строк) во время выполнения? Есть ли способ расширить Flash или SRAM?
Попробуйте использовать некоторые переменные оптимизации. Некоторые различные переменные обсуждаются здесь Используйте надлежащие области действия для ваших переменных, если это еще не сделано, компилятор оптимизирует использование оперативной памяти для вас, когда переменные не используются. Вы можете использовать Teensy 3.2 (с программным обеспечением Teensyduino), которое будет сравнимо с Arduino. Учитывая, что у вас заканчивается RAM / PROGMEM, это также означает, что вы, скорее всего, пытаетесь подтолкнуть Arduino. Teensy 3.2 не намного дороже. но имеет: 32-битную, 72 МГц (wtf !?) флэш-память 256 КБ, 64 КБ ОЗУ и 3x UART. Не больше памяти программ, как у ATmega2560, но должно быть достаточно оперативной памяти и увеличения скорости.Оптимизация
Низкоуровневое программирование для встроенных систем весьма отличается от программирования для устройств общего назначения, таких как компьютеры и мобильные телефоны. Эффективность (с точки зрения скорости и пространства) гораздо важнее, потому что ресурсы стоят на первом месте. Это означает, что самое первое, что нужно сделать, если вам не хватает места, это посмотреть, какие части вашего кода вы можете оптимизировать.
С точки зрения сокращения использования программного пространства (Flash) размер кода может быть довольно трудно оптимизировать, если вы неопытны или более привыкли к программированию для настольных компьютеров, которые не склонны к этим навыкам. К сожалению, не существует подхода «волшебной пули», который будет работать во всех ситуациях, хотя он помогает, если вы серьезно задумаетесь над тем, что в действительности должен иметь ваш эскиз . Если функция не нужна, уберите ее.
Иногда также полезно определить, где несколько частей вашего кода одинаковы (или очень похожи). Вы можете сжать их в многократно используемые функции, которые можно вызывать из нескольких мест. Однако имейте в виду, что иногда попытка сделать код слишком многократно используемым на самом деле делает его более многословным. Это сложный баланс для забастовки, который имеет тенденцию приходить с практикой. Можно потратить некоторое время на изучение того, как изменения кода влияют на вывод компилятора.
Оптимизация данных времени выполнения (SRAM) имеет тенденцию быть немного легче, когда вы к этому привыкли. Очень распространенная ловушка для начинающих программистов - использование слишком большого количества глобальных данных. Все, что объявлено в глобальном масштабе, будет существовать в течение всего времени существования эскиза, и это не всегда необходимо. Если переменная используется только внутри одной функции, и она не должна сохраняться между вызовами, то сделайте ее локальной переменной. Если значение должно быть разделено между функциями, подумайте, можете ли вы передать его как параметр вместо того, чтобы делать его глобальным. Таким образом, вы будете использовать SRAM только для тех переменных, которые вам действительно нужны.
Еще одним убийцей использования SRAM является обработка текста (например, использование String класса). Вообще говоря, вам следует избегать выполнения операций со строками, если это возможно. Они огромные боровы памяти. Например, если вы выводите много текста в последовательный порт, используйте несколько вызовов Serial.print() вместо вместо конкатенации строк. Также попытайтесь сократить количество строковых литералов в вашем коде, если это возможно.
Избегайте рекурсии, если это возможно. Каждый раз, когда выполняется рекурсивный вызов, стек поднимается на уровень глубже. Перефразируйте ваши рекурсивные функции, чтобы они стали итеративными.
Использование EEPROM
EEPROM используется для долговременного хранения вещей, которые изменяются только изредка. Если вам нужно использовать большие списки или справочные таблицы с фиксированными данными, подумайте о том, чтобы заранее сохранить их в EEPROM и извлекать только то, что вам нужно, когда это необходимо.
Очевидно, что EEPROM довольно ограничен по размеру и скорости и имеет ограниченное количество циклов записи. Это не лучшее решение для ограничения данных, но этого может быть достаточно, чтобы облегчить нагрузку на Flash или SRAM. Также возможно взаимодействие с аналогичным внешним хранилищем, таким как SD-карта.
Расширение
Если вы исчерпали все остальные варианты, возможно, расширение возможно. К сожалению, расширение флэш-памяти для увеличения места в программе невозможно. Тем не менее, есть возможность расширить SRAM. Это означает, что вы сможете реорганизовать свой эскиз, чтобы уменьшить размер кода за счет увеличения размера данных.
Получить больше SRAM на самом деле довольно просто. Один из вариантов - использовать один или несколько чипов 23K256 . Доступ к ним осуществляется через SPI, и есть библиотека SpiRAM, которая поможет вам их использовать. Просто знайте, что они работают при 3,3 В, а не 5 В!
Если вы используете Mega, вы также можете получить щиты расширения SRAM от Lagrangian Point или Rugged Circuits .
Иногда у меня заканчивается пространство размера программы для встроенной программы на C, которую я хочу наложить на arduino. Итак, какой чип мне нужно расширить память программ для arduino, чтобы я мог использовать более крупные программы на C с моим arduino?
ATmega2560 - это чип, который добавит для большего объема памяти, когда вы закончите память программы на Uno. Ссылаясь на веб-страницу Memory на arduino.cc, вы увидите, что у нее есть Flash = 256 КБ, SRAM = 8 КБ, EEPROM = 4 КБ, где чип ATmega328 на Uno имеет Flash = 32 КБ, SRAM = 2 КБ, EEPROM = 1 КБ.
Когда у вас заканчивается память программы на Uno, у вас есть около трех вариантов, которые являются практичными: сделать программу меньшей; удалить загрузчик (освободить один или два КБ); используйте другую плату с более мощным процессором. Процессор на плате Uno не имеет возможности получить доступ к программным байтам из других мест, кроме встроенной вспышки 32 КБ. Процессор на плате Mega2560 запускается с частотой в 8 раз больше, поэтому он может хранить большие программы, чем Uno.
Другой способ (кроме MCU с большим объемом памяти) не использует структуру Arduino и ее библиотеки. Такой уровень абстракции дорог в обоих направлениях - использование памяти и скорость. Но это намного сложнее. Если вам нужна библиотека, вам необходимо ее портировать (если вы не нашли родной) или хотя бы предоставить требуемые функции от Arduino.
Например, в Arduino IDE 1.6.11 пустой эскиз использует 444 байта вспышки. Используя один pinMode и digitalWrite(13, ! digitalRead(13)); + delay(500); означает 964 байта. Конечно, это, скорее всего, одноразовые. Если вы используете еще шесть контактов и переключаете их, он использует гораздо меньше памяти программ, чем первый (1192B). Но он все еще растет очень быстро.
В чистой C пустой программе примерно 134 байта. Та же функциональность (переключение на один вывод каждые 500 мс) занимает 158 байтов (и это быстрее).
Но для проекта хобби я бы пошел вместо ATMega2560 или ATMega644 /1284.
И вы также можете использовать прямой доступ к оборудованию. Переключить эскиз вывода с использованием регистров напрямую, а _delay_ms из avr-библиотек использует 468 байт вместо 964B. С задержкой с основными библиотеками Arduino это 602B.
Как насчет малины pi zero ? У меня есть пара и часто использую их как ардуины.
Вы также можете посмотреть на другие платформы. Например, Teensy 3.2 совместим с Arduino и имеет 2 Кбайт eeprom и 64 Кбайт оперативной памяти.
Вэнь, задавая широкий вопрос, полезно включить некоторые детали. Как сколько памяти и почему вы думаете, что вам нужно.
Я однажды указал процессор на 2 КБ программного пространства, 64 байта RAM и 1 KIPS. Инженер спросил, MIPS, я объяснял тысячи в секунду. Он ответил, что они не так медленно. В конце проекта был добавлен огромный пользовательский интерфейс, и память сжалась . но по-прежнему без проблем.
Итак, подумайте о том, чтобы быть более продуманным в отношении используемых ресурсов.
Чтобы ответить на вопрос:
1) Arduino не обеспечивает удобную шину для извлечения памяти. Таким образом, нет возможности подключить чип, чтобы получить больше памяти.
2) Arduino имеет SPI, поэтому можно получить доступ к SD-карте. Любой код на SD-карте должен быть загружен в исполняемую память. Это сложно, а не для новичков .
3) Как было предложено, затяните код. Это, вероятно, хороший способ узнать.
3) Переместитесь к более крупному Arduino.
Существуют различные процессоры Arduino. Некоторые в строке ACR, другие - в линии ARM. Переход на другое устройство в одной семье - относительно скромное изменение и может обеспечить до 368 байт прогаммы FLASH. Но это не платы Arduino.
Устройства Arduino ARM очень разные и используют разные библиотеки.
Проверьте Arduino Mega 2560, он очень похож на Uno, имеет аналогичный след, код должен относиться относительно легко и недорого.
В семействе AVR есть более крупные устройства.
Обратите внимание, что AVR32 - это другой процессор, и периферийные устройства достаточно различны, что это другое устройство.
Если вы хотите попасть в очень большие программы (гигабайты памяти), проверьте Beagleboard. Он имеет полный Linux, с виртуальной памятью (см. № 2 выше) и имеет более мощные механизмы ввода-вывода.
Очень простой и эффективный способ хранения данных – это использовать внутреннюю память Ардуино. Но это подходит для хранения небольших объемов данных, поскольку в Ардуино встроенная EEPROM имеет размер всего 512 байт.
При работе с более крупными или более сложными проектами нам может потребоваться сохранять дополнительные данные. В связи с этим возникает необходимость использовать внешнюю память, например 24LC256 .
Микросхема памяти 24LC256 имеет размер 256 Кбит, что на самом деле составляет 32 Кбайт (262 144 бит / 8 бит = 32 768 байт). Это в 62 раза больше встроенной памяти Ардуино!
Настройка оборудования
В этом примере мы будем использовать микросхему 24LC256 от Microchip. Если вы используете другую микросхему, убедитесь, что требования к выводам и питанию соответствующие, чтобы не повредить микросхему.
- вывод питания (8)
- вывод GND (4)
- вывод защиты от записи (7)
- вывод SCL (6)
- вывод SDA (5)
- выводы адреса (1,2,3)
Прежде чем мы перейдем к программной части, давайте подключим микросхему 24LC256 к нашей Ардуино.
Используя вышеприведенный рисунок, давайте приступим к подключению микросхемы. Сначала подключите GND и VCC к контактам 4 и 8 соответственно. Далее подключите выводы данных к плате Ардуино. Поскольку мы используем I2C шину, мы будем использовать контакты A4 и A5. Подключите контакт SDA 24LC256 (контакт 5) к контакту A4 Ардуино. Затем подключите SCL (контакт 6) к контакту A5 на Ардуино. Дважды убедитесь, что все подключено правильно, иначе могут произойти странные вещи, если вы их перепутаете.
После того, как выводы данных и питание подключены, у 24LC256 осталось еще четыре неподключенных вывода: вывод WP и три вывода адреса.
Контакт WP обеспечивает защиту от записи. Это позволяет вам контролировать процесс записи данных в EEPROM. Если на этом выводе низкий логический уровень (0), то запись разрешена. Если же на выводе WP будет высокий логический уровень (1), то запись запрещена, но при этом чтение всегда доступно. В рамках этого руководства мы будем записывать в EEPROM, так что вывод WP подключим к GND.
Последние три контакта устанавливают адрес 24LC256, который позволяет идентифицировать конкретную микросхему на I2C шине. Микросхема 24LC256 поставляется с уже установленными четырьмя битами адреса (1010), которые нельзя изменить. Однако последние три бита адреса можно изменять, и это позволяет нам подключать до восьми микросхем 24LC256 на одну шину I2C. Давайте посмотрим на рисунок ниже, чтобы поподробнее разобраться, как формируется этот адрес:
Для простоты понимания как работает I2C адрес, мы можем игнорировать биты начала (Start) и подтверждения (Acknowledge). Шина I2C работает так: 7-битный адрес передается вместе с битом чтения / записи, который сообщает микросхеме, должна ли она записывать входящие данные или читать.
Ардуино позаботится о последнем бите чтения / записи в зависимости от того, какую функцию мы используем. Так как мы используем стандартную библиотеку Ардуино Wire, нам не нужно беспокоиться об этом бите.
И теперь у нас остались семь средних битов, и, как упоминалось выше, первые четыре бита жестко запрограммированы, и мы не можем их изменить. Следующие три бита (A2, A1, A0) являются важными битами, которые мы можем изменять.
Итак, если мы соединим контакты 1, 2 и 3 микросхемы 24LC256 с GND, то микросхема будет иметь I2C адрес 0x50, а если все эти выводы подключить к Vcc, тогда микросхема будет иметь I2C адрес 0x57. Перебирая все комбинации, мы можем получить 8 адресов от 0x50 до 0x57.
Для упрощения просто соединим все контакты с GND, чтобы получился адрес 0x50.
После подключения адресных выводов аппаратная часть этого руководства завершена. Пора переходить к программному обеспечению!
Некоторые используют подтягивающие резисторы на выводах SCL и SDA Ардуино. Хотя это не повредит схеме, но в них нет необходимости, потому что при инициализации библиотеки Wire.h Ардуино знает, что выводы 4 и 5 будут использоваться для I2C, и в связи с этим активируются встроенные подтягивающие резисторы.
Скетч
Ниже приведен полный код работы с внешней памятью 24LC256. Далее рассмотрим его работу подробнее.
Чтобы использовать интерфейс I2C, нам необходимо подключить стандартную библиотеку Wire Ардуино, поэтому первым делом подключите Wire.h в верхней части скетча. Вы заметите, что сразу после подключения мы определяем переменную с именем disk1 и присваиваем ей шестнадцатеричное значение 0x50 – это адрес нашей памяти. Эта переменная не является обязательной, но она позволяет нам легко изменить адрес, к которому мы хотим получить доступ, не просматривая весь код и не заменяя значение.
Кроме того, если вы планируете добавить более одной микросхемы памяти, проще называть их disk1, disk2 и т. д., а не 0x50, 0x51… что может запутать.
Далее у нас есть функция setup() и функции loop(). В этом руководстве функция loop() оставлена пустой, поэтому мы просто сосредоточимся на функции setup().
Сначала мы инициализируем последовательное соединение, а затем инициируем соединение I2C, вызывая Wire.begin(). Это активирует контакты 4 и 5 для I2C, а также подключает внутренние подтягивающие резисторы.
Затем мы создаем новую переменную для хранения адреса ячейки EEPROM, в которую мы хотим записать (не адрес самой микросхемы EEPROM, а адрес байта, который мы хотим считать / записать). Поскольку эта EEPROM имеет 32 Кбайт памяти, этот адрес может быть любым числом от 0 до 32 767. Мы начнем с адреса 0.
После того, как мы все инициализировали, мы переходим к нашим двум основными функциям: writeEEPROM и readEEPROM, которые фактически выполняют всю основную работу записи / чтения байтов данных.
Давайте сначала начнем с функции writeEEPROM. Эта функция принимает три аргумента: адрес устройства (переменная disk1), адрес памяти EEPROM и байт данных, которые мы хотим записать.
Следующим аргументом является адрес ячейки EEPROM, в которую мы будем сохранять данные, и, как было сказано выше, этот адрес может находиться в диапазоне от 0 до 32 767. Наконец, нам нужно передать байт данных, который мы хотим сохранить.
Пример: writeEEPROM (disk1, address, 123) будет записывать десятичное число 123 в «address» (который равен 0) на устройство disk1 (0x50).
Давайте перейдем к самой функции writeEEPROM, чтобы узнать, что она делает.
Поскольку наша микросхема EEPROM имеет 32 000 ячеек, мы используем два байта (16 бит) для хранения адреса. Но поскольку мы можем отправлять только один байт за раз, то мы должны разделить его.
Первая функция отправки принимает eeaddress и сдвигает биты вправо на восемь. Затем мы выполняем побитовое И, чтобы получить только последние восемь бит. Чтобы понять это рассмотрим это на примере:
0100 1110 0010 0000 (eeaddress)
После сдвига на 8 бит вправо получаем
0100 1110
Теперь у нас есть первая половина адреса, пора получить вторую половину:
0100 1110 0010 0000 (eeaddress)
После побитового И 0xFF с eeaddress мы получаем
0010 0000
В результате этих операций микросхема 24LC256 получает адрес 1001 1100, а затем 0010 0000, который сообщает ей, что она должна сохранить следующий байт в ячейке с адресом 20000. Теперь, когда мы отправили адрес, мы отправляем данные, а затем завершаем процесс, вызывая функцию endTransmission.
Микросхема 24LC256 получит данные и запишет их по этому адресу. Чтобы завершить эту функцию, вы наверное заметили, что добавлена задержка в 5 миллисекунд. Это дает микросхеме время для завершения операции записи. Без этого, если вы попытаетесь выполнить последовательную запись, могут произойти странные вещи.
После того как мы научились сохранять наши данные в EEPROM, пора перейти к функции readEEPROM, которая позволяет считывать данные из микросхемы памяти.
Сначала мы объявляем переменную для хранения байта, который мы собираемся получить. Затем мы начинаем так же, как мы делали с функцией записи, запуская процесс beginTransmission, а затем отправляем адрес устройства, к которому мы хотим получить доступ (это работает точно так же, как функция записи).
Далее мы завершаем передачу. В итоге мы установили связь с 24LC256 с адресом, который нас интересует, так что теперь нам просто нужно запросить и прочитать данные.
Следующая функция requestFrom() отправляет этой микросхеме команду начать отправку данных по указанному выше адресу. Второй аргумент – сколько считать байтов (начиная с этого адреса). Мы запрашиваем только один. Наконец, мы проверяем, есть ли данные на шине I2C, и, если они есть, считываем их в переменную rdata. Возвращаем байт данных и все готово!
Это все, что вам действительно нужно знать, чтобы успешно использовать внешнюю память с Ардуино.
Очень часто при разработке приложений и систем на базе Arduino встает проблема нехватки памяти, присущая среде программирования микроконтроллеров. С помощью серия Arduino Mega возможно решение данной проблемы, т.к.имеются варианты исполнения данной платформы с Flash-памятью программ 128 КБайт или 256 КБайт. А как насчет встроенной памяти SRAM? Максимальный объем в нашем случае (Arduino Mega) не превышает 8 КБайт.
К счастью, серия Mega позволяет работать с внешней SRAM, и что самое главное, программа может получать беспрепятственно доступ к ней, как если бы это была внутренняя SRAM микроконтроллера. В статье мы рассмотрим аппаратное и программное решение задачи расширения памяти на базе микроконтроллера Atmel AVR ATmega1280.
XMEM
В техническом описании на микроконтроллер ATmega1280 содержится вся информация для инженеров, необходимая для расширения памяти. В документе это 9 глава «External Memory Interface» (XMEM). Блок схема ниже поясняет, как микроконтроллер организует связь с внешней памятью.
Мультиплексирование и защелка
Рис. 2. | Временные диаграммы работы интерфейса внешней памяти. |
Стоит заметить, что регистр-защелка должен успевать работать на тактовой частоте 16 МГц, поэтому серия 74HC не подойдет для нашей цели. В техническом описании на микроконтроллер рекомендуется серия 74AHC.
Адресация при объеме SRAM более 64 КБайт
Теперь, основная задача – это работа с внешней памятью объемом 512 КБайт с адресным пространством всего на 64 КБайт. Решение – разделить 512 КБайт на 8 банков памяти по 64 КБайт и сделать управление «видимостью» одного банка в каждый момент времени с использованием 3 выводов микроконтроллера.
Память объемом 512 КБайт при обращении к ней требует 19-битного адреса (адресные линии A0 – A18). Линии A0 – A15 мы подключим по интерфейсу xmem (интерфейс внешней памяти микроконтроллера) и управление 3 оставшимися (A16 – A18) осуществим с помощью цифровых линий ввода/вывода микроконтроллера.
Рис. 3. | Разделение внешней SRAM на 8 банков памяти объемом 64 КБайт. |
Следующее ограничение, которое накладывает карта памяти микроконтроллеров ATmega – нижние 8 КБайт SRAM (адресное пространство для такого объема) всегда занимаются внутренней памятью микроконтроллера. Это означает, что адресация внешней памяти осуществляется в диапазоне 0х2200 – 0хFFFF, т.е. мы теряем 69632 байта из общего объема 524288 Байт внешней SRAM. В спецификации на микроконтроллер объясняется метод адресации этих потерянных 8 КБайт, однако автор посчитал, что в данной задаче это не актуально.
Во второй части статьи представлены принципиальная схема подключения внешней ОЗУ к микроконтроллеру, список компонентов и рисунки печатной платы.
Читайте также: