AVR. Учебный курс. Простейшая программа.Print This Post

Автор DI HALT
Опубликовано 06 июля 2008 
Рубрики: AVR. Учебный курс
Метки: , ,

Итак, прежде чем что то делать надо понять как вообще выполняется программа в контроллере, как работает ядро процессора. Для этого нам хватит AVR Studio и его эмулятора. Не очень интересно, может даже занудно, но если этот шаг пропустить, то дальнейшие действия будут как бег в темноте.

Поскольку в демоплате Pinboard используется процессор ATmega16, то рассматривать мы будем именно его. Впрочем, как я уже говорил, для других контроллеров AVR это также будет справедливо. Отличия, конечно, есть, но они не существенные.

Запускаем AVR Studio (далее просто студия) и в выскочившем мастере сразу же создаем проект:

Откроется окно:

Увеличить

Оно может выглядеть чуток не так. Дело в том, что интерфейс студии очень легко конфигурируется и перекраивается под себя. Так что можно перетащить панели как нам удобно. Что я и сделал.

Систему команд хорошо бы распечатать себе на листочке. Их там всего около 130, кратким списком (тип команды, что делает и какие операнды) занимает пару листов формата А4. Учить не надо, прочитать раз на десять, чтобы помнить знать что у нас есть. Даже я периодически подглядываю в систему команд, хотя пишу на ассемблере уже много лет.
Я же команды которые буду использовать буду описывать по мере появления.

В центральном окне пишем код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
		.include "m16def.inc"   ; Используем ATMega16
;= Start macro.inc ========================================
 
; Тут будут наши макросы, потом. 
 
;= End macro.inc  ========================================
 
 
; RAM =====================================================
		.DSEG			; Сегмент ОЗУ
 
 
; FLASH ===================================================
		.CSEG			; Кодовый сегмент
 
 
; EEPROM ==================================================
		.ESEG			; Сегмент EEPROM

Это вроде шаблона для начала любого проекта. Правда кода тут 0 байт :) Только директивы.
Обрати также внимание на оформление кода. Дело в том, что в ассемблере нет никаких структурных
элементов — весь код плоский, тупо в столбик. Так что поэтому чтобы он был читабелен его надо в обязательно порядке разбивать на блоки, активно пользоваться табуляцией и расставлять комментарии. Иначе уже через пару дней ты не сможешь понять, что вообще написал.

В коде будут активно использоваться макросы. Поначалу они будут писаться в основном файле, в секции macro.inc, но потом я их вынесу в отдельный файл, чтобы не мешались. Так удобней.

Как я уже говорил, в микроконтроллере куча раздельных видов памяти. И тут используется гарвардская архитектура. Т.е. исполняемый код в одном месте, а переменные в другом. Причем это разные адресные пространства. Т.е. адрес 0000 в сегменте данных это не то же что и адрес 0000 в сегменте кода. Это дает большое преимущество в том, что данные не могут испортить код, но не дает писать полиморфные программы. Такая архитектура является традиционной для микроконтроллеров.

У обычного PC компа используется другая архитектура - Фон Неймановская. Там данные и код находятся в одном адресном пространстве. Т.е., скажем, с адреса 0000 по 0100 идет код, а с 100 до FFFF данные.

В нашей же программе и код и данные располагаются на одном листе, но чтобы компилятор понял где у нас что они выделяются директивами сегментации. Пока нас интересуют директива .CSEG после нее начинается исполняемый код. Там и будем писать нашу программу.

Возьмем и напишем:

1
2
3
4
5
; FLASH ===================================================
		.CSEG		; Кодовый сегмент
		NOP
		NOP
		NOP

Что мы сделали? А ничего! Команда NOP это команда затычка. Она не делает ничего, просто занимает 2 байта и 1 такт.

Речь не о команде NOP (там и обсуждать то нечего), а о том как оно все будет выполняться.

Запускай симуляцию (Ctrl+F7) когда пробежит прогресс бар компиляции/симуляции и возле первого NOP возникнет стрелочка нажми ALT+O — выскочит диалог настройки симуляции. Там тебе надо там только выставить частоту 8Мгц. Почему 8Мгц? Просто на Pinboard частота главного проца по дефолту 8Мгц. Если у тебя свои идеи на этот счет — можешь поправить как угодно. На симуляцию это не влияет.

Вернемся к нашей симуляции. Давай посмотрим как выглядит наша программа с точки зрения машинных кодов, как размещается в памяти. Интерес, по большей части, чисто теоретический, редко когда пригождается и по этому во многих учебных курсах по AVR на это даже внимание не заостряют. А зря! Т.к. упускается глубинное ощущение кода. Открой окно просмотра программ. View - Memory или Alt+4.

Там выбери тип памяти Programm. Это и есть наш Flash сегмент. Выставим в списке cols две колонки, чтобы было наглядней.

Это наша память программ. На голубом фоне идут адреса, они, как видишь, в словах. Т.е. двум байтам соответствует один адрес. Но, на самом деле это интерпретация адресации компилятором, микроконтроллер же может оперировать в памяти программ с точностью до байта.

0000 это код команды NOP. У нас три команды, поэтому шесть нулей.

Обрати внимание на то, что команды идут с адреса 0000. Это потому, что мы не указали ничего иного. А кодовый сегмент начинается с адреса 0000.

Теперь смотрим на программный счетчик Programm Counter, он показывает адрес инструкции которая будет выполнена.

Поскольку мы только стартанули, то он равен нулю — нулевая инструкция. Нажми F11, процессор сделает шаг, программный счетчик изменится на 1 и покажет следующую инструкцию. И так до тех пор, пока не дойдет до третьего NOP, а что у нас после него? А после него у нас FF до конца памяти. FF это несуществующая инструкция, на ней виртуальный контроллер перезагрузится с ошибкой invalid opcode, а реальный контроллер ее проигнорирует, пробежав по ним, как по NOP, до конца памяти.

Сбрось симуляцию микроконтроллера (Shift+F5) и вручную выстави в Program Counter адрес 0×000002 — проц сам перепрыгнет на последнюю команду NOP. Если менять Program Counter то проц будет выполнять те команды, которые мы ему укажем. Но как это сделать в реальном контроллере? В него то мышкой не залезешь!

Программный счетчик меняется командами переходов. Их много (условные, безусловные, относительные), о них я расскажу подробней чуть позже, пока же приведу один пример.

Добавим в наш код команду JMP и ее аргумент — адрес 0×000001.

1
2
3
4
5
		.CSEG			; Кодовый сегмент
		NOP
		NOP
		NOP
		JMP 0x000001

Команда JMP, как и все команды перехода, работает просто — записывает в Program Counter свой аргумент. В нашем случае — 0×000001.

Перекомпиль проект и посмотри на то, как меняется Program Counter (далее буду звать его PC) и вообще как теперь идет процесс выполнения программы. Видишь, после JMP в программный счетчик заносится новый адрес и процессор сразу же перепрыгивает в начало кода, но не на первую, а на вторую инструкцию. Программа зациклилась.

