// PIC16F876A Configuration Bit Settings // 'C' source line config statements // CONFIG #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled) #pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled) #pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include #define _XTAL_FREQ 20000000 // 20MHz #define THRESHOLD 0x20 #define TIMER_COUNT 60 #define BUFFERSIZE 16 #define CLK PORTCbits.RC4 #define DAT PORTCbits.RC5 #define CPORT TRISCbits.TRISC4 #define DPORT TRISCbits.TRISC5 #define WAIT 40 #define WAITHALF 20 // 0xE0から始まる2バイトコードは0xA0+独自コード // CTRL+キーのコードは0x90+独自コード const char keyMap[6][5] = { { 0, 0, 0xA0, 0x7C, 0x90}, // , , Num/, Num*, Ctrl+C { 0, 0x70, 0x72, 0x7A, 0x69}, // , Num0, Num2, Num3, Num1 {0x71, 0x91, 0x73, 0x74, 0x6B}, // Num., Ctrl+V, Num5, Num6, Num4 { 0, 0x6C, 0x7D, 0x7B, 0x75}, // , Num7, Num9, Num-, Num8 { 0, 0x77, 0, 0x79, 0x92}, // , NumLock, Num+, Ctrl+X { 0, 0, 0, 0xA1, 0} // , , , NumEnter, }; // 0xE0から始まる2バイトコード const char e0KeyMap[] = {0x4A, 0x5A}; // Num/, NumEnter // CTRLの組み合わせ const char ctrlKeyMap[] = {0x21, 0x2A, 0x22}; // Copy, Paste, Cut const char outB[] = {0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000}; const char setB[] = {0b11111110, 0b11111101, 0b11111011, 0b11110111, 0b11101111, 0b11011111}; char keyStat[6][5]; char keycode; volatile int row; volatile char keyBuffer[BUFFERSIZE]; volatile int head, knum; // アナログ入力 char analogRead(char ch) { ADCON0bits.CHS = ch; ADCON0bits.ADON = 1; // ADコンバータON __delay_us(20); // 充電待ち //ADコンバート ADCON0bits.GO = 1; // AD変換開始 while(ADCON0bits.GO); // AD変換終了待ち ADCON0bits.ADON = 0; return ADRESL; } // キーをバッファに入力 void enqueue(char ch) { if(knum < BUFFERSIZE) { keyBuffer[(head + knum) % BUFFERSIZE] = ch; ++knum; } } // キーをバッファから取り出す char dequeue() { char ch = 0xff; if(knum > 0) { ch = keyBuffer[head]; head = (head + 1) % BUFFERSIZE; --knum; } return ch; } // キー押した void pushKey(char code) { switch(code & 0xF0) { case 0xA0: // 2バイトコード(0xE0) enqueue(0xE0); enqueue(e0KeyMap[code & 0x0F]); break; case 0x90: // CTRL+ enqueue(0x14); // LeftCtrl enqueue(ctrlKeyMap[code & 0x0F]); break; default: enqueue(code); } } // キー離した void releaseKey(char code) { switch(code & 0xF0) { case 0xA0: // 2バイトコード(0xE0) enqueue(0xE0); enqueue(0xF0); enqueue(e0KeyMap[code & 0x0F]); break; case 0x90: // CTRL+ enqueue(0xF0); enqueue(ctrlKeyMap[code & 0x0F]); enqueue(0xF0); enqueue(0x14); // LeftCtrl break; default: enqueue(0xF0); enqueue(code); } } void keyCheck(int an, int r) { if(analogRead((char)an) > THRESHOLD) { if(keyStat[r][an] == 0) // キーが押された { pushKey(keyMap[r][an]); keyStat[r][an] = 1; } } else { if(keyStat[r][an] == 1) // キーが離された { releaseKey(keyMap[r][an]); keyStat[r][an] = 0; } } } // キースキャン割り込みエントリ void __interrupt() isr(void) { if (INTCONbits.TMR0IF) // TMR0 { INTCONbits.TMR0IF = 0; // 割り込みフラグクリア switch(row) { case 0: TRISB = setB[0]; PORTB = outB[0]; for(int i=2; i<5; i++) { keyCheck(i, 0); } break; case 1: case 3: TRISB = setB[row]; PORTB = outB[row]; for(int i=1; i<5; i++) { keyCheck(i, row); } break; case 2: TRISB = setB[2]; PORTB = outB[2]; for(int i=0; i<5; i++) { keyCheck(i, 2); } break; case 4: TRISB = setB[4]; PORTB = outB[4]; keyCheck(1, 4); keyCheck(3, 4); keyCheck(4, 4); break; case 5: TRISB = setB[5]; PORTB = outB[5]; keyCheck(3, 5); break; } if(++row > 5) row = 0; TMR0 = TIMER_COUNT; // タイマ再設定 } } // PCからコマンドを受け取る char getCommand() { char code = 0, onebit; while (DAT == 1); //スタートビットを待つ while (CLK == 0); //HIGHを待つ CPORT = 0; // CLK OUTPUT __delay_us(WAITHALF); //1バイト受け取る for(int i = 0; i < 8; i++) { CLK = 0; //クロック __delay_us(WAIT); CLK = 1; //クロック __delay_us(WAITHALF); onebit = DAT; code |= onebit << i; __delay_us(WAITHALF); } //パリティビットは読み取らない CLK = 0; __delay_us(WAIT); CLK = 1; __delay_us(WAIT); //ストップビットも読み取らない CLK = 0; __delay_us(WAIT); CLK = 1; __delay_us(WAIT); //ACK DPORT = 0; // DAT OUTPUT DAT = 0; CLK = 0; __delay_us(WAIT); CLK = 1; CPORT = 1; // CLK INPUT DAT = 1; //__delay_us(WAITHALF); DPORT = 1; // DAT INPUT __delay_us(WAIT); return code; } // PCにキーコードを送る void sendKeyCode(char code) { char onebit, parity = 0; DPORT = 0; // DAT OUTPUT DAT = 0; //スタートビット __delay_us(WAITHALF); CPORT = 0; // CLK OUTPUT CLK = 0; __delay_us(WAIT); CLK = 1; __delay_us(WAITHALF); //1バイト送る for(int i = 0; i < 8; i++) { onebit = (code >> i) & 1; DAT = onebit; __delay_us(WAITHALF); CLK = 0; __delay_us(WAIT); CLK = 1; __delay_us(WAITHALF); parity += onebit; } DAT = ~parity & 1; //パリティビット __delay_us(WAITHALF); CLK = 0; __delay_us(WAIT); CLK = 1; __delay_us(WAITHALF); DAT = 1; //ストップビット __delay_us(WAITHALF); CLK = 0; __delay_us(WAIT); CLK = 1; CPORT = 1; // CLK INPUT DPORT = 1; // DAT INPUT __delay_us(WAIT); } // PCからのコマンドを処理する void commandExec() { switch(getCommand()) { case 0xED: // LED点灯 sendKeyCode(0xFA); getCommand(); // LEDは無いので無視 sendKeyCode(0xFA); break; case 0xFF: // リセット sendKeyCode(0xFA); sendKeyCode(0xAA); break; case 0xF0: // スキャンコード sendKeyCode(0xFA); getCommand(); // コードセットは2 sendKeyCode(0xFA); break; case 0xFE: // 再送要求? sendKeyCode(0); // 他のキーボードを参考にした break; case 0xEE: // Echo sendKeyCode(0xEE); break; default: // 他のコマンドにはACKを返すだけ sendKeyCode(0xFA); break; } } void main(void) { CVRCONbits.CVREN = 0; // コンパレータ電力カット CVRCONbits.CVROE = 0; // コンパレータ VrefをRA2から切り離し SSPCONbits.SSPEN = 0; // SPI/I2C無効 SCK SDO SDI SSはI/Oにする RCSTAbits.SPEN = 0; // シリアルポート無効 RC6/RC7はデジタルI/Oにする ADCON0 = 0b10000000; //TAD 1/20μs * 32 = 1.6μs ADCON1 = 0b10000010; // VREF使わない AN0〜RA4はアナログI/Oにする CMCON = 0b00000111; // コンパレータOFF CCP1CON = 0b00000000; // CCP1(Capture/Compare/PWM)無効 CCP2CON = 0b00000000; // CCP2無効 // I/O設定 0.OUTPUT 1.INPUT TRISA = 0b00101111; // AD入力 TRISB = 0b00000000; TRISC = 0b00110000; // 出力 0.LOW 1.HI PORTA = 0b00000000; PORTB = 0b00000000; PORTC = 0b00010000; // タイマー0設定 インターバル時間10msにする // クロック周期 20MHz×4 = 0.05μs×4 = 0.2μs // タイマーカウント数 = インターバル時間÷クロック周期 = 10000÷0.2 = 50000 // プリスケーラ256にする 50000÷256 = 195.3125 ≒ 195カウント(TIMER_COUNT = 255-195) OPTION_REGbits.T0CS = 0; // TMR0クロックソースは内部クロック OPTION_REGbits.PSA = 0; // プリスケーラをTMR0へ割り当て OPTION_REGbits.PS = 0b111; // プリスケーラ256 row = 0; head = 0; knum = 0; // キー状態初期化 for(int i=0; i<6; i++) for(int j=0; j<5; j++) keyStat[i][j] = 0; __delay_ms(300); // PS/2→USBデバイス準備待ち // タイマー割り込み開始 TMR0 = TIMER_COUNT; INTCONbits.TMR0IE = 1; // TMR0割り込みを許可 INTCONbits.GIE = 1; // すべての割り込み許可 while(1) { if(CLK == 0) // PCからのコマンド要求 { INTCONbits.TMR0IE = 0; // TMR0割り込み禁止 commandExec(); INTCONbits.TMR0IE = 1; // TMR0割り込み許可 } else { keycode = dequeue(); if(keycode != 0xff) { sendKeyCode(keycode); } } } return; }