Как преобразовать эту программу в сборку из c++

Я пытаюсь реализовать программу на ассемблере для Arduino UNO. Потенциометр используется на выводе АЦП для переменного времени (t). LED1 мигает в течение t секунд, затем остается включенным в течение t секунд, затем гаснет, а другой светодиод (LED2) загорается на t секунд, а затем цикл повторяется (t — переменное время, которое определяется потенциометром). У меня есть рабочая программа на С++, но я хочу преобразовать ее в сборку. Я только начал изучать ассемблер и не очень хорошо в этом разбираюсь.

Часть, на которой я действительно застрял, — это функция AnalogRead(). Я не знаю, как реализовать это на сборке. Кто-нибудь знает, как это сделать?

#define led 3
#define led2 2

void setup()

{

Serial.begin(9600);

//инициализация контактов
pinMode(led,OUTPUT);

pinMode(led2,OUTPUT);

}
void loop()

{

// прочитать ввод на аналоговом выводе 0:
int sensorValue = analogRead(A0);

// Преобразование аналоговых показаний (от 0 до 1023) в напряжение (0-5 В):
int voltage = sensorValue * (5.0 / 1023.0);

// распечатать прочитанное значение:
Serial.println(voltage);

for(int i=0;i<2*voltage;i++)

{

digitalWrite(led,LOW);

delay(100);

digitalWrite(led,HIGH);

delay(100);

}


//красный остается включенным
digitalWrite(led,HIGH);

delay(voltage*500); 

digitalWrite(led,LOW);

//зеленый остается включенным
digitalWrite(led2,HIGH);

delay(voltage*500);

digitalWrite(led2,LOW);

}

, 👍2

Обсуждение

Вы можете запустить простой блинк-код, который не использует аналоговый ввод, на ассемблере?, @jsotola

