Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Как написать код на ассемблере AVR-на "С"-понятно.
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
serj32
Здравствуйте.Подскажите пожалуйста.Вот код на Си -здесь всё понятно.

if (u > 256) // 1.25V
PORTD = 0b00000011;
if (u > 384) // 1.875V
PORTD = 0b00000111

А вот как реализовать на ассемблере,дело в том ,что переменная-"u"-это двухбайтное число(10 разрядов)
у меня это регистры АЦП-ADCL , ADCH.
И если не трудно подскажите ,что сделал автор в 23 строке своей программы u = (ADCL|ADCH << 8);-взято отсюда http://radioparty.ru/index.php/prog-avr/pr...adc-avr?start=1
Если можно то подробнее.Спасибо.

MrYuran
Цитата(serj32 @ Sep 20 2013, 10:18) *
И если не трудно подскажите ,что сделал автор в 23 строке своей программы u = (ADCL|ADCH << 8);-взято отсюда

Код
u = (ADCL|ADCH << 8); // Считываем  ADC

Считал два 8-разрядных регистра и составил из них 16р слово
serj32
Цитата(MrYuran @ Sep 20 2013, 11:32) *
Код
u = (ADCL|ADCH << 8); // Считываем  ADC

Считал два 8-разрядных регистра и составил из них 16р слово

Вот два регистра ADCL и ADCH они восьмиразрядные ну произведём над этими регистрами операцию побитового ИЛИ и получим результат -восьмиразрядное число,а ещё сдвинем влево
на 8 разрядов результат-ну вообще все нули получатся-совсем я запутался.
adnega
Цитата(serj32 @ Sep 20 2013, 12:23) *
Вот два регистра ADCL и ADCH они восьмиразрядные ну произведём над этими регистрами операцию побитового ИЛИ и получим результат -восьмиразрядное число,а ещё сдвинем влево
на 8 разрядов результат-ну вообще все нули получатся-совсем я запутался.

Подразумевается, что ADCH перед сдвигом копируется во временную переменную с расширением типа.
Вопрос только какого? Видимо, типа как и "u"?
MrYuran
Цитата(serj32 @ Sep 20 2013, 11:23) *
Вот два регистра ADCL и ADCH они восьмиразрядные ну произведём над этими регистрами операцию побитового ИЛИ и получим результат -восьмиразрядное число,а ещё сдвинем влево
на 8 разрядов результат-ну вообще все нули получатся-совсем я запутался.

Вы не учитываете приоритет операций.
У сдвига он выше.
Итого, старший регистр сдвинется на 8 влево, сложится с младшим и результат запишется в результирующую переменную.
serj32
Цитата(adnega @ Sep 20 2013, 12:47) *
Подразумевается, что ADCH перед сдвигом копируется во временную переменную с расширением типа.
Вопрос только какого? Видимо, типа как и "u"?

Как я понимаю по строке 23 программы u = (ADCL|ADCH << 8);-взято отсюда http://radioparty.ru/index.php/prog-avr/pr...adc-avr?start=1
Пусть например АЦП выдало результат преобразования число 1001.
0b1111101001 -число 1001 в двоичной
это число будет находиться в регистрах ADCL и ADCH
тогда соответственно ADCL=0b11101001 в этом регистре младшие разряды
ADCH=0b00000011 в этом регистре старшие разряды

То в коде u = (ADCL|ADCH << 8) следующее:
В регистре ADCH будет 16 разрядное число - 0b0000000000000011
когда поизведём сдвиг влево на 8 разрядов ADCH << 8 то получим 0b0000001100000000
далее в ADCL у нас число 0b0000000011101001
произведя операцию поразрядного ИЛИ над числами 0b0000000011101001 и 0b0000001100000000 мы получаем в переменной "u" одно 16 разр.число
u=0b0000001111101001
Это и есть число 1001 и теперь с ним можно работать.
Правильно ли я написал.Спасибо.

