RS485 per programmare ATmega

Salve :slight_smile:

Supponiamo che io abbia un cavo UTP sulla trentina di metri che mi colleghi un convertitore USB-rs485 ad un max485 sul mio arduino, vorrei usare questa stessa linea per la programmazione dell'ATmega.
Sappiamo che questo standard è un half-duplex, quindi per ricevere devo avere sui pin 2,3 del max uno stato basso e viceversa per inviare..
Ora, il convertitore usb dovrebbe comportarsi così: sto in ascolto->devo inviare qualcosa?allora mi metto in trasmissione->torno di nuovo in ascolto(e lo fa)
Ma non il max montato sull'arduino. O meglio, non se non glielo dico esplicitamente io da codice.

Oltre ad usare lo standard 422 che implementa due linee separate e sempre differenziali per ogni direzione della trasmissione, c'è un modo per raggirare questo problema?
Come faccio a scrivere sul micro che quando lo programmo, se riceve deve impostare a livello basso un determinato piedino e viceversa quando trasmette?

(Sto dando per scontato che quando programmo un ATmega oltre a mandargli dati devo riceverli anche..)

Spero di essere stato chiaro :smiley:

Hai dimenticato che l'Atmega328 in remoto deve essere resettato, altrimenti non può essere avviato il bootloader per ricevere lo sketch dalla seriale e scriverlo in Flash.

Anch io vedo il problema nel reset del Arduino. Il problema del interfaccia per trasmetter i dati é secondario se non risolvi il problema del reset. Ok potresti usare 2 doppini: 1 RX 1 TX e il terzo per il reset.

Ciao Uwe

Spedito il segnale di reset non sarebbe un grosso problema modificare il bootloader affinché commuti la linea 485 in ricezione. Linea che poi verrebbe pilotata normalmente dallo sketch una volta che questo riparte.

Momento,momento,moomeeento :stuck_out_tongue:
Perciò che idea avete riguardo il fatto che devo alternare lo stato dei 2 pin del max durante la fase di cariamento?

Edit: modificare il bootloader.. e che ci scrivo? :slight_smile:

sciorty:
Momento,momento,moomeeento :stuck_out_tongue:
Perciò che idea avete riguardo il fatto che devo alternare lo stato dei 2 pin del max durante la fase di cariamento?

Edit: modificare il bootloader.. e che ci scrivo? :slight_smile:

Le istruzioni in C :stuck_out_tongue:

A parte gli scherzi, che ci vuoi scrivere? Un digitalWrite sul pin a cui hai collegato i 2 pin del Max485 in modo da mettere il chip in ricezione, poi lasci il resto del bootloader inalterato. In questo modo al reset, il bootloader imposta il Max in ricezione e poi riceve lo sketch. Secondo me funziona.

Ma durante la fase di caricamento l'atmega non invia anche dei dati oltre che riceverli? non so, roba tipo handshake.. anche perchè sulla scheda vedo che si accendono entrambi i led!

Per il reset dovrei farcela: mettiamo che la sezione del cavo utp sia 1mm la resistenza è di 19,5ohm/km, e mettendo proprio per assurdo che il pin reset assorba 1A calcolo una caduta sul cavo di 1V e qualcosa.. se non sto sbagliando..

sciorty:
Ma durante la fase di caricamento l'atmega non invia anche dei dati oltre che riceverli? non so, roba tipo handshake.. anche perchè sulla scheda vedo che si accendono entrambi i led!

Sì, devi analizzare il codice dell'Optiboot e modificarlo in modo che ogni volta che deve ricevere o che deve trasmettere sulla seriale venga settato il Max nel corretto stato.

Per il reset dovrei farcela: mettiamo che la sezione del cavo utp sia 1mm la resistenza è di 19,5ohm/km, e mettendo proprio per assurdo che il pin reset assorba 1A calcolo una caduta sul cavo di 1V e qualcosa.. se non sto sbagliando..

Qui non mi esprimo :sweat_smile:

leo72:

sciorty:
Ma durante la fase di caricamento l'atmega non invia anche dei dati oltre che riceverli? non so, roba tipo handshake.. anche perchè sulla scheda vedo che si accendono entrambi i led!

Sì, devi analizzare il codice dell'Optiboot e modificarlo in modo che ogni volta che deve ricevere o che deve trasmettere sulla seriale venga settato il Max nel corretto stato.

Non so proprio dove mettere mani, cioè non so neanche dove risiedano questi codici :sweat_smile:

