LINUX.ORG.RU

Зацикливается функция

 , ,


0

1

Микроконтроллер atmega48, можете думать что хотите по этому поводу, нравится мне AVR и все тут, внутренний тактовый генератор настроен на 1МГц.

Есть такой код.

#define F_CPU 1000000UL  // 1 MHz
#include <avr/io.h>
#include <util/delay.h>

void setup();

int main(void)            
{
    setup();
       
    while(1);

    return 0;
}

void setup()
{
    DDRB = 0x0;
    DDRC = 0x0;
    DDRD = 0x0;
    
    DDRC |= _BV(PC1);  // led pin as outpiut
    
    // left motor
    DDRB |= _BV(PB7);   // enable pin
    DDRD |= _BV(PD5);   // pin 1
    DDRD |= _BV(PD6);   // pin 2
    
    // right motor
    DDRB |= _BV(PB6);   // enable pin
    DDRB |= _BV(PB1);   // pin 1
    DDRB |= _BV(PB2);   // pin 2
    
    PORTC |= _BV(PC1);
    _delay_ms(100);
    PORTC &= ~_BV(PC1);
    _delay_ms(100);
}

Он должен настроить выходы контроллера, мигнуть светодиодом на выходе PC1 и уйти в бесконечный цикл, но не тут то было. Он начинает мигать светодиодом бесконечно, как будто setup() вызывается в в бесконечном цикле. Чяднт?.

★★★★

Если не ошибаюсь, watchdog вкючён по умолчанию, он и ребутит. Добавь в начало setup():

	/* disable watchdog */
	MCUSR = 0;
	wdt_disable();

Или убери соответствующий fuse.

А ещё возможно, злобный gcc вырезает while() nothing; Для разных уровней оптимизации будут разные результаты.

Используй -Os и как один из вариантов:

	for (;;)
		sleep_mode();

PS: можешь ещё тут у меня код надёргать: http://www.dim13.org/cgi-bin/cvsweb/src/avr/kernel/

beastie ★★★★★ ()
Последнее исправление: beastie (всего исправлений: 3)
Ответ на: комментарий от beastie

Сейчас попробую этот вариант. Только что раскопал 328 мегу в закромах, скомпилил под нее этот же код, прошил, и всё работало как надо. А еще сегодня утром у меня накрылся импульсный БП и выдал не знаю сколько вольт (но точно больше 20В) на платку с контроллером. Я быстро это заметил и всё отрубил, но фиг знает, может успел чего нибудь повредить.

WRG ★★★★ ()
Ответ на: комментарий от beastie

Попробовал отключить сторожевой таймер, так же мигает зацикливается. Вариант со sleep_mode() тоже не помог, а -Os у меня и так уже есть. Забавно, но если оставить только такой код в функции main, то мигает 1 раз как положено.

#define F_CPU 1000000UL  // 1 MHz
#include <avr/io.h>
#include <util/delay.h>

void setup();

int main(void)            
{
    DDRC |= _BV(PC1);  // led pin as outpiut 
    PORTC |= _BV(PC1);
    _delay_ms(100);
    PORTC &= ~_BV(PC1);
    _delay_ms(100);
  
    while(1);

    return 0;
}

void setup()
{
    DDRB = 0x0;
    DDRC = 0x0;
    DDRD = 0x0;
    
    DDRC |= _BV(PC1);  // led pin as outpiut
    
    // left motor
    DDRB |= _BV(PB7);   // enable pin
    DDRD |= _BV(PD5);   // pin 1
    DDRD |= _BV(PD6);   // pin 2
    
    // right motor
    DDRB |= _BV(PB6);   // enable pin
    DDRB |= _BV(PB1);   // pin 1
    DDRB |= _BV(PB2);   // pin 2
    
    PORTC |= _BV(PC1);
    _delay_ms(100);
    PORTC &= ~_BV(PC1);
    _delay_ms(100);
}

PS: За код спасибо. Глянул, есть что одолжить)

WRG ★★★★ ()
Последнее исправление: WRG (всего исправлений: 1)
Ответ на: комментарий от beastie

Ты совсем не в себе? Я попросил бинарь в elf формате, чтобы посмотреть, почему зацикливается. Или тут отвечать можно только звезданутым и патентованым... пользователям?

