⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pckb1200.asm

📁 把PC键盘变成MIDi音符控制适配器电路单片机程序
💻 ASM
📖 第 1 页 / 共 2 页
字号:
;  ****************************************************************
;  *   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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -