Как преобразовать эту программу в сборку из 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);
}
@user58745, 👍2
Обсуждение1 ответ
Очевидным решением для преобразования любой программы в ассемблер является... скомпилируй это! Дизассемблировать полученный бинарник можно командой нравится:
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()
, которую я написал выше, вы увидите
перевод на ассемблер почти тривиален.
- Как найти пиковое значение аналогового сигнала?
- Использование MAX30100 для Arduino для чтения АЦП в формате (0-1023)
- Печать string and integer LCD
- Почему мои часы реального времени показывают неверное время с моего ПК?
- Разные и самые быстрые способы вычисления синусов и косинусов в Arduino
- Arduino uno + cnc Shield v3 + драйвер шагового двигателя A4988 + AccelStepper?
- Отправьте несколько значений int из Python в Arduino, используя pySerial
- Глобальные переменные занимают много места в динамической памяти.
Вы можете запустить простой блинк-код, который не использует аналоговый ввод, на ассемблере?, @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