Druk Arduino Serial niepotrzebnie zmienia zachowanie programu

10

Używam licznika pętli zadeklarowanego w nagłówku:

int loop_counter = 0;

Używam tego licznika do wywoływania zdarzenia tak często. Kiedyś używałem modulo dla tego samego rodzaju zachowania, ale uprościłem go, aby łatwiej było z nim pracować (nadal powoduje to takie samo zachowanie)

void loop() {
    if(loop_counter > 100) loop_counter = 0;
    else loop_counter++;

    //Serial.println("hey");

    if(loop_counter == 0) {
         //do_something_important();
    }      
}

Wszystko jest dobrze i dobrze, dopóki nie spróbuję się komunikować Serial, odkomentując //Serial.println("hey"); ( "hey"w tym przykładzie, ponieważ dla mnie takie zachowanie jest absurdalne).

Powoduje to, loop_counterże do_something_important();sekcja kodu nigdy się nie uruchamia . Próbowałem deklarowania loop_counterjako volatile, że nic nie zmieni. Próbowałem Serial.printing loop_counter, a także zachowywałem się dziwnie (spowodowałoby to zawieszenie pętli). Serial.println("hey");działa w tym sensie, że na monitorze szeregowym dostaję dużo „hej” (tj. szybko dużo więcej niż 100 „hej”, liczba iteracji, przy których powinna zadziałać druga sekcja kodu)

Co może być przyczyną korzystania Serialz danych, które nie są (o ile wiem) powiązane, aby loop_countercałkowicie uniemożliwić prawidłowe działanie?

EDYCJA : Oto część głównego pliku, która ostatecznie stanowi problem (cóż, przyczyniając się do niego najwięcej (zużywając zbyt dużo pamięci)):



void display_state() {
  int i,j,index=0;
  short alive[256][2];

 for(i=0;i<num_rows;i++) { 
   for(j=0;j<num_cols;j++) {
     if(led_matrix[i][j]==1) { 
       alive[index][0]=i;
       alive[index][1]=j;
       index++;
     }
   }
 }
 alive[index][0]=NULL; //Null-terminate.
 alive[index][1]=NULL;

 //383 is a great number
 for(int idx=0;idx < index; idx++) {
   display(alive[idx][0],alive[idx][1]);
   delayMicroseconds(283);
 }
}

Oto „letters.h”:


    #ifndef _MY_LETTERS_H
    #define _MY_LETTERS_H

#define nrows 4
#define ncols 4

#define num_rows 16
#define num_cols 16

#define MAX_WORD_LENGTH 16
#define NUMBER_OF_CHARACTERS 26

#include <stdlib.h>

int loop_counter = 0 ; short led_matrix [ num_rows ] [ num_cols ];

const short letter_a [ nrows ] [ ncols ] = {{ 0 , 1 , 1 , 0 }, { 1 , 0 , 0 , 1 }, { 1 , 1 , 1 , 1 }, { 1 , 0 , 0 , 1 } }; const short letter_b [ nrows ] [ ncols ] = {{ 1 , 0 , 0 , 0 }, { 1 , 1 , 1 , 0 }, { 1 , 0 , 1 , 0 }, { 1 , 1 , 1 , 0 }}; const short letter_c [ nrows ] [ ncols ] = {{ 0 , 1 , 1 , 1 }, { 1 , 0 , 0 , 0 }, { 1 , 0 , 0 , 0 }, { 0 , 1 , 1 , 1 }}; const short letter_t [ nrows ] [ ncols ] = {{ 1 , 1 , 1 , 1 }, { 0 , 1 , 0 , 0 }, { 0 , 1 , 0 , 0 }, { 0 , 1 , 0 , 0 } };

typedef struct letter_node { const short * data ; letter_node * next ; int x ; int y ; } letter_node ;

letter_node aa = {&letter_a[0][0],NULL,1,1}; letter_node bb = {&letter_b[0][0],NULL,1,1}; letter_node cc = {&letter_c[0][0],NULL,1,1}; letter_node tt = {&letter_t[0][0], NULL , 1 , 1 };

letter_node letter_map [ NUMBER_OF_CHARACTERS ]; #endif

Więcej informacji: - Używam Uno (ATMega328)

