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