V_G
Вероятно, можно поступить проще, если повнимательней разобраться в сути неравенств из первого поста. Однозначно нижеприведенный вариант будет работать, если неравенства нестрогие (>=)
1. В настройке AЦП включить бит ADLAR (выравнивание результата по левому краю), результат сдвинется влево на 6 разрядов
2. Сравнивать только ADCH с 0x40 и с 0x60
serj32
Цитата(V_G @ Sep 20 2013, 14:29) *
Вероятно, можно поступить проще, если повнимательней разобраться в сути неравенств из первого поста. Однозначно нижеприведенный вариант будет работать, если неравенства нестрогие (>=)
1. В настройке AЦП включить бит ADLAR (выравнивание результата по левому краю), результат сдвинется влево на 6 разрядов
2. Сравнивать только ADCH с 0x40 и с 0x60

В смысле работать с восьмиразрядным числом потеряв 2 младших разряда.А вот это мне непонятно-с 0x40 и с 0x60
Это маска?
V_G
Это старшие байты Ваших порогов, сдвинутых влево на 6 разрядов
Если речь именно о сравнении с порогом, а не о каких-то вычислениях (в которых можно использовать все 10 разрядов), то потеря информации будет небольшой, либо ее вовсе не будет (в зависимости от реальной задачи)
serj32
Очевидно чтобы реализовать Си код
if (u > 256) // 1.25V
PORTD = 0b00000011;
if (u > 384) // 1.875V
PORTD = 0b00000111
на ассемлере будут команды :
cp
breq
brcs
Переменная "u"-это два байта-регистры-ADCL и ADCH.
А константы 256 и 384 необходимо разбить на двухбайтные числа у нас будут старший и младший байт числа.
Дальше необходимо сравнивать вначале младшие байты затем старшие и т.д и ничего не понимаю.
Рад буду всем откликнувшимся.Спасибо.
_Pasha
Цитата(serj32 @ Sep 20 2013, 19:58) *
Очевидно чтобы реализовать Си код

Очевидно(?) - надо взять сишный компилер, написать минимальный пример того, что нужно, скомпилировать и получить ассемблерный листинг, в котором найти нужный фрагмент и увидеть, из каких команд он состоит.
zombi
Цитата(serj32 @ Sep 20 2013, 19:58) *
необходимо сравнивать вначале младшие байты затем старшие и т.д

Сравнивайте старшие, а младшие только если старшие равны.


ЗЫ: У Вас оба сравнения ">" так должно быть?
serj32
Цитата(zombi @ Sep 20 2013, 22:56) *
Сравнивайте старшие, а младшие только если старшие равны.


ЗЫ: У Вас оба сравнения ">" так должно быть?

Здравствуйте.Да действительно">" так должно-это я пытаюсь отсюда http://electronix.ru/redirect.php?http://r...adc-avr?start=1
Си код под ассемблер переделать.Вот я немного попытался написать код-проверьте пожалуйста.

.include "m8def.inc"
.def temp1 = R16
.def temp2 = R17

.equ cons1 = 256
.equ cons2 = 384

;----вершина стека , инициализация портов,АЦП и т.д

in temp1,ADCL ;младший байт результата АЦП
in temp2,ADCH ;старший.................АЦП

cp temp1,low(cons1) ;сравниваем ADCL с младшим байтом константы 256
brcs metka ;если temp1 > low(cons1) флага С не будет и переход не произойдёт

cp temp2,high(cons1) ;тогда начинаем проверять старшие байты
brcs metka

out PORTD,0b00000011

cp temp1,low(cons2)
brcs metka

cp temp2,high(cons2)
brcs metka

out PORTD,0b00000111
zombi
Ход примерно правильный.
Но!!!
Я же уже писал что сперва нужно старшие байты сравнивать, а младшие только в случае равенства старших!
Для сравнения регистра с константой команда CPI

Цитата(serj32 @ Sep 20 2013, 22:56) *
Да действительно">" так должно-это я пытаюсь отсюда ...

