Почему atmega168/328p начинает перезагружаться?
Сначала мы устанавливаем фьюз-биты:
avrdude -c usbasp -p atmega328p -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0x07:m # same for atmega168
В следующих примерах мы используем следующие команды для компиляции и записи всех программ:
avr-gcc -Os -mmcu=atmega328p -c -o serial.o serial.c
avr-ld -o serial.elf serial.o
avr-objcopy -O ihex serial.elf serial.hex
avrdude -c usbasp -p atmega328p -U flash:w:serial.hex
Запишем следующую программу:
#define F_CPU 16000000UL
#include <avr/io.h>
void main (void) {
DDRB |= (1<<PB5);
PORTB |= (1<<PB5);
while (1);
}
Светодиод горит постоянно. И так будет бесконечно долго.
Затем мы записываем следующую программу:
#define F_CPU 16000000UL
#define BAUD 9600
#include <avr/io.h>
char data = 0;
void main(void) {
// Инициализировать USART:
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE; // устанавливаем скорость (старший бит)
UBRR0L = UBRRL_VALUE; // устанавливаем скорость (Младший бит)
UCSR0B |= (1<<RXEN0); // включить приемник
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00); // установить режим (8N1)
DDRB |= (1<<PB5); // включаем вывод на пин PB5 (светодиод по умолчанию на arduino)
while(1) {
while(!(UCSR0A&(1<<RXC0))); // ждем пока не будет получен байт
data = UDR0; // прочитать его
if (data) PORTB |= (1<<PB5); // включаем светодиод
}
}
Затем открываем терминал (9600,8N1) и нажимаем несколько клавиш, пока не погаснет светодиод. Теперь мы снова записываем первую программу, и светодиод постоянно мигает. Причина этого в том, что время сторожевого таймера запускается, когда мы нажимаем клавиши в терминале. Но почему и когда запускается WDT? Как сделать так, чтобы он не запускался?
Также следует отметить, что отключить мигание мы можем только при полном выключении/включении питания - только после этого светодиод горит постоянно, не мигая, по мере необходимости.
Это происходит на atmega168 и atmega328p, воспроизводимость 100%.
avr-gcc версии 4.8.1 avrdude версии 6.1
ПРИМЕЧАНИЕ: загрузчик вообще не используется.
@Igor Liferenko, 👍0
Обсуждение3 ответа
Лучший ответ:
Одна вещь, которую я обнаружил, экспериментируя с компиляцией командной строки, это то, что по какой-то странной причине код запуска не был связан с моим кодом, так что все пошло наперекосяк.
Наконец-то я обнаружил, что проблема заключается в отсутствующем флаге командной строки при связывании.
Во-первых, вам нужно связать с avr-gcc
, а не с avr-ld
. Во-вторых, вам необходимо передать флаг -mmcu=atmega328p
в команду связывания, чтобы она знала, какой код запуска следует связать.
Это моя полная последовательность командной строки для компиляции простой программы мигания:
avr-gcc -g -mmcu=atmega328p -ffunction-sections -fdata-sections -c -o blink.o blink.c
avr-gcc -mmcu=atmega328p -Wl,--gc-sections -o blink.elf blink.o
avr-objdump -S blink.elf > blink.dis
avr-objcopy -O ihex -R .eeprom blink.elf blink.hex
Вы заметили, что я также создаю разделы функций и данных, а затем собираю в мусор неиспользуемые разделы — хорошая практика, так как это может очень сильно уменьшить размер вашей готовой программы.
Я делаю это через Makefile, который выглядит так:
PREFIX=avr-
CC=${PREFIX}gcc
CXX=${PREFIX}g++
LD=${PREFIX}ld
AS=${PREFIX}as
OBJCOPY=${PREFIX}objcopy
OBJDUMP=${PREFIX}objdump
BIN=blink
MCU=atmega328p
OBJS=blink.o
CFLAGS=-g -mmcu=${MCU} -ffunction-sections -fdata-sections
CXXFLAGS=-g -mmcu=${MCU} -ffunction-sections -fdata-sections -fno-exceptions
LDFLAGS=-mmcu=${MCU} -Wl,--gc-sections
${BIN}.hex: ${BIN}.elf
${OBJCOPY} -O ihex -R .eeprom $< $@
${BIN}.elf: ${OBJS}
${CC} ${LDFLAGS} -o $@ $?
${OBJDUMP} -S $@ > ${BIN}.dis
install: ${BIN}.hex
avrdude -c usbasp -p ${MCU} -U flash:w:${BIN}.hex -qq
clean:
rm -f *.o *.elf *.hex
fuses:
avrdude -c usbasp -p ${MCU} -U lfuse:w:0xff:m -U hfuse:w:0xd6:m -U efuse:w:0x05:m -qq
Нет, как я уже сказал в своем ответе на EE, это не имеет ничего общего с загрузчиком или WDT. Вы, кажется, не понимаете принципиально, как все это дело работает..., @Kurt E. Clothier
Настройки WDT останутся неизменными даже после сброса. Таким образом, таймер будет продолжать работать и, следовательно, продолжать сбрасывать устройство. Что вызывает мерцание. Выключение и включение питания очистит настройки WDT.
PS Я не читал код, так как незакомментированные записи в регистр практически нечитаемы.
Код проблемы был сокращен до этого:
#define F_CPU 16000000UL
#include <avr/io.h>
char data;
void main(void) {
UBRR0H = 0x00;
UBRR0L = 0x67; // baud=9600
UCSR0B = 0x10; // 00010000
UCSR0C = 0x06; // 00000110
DDRB |= (1<<PB5);
PORTB |= (1<<PB5);
while(1) {
data = UDR0;
}
}
Это код, который работает:
#define F_CPU 16000000UL
#include <avr/io.h>
void main(void) {
char data;
UBRR0H = 0x00;
UBRR0L = 0x67; // baud=9600
UCSR0B = 0x10; // 00010000
UCSR0C = 0x06; // 00000110
DDRB |= (1<<PB5);
PORTB |= (1<<PB5);
while(1) {
data = UDR0;
}
}
Но я не знаю, как объяснить, что char внутри main устраняет проблему...
ИЗМЕНИТЬ Просто для протокола, вот еще два примера. Первый пример работает с обоими методами компиляции. Второй пример работает только со вторым методом компиляции. Примеры отличаются только тем, что в первом используется прямой код, а во втором — через функции.
#define F_CPU 16000000UL
#define BAUD 9600
#include <avr/io.h>
void main(void) {
char data;
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
DDRB = (1<<PB5);
while (1) {
PORTB ^= (1<<PB5);
while(!(UCSR0A&(1<<RXC0)));
data = UDR0;
UDR0 = data;
while(!(UCSR0A&(1<<TXC0)));
}
}
#define F_CPU 16000000UL
#define BAUD 9600
#include <avr/io.h>
void serialSetup(void) {
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}
void serialSend(char data) {
UDR0 = data;
while(!(UCSR0A&(1<<TXC0)));
}
char serialGet(void) {
while(!(UCSR0A&(1<<RXC0)));
return UDR0;
}
void main(void) {
char data;
serialSetup();
DDRB = (1<<PB5);
while (1) {
PORTB ^= (1<<PB5);
data = serialGet();
serialSend(data);
}
}
Это может быть связано со странной вещью, которую я видел при ручной компиляции, которую я просто не мог понять - казалось, что crt0 не был связан, за исключением случаев, когда у вас была глобальная переменная, которая вызывала только часть BSS crt. существовать. Если я вдруг добавлял в программу другую функцию, эта функция становилась точкой входа вместо main(). Все было перепутано и полностью расплавило мозг. И все же я вызывал те же параметры компилятора и командной строки, что и при компиляции API Arduino. Это не имело никакого смысла., @Majenko
На самом деле я только что решил свою проблему - и, вероятно, решит и вашу. Я опубликую ответ с подробностями., @Majenko
@Majenko: Да, это решило проблему!, @Igor Liferenko
@Majenko: Но я не понял, почему он самопроизвольно начинает перезагружаться, и что делают ваши команды, чтобы это исправить. Не могли бы вы дать несколько рекомендаций для дальнейшего чтения, чтобы понять, что вызывает проблему и как ее вылечить?, @Igor Liferenko
С WDT, как только он начал стрелять, его почти невозможно отключить. Вы можете отключить его только в «холодном» состоянии, чтобы он никогда не выстрелил. Кроме того, без надлежащего кода запуска (crt0) можно только догадываться, что на самом деле произойдет с программой при ее запуске - переменные не будут инициализированы, векторы исключений не будут настроены и т. д. и т. д., @Majenko
- AVRdude неправильно считывает значения байтов предохранителя
- Почему последовательная связь не работает на atmega168/328p?
- Запуск кода Arduino для Bluefruit LE на чистом ATMega328
- Проблема с таймером 0
- Не удается снова загрузиться после смены платы
- Отправка последовательных данных в прерывании
- Arduino UNO для получения подписи чипа ATmega328P-PU
- Поддерживают ли чипы ATMega 328/2560 JTAG-программатор и аппаратный отладчик?
void main(void) { #include <util/setbaud.h>
Это привлекло мое внимание. Довольно необычно делать включение в код? Я не уверен, что это может вызвать проблемы., @PaulКроме того, я не уверен, как сторожевой таймер связан с этим вопросом? Вы можете отключить его фьюзбит?, @Paul
и ваш
if (data) PORTB |= (1<<PB5);
будет включать и выключать светодиод всякий раз, когда данные установлены на что-то. Вы должны сбросить значение? Несмотря на то, чтоwhile(!(
) может заблокировать это., @Paul