AVR. Учебный курс. МакроассемблерPrint This Post

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

Перед изучением системы команд микроконтроллера надо бы разобраться в инструментарии. Плох тот плотник который не знает свой топор. Основным инструментом у нас будет компилятор. У компилятора есть свой язык — макроассемблер, с помощью которого жизнь программиста упрощается в разы. Ведь гораздо проще писать и оперировать в голове командами типа MOV Counter,Default_Count вместо MOV R17,R16 и помнить что у нас R17 значит Counter, а R16 это Default_Count. Все подстановки с человеческого языка на машинный, а также многое другое делается средствами препроцессора компилятора. Его мы сейчас и рассмотрим.

Комментарии в тексте программы начинаются либо знаком “;“, либо двойными слешами “//“, а еще AVR Studio поддерживает Cишную нотацию комментариев, где коменты ограничены “колючей проволокой” /* коммент */.

Оператор .include позволяет подключать в тело твоей программы кусок кода из другого текстового файла. Что позволяет разбить большую исходник на кучу мелких, чтобы не загромождать и не мотать туда сюда огромную портянку кода. Считай куда ты воткнул .include туда и вставился кусок кода из другого файла. Если надо подключать не весь файл, а только его часть, то тебе поможет директива .exit дойдя до которой компилятор выйдет из файла.

Оператор .def позволяет привязать к любому слову любое значение из ресурсов контроллера — порт или регистр. Например сделал я счетчик, а считаемое значение находится в регистре R0, а в качестве регистра-помойки для промежуточных данных я заюзал R16. Чтобы не запутаться и помнить, что в каком регистре у меня задумано я присваиваю им через .def символические имена.

1
2
.def 	schetchik = R0
.def 	pomoika = R16

И теперь в коде могу смело использовать вместо официального имени R0 неофицальную кличку schetchik
Одному и тому же регистру можно давать кучу имен одновременно и на все он будет честно откликаться.

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

1
.undef 	pomoika

Оператор .equ это присвоение выражения или константы какой либо символической метке.
Например, у меня есть константа которая часто используется. Можно, конечно, каждый раз писать ее в коде, но вдруг окажется, что константа выбрана неверно, а значит придется весь код шерстить и везде править, а если где-нибудь забудешь, то получишь такую махровую багу, что задолбаешься потом ее вылавливать. Так что нафиг, все константы писать надо через
.equ! Кроме того, можно же присвоить не константу, а целое выражение. Которое при компиляции посчитается препроцессором, а в код пойдет уже исходное значение. Надо только учитывать, что деление тут исключительно целочисленное. С отбрасыванием дробной части, без какого-либо округления, а значит 1/2 = 0, а 5/2 = 2

1
2
3
.equ 	Time = 5
.equ 	Acсelerate = 4
.equ 	Half_Speed = (Accelerate*Time)/2

Директивы сегментации. Как я уже рассказывал в посте про архитектуру контроллера AVR память контроллера разбита на независимые сегменты - данные (ОЗУ), код (FLASH), EEPROM

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

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

В сегменте кода уместны директивы:
Адресная метка. Любое слово, не содержащее пробелов и не начинающееся с цифры, главное, чтобы после него стояло двоеточие.

1
2
3
.CSEG
label:   LDI      R16,'A'
         RJMP   label

В итоге, после компиляции вместо label в код подставится адрес команды перед которой стоит эта самая метка, в данном случае адрес команды LDI R16,’A’
Адресными метками можно адресовать не только код, но и данные, записанные в любом сегменте памяти. Об этом чуть ниже.

.ORG address означет примерно следующее “копать отсюда и до обеда”, т.е. до конца памяти. Данный оператор указывает с какого адреса пойдет собственно программа. Обычно используется для создания таблицы прерываний.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
	.CSEG
	.ORG 0x0000		
	RJMP Start  ;перепрыгиваем таблицу векторов.
 
	.ORG INT0addr 	; External Interrupt0 Vector Address
	RJMP INT0_expection
 
	.ORG INT1addr 	; External Interrupt1 Vector Address
	RETI
 
	.ORG OC2addr 	; Output Compare2 Interrupt Vector Address
	RJMP PWM_1
 
	.ORG OVF2addr 	; Overflow2 Interrupt Vector Address
	RETI
 
	.ORG ICP1addr 	;Input Capture1 Interrupt Vector Address
	RETI
 
	.ORG 0х0032 		; Начало основной программы
 
Start: 	LDI R16,0x54 	; и понеслась

Статичные данные пихаются в флеш посредством операторов

.db массив байтов.
.dw массив слов - два байта.
.dd массив двойных слов - четыре байта
.dq массив четверных слов - восем байт.