Cыль не открывается sad.gif
Т.е. если значение АЦП больше 384 то пин D2 , за время выполнения этого кода, поменяет свое состояние дважды.
_Pasha
Цитата(zombi @ Sep 20 2013, 23:15) *
Ход примерно правильный.
Но!!!
Я же уже писал что сперва нужно старшие байты сравнивать, а младшие только в случае равенства старших!

Не учите людей плохому sm.gif
Код
ldi zL,low(constant1) ; загрузки по нормальному оформляют в виде макроса
ldi zH,high(constant1);

in temp1,ADCL
in temp2,ADCH
cp temp1,zL
cpc temp2,zH
brge kakaya_to_metka; или brlo или что там еще

Итд в таком же духе
Авровский асм имеет много псевдонимов у команд, но они хорошо отражают смысл того, что происходит, поэтому можно не стесняться
serj32
Цитата(_Pasha @ Sep 21 2013, 19:56) *
Не учите людей плохому sm.gif
Код
ldi zL,low(constant1); загрузки по нормальному оформляют в виде макроса
ldi zH,high(constant1);

in temp1,ADCL
in temp2,ADCH
cp temp1,zL
cpc temp2,zH
brge kakaya_to_metka; или brlo или что там еще

Итд в таком же духе
Авровский асм имеет много псевдонимов у команд, но они хорошо отражают смысл того, что происходит, поэтому можно не стесняться

Здравствуйте Pasha,интересный код сейчас буду разбираться.
По рекомендации zombi написал код,если не трудно проверьте пожалуйста.
И сомневаюсь там где NOP нодо ли мне это делать.Спасибо.
Код
.include "m8def.inc"
.def temp1 = R16
.def temp2 = R17

.equ cons1 = 256
.equ cons2 = 384

;----вершина стека , инициализация портов,АЦП и т.д

in temp1,ADCL             ;младший байт результата АЦП
in temp2,ADCH             ;старший.................АЦП

         cpi temp2,high(cons1)  ;сравниваем ADCH с старшим байтом константы 256
         breq  metka1        ;если temp2 = high(cons1) флаг Z будет установлен и произойдёт переход на metka1

         brcs  metka2        ;если старший байт ADCH > старшего байта константы 256-high(cons1) то флага C не будет и
         out PORTD,0b00000011;перехода на metka2 не будет и включим 2 крайних светодиода

         rimp step2             ;никчему проверять младшие байты-надо перепрыгнуть на проверку следующей константы

metka1:  cpi temp1,low(cons1);в связи с тем ,что старшие байты равны - сравниваем младшие,если байт в ADCL >младшего
         breq  metka3        ;байта константы то и перехода на metka3 не будет и соответственно
         out PORTD,0b00000011;включим два крайних светодиода

metka2:  nop
metka3:  nop

step2:   cpi temp2,high(cons2)  ;сравниваем ADCH с старшим байтом константы 384
         breq  setka1            

         brcs  setka2            
         out PORTD,0b00000111;включим три крайних светодиода

         rimp step3              

setka1:  cpi temp1,low(cons2)    
         breq  setka3            
         out PORTD,0b00000111;включим три крайних светодиода

setka2:  nop
setka3:  nop

step3:;и так далее в конце надо будет ещё задержку на таймере думать
_Pasha
Посмотрел.
1. out PORTD,0b00000011; - такой команды нет, есть две команды
Код
ldi  r16, 0b00000011; загрузка регистра непосредственными данными
out PORTD,r16; вывод в порт значения из регистра


2. Таки переделайте сравнение в то,что я написАл постом ранее, потому что вариант, предложенный Zombi путанный и методологически неправильный.
ae_
Если я правильно понял о чём речь, то в итоге должно получиться что-то вроде индикатора уровня: чем больше значение в ADC, тем больше включено разрядов PORTD.
Это можно сделать для 8 сравнений с разными константами и разными значениями для PORTD например так:
Код
.def    temp    =R16
.def    uL    =R20
.def    uH    =R21
;---------------------------------
.macro    inw        ; чтение в RH:RL из ioadrH:ioadrL
    in    @0L, @1L; RL <- ioadrL
    in    @0H, @1H; RH <- ioadrH
