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

今回は、PA15にLEDを接続しています。タクトスイッチはPA0に接続しています。
今回は、内部プルアップを使用します。
必要なもの(宣伝)
アリエクのサイトを貼り付けておきます。参考程度にどうぞ。(ブログの運営資金とかになります)
写真

今回は、こんな感じで作りました。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配布
プロジェクトを配布しておきます。今後も様々な関数を組み込んで、より充実させていく予定です。



コメント