Почему моя плата arduino не мигает должным образом?

У меня есть два файла

blink1.cpp

typedef unsigned char int8_t;
typedef volatile int8_t * volatile port_type;


port_type portB = (port_type) 0x25;
port_type ddrB = (port_type) 0x24;


void delay_500ms()
{
    asm (
    // 8000000 циклов
    "ldi  r19, 150 \n\t"
    "ldi  r20, 128 \n\t"
    "ldi  r23, 41 \n\t"
    "L1: \n\t"
    "dec  r20 \n\t"
    "brne L1 \n\t"
    "dec  r19 \n\t"
    "brne L1 \n\t"
    "dec  r23 \n\t"
    "brne L1 \n\t"
    );
}

int main()
{
    ddrB = (port_type) 0x20;

    while(true)
    {
        *portB = (int8_t) 0x20;
        delay_500ms();
        *portB = (int8_t) 0x00;
        delay_500ms();
    }
}

Blink1

blink2.cpp

typedef unsigned char int8_t;
typedef volatile int8_t * volatile port_type;


port_type portB = (port_type) 0x25;
port_type ddrB = (port_type) 0x24;
port_type portC = (port_type) 0x28;
port_type ddrC = (port_type) 0x27;
port_type portD = (port_type) 0x2B;
port_type ddrD = (port_type) 0x2A;


void delay_500ms()
{
    asm (
    // 8000000 циклов
    "ldi  r19, 150 \n\t"
    "ldi  r20, 128 \n\t"
    "ldi  r23, 41 \n\t"
    "L1: \n\t"
    "dec  r20 \n\t"
    "brne L1 \n\t"
    "dec  r19 \n\t"
    "brne L1 \n\t"
    "dec  r23 \n\t"
    "brne L1 \n\t"
    );
}

int main()
{
    ddrB = (port_type) 0x20;

    while(true)
    {
        *portB = (int8_t) 0x20;
        delay_500ms();
        *portB = (int8_t) 0x00;
        delay_500ms();
    }
}

Blink2


Разница между этими двумя файлами заключается в определении только нескольких неиспользуемых портов в blink2.

Я компилирую и загружаю их на плату

avr-g++ blink1.cpp -o blink1
avr-objcopy -O ihex -R .eeprom blink1 blink1.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyUSB0 -b 115200 -U flash:w:blink1.hex


avr-g++ blink2.cpp -o blink2
avr-objcopy -O ihex -R .eeprom blink2 blink2.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyUSB0 -b 115200 -U flash:w:blink2.hex

Первый не моргает должным образом, но второй делает это. В чем проблема первого кода?

, 👍1

Обсуждение

Ведут ли себя оба файла одинаково при использовании avr-g++ -Os blink1/2.cpp -o мигнуть 1/2 для компиляции?, @Maximilian Gerhardt

@MaximilianGerhardt, используя "- Os", оба действуют как и раньше., @ar2015

Я думаю, что "ddrB = (тип порта) 0x20; должно быть" *ddrB = (int8_t) 0x20; нет?, @Maximilian Gerhardt

@MaximilianGerhardt, Вы правы. Однако это исправление ничего не меняет. Поведение платы сильно зависит от переменной, которую вы определяете после ddrB, и ее значения., @ar2015

@MaximilianGerhardt, интересно, что когда я использую *ddrB = (int8_t) 0x20; с -Os " индикатор включается, но не мигает. Если я не использую " - Os, он будет нормально мигать., @ar2015

Возможно, вы уничтожаете содержимое используемых регистров. Проверьте разборку, чтобы увидеть, перемещаются ли регистры, которые вы используете в сборке, в стек перед использованием и отключаются после - если это не так, то вам следует сделать это вручную., @Majenko

https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers-and-Scratch-Registers, @Mat

Возможно, вы захотите обменять код сборки на код C для теста и посмотреть, в чем проблема, например, с кодом https://pastebin.com/Z5m2fvWt, @Maximilian Gerhardt

@MaximilianGerhardt: Ваша "задержка_500ms ()" более чем на порядок медленнее, чем оригинал., @Edgar Bonet


1 ответ


Лучший ответ:

2

Здесь есть несколько проблем.

Один из них уже был поднят в комментарии:

 
 int main()
 {
-    ddrB = (port_type) 0x20;
+    *ddrB = (int8_t) 0x20;
 
     while(true)
     {

Более серьезная проблема связана с командной строкой, которую вы использовали для компиляции программы. При компиляции для Uno вы должны добавить следующую опцию -mmcu=atmega328p. Таким образом, компилятор знает, какую версию среды выполнения C он должен добавить в вашу программу. В противном случае компилятор вообще не будет включать время выполнения.

Среда выполнения C выполняет некоторые важные инициализации. Например, он очищает регистр r1, который требуется ABI, и принимает чтобы быть таковым со стороны компилятора. Например, когда вы пишете *portB = (int8_t) 0x00;, компилятор копирует r1 в порт, предварительно не очищая его. Еще одна важная роль среды выполнения C заключается в вызове main(). Без него программа начнет выполнять любую функцию, которая окажется в начале флэш. Если вы не программируете в сборке, вы не можете контролировать это. Если первой функцией является main(), программа может работать так, как ожидалось. Если это другая функция, то эта функция может затем вернуться в загрузчик. Любое, казалось бы, безобидное изменение в программе может затем изменить поведение.

Затем несколько незначительных проблем, которые не должны мешать работе программы :

  • int8_t должен быть подписанным типом. Если вам нужна unsigned версия, используйте вместо нее uint8_t. Конечно, это ваши собственные определения типов, но создание типа, который конфликтует со стандартным типом , может только создать путаницу. Лучший вариант-отбросить определение и включить вместо него <stdint.h><stdint.h>.

  • В то время как port_type, безусловно, должен быть “указателем на изменчивые” данные, нет никакого смысла делать сам указатель изменчивым. Это может только помешать правильной оптимизации.

  • portB и ddrB должны быть const, и это может позволить компилятору лучше оптимизировать их. Но почему бы не использовать PORTB и DDRB из <avr/io.h>?

,

Я подтверждаю, что использование "avr-g++ -mmcu=atmega328p` вместо" avr-g++ " устранило проблему. Огромное спасибо., @ar2015