AVR. Учебный Курс. Виртуальные порты
Автор DI HALT
Опубликовано 11 Окт 2008
Рубрики: AVR. Учебный курс
Метки: Assembler, AVR, Порт, Программирование, Трюки
Глядя на то, как раскиданы порой ножки портов по корпусу контроллеров, у меня возникают большое подозрение, что разводчик кристалла дунул что то сильно забористое. Когда вперемешку идут выводы разных портов, да еще почти в рандомном порядке… Когда к этим портам вешаем что-либо разнобойное, то пофигу. А если надо подцепиться на прямую линейку шины данных, вроде того же LCD дисплея? Вот тут и начинается круголяние дорожек по плате. А если плата фаршированная донельзя? Тут приходится вводить перемычки, дополнительные слои и извращаться как только можно. Короче, та еще проблема.
Я решаю ее виртуальными портами. То есть завожу в системе переменную каждому биту которой привязываю какую-либо ножку. Поскольку все выводы независимы я могу компоновать виртуальный порт в любом порядке и из любых линий. Чертовски удобно. Впрочем, за удобство приходится платить - обработка такого порта требует несколько десятков команд. Но когда кристалл не забит под завязку, то можно позволить себе немного пошиковать во имя удобства.
Итак. Подключаем куда-нибудь в область процедур блок кода виртуального порта.
1 | .include "VPort.asm" |
Теперь надо сконфигурировать виртуальный порт. Все делается в define записи в файле VPort.asm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | ;==================================== ; Virtual Port ==== Virtual Port ;==================================== .def VP_REG = R16 .equ VPort0 = PORTD .equ VPort1 = PORTD .equ VPort2 = PORTD .equ VPort3 = PORTD .equ VPort4 = PORTD .equ VPort5 = PORTC .equ VPort6 = PORTC .equ VPort7 = PORTC .equ VDDR0 = DDRD .equ VDDR1 = DDRD .equ VDDR2 = DDRD .equ VDDR3 = DDRD .equ VDDR4 = DDRD .equ VDDR5 = DDRC .equ VDDR6 = DDRC .equ VDDR7 = DDRC .equ VPIN0 = PIND .equ VPIN1 = PIND .equ VPIN2 = PIND .equ VPIN3 = PIND .equ VPIN4 = PIND .equ VPIN5 = PINC .equ VPIN6 = PINC .equ VPIN7 = PINC .equ VP0 = 3 .equ VP1 = 4 .equ VP2 = 5 .equ VP3 = 6 .equ VP4 = 7 .equ VP5 = 0 .equ VP6 = 1 .equ VP7 = 2 |
Параметр VP_REG - указывает какой регистр мы будем юзать для обращения с портом. Данный регистр юзается только для записи и чтения из порта. Данные в нем не хранятся. Но можно сделать три регистра для DDR, PORT и PIN. Просто мне как то западло это было делать.
Также можно, по принципу процедуры VRPIN считывать по мере надобности данные и из PORT с DDR, но это потребует дофига памяти.
К парметрам VPORTx, VDDRx и VPINx привязываем конкретные регистры портов, а к параметру VP номер пина. В скопированном сюда куске кода у меня виртуальный порт состоит наполовину из порта С на половину из порта D.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | VRPort: SBRS VP_REG,0 CBI VPORT0,VP0 SBRC VP_REG,0 SBI VPORT0,VP0 . . . SBRS VP_REG,7 CBI VPORT7,VP7 SBRC VP_REG,7 SBI VPORT7,VP7 RET VRDDR: SBRS VP_REG,0 CBI VDDR0,VP0 SBRC VP_REG,0 SBI VDDR0,VP0 . . . SBRS VP_REG,7 CBI VDDR7,VP7 SBRC VP_REG,7 SBI VDDR7,VP7 RET VRPIN: CLR VP_REG SBIC VPIN0,VP0 SBR VP_REG,1 . . . SBIC VPIN7,VP7 SBR VP_REG,128 RET |
Эти три процедуры выполняют запись в реальные DDR, PORT и чтение из PIN порта. Выдавая все это дело в регистр виртуального порта.
При записи используется двойная проверка - вначале проверяем есть ли бит - если есть, то выставляем. Потом проверяем на отстствие бита - если нет, то ставим. Данный метод позволяет менять состояние отдельных битов, не трогая состояние соседней и не дергая состояние, если оно не изменилось. Для PORT и DDR это критично, так как появление там не того уровня черевато выгоранием порта или непредсказуемыми последствиями для периферии. А вот с чтением PIN все куда проще. Вначале мы сбрасываем выходной регистр виртуального порта, а потом выставляем биты, в соответствии с наличии битов в реальных PIN ячейках. Команда SBR выставляет указаные биты. Фактически она аналогична команде ORI. Нафига была делать две разные команды одной длины, делающие одно и то же, за то же число тактов, я так и не догнал. ИМХО это один из AVR маразмов. Кои тут встречаются порой.
Использование
Загрузка данных в виртуальный PORT или DDR:
1 2 3 4 5 | LDI VP_REG,0xFF RCALL VRPort LDI VP_REG,0x00 RCALL VRDDR |
А чтение осуществляется одной командой:
1 | RCALL VRPIN |
После чего данные достаются из регистра VR_REG
При реальном использовании процедуры эти желательно кромсать и обрезать. Например, срезая неиспользованые биты порта. Если дрыгание PORT и DDR не критично к импульсам смены состояния (скажем у клавиатуры). То можно сделать запись в VRDDR и VRPORT с предварительным обнулением, а потом с заносом единиц. Как это сделано в VRPIN.
Библиотека виртуального порта Vport.asm
Вот такая вот загогулина. Пользуюсь довольно давно. Всем рекомендую - удобно.
Комментарии
36 комментариев на «AVR. Учебный Курс. Виртуальные порты»
Оставьте свой отзыв
Вы должны войти, чтобы оставлять комментарии.