eqzx
źródło
Jaki jest twój rozmiar stosu? Czy jest szansa, że ​​możesz pomalować swój stos i sprawdzić, czy jest uszkodzony. Czy druk seryjny używa przerwań, czy kod jest ponownie wysyłany?
Ktc
Druk seryjny nie jest wywoływany przez żadne przerwania, używam go tylko w loop()funkcji. Jak mam pomalować swój stos, jeśli jedyna metoda wyjścia, którą mam ( Serial.print()), zawodzi mnie?
eqzx,
2
Aby wyeliminować ewentualne błędy i niezrozumiałe skutki uboczne pozornie trywialnych zmian, zamień kod w pytaniu na dosłowną, dokładnie znakową kopię szkicu zmniejszoną do minimum niezbędnego do uruchomienia problemu . Nie „to mój program, który zawodzi, jeśli…”, ale dokładnie ten minimalny program, który zawodzi w ten sposób.
Chris Stratton,

Odpowiedzi:

2

Miałem również podobny problem do tego i jestem bardzo pewien, że twój jest również związany z brakiem miejsca na stosie. Spróbuj maksymalnie zmniejszyć kod.

W moim przypadku czasami kod działał, gdy miałem w nim komunikat seryjny, ale wtedy nie działał, gdy go nie miałem. Miałem też przypadek, w którym wysyłanie wiadomości szeregowych powodowałoby nieskończone resetowanie arduino.

Używałem również arduino328. Prawdopodobnie powinieneś zmniejszyć rozmiar tablicy, jeśli masz jakikolwiek do najmniejszego możliwego do zaakceptowania rozmiaru.

Reza Hussain
źródło
dziękuję, ty i Dave Tweed to zrozumieliście. Zmodyfikowałem funkcję display_state (), aby nie potrzebowała dodatkowego przydziału. Rzadko wykonuję przetwarzanie wbudowane, chyba w pewnym momencie wszyscy musimy trafić w ścianę pamięci!
eqzx
Witam mam podobną sytuację. Zmieniam rozmiar tablicy z 128 na 96 i mój program działa dobrze. Ale myślę, że ten problem jest naprawdę nie do śledzenia podczas debugowania, ponieważ rozmiar mojej tablicy jest mniejszy niż rozmiar stosu deklaracji. Czy wiesz, gdzie mogę znaleźć informacje na temat tego rodzaju problemów?
Lion Lai,
4

Czy Twój kod inicjuje port szeregowy? Na przykład.

void setup()
{
    Serial.begin(9600);
}

Niezastosowanie się do tego może spowodować awarię przy pierwszym użyciu numeru seryjnego.

Toby Jaffey
źródło
Tak, mam to.
eqzx,
3

Może kończy Ci się pamięć? Wszystkie łańcuchy drukowane za pomocą Serial.print („coś”) mają miejsce w pamięci SRAM, równej liczbie znaków tego ciągu + 1 dla terminatora \ 0. Można skończyć pamięć, nawet jeśli rozmiar skompilowanego szkicu jest znacznie mniejszy niż pamięć flash Arduino, ponieważ SRAM ma tylko 2048 bajtów dla Atmega328 i 1024 bajtów dla Atmega 168. Miałem podobny problem, który rozwiązałem, skracając wszystkie SMS-y i usuwanie niepotrzebnych komunikatów debugowania.

Erion
źródło
Hmm Mam kilka wielowymiarowych tablic zadeklarowanych w moim nagłówku, może to jest problem? Czy są przechowywane w SRAM?
eqzx
1
@ nrhine1: W takim przypadku powinieneś prawdopodobnie pokazać nam cały szkic, a nie tylko te części, w których według ciebie leży problem.
Dave Tweed,
@DaveTweed Tak, zrobi.
eqzx,
1
Zauważam, że definiujesz dużo miejsca w pliku nagłówkowym, zamiast po prostu go tam deklarować (jeśli nie rozumiesz rozróżnienia, zobacz tę stronę ). Byłoby to niezwykłe w programie C; czy to normalna praktyka na Arduino? Być może kończysz się wieloma kopiami tych struktur. Ponadto definiujesz niektóre bardzo duże zmienne automatyczne, takie jak tablica „żywa” w display_state (), która potrzebuje ponad 1024 bajtów miejsca na stosie. Jestem prawie pewien, że kończy ci się pamięć.
Dave Tweed,
@DaveTweed dziękuję, ty i Reza to rozumiecie. Zmieniłem display_state()funkcję, aby nie potrzebowała dodatkowego przydziału. Rzadko wykonuję przetwarzanie wbudowane, chyba w pewnym momencie wszyscy musimy trafić w ścianę pamięci!
eqzx
1

Nie pokazałeś kodu inicjującego zmienną „loop_counter”. Czy to jest poza procedurą pętli () ?

Czy masz to zadeklarowane w taki sposób, że przylega ono do innego obszaru pamięci, który działa poza swoim zadeklarowanym rozmiarem i trompuje zmienną loop_counter?

