Matriz de leds – Parte 2 – mdl_print()
A alguns dias atrás, quando mostrei meu trabalho da matriz de leds para meu amigo Vinicius, ele me sugeriu “por que não fazer um programinha que gere essa matriz que é lida pelo arduino, pra ser mais fácil escrever na matriz de leds?”.
Dias depois, falando com outro amigo meu, Gabriel, pelo msn, ele sugeriu “e por que não então fazer algo como um print?”
Pois, depois de algum trabalho, foi o que fiz. Deixo aqui registrado tanto o código (até o momento, estou ainda usando a IDE do arduino, e não usando a técnica descrita no post anterior) quanto um pequeno vídeo com o resultado ![]()
Apenas por curiosidade, a IDE reportou o tamanho:
Binary sketch size: 2958 bytes (of a 126976 byte maximum)
Então, posso usar essa minha função em outros projetos, pois ainda tenho bastante espaço, hahahah ![]()
Para escrever, então, na matriz, usa-se a seguinte função que eu criei:
mdl_print(mensagem, tempo);
Onde a mensagem é a mensagem que deve aparecer no display (e deve ser composta APENAS POR LETRAS MAIUSCULAS).
E o tempo (em milisegundos) do deslocamento que a mensagem faz para a esquerda no display.
mdl significa “Matriz De Leds”
Chega de bláblálá, aqui está um olá mundo: (vídeo)
E o código (é um pouco grande por causa das definições de cada letra) (E o maldito wordpress continua bugando minha identação toda ¬¬”).
typedef struct {
int largura;
boolean* matriz;
} letra;
boolean __la[5][3] =
{
{1,1,1},
{1,0,1},
{1,1,1},
{1,0,1},
{1,0,1}
};
letra __a = { 3, (boolean*)__la };
boolean __lb[5][3] =
{
{1,1,0},
{1,0,1},
{1,1,0},
{1,0,1},
{1,1,0}
};
letra __b = { 3, (boolean*)__lb };
boolean __lc[5][3] =
{
{0,1,1},
{1,0,0},
{1,0,0},
{1,0,0},
{0,1,1}
};
letra __c = { 3, (boolean*)__lc };
boolean __ld[5][3] =
{
{1,1,0},
{1,0,1},
{1,0,1},
{1,0,1},
{1,1,0}
};
letra __d = { 3, (boolean*)__ld };
boolean __le[5][2] =
{
{1,1},
{1,0},
{1,1},
{1,0},
{1,1}
};
letra __e = { 2, (boolean*)__le };
boolean __lf[5][2] =
{
{1,1},
{1,0},
{1,1},
{1,0},
{1,0}
};
letra __f = { 2, (boolean*)__lf };
boolean __lg[5][4] =
{
{1,1,1,1},
{1,0,0,0},
{1,0,1,1},
{1,0,0,1},
{1,1,1,1}
};
letra __g = { 4, (boolean*)__lg };
boolean __lh[5][3] =
{
{1,0,1},
{1,0,1},
{1,1,1},
{1,0,1},
{1,0,1}
};
letra __h = { 3, (boolean*)__lh };
boolean __li[5][1] =
{
{1},
{1},
{1},
{1},
{1}
};
letra __i = { 1, (boolean*)__li };
boolean __lj[5][3] =
{
{0,0,1},
{0,0,1},
{0,0,1},
{1,0,1},
{1,1,1}
};
letra __j = { 3, (boolean*)__lj };
boolean __lk[5][4] =
{
{1,0,0,1},
{1,0,1,0},
{1,1,0,0},
{1,0,1,0},
{1,0,0,1}
};
letra __k = { 4, (boolean*)__lk };
boolean __ll[5][2] =
{
{1,0},
{1,0},
{1,0},
{1,0},
{1,1}
};
letra __l = { 2, (boolean*)__ll };
boolean __lm[5][5] =
{
{1,0,0,0,1},
{1,1,0,1,1},
{1,1,0,1,1},
{1,0,1,0,1},
{1,0,0,0,1}
};
letra __m = { 5, (boolean*)__lm };
boolean __ln[5][4] =
{
{1,0,0,1},
{1,1,0,1},
{1,1,0,1},
{1,0,1,1},
{1,0,0,1}
};
letra __n = { 4, (boolean*)__ln };
boolean __lo[5][3] =
{
{0,1,0},
{1,0,1},
{1,0,1},
{1,0,1},
{0,1,0}
};
letra __o = { 3, (boolean*)__lo };
boolean __lp[5][3] =
{
{1,1,0},
{1,0,1},
{1,1,0},
{1,0,0},
{1,0,0}
};
letra __p = { 3, (boolean*)__lp };
boolean __lq[5][4] =
{
{0,1,0,0},
{1,0,1,0},
{1,0,1,0},
{1,0,1,0},
{0,1,0,1}
};
letra __q = { 4, (boolean*)__lq };
boolean __lr[5][3] =
{
{1,1,0},
{1,0,1},
{1,1,0},
{1,0,1},
{1,0,1}
};
letra __r = { 3, (boolean*)__lr };
boolean __ls[5][3] =
{
{0,1,1},
{1,0,0},
{0,1,0},
{0,0,1},
{1,1,0}
};
letra __s = { 3, (boolean*)__ls };
boolean __lt[5][3] =
{
{1,1,1},
{0,1,0},
{0,1,0},
{0,1,0},
{0,1,0}
};
letra __t = { 3, (boolean*)__lt };
boolean __lu[5][4] =
{
{1,0,0,1},
{1,0,0,1},
{1,0,0,1},
{1,0,0,1},
{0,1,1,0}
};
letra __u = { 4, (boolean*)__lu };
boolean __lv[5][5] =
{
{1,0,0,0,1},
{1,0,0,0,1},
{0,1,0,1,0},
{0,1,0,1,0},
{0,0,1,0,0}
};
letra __v = { 5, (boolean*)__lv };
boolean __lw[5][5] =
{
{1,0,0,0,1},
{1,0,1,0,1},
{1,0,1,0,1},
{0,1,1,1,0},
{0,1,0,1,0}
};
letra __w = { 5, (boolean*)__lw };
boolean __lx[5][3] =
{
{1,0,1},
{1,0,1},
{0,1,0},
{1,0,1},
{1,0,1}
};
letra __x= { 3, (boolean*)__lx };
boolean __ly[5][3] =
{
{1,0,1},
{1,0,1},
{0,1,0},
{0,1,0},
{0,1,0}
};
letra __y= { 3, (boolean*)__ly };
boolean __lz[5][4] =
{
{1,1,1,1},
{0,0,1,0},
{0,1,0,0},
{1,0,0,0},
{1,1,1,1}
};
letra __z= { 4, (boolean*)__lz };
boolean __lespaco[5][1] =
{
{0},
{0},
{0},
{0},
{0}
};
letra __espaco = { 1, (boolean*)__lespaco };
boolean __lesclamacao[5][1] =
{
{1},
{1},
{1},
{0},
{1}
};
letra __esclamacao = { 1, (boolean*)__lesclamacao };
boolean __ldoisPontos[5][1] =
{
{0},
{1},
{0},
{1},
{0}
};
letra __doisPontos = { 1, (boolean*)__ldoisPontos };
boolean __lfechaParenteses[5][2] =
{
{1,0},
{0,1},
{0,1},
{0,1},
{1,0}
};
letra __fechaParenteses = { 2, (boolean*)__lfechaParenteses };
letra* __letras[] = {&__a, &__b, &__c, &__d, &__e, &__f, &__g, &__h,
&__i, &__j, &__k, &__l, &__m, &__n, &__o, &__p,
&__q, &__r, &__s, &__t, &__u, &__v, &__w, &__x,
&__y, &__z, &__espaco, &__esclamacao, &__doisPontos, &__fechaParenteses};
const int N_LINHAS = 5;
const int N_COLUNAS = 8;
int lin[N_LINHAS] = {27, 33, 39, 45, 47};
int col[N_COLUNAS] = {43, 41, 37, 35, 31, 29, 25, 23};
void mdl_init() {
for(int i = 0; i < N_LINHAS; i++)
pinMode(lin[i], OUTPUT);
for(int j = 0; j < N_COLUNAS; j++)
pinMode(col[j], OUTPUT);
__mdl_apagarTodos();
}
void __mdl_ligarLed(int i, int j) {
digitalWrite(lin[i], LOW);
digitalWrite(col[j], HIGH);
}
void __mdl_desligarLed(int i, int j) {
digitalWrite(lin[i], HIGH);
digitalWrite(col[j], LOW);
}
void __mdl_apagarTodos() {
for(int i = 0; i < N_LINHAS; i++)
digitalWrite(lin[i], HIGH);
for(int j = 0; j < N_COLUNAS; j++)
digitalWrite(col[j], LOW);
}
void mdl_mostraDesenho(boolean mensagem[5][8], unsigned long tempo) {
unsigned long proximo = millis() + tempo;
while(1) {
for(int i = 0; i < N_LINHAS; i++) {
for(int j = 0; j < N_COLUNAS; j++) {
if(mensagem[i][j]) {
__mdl_ligarLed(i, j);
delayMicroseconds(200);
__mdl_desligarLed(i, j);
}
}
}
if(millis() >= proximo)
return;
}
}
void mdl_print(char* str, int tempo) {
boolean mensagem[N_LINHAS][N_COLUNAS] = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0}
};
mdl_mostraDesenho(mensagem, tempo);
int i = 0;
int indice_letra = 0;
for(int i = 0; str[i] != '\0'; i++) {
switch(str[i]) {
case ' ':
indice_letra = 26;
break;
case '!':
indice_letra = 27;
break;
case ':':
indice_letra = 28;
break;
case ')':
indice_letra = 29;
break;
default:
indice_letra = str[i] - 65;
}
// Mostra a proxima letra
for(int j = 0; j < (*(__letras[indice_letra])).largura; j++) {
for(int k = 0; k < N_LINHAS; k++)
for(int l = 0; l < N_COLUNAS - 1; l++)
mensagem[k][l] = mensagem[k][l + 1];
for(int k = 0; k < N_LINHAS; k++)
mensagem[k][N_COLUNAS-1] = *((*(__letras[indice_letra])).matriz + (*(__letras[indice_letra])).largura*k + j);
// Mostra o desenho atual
mdl_mostraDesenho(mensagem, tempo);
}
// Espaco entre letras
for(int k = 0; k < N_LINHAS; k++)
for(int l = 0; l < N_COLUNAS - 1; l++)
mensagem[k][l] = mensagem[k][l + 1];
for(int k = 0; k < N_LINHAS; k++)
mensagem[k][N_COLUNAS-1] = 0;
// Mostra o desenho atual
mdl_mostraDesenho(mensagem, tempo);
}
}
void setup() {
mdl_init();
}
void loop() {
mdl_print("OLA MUNDO! ESTOU VIVO :) ", 180);
}
Programando o Arduino com avr-gcc + avr-lib + avrdude
Conversando ontem com o João Pizani (http://joaopizani.hopto.org/), descobri uma outra forma de programar meu pequeno ATmega1280 (o microcontrolador que vem no meu arduino Mega).
Eu utilizava a ferramenta do arduino (Arduino Software?) (http://arduino.cc/en/Main/Software), que é muito boa, na verdade. Mas, para conhecer, resolvi dar uma olhada no avr-gcc.
Como eu uso Ubuntu 10.10, para instalar as coisas é fácil assim:
sudo apt-get install gcc-avr avr-libc avrdude binutils-avr
Feito isso, segui para um pequeno projeto (sim, piscar leds. hahaha!).
(main.c):
#include <avr/io.h>
#include <util/delay.h>
int main(void) {
DDRB = 0xff;
while(1) {
_delay_ms(200);
PORTB = 0xff;
_delay_ms(200);
PORTB = 0x00;
}
return 0;
}
Não é legal ? o lib-avr tem várias funções prontas (http://www.nongnu.org/avr-libc/user-manual/modules.html), por exemplo, _delay_ms que eu usei no programinha acima. Lembrando que pra usar _delay_ms deve-se passar algum tipo de otimização pro compilador (-O1). (http://www.nongnu.org/avr-libc/user-manual/group__util__delay.htm)
Feito isso, hora de compilar.
O próprio Pizani me enviou o makefile que ele usa, mas eu tive que fazer algumas modificações pra funcionar, pelo fato de que meu arduino é diferente do dele. O código é grande… Role um pouco, ainda não terminei o post.
######### AVR Project Makefile Template ######### ###### ###### ###### Copyright (C) 2003-2005,Pat Deegan, ###### ###### Psychogenic Inc ###### ###### All Rights Reserved ###### ###### ###### ###### You are free to use this code as part ###### ###### of your own applications provided ###### ###### you keep this copyright notice intact ###### ###### and acknowledge its authorship with ###### ###### the words: ###### ###### ###### ###### "Contains software by Pat Deegan of ###### ###### Psychogenic Inc (www.psychogenic.com)" ###### ###### ###### ###### If you use it as part of a web site ###### ###### please include a link to our site, ###### ###### http://electrons.psychogenic.com or ###### ###### http://www.psychogenic.com ###### ###### ###### #################################################### ##### Target Specific Details ##### ##### Customize these for your project ##### # Name of target controller # (e.g. 'at90s8515', see the available avr-gcc mmcu # options for possible values) MCU=atmega1280 # id to use with programmer # default: PROGRAMMER_MCU=$(MCU) # In case the programer used, e.g avrdude, doesn't # accept the same MCU name as avr-gcc (for example # for ATmega8s, avr-gcc expects 'atmega8' and # avrdude requires 'm8') PROGRAMMER_MCU=$(MCU) # Name of our project # (use a single word, e.g. 'myproject') PROJECTNAME=piscaPisca # Source files # List C/C++/Assembly source files: # (list all files to compile, e.g. 'a.c b.cpp as.S'): # Use .cc, .cpp or .C suffix for C++ files, use .S # (NOT .s !!!) for assembly source code files. PRJSRC=main.c # Macro definition DEFINES=-DF_CPU=16000000UL # additional includes (e.g. -I/path/to/mydir) INC= # libraries to link in (e.g. -lmylib) LIBS= # Optimization level, # use s (size opt), 1, 2, 3 or 0 (off) OPTLEVEL=1 ##### AVR Dude 'writeflash' options ##### ##### If you are using the avrdude program ##### (http://www.bsdhome.com/avrdude/) to write ##### to the MCU, you can set the following config ##### options and use 'make writeflash' to program ##### the device. # programmer id--check the avrdude for complete list # of available opts. These should include stk500, # avr910, avrisp, bsd, pony and more. Set this to # one of the valid "-c PROGRAMMER-ID" values # described in the avrdude info page. # AVRDUDE_PROGRAMMERID=arduino # port--serial or parallel port to which your # hardware programmer is attached # AVRDUDE_PORT=/dev/ttyUSB0 # arduino needs this set up as 19200 AVRDUDE_BAUD=-b 57600#19200 #################################################### ##### Config Done ##### ##### ##### ##### You shouldn't need to edit anything ##### ##### below to use the makefile but may wish ##### ##### to override a few of the flags ##### ##### nonetheless ##### ##### ##### #################################################### ##### Flags #### # HEXFORMAT -- format for .hex file output HEXFORMAT=ihex # compiler CFLAGS=-I. $(INC) $(DEFINES) -g -mmcu=$(MCU) -O$(OPTLEVEL) \ -fpack-struct -fshort-enums \ -funsigned-bitfields -funsigned-char \ -Wall -Wstrict-prototypes \ -Wa,-ahlms=$(firstword \ $(filter %.lst, $(<:.c=.lst))) # c++ specific flags CPPFLAGS=-fno-exceptions \ -Wa,-ahlms=$(firstword \ $(filter %.lst, $(<:.cpp=.lst))\ $(filter %.lst, $(<:.cc=.lst)) \ $(filter %.lst, $(<:.C=.lst))) # assembler ASMFLAGS =-I. $(INC) -mmcu=$(MCU) \ -x assembler-with-cpp \ -Wa,-gstabs,-ahlms=$(firstword \ $(<:.S=.lst) $(<.s=.lst)) # linker LDFLAGS=-Wl,-Map,$(TRG).map -mmcu=$(MCU) \ -lm $(LIBS) ##### executables #### CC=avr-gcc OBJCOPY=avr-objcopy OBJDUMP=avr-objdump SIZE=avr-size AVRDUDE=avrdude REMOVE=rm -f ##### automatic target names #### TRG=$(PROJECTNAME).out DUMPTRG=$(PROJECTNAME).s HEXROMTRG=$(PROJECTNAME).hex HEXTRG=$(HEXROMTRG) $(PROJECTNAME).ee.hex GDBINITFILE=gdbinit-$(PROJECTNAME) # Define all object files. # Start by splitting source files by type # C++ CPPFILES=$(filter %.cpp, $(PRJSRC)) CCFILES=$(filter %.cc, $(PRJSRC)) BIGCFILES=$(filter %.C, $(PRJSRC)) # C CFILES=$(filter %.c, $(PRJSRC)) # Assembly ASMFILES=$(filter %.S, $(PRJSRC)) # List all object files we need to create OBJDEPS=$(CFILES:.c=.o) \ $(CPPFILES:.cpp=.o)\ $(BIGCFILES:.C=.o) \ $(CCFILES:.cc=.o) \ $(ASMFILES:.S=.o) # Define all lst files. LST=$(filter %.lst, $(OBJDEPS:.o=.lst)) # All the possible generated assembly # files (.s files) GENASMFILES=$(filter %.s, $(OBJDEPS:.o=.s)) .SUFFIXES : .c .cc .cpp .C .o .out .s .S \ .hex .ee.hex .h .hh .hpp .PHONY: writeflash clean stats gdbinit stats # Make targets: # all, disasm, stats, hex, writeflash/install, clean all: $(TRG) disasm: $(DUMPTRG) stats stats: $(TRG) $(OBJDUMP) -h $(TRG) $(SIZE) $(TRG) hex: $(HEXTRG) writeflash: hex $(AVRDUDE) -c $(AVRDUDE_PROGRAMMERID) -v \ -p $(PROGRAMMER_MCU) -P $(AVRDUDE_PORT) -e \ $(AVRDUDE_BAUD) -U flash:w:$(HEXROMTRG) install: writeflash $(DUMPTRG): $(TRG) $(OBJDUMP) -S $< > $@ $(TRG): $(OBJDEPS) $(CC) $(LDFLAGS) -o $(TRG) $(OBJDEPS) #### Generating assembly #### # asm from C %.s: %.c $(CC) -S $(CFLAGS) $< -o $@ # asm from (hand coded) asm %.s: %.S $(CC) -S $(ASMFLAGS) $< > $@ # asm from C++ .cpp.s .cc.s .C.s : $(CC) -S $(CFLAGS) $(CPPFLAGS) $< -o $@ #### Generating object files #### # object from C .c.o: $(CC) $(CFLAGS) -c $< -o $@ # object from C++ (.cc, .cpp, .C files) .cc.o .cpp.o .C.o : $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ # object from asm .S.o : $(CC) $(ASMFLAGS) -c $< -o $@ #### Generating hex files #### # hex files from elf ##### Generating a gdb initialisation file ##### .out.hex: $(OBJCOPY) -j .text \ -j .data \ -O $(HEXFORMAT) $< $@ .out.ee.hex: $(OBJCOPY) -j .eeprom \ --change-section-lma .eeprom=0 \ -O $(HEXFORMAT) $< $@ ##### Generating a gdb initialisation file ##### ##### Use by launching simulavr and avr-gdb: ##### ##### avr-gdb -x gdbinit-myproject ##### gdbinit: $(GDBINITFILE) $(GDBINITFILE): $(TRG) @echo "file $(TRG)" > $(GDBINITFILE) @echo "target remote localhost:1212" \ >> $(GDBINITFILE) @echo "load" >> $(GDBINITFILE) @echo "break main" >> $(GDBINITFILE) @echo "continue" >> $(GDBINITFILE) @echo @echo "Use 'avr-gdb -x $(GDBINITFILE)'" #### Cleanup #### clean: $(REMOVE) $(TRG) $(TRG).map $(DUMPTRG) $(REMOVE) $(OBJDEPS) $(REMOVE) $(LST) $(GDBINITFILE) $(REMOVE) $(GENASMFILES) $(REMOVE) $(HEXTRG) $(REMOVE) *~ ##### EOF #####
No caso, o que eu tive que mudar foi:
MCU=atmega1280
(pois meu microcontrolador é um atmega1280).
Para descobrir os possíveis microcontroladores para essa variavel, pelo terminal é só dar um avrdude -p? e ele vai listar os possíveis componentes.
Ou, também é possível dar um cat em /etc/avrdude.conf .
PROJECTNAME=piscaPisca
Apenas o nome do projeto…
PRJSRC=main.c
Todos os arquivos usados no projeto que devem ser compilados. Nesse caso, só o main.c mesmo
DEFINES=-DF_CPU=16000000UL
Frequência do CPU. Acredito que é usado nas internas da lib pra calcular, por exemplo, tempo (pra usar _delay_ms…), mas estou chutando…
Da pra descobrir isso, por exemplo, olhando nas especificações do arduino que se está usando (por exemplo, http://arduino.cc/en/Main/ArduinoBoardMega).
AVRDUDE_BAUD=-b 57600#19200
Demorei pra descobrir. O arduino do Pizani deve ser um Duemilanove pra usar 19200. Usando esse valor pro meu, ele dava um erro ao tentar “upar” o código pro meu arduino
Ainda bem que eu achei esse maluco (http://www.matrixmultimedia.com/mmforums/viewtopic.php?f=26&t=7846) que descreveu:
Uno = 115200 Baud, stk500
Duemilanove, Nano (ATmega328) = 57600 Baud, stk500
Diecimilia, Duemilanove, Nano (Atmega168) = 19200 Baud, stk500
Mega2560 = 115200 Baud, stk500v2
Mega1280 = 57600 Baud, stk500
Acho que foi só isso que mudei (o código mais acima já é o modificado).
Feito isso, a seguinte sequencia compila e manda o programa pro arduino:
make
make hex
make install
Feito
Javascript: innerHTML vs createElement vs jQuery 1.7.1
Bom, já faz tempo que eu tenho essa dúvida. Afinal o que é “melhor” usar entre os três?
Uma pesquisa rápida pelo Google resultou em algumas respostas, mas mesmo assim resolvi fazer meus próprios testes.
O objetivo do teste vai ser criar 500 vezes o seguinte:
<div style="border: 1px solid red">Oi</div><br>
Para todos os testes, estou usando Google Chrome 16.0.912.63 e Firefox 9.0.1, ambos rodando no Ubuntu 10.10.
Processador core i5 2.53Ghz.
1. innerHTML
x = document.createElement("div");
document.body.appendChild(x);
d = new Date();
for(i=500;i--;)
x.innerHTML += "<div style='border:1px solid red'>Oi</div><br>";
console.log(new Date() - d);
Google Chrome: 1628ms
Firefox: 1629ms
2. createElement
x = document.createElement("div");
document.body.appendChild(x);
d = new Date();
for(i=500;i--;) {
var y = document.createElement("div");
y.style.border = "1px solid red";
y.innerHTML = "Oi";
var z = document.createElement("br");
x.appendChild(y);
x.appendChild(z);
}
console.log(new Date() - d);
Google Chrome: 10ms
Firefox: 35ms
3. jQuery 1.7.1
Para o jQuery, é necessário “importar” a biblioteca. Para isso, basta executar o seguinte código:
jq = document.createElement("script");
jq.src = "http://code.jquery.com/jquery-1.7.1.min.js";
document.body.appendChild(jq);
E finalmente:
x = document.createElement("div");
document.body.appendChild(x);
d = new Date();
for(i=500;i--;)
$(x).append($("<div>").css({"border": "1px solid red"}).text("oi")).append($("<br>"));
console.log(new Date() - d);
Firefox: 144ms
Até +
Matriz de leds + Arduino
Já faz tempo que eu queria fazer uma matriz de leds pra brincar com meu Arduino. Essa semana eu tirei um tempo pra isso. Comprei os componentes, fiz alguns testes e obtive sucesso
Meu primeiro teste foi uma pequena mensagem que vai passando pela matriz. Eu filmei tudo e coloquei no meu canal no youtube:
Componentes necessários:
- 40 Leds vermelhos 5mm
- 5 Resistores 220Ω
- 1 Plaquinha já perfurada
- Fios (que eu tirei de alguns pedaços de cabo de rede que eu tinha em casa)
- Estanho (pra soldar as coisas)
- Ferro de solda
Eu não sou muito bom com essas coisas de eletrônica, mas tenho tentado estudar… Usei como referência esse esquema, disponível no próprio site do Arduino:
http://arduino.cc/playground/Main/DirectDriveLEDMatrix
E aqui vão algumas fotos do resultado final. Soldagem lvl up! Devo estar no lvl 2, agora…
Próximo passo foi programar a matriz.
Bom, se você tentar ligar todos os leds de uma vez só, vai descobrir que não vai funcionar muito bem. Eles vão até ligar, mas o brilho vai ser tão fraco, que não vai ter graça… Para resolver esse problema, a ideia é ligar um led de cada vez, só que bem rápido. Tão rápido que o olho humano não perceba que o led está piscando. Esse meu código nada tem a ver com o da referência (do site do arduino), pois eu fiz ele do zero, afinal, essa é a parte mais legal do projeto: programar
const int FRAMES_ANIMACAO = 60;
const int VELOCIDADE = 220; //ms
const int N_LINHAS = 5;
const int N_COLUNAS = 8;
int lin[N_LINHAS] = {27, 33, 39, 45, 47};
int col[N_COLUNAS] = {43, 41, 37, 35, 31, 29, 25, 23};
int leds[N_LINHAS][FRAMES_ANIMACAO] = {
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
int i, j, k;
long long proximo;
void setup() {
for(i = 0; i < N_LINHAS; i++)
pinMode(lin[i], OUTPUT);
for(j = 0; j < N_COLUNAS; j++)
pinMode(col[j], OUTPUT);
k = 0;
proximo = VELOCIDADE;
apagarTudo();
}
void apagarTudo() {
int i, j;
for(i = 0; i < N_LINHAS; i++)
digitalWrite(lin[i], HIGH);
for(j = 0; j < N_COLUNAS; j++)
digitalWrite(col[j], LOW);
}
void loop()
{
for(i = 0; i < N_LINHAS; i++) {
for(j = 0; j < N_COLUNAS; j++) {
apagarTudo();
if(leds[i][j+k]) {
digitalWrite(lin[i], LOW);
digitalWrite(col[j], HIGH);
}
delayMicroseconds(250);
}
}
if(millis() >= proximo) {
proximo = millis() + VELOCIDADE;
k = (k >= FRAMES_ANIMACAO-N_COLUNAS ? 0 : k+1);
}
}
Por algum motivo o wordpress estragou minha identação ![]()
Essa matriz gigantesca é a matriz de leds com 1 onde eu quero que o led esteja ligado e 0 caso contrário.
A mensagem escrita é exatamente a mesma do vídeo, ou seja, “Eu te amo mila” seguido de um coração.
Essa matriz especifica foi feita na mão, porque eu estava fazendo e testando aos poucos (é que deu alguns problemas no início, e as letras estavam saindo da esquerda pra direita. O problema foi resolvido arrumando a posição dos pinos da variavel lin)
Claro que isso tudo é, ainda, muito primitivo. Eu não quero ter que ficar fazendo matrizes malucas pra escrever com os leds. Num futuro (espero que próximo) pretendo melhorar isso. Por enquanto minha ideia é fazer um programa que tome como entrada uma string e gere essa matriz pra mim
Implementando Autômatos Finitos
Pensei em fazer esse post quando estava conversando com o Felipe Silveira no MSN sobre a solução do seguinte problema:
“Dado um número de tamanho qualquer, verificar se esse número é ou não divisível por 3”
A solução pensada foi a (clássica) de somar todos os dígitos do número e verificar se a soma é ou não divisível por 3. (quem tem dúvida uma busca rápida pelo Google deve ajudar).
A partir disso, o problema era a implementação (sabemos que a complexidade do algoritmo é O(n), sendo n o número de dígitos). A proposta do Felipe foi a de literalmente somar e verificar se a soma fosse ou não divisível por 3.
- #define TAMANHO 100000
- int divisivel(char numero[]) {
- int soma = 0, i;
- for(i = 0; i < TAMANHO; i++)
- soma = (numero[i] - 48 + soma) % 3;
- return soma == 0;
- }
Compilando com gcc a.c -O0 e executando 1000 vezes com números com tamanho 100000 temos um tempo de aproximadamente 2.1s.
Fui eu tentar uma solução diferente, baseada em autômatos finitos, então usei if’s e switch-case’s. Ficou bem grande, e não muito eficiente (além de nada elegante). Como ficou muito grande, mostro aqui apenas a ideia básica (acredito que todos conseguem entender como seria o código completo).
- #define MAX 100000
- estado = 0;
- i = 0;
- while((simbolo = sentenca[i++]) != ‘’) {
- switch(estado) {
- case 0:
- if(simbolo == ‘0’) estado = 0;
- if(simbolo == ‘1’) estado = 1;
- if(simbolo == ‘2’) estado = 2;
- if(simbolo == ‘3’) estado = 0;
- // …
- break;
- case 1:
- if(simbolo == ‘0’) estado = 1;
- if(simbolo == ‘1’) estado = 2;
- if(simbolo == ‘2’) estado = 0;
- if(simbolo == ‘3’) estado = 1;
- // …
- break;
- case 2:
- // …
- break;
- default:
- estado = -1;
- }
- }
- return (estado == 0);
Para minha surpresa, o tempo médio para as mesmas condições que as anteriores foi de ~4s. Então pensei que o problema poderia ser os if’s, pois, como comentou o Felipe, o assembly gerado seria algo como uma série de branch’s. Porém eu pensei que se eu mudasse para apenas switch-case’s aninhados, o código seria convertido em Jump-Address-Tables e talvez isso pudesse ajudar um pouco. Ficou algo como:
- switch(estado) {
- case 0:
- switch(simbolo) {
- case ‘0’: estado = 0; break;
- case ‘1’: estado = 1; break;
- case ‘2’: estado = 2; break;
- case ‘3’: estado = 0; break;
- case ‘4’: estado = 1; break;
- case ‘5’: estado = 2; break;
- case ‘6’: estado = 0; break;
- case ‘7’: estado = 1; break;
- case ‘8’: estado = 2; break;
- case ‘9’: estado = 0; break;
- }
- break;
- case 1:
- // …
Isso melhorou um pouco, e reduziu o tempo para ~3.2s. Mas ainda assim a série de somas continua sendo uma ideia aparentemente melhor.
Inicialmente fiquei um pouco surpreso com a ideia de que um simples automato não fosse melhor do que uma série de somas. Mas depois de pensar mais um pouco, percebi que o problema era a minha implementação de automato. Então comecei a imaginar uma solução mais simples e mais elegante. E o que me ocorreu foi a seguinte:
- #define MAX 100000
- // simb.: 0 1 2 3 4 5 6 7 8 9
- int a[][10] = /* e0: */ {{0, 1, 2, 0, 1, 2, 0, 1, 2, 0},
- /* e1: */ {1, 2, 0, 1, 2, 0, 1, 2, 0, 1},
- /* e2: */ {2, 0, 1, 2, 0, 1, 2, 0, 1, 2}};
- char s[MAX];
- int estado;
- int tamanho;
- void calcula() {
- int i;
- estado = 0;
- for(i = 0; i < MAX; i++)
- estado = a[estado][s[i]-48];
- return estado == 0;
- }
E finalmente temos um tempo de ~1.5s
É claro que todas as soluções são praticamente iguais em termos de desempenho. Forçamos aqui apenas para verificar a pequena diferença entre as implementações e para ter ideias de como implementar de forma bonita e eficiente um automato finito (determinístico). A grande questão não está verdadeiramente na questão do desempenho, mas sim nas diferentes formas de como implementar a mesma solução. Como achei tudo isso muito interessante, resolvi tirar um tempo para fazer esse post