Se i miei calcoli erano giusti avrei potuto usare la rs232 anche se tirata per il collo, mi sarei risparmiato i 20 euro del convertitore usbrs485 :astonished: =(

sciorty:
Per il reset dovrei farcela: mettiamo che la sezione del cavo utp sia 1mm la resistenza è di 19,5ohm/km, e mettendo proprio per assurdo che il pin reset assorba 1A calcolo una caduta sul cavo di 1V e qualcosa.. se non sto sbagliando..

per fortuna c'é sul Arduino sulla USB un fusibile da 0,5A. ]:smiley: ]:smiley:

Lo sapevo che avrei dovuto sottolinearlo quell' "assurdo" :stuck_out_tongue:

sciorty:
Non so proprio dove mettere mani, cioè non so neanche dove risiedano questi codici :sweat_smile:

Sono in /arduino-1.0.3/hardware/arduino/bootloaders/optiboot

E mo'? XD

Posso dedurre sia da cercare in questa zona.. (?):

/* Forever loop */
  for (;;) {
    /* get character from UART */
    ch = getch();

    if(ch == STK_GET_PARAMETER) {
      unsigned char which = getch();
      verifySpace();
      if (which == 0x82) {
	/*
	 * Send optiboot version as "minor SW version"
	 */
	putch(OPTIBOOT_MINVER);
      } else if (which == 0x81) {
	  putch(OPTIBOOT_MAJVER);
      } else {
	/*
	 * GET PARAMETER returns a generic 0x03 reply for
         * other parameters - enough to keep Avrdude happy
	 */
	putch(0x03);
      }
    }
    else if(ch == STK_SET_DEVICE) {
      // SET DEVICE is ignored
      getNch(20);
    }
    else if(ch == STK_SET_DEVICE_EXT) {
      // SET DEVICE EXT is ignored
      getNch(5);
    }
    else if(ch == STK_LOAD_ADDRESS) {
      // LOAD ADDRESS
      uint16_t newAddress;
      newAddress = getch();
      newAddress = (newAddress & 0xff) | (getch() << 8);
#ifdef RAMPZ
      // Transfer top bit to RAMPZ
      RAMPZ = (newAddress & 0x8000) ? 1 : 0;
#endif
      newAddress += newAddress; // Convert from word address to byte address
      address = newAddress;
      verifySpace();
    }
    else if(ch == STK_UNIVERSAL) {
      // UNIVERSAL command is ignored
      getNch(4);
      putch(0x00);
    }
    /* Write memory, length is big endian and is in bytes */
    else if(ch == STK_PROG_PAGE) {
      // PROGRAM PAGE - we support flash programming only, not EEPROM
      uint8_t *bufPtr;
      uint16_t addrPtr;

      getch();			/* getlen() */
      length = getch();
      getch();

      // If we are in RWW section, immediately start page erase
      if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);

      // While that is going on, read in page contents
      bufPtr = buff;
      do *bufPtr++ = getch();
      while (--length);

      // If we are in NRWW section, page erase has to be delayed until now.
      // Todo: Take RAMPZ into account
      if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);

      // Read command terminator, start reply
      verifySpace();

      // If only a partial page is to be programmed, the erase might not be complete.
      // So check that here
      boot_spm_busy_wait();

#ifdef VIRTUAL_BOOT_PARTITION
      if ((uint16_t)(void*)address == 0) {
        // This is the reset vector page. We need to live-patch the code so the
        // bootloader runs.
        //
        // Move RESET vector to WDT vector
        uint16_t vect = buff[0] | (buff[1]<<8);
        rstVect = vect;
        wdtVect = buff[8] | (buff[9]<<8);
        vect -= 4; // Instruction is a relative jump (rjmp), so recalculate.
        buff[8] = vect & 0xff;
        buff[9] = vect >> 8;

        // Add jump to bootloader at RESET vector
        buff[0] = 0x7f;
        buff[1] = 0xce; // rjmp 0x1d00 instruction
      }
#endif

      // Copy buffer into programming buffer
      bufPtr = buff;
      addrPtr = (uint16_t)(void*)address;
      ch = SPM_PAGESIZE / 2;
      do {
        uint16_t a;
        a = *bufPtr++;
        a |= (*bufPtr++) << 8;
        __boot_page_fill_short((uint16_t)(void*)addrPtr,a);
        addrPtr += 2;
      } while (--ch);

      // Write from programming buffer
      __boot_page_write_short((uint16_t)(void*)address);
      boot_spm_busy_wait();

#if defined(RWWSRE)
      // Reenable read access to flash
      boot_rww_enable();
#endif

    }
    /* Read memory block mode, length is big endian.  */
    else if(ch == STK_READ_PAGE) {
      // READ PAGE - we only read flash
      getch();			/* getlen() */
      length = getch();
      getch();

      verifySpace();
#ifdef VIRTUAL_BOOT_PARTITION
      do {
        // Undo vector patch in bottom page so verify passes
        if (address == 0)       ch=rstVect & 0xff;
        else if (address == 1)  ch=rstVect >> 8;
        else if (address == 8)  ch=wdtVect & 0xff;
        else if (address == 9) ch=wdtVect >> 8;
        else ch = pgm_read_byte_near(address);
        address++;
        putch(ch);
      } while (--length);
#else
#ifdef __AVR_ATmega1280__
//      do putch(pgm_read_byte_near(address++));
//      while (--length);
      do {
        uint8_t result;
        __asm__ ("elpm %0,Z\n":"=r"(result):"z"(address));
        putch(result);
        address++;
      }
      while (--length);
#else
      do putch(pgm_read_byte_near(address++));
      while (--length);
#endif
#endif
    }

    /* Get device signature bytes  */
    else if(ch == STK_READ_SIGN) {
      // READ SIGN - return what Avrdude wants to hear
      verifySpace();
      putch(SIGNATURE_0);
      putch(SIGNATURE_1);
      putch(SIGNATURE_2);
    }
    else if (ch == 'Q') {
      // Adaboot no-wait mod
      watchdogConfig(WATCHDOG_16MS);
      verifySpace();
    }
    else {
      // This covers the response to commands like STK_ENTER_PROGMODE
      verifySpace();
    }
    putch(STK_OK);
  }

