このブログは、サーバー代など、運営費のため広告やアフェリエイトがあります。
大変申し訳ありませんが、ご理解よろしくお願いします。

このブログは、サーバー代など、運営費のため広告やアフェリエイトがあります。
大変申し訳ありませんが、ご理解よろしくお願いします。

CH32V203K8T6のデジタル入力(スイッチ入力)

スイッチ回路の様子 電子工作
スポンサーリンク

こんにちは。Yukiです。
今回は、CH32V203K8T6のデジタル入力(タクトスイッチ入力)をしてみたいと思います。

回路図

CH32V203K8T6_スイッチ入力

今回は、PA15にLEDを接続しています。タクトスイッチはPA0に接続しています。

今回は、内部プルアップを使用します。

必要なもの(宣伝)

WCH-LinkEhttps://s.click.aliexpress.com/e/_DemgOeL
CH32V203K8T6https://s.click.aliexpress.com/e/_Dd39f3D
LEDhttps://s.click.aliexpress.com/e/_Dmo6BJZ
抵抗 1kΩhttps://s.click.aliexpress.com/e/_DemuIsP
抵抗 470kΩhttps://s.click.aliexpress.com/e/_DemuIsP

アリエクのサイトを貼り付けておきます。参考程度にどうぞ。(ブログの運営資金とかになります)

写真

スイッチ回路の様子

今回は、こんな感じで作りました。CH32V203K8T6のブレークアウト基板は、再設計した後、Boothで販売しようかなと思っています。(電源ICの入手性が悪いんですよね…)

早速プログラミング

#include <ch32v20x.h>

void RCC_init(void); // ペリフェラル有効化
void Pin_init(void); // 入出力設定

int main(void){
    RCC_init();
    Pin_init();

    while(1){
        if((GPIOA->INDR & (1 << 0)) == 0){
            GPIOA->OUTDR |= (1 << 15);
        }else{
            GPIOA->OUTDR &= ~(1 << 15);
        }
    }
}

// ペリフェラル有効化
void RCC_init(void) {
    // GPIOA有効
    RCC->APB2PCENR |= RCC_APB2Periph_GPIOA;
}

// 入出力設定
// 注:ペリフェラルはここでは設定しないこと
// 今回はOUTPUT 10MHzで固定
void Pin_init(void) {

    // PA0 SW (INPUT_PULLUP)
    // PA15 LED (OUTPUT)

    // スイッチ入力設定
    // PA0 Inputモード (MODEy)
    GPIOA->CFGLR &= ~GPIO_CFGLR_MODE0_0;
    GPIOA->CFGLR &= ~GPIO_CFGLR_MODE0_1;
    // PA0 プルアップ/プルダウンモード (CNFy)
    GPIOA->CFGLR &= ~GPIO_CFGLR_CNF0_0;
    GPIOA->CFGLR |= GPIO_CFGLR_CNF0_1;
    // PA0 プルアップに設定
    GPIOA->OUTDR |= GPIO_OUTDR_ODR0;

    // LED 出力設定
    // PA15 OUTPUTモード 10MHz (MODEy)
    GPIOA->CFGHR |= GPIO_CFGHR_MODE15_0;
    GPIOA->CFGHR &= ~GPIO_CFGHR_MODE15_1;
    // PA15 プッシュプルモード(CNFy)
    GPIOA->CFGHR &= ~GPIO_CFGHR_CNF15_0;
    GPIOA->CFGHR &= ~GPIO_CFGHR_CNF15_1;
}

Userのmain.cに書き込んでください。

簡単な説明

RCC_init()にて、指定したペリフェラルにクロックを供給し、準備可能にします。
今回は、RCC->APB2PCENRのRCC_APB2Periph_GPIOAビットを1にすることで、GPIOAを使用可能にしています。

Pin_init()にて、GPIOの設定(入出力設定など)をします。
今回は、GPIOA->CFGLRの各MODEとCNFビットにそれぞれ打ち込むことで、入出力が可能になります。例えば、MODE[1:0]ビットに0b01と打ち込めば、10MHzのOUTPUTモードになります。
0b00とすることで、INPUTモードになります。
CNF[1:0]ビットは、より細かい操作をするビットになります。OUTPUTモードの場合で、0b00であれば、プッシュプルモードとして動作します。INPUTモードの場合で、0b10とすると、プルアップ/プルダウンモードになります。

プルアップは、GPIO_OUTDRレジスタ(今回はGPIOA->OUTDR)の各ビットを1にすると、プルアップになります。

次にmain関数についての説明です。
GPIO_INDRレジスタ(今回はGPIOA->INDR)の各ビットに、それぞれGPIOの状態が格納されます。このレジスタの各ビットを条件分岐することで、ボタンの操作が出来るようになります。今回だと、プルアップなので、押していない時(オープン)の時、1になり、押している(クローズ)の時、0になります。

次に、GPIOの出力を行います。GPIO_OUTDRレジスタ(今回はGPIOA->OUTDR)の各ビットに、それぞれのGPIOの状態を格納します。それぞれのビットに1が入れば、そのピンはVcc(正確には最大Vcc-0.5v)。0を入れれば、GNDレベル(正確には最大GND+0.5V)になります。