niemand ()

while(1);

Вот эта штука, когда компилируешь с оптимизацией, часто не дает бесконечный цикл. Она просто выносится, как бесполезный код. Посмотри свой *.lst файл или вытащи оттуда кусочек, где комментарий 'while(1)', и покажи - будет видно. И если у тебя там не бесконечный цикл, то тогда у тебя программа вываливается не пойми куда, поэтому есть вероятность пересброса и мигание по новой.

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 3)

Если у тебя нет *.lst, то сделать его можно avr-objdump из объектного файла или ELF. Вообще, полезно в Makefile генерацию lst добавить. Для себя родного. Полезно смотреть.

$ avr-objdump -h -S <file>.elf | less

UPD. А вываливается наверняка. Как набрела на return 0, проскочив цикл, так и выскочила куда не надо.

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 3)
Ответ на: комментарий от Zubok

Глянул. Когда функция main() выглядит вот так, а оптимизация по размеру

int main(void)            
{
    setup();
    
    
        
    while(1){
    }

    return 0;
}

получается что-типа такого:

....
169 00000146 <setup>:
170  146:   14 b8           out 0x04, r1    ; 4
171  148:   17 b8           out 0x07, r1    ; 7
172  14a:   1a b8           out 0x0a, r1    ; 10
173  14c:   0e 94 40 00     call    0x80    ; 0x80 <motor_init>
174  150:   80 e0           ldi r24, 0x00   ; 0
175  152:   0e 94 47 00     call    0x8e    ; 0x8e <motor_enable>
176  156:   81 e0           ldi r24, 0x01   ; 1
177  158:   0e 94 47 00     call    0x8e    ; 0x8e <motor_enable>
178  15c:   39 9a           sbi 0x07, 1 ; 7
179  15e:   3a 9a           sbi 0x07, 2 ; 7
180  160:   82 e0           ldi r24, 0x02   ; 2
181  162:   90 e0           ldi r25, 0x00   ; 0
182  164:   0e 94 90 00     call    0x120   ; 0x120 <beep>
183  168:   83 e0           ldi r24, 0x03   ; 3
184  16a:   90 e0           ldi r25, 0x00   ; 0
185  16c:   0c 94 7d 00     jmp 0xfa    ; 0xfa <flash_led>
186 
187 00000170 <main>:
188  170:   0e 94 a3 00     call    0x146   ; 0x146 <setup>
189  174:   ff cf           rjmp    .-2         ; 0x174 <main+0x4>
190 
191 00000176 <_exit>:
192  176:   f8 94           cli
193 
194 00000178 <__stop_program>:

т.е. цикл-то всё таки есть, вываливаться не должно. Я склоняюсь с варианту повреждения контроллера при поломке БП. Видимо напруга >20В, пусть даже кратковременно, оказалась для него смертельной.

WRG ★★★★ ()
Ответ на: комментарий от WRG

т.е. цикл-то всё таки есть, вываливаться не должно. Я склоняюсь с варианту повреждения контроллера при поломке БП. Видимо напруга >20В, пусть даже кратковременно, оказалась для него смертельной.

Так, нет, погоди. Рано еще хоронить. Вот еще что проверь. Отключен ли BOD (Brown out detector). watchdog ты уже отключил. См. биты fuse. BOD при понижении питания делает reset.

Zubok ★★★★★ ()
Ответ на: комментарий от Zubok

UPD. А вываливается наверняка. Как набрела на return 0, проскочив цикл, так и выскочила куда не надо.

В avr_gcc после вызова main идет бесконечный цикл, это я помню.

niemand ()
Ответ на: комментарий от niemand

В avr_gcc после вызова main идет бесконечный цикл, это я помню.

Я этот момент не помню уже. Вполне может быть и так. Могу, конечно, проверить на тестовом коде... Впрочем, по метке 194 00000178 <__stop_program>: дальше и должен идти этот пустой цикл, который компилятор вставил.

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Ага, __stop_program это

42: ff cf rjmp .-2 ; 0x42 <__stop_program>

Только прерывания отключаются до этого (cli). Но ТСу это не страшно, как я понял