Intorno alla riga 292, metti l'attivazione del chip in ricezione.

  // Adaboot no-wait mod
  ch = MCUSR;
  MCUSR = 0;
  if (!(ch & _BV(EXTRF))) appStart();
***************** QUI  ************
#if LED_START_FLASHES > 0

Questo punto è buono perché è subito dopo la disattivazione di un eventuale watchdog e subito prima del proseguo del bootloader, dove viene qualche riga più sotto aperta la seriale.

Usa però la manipolazione diretta delle porte logiche, perché altrimenti digitalWrite non funziona (non hai il core Arduino caricato)..
A questo punto, la cosa diventa più maia....
Alla riga 503 c'è la funzinoe putch, che spedisce un byte sulla seriale. Qui dovresti mettere la funzione per attivare il Max in trasmissione.
Alla riga 533 c'è la funzione getch, che riceve un byte. Qui invece devi mettere la funzione per attivare il Max in ricezione.

Fatto questo, compili, flashi e... preghi :wink:

leo72:
Usa però la manipolazione diretta delle porte logiche

Leo, ma per chi mi hai preso? :stuck_out_tongue: vedo se riesco in qualche maniera a farmi aiutare a scuola :slight_smile:

sciorty:

leo72:
Usa però la manipolazione diretta delle porte logiche

Leo, ma per chi mi hai preso?

Repetita iuvant :stuck_out_tongue:

Ma quale repetita, mai fatte queste cose :frowning:

Stavo vedendo la funzione flash_led, che dovrebbe essere quella che viene ripetuta due volte per segnalare l'inizio di caricamento dello sketch facendo accendere il led13. Pensavo che magari avrei potuto prenderne spunto e semplicemente andare a modificare la porta :grin: :

void flash_led(uint8_t count) {
  do {
    TCNT1 = -(F_CPU/(1024*16));
    TIFR1 = _BV(TOV1);
    while(!(TIFR1 & _BV(TOV1)));
#ifdef __AVR_ATmega8__
    LED_PORT ^= _BV(LED);
#else
    LED_PIN |= _BV(LED);
#endif
    watchdogReset();
  } while (--count);
}

Ma sono solo arrivato a capire che se il micro non è un atmega 8 fa un operazione or tra LED_PIN e la funzione _BV(LED)..
Ma tutta queste variabili cosa contengono? Non trovo le assegnazioni!

PS. Forse stava meglio in Software questo topic :stuck_out_tongue_closed_eyes:

Comunque leggendo nel reference:

PORTD is a built-in constant that refers to the output states of digital pins 0,1,2,3,4,5,6,7
PORTD = B00110001; we have made pins 2,3 & 7 HIGH.

DDRD = DDRD | B11111100; // set direction bits for pins 2 to 7, leave 0 and 1 untouched (xx | 00 == xx)

Non si leggevano in ordine crescente da sinistra verso destra?

Ma carine queste cose comunque :slight_smile:

Per mettere in input un pin, devi prima vedere dalla piedinatura del microcontrollore a quale porta appartiene e poi imposti il suo bit.
Ad esempio, il pin D10 di Arduino è il piedino PB3, quindi porta "B", bit "3".
Ora, sapendo questo, col link che ti ho dato, vedi che il registro DDx regola la direzione dei pin di una porta.
Quindi se vuoi mettere quel pin come output, devi mettere a 1 il bit 3.

DDRB |= (1<<3);

Adesso lo stato del pin lo regoli con il registro PORTx corrispondente, quindi PORTB.
1 sul bit 3 mette "HIGH" quel pin, 0 lo mette LOW:

PORTB |= (1<<3); //mette HIGH
PORTB &= ~(1<<3); //mette LOW

Se vuoi metterlo su output, devi metterlo s

Eh si, stavo giusto leggendo questi operatori e ora me li studio e provo un po' :slight_smile: quindi posso adoperare queste porte nel file optiboot.c no?

Supponiamo che usi il pin 2 pensavo di fare così:
per impostare il pin come output DDRD |=B0000100

nella parte di trasmissione PORTD |=B0000100

nella parte di ricezione PORTD &=B1111011