AVR. Учебный Курс. Виртуальные порты
Глядя на то, как раскиданы порой ножки портов по корпусу контроллеров, у меня возникают большое подозрение, что разводчик кристалла дунул что то сильно забористое. Когда вперемешку идут выводы разных портов, да еще почти в рандомном порядке… Когда к этим портам вешаем что-либо разнобойное, то пофигу. А если надо подцепиться на прямую линейку шины данных, вроде того же LCD дисплея? Вот тут и начинается круголяние дорожек по плате. А если плата фаршированная донельзя? Тут приходится вводить перемычки, дополнительные слои и извращаться как только можно. Короче, та еще проблема.
Я решаю ее виртуальными портами . То есть завожу в системе переменную каждому биту которой привязываю какую-либо ножку. Поскольку все выводы независимы я могу компоновать виртуальный порт в любом порядке и из любых линий. Чертовски удобно. Впрочем, за удобство приходится платить — обработка такого порта требует несколько десятков команд. Но когда кристалл не забит под завязку, то можно позволить себе немного пошиковать во имя удобства.
Итак. Подключаем куда-нибудь в область процедур блок кода виртуального порта.
Теперь надо сконфигурировать виртуальный порт. Все делается в define записи в файле VPort.asm
;==================================== ; 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.
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:
LDI VP_REG,0xFF RCALL VRPort LDI VP_REG,0x00 RCALL VRDDR
А чтение осуществляется одной командой:
После чего данные достаются из регистра VR_REG
При реальном использовании процедуры эти желательно кромсать и обрезать. Например, срезая неиспользованые биты порта. Если дрыгание PORT и DDR не критично к импульсам смены состояния (скажем у клавиатуры). То можно сделать запись в VRDDR и VRPORT с предварительным обнулением, а потом с заносом единиц. Как это сделано в VRPIN.
Вот такая вот загогулина. Пользуюсь довольно давно. Всем рекомендую — удобно.
Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!
А я встрял на три года, как минимум, ежемесячной пахоты над статьями :)))))))))))) Спасибо вам за такой мощный пинок.
43 thoughts on “AVR. Учебный Курс. Виртуальные порты”
а если я портА использую как ацп (только адц0, например) — можно ли другие ножки на вход-выход переназначить?
По идее должно быть можно. Т.к. ацп работает подобно регистру PIN снимая данные с ножки. Просто не обрабатываешь их и все.
Можно, но падает точность оцифровки. Подробно о работе с АЦП читай в даташите на кристал. Там достаточно подробно расписано =)
Чето я не находил там ничего про точность оцифровки в зависимости от другого юзания порта.
Ну хз.. Может не в даташитах, а в апнотах.
В даташитах. Раздел АЦП, подраздел повышения точности.
А команды SBR и ORI это случаем не разное название одной и той же машинной инструкции? А то это практикуется иногда — для удобочитаемости.
Я тоже так думал. Сравнил коды — разные команды… Хотя нет. Я ошибся когда сравнивал. Да, Это одна и та же команда.
Разрешите задать вопрос не в тему :) Может ли сгореть один порт на контроллере? То есть при DDRA=0xFF и PORTA=0xFF получаю невнятные
2V. То же самое на C даёт всё как надо.
Может. У меня валяется тинька у которой три ноги выгорели.
А что за камень? Вдруг этот порт запитан от AVCC?
Разработчики чипов делают все правильно. Все 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 линии цифровых земель и питания. Не так уж и много.