Перед изучением системы команд микроконтроллера надо бы разобраться в инструментарии. Плох тот плотник который не знает свой топор. Основным инструментом у нас будет компилятор. У компилятора есть свой язык — макроассемблер, с помощью которого жизнь программиста упрощается в разы. Ведь гораздо проще писать и оперировать в голове командами типа 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 |
После имени через запятую передаются параметры, которые подставятся в код.
Макросы позволяют насоздавать себе удобных команд на все случаи жизни, по сути создать свой язык. Но надо помнить, что каждый макрос это тупо кусок кода, поэтому если макрос получается большой, то его лучше оформить в виде процедуры или функции — будет резкая экономия места в памяти, но выполняться будет чуток медленней.
Макроассемблер это мощнейшая штука. По ходу пьесы я буду вводить разные макросы и показывать примеры работы макроопределений.





1. Не понял про директиву .ORG Можно поподробнее?
2. В коде
Message: .db «Привет инопланетные придурки
по всей видимости упущена закрывающая кавычка.
3. Не понтно про
var1: .BYTE 1 — зачем это нужно? как можно применить?
4. Самое главное :). Рискуя показаться не в меру наглым, прошу сообщить мне свой e-mail, что бы я вам надоедал всякими вопросами, которые не получается приткнуть ни в одной теме на этом сайте. Просто мне не у кого больше спросить.
Если не хотите выкладывать свой адрес здесь, можно просто написать мне письмо. Мой адрес: мой_ник@gmail.com
Обещаю сильно не надоедать. :)
Мыло твоё уже нашёл. Буду писать. :)
директива ORG говорит компилятору — Копать отсюда и до конца.
Т.е. у тебя память идет последовательно 0..1..2..3..4..5 и так до самого конца. Если ничего не указывать, то компилер команды забьет начиная с нуля. А если сказать, что у нас ORG 5 то уже будут записываться начиная с 5го адреса.
Да, кавычку забыл. Надо будет поправить.
Для того, чтобы у тебя в ОЗУ был зарезервирован байт и был адрес на него. А уж что с этим байтом ты будешь делать тебе решать. Я как переменные использую.
А… с var1: .BYTE 1 понятно.
Просто в обычном ассемблере х86 я под переменные использовал
Name: .db 10, 156,34
а здесь так делать нельзя, т.к. память раздельная… Упустил этот момент.
отправь куданибудь, где можно толково почитать про обычный ассемблер, т.к. подразумевается, что читатель его уже знает. Желательно попроще, сам 2 курс и знаком только с приплюснутым…
Обычный это какой?
Хм, глупость сморозил. На николаев.орг нашел все. Буду разбираться, не шибко страшно вроде. :[
Хм, глупость сморозил. На николаев.орг нашел все. Буду разбираться, не шибко страшно вроде. :[ а имелось ввиду основы, регистры и прочее, команды…
WP Ajax Edit Comments — не работает почему-то ( пишет «загружаем» и все…
Купил давеча ATmega16-16PU и чёт нигде не могу найти что означает маркировка 16PU…
16Мгц максимальная частота. Корпус PDip, коммерческий температурный диапазон (-5…40С)
Благодарю!
не понятно с директивой .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
то все работает на ура
Гхм, может слишком много вложенных макросов?
У тебя в макросе St0of макрос outi плюс сложная макроподстановка. Попробуй вынести FF-@0 в отдельный EQU
Да, кстати, результат вычисления какой получается? Целый?
неа. дробный, но мне целое число нужно. пофиг как округленное
В принципе задал значение 1152 вместо 1200 чтобы при любых (>=2MHz) частотах XTAL был целый результат (хочу сделать универсально) — все заработало, но вопрос остался — Как макросредствами делать целочисленное деление?
Странно, но вот такой код работает на ура даже с учетом того, что дробное значение получается
ХЗ. В Хелпе к студии есть описание макроязыка. По моему там получается как с типом int — т.е. дробная часть просто теряется.
А ну так тут ты явно привел все к однобайтному результату взяв сразу Low байт и все дела. А там ты пытался напрямую сложить в однобайтный регистр двубайтное (или более) число, вот компилятор и не понял, что ты пытался ему скормить.
Че то я не совсем понял, чем отличаются директивы .def и .equ
equ означает что «Теперь вот это слово равно/эквивалентно этому числу и вместо числа может быть слово»
def означает что «Теперь этот регистр можно обозвать еще и таким словом »
Регистру мы можем присвоить кучу разных имен и это не будет ошибкой (варнинг будет, но на него можно забить)
А вот заэквивалентить одному слову несколько чисел уже нельзя (наоборот, разным словам одно число — запросто, без проблем).
т.е. как дефайн в си
.def OPA r16
.def OPA r17
А вот equ, это что-то вроде макро константы
а моджете прикрепить в конце статьи http://www.atmel.ru/Articles/Atmel11.htm для более глубоко ознакомления.. или не требуется?
В принципе, не проблема. Чуть позже добавлю.
жду инфу про арифметические (ну и про логические операции) на асм
и если можно то и комбинации арифметических операций например для вычисления синуса (макросом или процедурой\функцией?)
спасибо
Функции в подавляющем большинстве случаев, проще задавать таблицей. Вычислять их — дикий расход памяти и быстродействия.
Вычислять их — дикий расход памяти и быстродействия.
—-
ну да.. в ряд тейлора (циклом) до необходимой точность.. это мучительно для проца и памяти МК
задавать таблицей в томто и дело и неподходит…
ладно раскужу что хочу
хочеца сделать чтото типа мидиконтроллер — тоесть контроллер генерирующий звук при подключенной к нему некоторой клавиатуре… -но т.к. миди то нужно хранить семплы гдето например в ееprom- но памяти этой в МК будет очень мало (надо «допаивать» 24хх)
тогда яже свел задачу к генерации.. формулами — это экономит ROM память но предпожил что процессор потянет вычисления.. я так понял из ваших слов непотянет..
да и самое сложное в моей идее — это подбор формул легче семплами
допустим выбрал метод семплы.. а вот нет идеи как их ээ микшировать\сумировать(когда нажаты две и более кнопки на клавиатуре)? (повидимум програмно но как.. прирывания — нет имхо, акакже)
Ну почему не потянет — потянет, только сожрет много ПЗУ.
А что из себя представляет сэмпл?
впринципе как ты и сказал «проще задавать таблицей»
впринципе семпл wav файл (а в реале миди семпл я незнаю, но возможно также)
тоесть мидиконтроллером управляем семплами: изменить скорость воспроизведения(нота), продолжительность, громкость… и т.д.
но семплы не мало занимают места как уже понятно…
например если делать семплы wav то каждый будет весить гдето по 4-8КБ
только сожрет много ПЗУ.
—-
может ОЗУ?
кстати очееень жду статейку про подключение к МК озу
для тех МК в которых это поддерживается (типа мега8515 ) и для тех кто не поддерживает но можно(если можно) реализовать програмно
а также про dram (валяется планка сим.. думаю какбы примостырить к МК)
впринципе как ты и сказал «проще задавать таблицей»
—-
так и называют wavetable
Можно использовать синтезаторы типа YM3812 или аналогичные. Синтезировать качественно многоканальный звук из сэмплов или функционально с помощью 8разрядного МК задача если и решимая, то с качеством звука как из китайских говорящих кукол.
Здравствуйте! DI HALT, не могли ли вы написать (может быть макрос) как из переменной table (table:.BYTE 10) считать побайтно символы?
Заранее благодарен за ответ!
ПОбайтно? Хм.
Проще вот так:
LDS R16,Table ; первый байт
LDS R16,Table+1 ; второй байт ну и так далее.
Если надо программно выбирать значение, то тут есть другой подход.
LDI XL,low(table)
LDI XH,High(table)
LD R16,X
; считать первый байт или
LD R16,X+
;считать первый байт и после увеличить Х на 1
Ну или вручную складывать регистровую пару Х с константой-смещением. Вариантов тьма. Что что, а инструментарий работы с ОЗУ у AVR очень богатый.
А можно по подробней об ОПЕРАТОРАХ .equ .def !? К примеру, возможно ли присвоить имя не регистру, а скажем порту или даже лучше конкретному пину порта!? Возможно, ли определенным ИМЕНАМ присвоить значение?!
Очень хочется статейки по различным интерфейсам, и самое главное про библиотеки, и по подробней!
можно.
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
То есть получается (.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
Не, не получится.
Только SBI IIC_PORT,SDA
Но никто не мешает сделать тебе макрос, например:
.MACRO SBI_SDA
SBI IIC_PORT,SDA
.ENDM
И юзать в своей программе
SBI_SDA как одну команду. Тока макросы тогда уже удобней звать, например так:
SDA_H
SDA_L
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
Всю прогу макросом нельзя. Да и ни к чему это.
А небольшие кусочки — прочитай статью еще раз.
Вопрос по .DSEG
Если взять структуру исходных файлов, как, например, в прогах ADCsoftFilter, UARTundSoftADC, в каком месте нужно её указывать, или это все равно — компилятор сам разберётся?
«var1: .BYTE 1″ А как задать 2-х байтную переменную в ОЗУ, var1: .BYTE так?
И отсюда же следующий вопрос — как задать массив 2-х байтных чисел в ОЗУ. Может быть надо уазать var1: .BYTE 8 — это массив из 4-х двухбайтовых значений, правильно? Ну и соответственно, работать с ними, подразумевая, что каждый следующий элемент идёт через 2 байта.
DSEG везде где не CSEG :) Я обычно перед CSEG ставлю. Сразу после инклюдников обычно.
вроде бы .WORD можно определить, не помню уже. ПОзырь в описаниях макроязыка.
Собственно, я поставил в vectors.asm, но тоже получается перед .CSEG. Надо мне будет, наверное, завести отдельный фалик только с переменными ОЗУ и записать их все туда :) И подключить includе’ом в нужном месте
Кстати, в данном конкретном примере «Сразу после инклюдников» получится уже после .CSEG. Ну это так, к слову.
А как задать 2-х байтную переменную в ОЗУ, var1: .BYTE 2 — опечатался
А запись var1: .BYTE 8 я имею ввиду рассматривать как «массив из 4-х двухбайтовых значений», хотя ассемблеру, возможно, мои мысли и неведомы и он подразумевает просто последовательность данных в ОЗУ
А нет, вру. это не из той оперы. Тут только отожрать нужное количество байт. ВСе равно ты не можешь их инициализировать на уровне компилятора. Так что пофиг чем ты их задаешь, также тебе адреса придется вручную считать для каждого элемента массива.
Инициализировать-то потом не проблема. Главное правильно мне их задать. А «адреса придется вручную считать» это в смысле подразумевается 1-и 2-х байтовый элемент — это адрес 0-го + 2 ? Ну это тоже не очень большая проблема. Ведь в программе же я оперирую символьным имененм, и следующий элемент массива находится по вышеприведённому способу исходя из размера элемента массива.
И опять вопрос по оператору (.BYTE) .
Если мы выделили массив с меткой Variablе:
как установить указатель допустим Z на метку массива
ldi Zh,high(Variablе)
ldi Zh,low(Variablе)
а считывать массив
st Z+,r16
st Z+,r17
и т.д.
Или я ошибаюсь?
Именно так, только
ldi ZH,high(Variablе)
ldi ZL,low(Variablе)
А чтение это не st (это сохранение), а ld
DI HALT Спасибо ошибки замелил сам но отредактировать не смог не поддерживается у вас чтоли.
Но самое главное суть понял а позже буду внимательней уж извини.
И еще вопрос по .include .Вставлять я так понял этот оператор мона где угодно хоть посреди кода а помещать в прикрепляемый файл как куски того же кода так и подпрограммы (даже обработчики прерывания).
И еще расширение у файла должно быть асм. или произвольное и помещать его нуна в папку текущего проекта?
Я так понимаю что любой отлаженный кусок кода (инициализация портов) или подпрограмму просто вырезаем и сохраняем как отдельный файл с расширением асм. в папке проекта
а в том месте пишем .include имя файла.asm или я не прав?
Да. Расишерие может быть любым. Главное чтобы это был текстовый файл. Быть он может тоже где угодно, но длинные пути и пути с русскими буквами плохо отрабатываются.
Проверил расширение только asm катит.Иначе студия матерится.
Я могу ошибаться, но в статье косяк: надо не .EESEG, а .ESEG.
Не пойму, проект на основе Atmega48, при компиляции запнулось повидимому на макросе OUTI с такой ошибкой: macro.asm(6): error: Operand 1 out of range: 0xc4 И указывает на вторую строчку макроса, т.е. OUT @0,R16. И еще куча подобных ошибок, где этот макрос встречается. Мой проект http://slil.ru/28665386
Да, все верно. Открывай файл m48def.inc и смотри карту ио регистров. Те из них что помечены как memory mapped недоступны для команды OUT|IN которая используется в макросе OUTI. Загрузка/выгрузка в них идет как в ячейку памяти по командам STS/LDS
т.е. меняем OUT на STS и все работает.
Сделай еще один макрос OUTIm такого вида и юзай его для мемори маппед регистров.
Опа, никто не упомянул о таких дьявольских штуках, как .if\.endif, .ifdef\.endif :)
С их помощью можно создать почти свой язык программирования :)
Точно. Совсем про них забыл. Надо добавить. Кстати, а AVRassebler1 их поддерживает? В доке про них нет ни слова.
Все поддерживает, искать надо лучше ;) Help — AVR Tool User guide — AVR assembler — User’s guide — Directives. сам ведь не знал о их оффициальном существовании! О_о
Охрененно. Вот жеж надо было закопать О_о а я ведь там два раза ходил и не заметил.
Общие макросы переношу из проекта в проект и подключаю через:
.include «MACROS.MAC»
(кстати, кто-то жаловался что только расширение «.asm»)
Вот примерчик, работающий на разных AVR8
Насчет первой версии не знаю, но во второй всё охрененно пашет. Я рад: создал скрипты инициализации таймеров, USART, ADC и других нудных вещей — и не паришся!
Это как Сишная вставка посреди ассемблерного поля :)
Добрый день.
Подскажите пожалуЙста, можно ли в макросе, в качестве параметра»@» ставить ссылку на подпрограму?
Например:
,MACRO CALC_1
ldi @1, @2
cp r16, @2
BREQ @3
И тогда при вызове макроса написать:
CALC_1 r17,r18,METKA
где «METKA» будет являтся той ссылкой, на которую с макроса пойдет контроллер.
Думаю проблем не составит. Макропроцессору то без проблем что подсунуть.
Большое спасибо.
Сейчас пробовал макрос забабахать. Супер, все работает. Реально удобно. Уже начал использовать в деле. Только вот хочется узнать, откуда у DIHALTA слева в редакторе появился столбец с цифрами, то есть нумерация строк. У меня в AVR STUDIO нет такой фишки. Не подскажете кто, как такую себе сделать?
У меня тоже нет, а с чего ты взял, что у меня в АВР студио есть нумерация?
Ну вот, когда ты вверху показываешь пример кода. У тебя в редакторе слева есть нумерация строк. Да и сам редактор привлекательный. Попривлекательнее чем в AVR. Просто думал ты какой нибудь плагин доставил к проге. Значит это не AVR STUDIO. А что же это за редактор?
Это WordPress syntax hilighter — плагин к движку сайта который сам код подкрашивает.
OK! Понятно.
Мы тоже хотим красиво в коментах писать,
а то сваливается все в кучу….
И можно-ли редактировать свое сообщение?
DI HALT в придложении
>В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов
>фразы “привет лунатикам”, а дальше 000A, 000B, 000F.
>Последнии числа, хоть сами и невелики, но занимают по два байта каждое, так как .dw.
несостыковка с примером идущим сверху в котором нет описи «dw».
Это при условии что я ничего не пропустил :)
ой точно куда то кусок примера отвалился :) Спасибо щас поправлю
пока не видно
Еще упущена «.SET» (про «.IF», «.IFDEF», «.IFNDEF» уже вспоминалось)
Вот кусок из программы:
ну Блин!
Уже и вертикальную черточку поставил, чтоб не сплюскивало, так автоматика сайта пробелы сократила. Компьютер и так прохавает, а я людям для наглядности форматировал. Для восприятия лучше…
- — — — — — — — — — — -
Может программка есть какая?
Написал в ней, отформатировал, сюда вставил и все красиво.
А если нету — то надо на сайте что-то менять.
Ну напрягает… тексты программ в комментариях ко всем статьям — просто каша.
я то разберусь, но начинающие могут упустить(не понять) много полезного.
Глянул на местный форум — ситуация та же… код-лист == каша.
Даааа уж… Видимо не все так просто.
Удивительно! Обсуждения концепций OS и каменный век — рядом!
Я не хтмл программист. А готовый плагин который бы мог это делать я не нашел.
Спасибо, вижу ты облагородил сообщение «апреля 24, 2010 at 4:26″
Еще и в «24 Апр 2010 4:45″ желательно убрать передующие «|» и сдвиги добавить. Плиз…
лень
Я думал ты сидишь на готовом движке сайта. А если все сам…
Ну может кто поможет/подскажет DI HALT-у доработать/переработать обработку сообщений на сайте, а то и сайт перемастырить.
Ему над уроками надо мозговать, а он тратит время на редактирование наших текстов.
На готовом и сижу. Потому и не могу взять и что то там подкрутить если этого нет в настройках.
После установки AvrStudio4, запустить файлик помощи:
AVRTools\Help\AVRASM.chm
Там в закладке «Содержание» выбираем (раскрываем)
-> «AVR Assembler» -> «User’s Guide» -> «Directives»
И вот здесь, в разделе «Assembler directives», внимательно рассматриваем/изучаем директивы и примерчики (как раз по нашему учебному курсу).
DI HALT привет. А что означает выражение: макрос — лучше оформить в виде процедуры или функции? У тебя на сайте это где-то объясняется? Я бы почитал.
Имеется в виду, что макрос в этом случае использовать не стоит, а лучше сразу написать процедуру и вставить ее в код.
Constant: .db 10 ; или 0хAh в шестнадцатеричном коде
Message: .db «Привет лунатикам»
Words: .dw 10, 11, 12
В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов фразы “привет лунатикам”, а дальше 000A, 000B, 000F.
А разве последняя строчка не равна 000A,000B,000C?
ОПечатка, конечно же 000с
1. Получается каждая буква фразы .db «Привет лунатикам» займет один байт во флеш памяти по порядку?
2. И соответственно вместо каждой буквы будет вбит ее ASII символ?
3. Как бы это применить? Можно ли насоздавать таких сообщений которые нужно вывести на LCD дисплей, потом как нибудь вызывать эти метки?
1. Да
2. Да
3. Так и применяют, вписывая текстовые строки во флеш. О том как вызываются текстовые метки из памяти программ написано далее в курсе. Где то про работу с памятью через команды LPM
Ты имееш в виду это?
LDI ZL,low(data*2) ; заносим младший байт адреса, в регистровую пару Z
LDI ZH,high(data*2) ; заносим старший байт адреса, в регистровую пару Z
; умножение на два тут из-за того, что адрес указан в
; в двубайтных словах, а нам надо в байтах.
; Поэтому и умножаем на два
; После загрузки адреса можно загружать число из памяти
LPM R16, Z ; в регистре R16 после этой команды будет число 12,
; взятое из памяти программ.
; где то в конце программы, но в сегменте .CSEG
data: .db 12,34,45,23
Оно самое
Супер, осталось только замутить программу делающую это.
Подскажите пожалуйста где можно найти полное описание макроассемблера для AVR.
наткнулся на такой кусочек кода
; тактовая частота МГц
#define XCLK 9.6
.equ CN2MKS = INT((2*XCLK-7)/3) ; константа 2мкс
.equ CN5MKS = INT((5*XCLK-9)/3) ; константа 5мкс
.equ CN10MKS = INT((10*XCLK-9)/3) ; константа 10мкс
.equ CN50MKS = INT((50*XCLK-9)/3) ; константа 50мкс
А что такое INT() не понятно((
В хелпе к AVR Studio есть полное описание.
Видел я это полное описание не сильно густо там. Нам нравится когда Вы объясняете.
Я так и не разобрался с макросом, (хотя уже успел полистать и книжку revich-yuriy, да и в инете покапашился), но все равно пока нет полного списка операторов и их описания для ASM&AVR. Единственгное что понял точно, что макрос может иметь до 10 параметров, к которым в его теле обращаются через @0,@1,@2 и т.д. до @9, и что макрос это повторяющийся кусок кода который при трансляции просто тупо заменяет метки в тексте программы на «тело» макроса, т.е. экономии в размере самой программы нет. Применение макросредств целесообразно когда критично время выполнениия программы, а если нужна экономия памяти, тогда нужно использовать процедуру.
Взял вот отсюда: http://www.asm-faq.ru/direktivy-i-operatory-assemblera/69-makrooperatory.html
Макрооператоры
Макрооператор & (амперсанд) нужен для того, чтобы параметр, переданный в качестве операнда макроопределению или блоку повторений, заменялся значением до обработки строки ассемблером. Так, например, следующий макрос выполнит команду PUSH EAX, если его вызвать как PUSHREG A:
pushreg macro letter
push e&letter&x
endm
Иногда можно использовать только один амперсанд — в начале параметра, если не возникает неоднозначностей. Например, если передается номер, а требуется создать набор переменных с именами, оканчивающимися этим номером:
irp number,
msg&number db ?
endm
Макрооператор (угловые скобки) действует так, что весь текст, заключенный в эти скобки, рассматривается как текстовая строка, даже если он содержит пробелы или другие разделители. Как мы уже видели, этот макрооператор используется при передаче текстовых строк в качестве параметров для макросов. Другое частое применение угловых скобок — передача списка параметров вложенному макроопределению или блоку повторений.
Макрооператор ! (восклицательный знак) используется аналогично угловым скобкам, но действует только на один следующий символ, так что, если этот символ — запятая или угловая скобка, он все равно будет передан макросу как часть параметра.
Макрооператор % (процент) указывает, что находящийся за ним текст является выражением и должен быть вычислен. Обычно это требуется для того, чтобы передавать в качестве параметра в макрос не само выражение, а его результат.
Макрооператор ;; (две точки с запятой) — начало макрокомментария. В отличие от обычных комментариев текст макрокомментария не попадает в листинг и в текст программы при подстановке макроса. Это сэкономит память при ассемблировании программы с большим количеством макроопределений.
ВОПРОС: ЧТО ТАКОЕ ПАРАМЕТРЫ И КАК ЭТИМ ПОЛЬЗОВАТЬСЯ С ПРИМЕРАМИ ПОПРОЩЕ ТАК ЧТО БЫ МОЖНО БЫЛО БЫ УЛОВИТЬ СУТЬ, И СОСТАВТЕ ПОЖАЛУЙСТА СПИСОК МАКРООПЕРАТОРОВ И ОПИСАНИЕ К НИМ С ПРИМЕРАМИ.
с уважением Salpingot.
Я в курсе что это для х386 tasm masm и т.д.
Прикол в том, что AVRASM не умеет и 5% того, что умел тасм. Так что тут почти все что он умеет.
Примеры попроще я привести не могу. Куда уже проще?
Уважаемый Ди Халт, а можно описать каждую директиву предпроцессора, (т.е. для чего она нужна, где не верно внесите исправления пожалуйста, некоторые без перевода не знал как перевести):
Preprocessor directives, КАК Я ПОНЯЛ ИХ ВСЕГО 14.
1. #define
2. #elif
3. #else, ИНАЧЕ, ТОГДА, лучше всего смотрится с комбинацией if (если, тогда/иначе)
4. #endif
5. #error, при трансляции вывод ОШИБКИ,
6. #if , условие ЕСЛИ,
7. #ifdef ЕСЛИ_СИМВОЛИЧЕСКОЕ ИМЯ ???
8. #ifndef ОТМЕНИТЬ ЕСЛИ_СИМВОЛИЧЕСКОЕ ИМЯ ???
9. #include, ВСТАВКА ФАЙЛА с другими макросами, подпрограммами (процедурами) и т.п.
10. #message в ходе трансляции вывод СОБЩЕНИЯ,
11. #pragma
12. #undef ОТМЕНИТЬ СИМВОЛИЧЕСКОЕ ИМЯ
13. #warning, ПРЕДУПРЕЖДЕНИЕ, типа смотри сюды и будь внимателен.
14. # (empty directive) ЗНАЧОК ПУСТОЙ ДИРЕКТИВЫ
Я правильно понимаю, что есть только ТРИ макрооператора: ‘@’ и “.” ‘#’ при составлении макросов???
Идеальный вариант с примерам на каждый макрооператор (можно один большой).
ДА И ДЛЯ НОВИЧКОВ ТАКИХ КАК Я, МОЖЕТ ПРИГОДИТЬСЯ Справка по Ассемблеру для Atmel AVR
1. http://dfe3300.karelia.ru/koi/posob/avrlab/avrasm-rus.htm
2. http://mymcu.ru/Articles/Atmel11.htm
это понадобиться для того что бы было понятно дальше о чем пишет Ди Халт.
а вот пример и попроще как использовать макрос для включения ноги мк D7 на пин боард, т.е. в коде в том месте где нужно, можно просто написать имя макроса (у меня включение ноги D7 называется noga_mk_D7), как и говорил выше Ди Халт, с помошью макроязыка можно существенно оптимизировать программирование на асемблере.
Пример крайне неудачный. Т.к. планируется включить вывод 7, а по факту меняем содержимое всего порта и воздействуем на выводы 0..6
Если уж делать то так:
.macro noga_mk_D7
SBI DDRD,7
SBI PORTD,7
.endm
Думаю, данный кусок кода можно просто через метку сделать, на мой взгляд так будет чуть проще.
В смысле через метку? В подпрограмму вынести? Не проще. ПРидется делать вызовы CALL и RET, что сожрет всю экономию.
Нет, не в подпрограмму. salpingot предложил как макросом включить ногу, как бы для существенной оптимизации. Спасибо, за данный пример, с него я и понял, что такое макрос. Но для простого включения ноги макрос… Вот, я и подумал, что может лучше через метку
rjmp noga_mk_D7;
noga_mk_D7:
SBI DDRD,7
SBI PORTD,7
Может я и ошибаюсь.
P.S. Огромное спасибо DI HALT, помощь огромная благодаря этому сайту!
Ну да, сделаешь ты джамп, а как вернешься обратно?
salpingot благодарю за ссылки ;)
а ячейки озу можно как нибудь задефайнить?
ммм… догадываюсь что нужно будет писать не дефайн а equ. типа: .equ my_var = 0×060
МОжно и так, но это неудобно. Проще задать метки в DSEG
Можно. Делаешь в .DSEG метки и указываешь величину данных. Вот так:
При этом число после byte показывает размер переменных в байтах.
спасибо
; В свое время не смог понять смысл параметра в макросе,
; и тут меня осенило решил поделиться с теми до кого не дошло
; что такое параметр. Параметр — это переменная (X,Y и т.п.) в уравнении
; макроса, они будут заменены на значения РОН, (0-255)
; или выражения (.equ или .def) или и т.п.
; нумерация начинается с нуля разделяются запятой 0@,@1,@2,@3…@9,
; итого 10 параметров в теле одного макроса. Т.е. с помощью
; макросов можно писать своеобразные уравнения/формулы для
; значений в программе.
.MACRO stek;
ldi @1,low(@0);
ldi @2,high(@0);
out SPL,@1;
out SPH,@2;
.ENDM;
; применение макроса с параметрами для иницилизации стека одной строчкой,
stek (RAMEND),r20,r21; где:
; ^ ^ ^ ^ (попытался замутить стрелочки
; имя макроса | | | не знаю на сколько получилось)
; где параметр @0 , @1, @2, и т.д. можно до @9 разделитель запятая.
; и еще одна фишка если скампилировать код (ctrl + F7), и навести после
; в моем примере на (RAMEND) то можно будет увидить чему равнялось
; значение .equ или .def можете проверить в файле m16def.inc,
; значение а там .equ RAMEND =$45F, удобно получается очень, не держать
; все переменные в голове.
; ДА И ДЛЯ НОВИЧКОВ ТАКИХ КАК Я, повторю ссылки еще раз,
; МОЖЕТ ПРИГОДИТЬСЯ Справка по Ассемблеру для Atmel AVR
; 1. http://dfe3300.karelia.ru/koi/posob/avrlab/avrasm-rus.htm
; 2. http://mymcu.ru/Articles/Atmel11.htm
; как смог объяснил, если что пишите в личку, и обязательно на форум, что бы
; другие товарищи смогли нагуглить и ознакомиться.
стрелочки не получились напишу по другому stek=имя макроса (RAMEND)=@0,r20=@1,r21=@2;
Проблема твоего макроса в том, что он не учитывает тот факт, что у малых авр (тини) указатель стека может быть и однобайтным. Зависит от размера ОЗУ больше 256 байт или меньше
есть такая тема, просто я сделал специально такой пример, (вроде в пинбоорде стоит мега 16), цель которую я преследовал этим примером показать что это за знак «@» — т.е знак параметра, сам в одно время не мог понять что это просто обозначение переменной в уравнении, как в обычной математике.
а вот это другой пример более универсальный:
.macro stek;
ldi r16,low(RAMEND);
out SPL,r16;
.if (RAMEND)>=0×0100;
ldi r16,high(RAMEND);
out SPH,r16;
.endif;
.ENDM;
Во. Шаришь
А вот и дерективы препроцессора ссылочка для новичков http://azbukavb.narod.ru/cdoc/pdirectives.html
Подскажите, люди добрые, куда нырнуть на тему «ассемблер для AVR для занятых».
Погуглить то можно, но нет времени на проглатывание лишней инфы.
Что значит “ассемблер для AVR для занятых”? типа времени мало а хочу много? есть краткие курсы см. раздел книги на этом сайте справа есть указатель http://easyelectronics.ru/category/knigi скачай в телефон и читай. а если совсем коротко то можно обойтись и двумя командами, sbi ddrX, ? (где ? номер ножки) sbi portX,? (где ? номер ножки) вкл, сbi portX,? (где ? номер ножки) диодик выкл. (впервое время всегда хочется экшена, хотя бы проверить работает ли хоть что нибудь) Все равно нужно время, может уточнишь что значит занятых???
Простите, что не уточнил. Время выделено и почитать можно и с эрана и с твердой копии.
От уважаемого сообщества мне нужен необходимый и достаточный перечень литературы по теме. Так чтобы лаконично, но твердо схватить ассемблер за хвост. Можно взять не Ту книжку и… :)
З.Ы. Когда разменяешь 5й десяток, то могзи скрипеть начинают от накопленной инфы… :).
1. http://narod.ru/disk/7773184001/Микроконтроллеры%20AVR%20семейства%20Mega.djvu.html
2. http://narod.ru/disk/10432792001/Последняя%20на%20сегодня%20версия.doc.html
3. http://narod.ru/disk/10432877001/Расчеты%20сопративления.xls.html
Ну вот если надо кратко. Остальное есть в инете (включая этот ресурс, он тоже достаточно краткий).
Да тут и книжка не нужна. Достаточно систему команд перед глазами иметь.
Странно, у меня компилятор на .undef ругается.
Вот чо гласит мануал:
This directive is only available with AVRASM2.
‘The UNDEF directive is used to undefine a symbol previously defined with the DEF directive. This provides a way to obtain a simple scoping of register definitions, to avoid warnings about register reuse.
Syntax:
.UNDEF symbol
Example:
.DEF var1 = R16
ldi var1, 0×20
… ; do something more with var1
.UNDEF var1
.DEF var2 = R16 ; R16 can now be reused without warning.
Т.е. надо в качестве компилятора указать avrasm2
Опечатка:
Указывать размеры перменных нужно для того
Читал статью «Программы и прерывания». Дошёл до раздела «Подпрограммы vs Макросы». Решил попробовать написать макрос, аналогичный подпрограмме Wait (цикл DELAY раз). Получилось:
.MACRO MWAIT
M1: LDI R17, @0
DEC R17
NOP
BRNE M1
.ENDM
Зацикливается на нём. Пробовал разобраться, но при отладке программа не заходит в макрос, просто висит на этой строке. Видимо, это связано с меткой. В макросах в принципе нельзя использовать метку? Возможно, ответ на этот вопрос откроет мне глаза на то, что такое метка.
P.S. Почему-то под той статьёй комментарий не смог оставить. Пишет «необходимо войти».
И не будет заходить. Оно его будет выполнять как одну команду. Но т.к. студия подтупливает, а там у тебя задержка, то оно на нем и втупляет надолго.
В макросах нельзя использовать метки, т.к. если макрос используется дважды. то метка повторится, а это моветон. Но можно использовать переходы со смещением типо BRNE PC-1 или PC+1 где PC текущая строчка, а +X или -X смещение в словах относительно нее. Но учти что не все команды занимают размер одно слово. Бывают и в два слова. Тут лучше под отладчиком прогонять такой кусочек прежде чем его в макрос пихать.
Спасибо! Понял — всё руками, попробую. Только сразу скажу — не то, что надолго втупляет — а как будто бы навсегда :). То есть ставлю брейк на следующую после макроса команду, F5, ну и всё — висим.
Ой, я дурак! Метку на LDI, а не на DEC поставил. Но всё равно было полезно — узнал, что всё же нельзя метки ставить в макрос на случай повторения.
Кстати, отлаживать с развернутыми макросами можно по вкладке дизассемблер. Там нет символических имен, зато все макросы развернуты
А что — совсем нет локальных меток в макросах? По-моему в masm под x86 локальные метки есть. Я хочу использовать вложенные макросы и не хотелось бы каждый раз вручную вычислять длину перехода.
Увы сравнивать masm и avrasm это что сравнивать последнуюю модель мерседеса и жигули копейку.
а есть директивы как в ассемблере под mcs51 типа .org чтобы распологать команды в определенных местах памяти?
конечно есть. Угадай как она зовется? ;)
ой невнимательно просмотрел!!
здравствуйте. Di halt скажи че не так я написал, студия ругается.
ругается на макрос pop_stec
.macro push_stec
in r00,sreg
push r00
.endmacro
.macro pop_stec
pop r00
out r00,sreg
.endmacro
;==========================================êîíåö ìàêðîñàì=======================
;———————————————îáðàáîò÷èêè ïðåðûâàíèé————-
rx_ok: push_stec
in r16,udr
pop_stec
reti
wtf r00 ??? Нет такого регистра, есть r0
Здраствуйте. Вот прачьол статью все коментарии, и так не могу понять что такое параметр макроса «@0, @1,…@9″. Если можна поясните болие детально, ато зациклиса ни них!!! :-(
Интересуют такие свойства как:
1. Что такое етот параметр «@» детальная инфа ?
2. Как присвоить ему дание? тоесть,обявление макроса(возьмем пожалуй товой пример DI HALT):
.MACRO SUBI16
subi @1,low(@0) ———вичитать с параметра 1 параметр 0 (но где и каким образом мне задать ети значения, дание или константи, чтоб копилятор занал что вичитать)
sbci @2,high(@0)
.ENDM
Зарание большое спасибо