1
2
3
Constant:	.db  	10     ; или 0хAh в шестнадцатеричном коде 
Message:	.db  	"Привет лунатикам"
Words: 		.dw	10, 11, 12

В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов фразы “привет лунатикам”, а дальше 000A, 000B, 000С.
Последнии числа, хоть сами и невелики, но занимают по два байта каждое, так как обьявлены как .dw.

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

Тут действует оператор .BYTE позволяющий указать на расположение данных в памяти.

1
2
var1: 		.BYTE 1
table: 		.BYTE 10

В первом случае мы указали переменную var1 состоящую из одного байта.
Во втором случае у нас есть цепочка из 10 байт и переменная table указывающая на первый байт из цепочки. Адрес остальных вычисляется смещением.
Указывать размеры перменных нужно для того, чтобы компилятор их правильно адресовал и они не налезали друг на друга.

.EESEG сегмент EEPROM, энергонезависимая память. Можно писать, можно считывать, а при пропаже питания данные не повреждаются.
Тут действуют те же директивы что и в flash — db, dw, dd, dq.

MACRO - оператор макроподстановки. Вот уж реально чумовая вещь. Позволяет присваивать имена целым кускам кода, мало того, еще параметры задавать можно.

1
2
3
4
.MACRO SUBI16		; Start macro definition 
        subi @1,low(@0)	; Subtract low byte 
        sbci @2,high(@0)	; Subtract high byte 
.ENDM                       	; End macro definition

@0, @1, @2 это параметры макроса, они нумеруются тупо по порядку. А при вызове подставляются в код.

Вызов выглядит как обычная команда:

1
SUBI16 	0x1234,r16,r17

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

Макроассемблер это мощнейшая штука. По ходу пьесы я буду вводить разные макросы и показывать примеры работы макроопределений.

Комментарии