niemand ()
Ответ на: комментарий от WRG

Проверил. Все 3 бита, которые за него отвечают, выставлены в 1, т.е. согласно даташиту он отключен.

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

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от WRG

Да он вроде даже не должен доходить до того места, где они отключаются.

Должен, компилятор сам встраивает вызов exit. Побегу прогуляюсь, а ты давай лучше весь бинарь и комманду как собираешь/Makefile. Вдруг, что там что-то есть

niemand ()
Ответ на: комментарий от WRG

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

На всякий случай в начале программы сделай cli();, чтобы гарантирвоано отключить прерывания. Вдруг чо.

Zubok ★★★★★ ()
Ответ на: комментарий от niemand

А «внутренний тактовый генератор на 1 MГц» - это дефолт? Я уже не помню. Надо ли для него настраивать fuse-биты?

Если у него внешний, то пофиг. У меня тут даташит только на Atmega128 есть, а на 48 нет. В 128 по умолчанию стоит внутренний осциллятор @ 1 МГц. Но это тоже было бы хорошо проверить. Однако в данном коде это только на _delay_ms окажет влияние, если я ничего не упустил.

Zubok ★★★★★ ()
Ответ на: комментарий от Zubok

моторы не включаю же, просто инициализирую их выходы. Ну есть пьезопищилка, но пока не пользую, да и много она не жрёт. Светодиод на PC1 тоже врят-ли много берет, чтобы просаживать питание.

собираю таким makefile

##############################
TARGET = 	robot
MMCU = 		atmega48     
F_CPU = 	1000000UL    
PROGRAMMER =	stk500
OPTIMIZATION = 	s
PROG_PORT = 	/dev/ttyACM0
PROG_SPEED = 	9600