Arduino風にしてみる

ポート操作(特にGPIOの初期化とか)については、Arduino風でpinMode的なのが使えるようになりたいなと思っています。

ということで、書いていきます。まずは、GPIOxの有効化です。

//GPIOAの有効化
void GPIOA_init(void){
    RCC->APB2PCENR |= RCC_APB2Periph_GPIOA;
}

これと同じように、GPIOA,B,C,Dを作りました。(CH32V203K8T6にGPIOCはないですが、互換性の関係で作りました)

次に、GPIO_OUTPUT時の、スピード指定についてです。これは、Arduinoにはない機能だったので、関数を作りました。

//GPIO OUTPUT時の速度指定
//引数:speed:速度指定 mylib.h参照
//戻り値:入力されている値を返す。
//speedに0を指定すると、値が書き換わらない
int GPIO_output_speed(int speed){
    static int mode_speed = OUTPUT_2MHz;
    if(speed){
        mode_speed = speed;
    }
    return mode_speed;
}

分かりにくいですが、こんな感じにプログラムしました。引数 speedに0が入った場合、あらかじめ保存されているmode_speedが返されます。これは、pinMode内部で使用する想定で作られています。

次に、pinMode(入出力設定)です。これは、先に、GPIOx(A,B,C,D)の振り分けを行ってしまいます。(本当は、もっと良い方法があるのかもしれませんが、私にはこれが限界です…)次に、INPUT/OUTPUTで振り分けを行い、最終的に、ビット演算と置き換え関数により処理しています。