Похоже, вы хотите отказаться от всей работы, проделанной людьми в среде Arduino, и заняться тем, что называется [программированием на «голом железе»](https://www.techopedia.com/definition/3745/bare-metal-programming). Если это так, скажите об этом, и я отвечу новым ответом на ваш вопрос. Имейте в виду, что это погрузит вас в программирование прошивки, где вам нужно будет делать все, что среда Arduino делает за вас. Это может стать настолько техническим, что некоторые компании нанимают специальных программистов встраиваемых систем, которые специализируются на пакетах поддержки плат или BSP., @st2000

@jsotola Да, я могу это сделать, @user58745

@st2000 Да, пожалуйста, @user58745

Загрузите копию таблицы данных Atmega328P отсюда http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf Примеры приведены в C и в сборке для многих вещей. См. Раздел 24. Я думаю, что вы также можете использовать команды AVRDUDE для создания/сохранения ассемблерного кода как части процесса компиляции, написать простой код и посмотреть, как это делает компилятор., @CrossRoads

Ну, @CrossRoads в основном сказал вам, что делать. Вам нужно будет прочитать и понять части таблицы данных, в которых описывается интересующее вас периферийное устройство, а также ядро процессора. Вам не нужно беспокоиться об электрических или физических деталях упаковки. Да, я знаю, что в PDF 600 страниц. (Это то, от чего вас абстрагирует Arduino.) Добро пожаловать в прошивку и программирование на «голом железе»!, @st2000


1 ответ


3

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

avr-objdump -S -C the_program.elf > the_program.lss

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

Часть, на которой я действительно застрял, — это функция AnalogRead().

Вот оно:

000003a6 <analogRead>:
 3a6:   8e 30           cpi r24, 0x0E   ; 14
 3a8:   08 f0           brcs    .+2         ; 0x3ac <analogRead+0x6>
 3aa:   8e 50           subi    r24, 0x0E   ; 14
 3ac:   87 70           andi    r24, 0x07   ; 7
 3ae:   20 91 00 01     lds r18, 0x0100 ; 0x800100 <__data_start>
 3b2:   90 e4           ldi r25, 0x40   ; 64
 3b4:   29 9f           mul r18, r25
 3b6:   90 01           movw    r18, r0
 3b8:   11 24           eor r1, r1
 3ba:   82 2b           or  r24, r18
 3bc:   80 93 7c 00     sts 0x007C, r24 ; 0x80007c <__TEXT_REGION_LENGTH__+0x7e007c>
 3c0:   80 91 7a 00     lds r24, 0x007A ; 0x80007a <__TEXT_REGION_LENGTH__+0x7e007a>
 3c4:   80 64           ori r24, 0x40   ; 64
 3c6:   80 93 7a 00     sts 0x007A, r24 ; 0x80007a <__TEXT_REGION_LENGTH__+0x7e007a>
 3ca:   80 91 7a 00     lds r24, 0x007A ; 0x80007a <__TEXT_REGION_LENGTH__+0x7e007a>
 3ce:   86 fd           sbrc    r24, 6
 3d0:   fc cf           rjmp    .-8         ; 0x3ca <analogRead+0x24>
 3d2:   80 91 78 00     lds r24, 0x0078 ; 0x800078 <__TEXT_REGION_LENGTH__+0x7e0078>
 3d6:   20 91 79 00     lds r18, 0x0079 ; 0x800079 <__TEXT_REGION_LENGTH__+0x7e0079>
 3da:   90 e0           ldi r25, 0x00   ; 0
 3dc:   92 2b           or  r25, r18
 3de:   08 95           ret

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

avr-nm -nC the_program.elf

выводит символы, связанные с каждым адресом ОЗУ и флэш-памяти. В Чтобы отличить их друг от друга, к каждому адресу ОЗУ добавляется 0x800000. Этот сообщит вам, что в этой конкретной программе адрес ОЗУ 0x0100 фактически является адресом переменной analog_reference из Основная библиотека Ардуино. Таким образом, вы можете заменить строку

 3ae:   20 91 00 01     lds r18, 0x0100 ; 0x800100 <__data_start>

от

    lds  r18, analog_reference

Затем вы можете использовать таблицу данных, чтобы получить имена подключенных к памяти операций ввода-вывода. регистры и имена битов из этих регистров. я сделал все это для вас, так что вот подчищенная разборка аналоговое чтение():

analogRead:
    cpi  r24, 14
    brcs 1f
    subi r24, 14
1:  andi r24, 0x07
    lds  r18, analog_reference
    ldi  r25, 1<<REFS0
    mul  r18, r25
    movw r18, r0
    clr  r1
    or   r24, r18
    sts  _SFR_MEM_ADDR(ADMUX), r24
    lds  r24, _SFR_MEM_ADDR(ADCSRA)
    ori  r24, 1<<ADSC
    sts  _SFR_MEM_ADDR(ADCSRA), r24
2:  lds  r24, _SFR_MEM_ADDR(ADCSRA)
    sbrc r24, ADSC
    rjmp 2b
    lds  r24, _SFR_MEM_ADDR(ADCL)
    lds  r18, _SFR_MEM_ADDR(ADCH)
    ldi  r25, 0
    or   r25, r18
    ret

Если вы сравните это с исходным кодом analogRead(), это теперь должно иметь смысл.

Я пытаюсь реализовать программу на ассемблере

Теперь, если вы хотите написать сборку самостоятельно, это целый другая история. Если вы позволите себе использовать ядро Arduino, то вы можете просто

    ldi r24, 14  ; pin A0 is pin 14
    call analogRead

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

Шаг 1: узнайте, как запрограммировать ATmega328P на простом C, на уровень регистрации, не используя никакую другую библиотеку, кроме avr-libc. Вы можете начать прочитав это краткое руководство о прямом доступе к порту. Но тогда вам следует быстро перейти к изучению техническое описание и руководство по avr-libc. Да, это много, чтобы переварить. Надеюсь, вы не ожидали, что это будет быстро и легкий. Но обратите внимание, что в этих документах вы можете пропустить все главы. связанные с функциями, которые вы не собираетесь использовать.

Я действительно застрял на функции AnalogRead().

В конкретном случае с вашей программой-примером вам не нужна версия. из analogRead(), который может считывать любой вывод с любой ссылкой. Ты можешь вместо этого напишите более простую версию, которая просто читает текущий выбранный закрепить, используя текущую выбранную ссылку:

int simple_analog_read(void)
{
    ADCSRA |= 1<<ADSC;                    // начинаем конвертацию
    while (ADCSRA & (1<<ADSC)) continue;  // ждем, пока это будет сделано
    return ADC;                           // возвращаем показания
}

Конечно, это предполагает, что вы правильно инициализировали ADC.

int voltage = sensorValue * (5,0 / 1023,0);

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

int voltage = (sensorValue * 5) >> 10;

что, кстати, более правильно, так как масштабный коэффициент 5/1024, а не 5/1023 (см. спецификацию).

Шаг 2. Изучите ассемблер и перепишите низкоуровневый код C на сборка. Если ваш код C достаточно низкоуровневый, это не должно быть слишком жесткий. В качестве учебного пособия вы можете попросить компилятор сделать это за вас и затем попытайтесь понять, что он сделал. Если вы просто попытаетесь скомпилировать simple_analog_read(), которую я написал выше, вы увидите перевод на ассемблер почти тривиален.

,