终于把板子给焊上了,之所以会这么久是因为做好的PCB还必须集运才送来,得十天八天,影响开发速度,软件方面就容易得多,因为我之前已经做了两款扫描电子琴琴键的软件,有88键的,包含力度感应,相当复杂,当初幸好有网友yzwzfyz指点一番,不然还真做不出来。
现在这个就非常简单了,只用到8条X线,5条Y线,可检测40个键,所以把旧软件简化,用了两晚就解决了,编程思路如下:
1. 向某一条Y线送出0, 其他保持1,读取X线的值(8位),这8位有某个按键被按下,该位是0。
2. 由于8位连接的按键会同时被按下,所以得用移位逐个检测每个位,并结合Y的值算出是琴上的那个键被按下。
3. 确定被按下的键必须在记忆区相应的位置设定标记1,然后送出NOTEON的MIDI码。
4. 没有按下的键也必须在其记忆区相应的位置检查标记,若为1表示之前已经送出NOTEON,所以必须送出NOTEOFF的MIDI码,再把标记设为0.
5. 按键不必有防抖程序,因为每一次扫描会有间隔,不会出现连续开关的情况,经过测试的确如此。
注:Y线是连接二极管的负极,X线是连接二极管的正极。
SCANNER: ;键盘扫描主程序
;LCALL DELAY3 ;这个试验时可开,但正式使用要关。
SCANP1: ;输出到P1,其中一列是0,然后从P2读入,8位代表列,如某行有按下则为0,否则为1,8行都有可能同时按下。
MOV 35H, #0 ;THIS WILL ADD TO ROW TO MAKE 0-7
MOV 32H, #5 ;8 BITS TO SHIFT
MOV 50H, #11111111B ;THIS BYTE FOR SHIFT AND OUTPUT TO P1
MOV P1, #11111111B ;CLEAR P1 AFTER EVERY BIT SCAN
LCALL DELAY1
CLR C ;PUSH A ZERO INTO 50H AT FIRST ROTATE
SCANP1A:
MOV A, 50H
RLC A ;改用左移,第一次(C=0)被推入,变成11111110,而C变成1,第二次移0被左移,变成11111101
MOV 50H, A ;保留进50H,下次再用
MOV P1, A ;输出到P1
;MOV P1, #11101111B ;第一步:这个用来检测每一根输出线(逐个放0,只有5根线BIT0-BIT4)
LCALL DELAY0 ;稍等
MOV A, P2 ;READ FROM P2
;LCALL SENTONEBYTE1
LCALL EACHDATA
SETB C ;BEFORE LOOP SET C FOR SHIFT ONE TO 50H
MOV P1, #11111111B ;CLEAR P1 AFTER EVERY BIT SCAN
LCALL DELAY1
;LCALL DELAY3
DJNZ 32H, SCANP1A ;5 TIME LOOP
JMP SCANNER
EACHDATA: ;32H 从8逐渐减到1,代表是那一列被设为0,与监测到0的行相乘就得到扫描号码。
PUSH PSW
MOV 34H, #8 ;8行检测
MOV 33H, A ;A 是由P2 读来的8BIT,每个BIT代表一个扫描线与P1那个被设定为0之间的按键是否有按下,按下则为零。
EACHDATA1: ;8次重复由34H控制
MOV A, 33H
LCALL COUNTPOSITION ;先算出当前键的位置
MOV 3EH, A ;回来后A是按键数目
LCALL KEYMAP ;查表得到该按键在键盘上的排列号码
MOV 3AH, A ;保存键盘号码到3AH
MOV A, 33H
RLC A ;向左移,检查C看是1还是0
MOV 33H, A ;SAVE BACK FOR NEXT ROTATE
JNC DOKEY1 ;CONTACT POINT NOT CONTACT GO NOTEOFF SUBROTINE
LCALL NOTEOFFLAH ;NOTE OFF SUBROUTINE
JMP DOKEY2
DOKEY1:
MOV A, 3AH ;按键码
;LCALL SENTONEBYTE1
LCALL NOTEONLAH ;保存区相应位置放1
DOKEY2:
DJNZ 34H, EACHDATA1 ;8 LOOP NEEDED
DOKEYEXIT:
POP PSW
RET
NOTEOFFLAH:
MOV DPH, #0
MOV DPL, 3AH
MOVX A, @DPTR ;READ OLD DATA
JZ NOTEOFFX
MOV A, #0
MOVX @DPTR, A ;标志为1
LCALL SENTNOTEOFF ;送出NOTEOFF,只限一次
NOTEOFFX:
RET
NOTEONLAH:
MOV DPH, #0
MOV DPL, 3AH
MOVX A, @DPTR ;READ OLD DATA
JNZ NOTEONX
MOV A, #1
MOVX @DPTR, A ;标志为1
LCALL SENTNOTEON ;送出NOTEON,只限一次
NOTEONX:
RET
SENTNOTEOFF:
MOV A, #10000000B ;MIDI NOTEOFF CHANNEL1
LCALL SENTONEBYTE1
LCALL SENTONEBYTE2
MOV A, 3AH
LCALL SENTONEBYTE1
LCALL SENTONEBYTE2
MOV A, #0
LCALL SENTONEBYTE1
LCALL SENTONEBYTE2
RET
SENTNOTEON:
MOV A, #10010000B ;MIDI NOTEON CHANNEL1
LCALL SENTONEBYTE1
LCALL SENTONEBYTE2
MOV A, 3AH
ANL A, #01111111B ;MASKING BIT7 音符
LCALL SENTONEBYTE1
LCALL SENTONEBYTE2
MOV A, #7FH ;力度
LCALL SENTONEBYTE1
LCALL SENTONEBYTE2
RET
COUNTPOSITION: ;这个算法不一般
PUSH PSW
MOV A, 32H ;列ROW POSITION 1-8
MOV B, #8 ;8 CONTACT POINT FOR EACH ROW
MUL AB ;得到的数字是 8,16,24,32,40.......128
CLR C
SUBB A, #8 ;MAKE A 0,8,16,24,32,40,48,56,64,72,80,88,96,104,112,120
ADD A, 34H ;加上34H 1-8 行COLUME
POP PSW
RET
KEYMAP: ;由于此款排列顺序,无需查表,直接运算
MOV A, #50H ;从小变大!
CLR C
SUBB A, 3EH
RET
|