SOURCES = $(wildcard ./src/*.c)
##############################

#SRCDIR = ./src
OBJECTS = $(addprefix ./obj/, $(notdir $(SOURCES:.c=.o)))

CC = avr-gcc
CFLAGS = -Wall \
	 -std=c99 \
	 -mmcu=$(MMCU) \
	 -DF_CPU=$(F_CPU) \
	 -O$(OPTIMIZATION)
	 
SIZE = avr-size
SIZE_FLAGS = -C --mcu=$(MMCU)
	 
AVRDUDE_FLAGS = -p $(MMCU) -P $(PROG_PORT) -c $(PROGRAMMER)

all:	$(TARGET).hex size

$(TARGET).elf: $(OBJECTS)
	$(CC) $(CFLAGS) -o $@ $^

# $(TARGET).o:
# 	@mkdir -p obj
# 	$(CC) $(CFLAGS) -c $(SRCDIR)/$
obj/%.o: src/%.c
	@mkdir -p obj
	$(CC) $(CFLAGS) -c $< -o $@
	
$(TARGET).hex:	$(TARGET).elf
	avr-objcopy -O ihex $(TARGET).elf $(TARGET).hex
	
size:	
	$(SIZE) $(SIZE_FLAGS) $(TARGET).elf

clean:
	rm -r $(OBJECTS) $(TARGET).hex $(TARGET).elf
	
burn:
	avrdude $(AVRDUDE_FLAGS) -U flash:w:$(TARGET).hex
	

в коде маленько прибрался, теперь он выглядит так

#define F_CPU 1000000UL  //1  MHz
#include <avr/io.h>
#include <util/delay.h>
#include "motor.h"
#include "debug.h"

void setup();

int main(void)            
{
    setup();
    
//     motor_run(MLeft, SFwd);
//     motor_run(MRight, SFwd);
    
        
    while(1){
//         motor_run(MLeft, SFwd);
//         motor_run(MRight, SFwd);
//         
//         _delay_ms(1000);
//         
//         motor_stop(MLeft);
//         motor_stop(MRight);
//         
//         _delay_ms(500);
//         
//         motor_run(MLeft, SFwd);
//         
//         _delay_ms(1000);
//         
//         motor_stop(MLeft);
//         
//         _delay_ms(500);
    }

    return 0;
}

void setup()
{    
    DDRB = 0x0;
    DDRC = 0x0;
    DDRD = 0x0;
    
    motor_init();
    motor_enable(MLeft);
    motor_enable(MRight);
    
    DDRC |= _BV(PC1);   // led pin as outpiut
    DDRC |= _BV(PC2);   // buzzer pin as output
     
    beep(2);
    flash_led(3);
}

Теперь кстати зацикливается не setup(), а beep() который вызывается внутри setup(), до flash_led() даже не доходит. Такое ощущение что где-то портится адрес возврата и он не может вернуться из функции обратно. Вот функции beep() и led_flash().

void flash_led(int n)
{
    while(n--){
        PORTC |= _BV(PC1);
        _delay_ms(100);
        PORTC &= ~_BV(PC1);
        _delay_ms(100);
    }
}

void beep(int n)
{
    while(n--){
        PORTC |= _BV(PC2);
        _delay_ms(100);
        PORTC &= ~_BV(PC2);
        _delay_ms(100);
    }
}

Когда вынимаю из платы мегу48 и ставлю мегу328, перекомпилирую этот же самый код для него и прошиваю, всё работает идеально, как написано в программе. Когда возвращаю мегу48, опять что-то зацикливается.

WRG ★★★★ ()
Ответ на: комментарий от niemand

По дефолту, настроен на внутрениий генератор на 8Мгц, но стоит предделитель 8, т.е. частота получается 1Мгц. Поскольку фьюзы я не менял, и даже проверил их, они совпадают с теми, что на m328, значит частота впорядке.

WRG ★★★★ ()
Ответ на: комментарий от WRG

А fuse у тебя какие? Можешь сказать? low byte, high byte и extended. И cli(); поставь в начало на всякий случай. Прямо в самое начало.

UPD: А чем шьешь? avrdude или чем-то другим? В какой-то из версий avrdude неправильно fuse выводились. Там два были перепутаны. Обрати внимание на это.

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от WRG

Ну если не fuse-биты (лучше пусть будут дефолтные), то я прям и не знаю, что это

niemand ()
Ответ на: комментарий от Zubok

фьюзы - E:01, H:DF, L:62

Я чуть чуть соврал. Он циклится не на beep() в коде выше, а уже на motor_init(). Но если убрать все motor_init() и motor_enable(), то циклится будет на beep(). Получается он циклится на ближайшей функции, вернуться не может. Если в setup() ничего не вызывать, то зациклится сам setup().

cli() поставил, то же самое.

WRG ★★★★ ()
Ответ на: комментарий от WRG

моторы не включаю же, просто инициализирую их выходы.

Ну вот я и спросил, подключено ли у тебя к чему-то реальному. Ну, там, к драйверам моторов, например. Питание может не подействовать на 328, но подействовать на 48. В мире нет ничего одинакового.

Zubok ★★★★★ ()
Ответ на: комментарий от Zubok

В какой-то из версий avrdude неправильно fuse выводились

Ни разу не попадалось. Зато если он не проходит верификацию, то помогало это

stty 9600 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo noflsh </dev/ttyS0

Вместо /dev/ttyS0 надо поставить своё, конечно же. ТС, а avrdude у тебя не ругается?

niemand ()
Ответ на: комментарий от niemand

Ни разу не попадалось. Зато если он не проходит верификацию, то помогало это

Нет, читал он правильно. Он при выводе информации для пользователя подписи перепутанные имел. По-моему, это версия 5.10 была.

Zubok ★★★★★ ()
Ответ на: комментарий от Zubok

Убрал все из setup() кроме настройки порта светодиода и собственно вызова flash_led(), результата нет.

WRG ★★★★ ()
Ответ на: комментарий от WRG

фьюзы - E:01, H:DF, L:62

Это вроде дефолт, всё ок

Хотя в мане написано, что extended byte стоит в 0xff (но все, кроме первого не используются)

niemand ()
Ответ на: комментарий от WRG

Даташит говорит, что и на m48, и на m328 все биты в E по дефолту установлены в 1.

А ты сам-то что прописываешь в avrdude? Вообще, я помню тему, что при чтении он может неиспользуемые биты читать как 0. То есть E:01 - это FF. Но на всякий случай пропихни ему FF явным образом, чтобы отметить, что это сделали.

Zubok ★★★★★ ()

Поставь включение ещё одного цветодиода после while(1), и сразу станет видно, выбрасывает его конпелятор, или надо копать дальше и глубже.

one_more_hokum ★★★ ()
Ответ на: комментарий от Zubok

avrdude почему-то предлагает вернуть его обратно

>$ avrdude  -c stk500 -p m48 -P /dev/ttyACM0 -U efuse:w:0xff:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.02s

avrdude: Device signature = 0x1e9205
avrdude: reading input file "0xff"
avrdude: writing efuse (1 bytes):

Writing |                                                    | 0% 0.00s ***failed;  
Writing | ################################################## | 100% 0.05s

avrdude: 1 bytes of efuse written
avrdude: verifying efuse memory against 0xff:
avrdude: load data efuse data from input file 0xff:
avrdude: input file 0xff contains 1 bytes
avrdude: reading on-chip efuse data:

Reading | ################################################## | 100% 0.01s

avrdude: verifying ...
avrdude: verification error, first mismatch at byte 0x0000
         0x01 != 0xff
avrdude: verification error; content mismatch

avrdude: safemode: efuse changed! Was ff, and is now 1
Would you like this fuse to be changed back? [y/n]

WRG ★★★★ ()
Ответ на: комментарий от WRG

Получается он циклится на ближайшей функции, вернуться не может

Ну и это как-то странно. Вот у тебя тут пример, который не циклится: Зацикливается функция (комментарий) . _delay_ms() тоже функция, но ты утверждаешь, что отрабатывает нормально.

Может, при достижении какого-то адреса в PC зацикливается, а не при вызове функции? На мой взгляд выглядит, будто бы происходит сброс по какой-то причине и запуск программы заново. А линия сброса у тебя как вообще подключена? Ничто на нее влиять не может по схемотехнике? Ты программатор отключаешь от разъема ISP?

Есть, кстати, бит fuse, который запрещает линию reset снаружи.

Zubok ★★★★★ ()
Ответ на: комментарий от one_more_hokum

Поскольку второго светодиода на плате нету, добавил включение пьезобибикалки перед return, она не включилась.

WRG ★★★★ ()
Ответ на: комментарий от WRG
avrdude: verification error, first mismatch at byte 0x0000
         0x01 != 0xff
avrdude: verification error; content mismatch

Это нормально. Так и должно быть. Значит, fuse правильные.

Note that some numerical values refer to fuses containing undefined bits (set to '1' here). Depending on the target device these fuse bits will be read either as '0' or '1'. Verification errors will occur if the values are read back with undefined bits set to '0'. Everything is fine if the values read from the device are either the same as programmed, or the following values (undefined set to '0')

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Ресет на данный момент болтается в воздухе, никуда не поключен. Схемотехника состоит из макетки и кучи проводов мгтф. Программатор отключаю не всегда, от него плата питается, чтобы провода не перетыкать постоянно. Но при питании от аккумулятора тоже самое.

Есть, кстати, бит fuse, который запрещает линию reset снаружи.

а как прошивать потом?

WRG ★★★★ ()
Ответ на: комментарий от WRG

Ресет на данный момент болтается в воздухе, никуда не поключен.

Слушай, ты бы его подтянул к питанию сопротивлением 10 кОм. Или он подтянут?

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от WRG

Ресет на данный момент болтается в воздухе, никуда не поключен

Да кто-ж так делает! Прицепи сброс куда (и как) нужно, по документации посмотри, и ещё, на всяк.случ., навесь прямо на корпус контролёра между питанием и землёй конденсатор металлоплёночный, 0.1 — 1 мкФ.

а как прошивать потом?

Параллельным программатором, выдрав мелкосхему из платы.

one_more_hokum ★★★ ()
Последнее исправление: one_more_hokum (всего исправлений: 1)
Ответ на: комментарий от WRG

нету у меня такого программатора, я студент-нищеброд)

Да не, вырубать RESET не надо в принципе. Но лучше не надо, раз нет программатора параллельного. Я это только для проверки. Линия не должна болтаться - она помеху с воздуха ловит. И если ты там что-то на выводах делаешь, то помеха может попасть на RESET, он сбрасывается и все по новой - циклически.

Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.