Это называется абсолютный переход. Он может сигануть куда угодно, в любую область памяти программ. Но за такую дальнобойность приходится платить. Если заглянешь в память, то увидишь там такую картину:

OC 94 - это код нашей комады, а 01 00 адрес перехода. Длина команды стала четыре байта. Два байта ушло на адрес. Вот, кстати, особенность памяти в том, что там данные записываются зеркально, т.е. младший байт числа по младшему адресу (порядок little-endian).

Но поскольку дальнобойные команды применяются редко, основной командой перехода в AVR является относительный переход RJMP. Основное отличие тут в том, что в PC не записывается не точный адрес куда надо перейти, а просто к PC прибавляется смещение. Вот так:

1
2
3
4
5
6
7
8
9
		.CSEG				; Кодовый сегмент
		NOP
		NOP
		NOP
		RJMP PC+2
		NOP
		NOP
		RJMP PC-6
		NOP

По идее, должно работать напрямую, т.е. RJMP +2. Но компилятор такую запись не понимает, он оперирует абсолютными адресами, приводя их потом в нужные смещения машинного кода. Поэтому применим макроопределение PC - это текущее значение счетчика в данном месте программы. Т.е.

1
JMP PC

Наглухо зациклит программу в этой строчке.

Благодаря относительному переходу, смещение можно запихать в те же два байта, что занимает команда. При этом код относительного перехода выглядит так Сх хх, где ххх это смещение от +/-2047 команд. Что обычно хватает с лихвой. В памяти же команда перехода выглядит как хх Сх, то есть байты переставлены.

Таким образом, длина команды у относительного перехода составляет 2 байта, против четырех у абсолютного. Экономия!!! Учитывая что в обычной программе подобные переходы на каждом шагу.

Вот только что же, каждый раз самому вручную высчитывать длину перехода? Ладно тут программка в три команды, а если их сотни? Да если дописал чуток то все переходы заново высчитывать?

Угу. Когда то давно, когда компиляторов еще не было так и делали. Cейчас же все куда проще — компилятор все сделает сам. Ему только надо указать откуда и куда. Делается это с помощью меток.

1
2
3
4
5
6
7
8
9
		.CSEG		; Кодовый сегмент
M1:		NOP
		NOP
		NOP
		RJMP M2
		NOP
M2:		NOP
		RJMP M1
		NOP

Метки могут быть из букв или цифр, без пробелов и не начинаться с цифр. Заканчиваются двоеточием. По факту, метка означает текущий адрес в словах. Так что ее можно использовать и в операциях. Надо только помнить, что она двубайтная, а наш контроллер однобайтный. Разобрать двубайтное значение по байтам можно с помощью директивы компилятора Low и High

Например,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
		.CSEG			; Кодовый сегмент
M1:		NOP
		NOP
		LDI	ZL,low(M2)	; Загрузили в индекс 
		LDI	ZH,High(M2)
 
		IJMP
 
		NOP
		NOP
		NOP
M2:		NOP
		RJMP M1
		NOP

Команда LDI загружает непосредственное значение в регистр старшей (от R16 до R31) группы. Например, LDI R17,3 и в R17 будет число 3. А тут мы в регистры R30 (ZL) загрузили младший байт адреса на который указывает метка М2, а в R31 (ZH) старший байт адреса. Если протрассируешь выполнение (F11) этого кода, то увидишь как меняются значения регистров R30 и R31 (впрочем 31 может и не поменяться, т.к. там был ноль, а мы туда ноль и запишем — адрес то мал). Смену значений регистров можно поглядеть в том же окне где и Program Counter в разделе Registers.

Команда IJMP это косвенный переход. Т.е. он переходит не по адресу который заложен в коде операции или идет после него, а по адресу который лежит в индексной регистровой паре Z. Помните я говорил, что шесть последних регистров старшей регистровой группы образуют три регистровые пары X,Y,Z используются для адресации? Вот это я и имел ввиду.

После IJMP мы переходим на нашу же М2, но хитровывернутым способом. Зачем вообще так? Не проще ли применить RJMP и JMP. В этом случае да, проще.

Но вот благодаря косвенному переходу мы можем программно менять точку перехода. И это просто зверский метод при обработке всяких таблиц и создании switch-case структур или конечных автоматов. Примеры будут позже, пока попробуйте придумать сами.

Давай подвинем нашу кодовую конструкцию в памяти на десяток байт. Добавь в код директиву ORG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; FLASH ===================================================
		.CSEG		; Кодовый сегмент
		NOP
 
		.ORG 0x0010
M1:		NOP
		NOP
		LDI	ZL,low(M2)	; Загрузили в индекс 
		LDI	ZH,High(M2)
 
		IJMP
 
		NOP
		NOP
		NOP
M2:		NOP
		RJMP M1
		NOP

Скомпиль и запусти. Открой память и увидишь что в точке с адресом 0000 у нас стоит наш NOP (Точка входа должна быть, иначе симулятор скукожит от такого когнитивного диссонанса. А вот дальше сплошные FF FF и лишь начиная с нашего адреса 0×0010 пошли коды остальных команд.

То есть мы разбили код, заставив директивой ORG компилятор перетащить его дальше, начиная с 0×0010 адреса.

Зачем это нам потребовалось? Ну, в первую очередь, это требуется чтобы перешагнуть таблицу векторов прерываний. Она находится в первых адресах.

Во-вторых, такая мера нужна чтобы записать код в конец флеша, при написании бутлоадеров (о них тоже дальше в курсе будет).

А еще таким образом можно прятать в коде данные, перемешивая данные с кодом. Тогда дизассемблирование такой программы превратится в ад =) своего рода, обфускация публичной прошивки, сильно затрудняющая реверс инженеринг.

Да, у кристалла есть биты защиты. Выставил которые и все, программу можно считать только спилив крышку кристалла с помощью электронных микроскопов, что очень и очень недешево. Но иногда надо оставить прошивку на виду, но не дать сорцов. И вот тогда превращение прошивки в кашу очень даже поможет =)

Ну или написать ее на Си ;))))))) (Есть вариант шифровки прошивки и дешифровки ее на лету бутлоадером, но это отдельная тема. О ней может быть расскажу)

Еще, там, в студии, же где и программный счетчик, есть ряд интересных параметров.
Во-первых, там отдельно вынесены наши индексные пары X,Y,Z и можно не лазать на дно раздела Registers.
Во-вторых, там есть Cycle counter — он показывает сколько машинных циклов протикал наш контроллер. Один машинный цикл в AVR равен одному такту. Frequency содержит частоту контроллера в данный момент, но мы задаем ее вручную.
А в-третьих, есть Stop Watch который показывает время выполнения. В микросекундах. Но можно переключить и в миллисекунды (пошарь в контекстном меню).

