Publicidade:

quarta-feira, 30 de dezembro de 2015

Arduino - Como gravar o bootloader (e sketch) em um Atmega328P-PU Standalone

Nesse artigo vou tentar descrever os passos que segui até conseguir carregar o bootloader em um Atmega328p-PU. 

Já fazia um tempo que eu havia comprado alguns kits para Arduino Standalone, como esse mostrado na imagem abaixo, mas que por preguiça acabaram ficando guardados. Hoje então resolvi fazer alguns testes com um e gravar o bootloader nele. Ele possui alguns componentes mínimos que geralmente são usados em placas Standalones, como o regulador de tensão, capacitores, o cristal oscilador, o Atmega328P e um slote onde CI será encaixado na placa.



O Procedimento não é algo tão complicado, mas podemos dizer que é um pouco chato de se fazer, pelo menos foi a impressão que tive ao tentar e vendo vários comentários na internet de pessoas que tiveram as mesmas dificuldades. Por algum motivo não consegui logo de começo, mesmo seguindo os procedimentos descritos em alguns artigos por ai. Mas depois de insistir um pouco consegui sem maiores problemas, por isso resolvi escrever mostrando os passos que segui.

Quando trabalhamos com o Arduino, os pinos do Atmega não ficam explicitos, então a imagem abaixo é bom guia pra nos orientarmos qual pino do Atmega é o seu correspondente do Arduino.


Mas antes de iniciarmos a ligação entre o Arduino e o Atmega, vamos deixar o Arduino Uno configurado para ser um programador ISP. Para isso, abra o arquivo que já está na própria IDE do Arduino, através do menu Arquivo --> exemplos --> ArduinoISP.




Com o arquivo Aberto, basta fazer o upload para o Arquivo Uno.

Com o Arduino Uno Configurado, iremos agora fazer a ligação entre os componentes, conforme a imagem abaixo. Nesse esquema que está abaixo, inicialmente eu não tinha utilizado aquele resistor de pullup de 10K para o pino de reset do Atmega. Mas depois de tentar algumas vezes e não conseguir, encontrei um artigo no qual o mesmo foi utilizado, então resolvi mante-lo, apesar de posteriormente eu ter testado sem o mesmo, e o upload foi feito do mesmo jeito. Mas aconselho manter o resistor. No Arduino Uno, também podemos notar um capacitor eletrolítico de 10uF. Também não tenho certeza se ele é exatamente necessário, mas por desencargo, mantive o mesmo conectado



Pinagem:


  ATmega328 | Arduino Uno 
19 | 13
18 | 12
17 | 11
1 (reset) | 10         
7, 20 |  5V  
  8, 22 |  Gnd  

Aqui podemos ver a montagem que eu fiz (ainda sem o resistor de 10K)



Com todos os componentes conectados, agora é hora de configurarmos a IDE do Arduino para fazermos o upload do bootloader. Pra isso iremos configurar a IDE como se estivéssemos programando um "Arduino Duemilanove or Diecimila", indo em Ferramentas --> Placa --> Arduino Duemilanove or Diecimila, como mostrado na imagem abaixo.




O Próximo passo é informar que iremos utilizar o ATmega328. Pra isso vamos em ferramentas --> Processador --> ATmega328.



O próximo passo agora é configurar o Programador para "Arduino as ISP".



Agora, com todos os passos anteriores configurados, é só gravar o booteloder. Pra isso vá em Ferramentas --> Gravar Bootloader. Como mostrado na imagem abaixo. Aguarde terminar o procedimento. Se tudo ocorreu certo, o pino 13 irá piscar, caso tenha um led conectado a ele. Apesar que nas imagens não aparece o led ligado, é interessante para fins de testes ligar um resistor e um led em série no pino 19 do Atmega (Conhecido como pino 13 no Arduino).



Com o bootloader gravado, é hora de testar o upload de um sketch qualquer. Pra isso abra o sketch que você deseja gravar no Atmega. faça as ligações dos componentes que serão necessários e então faça o upload da sketch, através da Opção "Carregar usando o programador" como mostrado na imagem abaixo.

Pra fugir um pouco do blink padrão do Arduino, fiz um blink "mais legal", que não utiliza o delay. e sim uma classe temporizadora desenvolvida por mim, que ainda não falei sobre ela, mas em breve farei um artigo explicando como ela funciona. O Código está mais abaixo.





/*********************************************************************************************************
************************************CLASSE MYTIMER********************************************************
**********************************************************************************************************/
struct MyTimerSequence{
  unsigned long * times;
  unsigned int size;
  unsigned int repeat;
};
class MyTimer{
  private:
    boolean          _enable;
    unsigned long    _mref;
    unsigned long    _mref_disable;
    unsigned long    _time_disable;
    MyTimerSequence *_sequences;
    long             _lag;
    int              _quantidade;
    int              _index_time;
    int              _last_index_time;
    int              _index_sequence;
    int              _last_index_sequence;
    void             (*_onChanging)( int index_sequence, int index_time ); //ponteiro para funcao do evento onChanging
    void             (*_onEnable)();
    void             (*_onDisable)();
  public:
    MyTimer(MyTimerSequence * sequences, int quantidade, long lag, unsigned long mref){
      _lag = lag;
      _mref = mref;
      _sequences = sequences;
      _quantidade = quantidade;
      _time_disable = 0;
      _enable = false;
      _undetermine();
    }
     
