Платы пришли ещё 13 апреля, но только сегодня сумел забрать на почте:
Качество очень даже впечатлило. На коробке сверху плата под ATmega128a, снизу в том же форм-факторе под ATmega2560 и справа - под SRAM в 512килобайт. Для платы памяти есть все на 5шт, для остальных нет камней. В подарок в коробке оказалась картонная ручка "for PCB" уж не знаю зачем.
Осталось заказать камни. Продаваны по ссылкам выше - ни один не ответил ни на один заданный вопрос. Только автоответчик "не беспокойтесь, нам для ответа требуется время" .. напрягает. Вроде бы есть желающий продаван поставить по 10шт с оптовой алибабы.. видимо буду заказывать у него.
Ну наконец-то. Вчера забрал с почты 10шт ATmega2560-16au. По распаковке слегка прифигел: судя по маркировке, камни произведены на 13-й неделе (с26 марта по 1 апреля) 2018 года. О как! Запаял основную часть платы, проверил БП: на выходе 4.95в без нагрузки и 4.93в с токоотдачей 5А .. очень нефиговые эти RT8289 оказались. (картинко - кликабельно)
Светодиоды формата SMD так и не закупил, запаял мелкие круглые по 3мм. Конденсаторов по 22пф тоже пока не нашлось в загашниках. Так обойдется.. Уже завтра запаяю SPI м попытаюсь прошить загрузчик. БП выглядит конечно брутальненько так. Диоды шоттки на 3А. Тем не менее под нагрузкой 5А вроде даже и не греются. :)
P.S. Пока паял, пришла идея положить периферийные разьемы "на бок", кроме разъема с памятью .. блин, где эта мысля была раньше? :)
Ура, ну не могу удержаться.. Вчерась (пока паял плату) дитенка самостоятельно (ну почти) впервые скомпилял себе на ноут под Убунтой драйвер Wi-Fi, который у него не встал при установке системы .. скоро, скоро будем принимать в свои ряды ещё одного "погромиста - сиониста". :)
4. 0xC000 .. 0xFFFF -- 15 сегментов SRAM по 16кб, переключаемые второй половиной (старшая тетрада) того же внешнего регистра.
Запись во внешний регистр делается "ногодрыгом" по пинам WR & RD, с выставлением в порт А данных при отключенном интерфейсе xmem Меги.
п.1,п.2 - тривиальны, ибо обращение к этим участкам легко контролируется самим камнем автоматически и ничего специального разъяснять компилятору не требуется, просто надо указать "что теперь памяти больше", только и всего:
Вариант 1 (минималистический) описания платы: указываем в описании, что у этой платы оперативной памяти 64 килобайта, то есть разрешаем распределение всего адресного пространства и далее "ручками" контролируем, где что лежит .. но это не комильфо.
Вариант2 (это вот хочется запилить):
Было бы полезно объяснить компилятору и линковщику, что теперь у него есть доп. сегменты ОЗУ в виде .lpage0 .. .lpage14 и .upage0 .. .upage14, переменные которых надо размещать с соответствующих адресов п.3, 4. И далее в коде использовать атрибуты явного назначения сегмента ОЗУ переменным, размещаемым в заданном сегменте. Переключение сегментов в рантайм, скорее всего также придется контроллировать ручками .. но может есть какие-то идеи как это (процедуру смены контекста) всучить компилятору для автоконтроля?
В общем, вопросы на дкоторыми сейчас мучаюсь:
1. Можно ли завести свои сегменты памяти, аналогично .data, .bss и КАК это сделать?
2. Можно ли им назначить конкретные начальные адреса ОЗУ, в т.ч. и одинаковые? Например: .lpage0 = .lpage1. = .. = .lpage14 = 0x8000
3. Можно ли указать каким-то квалификатором что эту переменную (или блок таковых) надо размещать строго в этом сегменте ОЗУ?
4. Можно ли всучить компилятору процедуру переключения сегментов для автоконтроля при обращении к переменной?
Например что-то типа такого:
// компилятору всучена такая функция:
void xmem_lowPage(uint8_t lowNum);
// в коде имеем типа такой массив,
// размещаемый первым во втором сегменте расширенного ОЗУ:
uint8_t attribute(__segment__(.lpage2)) buffer[4096];
// ...
// если в коде присутствует что-то типа такого:
for(int i=0; i<4096; i++){
buffer[i] = 0;
}
// , то компилятор мог оценить какой сегмент сейчас активен и
// дополнить его так самостоятельно:
xmem_lowPage(2);
for(int i=0; i<4096; i++){
buffer[i] = 0;
}
// правильно назначив начало массива скажем на начало этого сегмента (0x8000)
Э-э-э, а почему конец памяти на 0x90FF? У вас разве внутренняя SRAM не экранирует начало внешней?
Эта часть - понятна, делал аналогично, просто расширяя доступность адресов до 0xFFFF. Но вот теперь хочется допилить до нормальной работы со всеми 512к через компилятор.
В общем, нашел что можно накатать свой сценарий линковщику что и как расмещать в памяти и какой .. осталось найти доку на линковщик. :)
P.S. В общем, похоже можно тупо воспользоваться наличием в типовом сценарии линкера описаний типа *(.data*) и *(.bss*) то есть вставить в секции .data и .bss после данных ещё и всякие разные секции .data* - то есть похоже достаточно описать так:
Использовать подсекции в .bss И указать опцией линковщика что секция .bss_lpage2 начинается с 0x8000 .. в общем что-то стало понятней, надо пгобовать.. :)
В общем все не так просто, ибо символ . может только возрастать в пределах секции. К счастью есть команда OVERLAY совмещающая несколько секций с одного адреса .. только вот есть ли она у линковщика avr-gcc?
Кто-то может подсказать какой из скриптов линкера надо брать за основу для правки? Для avr6 (mega2560) в Ардуино ИДЕ имеется 6 разных вариантов, отличающихся расширением .. ни один из них не подключается в явном виде при сборке ..
Добавил согласно прописанному вывод avr-objdump в компиляцю, дополнительно прописал отдельную плату в board.txt типа так:
##############################################################
sram.name=Extended SRAM 512kbytes
sram.upload.tool=avrdude
sram.bootloader.tool=avrdude
sram.bootloader.low_fuses=0xFF
sram.bootloader.unlock_bits=0x3F
sram.bootloader.lock_bits=0x0F
sram.build.f_cpu=16000000L
sram.build.core=arduino
sram.menu.cpu.atmega2560.build.variant=sram
## ESRAM512k for ATmega2560
## -------------------------
sram.menu.cpu.atmega2560=ATmega2560
sram.menu.cpu.atmega2560.upload.protocol=wiring
sram.menu.cpu.atmega2560.upload.maximum_size=253952
sram.menu.cpu.atmega2560.upload.maximum_data_size=32256
sram.menu.cpu.atmega2560.upload.speed=115200
sram.menu.cpu.atmega2560.bootloader.high_fuses=0xD8
sram.menu.cpu.atmega2560.bootloader.extended_fuses=0xFD
sram.menu.cpu.atmega2560.bootloader.file=stk500v2/stk500boot_v2_mega2560.hex
sram.menu.cpu.atmega2560.build.mcu=atmega2560
sram.menu.cpu.atmega2560.build.board=AVR_MEGA2560
## Special linker script for paging memory and move heap up the stack:
sram.menu.cpu.atmega2560.compiler.c.elf.extra_flags=-T{build.variant.path}/avr6.x -Wl,--defsym=__heap_start=0x802200,--defsym=__heap_end=0x807fff
#sram.menu.cpu.atmega2560.compiler.c.extra_flags=
заодно разобрался со структурой этого меню, так что можно позже дополнить настройкой для других камней, частот и плат расширения..
Дополнительно создан каталог с "платой" в variants/sram, что и указано в boards.txt и в него положен свой файл скрипта линкера avr6.x Пока только для линковки по умолчанию (остальные вроде для ИДЕ не нужны):
собственно и усё .. теперь можно обьявлять данные в нужных секциях и листать их вручную.
Да, ещё в boards.txt указано что "куча" malloc() тут начинается с точки расширения SRAM, "за" стеком, который как обычно устанавливается на конец встроенной SRAM через скрипт старта ctors и константу RAMEND, определенную в недрах io.h и файлах камней.. размер памяти под переменные ограничен 32кб по размеру секции .data. ну .. чтобы "жизнь медом не казалась" :)
Макросы для упрощения назначения страниц объектам данных:
#define PAGE_LOW(pageNum) __attribute__((section(".lpage" #pageNum)))
#define PAGE_HIGH(pageNum) __attribute__((section(".hpage" #pageNum)))
// и тогда в коде можно так:
int buffer[8000] PAGE_LOW(2);
Запаял плату памяти. При смывании канифоли ацетоно-спиртовым раствором с микросхемы памяти слезла черная краска, и теперь на ней двойная маркировка. Вторая часть частично читается как JAPAN HMM6... -7, потребление платы 9мА .. вроде бы в норме. Покупал как JAPAN HMM625128ALF-5 (HMM625128BLFP-7) так прямо и было как то или иное (как понял).
В принципе, если это таже памать но на 70нсек, то оно "сойдет" и даже без циклов задержки..
Плата МУРК-2560 - вроде бы полностью рабочая. Выкладываю SVG для самостоятельного изготовления ЛУТ-ом:
Слой F_CU (зеркально), Слой B_CU (обычно) и расположение деталек и надписей:
Можно копировать прямо отсюда и печатать. По запросу могу предостатвить gerber-файлы, все какие надо. Писать не сюда, а на мыло. Размер платы 56х72мм. Индуктивность L1 изготавливать потоньше (плоскую) и размещать поверх диода D1
По номиналам преобразователя на базе RT8289: входную и выходную емкость крайне желательно собирать из нескольких конденсаторов, можно обыкновенную керамику в т.ч. из разных номиналов, поскольку ESR зависит от частоты.
Конденсаторы обвязки микросхемы ATmega2560 тут тоже СБОРНЫЕ из 2-х: 100нф + 470нф (хорошо показали себя и 680нф). Стабильность питания много выше чем при одном конденсаторе в каждом узле обвязки. Рекомендую.
И ещё: Боковые питающие разъемы подключаются через "перемычки" к этому стабилизатору. Это на случай, если надо силовые элементы питать отдельно от интерфейсного разьема (верхнего), микроконтроллера и платы памяти.. (а вдруг).
В общем, удалось ужаться в размер 56х88мм (7х11 лего дырок), на что сын сказал "здорово".
Вариант новой компоновки платы:
Похоже это окончательный вариант. Кликабельно. Все выводы сдвоены, внутренние для установки разъема "мама" вверх для подключения доп. плат (драйвера моторов, гироскопы, плата управления с дисплеем), а внешние - для установки строенных планок (подключение по типу серводвигателя) - "вбок", кроме правого (по рисунку) разъема под внешнюю память. Если "сервы вбок" не требуется, то тоже можно не устанавливать, но в таком разе будет маловато выводов по земле и питанию.
Оставшееся место заткнул регистром расширения адреса (по даташиту), можно не устанавливать или с ним, тупо втыкать микросхему памяти до 64кбайт, прямо в разьем расширения (все сигналы есть)
Предварительно развел вот такой вариант платы 56х88мм (кликабельно):
Ярко выделена земля, которой уделил особое внимание: "чистая земля АЦП" разведена отдельно и подключается к общей земле в одном месте. Все проводки АЦП входов перемежаются этой чистой землей для снижения перекрестных помех. Также отдельно разведена чистая земля для кварца. Выводы резонатора по возможности сделаны одинаковой длины.
Разводка ещё без площадной заливки землей и питанием, без выдержки толщины силовых проводов. Это - "потом", после замечаний..
На пустое место платы воткнул расширительный регистр адреса, соответственно мелкие микросхемы памяти до 64 килобайт можно подключать "напрямую" к контактам расширительного разьема (правый).
Настоятельно прошу критики, ибо не спец. по разводке.
В общем, сделал так (кликабельно). Плата вместе с боковыми плашками расширительного шилда:
Вечером в пятницу отправил на jlpcb.com, уже в субботу получил положительный отклик "можем такое сделать (10шт за 2 бакса)", оплатил во второй половине дня субботы и уже сегодня получил трек доставки .. слов нет, какое "гадство". :)
P.S. Платы приехали аж 24.08. Почта играла в партизан, пока не пришло уведомление от JLCPCB.com на почту.. визуальный осмотр "5 баллов" (ничего крамольного не нашел). Платы забрал с почты только сегодня (28.08).
Плата из поста №80 в окончательном виде "Ардуино как Лего". Оси и подпорки - исключительно для фотки. Общий вес с аккумуляторным блоком 3S 18650 (150гр) составил 245гр. :)
1в) В этом же файле, далее в секции AVR compile patterns добавляем хук вызова objdump после Create output files (.eep and .hex):
## POST objcopy: make objdump too:
recipe.hooks.objcopy.postobjcopy.1.pattern="{compiler.path}{compiler.objdump.cmd}" {compiler.objdump.flags}
1г) В папке <каталог с ИДЕ>/hardware/tool/avr/bin создаем файл с названием avr-objdump.sh и содержимым:
#! /bin/sh
$1avr-objdump -h -S $2 > $3
.. и если надо, меняем ему владельца и даем права на исполнение, типовыми chown,chmod ..
Собственно тут всё - после компиляции/заливки в папке /tmp/build_xxx/ можно будет обнаружить файлик с расширением *.lst - он и есть листинг всей сборки.
2. Доработка настроек ИДЕ для работы с этой и др. платами с расширенной памятью.
2а) если был пропущен п.1б, то тут ему самое место..
2б) В начало файла <каталог с ИДЕ>/hardware/arduino/avr/boards.txt , после menu.cpu=Processor вставляем это:
##-- описание новых пунктов меню - в самое начало файла boards.txt:
menu.BOARD=board subtype
menu.clock=Clock cpu/board
menu.BOD=BOD
menu.LTO=Compiler LTO
menu.BOOT=BOOT options
##-- собственно сборка для всех "мег" совместимой серии:
##############################################################
megaAll.name=*** ATmega640/1280/1281/2560/2561 ***
###--- common settings for all boards and MCU ---###
megaAll.upload.tool=avrdude
megaAll.upload.speed=115200
megaAll.upload.protocol=wiring
megaAll.bootloader.tool=avrdude
megaAll.bootloader.unlock_bits=0x3F
megaAll.bootloader.lock_bits=0x0F
megaAll.build.core=arduino
##--- LTO submenu ---##
# Compiler link time optimization
megaAll.menu.LTO.Os=Disabled
megaAll.menu.LTO.Os.compiler.c.extra_flags=
megaAll.menu.LTO.Os.compiler.c.elf.extra_flags=
megaAll.menu.LTO.Os.lto_elf_flags=
megaAll.menu.LTO.Os.compiler.S.flags=-c -g -x assembler-with-cpp
megaAll.menu.LTO.Os.compiler.cpp.extra_flags=
megaAll.menu.LTO.Os.ltoarcmd=avr-ar
megaAll.menu.LTO.Os_flto=Enabled
megaAll.menu.LTO.Os_flto.compiler.c.extra_flags=-Wextra -flto
megaAll.menu.LTO.Os_flto.compiler.c.elf.extra_flags=-w -flto
megaAll.menu.LTO.Os_flto.lto_elf_flags=-w -flto
megaAll.menu.LTO.Os_flto.compiler.S.flags=-c -g -x assembler-with-cpp -flto
megaAll.menu.LTO.Os_flto.compiler.cpp.extra_flags=-Wextra -flto
megaAll.menu.LTO.Os_flto.ltoarcmd=avr-gcc-ar
##--- board selector submenu ---##
megaAll.menu.BOARD.arduino2560=Arduino Mega 2560 (common)
megaAll.menu.BOARD.arduino2560.upload.maximum_data_size=8192
megaAll.menu.BOARD.arduino2560.build.variant=mega
megaAll.menu.BOARD.arduino2560.build.board=AVR_MEGA2560
megaAll.menu.BOARD.megaADK=Arduino Mega ADK
megaAll.menu.BOARD.megaADK.upload.maximum_data_size=8192
megaAll.menu.BOARD.megaADK.build.variant=mega
megaAll.menu.BOARD.megaADK.build.board=AVR_ADK
megaAll.menu.BOARD.esram32=with ext.SRAM 32 kbytes
megaAll.menu.BOARD.esram32.upload.maximum_data_size=32256
megaAll.menu.BOARD.esram32.build.variant=mega
megaAll.menu.BOARD.esram32.build.board=AVR_MEGA2560
megaAll.menu.BOARD.esram32.compiler.c.elf.extra_flags= {lto_elf_flags} -Wl,--defsym=__heap_start=0x802200,--defsym=__heap_end=0x807fff
megaAll.menu.BOARD.esram64=with ext.SRAM 64 kbytes
megaAll.menu.BOARD.esram64.upload.maximum_data_size=65024
megaAll.menu.BOARD.esram64.build.variant=mega
megaAll.menu.BOARD.esram64.build.board=AVR_MEGA2560
megaAll.menu.BOARD.esram64.compiler.c.elf.extra_flags={lto_elf_flags} -Wl,--defsym=__heap_start=0x802200,--defsym=__heap_end=0x80ffff
megaAll.menu.BOARD.esram5=MYPK-xxx with ext.SRAM 512kbytes
megaAll.menu.BOARD.esram5.upload.maximum_data_size=32256
megaAll.menu.BOARD.esram5.build.variant=sram
megaAll.menu.BOARD.esram5.build.board=AVR_MEGA2560
megaAll.menu.BOARD.esram5.compiler.c.elf.extra_flags={lto_elf_flags} -T{build.variant.path}/avr6.x -Wl,--defsym=__heap_start=0x802200,--defsym=__heap_end=0x807fff
##--- CLOCK submenu ---##
# low fuse bits:
# bit7 -- CKDIV8 ==0 divider 1/8 is ON!
# bit6 -- CKOUT ==0 CLKO is worked to output!
# bit5,4 -- SUT1,0: power on reset timing
# bit3,2,1,0 -- CKSEL3:0:
# -----------------------
# 0xF8..0xFF -- Low power external oscilator
# 0xF6..0xF7 -- Full swing external oscilator
# 0xF4..0xF5 -- Low frequency etxernal oscilator
# 0xF3 -- Internal 128kHz oscilator
# 0xF2 -- Calibrated internal oscilator (8Mhz)
# 0xF0 -- external clock no oscilator
#
megaAll.menu.clock.20ext=20 MHz external
megaAll.menu.clock.20ext.build.f_cpu=20000000L
megaAll.menu.clock.20ext.bootloader.low_fuses=0xf7
megaAll.menu.clock.18ext=18.432 MHz external
megaAll.menu.clock.18ext.build.f_cpu=18432000L
megaAll.menu.clock.18ext.bootloader.low_fuses=0xf7
megaAll.menu.clock.16ext=16 MHz external
megaAll.menu.clock.16ext.build.f_cpu=16000000L
megaAll.menu.clock.16ext.bootloader.low_fuses=0xf7
megaAll.menu.clock.12ext=12 MHz external
megaAll.menu.clock.12ext.build.f_cpu=12000000L
megaAll.menu.clock.12ext.bootloader.low_fuses=0xf7
megaAll.menu.clock.8ext=8 MHz external
megaAll.menu.clock.8ext.build.f_cpu=8000000L
megaAll.menu.clock.8ext.bootloader.low_fuses=0xf7
megaAll.menu.clock.int8=internal 8 MHz
megaAll.menu.clock.int8.build.f_cpu=8000000L
megaAll.menu.clock.int8.bootloader.low_fuses=0xE2
megaAll.menu.clock.int1M=internal 1 MHz
megaAll.menu.clock.int1M.build.f_cpu=1000000L
megaAll.menu.clock.int1M.bootloader.low_fuses=0x62
megaAll.menu.clock.128k=internal 128kHz
megaAll.menu.clock.128k.build.f_cpu=128000L
megaAll.menu.clock.128k.bootloader.low_fuses=0x62
##--- BOD Level submenu ---##
# extended fuse bits only this works:
megaAll.menu.BOD.dis=Disabled
megaAll.menu.BOD.dis.bootloader.extended_fuses=0xff
megaAll.menu.BOD.1v8=1.8v (4Mhz)
megaAll.menu.BOD.1v8.bootloader.extended_fuses=0xfe
megaAll.menu.BOD.2v7=2.7v (8Mhz)
megaAll.menu.BOD.2v7.bootloader.extended_fuses=0xfd
megaAll.menu.BOD.4v3=4.3v (16Mhz)
megaAll.menu.BOD.4v3.bootloader.extended_fuses=0xfc
#megaAll.bootloader.extended_fuses=0xfc
##--- BOOT submenu ---##
# High fuse bits this:
# bit7 -- OCDEN -- ?!?
# bit6 -- JTAGEN -- JTAG debugging is enable?
# bit5 -- SPIEN -- SPI loading is enable?
# bit4 -- WDTON -- watchdog is on always?
# bit3 -- EESAVE -- protected EEPROM for erase?
# bit2,1 -- BOOTSZ -- size for bootloader: 00-4096b, 01-2048b, 10-1024b, 11-512b
# bit0 -- BOOTRST -- from where reset start
# only SPI + bootsector=4096
megaAll.menu.BOOT.stk500v2=stk500v2
megaAll.menu.BOOT.stk500v2.bootloader.high_fuses=0xD8
megaAll.menu.BOOT.stk500v2.bootloader.file=stk500v2/stk500boot_v2_mega2560.hex
# optiboot in 1024 sector
megaAll.menu.BOOT.optiboot=Optiboot in 1024 bytes
megaAll.menu.BOOT.optiboot.bootloader.high_fuses=0xD6
megaAll.menu.BOOT.optiboot.bootloader.file=optiboot_flash_256kb/{build.mcu}/optiboot_flash_{build.mcu}_{upload.speed}_{build.f_cpu}.hex
##--- CPU submenu ---
megaAll.menu.cpu.atmega2560=ATmega2560
megaAll.menu.cpu.atmega2560.build.core=arduino
megaAll.menu.cpu.atmega2560.build.board=AVR_ATmega2560
megaAll.menu.cpu.atmega2560.build.mcu=atmega2560
megaAll.menu.BOOT.stk500v2.menu.cpu.atmega2560.upload.maximum_size=258048
megaAll.menu.BOOT.optiboot.menu.cpu.atmega2560.upload.maximum_size=261120
# special for 1280 arduino loader
megaAll.menu.cpu.atmega1280=ATmega1280
megaAll.menu.cpu.atmega1280.BOARD.mega2560.build.board=AVR_MEGA
megaAll.menu.BOOT.1280spec.menu.cpu.atmega1280=mega1280 arduino bootloader
megaAll.menu.BOOT.1280spec.menu.cpu.atmega1280.bootloader.high_fuses=0xDA
megaAll.menu.BOOT.1280spec.menu.cpu.atmega1280.bootloader.file=atmega/ATmegaBOOT_168_atmega1280.hex
megaAll.menu.BOOT.1280spec.menu.cpu.atmega1280.upload.speed=57600
megaAll.menu.BOOT.1280spec.menu.cpu.atmega1280.maximum_size=126976
megaAll.menu.BOOT.stk500v2.menu.cpu.atmega1280.maximum_size=126976
megaAll.menu.BOOT.optiboot.menu.cpu.atmega1280.maximum_size=130048
megaAll.menu.cpu.atmega1280.build.mcu=atmega1280
megaAll.menu.cpu.atmega640=ATmega640
megaAll.menu.cpu.atmega640.build.core=MegaCore
megaAll.menu.cpu.atmega640.build.variant=standard
megaAll.menu.cpu.atmega640.build.board=AVR_ATmega640
megaAll.menu.BOOT.stk500v2.menu.cpu.atmega640.upload.maximum_size=61440
megaAll.menu.BOOT.optiboot.menu.cpu.atmega640.upload.maximum_size=64512
megaAll.menu.cpu.atmega640.build.mcu=atmega640
megaAll.menu.cpu.atmega2561=ATmega2561
megaAll.menu.cpu.atmega2561.build.core=MegaCore
megaAll.menu.cpu.atmega2561.build.variant=standard
megaAll.menu.cpu.atmega2561.build.board=AVR_ATmega2561
megaAll.menu.BOOT.stk500v2.menu.cpu.atmega2561.upload.maximum_size=258048
megaAll.menu.BOOT.optiboot.menu.cpu.atmega2561.upload.maximum_size=261120
megaAll.menu.cpu.atmega2561.build.mcu=atmega2561
megaAll.menu.cpu.atmega1281=ATmega1281
megaAll.menu.cpu.atmega1281.build.core=MegaCore
megaAll.menu.cpu.atmega1281.build.variant=standard
megaAll.menu.cpu.atmega1281.build.board=AVR_ATmega1281
megaAll.menu.BOOT.stk500v2.menu.cpu.atmega1281.maximum_size=126976
megaAll.menu.BOOT.optiboot.menu.cpu.atmega1281.maximum_size=130048
megaAll.menu.cpu.atmega1281.build.mcu=atmega1281
2в) в папке <каталог с ИДЕ>/hardware/arduino/avr/variants создаем папку sram и копируем в ней файлик <каталог с ИДЕ>/hardware/arduino/avr/variants/mega/pins_arduino.h
2г) создаем там же файлик <каталог с ИДЕ>/hardware/arduino/avr/variants/sram/avr6.x с таким вот содержимым:
.. собственно, если ничего не забыл, то это всё. В ИДЕ появится опция выбора "*** ATmega640/1280/1281/2560/2561 ***" с расширенным меню и возможностью управлять выбором процессора из семейства, частотой камня, уровнем Bodlevel, включением опции LTO и типом платы: с памятью, без памяти и какой памятью..
Ну и под занавес, тестовый пример:
/**
* Extended Memory Sram (ems)
*
* !!! ВНИМАНИЕ: требует специального скрипта для линковщика с описанием секций RAM
*
* Тестовый код для управления внешней SRAM 512кбайт.
* Организация SRAM:
* PORTA -- AD0..7 совмещенная шина адреса/данных стробируется сигналом ALE ()
* регистры управления:
* XMCRA (0x74):
* 7 6 5 4 3 2 1 0
* SRE, SRL2, SRL1, SRL0, SRW11, SRW10, SRW01, SRW00
*
* SRE - enable xmem, SRL2:0 - lower/upper sectors {0,1:all upper, 2-7:0x4..0xE upper},
* SRW1x - wait states for upper, SRW0x - wait states for lower
*
* XMCRB (0x75):
* 7 2 1 0
* XMBK, ххх, XMM2, XMM1, XMM0
*
* XMBK - bus keeper enable, XMMx - Mask High Adress lines {0-all;1-PC7;2-PC6,7;..7-only 8 bit address)
*
* Регистр номеров страниц: старшая тетрада - 0..15 верхнее окно 0xC000, младшая тетрада - 0..15 нижнее окно 0x8000
* Запись в регистр страниц через PORTA при !RD = !WR = 0, то есть номер проходит в регистр, если оба сигнала LOW
* и защелкивается при записи HIGH в любой из пинов !RD, !WR.
*
* @author Arhat109
*/
// полезный, удобный макрос:
#define makeByte(h,l) ((((h)<<4)&0xF0)|((l)&0x0F))
// определяем где и что находится у микроконтроллера (ATmega2560):
// 19 линий интерфейса внешней SRAM:
#define EMS_ALE PG2
#define EMS_RD PG1
#define EMS_WR PG0
#define EMS_PORT_BUS PORTG
#define EMS_DDR_BUS DDRG
#define EMS_PORT_AD PORTA
#define EMS_DDR_AD DDRA
#define EMS_PORT_A PORTC
// последние адреса окон памяти и размеры окон:
#define EMS_OWN_END (0x21FF)
#define EMS_COMMON_END (0x7FFF)
#define EMS_LP_END (0xBFFF)
#define EMS_HP_END (0xFFFF)
#define SIZE_COMMONS (EMS_COMMON_END - EMS_OWN_END)
#define SIZE_LOWS (EMS_LP_END - EMS_COMMON_END)
#define SIZE_HIGHS (EMS_HP_END - EMS_LP_END)
// Доступные секции памяти (удобства для), см. спец. секции в расширении файла линковщика:
// На самом деле есть только 4 типа адресов (окон у процессора):
// внутренняя память, общий блок до 32кбайт и нижние/верхние окна для подключения страниц..
// без указания страницы - размещается во внутренеей памяти, секции .data, .bss, .. @see avr6.x
// EMS_COMMON -- общий блок с конца internal до 32768-го байта
// EMS_LOW() -- [0,15] -- размещение в "нижних" сегментах по 16кб с адреса 0x8000
// EMS_HIGH() -- [0,15] -- размещение в "верхних" сегментах по 16кб с адреса 0xC000
// Внимательно! последние страницы (15) - это тот же блок COMMON!
// это можно использовать для работы с экранированным куском в 8кб, через EMS_LOW(15)..
//
#define EMS_COMMON __attribute__((section(".cpage")))
#define EMS_LOW(pageNum) __attribute__((section(".lpage" #pageNum)))
#define EMS_HIGH(pageNum) __attribute__((section(".hpage" #pageNum)))
uint8_t emsPages; // копия регистра страниц в нижней SRAM
uint8_t emsWaits; // wait states @see emsOn(_ws);
/**
* ТОЛЬКО активация Extended Memory SRAM единым верхним куском без удержания шины
* + включение упр. сигналов на выход и заодно подтяжка к +5в
* @param uint8_t _ws -- wait states for interface
* @time 10t (0.625usec)
*/
#define emsOn(_ws) \
{ \
emsWaits = (_ws); \
XMCRA = (1 << SRE) | ((_ws)<<2); \
XMCRB = 0; \
EMS_DDR_BUS |= (1<<EMS_ALE)|(1<<EMS_RD)|(1<<EMS_WR); \
}
// отключалка интерфейса extSRAM, @time 1t
#define emsOff() (XMCRA = 0)
// =========================== Только сохранение в глобал: ======================== //
// @param uint8_t lp,hp,p
// emsSetLow(),emsSetHigh() -- @time 7t
// emsSetPages() -- 2t
#define emsSetLow(lp) (emsPages=(emsPages&0xF0) | ((uint8_t)(lp)&0x0F))
#define emsSetHigh(hp) (emsPages=((uint8_t)(lp)&0xF0) | (emsPages&0x0F))
#define emsSetPages(p) (emsPages=(uint8_t)(p))
// =========================== Установка И запись страниц: ======================== //
/**
* Пишем номера страниц блоков low, high в регистр страниц вручную, отключая extSRAM
* @time 27t (1.4375usec)
*/
void emsWritePages(void)
{
cli();
emsOff(); // 1t, отключаем интерфейс extSRAM
EMS_DDR_AD = 0xFF; // 1t, порт А (A/D) "на выход"
EMS_PORT_AD = emsPages; // 1t, номера страниц в A/D
EMS_PORT_BUS &= ~((1<<EMS_WR)|(1<<EMS_RD)); // 3t, оба сигнала в LOW уже "на выходе"
__asm__ __volatile__ ("nop" :::); // 1t, ждем 1 такт (62.5нсек)
EMS_PORT_BUS |= ((1<<EMS_WR)|(1<<EMS_RD)); // 3t, оба сигнала в HIGH
EMS_DDR_AD = 0; // 1t, освобождаем порта А
emsOn(emsWaits); // 10t, включаем интерфейс, восстанавливая тайминги
sei(); // 1t+1t+4t (cli,sei,ret)
}
// @param uint8_t lowPage
// @time 34t
#define emsWriteLow(lowPage) \
{ \
emsSetLow(lowPage); \
emsWritePages(); \
}
// @param uint8_t highPage
// @time 34t
#define emsWriteHigh(highPage) \
{ \
emsSetHigh(highPage); \
emsWritePages(); \
}
/**
* Проверяет заданный буфер на совпадение с шаблоном,
* в случае расхождения вызывает callback() и возвращает 1
* прерывает работу, если callback() возвращает не 0.
*
* @param void (*)callback(uint16_t _addr, uint8_t _pattern, uint8_t _readed)
* @return count summary errors
*/
uint16_t emsMemtest(
uint8_t * _buffer, // буфер поиска расхождения
uint16_t _size, // размер буфера
uint8_t _pattern, // искомый образец
uint8_t (*callback)(uint8_t *_addr, uint8_t _pattern, uint8_t _readed) // нашел тут это!
){
uint16_t res = 0;
do{
uint8_t tmp = *_buffer;
if( tmp != _pattern ){
res++;
if( callback(_buffer, _pattern, tmp) ) return res;
}
_buffer++;
}while( --_size > 0 );
return res;
}
// =========================== ТЕСТОВЫЙ ПРИМЕР ТУТ: ======================== //
// объявляем 3 массива в разных окнах:
uint8_t commons[SIZE_COMMONS] EMS_COMMON; // 24064 байт от внутренней SRAM в первых 32кбайтах
uint8_t lows[SIZE_LOWS] EMS_LOW(0); // 16кбайт от 32к до 48к
uint8_t highs[SIZE_HIGHS] EMS_HIGH(0); // 16кбайт от 48к до 64к
uint8_t patterns[4] = {0xFF, 0x00, 0xAA, 0x55}; // образцы записи в память
#define MAX_PAGES 3
struct{
uint8_t * address;
uint16_t size;
} pages[MAX_PAGES] = {{commons, SIZE_COMMONS}, {lows, SIZE_LOWS}, {highs, SIZE_HIGHS}};
/**
* callback() для вывода ошибки сохранения на монитор (и прервать работу)
*/
uint8_t testError(uint8_t *_addr, uint8_t _pattern, uint8_t _readed)
{
Serial.println();
Serial.print("ERROR at 0x"); Serial.print((uint16_t)_addr, HEX);
Serial.print(", pattern="); Serial.print(_pattern, DEC);
Serial.print(", readed="); Serial.print(_readed, DEC);
return 0;
}
// =========================== SETUP() ======================== //
void setup()
{
emsOn(2);
Serial.begin(115200);
}
void loop()
{
for(int8_t i=sizeof(patterns); i>=0; --i){
// memset(commons, patterns[i], SIZE_COMMONS);
// memset(lows, patterns[i], SIZE_LOWS);
// memset(highs, patterns[i], SIZE_HIGHS);
for(int page=0; page<MAX_PAGES; page++){
Serial.println();
Serial.print("PAGE="); Serial.print(page, DEC);
memset(pages[page].address, patterns[i], pages[page].size);
if(
emsMemtest(pages[page].address, pages[page].size, patterns[i], testError) != 0
){
Serial.println("..ERROR OCURED..");
}else{
Serial.print(" is OK :)");
}
}
}
}
// Скетч использует 2840 байт (1%) памяти устройства. Всего доступно 258048 байт.
// Глобальные переменные используют 283 байт (0%) динамической памяти, оставляя 31973 байт для локальных переменных. Максимум: 32256 байт.
P.S.
Конкретно эта плата (на фото выше) расширенной памяти у меня пока так и не заработала, валит ошибки, как и самая первая больше года назад .. буду смотреть под осцилом "что не так"..
1. Требуется проверка правильности boards.txt в части фьюзов остальных процессоров семейства .. у меня в наличии есть только 2560. Делал, списывая с проекта MegaCore, но не уверен что все разобрал верно..
2. Файл линковщика avr6.x содержит прямое указание на размер внутренней SRAM меги. Для использования с младшими моделами, и для расширения работы с серией ATmega128a, имеет смысл доработать boards.txt и avr6.x так, чтобы символ __heap_size определялся по типу процессора (сколько там у него есть своей SRAM) И типу платы (есть ли расширенная память и сколько её) ..
с наскока это сделать не удалось, мне остальные камни как-то без надобности, но для полноты картины - было бы неплохо.
P.S.
Точно также можно переработать boards.txt для разных плат под 328p и прочие камни семейства, заодно расширив возможности по управлению прошивкой и заливкой ..
На плате расширения памяти обнаружено большое количество непропаев .. руки из *опы..
Но! Что интересно: на базе макросов из примера легко создается тестовый скетч для .. ручного управления памятью чуть ли не в "секундных" таймингах и .. осцилограф можно заменить .. набором светодиодов (что и сделал). :)
По бокам платы и заодно с ней изготовлены боковые планки для расширительных разьемов (пайка к внешнему ряду сигнальных разьемов на плате). Внутренний ряд - под типовые штырьковые разьёмы "мама" для соединения с платами "вверх" или удлиненные "вврех/вниз". Кому что интересно - то и запаиваем. :)
Все надписи на плате - читаемы, все номиналы - подписаны. :)
Прозвонил, пропаял плату из поста №84 .. в итоге, 4-й канал мультиплескора оказался "битый". Не держит на своем выходе "1" когда отключен (сигнал по схеме page1). Засада, похоже придется выпаивать.
В общем, закрываю эту тему на грустной ноте: окончательно тест памяти не прошел, хотя и поменял мультиплексор и в ручном режиме он проходит успешно.
Код тестовых примеров (их тут несколько):
/**
* Extended Memory Sram (ems)
*
* !!! ВНИМАНИЕ: требует специального скрипта для линковщика с описанием секций RAM
*
* Тестовый код для управления внешней SRAM 512кбайт.
* Организация SRAM:
* PORTA -- AD0..7 совмещенная шина адреса/данных стробируется сигналом ALE
* регистры управления:
* XMCRA (0x0074):
* 7 6 5 4 3 2 1 0
* SRE, SRL2, SRL1, SRL0, SRW11, SRW10, SRW01, SRW00
*
* SRE - разрешает работу интерфейса,
* SRL2:0 - граница нижнего/верхнего сегмента (адресного пространства {0,1:all upper, 2-7:0x4000..0xE000 upper},
* SRW1x - количество доп. тактов ожидания для верхнего сегмента,
* SRW0x - оно же для нижнего, если он есть.
*
* XMCRB (0x0075):
* 7 2 1 0
* XMBK, ххх, XMM2, XMM1, XMM0
*
* XMBK - удекрживать данные на шине между тактами и адресом/данных (не переводить в Hz),
* XMMx - ширина шины адреса {0-all 16бит = 64k;1-PC7 не исп. 15бит 32k; 2-PC6 16k,..7-only 8 bit address без порта C)
*
* SRAM512kb имеет дополнительно:
* 1. Регистр номеров страниц: старшая тетрада - 0..15 верхнее окно 0xC000, младшая тетрада - 0..15 нижнее окно 0x8000
* Запись в регистр страниц: вручную, через PORTA при !RD = !WR = 0, то есть номер проходит в регистр, если оба сигнала LOW
* и защелкивается при записи HIGH в любой из пинов !RD, !WR или оба сразу.
* Чтение регистра страниц - не предусмотрено.
* Но из него есть комбинированный вывод 4 бит (0xF, старших или младших), в зависимости от PC7,PC6:00|01=>F, 10=>low, 11=>high;
* то есть, младшие 32к адресов (PC7=0) выдают F, а старшие (PC7=1) в зависимости от PC6 младшую или старшую тетраду.
* 2. Регистр защелка сладшей части адреса. Используется интерфейсом самостоятельно, но можно использовать вручную при дополнительно,
* при отключенном интерфейсе: PORTA записывается в регистр при ALE=1 и защелкивается в нем при ALE=0. Этот регистр имеет свои
* отдельные выводы на шине расширения памяти, также как регистр страниц.
*
* @author Arhat109
*/
// полезный, удобный макрос: собирает байт из младших полубайтов со сдвигами..
#define makeByte(h,l) (((((uint8_t)(h))<<4)&0xF0)|(((uint8_t)(l))&0x0F))
// определяем где и что находится у микроконтроллера (ATmega2560):
// 19 линий интерфейса внешней SRAM:
#define EMS_ALE PG2
#define EMS_RD PG1
#define EMS_WR PG0
#define EMS_PORT_BUS PORTG
#define EMS_DDR_BUS DDRG
#define EMS_PORT_AD PORTA
#define EMS_DDR_AD DDRA
#define EMS_PORT_ADR PORTC
#define EMS_DDR_ADR DDRC
// последние адреса окон памяти и размеры окон:
// [0-8.5k[ - "own page"(internal SRAM); [8.5k-32k[ - "common page";
// [32k-48k[ - "low page"; [48k-64k[ - "high page".
#define EMS_OWN_END (0x21FF)
#define EMS_COMMON_END (0x7FFF)
#define EMS_LP_END (0xBFFF)
#define EMS_HP_END (0xFFFF)
#define SIZE_COMMONS (EMS_COMMON_END - EMS_OWN_END)
#define SIZE_LOWS (EMS_LP_END - EMS_COMMON_END)
#define SIZE_HIGHS (EMS_HP_END - EMS_LP_END)
// Доступные секции памяти (удобства для), см. спец. секции в расширении файла линковщика:
// На самом деле есть только 4 типа адресов (окон у процессора):
// внутренняя память, общий блок до 32кбайт и нижние/верхние окна для подключения страниц..
// Данные, обьявленные:
// а) без указания страницы - размещается во внутренеей памяти (секции .data, .bss, @see avr6.x):
// б) EMS_COMMON -- в общем блоке от конца internal до 32767-го байта включительно;
// в) EMS_LOW() -- [0,15] -- размещение в "нижних" сегментах по 16кб с адреса 0x8000
// г) EMS_HIGH() -- [0,15] -- размещение в "верхних" сегментах по 16кб с адреса 0xC000
// Внимательно! последние страницы (15) - это тот же блок что и COMMON,
// но так можно иметь доступ к его младшим адресам, перекрытым внутренней памятью
//
#define EMS_COMMON __attribute__((section(".cpage")))
#define EMS_LOW(pageNum) __attribute__((section(".lpage" #pageNum)))
#define EMS_HIGH(pageNum) __attribute__((section(".hpage" #pageNum)))
uint8_t emsPages; // копия регистра страниц в нижней SRAM
uint8_t emsWaits; // количество тактов ожидания интерфейса @see emsOn(_ws);
/** 3t: установка служебных ног интерфейса "на выход" */
#define emsBusOut() (EMS_DDR_BUS |= (1<<EMS_ALE)|(1<<EMS_RD)|(1<<EMS_WR))
/** 3t: разрешение записи в регистр страницы (интерфейс д.быть отключен!) */
#define emsPagesOpen() (EMS_PORT_BUS &= ~((1<<EMS_WR)|(1<<EMS_RD)))
/** 3t: защелкивание в регистре страниц значения с шины AD (интерфейс д.быть отключен!) */
#define emsPagesLock() (EMS_PORT_BUS |= ((1<<EMS_WR)|(1<<EMS_RD)))
/**
* ТОЛЬКО активация Extended Memory SRAM единым верхним куском без удержания шины
* + включение упр. сигналов на выход и заодно подтяжка к +5в
*
* @param uint8_t _ws -- wait states for interface
* @time 8t(const _ws)/7t(register _ws)
*/
#define emsOn(_ws) \
{ \
emsWaits = (_ws); \
XMCRA = (1 << SRE) | (((uint8_t)(_ws))<<2); \
XMCRB = 0; \
}
/**
* отключалка интерфейса extSRAM
* @time 1t
*/
#define emsOff() (XMCRA = 0)
/**
* Настройка БЕЗ включения интерфейса для setup() или раньше
*
* @param uint8_t _ws -- wait states for interface
* @time 6t
*/
#define emsSetup() \
{ \
EMS_DDR_AD = 0xFF; \
EMS_DDR_ADR = 0xFF; \
emsBusOut(); \
}
/**
* =================== Макросы сохранения номера страницы в глобал: ================ //
* @param uint8_t lp,hp,p
* @time 7t для emsStoreLow(),emsStoreHigh()
* @time 2t для emsStorePages()
*/
#define emsStoreLow(lp) (emsPages=(emsPages&0xF0) | ((uint8_t)(lp)&0x0F))
#define emsStoreHigh(hp) (emsPages=((uint8_t)(lp)&0xF0) | (emsPages&0x0F))
#define emsStorePages(p) (emsPages=(uint8_t)(p))
/**
* =========================== Установка И запись страниц: ======================== //
* Пишем номера страниц блоков low, high в регистр страниц вручную, отключая extSRAM
*
* @time 31t (1.9375usec)
*/
void emsWritePages(void)
{
cli();
emsOff(); // 1t, отключаем интерфейс extSRAM
EMS_DDR_AD = 0xFF; // 1t, порт А (A/D) "на выход"
emsPagesOpen(); // 3t, оба сигнала !RD,!WR в LOW
EMS_PORT_AD = emsPages; // 1t, номера страниц в A/D
__asm__ __volatile__ ("nop" :::); // 1t, ждем 1 такт (62.5нсек)
emsPagesLock(); // 3t, оба сигнала !RD,!WR в HIGH
EMS_DDR_AD = 0; // 1t, освобождаем порта А
emsOn(emsWaits); // 10t, включаем интерфейс, восстанавливая тайминги
sei(); // 1t+1t+4t+4t (cli+sei+ret+call)
}
/**
* Запись в регистр страниц номер low page [0..15]
*
* @param uint8_t lowPage
* @time 38t
*/
#define emsWriteLow(lowPage) \
{ \
emsStoreLow(lowPage); \
emsWritePages(); \
}
/**
* Запись в регистр страниц номер high page [0..15]
*
* @param uint8_t highPage
* @time 38t
*/
#define emsWriteHigh(highPage) \
{ \
emsStoreHigh(highPage); \
emsWritePages(); \
}
/**
* Проверяет заданный буфер на совпадение с шаблоном,
* в случае расхождения вызывает callback().
* Прерывает работу, если callback() возвращает не 0.
*
* @param void (*)callback(uint16_t _addr, uint8_t _pattern, uint8_t _readed)
* @return count summary errors
*/
uint16_t emsMemtest(
uint8_t * _buffer, // буфер поиска расхождения
uint16_t _size, // размер буфера
uint8_t _pattern, // искомый образец
uint8_t (*callback)(uint8_t *_addr, uint8_t _pattern, uint8_t _readed) // нашел тут это!
){
uint16_t res = 0;
do{
uint8_t tmp = *_buffer;
if( tmp != _pattern ){
res++;
if( callback(_buffer, _pattern, tmp) ) return res;
}
_buffer++;
}while( --_size > 0 );
return res;
}
// =========================== ТЕСТ РУЧНОГО УПРАВЛЕНИЯ: ======================== //
/**
* в loop():
* Функция медленной и ручной побитной установки значений на шинах для
* проверки светодиодами работоспосоности пайки и платы в целом..
* щелкает сигналом ALE для проверки записи в регистр защелки адреса.
* Также можно проверить работу мультиплексора адреса от PC6,7
*/
void manualTest()
{
for(uint8_t bit=0; bit<=7; bit++){
EMS_PORT_BUS |= (1<<EMS_ALE); // разрешаем запись
emsPagesOpen(); // открываем регистр страниц на запись
EMS_PORT_AD = (1<<bit); // младшая часть адреса в защелку
EMS_PORT_BUS &= ~(1<<EMS_ALE); // защелкиваем младшую часть адреса (шина AD)
delay(500); // на посмотреть..
emsPagesLock(); // защелкиваем и в регистр страниц тоже
EMS_PORT_ADR = (1<<bit); // старшая часть адреса отдельно
delay(500);
}
}
/**
* Имитация записи/четния SRAM вручную
*/
void manualTest2()
{
EMS_PORT_ADR = 0; // старшая часть адреса тут не нужна
for(uint8_t bit=0; bit<=7; bit++){
EMS_PORT_AD = (1<<bit); // младшая часть адреса (bit) в защелку
EMS_PORT_BUS |= (1<<EMS_ALE); // разрешаем запись адреса
delay(500); // на посмотреть..
EMS_PORT_BUS &= ~(1<<EMS_ALE); // защелкиваем младшую часть адреса (шина AD)
EMS_PORT_AD = ~(1<<bit); // данные на шину (кроме bit)
EMS_PORT_BUS &= ~(1<<EMS_WR); // запись
delay(500);
EMS_PORT_BUS |= (1<<EMS_WR); // типа записали
EMS_PORT_AD = 0; // гасим все после записи
delay(1000);
EMS_PORT_AD = (1<<bit); // младшая часть адреса снова в защелку
EMS_PORT_BUS |= (1<<EMS_ALE); // разрешаем запись адреса
delay(500); // на посмотреть..
EMS_PORT_BUS &= ~(1<<EMS_ALE); // защелкиваем младшую часть адреса (шина AD)
EMS_DDR_AD = 0; // шина AD на ввод
EMS_PORT_BUS &= ~(1<<EMS_RD); // чтение
delay(500);
EMS_PORT_BUS |= (1<<EMS_RD); // типа прочли
EMS_DDR_AD = 0xFF; // шина AD на вывод
EMS_PORT_AD = 0; // гасим все после чтения
delay(1500);
}
}
// ============================= Простейший тест: ========================== //
volatile uint8_t *adr;
void simpleTest(void)
{
uint8_t tmp;
Serial.println("Simple test started..");
for( adr=(uint8_t*)(EMS_OWN_END+1); adr<=(uint8_t*)(EMS_OWN_END+10); adr++){
// for( adr=(uint8_t*)(0x1000); adr<=(uint8_t*)(0x1000+10); adr++){
*adr = 0xAA;
tmp = *adr;
if( tmp != 0xAA ){ Serial.println(tmp, DEC); }
else { Serial.println("ok"); }
}
Serial.println("Simple test ended");
do{}while(1);
}
// =========================== ТЕСТОВЫЙ ПРИМЕР ТУТ: ======================== //
// объявляем 3 массива в разных окнах:
uint8_t commons[SIZE_COMMONS] EMS_COMMON; // 24064 байт от внутренней SRAM в первых 32кбайтах
uint8_t lows[SIZE_LOWS] EMS_LOW(0); // 16кбайт от 32к до 48к
uint8_t highs[SIZE_HIGHS] EMS_HIGH(0); // 16кбайт от 48к до 64к
uint8_t patterns[4] = {0xFF, 0x00, 0xAA, 0x55}; // образцы записи в память
/** собираем из обьявленных массивов пакет для тестирования */
#define MAX_PAGES 3
struct{
uint8_t * address;
uint16_t size;
} pages[MAX_PAGES] = {{commons, SIZE_COMMONS}, {lows, SIZE_LOWS}, {highs, SIZE_HIGHS}};
/**
* callback() для вывода ошибки сохранения на монитор (и прервать работу)
*/
uint8_t testError(uint8_t *_addr, uint8_t _pattern, uint8_t _readed)
{
Serial.println();
Serial.print("ERROR at 0x"); Serial.print((uint16_t)_addr, HEX);
Serial.print(", pattern="); Serial.print(_pattern, DEC);
Serial.print(", readed="); Serial.print(_readed, DEC);
return 0;
}
/**
* Собственно тест памяти:
* перебираем образцы, и страницы в пакете, заливаем страницу образцом
* и проверяем на совпадение с шаблоном
*/
void test(void){
uint16_t errors;
for(int8_t i=sizeof(patterns); i>=0; --i){
for(int page=0; page<MAX_PAGES; page++){
Serial.println();
Serial.print("PAGE="); Serial.print(page, DEC);
errors=0;
memset(pages[page].address, patterns[i], pages[page].size);
errors = emsMemtest(pages[page].address, pages[page].size, patterns[i], testError);
if( errors != 0 ){
Serial.print(" .. ERROR OCURED=");
Serial.println(errors, DEC);
}else{
Serial.println(" .. is OK)");
}
}
}
}
// =========================== SETUP() ======================== //
void setup()
{
// emsSetup();
emsOn(0);
Serial.begin(115200);
}
void loop()
{
// manualTest();
// manualTest2();
simpleTest();
// test();
}
emsSetup() - настройка пинов для ручного управления в тестах manualTest() и manualTest2(). Они оба проходят нормально, второй тест в память пишет и читает как надо (память - статическая, отрабатывает по 0.5сек вполне нормально), проверялось подключением кучки светодиодов к шине расширения памяти (как на панельках старых ЭВМ).
В simpleTest() закомментированный цикл - заменяет чтение/запись на внутр. SRAM - выдает "ok". А вот замена адресов цикла на внешнюю SRAM - выдает все 10 ошибок чтения/записи с любой задержкой 0..3.
Результат компиляции - смотрел: хоть и коряво, но всё вполне верно. Настройка boards.txt работает нормально, все адреса транслируются куда надо..
Похоже дальше надо смотреть на осцилографе и желательно многолучевом .. у меня такого нет, да и убрано в общем-то всё уже далеко.
Таким способом можно подключать любую параллельную память для работы как с внутренней SRAM через простые объявления данных (массивов, переменных и т.д.). Проблема у меня в том, что не смог пройти даже simpleTest(), хотя "вручную" в память пишется и читается как надо, по крайней мере по 8-и "избранным" адресам.
P.S. посмотрел, конкретно для этой даже особо схему не потребуется перерисовывать. Так, перенести пару ножек из-за разницы корпусов SOP-32 и SOP-28 ..
Кстати, цикл доступа к внешней SRAM у меги 2560 занимает 3 цикла шины, что снижает требования к чипам до 62.5 * 3 = 180нс. Так что имеющиеся 70нсек - что называется "за глаза" и даже без циколов ожидания.
В общем, так и осталось непонятным почему в ручном режиме память пишется и читается (по 8-и избранным адресам "как получилось"), а в нормальном режиме из неё читается .. младший байт адреса. Как буд-то как ALE защелкнул на шине адрес, так он на ней и подвис..
Достал плату, потестил ещё разок, проверил все тайминги. В общем, схему надо откорректировать:
1. Сигнал ALE подтягивать резистором к питанию - вредно. Резистор лучше убрать;
2. Сигнал CS микросхемы памяти надо посадить на землю, подавать на него сигнал ALE через диод - вредно, резко удлиняется цикл чтения и память перестает работать даже 3-я циклами ожидания.
С такими изменениями - память работает и даже без циклов ожидания, по крайней мере первые 32к (которые в общем-то почти последние в самой памяти), контроллер страниц ещё досконально не проверял, но там "дурить" особо нечему.
Только программно надо помнить о том, что при записи номера страниц в регистр памяти сигнал WR надо теперь устанавливать первым, чтобы перевести ноги данных в третье состояние раньше, чем на шину выйдут номера страниц из микроконтроллера. Когда оба сигнала WR,RD в нуле - микросхема памяти переводит свою шину также в третье состояние, что и решает проблему отказа от соединения CS и ALE.
В общем, на этом всё. Схема - вполне рабочая, ПО - тоже. Заливал в т.ч. и с настрокой на внутренний генератор 8Мгц - работает.
P.S. Дотестил диспетчер страниц памяти. На схему надо добавить резистор с базы на землю у Q1 не более 4.7к-9.1к, иначе он не закрывается полноценно. Резистор R14 2.4к стоит уменьшить до 1к. В таком варианте, пауза для записи номера страницы получается около 45-90 тактов ЦПУ на 16Мгц. Чем меньше резистор с базы на землю - тем быстрее запирается этот транзистор. В целом некритично, поскольку это механизм переключения страниц. В остальном - плата полностью рабочая.
В общем, ногой CS микросхемы памяти, таки надо управлять. Дело в том, что при записи номеров страниц в регистр страниц тут использован факт одновременно низкого уровня на выходах WR и RD, а поскольку на шине адреса SRAM все равно "какой-то" адрес получается выставлен (защелка младших адресов - что-то помнит с предыдущего обращения, а старшая часть адреса порт "С" - тоже или в нулях или в высоком состоянии), то снятие сигналов WR=RD=1 .. приводит к записи номера страницы кроме регистра ещё в какую-то ячейку памяти. Фигня.
В общем, не придумал способа КАК из сигналов шины (и только) реализовать управление ногой CS, поэтому вывел её на отдельную ножку МК. Отключаем доступ к памяти CS=1 для перезаписи номеров страниц и включаем CS=0 в макросе emsOn(). Теперь дополнительная нога микроконтроллера управляет сигналом CS через вход "EN", к которому она подсоединяется внешним проводом, соответственно можно выбрать любую. У меня сейчас это PCINT15 (см. пост№77)
Можно. Пишщите на почту arhat109 собака на мейл который ru, впрочем тут мой ящик тиражирован многажды. :)
P.S. Готовых плат сейчас уже нет. Сами платы травятся, наносятся все обозначения в Китае (тут есть где). Для повтора есть только вариант в линуксо-праведном Kicad..
Для платы из поста №88 сейчас из Китая едут платы ОЗУ на 512кб + разьем для SD-карт по SPI шине. Жду, новую версию для пайки "последнего релиза".. :)
да ну нах... можно было просто увеличить с краев, добавив ряды контактов и плату оставить формата ардуино мега, дичь полная получилась - никому, кроме 10ти человек, ненужная. Вообще дичь, с учетом того что плата и схема ардуины меги есть всети к тому же там еще куча места пустого. Меня надо было српосить прежде чем делать.
Платы пришли ещё 13 апреля, но только сегодня сумел забрать на почте:
Качество очень даже впечатлило. На коробке сверху плата под ATmega128a, снизу в том же форм-факторе под ATmega2560 и справа - под SRAM в 512килобайт. Для платы памяти есть все на 5шт, для остальных нет камней. В подарок в коробке оказалась картонная ручка "for PCB" уж не знаю зачем.
Осталось заказать камни. Продаваны по ссылкам выше - ни один не ответил ни на один заданный вопрос. Только автоответчик "не беспокойтесь, нам для ответа требуется время" .. напрягает. Вроде бы есть желающий продаван поставить по 10шт с оптовой алибабы.. видимо буду заказывать у него.
Нашол у себя в загашнике 4 штуки w24512ak-15. Это CMOS Static RAM по 64 КБайта каждая. Никому не надо? Аддам за пузырь. Новосибирск
Сделай лучше на них Мегу с расширенной ОЗУ. Должно вполне просто получиться. Только регистр нужен шустрый типа 74HC573D у меня другие не завелись.
Не, мне они без надобности, сиравно скоро на кладбище переежять. Для моих поделок и 8кБ много.
Ладно, без пузыря аддам. :-)
Хе, хе, жаль что мне УЖЕ не надо. Пузыря не жалко.)
Вот в том-то и дело что УЖЕ не надо .. уже пришли 5шт на 512 килобайт и 10шт уже едут .. смысл в плате на 64кб? :)
Ну наконец-то. Вчера забрал с почты 10шт ATmega2560-16au. По распаковке слегка прифигел: судя по маркировке, камни произведены на 13-й неделе (с26 марта по 1 апреля) 2018 года. О как! Запаял основную часть платы, проверил БП: на выходе 4.95в без нагрузки и 4.93в с токоотдачей 5А .. очень нефиговые эти RT8289 оказались. (картинко - кликабельно)
Светодиоды формата SMD так и не закупил, запаял мелкие круглые по 3мм. Конденсаторов по 22пф тоже пока не нашлось в загашниках. Так обойдется.. Уже завтра запаяю SPI м попытаюсь прошить загрузчик. БП выглядит конечно брутальненько так. Диоды шоттки на 3А. Тем не менее под нагрузкой 5А вроде даже и не греются. :)
P.S. Пока паял, пришла идея положить периферийные разьемы "на бок", кроме разъема с памятью .. блин, где эта мысля была раньше? :)
Ура, ну не могу удержаться.. Вчерась (пока паял плату) дитенка самостоятельно (ну почти) впервые скомпилял себе на ноут под Убунтой драйвер Wi-Fi, который у него не встал при установке системы .. скоро, скоро будем принимать в свои ряды ещё одного "погромиста - сиониста". :)
И так, очередная крупная переработка (третья) arhat.h в связи с работой с расширенной памятью SRAM.
Имеем такую организацию адресов памяти:
1. 0x0000 .. 0x2200 (кажется так, не важно) - внутренняя SRAM меги 2560 (8200байт)
2. 0x2200 .. 0x7FFF -- общая страница внешнего SRAM (32кб), видна всегда.
3. 0x8000 .. 0xBFFF -- 15 сегментов SRAM по 16кб, переключаемые 1/2 (младшая тетрада) отдельного регистра блока расширенной памяти;
4. 0xC000 .. 0xFFFF -- 15 сегментов SRAM по 16кб, переключаемые второй половиной (старшая тетрада) того же внешнего регистра.
Запись во внешний регистр делается "ногодрыгом" по пинам WR & RD, с выставлением в порт А данных при отключенном интерфейсе xmem Меги.
п.1,п.2 - тривиальны, ибо обращение к этим участкам легко контролируется самим камнем автоматически и ничего специального разъяснять компилятору не требуется, просто надо указать "что теперь памяти больше", только и всего:
Вариант 1 (минималистический) описания платы: указываем в описании, что у этой платы оперативной памяти 64 килобайта, то есть разрешаем распределение всего адресного пространства и далее "ручками" контролируем, где что лежит .. но это не комильфо.
Вариант2 (это вот хочется запилить):
Было бы полезно объяснить компилятору и линковщику, что теперь у него есть доп. сегменты ОЗУ в виде .lpage0 .. .lpage14 и .upage0 .. .upage14, переменные которых надо размещать с соответствующих адресов п.3, 4. И далее в коде использовать атрибуты явного назначения сегмента ОЗУ переменным, размещаемым в заданном сегменте. Переключение сегментов в рантайм, скорее всего также придется контроллировать ручками .. но может есть какие-то идеи как это (процедуру смены контекста) всучить компилятору для автоконтроля?
В общем, вопросы на дкоторыми сейчас мучаюсь:
1. Можно ли завести свои сегменты памяти, аналогично .data, .bss и КАК это сделать?
2. Можно ли им назначить конкретные начальные адреса ОЗУ, в т.ч. и одинаковые? Например: .lpage0 = .lpage1. = .. = .lpage14 = 0x8000
3. Можно ли указать каким-то квалификатором что эту переменную (или блок таковых) надо размещать строго в этом сегменте ОЗУ?
4. Можно ли всучить компилятору процедуру переключения сегментов для автоконтроля при обращении к переменной?
Например что-то типа такого:
http://avr-libc.narod.ru/mem_sections.html
В этом случае, можно будет завести препроцессорные макросы типов, скрывающие атрибутирование сегмента ОЗУ, хотя бы типа так:
#define setVar(type, seg, name) \
type __atribute__(__segment__(seg)) name
Да, это про то самое, спасибо.
То есть можно указать "хде" сложить переменную через такое:
И в настройке платы указать какие секции с каких адресов начинаются. Это сильно упрощает вопрос.
Остается понять КАК дополнить типовые секции для avr новыми названиями .. своими. И можно ли всучить компилятору контроль за переключением секций ОЗУ.
Это я себе так делаю для 32к,
и компилятору говорю
128.menu.sram.32K.compiler.c.elf.extra_flags=-Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x8090ff
Э-э-э, а почему конец памяти на 0x90FF? У вас разве внутренняя SRAM не экранирует начало внешней?
Эта часть - понятна, делал аналогично, просто расширяя доступность адресов до 0xFFFF. Но вот теперь хочется допилить до нормальной работы со всеми 512к через компилятор.
В общем, нашел что можно накатать свой сценарий линковщику что и как расмещать в памяти и какой .. осталось найти доку на линковщик. :)
Ха дошло. Вы заэкранированный кусок в функции init() перегоняете на старшую часть адресов, управляя выводом PC7. Разумно. :)
Пока нашел это: http://www.opennet.ru/docs/RUS/gnu_ld/gnuld-3.html#ss3.1 может кому пригодится тоже. Не AVR, но все же..
P.S. В общем, похоже можно тупо воспользоваться наличием в типовом сценарии линкера описаний типа *(.data*) и *(.bss*) то есть вставить в секции .data и .bss после данных ещё и всякие разные секции .data* - то есть похоже достаточно описать так:
Использовать подсекции в .bss И указать опцией линковщика что секция .bss_lpage2 начинается с 0x8000 .. в общем что-то стало понятней, надо пгобовать.. :)
В общем все не так просто, ибо символ . может только возрастать в пределах секции. К счастью есть команда OVERLAY совмещающая несколько секций с одного адреса .. только вот есть ли она у линковщика avr-gcc?
В общем, пока понимаю так:
Получается что надо писать свой скрипт линкеру, где можно указать эту раскладку адресов выше на требуемые куски памяти, типа так:
MEMORY
{
INTERNAL_RAM [rwa!x] : ORIGIN = 0x800200, LENGTH = 8000;
COMMON_RAM [rwa!x] : ORIGIN = 0x802200, LENGTH = 32768-8200;
LOW_WINDOW [rw!x] : ORIGIN = 0x808000, LENGTH = 16K;
UPPER_WINDOW [rw!x] : ORIGIN = 0x80C000, LENGTH = 16K;
}
И далее добавлять алиасы на каждую физ.страничку, типа такого:
REGION_ALIAS(lowPage0, LOW_WINDOW);
REGION_ALIAS(lowPage1, LOW_WINDOW);
..
REGION_ALIAS(upperPage14, UPPER_WINDOW);
и уже потом определять секции типа так:
SECTIONS {
.lpage0 (NOLOAD) : { *(.lpage0) } > lowPage0
.lpage1 (NOLOAD) : { *(.lpage1) } > lowPage1
...
.upage14 (NOLOAD) : { *(.upage14) } > upperPage14
}
После этого уже можно будет привязывать данные к требуемым сегментам в программах через __atribute__((section(...))):
Н-да. Тут советов ждать не приходится, раз уж даже на киберфоруме, среди 42тыщ "программистов" не нашлось спецов по линкеру..
Кто-то может подсказать какой из скриптов линкера надо брать за основу для правки? Для avr6 (mega2560) в Ардуино ИДЕ имеется 6 разных вариантов, отличающихся расширением .. ни один из них не подключается в явном виде при сборке ..
Вау .. все получилось как надо!
Результат реассемблирования через objdump (весь файл!):
Ещё раз спасибки автору http://arduino.ru/forum/obshchii/nastroika-kompilyatsii-v-arduino-ide - оказалось очень полезно.
Добавил согласно прописанному вывод avr-objdump в компиляцю, дополнительно прописал отдельную плату в board.txt типа так:
заодно разобрался со структурой этого меню, так что можно позже дополнить настройкой для других камней, частот и плат расширения..
Дополнительно создан каталог с "платой" в variants/sram, что и указано в boards.txt и в него положен свой файл скрипта линкера avr6.x Пока только для линковки по умолчанию (остальные вроде для ИДЕ не нужны):
собственно и усё .. теперь можно обьявлять данные в нужных секциях и листать их вручную.
Да, ещё в boards.txt указано что "куча" malloc() тут начинается с точки расширения SRAM, "за" стеком, который как обычно устанавливается на конец встроенной SRAM через скрипт старта ctors и константу RAMEND, определенную в недрах io.h и файлах камней.. размер памяти под переменные ограничен 32кб по размеру секции .data. ну .. чтобы "жизнь медом не казалась" :)
Макросы для упрощения назначения страниц объектам данных:
Упс. Выкладываю исправленный avr6.x:
Предыдущий вариант оказалось что пришивает секции друг к другу, если они из одного окна. Этот ведет себя верно во всех случаях.
Остается только допереть КАК сделать возможность распихивания константно проинициализированных данных, аналогично секции .data
Сделал вот такой кусок в boards.txt:
Компилять из него ещё не пробовал, хочется понять будет такое внятно работать или нет?
Вопрос в части таких строк этого описания:
где значение "пересекается" для двух опций: типа процессора и типа загрузчика .. ничего не нашел внятного в описании правил построения этого файла.
В меню Ардуино ИДЕ 1.8.5 - вроде бы рисует все верно..
Фото последнего варианта платы (предыдущую - запортил безвозвратно):
Исследования стабилизатора в этой теме: http://arduino.ru/forum/apparatnye-voprosy/dc-dc-preobrazovatel-na-baze-...
Запаял плату памяти. При смывании канифоли ацетоно-спиртовым раствором с микросхемы памяти слезла черная краска, и теперь на ней двойная маркировка. Вторая часть частично читается как JAPAN HMM6... -7, потребление платы 9мА .. вроде бы в норме. Покупал как JAPAN HMM625128ALF-5 (HMM625128BLFP-7) так прямо и было как то или иное (как понял).
В принципе, если это таже памать но на 70нсек, то оно "сойдет" и даже без циклов задержки..
Получилось пока так:
Дорисовал раскладку ножек по контактам (pi mapping):
Сверху (слева на право): I2C, 3 свободных ноги, SPI - слева от питания и UART0, 1, 2 справа от питания.
Левый ряд (сверху вниз): Таймер 4, Таймер 5, Таймер 2, АЦП 8 -15.
Правый ряд (сверху вниз): Таймер 3, Таймер 1, Таймер 0, АЦП 0-7.
Снизу мелкий разъем - 7 PCINT прерываний 9-15 и там же UART3
Самый нижний разъем - интерфейс расширения SRAM вместе с выходами с регистра - защелки адреса (А0-А7).
P.S. Постепенно тестируем плату. Пока ногодрыги на всех ножках работают нормально, плата заливается без вопросов.
Плата МУРК-2560 - вроде бы полностью рабочая. Выкладываю SVG для самостоятельного изготовления ЛУТ-ом:
Слой F_CU (зеркально), Слой B_CU (обычно) и расположение деталек и надписей:
Можно копировать прямо отсюда и печатать. По запросу могу предостатвить gerber-файлы, все какие надо. Писать не сюда, а на мыло. Размер платы 56х72мм. Индуктивность L1 изготавливать потоньше (плоскую) и размещать поверх диода D1
По номиналам преобразователя на базе RT8289: входную и выходную емкость крайне желательно собирать из нескольких конденсаторов, можно обыкновенную керамику в т.ч. из разных номиналов, поскольку ESR зависит от частоты.
Конденсаторы обвязки микросхемы ATmega2560 тут тоже СБОРНЫЕ из 2-х: 100нф + 470нф (хорошо показали себя и 680нф). Стабильность питания много выше чем при одном конденсаторе в каждом узле обвязки. Рекомендую.
И ещё: Боковые питающие разъемы подключаются через "перемычки" к этому стабилизатору. Это на случай, если надо силовые элементы питать отдельно от интерфейсного разьема (верхнего), микроконтроллера и платы памяти.. (а вдруг).
В общем, в этой теме есть всё необходимое для домашнего повторения и настройки программирования расширенной памяти через компилятор.
Как только соберу этот бутерброд окончательно выложу итоговые фотки и на этом проект можно будет считать завершенным. :)
Итого получился вот такой вот "бутерброд", можно пилить экран с кнопками и втискивать в лего детальки:
Возможно стоит перепаять транзисторы КТ3102Б на какие-нить SMD варианты, а то торчат сильно.. :)
В общем, удалось ужаться в размер 56х88мм (7х11 лего дырок), на что сын сказал "здорово".
Вариант новой компоновки платы:
Похоже это окончательный вариант. Кликабельно. Все выводы сдвоены, внутренние для установки разъема "мама" вверх для подключения доп. плат (драйвера моторов, гироскопы, плата управления с дисплеем), а внешние - для установки строенных планок (подключение по типу серводвигателя) - "вбок", кроме правого (по рисунку) разъема под внешнюю память. Если "сервы вбок" не требуется, то тоже можно не устанавливать, но в таком разе будет маловато выводов по земле и питанию.
Оставшееся место заткнул регистром расширения адреса (по даташиту), можно не устанавливать или с ним, тупо втыкать микросхему памяти до 64кбайт, прямо в разьем расширения (все сигналы есть)
Разведу - отдам в jlpcb на изготовление..
Предварительно развел вот такой вариант платы 56х88мм (кликабельно):
Ярко выделена земля, которой уделил особое внимание: "чистая земля АЦП" разведена отдельно и подключается к общей земле в одном месте. Все проводки АЦП входов перемежаются этой чистой землей для снижения перекрестных помех. Также отдельно разведена чистая земля для кварца. Выводы резонатора по возможности сделаны одинаковой длины.
Разводка ещё без площадной заливки землей и питанием, без выдержки толщины силовых проводов. Это - "потом", после замечаний..
На пустое место платы воткнул расширительный регистр адреса, соответственно мелкие микросхемы памяти до 64 килобайт можно подключать "напрямую" к контактам расширительного разьема (правый).
Настоятельно прошу критики, ибо не спец. по разводке.
В общем, сделал так (кликабельно). Плата вместе с боковыми плашками расширительного шилда:
Вечером в пятницу отправил на jlpcb.com, уже в субботу получил положительный отклик "можем такое сделать (10шт за 2 бакса)", оплатил во второй половине дня субботы и уже сегодня получил трек доставки .. слов нет, какое "гадство". :)
P.S. Платы приехали аж 24.08. Почта играла в партизан, пока не пришло уведомление от JLCPCB.com на почту.. визуальный осмотр "5 баллов" (ничего крамольного не нашел). Платы забрал с почты только сегодня (28.08).
Плата из поста №80 в окончательном виде "Ардуино как Лего". Оси и подпорки - исключительно для фотки. Общий вес с аккумуляторным блоком 3S 18650 (150гр) составил 245гр. :)
Собрал в одном посту всё, что необходимо доработать и/или дополнить в Ардуино ИДЕ, чтобы удобно общаться с расширенной памятью:
1. Добавление objdump в список шаблонов исполнения в platform.txt, тут под Линукс, под винду есть ссыль выше:
1а) добавляем опции для objdump. Файл <каталог с ИДЕ>/hardware/arduino/avr/platform.txt, секция "AVR compile variables"
1б) К этому пункту не относится, но там же сразу в этой же секции убираем включение -flto "по умолчанию (оригинальные строки под комментом):
1в) В этом же файле, далее в секции AVR compile patterns добавляем хук вызова objdump после Create output files (.eep and .hex):
1г) В папке <каталог с ИДЕ>/hardware/tool/avr/bin создаем файл с названием avr-objdump.sh и содержимым:
.. и если надо, меняем ему владельца и даем права на исполнение, типовыми chown,chmod ..
Собственно тут всё - после компиляции/заливки в папке /tmp/build_xxx/ можно будет обнаружить файлик с расширением *.lst - он и есть листинг всей сборки.
2. Доработка настроек ИДЕ для работы с этой и др. платами с расширенной памятью.
2а) если был пропущен п.1б, то тут ему самое место..
2б) В начало файла <каталог с ИДЕ>/hardware/arduino/avr/boards.txt , после menu.cpu=Processor вставляем это:
2в) в папке <каталог с ИДЕ>/hardware/arduino/avr/variants создаем папку sram и копируем в ней файлик <каталог с ИДЕ>/hardware/arduino/avr/variants/mega/pins_arduino.h
2г) создаем там же файлик <каталог с ИДЕ>/hardware/arduino/avr/variants/sram/avr6.x с таким вот содержимым:
.. собственно, если ничего не забыл, то это всё. В ИДЕ появится опция выбора "*** ATmega640/1280/1281/2560/2561 ***" с расширенным меню и возможностью управлять выбором процессора из семейства, частотой камня, уровнем Bodlevel, включением опции LTO и типом платы: с памятью, без памяти и какой памятью..
Ну и под занавес, тестовый пример:
P.S.
Конкретно эта плата (на фото выше) расширенной памяти у меня пока так и не заработала, валит ошибки, как и самая первая больше года назад .. буду смотреть под осцилом "что не так"..
@TODO:
1. Требуется проверка правильности boards.txt в части фьюзов остальных процессоров семейства .. у меня в наличии есть только 2560. Делал, списывая с проекта MegaCore, но не уверен что все разобрал верно..
2. Файл линковщика avr6.x содержит прямое указание на размер внутренней SRAM меги. Для использования с младшими моделами, и для расширения работы с серией ATmega128a, имеет смысл доработать boards.txt и avr6.x так, чтобы символ __heap_size определялся по типу процессора (сколько там у него есть своей SRAM) И типу платы (есть ли расширенная память и сколько её) ..
с наскока это сделать не удалось, мне остальные камни как-то без надобности, но для полноты картины - было бы неплохо.
P.S.
Точно также можно переработать boards.txt для разных плат под 328p и прочие камни семейства, заодно расширив возможности по управлению прошивкой и заливкой ..
На плате расширения памяти обнаружено большое количество непропаев .. руки из *опы..
Но! Что интересно: на базе макросов из примера легко создается тестовый скетч для .. ручного управления памятью чуть ли не в "секундных" таймингах и .. осцилограф можно заменить .. набором светодиодов (что и сделал). :)
Фото платы из поста №83:
По бокам платы и заодно с ней изготовлены боковые планки для расширительных разьемов (пайка к внешнему ряду сигнальных разьемов на плате). Внутренний ряд - под типовые штырьковые разьёмы "мама" для соединения с платами "вверх" или удлиненные "вврех/вниз". Кому что интересно - то и запаиваем. :)
Все надписи на плате - читаемы, все номиналы - подписаны. :)
Прозвонил, пропаял плату из поста №84 .. в итоге, 4-й канал мультиплескора оказался "битый". Не держит на своем выходе "1" когда отключен (сигнал по схеме page1). Засада, похоже придется выпаивать.
В общем, закрываю эту тему на грустной ноте: окончательно тест памяти не прошел, хотя и поменял мультиплексор и в ручном режиме он проходит успешно.
Код тестовых примеров (их тут несколько):
emsSetup() - настройка пинов для ручного управления в тестах manualTest() и manualTest2(). Они оба проходят нормально, второй тест в память пишет и читает как надо (память - статическая, отрабатывает по 0.5сек вполне нормально), проверялось подключением кучки светодиодов к шине расширения памяти (как на панельках старых ЭВМ).
В simpleTest() закомментированный цикл - заменяет чтение/запись на внутр. SRAM - выдает "ok". А вот замена адресов цикла на внешнюю SRAM - выдает все 10 ошибок чтения/записи с любой задержкой 0..3.
Результат компиляции - смотрел: хоть и коряво, но всё вполне верно. Настройка boards.txt работает нормально, все адреса транслируются куда надо..
Похоже дальше надо смотреть на осцилографе и желательно многолучевом .. у меня такого нет, да и убрано в общем-то всё уже далеко.
А если FRAM приделать?
http://www.gaw.ru/html.cgi/txt/ic/Ramtron/Parallel/FM1808.htm
https://ru.aliexpress.com/item/10-FM1808-70-S-FM1808-70-FM1808-1808-SOP2...
Таким способом можно подключать любую параллельную память для работы как с внутренней SRAM через простые объявления данных (массивов, переменных и т.д.). Проблема у меня в том, что не смог пройти даже simpleTest(), хотя "вручную" в память пишется и читается как надо, по крайней мере по 8-и "избранным" адресам.
P.S. посмотрел, конкретно для этой даже особо схему не потребуется перерисовывать. Так, перенести пару ножек из-за разницы корпусов SOP-32 и SOP-28 ..
Кстати, цикл доступа к внешней SRAM у меги 2560 занимает 3 цикла шины, что снижает требования к чипам до 62.5 * 3 = 180нс. Так что имеющиеся 70нсек - что называется "за глаза" и даже без циколов ожидания.
В общем, так и осталось непонятным почему в ручном режиме память пишется и читается (по 8-и избранным адресам "как получилось"), а в нормальном режиме из неё читается .. младший байт адреса. Как буд-то как ALE защелкнул на шине адрес, так он на ней и подвис..
Достал плату, потестил ещё разок, проверил все тайминги. В общем, схему надо откорректировать:
1. Сигнал ALE подтягивать резистором к питанию - вредно. Резистор лучше убрать;
2. Сигнал CS микросхемы памяти надо посадить на землю, подавать на него сигнал ALE через диод - вредно, резко удлиняется цикл чтения и память перестает работать даже 3-я циклами ожидания.
С такими изменениями - память работает и даже без циклов ожидания, по крайней мере первые 32к (которые в общем-то почти последние в самой памяти), контроллер страниц ещё досконально не проверял, но там "дурить" особо нечему.
Только программно надо помнить о том, что при записи номера страниц в регистр памяти сигнал WR надо теперь устанавливать первым, чтобы перевести ноги данных в третье состояние раньше, чем на шину выйдут номера страниц из микроконтроллера. Когда оба сигнала WR,RD в нуле - микросхема памяти переводит свою шину также в третье состояние, что и решает проблему отказа от соединения CS и ALE.
В общем, на этом всё. Схема - вполне рабочая, ПО - тоже. Заливал в т.ч. и с настрокой на внутренний генератор 8Мгц - работает.
P.S. Дотестил диспетчер страниц памяти. На схему надо добавить резистор с базы на землю у Q1 не более 4.7к-9.1к, иначе он не закрывается полноценно. Резистор R14 2.4к стоит уменьшить до 1к. В таком варианте, пауза для записи номера страницы получается около 45-90 тактов ЦПУ на 16Мгц. Чем меньше резистор с базы на землю - тем быстрее запирается этот транзистор. В целом некритично, поскольку это механизм переключения страниц. В остальном - плата полностью рабочая.
Вот теперь - точно всё. :)
Блин, не всё. :)
В общем, ногой CS микросхемы памяти, таки надо управлять. Дело в том, что при записи номеров страниц в регистр страниц тут использован факт одновременно низкого уровня на выходах WR и RD, а поскольку на шине адреса SRAM все равно "какой-то" адрес получается выставлен (защелка младших адресов - что-то помнит с предыдущего обращения, а старшая часть адреса порт "С" - тоже или в нулях или в высоком состоянии), то снятие сигналов WR=RD=1 .. приводит к записи номера страницы кроме регистра ещё в какую-то ячейку памяти. Фигня.
В общем, не придумал способа КАК из сигналов шины (и только) реализовать управление ногой CS, поэтому вывел её на отдельную ножку МК. Отключаем доступ к памяти CS=1 для перезаписи номеров страниц и включаем CS=0 в макросе emsOn(). Теперь дополнительная нога микроконтроллера управляет сигналом CS через вход "EN", к которому она подсоединяется внешним проводом, соответственно можно выбрать любую. У меня сейчас это PCINT15 (см. пост№77)
а глупый вопрос - можно приобрести платы или попросить чертежы плат в спринтлауте или аналогичном для повторения....Заранее спасибо.
Можно. Пишщите на почту arhat109 собака на мейл который ru, впрочем тут мой ящик тиражирован многажды. :)
P.S. Готовых плат сейчас уже нет. Сами платы травятся, наносятся все обозначения в Китае (тут есть где). Для повтора есть только вариант в линуксо-праведном Kicad..
Для платы из поста №88 сейчас из Китая едут платы ОЗУ на 512кб + разьем для SD-карт по SPI шине. Жду, новую версию для пайки "последнего релиза".. :)
а мочему нельзя было сделать все как у платы ардуино 2560 r3 ?
У меня сохранились эти ссылки:
http://arduino.ru/forum/otvlechennye-temy/vopros-dlya-obsuzhdeniya - тут наверное самый полный ответ на Ваш вопрос.
http://arduino.ru/forum/obshchii/atmega-128a-au-vneshnyaya-sram-62256-74... - ещё одна успешная попытка для 64кб и Атмега128а
arduino.ru/forum/proekty/lego-kirpich-iz-mega2560 - ещё одна тема Архата, есть про многое в т.ч. и меги с памятью
arduino.ru/forum/proekty/nedovolnym-super-dunya-est-zhelayushchie-prinyat-uchastie поиском (верхний правый угол) нашлась ещё эта тема..
да ну нах... можно было просто увеличить с краев, добавив ряды контактов и плату оставить формата ардуино мега, дичь полная получилась - никому, кроме 10ти человек, ненужная. Вообще дичь, с учетом того что плата и схема ардуины меги есть всети к тому же там еще куча места пустого. Меня надо было српосить прежде чем делать.