.endm
;---------------------------------
.macro    cpiw        ; сравнение RH:RL с 16-бит константой
    ldi    temp, high(@1); используя временный регистр
    cpi    @0L, low(@1); сравнить младшие байты
    cpc    @0H, temp; сравнить старшие байты с учётом переноса
.endm
;---------------------------------
.macro    caseADC
    cpiw    u, @0    ; сравнить значение u с 16-бит константой
    brlo    skip    ; перейти, если u < const
    ldi    temp, @1; иначе загрузить в temp значение для вывода в PORTD
    rjmp    done    ; выход, пропустить все оставшиеся сравнения
skip:            ; перейти к следующему сравнению
.endm
;---------------------------------
; выше было только описание констант, макросов и регистров
; вот сама программа:

    inw    u, ADC        ; прочитать в uH:uL значение из ADCH:ADCL
    caseADC    1001, 0b11111111; сравниваем от бОльших значений
    caseADC    701,  0b01111111; к меньшим
    caseADC    526,  0b00111111
    caseADC    394,  0b00011111
    caseADC    296,  0b00001111
    caseADC    222,  0b00000111
    caseADC    166,  0b00000011
    caseADC    125,  0b00000001
    ldi    temp, 0b00000000; значение для PORTD в случае, когда ADC<min
done:
    out    PORTD, temp    ; выводим в PORTD значение, соответствующее ADC
zombi
Цитата(_Pasha @ Sep 22 2013, 07:18) *
потому что вариант, предложенный Zombi путанный и методологически неправильный.

biggrin.gif
Однако дополнительных регистров не требует.
Путанная реализация.
На вкус и цвет ...
serj32
Считаю,что тема была бы не законченной не приведя законченный код индикатора.Спасибо всем за помощь.
Не сомневаюсь,что не совсем хорошо,но я ещё только начинающий.

Код
;******************************************
;Автор:                     *
;Дата :10/10/2013                          *
;Имя файла:indADC.asm                     *
;Для AVR : ATmega8A,в корпусе DIP28       *
;Тактовая частота:3.686 Мгц.              *
;Исследование АЦП. Индикатор на           *
;светодиодах.Прерывание .Однократный      *
;режим преобразования АЦП.                *
;******************************************
.include "m8Adef.inc"           ;Файл определений для ATmega8A
.list                           ;Включение листинга
.def temp = r16                 ;Временный регистр
.def leder = r19                ;Регистр индикации
.def temp1 = r20
;**** Описание макроса ********************

.macro    caseADC
       ldi temp1,high(@2)
       cpi zL,low(@2)
       cpc zH,temp1
       brlo  t@0
       ldi leder,@1
       out PORTD,leder
t@0:
.endm

;****Векторы прерываний *******************
.org $0                         ;Установка текущего адреса на ноль по "RESET"
       rjmp init                 ;Переход на начало программы

.org $00E                       ;Вектор прерывания АЦП
       rjmp adca                 ;преобразование завершено                 ;
;****Модуль инициализации******************

init: ldi temp,low(RAMEND)      ;Установка указателя стека
       out SPL,temp              ;на последнюю
       ldi temp,high(RAMEND)     ;ячейку
       out SPH,temp              ;ОЗУ
       ldi temp,0b11111111       ;Порт PD на вывод
       out DDRD,temp             ;используем выводы PD0...PD7
       out PORTD,temp            ;Погасим светодиоды на PD0...PD7

       clr temp                  ;Аналоговые входы порта PC
       out DDRC,temp             ;на выводе PC0 "сидит" канал ADC0
       out PORTC,temp            ;отключаем подтягивающие резисторы
      
      