а если я портА использую как ацп (только адц0, например) - можно ли другие ножки на вход-выход переназначить?
По идее должно быть можно. Т.к. ацп работает подобно регистру PIN снимая данные с ножки. Просто не обрабатываешь их и все.
Можно, но падает точность оцифровки. Подробно о работе с АЦП читай в даташите на кристал. Там достаточно подробно расписано =)
Чето я не находил там ничего про точность оцифровки в зависимости от другого юзания порта.
Ну хз.. Может не в даташитах, а в апнотах.
А команды SBR и ORI это случаем не разное название одной и той же машинной инструкции? А то это практикуется иногда - для удобочитаемости.
Я тоже так думал. Сравнил коды - разные команды… Хотя нет. Я ошибся когда сравнивал. Да, Это одна и та же команда.
Разрешите задать вопрос не в тему :) Может ли сгореть один порт на контроллере? То есть при DDRA=0xFF и PORTA=0xFF получаю невнятные ~2V. То же самое на C даёт всё как надо.
Может. У меня валяется тинька у которой три ноги выгорели.
Разработчики чипов делают все правильно.
Все IO делятся на группы, которые питаются от своих power domains. Никогда не задумывался почему есть куча VDD/VSS?
Так вот, есть такое понятие как Spurious switching activity (SSA). Есть предельное значение, зависящее от количества пинов, педов питания и т.д. Чтобы не позволить всей группе сразу изменить значения их разводят на разные домейны.
А какая связь между питанием и логической организацией портов? Ведь по сути дела на этапе проектирования любой порт можно развести на его питание как угодно, а потом, при микропрограммировании УУ, обозвать так как нужно. Например по порядку. Ведь в тех же большеногих МК порты идут последовательно. А вот в каких нибудь мелких Тини у которых и питание то одно, разбросаны черти как.
Нет - питание идет на группу близлежащих пинов.
VSS1 pin11 pin12 pin13 pin14 VDD1 VSS2 pin21 pin22 pin23 pin24 VDD2
Это понятно. Для больших кристаллов с кучей питания и земли это естественно. Но там то мне и придраться не к чему. Глядя на ту же мегу128 или даже 8535 - все пины идут по порядку. А вот перед глазами распиновка Тини2313 - питание у ней одно (20,10) а пины портов все в перемешку. A2,D0,D1,A1,A0,D2,D3 Смысл?
Опять же restriction на одновременное изменение соседних пинов. Это особенно важно для чипов с маленьким количеством питания и земли.
И еще есть наводки и т.д.
Ну если только из этих соображений. Хотя с другой стороны корпуса порт В в полном составе :)
И где вы такую траву берете?… Дополнительные выволы питания и земли у контроллеров малой и средней сложности делают для питания АНАЛОГОВЫХ цепей, чтобы снизить влияние шума цифровой части на точность АЦП. С той же целью для снижения цифрового шума у контроллеров с АЦП есть возможность запускать преобразование в “спящем” режиме, приостанавливая в это время работу большей части цифровой начинки. А раскидывать пины портов по разным группам питания… Это вам не Пентиум. Просто в даташите есть ограничение: суммарный ток ВСЕХ портов в ОБЩЕМ проводе не должен превышать определенной величины (обычно порядка 150 - 200мА в зависимости от типа контроллера). Потому что, например, при допустимом токе через каждую линию порта для PICов = +_25ма, для 40выводного корпуса с его 32линиями портов и их одновременной максимальной нагрузке суммарный ток мог бы составить 0,8A, что конечно, многовато для внутренних соединений не силовой микросхемы.
А чередование выводов разных портов чаще связано с возможностью дальнейшего развития уже существующих устройств путем использования корпуса с большим числом выводов, но чтобы при этом не менять расположение и разводку уже существующих цепей. Гляньте, например, на подключение разных микросхем к программатору.
Например, у PICов можно в длинной панельке программировать много более коротких микросхем, просто сдвигая их определенным образом.
Не он верно глаголит. Посмотри на АТМЕГА 8535 или на многие плисины. Там куча входов питания. У той же меги8535 3 входа цифрового питания, 1 вход аналогового и 4ре земли. У Меги 128 вроде бы еще больше. Слышал про случаи выгорания микрух если не соединить все земли и питаловы - внутренние какие то цепи перегреваются и дохнут.
Вот, смотрю ПДФ на ATmega8535. DIP 40:
10 -Vcc, 11 - Gnd - для цифровой части.
30 - AVcc, 31 - Gnd - для аналоговой части.
У TQPF, PLCC - действительно, добавлено по паре земель и питаний, так у них 4 ноги лишних, надо было чем - то занять, а кроме того, - корпус мелкий, широкие шины земли и питания не подведещь, у тонких - резко растет индуктивность, вот и распараллелили. Как видим, для DIP 40 этого не требовалось.
ATmega128: 64 ноги, кроме аналоговой земли и питания - всего по 2 линии цифровых земель и питания. Не так уж и много.
все очень просто - мы разрабатываем контроллеры.
Тесей?
что это такое?
Микроконтроллер такой. В Зеленограде вроде бы его разрабатывают.
Ангстрем делает Сам не щупал, но беглым взглядом оглядел - прикольный.
Нет - мы для себя в основном делаем (Sandisk).
А готовый купить что, - дороже? Или негде? Стоит ли изобретать велосипед для мелкосерийного применения? Это ведь жутко дорого в пересчете на изделие. Правда, если это связано с безопасностью особо важных систем, то конечно…
1. Посчитай выгоду: 5 миллионов чипов с экономией хотя бы по пол-доллара на каждом.
2. Нет готовых. У нас решениия построены на hardware acceleration. Иначе очень медленно.
Все ж затраты на разработку и запуск в производство заказных микросхем довольно велики… Трудно конкурировать с массовыми изделиями, у которых это уже окупилось.
Аппаратные специализированные ускорители - это конечно хорошо и эффективно, да вот только само их проектирование и оптимизация топологии - задачка тоже не из простых… В общем, я вам не завидую. Хотя, бывало, лет 20-30 назад и сам “лез на танки”… А теперь слишком ленивым стал. Устал, наверное. Или стимула нет…
Как раз наоборот. Это все очень интересно.
А насчет “не просто” - так это сначала. А потом уже все отработано.
трудно сказать без знания внутреннего строения чипа.
Иногда еще делают виртуальные группы питания. Может это и есть тот случай.
Артём , давай разбирать I2C или SPI интерфейсы на АСМе , а то у мя всё встала разработка , никак не могу разобраться , микроэлектроника привязала каждые библиотеки к микросхемам а открытого кода не дала на паскале , (ГА_Д_Ы) , щас висит у мя всё, Тера не отвечает на мои вопросы , судя по всему так оно и есть я был прав что I2C висит в паскале 8,0,0,1.
Посмотри после компиляции листинг в Асме, там все расписано.
Кроме View Assembly, глянь еще View Statistics, там тоже подробно все расписано. (на вкладке POM также есть ассемблерный листинг). Попробуй прогнать HEX код на симуляторе PIC Simulator IDE, там все прекрасно видно в процессе прогона, и регистры, и сигналы на ногах, и дизассемблер встроенный есть. На микросхемах памяти с посл. шиной обычно есть ноги для задания адреса микросхемы. Обычно, если она одна, их садят на землю, что соответствует 0 адресу.
Гляньте еще вот там:
может что пригодится. Можно также взять описание какой - нибудь готовой разработки с энергонезависимой памятью, и посмотреть, как там сделано.
Теперь, кроме разводчиков кристалла, дунули, что то сильно забористое и разработчики. Вы будете смеяться, в новых микроконтроллерах Xmega реализованы виртуальные порты. Может быть они читали эту страничку, но так нифига и не поняли? :)
Виртуальный порт в их понимании, совсем не то же самое что и в этой статье :) к сожалению конечно.
Виртуальные порты (регистры) расположены в адресном пространстве I/O памяти, таким образом, для работы с виртуальными портами можно использовать специфические команды этого адресного пространства, недоступные для расширенной области I/O. С помощью регистров управления, определяют на какой именно реальный порт будет отображаться виртуальный. Вот такие нововведения. Команды, как мы знаем, занимают один такт, а вот чтобы результат оказался на ножке контроллера потребуется все же два такта, но в это время процессор уже забудет о былой команде, и будет делать свои дела. Таким образом, с помощью виртуального порта мы выполняем за один такт то, на что при прямой адресации ушло бы 3 такта. При достаточно большом количестве операций с отдельными линиями порта - выгода на лицо.
Любопытно. А это же удобно! МОжно теперь биты проверять прям в кишках не доставая их в регистры.
Такой вопрос. Есть устройство, там применяется регистр 1533ир37. Плата готова, комплектуха куплена, и вот мне захотелось немного изменить логику работы. Для этого моя 1533ир37 должна при подаче на вывод 1 не переводить все выходы в Z состояние, а вывести нули на всех выходах. чтоб все не переделывать, решил из тини2313 сделать 1533ир37. Но в тини нога номер 1 - нога сброса. Можно ли, и как использовать ногу сброса, как обычный порт.
О, месье знает толк в извращениях!
Да, можно и так. Но при этом ты тиньку должен прошить раз и навсегда. Т.к. отрубив ресет ты теряешь возможность ее перешить. Вообще. И никто не спасет тебя, даже высоковольтный программатор (судя по эррате в даташите есть баг который не дает сбросить этот фуз)
ну почему же извращение. Неужели проще и дешевле менять схему, делать новую плату, отлавливать новые баги….. А так цена вопроса - 1,5уе на тини и 5 мин на написание кода для профессионала.(алгоритм то - простейший, при логической 1 на ноге 11, запомнить и вывести на ноги 12-19 то что было на ногах 2-9. При логической 1 на ноге 1, вывести на ноги 12-19 - логический ноль). У тини 2313 ноги 12-19 как раз целый порт С. А вот ноги 2-9 сборка из порта А и D. Вот как мне подсказали собрать виртуальный порт (были и покрасивее варианты, но после компиляции этот оказался самым коротким)
x=0;
if (PIND & _BV(0)) x=_BV(7);
if (PIND & _BV(1)) x|=_BV(6);
if (PINA & _BV(1)) x|=_BV(5);
if (PINA & _BV(0)) x|=_BV(4);
if (PIND & _BV(2)) x|=_BV(3);
if (PIND & _BV(3)) x|=_BV(2);
if (PIND & _BV(4)) x|=_BV(1);
if (PIND & _BV(5)) x|=_BV(0);
PORTС = x;
Работает!
Теперь нужно сделать так, чтоб при нуле на выводе 11 порт С не менял своего состояния. а при лог 1 на выводе1(RESET) весь порт С был равен 0.
К сожалению мой уровень не позволяет решить даже такую задачу(мой единственный самостоятельный проект - гирлянда 12 каналов по 4 проводам(как же я был рад когда заработало)) Тут же я не могу понять как опрашивать какой то конкретный пин. Все, так называемые “обучалки с нуля” подразумевают что такие элементарные вещи человек уже знает. А я вот не знаю. Мне нужна обучалка не с 0, а с -1 наверное ))) .
Идем дальше. Так в какой регистр нужно чего записать чтоб вывод RESET стал обычным выводом???
А то что тини больше не прошьешь - не беда. Я сейчас строю уличные часы-термометр(на 1000 светодиодах!). Врятли их кто то будет кромсать в ближайшие годы.
Извините за много букв.
Опрашивать какой то конкретный пин - как ты и сделал. Т.е. читаем весь порт, потом снимаем с него маску. Компилятор сам подставит туда SBIC/SBIS если будет такая возможность.
Обращайся к нему как обычно, на тиньке он ПА2 . А потом, Когда все уже будет проверено и отлажено. После последней прошивки выстави фуз бит RSTDSBL и все - ресет будет как вывод.