Michael Karas
źródło
Próbowałem to zadeklarować na wiele różnych sposobów, w wielu różnych miejscach. W nagłówku, tuż powyżej loop()itp. Czy mówisz, że Serial.print()metoda może jakoś go nadpisać?
eqzx
To, co miałem na myśli przez poprzedni komentarz, to fakt, że jestem prawie pewny, że odizolowałem „złe” zachowanie od istnienia Serial.print (). Kiedy go nie ma, wszystko działa dobrze.
eqzx
@ nrbine1 - Wydaje mi się, że twoja zmienna globalna „loop_counter” jest wykorzystywana przez metodę Serial.print (), jak zasugerowałem w mojej odpowiedzi. W odpowiedzi posipiet zostałeś zapytany, czy obiekt Serial został poprawnie zainicjowany. Jeśli tego nie zrobiono, może to wyjaśniać „tromping” na twoim liczniku, ponieważ Serial.print () próbuje użyć bufora, który nie został poprawnie przydzielony i skonfigurowany.
Michael Karas,
Dodałem całe moje źródło.
eqzx
1

Nie widzę w twoim kodzie, gdzie dzwonisz loop(). Nie wygląda również na to, że używasz loop_counterpoza tą funkcją. Czy istnieje powód, dla którego ogłaszasz to globalnie? Zakładam, że to dlatego, że chcesz, aby zachował swoją wartość między rozmowami. Możesz to zrobić za pomocą statycznej zmiennej lokalnej .

void loop() {
    static int loop_counter = 0;

    if(loop_counter > 100)
    {
        loop_counter = 0;
    }
    else
    {
        loop_counter++;
    }

    Serial.println("hey");

    if(loop_counter == 0)
    {
         //do_something_important();
    }      
}

Powinno to zapewnić, że żadne inne funkcje zewnętrzne nie będą mogły na niego uderzyć. Należy zawsze deklarować zmienne w możliwie najmniejszym zakresie, aby uniknąć niepożądanych zachowań.

Jeśli to nie zadziała, będziesz musiał naprawdę przeanalizować zużycie pamięci. Sprawdź to EE.SE Pytania i odpowiedzi dotyczące różnych przykładowych kodów, aby to zrobić w Arduino.

embedded.kyle
źródło
Próbowałem już uczynić go statycznym. To nie pomogło. To jest inna iteracja. setup()i loop()są funkcjami, które arduino uruchamia domyślnie, setup()po pierwsze, po loop()drugie. loop()jest zasadniczo podobny main(), z wyjątkiem tego, że jest wywoływany wielokrotnie. referencja: arduino.cc/en/Reference/loop Sprawdzę ten link.
eqzx
znowu, jak wspomniałem w innych komentarzach, nie mogę debugować Serial.print(). Wygląda na to, że będę musiał wyjść poza normalne processingIDE, jeśli chcę móc korzystać z GDB
eqzx,
@ nrhine1 Powiedziałeś, że Serial.print()działa dobrze, ponieważ drukuje „hej” dużo. To loop_counterdaje ci problem. Spróbuj usunąć if(loop_counter == 0)kod, wstaw get_free_memory()kod (pozostaw loop_counterprzyrost) i uruchom go. To przynajmniej powie ci, jeśli masz jakieś poważne problemy z przydzielaniem pamięci.
embedded.kyle
1

Biblioteka szeregowa oprogramowania Arduino używa przerwań. (patrz „softwareSerial.cpp, .h”). Możesz mieć problem polegający na tym, że ISR „wkracza” na główny kod (lub odwrotnie). Spróbuj użyć flag blokady, aby kod czekał na zakończenie operacji drukowania.

Bob Kugler
źródło
0

W pewnym momencie miałem wrażenie, że mam ten sam problem. Wtedy rozwiązałem go, dodając opóźnienie (1) przed lub po serial.println. Tak było z Arduino 0022 w systemie Linux. Nie jestem pewien, która to tablica, prawdopodobnie serial Boarduino. Nie mogę tego też odtworzyć.

Obecnie działa dla mnie na pokładzie USB z Arduino 1.01 w systemie Windows:

int loop_counter = 0;
int led = 13;

void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);}

void loop() {
    if(loop_counter > 100) {
      loop_counter = 0;
    }
    else {
      loop_counter++;
    }

    Serial.println(loop_counter);

    if(loop_counter == 0) {
      Serial.println("hey hey orange, hey hey!");
    }      
}
posipiet
źródło
Dzieki za sugestie. Niestety nie rozwiązało to problemu.
eqzx