85 комментариев на «AVR. Учебный курс. Макроассемблер»


  1. Stebanoid 25 Сен 2008 4:38

    1. Не понял про директиву .ORG Можно поподробнее?
    2. В коде
    Message: .db “Привет инопланетные придурки
    по всей видимости упущена закрывающая кавычка.
    3. Не понтно про
    var1: .BYTE 1 - зачем это нужно? как можно применить?

    4. Самое главное :). Рискуя показаться не в меру наглым, прошу сообщить мне свой e-mail, что бы я вам надоедал всякими вопросами, которые не получается приткнуть ни в одной теме на этом сайте. Просто мне не у кого больше спросить.
    Если не хотите выкладывать свой адрес здесь, можно просто написать мне письмо. Мой адрес: мой_ник@gmail.com
    Обещаю сильно не надоедать. :)

    Stebanoid

    Мыло твоё уже нашёл. Буду писать. :)

    DI HALT

    директива ORG говорит компилятору - Копать отсюда и до конца.
    Т.е. у тебя память идет последовательно 0..1..2..3..4..5 и так до самого конца. Если ничего не указывать, то компилер команды забьет начиная с нуля. А если сказать, что у нас ORG 5 то уже будут записываться начиная с 5го адреса.

    Да, кавычку забыл. Надо будет поправить.

    Для того, чтобы у тебя в ОЗУ был зарезервирован байт и был адрес на него. А уж что с этим байтом ты будешь делать тебе решать. Я как переменные использую.

    Stebanoid

    А… с var1: .BYTE 1 понятно.
    Просто в обычном ассемблере х86 я под переменные использовал
    Name: .db 10, 156,34
    а здесь так делать нельзя, т.к. память раздельная… Упустил этот момент.


  2. skadi.exe 08 Янв 2009 1:09

    отправь куданибудь, где можно толково почитать про обычный ассемблер, т.к. подразумевается, что читатель его уже знает. Желательно попроще, сам 2 курс и знаком только с приплюснутым…

    DI HALT

    Обычный это какой?

    skadi.exe

    Хм, глупость сморозил. На николаев.орг нашел все. Буду разбираться, не шибко страшно вроде. :[

    skadi.exe

    Хм, глупость сморозил. На николаев.орг нашел все. Буду разбираться, не шибко страшно вроде. :[ а имелось ввиду основы, регистры и прочее, команды…

    skadi.exe

    WP Ajax Edit Comments - не работает почему-то ( пишет “загружаем” и все…


  3. ymn 31 Янв 2009 13:06

    Купил давеча ATmega16-16PU и чёт нигде не могу найти что означает маркировка 16PU…

    DI HALT

    16Мгц максимальная частота. Корпус PDip, коммерческий температурный диапазон (-5…40С)

    ymn

    Благодарю!


  4. testicq 10 Фев 2009 2:54

    не понятно с директивой .equ. вот кусок кода, который не работает должным образом:


    ;+Установка времени следующего срабатывания Timer0
    ; Параметры: @0 - число тиков, через которое сработает таймер
    ; Модифицирует: R16
    .MACRO st0of
    outi TCNT0, $FF-@0
    .ENDMACRO

    .equ XTAL = 8000000 ; Частота МК

    .equ t0_divider = 256 ; Предделитель timer0
    .equ impulse_time = 1200; ; Время в микросекундах
    .equ t0_ticks = (XTAL*impulse_time)/(t0_divider*1000000) ; Время в тиках

    st0of t0_ticks ; Задаем время до срабатывания (в тиках)

    если поставить

    st0of 37

    то все работает на ура

    DI HALT

    Гхм, может слишком много вложенных макросов?

    У тебя в макросе St0of макрос outi плюс сложная макроподстановка. Попробуй вынести FF-@0 в отдельный EQU

    DI HALT

    Да, кстати, результат вычисления какой получается? Целый?

    testicq

    неа. дробный, но мне целое число нужно. пофиг как округленное
    В принципе задал значение 1152 вместо 1200 чтобы при любых (>=2MHz) частотах XTAL был целый результат (хочу сделать универсально) - все заработало, но вопрос остался - Как макросредствами делать целочисленное деление?

    testicq

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

    .equ 	XTAL = 8000000		; Частота МК
     
    .equ	one_time = 1800; 	; Время проседания линии при логич. "1" (мс.)
    .equ	zero_time = 600; 	; Время проседания линии при логич. "0" (мс.)
    .equ	check_time = zero_time+(one_time-zero_time)/2; 	; Время проверки переданного бита (мс.)
     
    .equ	t0_divider = 256	; Предделитель timer0
    .equ	t0_check_ticks = XTAL/1000000*check_time/t0_divider ; Время в тиках
     
    outi	TCNT0, Low($FF-t0_check_ticks)
    DI HALT

    ХЗ. В Хелпе к студии есть описание макроязыка. По моему там получается как с типом int - т.е. дробная часть просто теряется.

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


  5. skywalker 11 марта 2009 16:30

    Че то я не совсем понял, чем отличаются директивы .def и .equ

    DI HALT

    equ означает что “Теперь вот это слово равно/эквивалентно этому числу и вместо числа может быть слово”
    def означает что “Теперь этот регистр можно обозвать еще и таким словом ”

    Регистру мы можем присвоить кучу разных имен и это не будет ошибкой (варнинг будет, но на него можно забить)

    А вот заэквивалентить одному слову несколько чисел уже нельзя (наоборот, разным словам одно число - запросто, без проблем).

    MorskoyZmey

    т.е. как дефайн в си

    .def OPA r16
    .def OPA r17

    А вот equ, это что-то вроде макро константы


  6. Ridik911 29 марта 2009 0:35

    а моджете прикрепить в конце статьи http://www.atmel.ru/Articles/Atmel11.htm для более глубоко ознакомления.. или не требуется?

    DI HALT

    В принципе, не проблема. Чуть позже добавлю.


  7. Ridik911 13 Апр 2009 22:45

    жду инфу про арифметические (ну и про логические операции) на асм
    и если можно то и комбинации арифметических операций например для вычисления синуса (макросом или процедурой\функцией?)
    спасибо

    DI HALT

    Функции в подавляющем большинстве случаев, проще задавать таблицей. Вычислять их - дикий расход памяти и быстродействия.

    Ridik911

    Вычислять их - дикий расход памяти и быстродействия.
    —-
    ну да.. в ряд тейлора (циклом) до необходимой точность.. это мучительно для проца и памяти МК

    задавать таблицей в томто и дело и неподходит…
    ладно раскужу что хочу

    хочеца сделать чтото типа мидиконтроллер - тоесть контроллер генерирующий звук при подключенной к нему некоторой клавиатуре… -но т.к. миди то нужно хранить семплы гдето например в ееprom- но памяти этой в МК будет очень мало (надо “допаивать” 24хх)
    тогда яже свел задачу к генерации.. формулами - это экономит ROM память но предпожил что процессор потянет вычисления.. я так понял из ваших слов непотянет..
    да и самое сложное в моей идее - это подбор формул легче семплами

    допустим выбрал метод семплы.. а вот нет идеи как их ээ микшировать\сумировать(когда нажаты две и более кнопки на клавиатуре)? (повидимум програмно но как.. прирывания - нет имхо, акакже)

    DI HALT

    Ну почему не потянет - потянет, только сожрет много ПЗУ.

    А что из себя представляет сэмпл?

    Ridik911

    впринципе как ты и сказал “проще задавать таблицей”
    впринципе семпл wav файл (а в реале миди семпл я незнаю, но возможно также)
    тоесть мидиконтроллером управляем семплами: изменить скорость воспроизведения(нота), продолжительность, громкость… и т.д.
    но семплы не мало занимают места как уже понятно…
    например если делать семплы wav то каждый будет весить гдето по 4-8КБ

    только сожрет много ПЗУ.
    —-
    может ОЗУ?

    кстати очееень жду статейку про подключение к МК озу
    для тех МК в которых это поддерживается (типа мега8515 ) и для тех кто не поддерживает но можно(если можно) реализовать програмно
    а также про dram (валяется планка сим.. думаю какбы примостырить к МК)

    Ridik911

    впринципе как ты и сказал “проще задавать таблицей”
    —-
    так и называют wavetable

    Agrin

    Можно использовать синтезаторы типа YM3812 или аналогичные. Синтезировать качественно многоканальный звук из сэмплов или функционально с помощью 8разрядного МК задача если и решимая, то с качеством звука как из китайских говорящих кукол.


  8. RMiSe 13 мая 2009 14:07

    Здравствуйте! DI HALT, не могли ли вы написать (может быть макрос) как из переменной table (table:.BYTE 10) считать побайтно символы?
    Заранее благодарен за ответ!

    DI HALT

    ПОбайтно? Хм.

    Проще вот так:
    LDS R16,Table ; первый байт
    LDS R16,Table+1 ; второй байт ну и так далее.

    Если надо программно выбирать значение, то тут есть другой подход.

    LDI XL,low(table)
    LDI XH,High(table)

    LD R16,X
    ; считать первый байт или

    LD R16,X+
    ;считать первый байт и после увеличить Х на 1

    Ну или вручную складывать регистровую пару Х с константой-смещением. Вариантов тьма. Что что, а инструментарий работы с ОЗУ у AVR очень богатый.


  9. VladislavZ 22 Окт 2009 1:13

    А можно по подробней об ОПЕРАТОРАХ .equ .def !? К примеру, возможно ли присвоить имя не регистру, а скажем порту или даже лучше конкретному пину порта!? Возможно, ли определенным ИМЕНАМ присвоить значение?!
    Очень хочется статейки по различным интерфейсам, и самое главное про библиотеки, и по подробней!

    DI HALT

    можно.

    def работает ТОЛЬКО с регистрами. А вот имена портов это не более чем equ под имя порта, все они прописаны в inc файле соответствующего мк.

    вот у меня обычно такая ботва:
    .def COK = R26
    .def SEDOK = R27
    .def Catch = R28
    .def Armed = R29

    .equ R_ON = 6
    .equ R_ON_P = PORTB
    .equ R_ON_D = DDRB

    .equ R_EN = 7
    .equ R_EN_P = PORTB
    .equ R_EN_D = DDRB

    VladislavZ

    То есть получается (.equ R_ON = 6) R_ON 6-ой пин неизвестного пора ; (.equ R_ON_P = PORTB) R_ON_P все пины портаВ. А совместить не как не получится? К примеру, я хочу управлять 4 пином ddrC, 5 пином ddrC по отдельности и каждому присвоить свое имя, допустим scl ddrC,4; sda ddrC,5 и потом уже в коде так

    Sbi sda
    Sbi scl
    Cbi sda
    Sbrc sda

    DI HALT

    Не, не получится.

    Только SBI IIC_PORT,SDA

    Но никто не мешает сделать тебе макрос, например:

    .MACRO SBI_SDA
    SBI IIC_PORT,SDA
    .ENDM

    И юзать в своей программе
    SBI_SDA как одну команду. Тока макросы тогда уже удобней звать, например так:

    SDA_H
    SDA_L


  10. Alex_Megavolt_79 21 Ноя 2009 2:27

    Di Halt, я не понял как кусок проги или всю её сделать как макрос и юзать. Например вот я не давно изучал динамическую индикацию, вот как всю её или часть забахать макрос и как задавать параметры то есть заюзать:

    ; Выполняемые функции: Динамическая индикация
    ;__________________________________________________________________________

    .device ATtiny2313
    .nolist
    .include “d:\avr\def\tn2313def.inc”
    .list

    ;=====================================================================
    ; Объявления:

    .def temp1=r1
    .def temp2=r2
    .def n_digit=r3
    .def counter=r4
    .def digit_out=r5
    .def n_point=r6
    .def temp=r16

    .equ crystal=6000000 ;частота кварца
    .equ Sciler=1024 ;предделитель таймера
    .equ N_TC0_2500uc=255-crystal/(Sciler*400);число тиков таймера ТС0 для организации
    ;задержи при динамической индикации
    .equ amount_digit=4 ;маска, нужно ввести количество разрядов индикатора
    ;в данном случае 4-х разрядный индикатор

    ;======================================================================
    ; Начало программы:

    .dseg

    Digit: .byte 4
    Point: .byte 1

    .cseg
    .org 0
    rjmp RESET ;Reset Handler
    nop ;rjmp INT0 ;External Interrupt0
    nop ;rjmp INT1 ;External Interrupt1
    nop ;rjmp ICP1 ;Input capture interrupt 1
    nop ;rjmp OC1A ;Timer/Counter1 Compare Match A
    nop ;rjmp OVF1 ;Overflow1 Interrupt
    rjmp OVF0 ;Overflow0 Interrupt
    nop ;rjmp URXC0 ;USART0 RX Complete Interrupt
    nop ;rjmp UDRE0 ;USART0 Data Register Empty Interrupt
    nop ;rjmp UTXC0 ;USART0 TX Complete Interrupt
    nop ;rjmp ACI ;Analog Comparator Interrupt
    nop ;rjmp PCINT ;Pin Change Interrupt
    nop ;rjmp OC1B ;Timer/Counter1 Compare Match B
    nop ;rjmp OC0A ;Timer/Counter0 Compare Match A
    nop ;rjmp OC0B ;Timer/Counter0 Compare Match B
    nop ;rjmp USI_START ;USI start interrupt
    nop ;rjmp USI_OVF ;USI overflow interrupt
    nop ;rjmp ERDY ;EEPROM write complete
    nop ;rjmp WDT ;Watchdog Timer Interrupt
    ; for compatibility purpose

    ;======================================================================
    ; Инициализация

    RESET:
    ldi temp,low (RAMEND) ;Определение начала стека
    out SPL,temp

    ldi temp,0b0001111 ;Конфигурирование портов
    out DDRD,temp ;Порт D вход\выход
    ser temp
    out DDRB,temp ;Порт B выход
    out PIND,temp ;Команда для отладчика
    out PORTD,temp ;Включаем подтягивающие резисторы
    out PORTB,temp ;Выключаем матрицу

    ldi temp,15 ;Инициализация сторожевого таймера
    out WDTCR,temp
    rcall TNCT_0 ;Инициализация таймера ТС0

    ;======================================================================
    ; для проверки работы индикатора выводит на индикатор число 4321
    ; запятая во втором знакоместе
    ldi Temp,4
    sts Digit ,Temp ;загрузка начальных значений число 4321
    ldi Temp,3
    sts Digit+1,Temp
    ldi Temp,2
    sts Digit+2,Temp
    ldi Temp,1
    sts Digit+3,Temp

    ldi Temp,1
    sts Point,Temp ;загрузка номера разряда запятой 2-й разряд
    clr counter

    ;======================================================================
    ; Основной блок программы

    main:

    wdr ;”Бросим кость собаке”

    rjmp main ;Начинаем всё сначала

    ;=======================================================================
    ; Инициализация таймеров

    TNCT_0:
    cli ;запрещаем прерывания
    ldi temp,N_TC0_2500uc ; инициализируем таймер Т\С0 на Т=0,0025 с
    out TCNT0,temp
    ldi temp,0b00000000
    out TCCR0A,temp
    ldi temp,(1<<CS02)+(1<<CS00) ;прескайлер таймера 1024
    ;ldi temp,(1<<CS00) ;прескайлер таймера 1 для отладки
    out TCCR0B,temp
    ldi temp,(1<<TOIE0) ;прерывания Т\С0 по переполнению
    out TIMSK,temp
    sei
    reti

    ;========================================================================
    ; Обработчик прерываний

    OVF0:
    rcall TNCT_0 ;Инициализация таймера ТС0
    ldi ZL,Low(Digit) ;инициализация массива
    ldi ZH,High(Digit)
    clr temp2
    add ZL,counter ;выбираем нужный номер ячейки
    adc ZH,temp2
    ld digit_out,Z ;загружаем ячейку
    lds n_point,Point
    rcall Decoder_n_digit
    rcall Decoder
    out PORTD,n_digit
    out PORTB,digit_out
    inc counter ;увеличиваем номер разряда
    mov temp,counter
    andi temp,(amount_digit-1) ;отсекаем количество разрядов по маске
    mov counter,temp
    reti

    ;=========================================================================
    ; Подпрограммы
    Decoder:
    ;преобразует десятичную цифру разряда в 7-ми сегментный код индикатора

    clr temp
    ldi ZL,Low(DcMatrix*2) ;инициализация массива перекодировки
    ldi ZH,High(DcMatrix*2)
    clr Temp2 ;прибавление переменной
    add ZL,digit_out ;к 0-му адресу массива
    adc ZH,Temp2
    lpm ;загрузка значения 7-ми сегментного кода индикатора
    mov digit_out,r0
    clr temp
    cp n_point,temp ;проверяем номер разряда зяпятой если
    breq PC+6 ;если он равен 0 то выходим из подрограммы
    cp counter,n_point ;если он не равен нулю сравниваем с
    brne PC+4 ;номером разряда вкл в данный момент
    mov temp,digit_out
    andi temp,0b01111111 ;если равен зажигаем зяпятую иначе
    mov digit_out,temp
    ret ;выходим из подпрограммы

    DcMatrix:
    ;массив - таблица истинности декодера
    ; hgfedcba hgfedcba
    .db 0b11000000,0b11111001 ;0;1
    .db 0b10100100,0b10110000 ;2;3
    .db 0b10011001,0b10010010 ;4;5
    .db 0b10000010,0b11111000 ;6;7
    .db 0b10000000,0b10010000 ;8;9

    Decoder_n_digit:
    ldi ZL,Low(DnMatrix*2) ;инициализация массива
    ldi ZH,High(DnMatrix*2)
    clr Temp2 ;прибавление переменной
    add ZL,counter ;к 0-му адресу массива
    adc ZH,Temp2
    lpm ;загрузка значения
    mov n_digit,r0
    ret

    DnMatrix:
    ;массив - таблица истинности декодера разряда

    .db 0b11111110,0b11111101 ;0;1
    .db 0b11111011,0b11110111 ;2;3

    DI HALT

    Всю прогу макросом нельзя. Да и ни к чему это.

    А небольшие кусочки - прочитай статью еще раз.


  11. Flint 02 Дек 2009 23:18

    Вопрос по .DSEG
    Если взять структуру исходных файлов, как, например, в прогах ADCsoftFilter, UARTundSoftADC, в каком месте нужно её указывать, или это все равно - компилятор сам разберётся?
    “var1: .BYTE 1″ А как задать 2-х байтную переменную в ОЗУ, var1: .BYTE так?
    И отсюда же следующий вопрос - как задать массив 2-х байтных чисел в ОЗУ. Может быть надо уазать var1: .BYTE 8 - это массив из 4-х двухбайтовых значений, правильно? Ну и соответственно, работать с ними, подразумевая, что каждый следующий элемент идёт через 2 байта.

    DI HALT

    DSEG везде где не CSEG :) Я обычно перед CSEG ставлю. Сразу после инклюдников обычно.

    вроде бы .WORD можно определить, не помню уже. ПОзырь в описаниях макроязыка.

    Flint

    Собственно, я поставил в vectors.asm, но тоже получается перед .CSEG. Надо мне будет, наверное, завести отдельный фалик только с переменными ОЗУ и записать их все туда :) И подключить includе’ом в нужном месте

    Flint

    Кстати, в данном конкретном примере “Сразу после инклюдников” получится уже после .CSEG. Ну это так, к слову.


  12. Flint 02 Дек 2009 23:25

    А как задать 2-х байтную переменную в ОЗУ, var1: .BYTE 2 - опечатался
    А запись var1: .BYTE 8 я имею ввиду рассматривать как “массив из 4-х двухбайтовых значений”, хотя ассемблеру, возможно, мои мысли и неведомы и он подразумевает просто последовательность данных в ОЗУ

    DI HALT

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

    Flint

    Инициализировать-то потом не проблема. Главное правильно мне их задать. А “адреса придется вручную считать” это в смысле подразумевается 1-и 2-х байтовый элемент - это адрес 0-го + 2 ? Ну это тоже не очень большая проблема. Ведь в программе же я оперирую символьным имененм, и следующий элемент массива находится по вышеприведённому способу исходя из размера элемента массива.


  13. tarasadidas 09 Дек 2009 12:36

    И опять вопрос по оператору (.BYTE) .
    Если мы выделили массив с меткой Variablе:
    как установить указатель допустим Z на метку массива

    ldi Zh,high(Variablе)
    ldi Zh,low(Variablе)
    а считывать массив
    st Z+,r16
    st Z+,r17
    и т.д.
    Или я ошибаюсь?

    DI HALT

    Именно так, только

    ldi ZH,high(Variablе)
    ldi ZL,low(Variablе)

    А чтение это не st (это сохранение), а ld


  14. tarasadidas 09 Дек 2009 14:32

    DI HALT Спасибо ошибки замелил сам но отредактировать не смог не поддерживается у вас чтоли.
    Но самое главное суть понял а позже буду внимательней уж извини.


  15. tarasadidas 11 Дек 2009 12:33

    И еще вопрос по .include .Вставлять я так понял этот оператор мона где угодно хоть посреди кода а помещать в прикрепляемый файл как куски того же кода так и подпрограммы (даже обработчики прерывания).
    И еще расширение у файла должно быть асм. или произвольное и помещать его нуна в папку текущего проекта?
    Я так понимаю что любой отлаженный кусок кода (инициализация портов) или подпрограмму просто вырезаем и сохраняем как отдельный файл с расширением асм. в папке проекта
    а в том месте пишем .include имя файла.asm или я не прав?

    DI HALT

    Да. Расишерие может быть любым. Главное чтобы это был текстовый файл. Быть он может тоже где угодно, но длинные пути и пути с русскими буквами плохо отрабатываются.


  16. tarasadidas 11 Дек 2009 17:48

    Проверил расширение только asm катит.Иначе студия матерится.


  17. Spok 02 Фев 2010 13:35

    Я могу ошибаться, но в статье косяк: надо не .EESEG, а .ESEG.


  18. Flint 17 Фев 2010 21:28

    Не пойму, проект на основе Atmega48, при компиляции запнулось повидимому на макросе OUTI с такой ошибкой: macro.asm(6): error: Operand 1 out of range: 0xc4 И указывает на вторую строчку макроса, т.е. OUT @0,R16. И еще куча подобных ошибок, где этот макрос встречается. Мой проект http://slil.ru/28665386

    DI HALT

    Да, все верно. Открывай файл m48def.inc и смотри карту ио регистров. Те из них что помечены как memory mapped недоступны для команды OUT|IN которая используется в макросе OUTI. Загрузка/выгрузка в них идет как в ячейку памяти по командам STS/LDS

    т.е. меняем OUT на STS и все работает.
    Сделай еще один макрос OUTIm такого вида и юзай его для мемори маппед регистров.


  19. ZhekSooN 21 марта 2010 19:01

    Опа, никто не упомянул о таких дьявольских штуках, как .if\.endif, .ifdef\.endif :)
    С их помощью можно создать почти свой язык программирования :)

    DI HALT

    Точно. Совсем про них забыл. Надо добавить. Кстати, а AVRassebler1 их поддерживает? В доке про них нет ни слова.

    ZhekSooN

    Все поддерживает, искать надо лучше ;) Help - AVR Tool User guide - AVR assembler - User’s guide - Directives. сам ведь не знал о их оффициальном существовании! О_о

    DI HALT

    Охрененно. Вот жеж надо было закопать О_о а я ведь там два раза ходил и не заметил.

    auara

    Общие макросы переношу из проекта в проект и подключаю через:
    .include “MACROS.MAC”

    (кстати, кто-то жаловался что только расширение “.asm”)

    Вот примерчик, работающий на разных AVR8

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    ;***************************************************************************
    .macro	mEORBi		;XOR Bit in register I/O
    .IF @0<64
    	IN	rM,@0
    	EOR	rM,@1
    	OUT	@0,rM
    .ELSE
    	LDS	rM,@0
    	EOR	rM,@1
    	STS	@0,rM
    .ENDIF
    .endmacro
    ;;--------------------	mEORBi	regIO,reg
    ;;--------- Ex: ------	mEORBi	TIFR,R17

  20. ZhekSooN 22 марта 2010 18:17

    Насчет первой версии не знаю, но во второй всё охрененно пашет. Я рад: создал скрипты инициализации таймеров, USART, ADC и других нудных вещей - и не паришся!
    Это как Сишная вставка посреди ассемблерного поля :)


  21. A_ndrej 22 марта 2010 23:11

    Добрый день.
    Подскажите пожалуЙста, можно ли в макросе, в качестве параметра”@” ставить ссылку на подпрограму?
    Например:
    ,MACRO CALC_1
    ldi @1, @2
    cp r16, @2
    BREQ @3

    И тогда при вызове макроса написать:
    CALC_1 r17,r18,METKA
    где “METKA” будет являтся той ссылкой, на которую с макроса пойдет контроллер.

    DI HALT

    Думаю проблем не составит. Макропроцессору то без проблем что подсунуть.


  22. A_ndrej 22 марта 2010 23:36

    Большое спасибо.


  23. dima_m 26 марта 2010 0:13

    Сейчас пробовал макрос забабахать. Супер, все работает. Реально удобно. Уже начал использовать в деле. Только вот хочется узнать, откуда у DIHALTA слева в редакторе появился столбец с цифрами, то есть нумерация строк. У меня в AVR STUDIO нет такой фишки. Не подскажете кто, как такую себе сделать?

    DI HALT

    У меня тоже нет, а с чего ты взял, что у меня в АВР студио есть нумерация?

    dima_m

    Ну вот, когда ты вверху показываешь пример кода. У тебя в редакторе слева есть нумерация строк. Да и сам редактор привлекательный. Попривлекательнее чем в AVR. Просто думал ты какой нибудь плагин доставил к проге. Значит это не AVR STUDIO. А что же это за редактор?

    DI HALT

    Это Wordpress syntax hilighter - плагин к движку сайта который сам код подкрашивает.

    dima_m

    OK! Понятно.

    auara

    Мы тоже хотим красиво в коментах писать,
    а то сваливается все в кучу….
    И можно-ли редактировать свое сообщение?


  24. bdp 23 Апр 2010 1:46

    DI HALT в придложении
    >В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов
    >фразы “привет лунатикам”, а дальше 000A, 000B, 000F.
    >Последнии числа, хоть сами и невелики, но занимают по два байта каждое, так как .dw.
    несостыковка с примером идущим сверху в котором нет описи “dw”.

    Это при условии что я ничего не пропустил :)

    DI HALT

    ой точно куда то кусок примера отвалился :) Спасибо щас поправлю

    auara

    пока не видно


  25. auara 24 Апр 2010 4:45

    Еще упущена “.SET” (про “.IF”, “.IFDEF”, “.IFNDEF” уже вспоминалось)

    Вот кусок из программы:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    |.set uD=$00
    |.set uB=$00
    |;————————
    |.IF cInpMax >= $0C
    | .IFNDEF cInp1_Port or cInp1_Pin
    | .ERROR “Not defined cInp1_Port or cInp1_Pin”
    | .ENDIF
    | .set U=$FF-(1<<cInp1_Pin)
    | .if cInp1_Port == PortD
    | .db uD&U, U
    | .db uB, $FF
    | .else
    | .db uD, $FF
    | .db uB&U, U
    | .endif
    |.ENDIF
    |
    auara

    ну Блин!
    Уже и вертикальную черточку поставил, чтоб не сплюскивало, так автоматика сайта пробелы сократила. Компьютер и так прохавает, а я людям для наглядности форматировал. Для восприятия лучше…
    - - - - - - - - - - - -
    Может программка есть какая?
    Написал в ней, отформатировал, сюда вставил и все красиво.
    А если нету - то надо на сайте что-то менять.
    Ну напрягает… тексты программ в комментариях ко всем статьям - просто каша.
    я то разберусь, но начинающие могут упустить(не понять) много полезного.

    auara

    Глянул на местный форум — ситуация та же… код-лист == каша.
    Даааа уж… Видимо не все так просто.
    Удивительно! Обсуждения концепций OS и каменный век — рядом!

    DI HALT

    Я не хтмл программист. А готовый плагин который бы мог это делать я не нашел.

    auara

    Спасибо, вижу ты облагородил сообщение “апреля 24, 2010 at 4:26″
    Еще и в “24 Апр 2010 4:45″ желательно убрать передующие “|” и сдвиги добавить. Плиз…

    DI HALT

    лень

    auara

    Я думал ты сидишь на готовом движке сайта. А если все сам…
    Ну может кто поможет/подскажет DI HALT-у доработать/переработать обработку сообщений на сайте, а то и сайт перемастырить.
    Ему над уроками надо мозговать, а он тратит время на редактирование наших текстов.

    DI HALT

    На готовом и сижу. Потому и не могу взять и что то там подкрутить если этого нет в настройках.


  26. auara 25 Апр 2010 4:11

    После установки AvrStudio4, запустить файлик помощи:
    AVRTools\Help\AVRASM.chm
    Там в закладке “Содержание” выбираем (раскрываем)
    -> “AVR Assembler” -> “User’s Guide” -> “Directives”
    И вот здесь, в разделе “Assembler directives”, внимательно рассматриваем/изучаем директивы и примерчики (как раз по нашему учебному курсу).


  27. dima_m 08 июня 2010 12:04

    DI HALT привет. А что означает выражение: макрос - лучше оформить в виде процедуры или функции? У тебя на сайте это где-то объясняется? Я бы почитал.

    DI HALT

    Имеется в виду, что макрос в этом случае использовать не стоит, а лучше сразу написать процедуру и вставить ее в код.


  28. skywalker 13 июля 2010 19:33

    Constant: .db 10 ; или 0хAh в шестнадцатеричном коде
    Message: .db “Привет лунатикам”
    Words: .dw 10, 11, 12

    В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов фразы “привет лунатикам”, а дальше 000A, 000B, 000F.

    А разве последняя строчка не равна 000A,000B,000C?

    DI HALT

    ОПечатка, конечно же 000с

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

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


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

Реклама: