![]() |
發布時間: 2018-2-13 14:49
正文摘要:各位好! 我是新手,請大家多關照,最近想設計一臺用STC單片機做的61鍵無線藍牙電子琴,用現成的廉價電子琴來改裝,打算直接掃描16X8行列的狀態加以處理,這部分程序已經寫好測試妥當了(附程序),由于這是一個 ... |
我剛測定過,空鍵時掃描一次的時間包括處理為1.584ms, 一鍵時是1.592ms, 五鍵同按是1.618ms, 一秒鐘能檢測到大概500次按鍵,以電子琴來說速度可以了,這琴鍵按鈕是拆廠家成品電子琴來試的,是16X8的行列,16位兩個接口P2P3為輸出,8位為輸入P0, 所以得根據這樣的結構編程, 我在寫的時候才發現就算沒有什么鍵按下還得逐個檢查記憶體的,因為要查出0FFH再發出noteoff信號,這里面有個憂慮就是擔心在檢查記憶體時產生時鐘中斷改變了DPL而讀錯, 所以開始時先關掉時鐘,檢查完再開時鐘,應該會影響到時鐘進位的精度。那個LCALL DELAY2 是個惡夢,不是防抖動用的,原來STC單片機的輸出有滯后,在輸出0后,再回復1會滯后,造成誤讀,所以要延時讓輸出穩定,這現象讓我花費了一天時間才發現。關于用IO中斷,STC單片機好像沒有提供這個功能,無法實現。 這程序若可以用我就會制作一個電路板來試,可以的話就用來取代原來那臺電子琴的電路板,變成無線電子琴了。 |
時間中斷后,輪查122個計時器。為0的放棄,不為0的+1計時。 掃到第一路鍵(合理的安排是,61個第一路鍵放于前兩路掃描中;另61個第二路鍵放在后兩路掃描中)置1,掃到第二路鍵取對應計時器值分析力度,用后清0. |
硬件IO的排列有講究。你需要用最短的掃描時間,捕捉到是哪個按鍵按下了。 所以同一個琴鍵是的兩個鍵,不要放在同一個P口上。 要點: 1、不要用防彈動程序,以節省時間,一次即認可。 2、一次讀8個鍵,無鍵立即轉讀下8個鍵,如此,在無鍵的情況下,讀16次P口即可完成對所有鍵掃描。 3、如果有鍵,則再花點時間區分是哪個鍵,需要起動哪個計時器。 4、另一種更好的方案是,用IO口中斷: 正常輪流掃四路通道,中斷后先查是哪個Px產生了中斷,再查掃描的是哪一路,就能確定是哪個鍵了。 |
硬件上:你用了5個口,滿算是40個IO 你需要:61*2=122個口 建議用:4路掃描,乘以31個口。掃描路數越少,用時越短。這個程序主要矛盾是速度! |
樓主果然聰明,一點就通,悟性好。未來不可限量。祝你春節快樂! |
寫了兩個晚上才測試妥當,只要邏輯正確,其他可慢慢修改了,這東西難寫處在于io口幾乎用完,無法用LED燈檢查結果,另外運行時T1會中斷修改一些寄存器如DPL, 增加調試的困難,無論如何算是寫成了,就上載給大家參考了, 再謝yzwzfyz 總工。 ;THIS IS FOR KEYBOARD TESTING ;32H FOR ROW COUNT LOOP ;33H FOR STORE SCAN DATA(MAY CONAIN FEW 0 THAT MEAN CONTACT) ;34H FOR LOOP ;35H FOR ADD TO ROW FORM 1-8 OR 9-16 ;37H FOR LOOP CONVERT ROW NUMBER TO KEYNUMBER ;3AH FOR KEYNUMBER FIRSTBUFFER EQU 80H LASTBUFFER EQU 0FFH BRT EQU 09CH BRTLOAD EQU 226 ;226(18.432, 38400 SMOD=1) 241(18.432, 38400) 250(3.68, 38400) 247(11.0592, 38400), 251(18.432, 115200) S2CON EQU 9AH ;S2SM0, S2SM1, S2SM2, S2REN, S2TB8, S2RB8, S2TI, S2RI IE2 EQU 0AFH ;X, X, X, X, X, X, ESPI, ES2 S2BUF EQU 9BH P4 EQU 0C0H AUXR EQU 8EH AUXR1 EQU 0A2H WAKE_CLKO EQU 08FH ORG 0000H LJMP MAIN ORG 0003H LJMP EXT_INT0 ;EXTERNAL INTERRUPT0 ORG 000BH LJMP TIMER_0 ;TIMER0 INTERRUPT ORG 0013H LJMP EXT_INT1 ;EXTERNAL INTERRUPT1 ORG 001BH LJMP TIMER_1 ;TIMER_1 INTERRUPT ORG 0023H LJMP UART1 ;UART1 RECEIVED INTERRUPT ORG 002BH LJMP ADC ORG 0033H LJMP LVD ORG 003BH LJMP PCA ORG 0043H LJMP UART2 ;UART2 RECEIVED INTERRUPT ORG 004BH LJMP SPI ORG 0100H ; PROGRAM SET MAIN: MOV P0, #0FFH MOV P1, #0FFH MOV P2, #0FFH MOV P3, #0FFH MOV P4, #0FFH ;MOV 97H, #00000101B ;SLOW DOWN MOV WAKE_CLKO,#00000000B ;ENABLE BRT(=4),T1(=2) T0(=1) HAVE CLOCK OUTPUT BRT@P1.0 T1@P3.5 T0@P3.4 MOV BRT, #BRTLOAD ;RELOAD 1152000 MOV AUXR,#01011101B ;T0x12,T1x12,UART_M0x6,BRTRUN,S2SMOD,BRTx12,EXTRAM,S1BRS ;MOV AUXR1, #00010000B ;MOVE UART2 TO TX2=P4.3 RX2=4.2 MOV TMOD, #00010001B ;TIMER_1 AS MOD1 (16BIT COUNTER) ;TIMER_0 AS MOD 1 (16BIT COUNTER) ;GATE, C/T,M1,M0(T1) GATE, C/T,M1,MO(T0) MOV PCON, #10000000B ;THIS DOUBLE THE BRT AND T0 T1 RATE SETB ET1 ;ENABLE T1 INTERRUPT SETB TR1 ;RUN T1 LCALL INITIAL_UART2 ;USE BRT AS SERIAL BAUD GENERATE FOR UART2 SETB EA ;ENAABLE ALL INTERRUPT MOV P2, #10101010B ;SHOW START LCALL CLEANRAM MOV R0, #FIRSTBUFFER ;INIT THE BTYE TO SENT IN BUFFER POINTER MOV R1, #FIRSTBUFFER ;INIT THE POINTER FOR BYTE CAN STORE IN BUFFER SCANNER: SCANP2: ; SHIFT P2 AND READ P0 MOV 32H, #8 ;8 BITS TO SHIFT MOV 50H, #11111111B ;THIS BYTE FOR SHIFT AND OUTPUT TO P2 MOV 35H, #0 ;THIS WILL ADD TO ROW TO MAKE 0-7 CLR C SCANP2A: MOV A, 50H RRC A MOV 50H, A MOV P2, A ;P2 ONE ROW LOW LCALL DELAY4 ;MUST WAIT STABLE MOV A, P0 ;READ FROM P0 CPL A LCALL STOREDATA SCANNEXT1: DJNZ 32H, SCANP2A MOV P2, #11111111B ;END OF SCANP2 SCANP3: ; SHIFT P3 AND READ P0 MOV 32H, #8 ;8 BITS TO SHIFT MOV 50H, #11111111B ;THIS BYTE FOR SHIFT AND OUTPUT TO P2 MOV 35H, #8 ;THIS WILL ADD TO ROW TO MAKE 0-7 CLR C SCANP3A: MOV A, 50H RRC A MOV 50H, A MOV P3, A ;P2 ONE ROW LOW LCALL DELAY4 ;MUST WAIT STABLE MOV A, P0 ;READ FROM P0 CPL A LCALL STOREDATA SCANNEXT2: DJNZ 32H, SCANP3A MOV P3, #11111111B ;END OF SCANP NOP ;CHECK BUFFER AND SENT MOV A, R0 XRL A, R1 JZ SCANNEREXIT ;R0 R1 EQUAL NO NEW BYTE LCALL OUTBUFFER SCANNEREXIT: JMP SCANNER PUTBUFFER: MOV @R1, A CJNE R1, #LASTBUFFER, NEXTR1 MOV R1, #FIRSTBUFFER JMP PUTBUFFEREXIT NEXTR1: INC R1 ;POINT TO NEXT BUFFER PUTBUFFEREXIT: RET OUTBUFFER: MOV A, @R0 LCALL SENTONEBYTE2 CJNE R0, #LASTBUFFER, NEXTR0 MOV R0, #FIRSTBUFFER JMP OUTBUFFEREXIT NEXTR0: INC R0 ;POINT TO NEXT BUFFER OUTBUFFEREXIT: RET STOREDATA: ;32H CONTAIN ROW UMBER THAT CAUSE LOW PUSH PSW MOV 34H, #8 ;8 BIT TO SHIFT MOV 33H, A ;A CONTAIN 8BITS WITH FEW HIGH(CAUSE BY KEYPRESS) STOREDATA1: LCALL COUNTPOSITION ;CODE BY SCANNER 4C 54 COUNT BY LCALL KEYMAP ;RETURN KEYNUMBER A AFTER LOOKUP THE MAP 3D BD MOV 3AH, A ;SAVE KEYNUMBER TO 3AH, 0-127 IS UPPER KEY, 128-255 IS LOWER KEY MOV A, 33H RLC A ;CHECK EACH BIT OF 33H SEE 0 OR 1 AND DECIDE WHERE TO GO MOV 33H, A JNC DOKEY1 ;CONTACT POINT NOT CONTACT GO NOTEOFF SUBROTINE LCALL NOTEON ;NOTEON CHECK JMP STOREDATANEXT DOKEY1: LCALL NOTEOFF ;NOTEOFFCHECK STOREDATANEXT: DJNZ 34H, STOREDATA1 DOKEYEXIT: POP PSW RET NOTEON: CLR TR1 ;TIMER1 STOP MOV A, 3AH RLC A ;CHECK UPPER OR LOWER JC NOTEON1 ;LOWER KEY SO DO OUTPUT ;----------UPPER MOV DPH, #0 MOV DPL, 3AH MOVX A, @DPTR ;READ OLD DATA JNZ NOTEONEXIT ;ALREADY CONTACTED, DO NOTHNG MOV A, #1 MOVX @DPTR, A ;CONTACT JUST START, PUT 1 FOR COUNT UP BY TIMER EVERY 1MS JMP NOTEONEXIT ;----------------- NOTEON1: MOV A, 3AH ANL A, #01111111B ;MASKING BIT7 MOV DPH, #0 MOV DPL, A MOVX A, @DPTR ;READ VELOCITY JZ NOTEONEXIT ;UPPER KEY NOT YET CONTACTED, DO NOTHING MOV 3BH, A ;SAVE VELOCITY INC A JZ NOTEONEXIT ;IF FFH THEN ZERO MEAN NOTE ALREADY OUTPUT, NEED WAIT FOR NOTEOFF, DO NOTHING MOV A, #10010000B ;MIDI NOTEON CHANNEL1 LCALL PUTBUFFER ;SENT KEY NUMBER MOV A, 3AH ANL A, #01111111B ;MASKING BIT7 LCALL PUTBUFFER ;SENT KEY NUMBER MOV A, 3BH LCALL PUTBUFFER ;SENT VELOCITY MOV A, 3AH ANL A, #01111111B ;MASKING BIT7 MOV DPH, #0 MOV DPL, A MOV A, #0FFH MOVX @DPTR, A ;MARK AS FFH WAIT FOR RELEASE AND SENT NOTEOFF NOTEONEXIT: SETB TR1 RET NOTEOFF: CLR TR1 MOV A, 3AH RLC A ;CHECK UPPER OR LOWER JC NOTEOFFEXIT ;LOWER KEY SO DO NOTHING NOP ;UPPER KEY RELEASE, CHECK 0FFH FOR SENT NOTEOFF MOV DPH, #0 MOV DPL, 3AH MOVX A, @DPTR ;READ OLD DATA INC A JNZ NOTEOFFEXIT ;IF FFH THEN ZERO MEAN NO NOTE OFF NEED, DO NOTHING ;--SENT NOTEOFF MOV A, #10000000B ;MIDI NOTEON CHANNEL1 LCALL PUTBUFFER ;SENT KEY NUMBER MOV A, 3AH LCALL PUTBUFFER MOV A, #0 LCALL PUTBUFFER MOV DPH, #0 MOV DPL, 3AH MOV A, #0 MOVX @DPTR, A ;MARK THAT KEY LOCATION 0 TO RECEIVED NEW CONTACT PRESS NOTEOFFEXIT: SETB TR1 RET COUNTPOSITION: PUSH PSW MOV A, 32H ;ROW POSITION 1-8 ADD A, 35H ;IF P2 THEN ADD 0(REMAIN 1-8), IF P3 THEN ADD 8(BECOME 9-16) MOV B, #8 ;8 CONTACT POINT FOR EACH ROW MUL AB ;NUMBER 8,16,24,32,40.......128 CLR C SUBB A, #8 ;MAKE A 0,8,16,24.....120 ADD A, 34H ;34H CONTAIN 1-8 COLUME NUMBER(EACH BIT OF 33H) FORM 1 TO 128 DEC A ;MAKE AS 0-127 SYSTEM POP PSW RET KEYMAP: MOV DPH, #10H MOV DPL, #00H MOVC A,@A+DPTR RET TIMER_1: ;USE FOR MICRO CLOCK 1MS 184, 30 ,100US=248 250 PUSH ACC PUSH PSW MOV 42H, #128 MOV DPH, #0 TIMERLOOP: MOV DPL, 42H MOVX A, @DPTR JZ TIMER_1NEXT CJNE A, #0FFH, TIMERLOOP1 ;NOTEOFF NEED CPL P1.7 JMP TIMER_1NEXT TIMERLOOP1: CJNE A, #0FEH, TIMER_1A ;NOT YET MAX JMP TIMER_1NEXT TIMER_1A: INC A MOVX @DPTR, A TIMER_1NEXT: DJNZ 42H, TIMERLOOP TIMER_1EXIT: MOV TH1, #248 MOV TL1, #250 POP PSW POP ACC RETI SENTONEBYTE2: ;A SENT OUT PUSH ACC MOV IE2, #00H ;DISABLE THE SECONDARY UART INTERRUPT, ES2=0 MOV S2BUF, A ;LOAD A TO BUFFER MOV A, S2CON ;1111,1101, CLEAR SECONDARY UART TRANSIMIT INTERRUPT FLAG ANL A, #0FDH MOV S2CON, A UART2WAIT: MOV A, S2CON ANL A, #02H ;0000,0010 CJNE A,#02H, UART2WAIT MOV A, S2CON ANL A, #0FDH ;1111,1101, CLEAR SECONDARY UART TRANSIMIT INTERRUPT FLAG MOV S2CON, A MOV IE2, #01H ;ENABLE THE SECONDARY UART INTERRUPT, ES2=1 NOP NOP POP ACC RET DELAY4: NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP RET EXT_INT0: ;IF P3.2 H2L COME HERE SET TIMER, ONLY FIRST H2L WILL ACT RETI TIMER_0: ;CHECK INPUT MIDI SIGNAL AT P3.2 RETI EXT_INT1: ;USE FOR INFRA RED RETI INITIAL_UART2: ;USE FOR SENT MIDI DATA MOV S2CON, #01010000B ;SET AS BAUD VERIABLE, NO ODD/EVEN CHECK MOV IE2, #01H ;ENABLE UART2 INTERRUPT RET UART1: RETI ADC: RETI PCA: RETI LVD: RETI SPI: RETI UART2: ;NO READ RETI CLEANRAM: MOV DPH,#0 MOV DPL, #0 CLEANRAM1: MOV A, #0 MOVX @DPTR, A INC DPL MOV A, DPL JNZ CLEANRAM1 CLEANRAMB: MOV DPH,#1 MOV DPL, #0 CLEANRAMB1: MOV A, #0 MOVX @DPTR, A INC DPL MOV A, DPL JNZ CLEANRAMB1 RET ORG 1000H ;KEY MAPPING CONVERT SCANNER NUMBER TO KEYBOARD PHYSICAL POSITION (MUSIC KEY 1-61) ;UPPER-KEY+128=LOWER-KEY ;OPTAIN UPPER CONTACT POINT'S SCAN CODE MANUALLY AND PUT KEYNUMBER AT SCAN CODE POSITION ;OPTAIN LOWER CONTACT POINT'S SCAN CODE MANUALLY AND PUT KEYNUMBER+128 AT SCAN CODE POSITION ; 00H 01H 02H 03H 04H 05H 06H 07H 08H 09H 0AH 0BH 0CH 0DH 0EH 0FH ; --------------------------------------------------------------- DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 0F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 1F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 2F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 3F DB 00H,00H,00H,00H,00H,00H,00H,00H,39H,3AH,3BH,3CH,3DH,00H,00H,00H ;0 80 4F DB 0B9H,0BAH,0BBH,0BCH,0BDH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 96 5F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 6F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 7F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 8F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 9F DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 AF DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 BF DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 CF DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 DF DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 EF DB 00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H ;0 16 FF END |
我看你是用匯編寫程序的,是真想學好單片機的,故點一下。 |
yzwzfyz你好! 果然有好辦法,我這兩天試著自己想辦法,把計數器的值寫在記憶區的笨辦法,越寫越亂,快要放棄,但作為初學者,這種磨煉還是應該的,現在有好辦法了,今晚應該就能寫好了,多謝指教。 |
當然-1也可以,看你愛好。 |
你的構思是正確的,精心安排一下是可以實現的。 1、建議你將力度安排為255級,這樣一位就夠了。 2、時基要重新設計并制作,當計數=255時,設計為最慢的時間,比此時間更慢就不認可。 3、第一個鍵后計數器設置為1. 4、當計數器=0時,不計數。 5、當計數器<>0時,每個時基+1 6、如此,第二個鍵時,讀到0就作未按處理,不是0就計算速度(力度)。用過了清0。 要點:設計適當的時基,即多久+1. 如果要分得更細,則需增加計數器的位數。 |