;****Инициализация АЦП *********************
       ldi temp,0b10001101       ;ADEN=1,ADIE=1,Fadc=Fclk/32,ADFR=0(режим однократного преобразования)
       out ADCSRA,temp           ;Fadc=115 кГц при Fclk=3,69 МГц
       ldi temp,0b01000000       ;Выбираем канал ADC0 по выводу PC0
       out ADMUX,temp            ;Опорное напряжение AVCC=VCC равно напр.питания 5 вольт,ADLAR=0 (выравнивание по правому)
       sei                       ;Разрешение прерываний
       sbi ADCSRA,ADSC           ;Запуск преобразования
loop: rjmp loop                 ;Цикл ожидания прерываний
;****Обработка прерывания от АЦП **********
adca: in zL,ADCL               ;
       in zH,ADCH               ;

        cbi ADCSRA,ADIE          ;Запрет прерывания от АЦП
        ldi temp1,high(128)
        cpi zL,low(128)
        cpc zH,temp1
        brlo t0
        ldi leder,0b11111110
        out PORTD,leder

        rjmp t1
t0:    ldi leder,0b11111111
        out PORTD,leder

t1:      
caseADC  2,0b11111100,256
caseADC  3,0b11111000,384
caseADC  4,0b11110000,512
caseADC  5,0b11100000,640
caseADC  6,0b11000000,768
caseADC  7,0b10000000,896
caseADC  8,0b00000000,1020
    
        sbi ADCSRA,ADIE         ;Разрешение прерывания от АЦП
        sbi ADCSRA,ADSC         ;Пуск преобразования
        reti
ILYAUL
Код
cbi ADCSRA,ADIE         ;Запрет прерывания от АЦП
sbi ADCSRA,ADIE        ;Разрешение прерывания от АЦП

Лишнее
kolobok0
Цитата(serj32 @ Oct 10 2013, 21:59) *
Код
.....
.org $0                        ;Установка текущего адреса на ноль по "RESET"
       rjmp init                ;Переход на начало программы

.org $00E                      ;Вектор прерывания АЦП
       rjmp adca                ;преобразование завершено                ;
;****Модуль инициализации******************
...       ldi temp,0b11111111      ;Порт PD на вывод
...;****Обработка прерывания от АЦП **********
...


Код
.org   $00E

и иже лучше писать как
Код
.org    INT6addr

где INT6addr это объявленное уже(!) смещение в файлики который вы с верху в исходнике заинклюдили, то биш "m8Adef.inc"

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

маски в регистр лучше загружать человекопонятными. Т.е. перед кодом (или в своём отдельном инклудники) делаете дефайны, а потом их юзаете на масках. Это если их нет в волшебном файлике "m8Adef.inc" (что редкость!).
тогда код будет читаться более понятнее (даже через пару десяткофф лет). Как пример:

Код
    ori        tempA,((1 << LED_DIG1) | (1 << LED_DIG2) | (1 << LED_DIG3))
....
    ori        tempA,(1 << OCIE0)
    out        TIMSK,tempA


это так-же можно применять к портам и пинам вывода-ввода информации. Тогда вы получаете следующие профиты:
1) код легче портируем, наименование порта меняется в заголовке файла или инклудника.
2) код легче читаем - понятно что в порт(к примеру) дисплея вывели символ, или там выдали разрешение, или там синхросигнал...
3) код легче воспринимаем - т.к. идёт абстрагирование от конкретного порта, вольно или не вольно человек концентрируется на логике кода, а не названии порта и его соответствие со схемой.
Сергей Борщ
QUOTE (kolobok0 @ Oct 10 2013, 23:46) *
3) код легче воспринимаем - т.к. идёт абстрагирование от конкретного порта, вольно или не вольно человек концентрируется на логике кода, а не названии порта и его соответствие со схемой.

4) сложнее ошибиться в номере бита где-то в одном обращении из многих. Скорее уж эта ошибка будет в том самом глобальном файле объявлений, а исправив ее там исправятся сразу все обращения. Не нужно будет скрупулезно шерстить все файлы исходников в поисках обращений к одному и тому же биту порта.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.