Teclado construído por volta de 2009… com intuito de aprender a programação de 8051 utilizando linguagem Assembly e também de reutilizar material que iria para o lixo.
Um teclado Casio estava para ir pro lixo sem suas placas e botões. Então veio a ideia de criar um teclado MIDI com o 8051 (AT89S51). Fiz uma placa principal já com o AT89S51 , uma fonte de 5v, cristal e barras de pinos para facilitar projetos (seria uama espécie de Arduino pré histórico 😉 ). Inicialmente a dúvida seria como ler as 49 teclas… a solução foi ler em formato de matriz, onde temos 8 linhas e 7 colunas o que dá o máximo de 56 teclas. As teclas foram agrupadas e lidas de 8 em 8 sendo que cada uma das 7 colunas é acionada de cada vez. Sendo assim, acionando a coluna 1 podemos ler as teclas de 1 a 8 e acionando a coluna 2 teremos leitura das teclas 9 a 16 e por aí segue. Para isso, foi selecionado o circuito integrado ULN2003 para servir de buffer e selecionar a coluna que estará ativa em cada ciclo.
O programa abaixo fixa uma coluna por vez e lê linha por linha e depois seleciona a próxima coluna. O estado das teclas são armazenados e a cada loop são comparados.
- Se a tecla não estava pressionada e agora está? envia comando ON
- Se a tecla não estava pressionada e agora também não? Não faz nada
- Se a tecla estava pressionada e agora não está? Envia comando OFF
- Se a tecla estava pressionada e agora continua pressioanda? Não faz nada
;www.omecatronico.com.br
;Autor: Almir M Ventura
;Variaveis e constantes associadas——–
;R0 ponteiro dos bits!
linha EQU R1 ;indica qual linha estamos trabalhando…
nota EQU R2 ;byte que guarda a nota a ser tocada ou interrompida.
canal EQU R3 ;byte que guarda o canal MIDI ( vai de 1 a 16)
coluna EQU P1 ;PORT 1 varrerá as colunas e PORT 0 serão as linhas
;************************* RESET ************************
ORG 0h ; Diretiva de origem no endereco 0h
LJMP INICIO ; Salto para o inicio do programa
;***INT EXTERNA_0***
ORG 03h ; Externa 0
LJMP EXTERNA_0 ; Salta para Rotina Externa 0
;***INT TIMER_0***
ORG 0Bh ; TIMER 0
LJMP TIMER_0 ; Salta para Rotina TIMER 0
;***INT EXTERNA_1***
ORG 13h ; EXTERNA 1
LJMP EXTERNA_1 ; Salta para Rotina EXTERNA 1
;***INT TIMER_1***
ORG 1Bh ; TIMER 1
LJMP TIMER_1 ; Salta para Rotina TIMER 1
;***INT SERIAL***
ORG 23h ; SERIAL
LJMP SERIAL_COM ; Salta para Rotina SERIAL_COM
INICIO:
;vamos gravar nas memorias das teclas que elas estavam pressionadas…
;com isso ao ligar vamos enviar off para todas as teclas no primeiro loop!
MOV 20h, #0 ; “zera” memoria das teclas 1->8
MOV 21h, #0 ; 8->16
MOV 22h, #0 ; 17->24
MOV 23h, #0 ; 25->32
MOV 24h, #0 ; teclas 33->40
MOV 25h, #0 ; teclas 41->48
MOV 26h, #0 ; teclas 49->56
;configurando a porta serial…
;timer 1 para 19200 baud
;TH1 = 256 – ((Crystal / 192) / Baud)
;TH1 = 256 – ((11059000 / 192) / 57600)
;TH1 = 256 – ((57699) / 57600)
;TH1 = 256 – 1 = 255
MOV PCON,#128 ; seta o SMOD para dobrar o Baud Rate
MOV TMOD,#00100000B ;configurar Timer1 no modo 2 (8bits auto recarregavel)
MOV TH1,#255 ;seta para 57600 baud o timer1
MOV TL1,#255 ;
SETB TR1 ;liga timer 1
;configurando a serial com SM0=0 SM1=1(8-bit UART Set by Timer 1 (*)) e setando a flag ;TI (de transmissao concluida) …
mov scon,#01000010B
LOOP:
;vamos ler o canal MIDI selecionado
MOV A,P2 ;salva P2 -> Acc
ANL A,#15 ;vamos cortar o nible “alto” fazedno uma AND 00001111
MOV canal, A ; A -> canal (salvamos canal MIDI no formato 0000xxxx)
;lendo qual oitava será a primeira
;————————————————————————————-
; no loop abaixo sempre C é o estado atual e A vai com a “memoria” anterior
MOV R0,#20h ;inicia o ponteiro R0 com o 1 byte orientado “bit a bit” 20h
MOV nota,#46 ;inicia na 3 oitava? será?? =D
MOV coluna,#1 ;ativa primeira coluna
MOV linha,#1 ;inicicamos a primeira linha…
MINI_LOOP:
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 00000001 entao A ficará com 0000000X
MOV C, P0.0 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
MOV linha,#2 ;indicamos a segunda linha…
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 00000010 entao A ficará com 000000X0
MOV C, P0.1 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
MOV linha,#4 ;indicamos a terceira linha…
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 00000100 entao A ficará com 00000X00
MOV C, P0.2 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
MOV linha,#8 ;indicamos a quarta linha…
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 00001000 entao A ficará com 0000X000
MOV C, P0.3 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
MOV linha,#16 ;indicamos a quinta linha…
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 00010000 entao A ficará com 000X0000
MOV C, P0.4 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
MOV linha,#32 ;indicamos a sexta linha…
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 00100000 entao A ficará com 00X00000
MOV C, P0.5 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
MOV linha,#64 ;indicamos a setima linha…
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 01000000 entao A ficará com 0X000000
MOV C, P0.6 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
MOV linha,#128 ;indicamos a oitava linha…
MOV A, @R0 ;pega Byte da memoria das 8 teclas atuais e joga no A
ANL A, linha ;faz um and entre a e 10000000 entao A ficará com X0000000
MOV C, P0.7 ;copia valor do pino em C …e chama sub rotina pra resolver a bronca!
ACALL DECIDE ;decide e resolve tudo e depois volta…
INC nota ;incrementa nota…
INC R0 ;incrementa ponteiro dos bits para um novo byte…
MOV A,coluna ;joga em A a coluna atual
RL A ;rotaciona a esquerda
MOV coluna,A ;devolve pra porta a nova coluna
MOV linha,#1 ;inicicamos a primeira linha novamente…
CJNE R0,#27h, MINI_LOOP ;é o byte 27h ? entao continua no MINI_LOOP ate terminarem as 56 teclas…
SJMP LOOP ;era memoria 27h…fim do teclado! reiniciar tudo!
;Continua …
;************************* SUB ROTINAS *****************************************
DECIDE:
JNC PRESSIONADA ;testa se a tecla esta pressionada (c=0)
;nao esta pressionada (C=1)…entao ou ja estava assim ou acabou de soltar a tecla!
JZ SOLTOU ;Acc = 0? pula pra soltou!(significa que memoria constava tecla pressionada = 0)ou seja…soltou a tecla
RET ;Acc=1! apenas retorna… memoria era 1 (tecla estava em repouso…)
SOLTOU:
MOV A,linha ;linha indica qual bit sera setado!
ORL A, @R0 ;vamos atualizar o bit da memoria!!! ex se xxxxxxxx OR 00100000 teremos xx1xxxxx setando o bit que queriamos…
MOV @R0, A ;o resultado jogamos no byte o qual R0 aponta!
ACALL NOTA_OFF ;agora mandamos o comando de desligar
RET
PRESSIONADA:
;tecla pressionada… entao vamos ver se acabou de ser pressionada ou se ja estava pressionada anteriormente!
JNZ APERTOU ;Acc = 1? pula pra apertou!(significa que memoria constava tecla solta = 1)ou seja…apertou a tecla
RET ;Acc=0! apenas retorna… memoria era 0 (tecla estava pressionada ja…)
APERTOU:
MOV A,linha
CPL A ;complementa A ex se 0001 vai para 1110
ANL A, @R0 ;vamos atualizar o bit da memoria!!! ex se xxx1xxxx AND 11101111 teremos xxx0xxxx zerando o bit que queriamos…
MOV @R0, A ;o resultado jogamos no byte o qual R0 aponta!
ACALL NOTA_ON
RET
;——————————————————————————-
NOTA_ON:
MOV A, canal ;faz canal -> Acc ( o canal esta no fomato 0000xxxx) onde xxxx representa o numero do canal
ORL A,#90h ;Nota ON combinada com o canal escolhido (onde ficará 1001xxxx)
ACALL ENVIA_BYTE
MOV A,nota ;envia o numero da nota
ACALL ENVIA_BYTE
MOV A,#70 ;velocidade
ACALL ENVIA_BYTE
RET
;——————————————————————————-
NOTA_OFF:
MOV A, canal ;faz canal -> Acc ( o canal esta no fomato 0000xxxx)
ORL A,#80h ;Nota OFF combinada com o canal escolhido (onde ficará 1000xxxx)
ACALL ENVIA_BYTE
MOV A,nota ;envia o numero da nota
ACALL ENVIA_BYTE
MOV A,#0 ;velocidade zero significa nota off
ACALL ENVIA_BYTE
RET
;——————————————————————————–
ENVIA_BYTE:
JNB SCON.1,ENVIA_BYTE ;checa se ja nao existe alguma coisa sendo enviada pq a flag ta zerada…enquanto nao terminar o envio espera…
CLR SCON.1 ;tx livre! limpa flag de enviado
MOV SBUF,A ;manda enviar o conteudo de Acc
RET
;************************* TRATAMENTO DAS INTERRUPÇÕES *************************
EXTERNA_0: NOP ; Código de tratamento de externa0
RETI
TIMER_0: NOP ;
RETI
EXTERNA_1: NOP ; Código de tratamento de externa1
RETI
TIMER_1: NOP ; Código de tratamento de timer1
RETI
SERIAL_COM: NOP ; Código de tratamento de serial
RETI
;*******************************************************************************
END ; Fim de Arquivo p/ compilação ufa ;D
Bem espero que seja útil para alguém… é arcaico mas para quem está procurando sobre 8051 é uma boa estudar o código 😉 Quem quiser um código MIDI em Arduino deixa um comentário explicando o que deseja que quando tiver tempo faço um post sobre o assunto.
israel
qual software?
Almir
Quando fiz o projeto usei o Hairless MIDI<->Serial
https://projectgus.github.io/hairless-midiserial/
Que fazia conversão do MIDI pela porta serial(pode ser usb) para uma porta virtual MIDI. A partir daí é só usar qualquer software de música que aceite MIDI. Nos testes tinha usado algo como “Virtual MIDI Piano Keyboard”
https://sourceforge.net/projects/vmpk/