    void _undetermine(){
      _index_time          = -1;
      _last_index_time     = -1;
      _index_sequence      = -1;
      _last_index_sequence = -1;
      update();
    }
    void setSequences(MyTimerSequence * sequences, int quantidade){
      _sequences = sequences;
      _quantidade = quantidade;
      _undetermine();
      update();
    };
    void setMillisRef(unsigned long mref)                  { _mref    = mref;        _undetermine();    }
    void setLag(long lag)                                  { _lag     = lag;         _undetermine();    }
    boolean isChanging()                                   { return (( _index_time != _last_index_time)||( _index_sequence != _last_index_sequence))&&(_enable); }
    boolean isIndexTime(int index_sequence, int index_time){ return ( _index_time == index_time)&&(_index_sequence == index_sequence)&&(_enable);   }
    void setOnChanging( void (*onChanging)(int, int) )     { _onChanging = onChanging;                  }
    void setOnEnable  ( void (*onEnable)() )               { _onEnable   = onEnable;                    }
    void setOnDisable ( void (*onDisable)() )              { _onDisable  = onDisable;                   }
    void update();
     
    void enable()  { _enable = true;  if ( _onEnable )  { (*_onEnable )(  ); }  } 
    void disable() { _enable = false; if ( _onDisable ) { (*_onDisable)(  ); }  } 
     
    void setTimeDisable(unsigned long time){ _time_disable = time; _mref_disable = millis(); } 
};
void MyTimer::update(){
  if ((millis() - _time_disable > _mref_disable) && (_time_disable > 0)) { disable(); }  //verifica se está configurado pra desabilitar por time
  if (!_enable) { return; }
   
  unsigned long s = 0;
  for (int i=0; i<_quantidade;i++){ 
    for (int j=0; j<_sequences[i].repeat; j++){
      for (int k=0; k<_sequences[i].size; k++){
        s += _sequences[i].times[k]; 
      }
    }
  }
  long adjustment      = _mref % s;
  long rest            = (millis() + s - adjustment - _lag) % s;
  _last_index_time     = _index_time;
  _last_index_sequence = _index_sequence;
   
   
  boolean ind_break = false;
  s = 0;
  for (int i=0; i<_quantidade;i++){ 
    for (int j=0; j<_sequences[i].repeat; j++){
      for (int k=0; k<_sequences[i].size; k++) {
        s += _sequences[i].times[k]; 
        if (rest < s) {  
          _index_time = k; 
          _index_sequence = i;
          ind_break = true;
          break; 
        } 
      }
      if (ind_break) { break; }
    }
    if (ind_break) { break; }
  }
  if ( isChanging() && _onChanging ) { (*_onChanging)(  _index_sequence, _index_time  ); }
}
/*********************************************************************************************************
************************************FIM CLASSE MYTIMER****************************************************
**********************************************************************************************************/
 
unsigned long seq01[] = {750};
unsigned long seq02[] = {60, 60};
unsigned long seq03[] = {200};
 
MyTimerSequence sequences[] = { 
                                 { seq01, sizeof(seq01)/sizeof(unsigned long), 1},
                                 { seq02, sizeof(seq02)/sizeof(unsigned long), 10},
                                 { seq03, sizeof(seq03)/sizeof(unsigned long), 1},
                                 { seq02, sizeof(seq02)/sizeof(unsigned long), 6},
                                 { seq03, sizeof(seq03)/sizeof(unsigned long), 1},
                                 { seq02, sizeof(seq02)/sizeof(unsigned long), 3}
                              } ;
 
  
MyTimer t1(sequences, sizeof(sequences)/sizeof(MyTimerSequence), 0, 0);  //tem um atraso de 100 milissegundos em relação a referencia 0
    
void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
   
  t1.setOnChanging( onChanging_t1 );
  t1.enable();
}
  
void loop() {  
  t1.update();
}
  
void onChanging_t1(int sequence, int index ) {
  digitalWrite(13, LOW); 
  if (index == 0 && (sequence == 1 || sequence == 3 || sequence == 5)){ digitalWrite(13, HIGH);  }
   
}


E como fazer o upload através do Arduino Mega (ou outras placas Arduino)?

O Mesmo procedimento mostrado anteriormente também pode ser feito através do Arduino Mega, ou ainda com outros Arduinos. O Procedimento é fazer o upload da sketch ArduinoISP (mostrado lá no início) para o Arduino Mega e seguir os demais passos mostrados anteriormente, apenas tomando o cuidado na hora de ligar os pinos, que são diferentes do uno:


    ATmega328 | Arduino Mega
            19 | 52 (SCK)
              18 | 50 (MISO)
              17 | 51 (MOSI)
  1 (reset) | 53 (RST)
7, 20 | 5V   
  8, 22 | Gnd   

Para outras Placas, certifique-se de utilizar os pinos corretos, pra isso terá que descobrir quais são os pinos ISP: SCK, MISO, MOSI e RST


Um comentário:

  1. Obrigado por compartilhar seu conhecimento!!!
    Amigo você teria condições de ensinar como gravar o bootloader em um atmega 16/32 e usar na
    IDE do Arduino? Ja procurei em vários lugares e são muito confusos e superficiais e não ensinam o "como" fazer em palavras mais simples...Obrigado desde ja...

    ResponderExcluir