?? pckb1200.asm
字號:
; ****************************************************************
; * PCkeybrd.asm for Atmel AVR 90S1200
; *
; * 1. Play MIDI notes using a detached PC keyboard
; * 2. Put sequencial numbers out the MIDI port every half second
; * 3. Continous MIDI output at full speed - 0x00 to 0xFF then rollover
; * 4. Block MIDI output at keyswitch press - send 128 bytes (0x00 to 0x7F) to MIDI at full speed
; *
; * version 1.27 Aug 28, 2003 Alan Probandt Portland Oregon USA
; *
; * [ Use TimesNewRoman font in text editor to format this file correctly ]
; *
; *
; * Read keypresses from PC Keyboard into AVR 1200
; * -Assemble logic changes on AVR pin 6 into PC keyboard scan codes.
; * -Decode keyboard scancodes into MIDI note numbers.
; * -Send MIDI messages out the MIDI out port 31250/8/N/1
; *
; * PC keyboard clock signal on AVR pin 6 (INT1)
; * PC keyboard data signal on AVR pin 7 (PORTD bit 4)
;
; RESET- active low PIN 1 | VCC PIN 20 +5V
; RTS PIN 2 PD0 | PB7 PIN 19
; TXD PIN 3 PD1 | PB6 PIN 18
; 3.579545MHz PIN 4 XTAL1 | PB5 PIN 17
; must use only PIN 5 XTAL2 | PB4 PIN 16
; INT0 kbd clock PIN 6 PD2 | PB3 PIN 15
; kbd data PIN 7 PD3 | PB2 PIN 14
; MIDI out PIN 8 PD4 | PB1 PIN 13
; PIN 9 PD5 | PB0 PIN 12
; PIN 10 GROUND | PD6 PIN 11
;
; ************************************************************
.nolist
.include "1200def.inc"
.list
; crystal-dependent timer values 3.579545 MHz (TV color burst crystal)
; 0.279uS * 1024 = 285.7uS timer prescaled increment period
; 73,138 microseconds per Timer Overflow
.equ Quarter_second = 5 ; approx number of timer overflows (using sysclk/1024 prescaler) per 250 milliseconds
.equ TwentyMilliseconds = 70 ; approx number of timer counts (using sysclk/1024 prescaler) per 20 milliseconds
.equ MIDItxpin = 4 ; MIDI Transmit pin
.equ INITIAL_BITCOUNT = 11 ; used by Get_Scan
.equ PARITY_BITCOUNT = 3 ; used by Get_Scan
.equ NOTEOFFSET = 33
.equ CHANGE_VOICE_OFFSET = $2d
.equ ALL_NOTES_OFF_DECODE = $2d
.equ REGISTER_AVAILABLE = $FF
.equ NUMBER_of_POSSIBLE_NOTES = 8
.equ FIRST_STORAGE_REGISTER = 8
.equ NOTE_ON_VELOCITY = 0x60
.equ NOTE_OFF_VELOCITY = 0
.equ NOTE_ON_MSG = $90
.equ CHANGE_VOICE_MSG = $c0
.equ MIDI_CONTROLLER_MSG = $b0
.equ ALL_NOTES_OFF_BYTE = 123 ; $7b
.equ ALL_NOTES_OFF_BYTE_2nd = 0
.equ PSEUDO_STACK = $65 ; imitate a real stack when using ICE200 for 1200 chip
.equ PSEUDO_STACK_POINTER = $3d
.equ KBD_ACK = $0fa
.equ T_flag = 6
.equ Sign_flag = 4
.equ Toggler6 = 6
.equ SM_flag = 4 ; Sleep Mode (unused) - delay loop counter bit flag { 0 = long period is timed out}
.equ PushSwitch1 = 4
; keyflag register's bit values
.equ Edge_flag = 7 ; use up or down edge while inputting char from keyboard
.equ Shift_flag = 5 ; the shift key (either right or left) is being pressed
.equ Break_flag = 4 ; the last scancode was an $f0 - the break code sent when a key is released
.equ OnOff_flag = 3 ; set means turn on the note whose MIDI number was decoded from the most recent scancode
.equ ACK_flag = 2 ; the most recent scancode received was $FA - keyboard acknowledge
.equ E0_flag = 1 ; the extended scancode $E0 was sent - use the E0table to decode scancode
;*********************************************************************************************
; Register definitions
;
; upper registers
.def bitcount = r16 ; used by INT0 irq [getscan]
.def scancode = r17 ; storage of data bits received from PC keyboard
.def scanbits = r18 ; shift register for holding PC keyboard data while being assembled
.def keyflags = r19
.def DecodeValue = r20 ; PC keyboard char decoded to NoteOn value, ChangeVoice value, or AllNotesOff cmd
.def txbitcnt = r21 ; used by PutMIDI
.def TxData = r22 ; the byte being sent out the MIDI port - PutMIDI
.def temp = r23 ; scratch register
.def irqtemp = r24 ; interrupt's scratch register
.def EEPROM_Ptr = r25
.def regcnt = r26
.def OverflowCount = r27
.def MIDIcounter = r28
; lower registers r0 - r15
; r0 is temporary storage of byte retrieved from EEPROM
.def newvoice = r1
.def NoteOffsetValue = r2
.cseg
.org $0000
rjmp reset ; Reset handler $000
.org INT0addr
rjmp get_scan ; External interrupt 0 handler
.org OVF0addr
reti ; Timer0 overflow handler
.org ACIaddr
reti ; Analog comparator handler
RESET:
; ldi temp,PSEUDO_STACK ; imitate a real stack when using ICE200 for 1200 chip
; out PSEUDO_STACK_POINTER,temp ;init Stack Pointer - does nothing on real 1200 chip
; set-up ports for input/output and pull-up resistors
ser temp
out portb,temp ; port B pull-up resistors are all on
out portd,temp ; port D pull-up resistors are all on
clr temp
out ddrb,temp ; port b is all inputs the DIP switches are here Pb0 to PB3
ldi temp,(1<<MIDItxpin) + (1<<Toggler6) ; toggler6 (pin 11) is not used in this version
out ddrd,temp ; port b is all inputs the DIP switches are Pb0 to PB3 - pushswitch is PB4
; sei ; enable interrupts from Status Register (each routine will switch the General Interrupt Enabler)
;*******************************************************************************************
; Get keypresses from standalone PC keyboard - convert to notes and send to MIDI port
;
; Subroutines: 'decode_data'
;
; Interrupt procedures: external interrupt int0 -- 'getscan'
;
; Most time spent in 'GetChar' which checks T flag for fully received keypress from PC keybrd
; When T is set, 'decode_data' called to convert keypress to note or cmd.
;*******************************************************************************************
;
PCkb_Main:
ldi temp,(1<<ISC01) ; setup INT0 interrupt on falling edge
out MCUCR, temp
ldi temp, (1<<INT0)
out GIMSK,temp ; enable external INT0 - General Interrupt Mask Register
ldi zl, FIRST_STORAGE_REGISTER - 1 ; fill note storage area with REGISTER_AVAILABLE sentinals
ldi regcnt, NUMBER_of_POSSIBLE_NOTES
ldi temp, REGISTER_AVAILABLE
rst1: inc zl
st z, temp
dec regcnt
brne rst1
clr keyflags ; edge = bit 7: shift = bit 1: Break = bit 0
ldi bitcount,INITIAL_BITCOUNT ; = 11
clt ; T flag used to indicate that a char from kybd is ready to transmit
ldi temp, NOTEOFFSET
mov NoteOffsetValue, temp
sei ; switch on the general interrupt enabler
GetChar: in temp, SREG ; MAIN LOOP of program 99.9% of time spent here
sbrs temp,T_flag ; if T flag (SREG bit 6) is set, then char from keyboard is ready to transmit
rjmp GetChar
clt ; clear T flag - prepare for next char
rcall decode_data ; convert the scancode from the keyboard into an ASCII character
; send scancode and keyflags - return DecodeValue
tst DecodeValue ; DecodeValue =0 ; ignore scancode - it's a shift character or break scancode
brne Valid_key
rjmp GetChar
Valid_key:
ldi temp,ALL_NOTES_OFF_DECODE ; $80 is this program's code number for All_Notes_Off
cp DecodeValue,temp
breq AllNotesOff
brlo PlayNote ; DecodeValue value is lower than $80
brbc Sign_flag,ChangeVoice ; DecodeValue value is greater than $80 - branch if bit in SREG is 0
PlayNote:
sbrc keyflags,OnOff_flag ; if OnOff flag is set, then char is a MIDI note number to be played
rjmp playMIDInote
sbrs keyflags,OnOff_flag ; if OnOff flag is clear, then char is a playing MIDI note to be turned off
rjmp TurnOffMIDInote
; *********************************************************************************************
; AllNotesOff - 1) Put Note_Available sentinal into the register bank for all possible notes.
; This indicates that all possible note spaces are available because all notes are off.
; 2) Send the All_Notes_Off MIDI control message to the synthesizer.
;
; 3) jump back to GetChar routine
;*********************************************************************************************
AllNotesOff:
ldi ZL,FIRST_STORAGE_REGISTER - 1
ldi regcnt,NUMBER_of_POSSIBLE_NOTES
ser temp
ANO1: inc zl ; first check 8 storage registers to see if the note is already playing
st z,temp
dec regcnt
brne ANO1 ; make all storage registers available for new notes at space-bar press
ldi TxData, MIDI_CONTROLLER_MSG
rcall putMIDI
ldi TxData, ALL_NOTES_OFF_BYTE
rcall putMIDI
ldi TxData, ALL_NOTES_OFF_BYTE_2nd
rcall putMIDI
rjmp getchar
; *********************************************************************************************
; ChangeVoice - 1) Put Note_Available sentinel into the register bank for all possible notes.
; This indicates that all possible note spaces are available because all notes are off.
; 2) Send the MIDI program change message to the synthesizer. This will turn off all
; sounding notes for the selected MIDI channel (channel 1 by default).
;
; 3) jump back to GetChar routine.
;*********************************************************************************************
ChangeVoice:
ldi ZL,FIRST_STORAGE_REGISTER - 1
ldi regcnt,NUMBER_of_POSSIBLE_NOTES
ser temp
CV1: inc zl ;a change voice command turns off all sounding notes
st z,temp
dec regcnt
brne CV1 ; make all storage registers available for new notes when voice changes
mov temp,DecodeValue
subi temp,CHANGE_VOICE_OFFSET
mov newvoice,temp
ldi TxData,CHANGE_VOICE_MSG
rcall putMIDI
mov TxData,newvoice
rcall putMIDI
rjmp GetChar
;********************************************************************************
; GET_SCAN: ; signal handler for external interrupt int0 (AVR pin 6)
; called by each transition of the keyboard clock signal
; assemble the scancode from keyboard data signal
; ( UltraEdit32 - use column mode or terminal font to see correctly)
;clk----|_|----|_|---|_|---|_|---|_|---|_|----|_|---|_|---|_|---|_|---|_|---------- (---- = logic high +5v)
;data-\___/-----\___/-----\___/-----\___/-----\___/--------------------
; 11 10 9 8 7 6 5 4 3 2 1 bitcount on falledg
; start 0 1 2 3 4 5 6 7 P stop data bit
; Bitcnt 3 to 10 is data. start bit = 11, parity bit=bit 2; stop bit = bit 1
;*********************************************************************************
get_scan: ; Routine first entered at falling edge; then on both edges
sbrc keyflags,Edge_flag ; test edge flag
rjmp up_edge ; bit 7 (edge) = 0 => falling ;; bit 7 (edge) = ff => rising
cpi bitcount,INITIAL_BITCOUNT ; falling edge of the FIRST clock pulse from the PC keyboard ; start bit so do nothing
breq I1two
cpi bitcount,PARITY_BITCOUNT ; = 3 ; test for parity bit and stop bit
brlo I1two ; must use bitcount 3 for compare because branch tests only for lower
lsr scanbits ; shift data right one bit - data bit 7 gets 0
sbic PIND,PD3 ;set scancode bit if bit 3 on input port D is set (pin 7 on 2313 chip)
ori scanbits,$80 ; if data from kbrd is 1, then set bit 7 only and let other bits unchanged
ldi irqtemp, ( (1<<ISC01) | (1<<ISC00) )
out MCUCR, irqtemp ; Set interrupt on rising edge - INT0
sbr keyflags,(1<<edge_flag) ; set edge flag
reti
I1two:
ldi irqtemp, ( (1<<ISC01) | (1<<ISC00) ) ; just exit if bitcnt is 11, 2, or 1
out MCUCR, irqtemp ; Set interrupt on rising clock edge - INT0
sbr keyflags,(1<< Edge_flag) ; set edge flag
reti
up_edge:
ldi irqtemp, 1<<ISC01
out MCUCR, irqtemp ; Set interrupt on falling clock edge - INT0
cbr keyflags,1<<Edge_flag ; clear edge flag
dec bitcount
brne exit_int1 ; All bits received?
ldi bitcount,INITIAL_BITCOUNT
set ; set T flag to let main program know a new char is ready
mov scancode,scanbits ; scanbits can be used to assemble new char while scancode is being decoded
exit_int1: ; clear any pending interrrupt
clr irqtemp
out GIMSK,irqtemp ; disable external INT0 - General Interrupt Mask Register
out MCUCR,irqtemp
ldi irqtemp, (1<<ISC01) ; interrupt on falling edge
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -