STM32 マイコンボードに 48 キーボードを実装


こういうのを作りました。

![IMG_20191102_104525.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/feb9d11c-28af-64be-ddfd-398642232757.jpeg)

2017年のAVTOKYOのバッジを2017年に作りましたが、今回はそれにキーボードをつくってみた。

元々、エニグマ暗号機を実装したマイコンボードをつくろうかとおもっていて、そのためにキーボードがほしいなあ。

# 検討

- QWERTYキーボードがほしい
- タッチパネル方式
- → コストが
- タッチパッド方式
--  誘電方式
-- 抵抗方式
- メカキー

タッチパネル方式でマトリックスを作りたかったがどうにもいい解決策ができなかった。
結果、メカキーを採用。
このため、物理スイッチを基板上に実装しないといけなくなった。


# マトリックスキーボード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/7fb10374-3e60-2950-45f9-e19c754747ef.png)

特殊キーは以下の12キーをサポート

ESC SPACE TAB
CTRL CAPS
BS ENTER 記号 ↑↓←→

数字10＋アルファベット26＋特殊キー12




# マトリックス方式
今回は14本の信号線でコントロールることにした。

1x13 13キー
2x12 24キー
3x11 33キー
4x10 40キー
5x9 45キー
6x8 48キー
7x7 49キー

48キーボードのマトリックスを6x8で考えました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/59a43f42-2232-fd10-4b7e-82ef3e08db68.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/c16d07c6-03b5-42ab-bf8c-108907b72dc1.png)
49キーボードも7x7で考えることもできますが、まだ僕には早すぎた。

# ロールオーバー

## 同時に3つまでのキーが押されてもok
 3キーロールオーバー
## 同時に2つまでのキーが押される
2キーロールオーバー

2キーロールオーバー以上を実現するとなると、ダイオードが必要

今回は、キットで作ることを考え部品点数を削減したい。
そのため隣り合うキーをまちがって押してもなるべく干渉しないように配列を工夫した。




![keymatrix-zoom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/b67c37e0-efc1-47d1-a8e3-ced6b59fcef7.png)


その他

- STM32のオープンドレインで駆動
- 抵抗は表面
- 49キーはキーボードコントローラの配下にあり、ジョイパッドキーはキーボードコントローラの配下に無い。

# テストプログラム