//GPIO入出力設定
//pin:ピン番号(mylib.h参照)
//mode:モード(OUTPUTなど mylib.h参照)
void pinMode(int Pin,int mode){
    //GPIOがA(0x1y)だったら
    if((Pin & 0xF0) == 0x10){
        //OUTPUTだったら
        if((mode & 0b1000) == 0){
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOA->CFGLR = bit_replace(GPIOA->CFGLR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOA->CFGLR = bit_replace(GPIOA->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOA->CFGHR = bit_replace(GPIOA->CFGHR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOA->CFGHR = bit_replace(GPIOA->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }
        }else{//INPUT
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOA->CFGLR = bit_replace(GPIOA->CFGLR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOA->CFGLR = bit_replace(GPIOA->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOA->CFGHR = bit_replace(GPIOA->CFGHR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOA->CFGHR = bit_replace(GPIOA->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
                
            }
            //プルアップもしくはプルダウンを選択している場合
            if((mode & 0b0011) == 0b10){
                GPIOA->OUTDR = bit_replace(GPIOA->OUTDR,((mode & 0b0100) >> 2),1,(Pin & 0x0F));
            }
        }
    }
    //GPIOがB(0x2y)だったら
    if((Pin & 0xF0) == 0x20){
        //OUTPUTだったら
        if((mode & 0b1000) == 0){
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOB->CFGLR = bit_replace(GPIOB->CFGLR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOB->CFGLR = bit_replace(GPIOB->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOB->CFGHR = bit_replace(GPIOB->CFGHR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOB->CFGHR = bit_replace(GPIOB->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }
        }else{//INPUT
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOB->CFGLR = bit_replace(GPIOB->CFGLR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOB->CFGLR = bit_replace(GPIOB->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOB->CFGHR = bit_replace(GPIOB->CFGHR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOB->CFGHR = bit_replace(GPIOB->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }
            //プルアップもしくはプルダウンを選択している場合
            if((mode & 0b0011) == 0b10){
                GPIOB->OUTDR = bit_replace(GPIOB->OUTDR,((mode & 0b0100) >> 2),1,(Pin & 0x0F));
            }
        }
    }
    //GPIOがC(0x3y)だったら
    if((Pin & 0xF0) == 0x30){
        //OUTPUTだったら
        if((mode & 0b1000) == 0){
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOC->CFGLR = bit_replace(GPIOC->CFGLR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOC->CFGLR = bit_replace(GPIOC->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOC->CFGHR = bit_replace(GPIOC->CFGHR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOC->CFGHR = bit_replace(GPIOC->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }
        }else{//INPUT
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOC->CFGLR = bit_replace(GPIOC->CFGLR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOC->CFGLR = bit_replace(GPIOC->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOC->CFGHR = bit_replace(GPIOC->CFGHR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOC->CFGHR = bit_replace(GPIOC->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }
            //プルアップもしくはプルダウンを選択している場合
            if((mode & 0b0011) == 0b10){
                GPIOC->OUTDR = bit_replace(GPIOC->OUTDR,((mode & 0b0100) >> 2),1,(Pin & 0x0F));
            }
        }
    }
    //GPIOがD(0x4y)だったら
    if((Pin & 0xF0) == 0x40){
        //OUTPUTだったら
        if((mode & 0b1000) == 0){
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOD->CFGLR = bit_replace(GPIOD->CFGLR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOD->CFGLR = bit_replace(GPIOD->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOD->CFGHR = bit_replace(GPIOD->CFGHR,GPIO_output_speed(0),2,((Pin & 0b0111) * 4));
                //CNF
                GPIOD->CFGHR = bit_replace(GPIOD->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }
        }else{//INPUT
            //レジスタがHigh側かLow側か
            if((Pin & 0b1000) == 0){
                //LOW側
                //MODE
                GPIOD->CFGLR = bit_replace(GPIOD->CFGLR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOD->CFGLR = bit_replace(GPIOD->CFGLR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }else{
                //HIGH側
                //MODE
                GPIOD->CFGHR = bit_replace(GPIOD->CFGHR,0b00,2,((Pin & 0b0111) * 4));
                //CNF
                GPIOD->CFGHR = bit_replace(GPIOD->CFGHR,(mode & 0b0011),2,(Pin & 0b0111) * 4 + 2);
            }
            //プルアップもしくはプルダウンを選択している場合
            if((mode & 0b0011) == 0b10){
                GPIOD->OUTDR = bit_replace(GPIOD->OUTDR,((mode & 0b0100) >> 2),1,(Pin & 0x0F));
            }
        }
    }
}

次に、digitalReadです。これは、IOポートの入力関数です。これも同様に、GPIOx(A,B,C,D)を振り分け、ビット演算と条件分岐で振り分けています。

// GPIOを取得する関数
// 戻り値:ピンがHIGH(1)かLOW(0)か
// 0xFFが返却されたらエラー
unsigned char digitalRead(int Pin) {
    // GPIOxがA(0x1y)だったら
    if ((Pin & 0xF0) == 0x10) {
        if (GPIOA->INDR & (1 << (Pin & 0x0F))) {
            return 1;
        } else {
            return 0;
        }
    }
    // GPIOxがB(0x2y)だったら
    if ((Pin & 0xF0) == 0x20) {
        if (GPIOB->INDR & (1 << (Pin & 0x0F))) {
            return 1;
        } else {
            return 0;
        }
    }
    // GPIOxがC(0x3y)だったら
    if ((Pin & 0xF0) == 0x30) {
        if (GPIOC->INDR & (1 << (Pin & 0x0F))) {
            return 1;
        } else {
            return 0;
        }
    }
    // GPIOxがD(0x4y)だったら
    if ((Pin & 0xF0) == 0x40) {
        if (GPIOD->INDR & (1 << (Pin & 0x0F))) {
            return 1;
        } else {
            return 0;
        }
    }
    return 0xFF; // エラー
}

最後に、digitalWriteです。IOポートの出力関数です。これらも同様に、GPIOx(A,B,C,D)を振り分け、引数valの条件分岐によって、ビット演算をしています。

// デジタル値出力
// Pin:ピン 名付け 0xYZ Y:GPIOx(A:1 B:2 C:3 ...)
//     Z:ポート番号 GPIOA-10 → 0x1Aとなる。
// val:出力値 (1or0)
void digitalWrite(int Pin, int val) {
    // GPIOxがA(0x1y)だったら
    if ((Pin & 0xF0) == 0x10) {
        if (val == 0) {
            GPIOA->OUTDR &= ~(1 << (Pin & 0x0F));
        } else {
            GPIOA->OUTDR |= (1 << (Pin & 0x0F));
        }
    }

    // GPIOxがB(0x2y)だったら
    if ((Pin & 0xF0) == 0x20) {
        if (val == 0) {
            GPIOB->OUTDR &= ~(1 << (Pin & 0x0F));
        } else {
            GPIOB->OUTDR |= (1 << (Pin & 0x0F));
        }
    }
    // GPIOxがC(0x3y)だったら
    if ((Pin & 0xF0) == 0x30) {
        if (val == 0) {
            GPIOC->OUTDR &= ~(1 << (Pin & 0x0F));
        } else {
            GPIOC->OUTDR |= (1 << (Pin & 0x0F));
        }
    }

    // GPIOxがD(0x4y)だったら
    if ((Pin & 0xF0) == 0x40) {
        if (val == 0) {
            GPIOD->OUTDR &= ~(1 << (Pin & 0x0F));
        } else {
            GPIOD->OUTDR |= (1 << (Pin & 0x0F));
        }
    }
}

//  ビット置き換え関数
//  引数 data:置き換え前のビット列 | byte:置き換えるビット |
//  len:置き換えるビットの長さ | shift:シフト数
uint32_t bit_replace(uint32_t data, uint32_t byte, uint8_t len, uint8_t shift) {
    uint32_t mask = ~(((1 << len) - 1) << shift);
    data &= mask;
    data |= byte << shift;
    return data;
}

Arduino風関数のGitHub配布

プロジェクトを配布しておきます。今後も様々な関数を組み込んで、より充実させていく予定です。

Build software better, together
GitHub is where people build software. More than 150 million people use GitHub to discover, fork, and contribute to over...

参考になったら、コーヒー1杯奢ってくれるとうれしいです
Buy Me A Coffee
電子工作
スポンサーリンク
シェアする
denshi1996をフォローする
スポンサーリンク

コメント

タイトルとURLをコピーしました