Нужна помощь в программировании ардуино на ассемблере
Я новичок в использовании Arduino и пытаюсь запрограммировать его для реализации шаблона мигания с двумя светодиодами. Когда один светодиод выключен, другой должен гореть. Программу нужно делать на ассемблере. У меня есть рабочая программа на С++, но мне нужно преобразовать ее в сборку. У меня есть Arduino UNO ATmega 328p
Вот моя программа на С++:
const int led = 13;
const int led2 = 12;
void setup() {
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);
}
void loop() {
digitalWrite(led, HIGH);
delay(1000);
digitalWrite(led, LOW);
digitalWrite(led2, HIGH);
delay(1000);
digitalWrite(led2, LOW);
}
@user58745, 👍0
Обсуждение4 ответа
Попробуйте этот метод, чтобы преобразовать свой скетч
https://sourceforge.net/projects/arduino-to-assembly-converter/
Я пробовал это, но когда я запускаю его, появляется несколько ошибок, @user58745
Также вы должны проверить комментарии Крисла о том, что это «путь к большому количеству сгенерированного кода» и «очевидно, что вы не делали этого сами». Если вы не стремитесь получить F в своем проекте, автоматическое преобразование кода C — это **не** ответ. Конечно, это полностью ваш выбор, в каком классе вы хотите работать., @GMc
В случае, если это школьное задание и вам действительно нужно программировать непосредственно на ассемблере, вы, вероятно, не захотите напрямую преобразовывать свой скетч C++. Он будет генерировать намного больше кода на ассемблере, чем действительно необходимо для попеременного мигания двух светодиодов (например, digitalWrite()
содержит не только фактическую запись в соответствующие пин-регистры, но и намного больше кода), и это будет очевидно, что вы это не сами написали.
В этом случае вам придется пойти по более сложному пути. Вы должны начать с изучения существующих программ мигания на ассемблере. Через Google я нашел эту серию на github gist, где также есть пример мигания. Вы можете получить часть программы оттуда. Также есть список ассемблерных команд с описанием.
В конце концов, все, что делается на микроконтроллере, сводится к чтению и записи регистров специальных функций (SFR). Контакты организованы в порты с 8 (а иногда и меньше) контактами. Затем каждый порт представлен одним байтом в соответствующем SFR (1 бит на контакт). Для изменения направления контакта между выходом и входом используются регистры DDRX (где X — символ используемого порта). Для фактической установки состояния выхода (или включения/отключения внутреннего подтягивающего резистора) используются регистры PORTX. Текущее состояние (НИЗКИЙ или ВЫСОКИЙ; при вводе или выводе) можно прочитать из регистров PINX. Для этих целей необходимо прочитать техническое описание микроконтроллера (в данном случае Atmega328P) (имеется в виду: не полностью, но достаточно, чтобы чувствовать себя комфортно, получая из него необходимую информацию).
Узнайте, как устанавливать отдельные биты в байте (в SFR). Вы можете узнать это из связанного примера или из руководств в Интернете. Затем напишите код, чтобы записать правильное значение в соответствующие биты DDRX, чтобы настроить выводы светодиода на выход. Затем вы можете использовать это для записи значения на выходной контакт.
Для моргания нужен таймер. Если вы погуглите что-то вроде «arduino assembler blink», вы также найдете пример, который настраивает таймер для мигания. В основном это включает в себя установку требуемых значений SFR таймеров. Если вы не хотите этого делать, вам может сойти с рук выполнение большого количества операций "nop" (которые ничего не делают). Вам нужно будет написать цикл, который будет выполняться большое количество раз. (очень "нет" потребуется 1 тактовый цикл - спасибо Ганиме за поиск). Часы работают на частоте 16 МГц, так что вы можете посчитать, сколько времени нужно 1 "nop", а затем подсчитать, сколько их вам нужно).
Это [один цикл на NOP](https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_NOP.html), который является наиболее разумным для архитектуры AVR., @Ghanima
@Ghanima Спасибо, я исправил номер в своем ответе., @chrisl
Это «должно» выполняться на ассемблере как задание, верно? В противном случае вы бы «захотели изучить» ассемблер.
Я предлагаю вам изменить программу C, чтобы она использовала манипуляции с портами, например:
void setup() {
DDRB |= 0b00100000; // D13 в режим вывода
}
void loop() {
PORTB |= 0b00100000; // включаем D13
delay(2000);
PORTB &= ~0b00100000; // выключаем D13
delay(2000);
}
Теперь, по крайней мере, вы прошиваете D13 без использования довольно сложных библиотечных функций digitalWrite и pinMode. Задержка — еще одна проблема, которую я оставлю вам для изучения, однако моя страница о таймерах может вам помочь.
Теперь вы можете разобрать сгенерированный код, чтобы увидеть, что компилятор сгенерировал на ассемблере, и использовать это, чтобы научиться это делать.
Например, когда я скомпилировал приведенный выше код и включил "подробную компиляцию", я обнаружил расположение сгенерированного файла .elf.
Запускаем avr-objdump для этого файла (примерно так):
avr-objdump -S /tmp/build436d41bc5c0da39afe99cd9c05ac272a.tmp/sketch_aug13a.ino.elf > temp.txt
Дает мне следующий код для "loop":
00000094 <loop>:
94: 2d 9a sbi 0x05, 5 ; 5
96: 60 ed ldi r22, 0xD0 ; 208
98: 77 e0 ldi r23, 0x07 ; 7
9a: 80 e0 ldi r24, 0x00 ; 0
9c: 90 e0 ldi r25, 0x00 ; 0
9e: 0e 94 c5 00 call 0x18a ; 0x18a <delay>
a2: 2d 98 cbi 0x05, 5 ; 5
a4: 60 ed ldi r22, 0xD0 ; 208
a6: 77 e0 ldi r23, 0x07 ; 7
a8: 80 e0 ldi r24, 0x00 ; 0
aa: 90 e0 ldi r25, 0x00 ; 0
ac: 0c 94 c5 00 jmp 0x18a ; 0x18a <delay>
В основном мы видим, что есть одна инструкция "sbi" для включения светодиода и одна инструкция "cbi" для его выключения.
Что касается остального, то я не хочу делать за вас домашнюю работу. Получайте удовольствие, изучая программирование на ассемблере!
Есть также несколько способов включения ассемблерного кода прямо в Arduino IDE.
При включении ассемблера в IDE Arduino исходный код на ассемблере собирается, связывается с вашим проектом и загружается в Arduino из IDE — точно так же, как и любая другая программа.
Это позволяет легко начать работу с ассемблером, сохраняя при этом знакомство с Arduino IDE. Почему? Потому что, поскольку вы можете смешивать код C/C++ с кодом на ассемблере, это означает, что вам не нужно писать полную программу на ассемблере, которая управляет всеми мельчайшими деталями аппаратной инициализации - вы можете начать с малого и расширять свой ассемблер по мере того, как вы чувствуете. удобно или нужно это сделать. Как и в приведенном ниже примере, вы можете начать всего с двух строк ассемблера, которые при желании впишутся в более крупную программу.
Ниже приведены инструкции по созданию смешанной программы C/Assembler в Arduino. Я также привожу пример того, как передать параметры и получить результат от функции Ассемблера.
Чтобы создать смешанную программу C + Assembler, создайте новый проект в Arduino IDE (назовите его как хотите) и введите в IDE следующий код:
extern "C" {
void myInit();
void myLoop();
int myAdd(int, int);
}
void setup() {
Serial.begin(9600);
myInit();
// Это всего лишь фиктивный пример, показывающий встроенный ассемблер, использующий "директиву" asm
// Обратитесь к этому руководству или аналогичному для получения подробной информации о встроенном ассемблере в Arduino:
// https://ucexperiment.wordpress.com/2016/03/11/arduino-inline-assembly-tutorial-5-2/
asm(
"ldi r26,42"
);
}
void loop() {
myLoop();
int ans = myAdd(240, 25);
delay(ans);
Serial.print("Answer: ");
Serial.println(ans);
delay(1000);
}
Далее щелкните маленькую стрелку вниз в правом верхнем углу среды IDE, как показано на следующей схеме, и выберите "Новая вкладка":
Введите myFunctions.S
в качестве имени новой вкладки, как показано ниже. Обратите внимание, что имя на самом деле не имеет значения, но оно должно заканчиваться расширением .S, а "S" должна быть заглавной "S", а не строчной "s":
Вставьте следующий код на вкладку myFunctions.S
.
; Example assembler file that blinks an LED.
; and adds two numbers together.
#define __SFR_OFFSET 0
#include "avr/io.h"
// Declare the three function entry points as "globals"
.global myInit
.global myLoop
.global myAdd
myInit:
sbi DDRB,5 ; Set PB5 (the built in LED on Arduino Uno) as output
ret
myLoop:
ldi r20,250 ; Set the delay duration in ms. Maximum value is 255.
call myDelay_ms
sbi PORTB,5 ; Set PB5 HIGH
ldi r20,250 ; Delay for another 250 ms.
call myDelay_ms
cbi PORTB,5 ; Set PB5 LOW
ret
; These symbols are for internal to this assembler file's use only.
; They are private because they are not listed as a global symbol
; Also, they do not conform to the subroutine calling conventions used
; by the Arduino IDE's compiler. i.e. they are not compatible with
; being called directly from C. However, since we are calling them from
; our own assembler, we can (almost) make up any calling convention we like.
myDelay_ms:
; Delay about r20*1ms. Destroys r20, r30, and r31.
; One millisecond is about 16000 cycles at 16MHz.
; The basic loop takes about 5 cycles, so we need about 16,000,000 / 5 = 3000 loops.
; NB: most instructions are 8 bit, so we load the 16 bit counter using
; two 8 bit loads.
ldi r31, 3000>>8 ; high(3000)
ldi r30, 3000&255 ; low(3000)
delaylp:
sbiw r30, 1 ; Decrement our 1 ms counter (this instruction operates on a 16 bit value in R31:R30).
brne delaylp ; If R30 is non zero, loop back
subi r20, 1 ; Otherwise we have passed 1 ms so, decrement the high order byte of our counter.
brne myDelay_ms ; If this is non zero, loop back.
ret ; Otherwise, we have counted down from R20 * 3,000 - so return.
; Function to add two integers and return an integer.
; For few parameter functions, the Arduino IDE uses registers built into the
; Microcontroller (i.e. the CPU) to exchange data between subroutine caller and callee.
; Return values are as follows:
; - single Byte (e.g. char, byte) R24 only
; - double Byte (e.g. int) R25:R24 (R25 is the high order byte)
; - quad Byte (e.g. long) R25:R24:R23:R22 (R25 is the high order byte)
; Parameters are passed in similar ways starting with R25 and working down, but exactly
; what is where will depend upon the parameter types.
; Refer to this tutorial for more details or the AVR compiler documentation from ATMEL
; https://ucexperiment.wordpress.com/2016/04/02/arduino-inline-assembly-tutorial-12-functions/
; In our case, a is passed in R25:R24 and b is passed in R23:R22
myAdd:
add r24, r22 ; Add the low order bytes
adc r25, r23 ; Add the high order bytes PLUS any value carried from the previous addition.
ret ; The result is in R25:R24 which is where it needs to be to pass back to the C code.
Загрузите его в свой Uno, запустите последовательный монитор, и вы должны увидеть, что выводится некоторая сумма (ну, одна сумма выводится постоянно). Не стесняйтесь изменять значения в основном цикле, чтобы увидеть другие результаты.
Я добавил в код множество комментариев и несколько ссылок, чтобы вы могли начать поиск дополнительной информации.
Еще одна ссылка, которая будет полезна, если вы собираетесь возиться с ассемблером на UNO, — это справочное руководство/техническое описание ATMega-328P (все 662 страницы), которое вы можете получить по адресу Справочная страница Microchip ATmega328p. Там есть еще целая куча полезных ресурсов, которые, вероятно, пригодятся время от времени.
- СВЕТОДИОД мигает в сборке с выходом timer1 сравнение прерывание не работает
- Я закирпичил свой Arduino Uno? Проблемы с загрузкой скетчей на плату
- Не удается снова загрузиться после смены платы
- Разные и самые быстрые способы вычисления синусов и косинусов в Arduino
- Генерация стабильной частоты
- В чем разница между ATMEGA32 и ATMEGA328?
- Arduino UNO для получения подписи чипа ATmega328P-PU
- Последовательная связь ESP8266 с ATMega328P
Почему это «нужно» делать в сборке? Школьный проект?, @Duncan C
И что вы ждете от нас, чтобы сделать для вас? Это больше похоже на призыв к кому-то сделать работу за вас, чем на вопрос. Вам нужно будет изучить различные регистры, которые управляют различными линиями GPIO, а также набор инструкций для вашей модели Arduino., @Duncan C
Вы пробовали веб-поиск? Не думаю :) https://www.instructables.com/id/Command-Line-Assembly-Language-Programming-for-Ard-2/ Начните с шага 3 и измените. Это также можно сделать, написав ассемблер на C. В ядре Arduino есть несколько функций, написанных в этом стиле, https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring.c #L120, @Mikael Patel
Конечно, я искал его, я пришел сюда в крайнем случае, @user58745