```

// AVTokyo2019Badge-m0
// keyboard test
#include "EEPROM.h"

#include <SPI.h>
#include <Wire.h>
// #include <Adafruit_GFX.h>
#include <Adafruit_SSD1306_STM32.h>

#define PAGE_SIZE 0x400
#define MEM_START 0x8000000

#define OLED_MOSI  PA7
#define OLED_CLK   PA5
#define OLED_DC    PA6
#define OLED_CS    PA4
#define OLED_RESET PA3 
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);


#define BUTTONA PA0
#define BUTTONB PC14
#define BUTTONUP PB5
#define BUTTONDOWN PB6



#define KEYX0 PB1
#define KEYX1 PA2
#define KEYX2 PA1
#define KEYX3 PC15 // PC15 source is 0mA (Use Open Drain mode)
#define KEYX4 PB9
#define KEYX5 PB4

#define KEYY0 PB3
#define KEYY1 PA10
#define KEYY2 PA9
#define KEYY3 PA8
#define KEYY4 PB15
#define KEYY5 PB14
#define KEYY6 PB13
#define KEYY7 PB12


#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2

#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16


 int i,counter=0;
#define LED  PC13
#define BOARD_BUTTON_PIN PB10 
#define COPY_PIN PA8
#define PWM_PIN0  PB0  // TIMER3
#define PWM_PIN1  PB1  // TIMER3

int keyY[8]={KEYY0,KEYY1,KEYY2,KEYY3,KEYY4,KEYY5,KEYY6,KEYY7};
int keyX[6]={KEYX0,KEYX1,KEYX2,KEYX3,KEYX4,KEYX5};

static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B0000000,
  B00000011, B11100000,
  B00001101, B11110000,
  B00011011, B11111000,
  B00110011, B11111100,
  B01111110, B11111110,
  B11111110, B11111111,
  B11110011, B10011111,
  B11111111, B11111111,
  B11001101, B01110011,
  B01111011, B10100110,
  B00111111, B11111100,
  B00011111, B11111000,
  B00001100, B11110000,
  B00000111, B01100000,
  B00000011, B11000000 };

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

char readbuffer[255];
char checksumstr[3]={'A','A',0};
char namestr[5]={'A','A','A',0};
String temp;
char menustr[5][20] = { 
  "   keyboard    ",
  "   keycheck    ",
  "   keydisp     ",
  "    credit     ",
  "factory reset  "
  };

long av=500;

void testdrawchar(void) {
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);

  for (uint8_t i=0; i < 168; i++) {
    if (i == '\n') continue;
    display.write(i);
    if ((i > 0) && (i % 21 == 0))
      display.println();
  }    
  display.display();
}

void testdrawcircle(void) {
  for (int16_t i=0; i<display.height(); i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, WHITE);
    display.display();
  }
}


int menu(void){
    int menuitem,j;
    int exitflag=0;
    int namey=30;
    int maxitem=5;
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(0,10);  
    display.print("AV");
    display.setTextSize(1);
    display.print(" = ");
    display.print(av);

    while ( exitflag != 1 ){

      delay(200);
      if ( 1==digitalRead(BUTTONDOWN))
        {
          menuitem=menuitem+1;
        }
      if ( 1==digitalRead(BUTTONUP))
        {
          menuitem=menuitem-1;
        }
      if ( 1==digitalRead(BUTTONB))
        {
          exitflag=1;
        }
      if ( maxitem < menuitem  ) { menuitem = 0; }
      if ( 0 > menuitem  ) { menuitem =maxitem; }
      display.setCursor(0,30);  
      display.setTextSize(1);
      for ( j = 0; j<maxitem+1 ; j++ ){
        display.setCursor(24,20+j*(8*1+1));  
        if ( j == menuitem ) {
           display.setTextColor(BLACK,WHITE);
        } else {
           display.setTextColor(WHITE,BLACK);       
        }
        display.println(menustr[j]);
      }
      display.display();
    }
  return(menuitem);
}



int credit(void){
    int menuitem,j;
    int exitflag=0;
    int namey=30;
    int maxitem=5;
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0,0);  
    display.println("    -   credit   -");
    display.display();
    while ( exitflag != 1 ){

      delay(200);
      if ( 1==digitalRead(BUTTONB))
        {
          exitflag=1;
        }
      display.setCursor(0,10);  
      display.setTextSize(1);
      display.println("This is OSHW.");
      display.println("License:Public Domain");
      display.println("Powered By AVTOKYO");
      display.println("        &");
      display.println("LowLevelStudySociety");
      display.println("nodrinknohack proj.");
      display.display();
    }
  return(menuitem);
}



int buttunwait(int wait){

   int exitflag=0;
    for ( int  i= 0;( i < wait*10 )&&  (exitflag != 1); i++ ){
   // display.println(j);

      delay(100);
      if ( 1==digitalRead(BUTTONB)||1==digitalRead(BUTTONDOWN)||1==digitalRead(BUTTONUP) )
        {
          exitflag=1;
        }
    }    
    while ( 1==digitalRead(BUTTONB)||1==digitalRead(BUTTONDOWN)||1==digitalRead(BUTTONUP) )
        {
          delay(100);
        }

}

int keyboard(void)
{
  
    int menuitem,i,j;
    int exitflag=0;
    int namey=30;
    int maxitem=5;
    int keyx=0;
    int keyy=0;
        
    while ( exitflag != 1 ){
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(WHITE);
      display.setCursor(0,0);  
      display.println("    -   keyboard   -");
      display.display();

  
      delay(200);
      if ( 1==digitalRead(BUTTONB))
          {
            exitflag=1;
          }
      keyx=-1;
      keyy=0;
      digitalWrite(KEYX0,1);
      digitalWrite(KEYX1,1);
      digitalWrite(KEYX2,1);
      digitalWrite(KEYX3,1);
      digitalWrite(KEYX4,1);
      digitalWrite(KEYX5,1);
      for ( int x=0; x<6;x++){
        digitalWrite(keyX[x],0);
        int key=0;
        if ( 0==digitalRead(KEYY0)) key+=1;
        if ( 0==digitalRead(KEYY1)) key+=2;
        if ( 0==digitalRead(KEYY2)) key+=4;
        if ( 0==digitalRead(KEYY3)) key+=8;
        if ( 0==digitalRead(KEYY4)) key+=16;
        if ( 0==digitalRead(KEYY5)) key+=32;
        if ( 0==digitalRead(KEYY6)) key+=64;
        if ( 0==digitalRead(KEYY7)) key+=128;
        if ( 0 != key )
         keyx=x;  
        keyy += key;
        digitalWrite(keyX[x],1);
      }
      display.setTextSize(1);
      display.setCursor(0,10);
      display.print("This is Keyboard.");
      display.display();
      display.setTextSize(1);
      display.setCursor(0,30);  
      display.setTextColor(WHITE,BLACK);

      display.print(keyx);
      display.print("  ");
      display.print(keyy);
      display.display();
      delay(200);
      
    }
  return(menuitem);
  
}

int keycheck(void)
{
  
    int menuitem,i,j;
    int exitflag=0;
    int namey=30;
    int maxitem=5;
    int keyx=0;
    int keyy=0;
        
    while ( exitflag != 1 ){
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(WHITE);
      display.setCursor(0,0);  
      display.println("    -   keycheck   -");
      keyx=0;
      keyy=0;
      digitalWrite(KEYX0,1);
      digitalWrite(KEYX1,1);
      digitalWrite(KEYX2,1);
      digitalWrite(KEYX3,1);
      digitalWrite(KEYX4,1);
      digitalWrite(KEYX5,1);
      for ( int x=0; x<6;x++){
        digitalWrite(keyX[x],0);
        int key=0;
        for ( int y=0; y<8;y++){
          if ( 0==digitalRead(keyY[y])){ display.print(1);} else {display.print(0);}
          display.print(" ");
        }
        display.println();
        digitalWrite(keyX[x],1);
        delay(10);
      }
      display.display();
      delay(200);
      if ( 1==digitalRead(BUTTONB))
          {
            exitflag=1;
          }
      
    }
  return(menuitem);
  
}

int keydisp(void)
{
  
    int menuitem,i,j;
    int exitflag=0;
    int namey=30;
    int maxitem=5;
    int keyx=0;
    int keyy=0;
        
    while ( exitflag != 1 ){
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(WHITE);
      display.setCursor(0,0);  
      display.println("    -   keydisp   -");
      keyx=0;
      keyy=0;
      digitalWrite(KEYX0,1);
      digitalWrite(KEYX1,1);
      digitalWrite(KEYX2,1);
      digitalWrite(KEYX3,1);
      digitalWrite(KEYX4,1);
      digitalWrite(KEYX5,1);
      for ( int x=0; x<6;x++){
        digitalWrite(keyX[x],0);
        int key=0;
        for ( int y=0; y<8;y++){
          if ( 0==digitalRead(keyY[y])){ display.print(1);} else {display.print(0);}
          display.print(" ");
        }
        display.println();
        digitalWrite(keyX[x],1);
        delay(10);
      }
      display.display();
      delay(200);
      if ( 1==digitalRead(BUTTONB))
          {
            exitflag=1;
          }
      
    }
  return(menuitem);
  
}

void action()
{
  int state=  digitalRead(BOARD_BUTTON_PIN);
  // digitalWrite(PWM_PIN0,state);
  if (HIGH == state ){
   Timer3.pause();
  Timer3.setCount(0);
  Timer3.refresh();
   } else {
    Timer3.resume();
  }
}

void setup()   {                


  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC);
  // init done

  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(1000);

  disableDebugPorts();  // for use  PA15/PB3/PB4
  pinMode(BUTTONUP,INPUT_PULLDOWN);
  pinMode(BUTTONDOWN,INPUT_PULLDOWN);
  pinMode(BUTTONB,INPUT_PULLDOWN);
  pinMode(BUTTONA,INPUT_PULLDOWN);

  pinMode(KEYX0,OUTPUT_OPEN_DRAIN);
  pinMode(KEYX1,OUTPUT_OPEN_DRAIN);
  pinMode(KEYX2,OUTPUT_OPEN_DRAIN);
  pinMode(KEYX3,OUTPUT_OPEN_DRAIN);
  pinMode(KEYX4,OUTPUT_OPEN_DRAIN);
  pinMode(KEYX5,OUTPUT_OPEN_DRAIN);

  pinMode(KEYY0,INPUT_PULLUP);
  pinMode(KEYY1,INPUT_PULLUP);
  pinMode(KEYY2,INPUT_PULLUP);
  pinMode(KEYY3,INPUT_PULLUP);
  pinMode(KEYY4,INPUT_PULLUP);
  pinMode(KEYY5,INPUT_PULLUP);
  pinMode(KEYY6,INPUT_PULLUP);
  pinMode(KEYY7,INPUT_PULLUP);

  Serial.begin(2400);
  Serial3.begin(2400);
  delay(100);


  Timer3.pause();
  Timer3.setPrescaleFactor(947);   // システムクロック 72MHzを10ｋHzに分周
  Timer3.setOverflow(2);      // wave width = 2 ( 2 * 72MHz / 947 = 38Khz)
  pwmWrite(PWM_PIN0, 1);       // pulse width = 1 (1/2 duty 50%)
  pwmWrite(PWM_PIN1, 1);      // pulse width = 1 (1/2 duty 50%)

  Timer3.setCount(1);
  Timer3.refresh();
    Timer3.resume();
 //   Timer3.resume();
 delay(100);
  Timer3.pause();
  Timer3.setCount(0);
  Timer3.refresh();

    Serial.println("start ...");
  delay(100); 
 

  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0,0);  
  display.setTextColor(WHITE,BLACK);



}

void loop() {
    // text display tests
  int menucode=0;
  // nameentry();
  menucode=menu();
  if ( 0 == menucode ){
    keyboard();
  }
  if ( 1 == menucode ){
    keycheck();
  }
  if ( 2 == menucode ){
    keydisp();
  }
  if ( 3 == menucode ){
    credit();
  }


}
```