Если тебе кажется, что все слишком просто и я чрезмерно все разжевываю. Хы, не расслабляйся, у меня сложность растет по экспоненте. Скоро пойдет работа на прерываниях, а потом будем писать свою операционную систему :))))))

Комментарии

113 комментариев на «AVR. Учебный курс. Простейшая программа.»


  1. Beams 03 Авг 2008 20:19

    “Если вместо стройной иерархии структур в нашей голове образовалась каша, не стоит волноваться - это нормально. Постепенно она утрясётся и все структуры встанут на свои места, так что оставим их дозревать, а сами сосредоточимся на текущих проблемах.” Крис Касперски.

    Думается мне само то вставить в эпиграф ;)

    DI HALT

    Примерно так. Я действую тем же способом, что и учился сам. Взял и поставил задачу, а уж как она решается, из каких кусков делается… Разберемся по ходу дела.


  2. Arhangelsk 05 Авг 2008 0:26

    “Поехали”! Все. Начали. Можно пробовать эмулировать схему в протеусе.
    PS: А когда планируется первая “протеусная” статья?

    DI HALT

    Могу после 6го выложить что нибудь. ПОка запара - статья в “Хакер” висит, а дедлайн оооочень скоро :)


  3. Softy 17 Авг 2008 15:03

    Что такое стек? Это область памяти, работающая по принципу первый вошел-последний вышел.А не наоборот, последний вошёл, первый вышел (LIFO)? Хотя по логике вроде тоже самое.

    DI HALT

    ЭЭэээ а чем отличается? Вопрос лишь в формулировке. Да это оно.


  4. sinobi 24 Авг 2008 19:20

    Интересно о ассемблере пишут,а о компиляторе нет ни слова,непонятно.Или я где-то просмотрел?Люди которые незнают как компильнуть как это будут делать это?

    DI HALT

    По моему я упоминал, что все работы ведутся в авр студии. Там полный GUI так что никаких ключей и гемороя. Там же и отладка.


  5. Shorst 15 Сен 2008 19:28

    Вопрос по строчке OUTI SPL,low(RAMEND): функция low(RAMEND) выбирает из RAMEND младший байт и по нему устанавливает конец ОЗУ МК, а старший байт подразумевается равным нулю.Т.к. значение RAMEND равно $045f,то мы ограничиваем размер ОЗУ стеком до 95 байт (5F)?

    DI HALT

    Эт косяк. Я думал что уже везде поправил. Сей кусок кода была скопипащен из кода для Тини2313 у которой 128байт ОЗУ и значит там не требуется второй байт для адресации. У меги 8 уже 1кб. Конечно же надо еще загрузить старший байт

    OUTI SPH,high(RAMEND)

    Копипаст зло!


  6. VVV 16 Окт 2008 5:26

    Возможно я чето пропустил, по этому не до конца понимаю почему все-таки ты всегда зажигаешь светодиоды именно нолем? Здесь есть какие-то приемущества или это просто дело привички?
    И еще, где можно посмотреть перечень контролеров Atmel (можно и не только Atmel) и их возможности? А то я пробовал что-то разузнать на их сайте - ничего для “домашнего” применения не встретил и ваше там только какие-то супермодные все…

    DI HALT

    1) Дело привычки - С51 не умел тянуть ноги вверх

    UPD:
    Потер те две причины. Они к подтягу на 1 %) Остается только привычка. А вообще, последнее время, на реальных схемах делаю так, как удобней разводить. Иногда бывает и так и эдак в одной и той же схеме. А чтобы не путаться, то увязываю все в макросы. Типо ON/OFF
    Блин гоню с недосыпа.

    promelec.ru/catalog_info/48/70/212/73/
    ну вот например.

    Там же, в каталоге промэлектроники можно позырить такие же таблицы и на пики, и на МСП430 и на С51

    @mva

    кстати, на STK500 (думаю и на 600 тоже) диоды тоже нулём зажигаются. Так что не у одного тебя такие привычки :)


  7. vanchester 19 Окт 2008 9:53

    DI HALT, нужна помощь.

    Прошил в ATMega8535 твою программу Mega8Robot.asm, предварительно подправив inc-файл и прерывания. Воткнул в собранную схемку - все работает с точностью до наоборот (команда CBI заставляет светодиод светиться, команда SBI - гаснуть)… (для прошивки использую uniprof, программатор собрал по схемке, которая у тебя на сайте)

    Например, такая программка:
    .include “m8535def.inc”
    .def acc = R16 ; Accumulator

    .MACRO outi
    LDI R16,@1
    OUT @0,R16
    .ENDMACRO

    .CSEG
    .ORG 0×0030
    RJMP Reset

    Reset: OUTI SPL,low(RAMEND)
    OUTI SPH,High(RAMEND)
    OUTI DDRB, 0xFF
    main:
    OUTI PORTB, 0×00
    RJMP main

    В симуляторе все как положено, все выводы PORTB “выключенные”, а реально если межу выводами и Vcc поставить диоды, они светятся. Попробовал заменить 0×00 на 0xFF, светодиоды перестают светиться. Где я чего напутал? Спасибо.

    DI HALT

    а ты диод как ставишь? Тут ситуация такая:

    Если диод подключен между +5 и выводом мк то от 0 на выходе он будет светится.
    (ток идет с +5 через диод на 0 на ножке)

    Если же включить между выводом и GND то будет светится от 1.
    (Ток идет с +5 на ножке через диод на землю.)

    Проследи путь тока через диод и все станет ясно :)

    vanchester

    Теперь поянтно :)


  8. Sergey 24 Ноя 2008 0:36

    DI HALT возник вопрос по программе из статьи “Домашний терминатор” журнала Хакер.
    Посмотрел текст прграммы:
    1. ждем нажатия кнопки
    2. если да, то зажигаем лампу и подаем 1 к левому и правому двиг. и разрешения
    и т.д.
    Потом зашил скомпиленый файл .hex, проверил па макетке - никаких реакций на нажатие кнопки (PB0 подключаем к земле) и последующие нажатия кнопок датчика касания.
    Скомпилировал файл с прошивкой из исходных текстов - размер оказался другим (больше). Проверил на макетке. При подачи питания едет (есть разрешения и 1 на двигателях) нажимаю кнопку правый двигатель останавливается, не реагирует на датчики касания.
    Подскажи где ошибка. Спасибо.


  9. Timy 09 Янв 2009 0:47

    долго мучил DiHalt-а ))) потом в сети нашол :


  10. Ridik911 14 Апр 2009 1:13

    так.. получается у нас для определения будет ли порт вход или выход - ДДР если 1 то выход, если 0 то вход?
    например

    ldi r16,0xff ;весь порт на выход
    ldi r17,123
    out ddrc,r16 ;вкл. порт на выход
    out portc,r17;выводиим число

    я правильно понимаю надеюсь.. только студия тупит чота а мож я… когда уже непосредственно вывожу на ПОРТЦ некоторое число до 255 то оноже идет и по ПИНЦ.. иль так задумано (хапсим не поддерживает новую стройку студии)

    DI HALT

    Так и есть. Значение DDR определяет направление порта. 0 вход, 1 выход.

    Если у тебя линия на выход настроена, то выдача значения в PORT одновременно вылазит и в PIN, что естественно. Т.к. PIN это значение которое непосредственно на ноге.

    Т.е. грубо говоря так:
    DDR - направление
    PORT - что мы хотели получить на выходе
    PIN - что у нас реально получилось.

    Т.е. пример, ты делаешь DDR на выход, выдаешь в PORT 1, ножка пытается установиться в единицу. Но вот засада - она на ноль оказалась по ошибке закрочена. В итоге транзистор сгорает, нога безвольно повисает в нуле, и несмотря на то что в PORT=1 из PIN считается 0, т.к. ножка сгорела и в единицу встать не может.

    Ridik911

    >Но вот засада - она на ноль оказалась по ошибке закрочена. В итоге транзистор сгорает, нога безвольно повисает в нуле, и несмотря на то что в PORT=1 из PIN считается 0, т.к. ножка сгорела и в единицу встать не может.
    —-
    это какбы контрпример таксказать..
    а так понял. спасибо… надежды ждать разработчиков хапсим нет.. такчто запустил для проверки в VMLAB (чтото типо хапсим для кодевизон).. только никак не получается подключить диоды нога-земля (получается нога-Vdd(+5)) - вообщем инвертированость дает

    .POWER VDD=5 VSS=0 ; Power nodes
    .CLOCK 32k ; Micro clock
    .STORE 250m ; Trace (micro+signals) storage time

    D1 VDD PD0 ; LED code.
    D2 VDD PD1 ; (since no more LEDs are available

    D8 VDD PD7 ;in the control panel)

    , но для проверки и наглядности достаточно


  11. Security 25 Апр 2009 3:21

    незнал куда написать, но проблема в программировании.
    подключил 12-ти разрядный АЦП(AD7892-2) к микроконтроллеру (ATmega16), к порту Ц(младшие 8) и к порту Б(старшие 4). выставляю данные на выход АЦП (на входы портов), читаю порты:
    buffer[0]=PINC;
    buffer[1]=PINB & 0×0F;
    получаю например такой результат:
    buffer[0]: 0х42
    buffer[1]: 0×00
    потом беру меряю тестером напруги на ножках микроконтроллера:
    порт С = 0х78
    порт В = 0х00
    я так понимаю что непрально я считываю инфу с портов. оба порта настроены так:
    DDRC=0×00; // порт С вход
    PORTC=0xFF; // подтягивающие резисторы подключены.
    подскажите в чем я ошибся. или предложите альтернативу решения данной проблемы.

    DI HALT

    Считываешь правильно и настроил порт ты тоже как надо.

    Т.е. у тебя считаное не равно тому что реально на входе? А ты точно уверен в том, что выходное значение АЦП не плавает? Отключи АЦП и подай какой нибудь левый код вручную.

    Security

    “Т.е. у тебя считаное не равно тому что реально на входе?” да именно.
    выход АЦП не плавает (тыкал в каждый вывод счетчик, на ТТЛ, т.е. скорость реакции бешанная 5нс) ниче не щитает. четко показывает “1″ “0″.
    А не может быть такой хитрости, что порт С вообще не предназначен для считывания инфы, а только на вывод? Просто в примере по которому я делаю, использвется вместо порта С порт А, но я подумал, что это не принципиально и подпаял на С.

    DI HALT

    Кажись понял в чем косяк! На этом порту ЕМНИП висит JTAG и он по дефолту вроде бы включен. Выруби нах в фузах бит JTAGEN

    Security

    Пусть за мной будет замечена гомосятина, но я тебя расцелую!!! ;)
    Да, по умолчанию оказалось JTAG включен. Скинул флаг начало читать то что подает АЦП.
    Огромное спасибо!

    DI HALT

    Старые грабли. Я сам никогда мегу16 не юзал, а про них где то упоминалось уже.


  12. bdp 14 мая 2009 20:58

    Проемулировал програмку в AVRStudio, и тут такая лажа приключилась с кнопкой старта:
    Значения DDRB,0 = 0 и PORTB,0 = 1 а вот значение PINB,0 осталось равным “0″, и поэтому програма проходила дальше (а кнопку то я типа и не нажимал). Таже лажа с контролем припятствий.
    Насколько я помню по предидущей лекции это глюк самой AVRStudio, или я гдето лоханулся.
    Набраний код перепроверил три раза О_о

    DI HALT

    Да, студия сама PIN не выставляет. Откуда ей знать какой он должен быть? Его надо вручную задавать.


  13. SIM 08 Авг 2009 3:54

    В предыдущем примере где на ноги выводили похожее на 0101010 (hex АА) небыло теблицы прерываний. Начинало работать сразу основное тело программы?
    Программа начиналась по адресу
    .ORG 0×0030
    я так понимар при включении микроконтроллер кидается на адрес 0×0000 там “nop” и так ползет 30 тактов до адреса 0х0030? или сразу как-то переходит?

    2. Вход reset я так понял может использоваться как порт ввода-вывода? В том же Attiny2313 он помечен как (RESET/dW) PA2. Как тогда сбросить контроллер? непонятно пока что :-)

    DI HALT

    Да там стартует на 0000 потом ползет через всю таблицу прерываний до 30 адреса и оттуда выполняет.

    Но по правильному бы лучше сделать так:
    ORG 0000
    RJMP RESET
    [таблица векторов прерываний]
    ORG [end vectors size]
    RESET:

    Сейчас подправлю там текст проги, чтобы сразу было как принято.

    2. Да его можно использовать как порт, при этом теряется возможность внешнего сброса и ТЕРЯЕТСЯ ВОЗМОЖНОСТЬ ПРОШИВКИ ЧЕРЕЗ SPI только параллельным программатором на 12вольтах.


  14. RunningWolf 19 Окт 2009 1:51

    Во-первых, огромное спасибо автору. Хорошая подача материала, действительно помогло перейти от раздумий “хорошо бы…” к делу не имея никаких навыков в электронике.
    Респект!

    А теперь по теме: =)
    Собрал программатор Громова. Воткнул USB-COM - получил COM26. Черт! Uniprof только до COM5. Ладно, бог с ним - попробовал avrdude - фиг вам! А, чтоб тебя..!
    Достал с антресолей маму с PIII и COM-портами на маме, воткнул программатор - как ни странно, заработало сразу. Ура!
    Т.к. на кошках тренироваться скучно, решил прошить программку из этой статьи.
    Подключил, правда, только:
    - кпноку “Старт” (PB0)
    - кнопку “Левый передний датчик” (PC0)
    - светодиоды: отладочный(PC3), левый разрешен(PB2), левый направление(PD2, PD3)
    Прошил, включил и… оно не работает. МК: Atmega8-16PU. После запуска загораются вообще все светодиоды и на кнопки не реагирует.

    Подумав, пошел в обход и таки получил, простую прогу, которая зажигает желаемые лампы при нажатии на те или иные кнопки.
    Т.о. проблемы либо в схеме, либо в программке, либо в Mege либо, во мне =)

    По проге из этой статьи:
    1) Входы подтянуты к 1-це. Это логический 0 или логическая 1?
    2) Светодиоды подключены к +5В. Когда контроллер чисты (не прошит) - не горит ничего. Т.е. на всех выходах - логическая 1.
    Судя по тому, что при запуске проги из этой статьи сразу загораются все светодиоды - значит на всех выходах сразу устанавливаются нули. Т.е. проходят CBI для каждого порта.
    Где они есть в этой программке? Это макросы LIGHT_ON, LDR_DISABLE, L_Run_F, L_Run_B, L_Stop. Т.е. в самых разных частях. Такое впечатление, что оно после запуска крутится в главном цикле, игнорируя все условия.

    3) Изначально все входы находятся в одном состоянии (либо 0, либо 1, в зависимости от ответа на вопрос 1 =)). Нажатие кнопки меняет состояние порта на противоположное (пока кнопка нажата). Команда SBIС проверяет вход=0. SBIS проверяет, что вход = 1. Как тогда в одном месте для проверки нажатия используется SBIC, а в другом - SBIS? Ведь изменение уровня от кнопки в обоих случаях одинаковое. Или как?

    DI HALT

    “Собрал программатор Громова. Воткнул USB-COM - получил COM26″
    А толку, Громов все равно не работает с этими конвертерами. Хотя номер порта можно и сменить в диспетчере оборудования.

    1) Это вход с подтягом. Они подтянуты к 1це. Т.е. когда кнопка не нажата на порту 1. Нажатая кнопка садит порт на 0

    2) Когда контроллер чист на всех выводах HiZ т.е. считай что они вообще никуда не подключены.
    А теперь смотрим что в программе:
    Мы при инициализации порты светодиодов сразу же выставляем на выход:

    SBI DDRD,LDR0 ; А тут..
    SBI DDRD,LDR1 ; микросхемой..
    SBI DDRB,LDRE ; двигатели….
    SBI DDRC,RDR0
    SBI DDRC,RDR1
    SBI DDRB,RDRE

    Но! При старте во всех регистрах PORT по дефолту стоят нули. А когда мы DDR устанавливаем в 1, то порт становится на выход и в результате (т.к. PORT = 0) на ноге у него получается 0, вот диоды и зажигаются все сразу.

    3) Ты не совсем вкурил в алгоритм. SBIS и SBIC это не команды проверки кнопки. Это проверка условия.

    В первом случае (кнопка) нам надо понять когда она нажата и по этому факту выскочить из цикла.

    А во втором случае нам удобней отслеживать другое условие, т.е. пока не нажата пропускаем переход на StopAll. Как только нажата - переходим и стопорим все движки.

    использовать можно sbic и sbis в зависимости от тогокак нам удобней в данный момент.
    т.е. пропустить команду если бит есть или пропустить команду если бита нет.

    RunningWolf

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

    Вот мой код:

    .include “m8def.inc”

    .MACRO outi
    LDI R16,@1
    OUT @0,R16
    .ENDMACRO

    .CSEG
    .ORG 0×0000
    RJMP RESET

    .ORG 0×0030

    RESET:

    CBI DDRB,0
    SBI PORTB,0

    CBI DDRC,0
    SBI PORTC,0

    SBI DDRC,3
    SBI DDRB,2

    SBI DDRD,2
    SBI DDRD,3

    ; Init stack
    OUTI SPL,low(RAMEND)
    OUTI SPH,High(RAMEND)

    Main:
    ;SBIS PINB,0
    SBIC PINB,0
    RJMP Pause

    ; Off light
    SBI PORTC,3
    SBI PORTB,2

    ; On Light
    CBI PORTD,2
    CBI PORTD,3

    Pause:
    ;SBIS PINC,0
    SBIC PINC,0
    RJMP Main

    SBI PORTD,2
    SBI PORTD,3

    CBI PORTC,3
    CBI PORTB,2

    RJMP Pause

    Как оно живет “в натуре”: После запуска загорается все (PD2, PD3, PC3, PB2). При замыкании на землю PB0 остаются гореть только PD2, PD3. При заземлении PC0 наоборот горят только PC2, PB3.

    Если я правильно понимаю, то при наличии подтяга (он, вроде обеспечивается инструкциями CBI DDRB,0 и SBI PORTB,0) на порту, если его никто не трогает, всегда висит 1-ца. Т.е. пока держишь землю - идет ноль. Отключил землю - порт ушел опять в 1. Так?
    Если да, то не понятно как может крутится блок Pause (горят только PC2, PB3), когда я уже отпустил кнопку (снял землю) на PC0? Ведь тогда PC0 опять встанет в 1, SBIC PINC,0 не сработает и при следующем же цикле оно должно уйти по RJMP Main. Так?

    С другой стороны, если подтяга нет, то не понятно, как крутится блок Main (горят только PD2, PD3)? Ведь опять-таки тогда должны все время срабатывать оба условия и гореть все диоды.

    Вобщем, что-то я не догоняю :( Не дай помереть дураком =)

    DI HALT

    Подтяг проверяется просто - в свободном состоянии на выводе должен быть +5в

    DI HALT

    Так, стоп. Где ты у меня паузу увидел?

    RunningWolf

    Ты про “блок Pause”? Это я логику своей проги (см. пред. пост) имел в виду.
    У тебя, я так понимаю, циклы только:
    1) Ожидание нажатия старт (Main: … RJMP Main)
    2) Ожидание сигнала от датчиков (Seek: … RJMP Seek)
    3) Общий цикл (Main: … последний RJMP Main)

    Я про свою прогу. Никак не могу понять, как оно уходит в цикл Pause когда PC0 уже НЕ заземлен. А в реальности он туда уходит (стоит только коснуться землей PC0 и затем убрать землю), т.к. PB2 и PC3 продолжают гореть, а PD2, PD3 - погашены.

    DI HALT

    Дребезг контактов учел?

    RunningWolf

    А про USB-COM и Громова. Вроде кто-то писал, что иногда оно работает. Надо же было попробовать =) Да и все-равно оно надо для USBASP.


  15. SergeyDon 30 Окт 2009 20:19

    чем отличаются например команды: JMP и CALL? ну кроме количества тактов исполнения.
    ставил и так и так работает…

    DI HALT

    Принципиально отличаются.

    JMP это просто переход.
    А CALL это переход с запоминанием точки перехода и по команде RET мы в эту точку вернемся.

    Мало того, часто делая CALL мы можем сорвать стек. Так что за каждым CALL должен следовать RET На какую глубину ушли из нее же возвращаемся.

    SergeyDon

    СПС.
    я разобрался!
    просто я стек не инициализировал, и у меня при вызове что JMP, что CALL
    при достижении Ret взвращалось на адресс 0х0000.
    на сайте gaw.ru там даташит с переводом на русскй, так там вообщенет про стек и его инициализацию… ничего нет!


  16. Sergas5 03 Фев 2010 11:40

    Привет всем! DI HALT, о какой отладочной плате идет речь? О той, которая в разделе “Демоплата” или которая в разделе “Создание печатной платы методом лазерного утюга”. Кстати, где можно прочитать о последней - что-то нигде не нашел. Заранее спасибо!!!

    DI HALT

    О любой. Изначально было о той что в статье про ЛУТ.

    А нигде. Тогда еще не было четкой логики сайта поэтому и не было отдельной статьи.


  17. A_ndrej 06 Фев 2010 22:54

    Не могу понять.
    В вашей программе в этом куске:
    ” ; Датчики касания
    .equ L_FNT = 0 ; Правый передний
    .equ R_FNT = 5 ; Левый передний

    .equ L_BCK = 4 ; Левый задний
    .equ R_BCK = 3 ; Правый задний

    ; Кнопка запуска
    .equ ST_BTN = 0

    ; Двигатели.
    .equ LDR0 = 2 ; Левый двигатель бит 0
    .equ LDR1 = 3 ; Левый двигатель бит 1
    .equ LDRE = 2 ; Левый двигатель разрешение

    .equ RDR0 = 2 ; Правый двигатель бит 0
    .equ RDR1 = 1 ; Правый двигатель бит 1
    .equ RDRE = 1 ; Правый двигатель разрешение

    ; Сигнальные лампы
    .equ LIGHT = 3

    /* Откуда я взял эти цифры? А поглядите на номера портов к которым прицеплены
    все эти датчики и двигатели. Вот! Т.е. я просто обозвал биты как мне было
    удобно. Чтобы не держать в памяти где у меня что подключено. */ ”

    я не понимаю, не откуда взялись эти цифры(уже понял), а как эти биты не путаются с такими же битами других портов?
    Ведь в каждом порту есть биты 0…7. Как же ваши биты не путаются с остальными?

    DI HALT

    Ну так я же явно указываю имя порта когда обращаюсь к этим битам

    A_ndrej

    Как указываете?
    В этом же куске кода?
    В упор не вижу.

    DI HALT

    Ну вот, например:

    CBI DDRC,L_FNT

    DDRC нам какбэ намекает, что мы пишем в регистр DDR порта С


  18. MWAndry 07 Фев 2010 22:50

    DI-HALT, обьясни пожалуйста, зачем ты в определниях обозвал регистр R16
    .def ACC = R16 ; Accumulator
    Ведь дальше по коду это имя не встречается.
    В макросе OUTI ты используеш R16 а не АСС.
    Это очепятка или я что-то пропустил?

    DI HALT

    Да это привычка из C51 ассемблера. Там был АСС как основной регистр. А тут как то забылось уже.

    MWAndry

    Тобиш если я эту строку удалю из кода, ни на что это не повлияет?

    DI HALT

    Скорей всего нет.

    MWAndry

    Спасибо за опаретивный ответ.
    Отдельное спасибо вообще за все учебные статьи. Спаял програматор Громова, удалось прошить контроллер.
    Но что то не так со статьей про UART. Жму “читать полностью” а мне выбрасывается вот такой символ “<l” и ничего больше.

    DI HALT

    Какая именно статья глючит? Только что проверил статью “Передача данных через UART” нормально она открывается.

    MWAndry

    С рабочего компьютера всё открылось и у меня. Значит домашняя сеть барахлит. Звыняйте за беспокойство.


  19. MWAndry 08 Фев 2010 1:18

    Оперативный ответ. Звиняйте, часто очепятываюсь.


  20. A_ndrej 08 Фев 2010 20:41

    Есть один вопрос.
    Хочу в качестве своей первой программы сделать спидометр на моторолер.
    Поцеплю на диск колеса магнит, на барабан геркончи или датчик Холла, и буду ловить их показания ATмега восьмеркой.
    После прочитаного, казалось, что может быть проще)))
    А когда начал разбиратся то появилось сколько подводных камней, что я теперь не вылажу с нета в поискак каких то аналогий. А везде всякие спидометры и тахометры настроены только на свои параметры колеса, и даются только hex файлы.
    Хожу теперь в ступоре, по пол ночи не могу заснуть(все это в голове копошится, а отказыватся от идеи не хочется(уже сколько времени на нее потратил).
    Как я понимаю для того что бы измерить скорость, нужно измерить период приходящего импульса.
    Я думаю что один таймер нужно запитать от внешнего источника(от геркона), а второй пустить от встроенного генератора.
    Когда первый импульс с геркона приходит, то второй таймер запускается и начинает считать.
    Как только приходит второй импульс то второй таймер выдает свои подсчеты(время между импульсами), и начинает считать заново до прихода нового импульса.
    Все это потом считается по формуле V=l/t? где V- скорость, L-окружность колеса(то есть пройденый путь),t- время(результат подсчета второго таймера).
    Так, все понятно, а как это росписать в коде ума не приложу.
    Может что подскажете?
    Или ссылочку какую то дадите с примером?

    P.S. Еще подскажите пожалуйста как делить и умножать в AVR?

    DI HALT

    Проще наверное даже так сделать: геркон на прерывание INT. По первому импульсу запускаешь таймер, по второму останавливаешь и считываешь натиканное.

    Городить вычисления думаю тебе не нужно. У тебя же не высокоточная схема. Проще сделай табличные вычисления. Забабахай себе таблицу скоростей с шагом в 0.5км/ч под твое колесо или как те еще надо. И последовательно сравнивай натиканное по таблице пока не будет больше/меньше (смотря с какой стороны пойдешь сравнивать) это и будет текущая скорость. Будет быстрей и компактней.

    A_ndrej

    Спасибо.
    Как отсеживать понял. А как изобразить в коде сравнение с задаными таблице значениями?
    Это надо чтобы ставился флаг С, а потом отлавливать его командами BRSH и BRLO?
    Если да, то как сделать так чтобы флаг С ставился, и куда его вообще ставить чтобы потом можно было сравнивать с заранее заданыыми значениями?

    DI HALT

    Да. Делаешь CP Rn,Rn двух регистров где у тебя табличное и текущее. И на основании этой команды выскочат флаги, ну а там BR** и все.

    A_ndrej

    Ааааааааааа!!!


  21. A_ndrej 08 Фев 2010 21:47

    Дошло))))
    Большое спасибо))


  22. A_ndrej 09 Фев 2010 1:58

    А как задать таймер?
    Что то типа:

    ldi temp, 0х01; без деления в предделитете
    out TCCR0, temp
    ?

    Так как прерывания по переполнению не надо, то разрешение на прерывание по переполнению не ставить?
    Чтобы сбить таймер в ноль надо в предыдущем коде в temp записать ноль, или
    написать clr TCCR0?
    А как снять количество натиканых импульсов с таймера?
    Написать out temp, TCCR0 или как?
    Если можете - ответьте.
    Не обижайтесь пожалуйста на бесконечные расспросы :-), - хочется все таки разобратся.

    DI HALT

    Про таймеры есть отдельная статья. там все расписано.

    У таймера дофига регистров. TCCR0 это только один из них- управляющий. Есть еще регистры масок прерываний, и еще два управляющих регистра. ;)))

    А счет идет в счетном регистре TCNT И вот оттуда надо считать. Ну и про сброс предделителя не стоит забывать ;)


  23. Igor 10 Фев 2010 13:45

    День добрый.
    У меня есть один вопрос.
    Можно ли реализовать как на некоторых китайских игрушках.
    Несколько кнопок и задаю программу,кнопка вперед,влево,право и тд. после нажимаем
    старт и танк исполняет то,что там напрограммировал.для каждого нажатия к примеру:
    20 см вперед или назад,10 град. поворот влево или право и тд.
    Реально это или нет?

    DI HALT

    Реально. Но придется писать что то вроде небольшой виртуальной машины.


  24. Dmitri 02 Апр 2010 21:51

    Команда JMP 0×000001 не работает что-то в mega8. Здесь говорят http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=33273&start=0 что она не айс и её закрыли.

    DI HALT

    В Мега8 нет такой команды. Там нет и особой нужды сигать дальше чем RJMP - память коротка.


  25. krestic 04 Апр 2010 2:33

    Чудицца мне что коменты тут совсем не к этой статье:) Диоды, “Домашний терминатор”- а в статье только про НОПы…
    А в чем великий смысл ставить такое количество нулей в 0х000001 ? Почему именно шестизначное число, а не восьми или двух?

    DI HALT

    Привычка адреса указывать так.


  26. Shuterkent 17 Апр 2010 0:41

    Читал я читал множество статей твоих прочитал ДиХалт но так и не вкурил, допустим мне нужно выставить к примеру Порт РВ4 на вход, так вот как я понимаю мне нужно регистр DDRB выставить на 0, а порт в зависимости, что я хочу там увидить или PORT равен 1, или PORTB=0, и еще правильно ли я понял, если у нас в регистре PORTB равен 0 то при нажатие на кнопку он менятся на 1???. А теперь собственно вопрос что я должен увидеть в AVR studio, когда соответствинные регистры выставлены, и как понять что это 1 и что это 0 и где???. Просто я только начинаю разбираться в микроконтроллерах, а сайт у тебя классный очень много интересного даже его выкачал себе)))!

    DI HALT

    На вход DDR=0 это всегда.

    А PORT определяет режим работы входа.

    1 - “активный” вход с подтягом к +5, например для отслеживания кнопок.
    0 - пассивный вход с Hi-Z. Для отслеживания шин всяких.

    Реальное Значение из порта БЕРЕТСЯ В РЕГИСТРЕ PIN!!!


  27. Shuterkent 17 Апр 2010 18:11

    тоесть, если я выставил DDR=0, PORT=1 то только тогда я могу подключить кнопку, а реальное значение вкл. она или не вкл. я просматриваю в PIN. Правильно я понял???

    DI HALT

    Да


  28. Shuterkent 17 Апр 2010 18:53

    Вот только начиал читать про макроассамблер там где простейшая программа, так вот я не понял как работает логика контроллера, а может и не увидил. Ну к примеру есть у меня:
    Порт первый
    DDR=0 Port=1-это я подкл. кнопку
    Порт второй
    DDR=1 Port=0-это я подкл. светодиод
    Так вот это первоначальное состояние.
    Так вот нажал я кнопку, мне нужно что б светодиод засветился так что Port=0 должен выставиться на Port=1.
    Как это пишеться с помощью ассамблера??!

    DI HALT

    Эм… вообщето PORT это целый байт. И в нем 8 бит, а всего таких портов у МК несколько может быть. Так что у тебя на одну ногу кнопка, на другую светодиод. А говоря про DDR=0 я имел ввиду КОНКРЕТНЫЙ БИТ порта.

    Например DDRD.7=0, а DDRD.6=1 и одна нога работает на вход, другая на выход.

    Соответственно на ассемблере настройка порта таким образом делается так:

    LDI R16,0<<7|1<<6
    OUT DDRD,R16


  29. Shuterkent 17 Апр 2010 22:07

    аааа понял! спасибо


  30. Kots 21 Апр 2010 17:33

    DI HALT, а какой прогой выделение жёлтым организуется? Оч удобно и наглядно, а сам вкурить метод никак не могу. =)

    DI HALT

    FastStone Viewer - рисование - выделение.

    Kots

    Спасибо)


  31. sanjok 01 мая 2010 20:57

    можна поподробнее о регистрах R0-R31, не могу понять с чем их есть,
    ето временная память которая роботает с всеми функциями,
    я должен постоянно в нее загружать, а потом с ней роботать, и обратно в оперативку?

    DI HALT

    Это 31 ячейка которыми ты можешь распоряжаться как угодно, но поскольку только с ними можно выполнять математические и логические операции, то они используются как временные оперативыне регистры, а результаты сохраняются в оперативку, т.к. регистров на все не хватит.

    sanjok

    спасибо


  32. Sergas5 08 мая 2010 12:21

    DI HALT, а где можно взять систему команд для микроконтроллера желательно на русском языке?

    DI HALT

    Я пользуюсь той, что в книге Евстифеева Контроллеры AVR серии Mega как то так. (у меня в разделе книги вроде бы еще есть ссылка). Вообще хорошая книжка - перевод даташита по сути.


  33. k0ndr 18 мая 2010 22:46

    Странно… При запуске кода с командами LDI и IJMP студия (v.4.18.700) выдаёт такую ощибку:
    D:\Program Files\Atmel\Projects\3.asm(4): error: Undefined symbol: ZL
    D:\Program Files\Atmel\Projects\3.asm(4): error: Invalid register
    D:\Program Files\Atmel\Projects\3.asm(5): error: Undefined symbol: ZH
    D:\Program Files\Atmel\Projects\3.asm(5): error: Invalid register
    D:\Program Files\Atmel\Projects\3.asm(15): No EEPROM data, deleting D:\Program Files\Atmel\Projects\3.eep

    Assembly failed, 4 errors, 0 warnings

    Что может быть не так?

    DI HALT

    Команды покажи? Может у тебя inc файл не подгрузился и символические имена не подцепились?


  34. k0ndr 19 мая 2010 1:54

    Оу, точно, когда перезапускал студию, забыл inc подгрузить. Спасибо, заработало нормально.


  35. dima_m 12 июня 2010 13:25

    Чисто условный пример, чтоб понять как идет запись данных во флеш память.
    1. Допустим я хочу записать число по адресу $0000. Как я понял оно запишется в ячейку 0, а не в 1?
    2. И если число увеличивать, то после 255, данные начнут записываться в ячейку 1?
    3. То есть запись будет идти как обычно только задом наперед (little-endian) ?

    ячейка ячейка
    $0000 - 0 1
    $0001 - 2 3
    $0002 - 4 5

    DI HALT

    Адресация флеша идет в словах (компилятору так удобней) т.е. 0000 это адрес первых двух байт (начала) 0001 адрес вторых двух байт и так далее.

    В реальности (когда мк сам обращается к данным во флеше) же адрес побайтный, 16ти разрядный.

    Когда указываешь адрес 0000 то запишется в реальный адрес 0000 в 0001 ты записать отдельный байт напрямую не сможешь. Только сгруппировав его в слово с другим байтом, либо вручную вписав в хекс.

    2. Не понял вопроса.

    Да, там младший байт по младшему адресу.

    dima_m

    2. Вопрос некорректный, ладно опустим его.


  36. A_ndrej 11 июля 2010 18:47

    Подскажите пожалуйста можно ли писать так:
    in XL,ICR1L
    in XH,ICR1H

    ldi Y,868
    cp Y,X

    В смысле можно ли оперировать сразу X или Y не расписывая на старшие и младшие разряды.
    И нет ли ошибки в выражении ldi Y,868
    или вообще в том что я накалякал?

    DI HALT

    Нельзя. Проц то у нас 8ми разрядный. Он не может сравнить два байта сразу. Да и регистра такого Y или Х нету как таковых. Они сделаны лишь для удобства - не более чем просто обозначение. На деле есть только XL XH (которые тоже не более чем обозначение, т.к. это R26,R27)

    А вот IN XL,ICR1L вполне возможно. Т.к. это тоже самое что и
    IN R26,ICR1L

    A_ndrej

    Спасибо, дошло)

    A_ndrej

    Спасибо, дошло)
    А как тогда выполнить сравнение с числом
    di Y,868
    cp Y,X
    ?
    Разделять его на два разряда?

    DI HALT

    Сравинвать побайтно, сначала младшие . а потом старшие. Старший сравнивать через команду CPC, чтобы с учетом переноса было.

    A_ndrej

    Щас мозг лопнет:-)

    DI HALT

    Смотри. Есть Х и Y

    CP XL,YL
    CPC XH,YH
    BREQ ….

    И все, и не надо лопать мозг. Таким образом, кстати, можно щелкать скольки угодно байтные переменные. Просто наращивая цепочку CPC операторов.


  37. A_ndrej 11 июля 2010 21:02

    А как игреку присвоить число 868? преобразовать в двоичный код - и разделить на 11 и 01100100.
    ldi YL,1101100100
    ldi YH,11
    Так что ли?

    DI HALT

    Ага делишь число просто на биты и все. Но есть способ проще - доверь это компилятору!

    LDI YL,Low(868)
    LDI YH,High(868)

    И все, компилятор сам разложит все по байтам.

    A_ndrej

    Понял.
    Блин, ну и головоломка:-)
    Это я пытаюсь писать прогу для спидометра которую закинул полгода назад.
    Использую вывод ICP1 для геркона и сравниваю то что нащелкал счетчик до захвата, с просчитанными цифрами чтобы индицировать скорость на экране.
    А вот когда мне нужно будет сравнить не двух разрядное число с таким же 2х разрядным показателем счетчика, а одноразрядное число(например 23) с двух разрядным показанием счетчика? Как быть?

    Также:
    LDI YL,Low(23)
    LDI YH,High(23)
    просто он поставит в YH сплошные ноли, или надо делать как то по другому?

    DI HALT

    >просто он поставит в YH сплошные ноли
    А почему тебя это смущает? ;)))) Так и должно быть!


  38. A_ndrej 11 июля 2010 21:33

    Да не, просто интересно, пусть ставит, лишь бы работало:-)


  39. Anhanger 15 июля 2010 0:15

    Объясните, пожалуйста…начинающему)
    ПОчему может не сбрасываться память в студииАВР 4?
    Допустим, вбиваю код 3 NOPa, в “программ мемори” в трех ячейках 6 байтов нулей после запуска эмуляции. Далее стираю один из NOP, т е остается их два. Нажимаю РЕЗЕТ, затем снова ЭМулящию. По идее, должно остаться четыре байта нулей…(?) А-н-нет, их остается все также шесть. Почему)? (криво установилась прога или криво с ней работаю)?)

    DI HALT

    А перекомпиляцию проекта кто будет делать?

    Anhanger

    Что нужно для этого нажать)?

    DI HALT

    F7

    Anhanger

    Нажимал(((
    Напишите, пожалуйста, тогда последовательность нажатия клавиш
    Допустим, запущена эмуляция с тремя этими командами НОП. Далее жму РЕЗЕТ, далее СТОП, далее Ф7, далее КОНТРЛ Ф? или Старт Дебаггинг.
    ПЕРЕД нажатием на резет удаляю третью команду НОП)

    DI HALT

    В эмуляции удали любую команду NOP и попробуй испольнить эмуляцию на еще один шаг дальше. Компилятор спросит у тебя разрешение перекомпилить проект (вопрос Rebuild project?) отвечай да и проект будет перекомпилирован, а дебаг запущен заново. Тут то та команда и исчезнет

    Anhanger

    …Простите за мое невежество…
    1. Проект с тремя НОП запущен
    2. Удаляю один последний НОП
    3. Нажимаю Ф11
    4. Прога спрашивает “РеБилд?” - нажимаю ДА
    5 Проект перезапускается.
    5а В Программ мемори остаются все те же три строки нулей(

    DI HALT

    Ааа ну есть такой прикол. Меньше не больше. А напиши туда какую нибудь другую команду - она отобразится. Это, кстати, прикол авр студии. Она не стирает память перед запуском, оставляя там предыдущие значения. Особенно это вставляет в оперативке (RAM). КОгда будешь работать с ОЗУ учитывай этот факт. И стирай память ВСЮ в самом начале работы, специальным циклом инициализации памяти (тупо записывая туда в цикле нули). Ну там дальше будет пример.

    Anhanger

    Большое Спасибо, DI HALT!

    DI HALT

    Можешь сохранить проект, закрыть студию, открыть проект заново. И запустить. Будет два нопа.

Оставьте свой отзыв

Вы должны войти, чтобы оставлять комментарии.


Материалы сайта являются авторскими. Копирование и публикация материалов без активной ссылки на первоисточник запрещено.

Реклама: