プログラムの手順
プログラムは MounRiver Studio Ⅱ を使用するので、予めインストールをすること。
インストールについてはメニューの『MounRiver Studioでプログラム』で説明しています。
プログラムの手順は下記の様になります。
インストールについてはメニューの『MounRiver Studioでプログラム』で説明しています。
プログラムの手順は下記の様になります。
1. クロック速度の設定
2. I/Oの設定
3.~ プログラム
の手順でプログラムを進めて行きます。1.クロック速度の設定
1.1クロック速度とは
プログラムはシステムクロックと言うHi-Loの繰り返し信号によって読み取られ、実行する。
クロックが早ければ処理する時間が短くなるので、多くの処理ができる。
ただしクロックの速さとMPUの消費電流はクロックが早ければ消費電流は多くなる。
例えばクロック周波数FHCLK 8MHzだと1.21(mA)だが 144MHzでは12(mA)と増る。
500kHzだとなんと最低で0.31(mA)に抑えることができる。
電池などで駆動する物を作成するときはこの消費電流が装置設計の大きな要因となるので、一概に処理速度を早くするのではなく、プログラムの処理内容(数)により決める必要がある。
クロックが早ければ処理する時間が短くなるので、多くの処理ができる。
ただしクロックの速さとMPUの消費電流はクロックが早ければ消費電流は多くなる。
例えばクロック周波数FHCLK 8MHzだと1.21(mA)だが 144MHzでは12(mA)と増る。
500kHzだとなんと最低で0.31(mA)に抑えることができる。
電池などで駆動する物を作成するときはこの消費電流が装置設計の大きな要因となるので、一概に処理速度を早くするのではなく、プログラムの処理内容(数)により決める必要がある。
1.2設定方法
速度の設定は system_ch32v20x.c に記載されている文を加工することにより簡単に設定ができます。
加工とは、コメントアウトを外すだけです。
下記の例は、内部発信子を使用してクロックを最大スピードの48MHZで使用します。
加工とは、コメントアウトを外すだけです。
下記の例は、内部発信子を使用してクロックを最大スピードの48MHZで使用します。
|
48MHz_HIS 波形周波数= 8.13(MHz) Hi-Lo処理時間61.5(ns) Lo-Hi処理時間61.5(ns)
|
HSI_VALUE(8Mhz_HIS相当) 波形周波数= 1.351(MHz) Hi-Lo処理時間370(ns) Lo-Hi処理時間370(ns)
|
2.I/Oの設定
2.1設定概要
pinの入力・出力での選択項目は下表のとおり
実際にMUPのどんなレジスタが対象になるかはGPIO*_CFGLRを参照。
| 入力 | 出力 |
|---|---|
|
ハイインピーダンス プルアップ プルダウン アナログ入力 |
オープンドレイン LO側駆動 LO,HIGH 両方駆動 代替機能として使用 信号の立ち上がり時間 |
2.2記述方法
設定プログラムは main.c に記載する。
このプログラムは初期設定なので、サブルーチンとして記載した。
下記例は PB0 を 出力 として設定しています。
このプログラムは初期設定なので、サブルーチンとして記載した。
下記例は PB0 を 出力 として設定しています。
サブルーチンの説明
① GPIOポートBのクロック供給を有効にする。
② GPIO_InitTypeDefという構造体の変数 GPIO_InitStructure を宣言し、すべてのメンバーをゼロで初期化するC言語のコード。
③ GPIO_InitStructure という構造体の中に設定されている、ある特定のGPIOピンをGPIO_Pin_0として指定する。
④ 汎用入出力(GPIO)ピンのモードを「プッシュプル出力」に設定する。
⑤ 汎用出力モードでの信号立ち上がり、立ち下がりの特性を周波数として設定する場所で、ここでは50MHzに設定しています。
⑥ ポートBの汎用入出力(GPIO)ピンを、GPIO_InitStructure という構造体で定義された設定内容に基づいて初期化する。
注意:直接レジスターに書き込む場合は GPIO*_CFGLRを参照。
ポートBの他にAのクロック供給を有効にする場合は or すれば良い
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
② GPIO_InitTypeDefという構造体の変数 GPIO_InitStructure を宣言し、すべてのメンバーをゼロで初期化するC言語のコード。
③ GPIO_InitStructure という構造体の中に設定されている、ある特定のGPIOピンをGPIO_Pin_0として指定する。
ポートA,B,Cは⑥で設定することになるので注意すること。
④ 汎用入出力(GPIO)ピンのモードを「プッシュプル出力」に設定する。
その他設定内容は下表のとおり。
| 記述名 | 説 明 |
|---|---|
| GPIO_Mode_IN_FLOATING | 入力:高インピーダンス |
| GPIO_Mode_IPU | 入力:プルアップ |
| GPIO_Mode_IPD | 入力:プルダウン |
| GPIO_Mode_AIN | 入力:アナログ |
| GPIO_Mode_Out_0D | 出力:オープンドレイン LO側駆動 |
| GPIO_Mode_Out_PP | 出力: LO,HIGH 両方駆動 |
| GPIO_Mode_AF_0D | 代替機能出力:オープンドレイン LO側駆動 |
| GPIO_Mode_AF_PP | 代替機能出力:LO,HIGH両方駆動 |
⑤ 汎用出力モードでの信号立ち上がり、立ち下がりの特性を周波数として設定する場所で、ここでは50MHzに設定しています。
その他設定内容は下表のとおり。
各設定値の立ち上がり・下がり時間の測定結果
負担抵抗の有無で違いが有るかも参考に測定した
| 記述名 | 説 明 |
|---|---|
| GPIO_Speed_2MHz | 下の測定結果を参照 |
| GPIO_Speed_10MHz | |
| GPIO_Speed_50MHz |
各設定値の立ち上がり・下がり時間の測定結果
負担抵抗の有無で違いが有るかも参考に測定した
|
FREQ_48MHz 負担抵抗なし Speed 50MHz 立ち上がり、下がり時間 12ns
|
FREQ_48MHz 負担抵抗430(Ω) Speed 50MHz 立ち上がり、下がり時間 16ns
|
FREQ_48MHz 負担抵抗430(Ω) Speed 2MHz 立ち上がり、下がり時間 20ns
|
⑥ ポートBの汎用入出力(GPIO)ピンを、GPIO_InitStructure という構造体で定義された設定内容に基づいて初期化する。
注意:直接レジスターに書き込む場合は GPIO*_CFGLRを参照。
3.信号出力プログラム
3.1プログラム概要
プログラムのHi-loの間でできるだけ早く繰り返す様にしたプログラム
3.2記述方法
書き方として幾つかの方法がある。
今回は 9種類の書き方でON,OFF時間の変化を測定した。
main.c に下記内容のプログラムを記載し、実行では //○番号 を外してコンパイル、プログラムの書き込みを行った。
WriteBit:WriteBit GPIO_WriteBit()関数を使用したプログラム
OUTDR:OUTDR レジスタに値を入れたプログラム
BBSHR:SHR レジスタのset(BS**)またはreset(BR**)に値を入れたプログラム
各レジスターについては GPIO*_OUTDR および GPIO*_BSHR を参照
今回は 9種類の書き方でON,OFF時間の変化を測定した。
main.c に下記内容のプログラムを記載し、実行では //○番号 を外してコンパイル、プログラムの書き込みを行った。
プログラムの違いによるHi-Lo時間測定結果
測定条件 設定クロック:48MHz_HSI スピード:50MHz 負担抵抗:430Ω|
No① (1->0)516ns (0->1)400ns
|
No② (1->0)392ns (0->1)294ns
|
No③⑤ (1->0)143ns (0->1)147ns
|
No④⑥⑧⑨ (1->0)59ns (0->1)66ns
|
No⑦ (1->0)164ns (0->1)147ns
|
プログラムの違と速さ一覧
| 値の入力方法 | WriteBit | OUTDR | BSHR |
|---|---|---|---|
| or and xor 入力 | ① 1.095MHz | ③⑤ 3.44MHz | ⑦ 3.21MHz |
| ダイレクト入力 | ② 1.46MHz | ④⑥ 8.03MHz | ⑧⑨ 8.03MHz |
OUTDR:OUTDR レジスタに値を入れたプログラム
BBSHR:SHR レジスタのset(BS**)またはreset(BR**)に値を入れたプログラム
各レジスターについては GPIO*_OUTDR および GPIO*_BSHR を参照
注意事項
※④⑥⑧⑨については同じポートで入出力など有った場合の影響について確認していない。特に④⑧は注意が必要。4.信号入力プログラム
4.1プログラム概要
ボタンを押している時に①100ms間隔でHi,Lo信号を繰り返し出力するものを作ります。
スピード設定 3項と同じ 48MHzHSI
出力ピンはPB0 3項と同じ Out_PP,50MHz,負担抵抗430Ω
入力ピンはPB3 ②プルアップ ※プルアップとは入力ピンに抵抗を通してVDDに接続する
③入力の検出
ボタンスイッチは PB3とGNDに接続する。 ④ポタンを押すことにより接点が閉じる。
スピード設定 3項と同じ 48MHzHSI
出力ピンはPB0 3項と同じ Out_PP,50MHz,負担抵抗430Ω
入力ピンはPB3 ②プルアップ ※プルアップとは入力ピンに抵抗を通してVDDに接続する
③入力の検出
ボタンスイッチは PB3とGNDに接続する。 ④ポタンを押すことにより接点が閉じる。
4.2記述方法
書き方は幾つかあるが、3項①で使用した GPIO_WriteBit 系の関数を使用する。
読み出し方は
GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3);
を使用する。
また、100ms間隔でHi,Lo信号を繰り返し出力するため、処理を100(ms)停止する関数を使用する。
Delay_Ms(100);
だが、事前に初期化処理をするための処理をmain()に書き込む
Delay_Init();
以下にプログラムを示す。
黄色が入力PB3
読み出し方は
GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3);
を使用する。
また、100ms間隔でHi,Lo信号を繰り返し出力するため、処理を100(ms)停止する関数を使用する。
Delay_Ms(100);
だが、事前に初期化処理をするための処理をmain()に書き込む
Delay_Init();
以下にプログラムを示す。
プログラムの実行結果
青色が出力PB0黄色が入力PB3
5.割り込みプログラム
5.1割り込みの必要性
☆GoogleのAI先生のご意見を下記に記載します。
割り込み処理が必要となる主な理由は、CPUリソースの効率化、即応性の向上、省電力化の3つに集約される。
GPIOの状態変化(ボタンの押下やセンサーからの信号など)を検知する方法には、主に「ポーリング」と「割り込み」の2つがあります。
それぞれの特徴を理解することで、なぜ割り込み処理が必要なのかが明確になります。
デメリット:
CPUリソースの無駄: 変化が滅多に起こらない場合でも、CPUは常に状態を確認し続けるため、その分の処理能力を消費します。
即応性の遅延: 状態変化がチェックの周期とずれると、反応が遅れる可能性があります。
省電力性の低下: CPUを継続的に動かすため、バッテリー駆動機器などでは電力消費が大きくなります。
メリット:
CPUリソースの効率化: 普段はGPIOの状態を監視する必要がないため、CPUは他の重要なタスクに集中できます。
即応性の向上: 状態変化が起こった瞬間にCPUが通知されるため、迅速な応答が求められる場合に最適です。
省電力化: 待機中はCPUがスリープ状態になることができるため、大幅な消費電力の削減につながります。
どのような場合にGPIO割り込みが必要か?
具体的な応用例としては、以下のようなケースでGPIO割り込み処理の必要性が高まります。
応答性が重要なアプリケーション: ボタンの押下を検知して即座に処理を開始する、モーターの回転数変化にリアルタイムで対応するなど、遅延が許されない用途。
省電力設計: バッテリー駆動のIoTデバイスなどで、イベント発生時のみCPUを起動させて処理を行う。
マルチタスク処理: 複数のタスクを並行して実行している場合に、GPIOの状態監視にCPUが拘束されないようにする。
外部からのイベント通知: 外部のセンサーやデバイスからの信号を、メインプログラムの実行を妨げずに受け取る。
以上から例題をとおして、割り込み処理の必要性をみる。
割り込み処理が必要となる主な理由は、CPUリソースの効率化、即応性の向上、省電力化の3つに集約される。
GPIOの状態変化(ボタンの押下やセンサーからの信号など)を検知する方法には、主に「ポーリング」と「割り込み」の2つがあります。
それぞれの特徴を理解することで、なぜ割り込み処理が必要なのかが明確になります。
ポーリング (Polling)
方法: CPUが定期的にGPIOの状態をチェックし、変化がないかを確認する。デメリット:
CPUリソースの無駄: 変化が滅多に起こらない場合でも、CPUは常に状態を確認し続けるため、その分の処理能力を消費します。
即応性の遅延: 状態変化がチェックの周期とずれると、反応が遅れる可能性があります。
省電力性の低下: CPUを継続的に動かすため、バッテリー駆動機器などでは電力消費が大きくなります。
GPIO割り込み (Interrupt)
方法: GPIOの状態が変化した際に、ハードウェアが自動的にCPUへ信号を送り、実行中の処理を一時中断させて、専用の「割り込みハンドラ」を実行させる。メリット:
CPUリソースの効率化: 普段はGPIOの状態を監視する必要がないため、CPUは他の重要なタスクに集中できます。
即応性の向上: 状態変化が起こった瞬間にCPUが通知されるため、迅速な応答が求められる場合に最適です。
省電力化: 待機中はCPUがスリープ状態になることができるため、大幅な消費電力の削減につながります。
どのような場合にGPIO割り込みが必要か?
具体的な応用例としては、以下のようなケースでGPIO割り込み処理の必要性が高まります。
応答性が重要なアプリケーション: ボタンの押下を検知して即座に処理を開始する、モーターの回転数変化にリアルタイムで対応するなど、遅延が許されない用途。
省電力設計: バッテリー駆動のIoTデバイスなどで、イベント発生時のみCPUを起動させて処理を行う。
マルチタスク処理: 複数のタスクを並行して実行している場合に、GPIOの状態監視にCPUが拘束されないようにする。
外部からのイベント通知: 外部のセンサーやデバイスからの信号を、メインプログラムの実行を妨げずに受け取る。
以上から例題をとおして、割り込み処理の必要性をみる。
5.2割り込み無しのプログラム
5.2.1 要望する動作
メインで常に100msで一区切りの仕事(プログラム)が連続動作しており、ボタンを押した時・離した時に同時に違う仕事(プログラム)を実施するものを作成する。具体的には下記動作のプログラムを割り込み無しで作成する。
(1)100(ms)毎にHi-Loを繰り返す信号を出す。
なお100(ms)はなにかの仕事(プログラム)をしているとみなし『Delay(100)』で処理する。
(2)ボタンと押すと Lo信号、離すと Hi信号に変化する。
各端子はPB0が(1)の出力、PB1が(2)の出力、PB3にスイッチを接続、スイッチの片方はGNDに接続する。
5.2.2 プログラム
5.2.3 プログラムの実行波形
黄色信号が PB3 で、スイッチを押すと Lo 、離すと Hi の信号が PB3 に入る。水色信号が PB0 から出ている信号で、100(ms)毎にHi-Loを繰り返している。
ピンク色が PB1 から出ている信号だが・・

5.2.4 プログラムの問題点
PB1の信号はボタンスイッチを押せばLo、離せばHiになるはずだが、
PB1のピンク色信号が変化するのは、PB0の水色信号が変化する時になっている。
この動作はプログラムのとおりだが・・・
『要望する動作』では 『ボタンを押した時・離した時に同時に違う仕事』と記載されていることから要望と同じ様になっていない。
この動作を簡単に動作させるには割り込みを使用する。
PB1のピンク色信号が変化するのは、PB0の水色信号が変化する時になっている。
この動作はプログラムのとおりだが・・・
『要望する動作』では 『ボタンを押した時・離した時に同時に違う仕事』と記載されていることから要望と同じ様になっていない。
この動作を簡単に動作させるには割り込みを使用する。
5.3 割り込みを使うプログラム
割り込み処理に不明の異常があり端子の役目を変更した。
PB0 で、スイッチを押すと Lo 、離すと Hi の信号が P0 に入る。(オシロでは黄色信号)
PA0 から出ている信号で、100(ms)毎にHi-Loを繰り返している。(オシロでは水色信号)
PA1 から出ている信号。(オシロではピンク色)
割り込みの㉑㉒㉓は 割り込みプログラムを記載した ch32v20x_it.c にまとめて記載した方が良いかも?
PB0 で、スイッチを押すと Lo 、離すと Hi の信号が P0 に入る。(オシロでは黄色信号)
PA0 から出ている信号で、100(ms)毎にHi-Loを繰り返している。(オシロでは水色信号)
PA1 から出ている信号。(オシロではピンク色)
5.3.1 プログラム概要
ボタンを押した時、ボタンを離した時のプログラムを割り込み処理させる5.3.2 プログラムの流れ
(1)GPIO_Init
外部ピンの設定
(2)GPIO_EXTILineConfig
GPIOピンを割り込みとして使えるように設定する。
(3)EXTI_Init
GPIOピンからの信号を、CH32Vマイコン内のどの外部割り込みラインに接続するかを設定する。
(4)NVIC_Init
特定の外部割り込みライン(例:EXTI Line 1)が、割り込みを発生させたときに、
CPUに処理を依頼できるように設定(有効化)する。
また、その割り込みの優先度を決定する。
(5)int main (void) {メイン処理}
(6)void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void EXTI0_IRQHandler(void){ 割り込み処理 }
GPIOピンからの信号がトリガーとなり、割り込みが発生すると、
CPUがNVICの設定に基づいて割り込みハンドラを呼び出す。
外部ピンの設定
(2)GPIO_EXTILineConfig
GPIOピンを割り込みとして使えるように設定する。
(3)EXTI_Init
GPIOピンからの信号を、CH32Vマイコン内のどの外部割り込みラインに接続するかを設定する。
(4)NVIC_Init
特定の外部割り込みライン(例:EXTI Line 1)が、割り込みを発生させたときに、
CPUに処理を依頼できるように設定(有効化)する。
また、その割り込みの優先度を決定する。
(5)int main (void) {メイン処理}
(6)void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void EXTI0_IRQHandler(void){ 割り込み処理 }
GPIOピンからの信号がトリガーとなり、割り込みが発生すると、
CPUがNVICの設定に基づいて割り込みハンドラを呼び出す。
注意事項:
GPIO_Pin_* 、EXTI_Line* 、GPIO_PinSource* 、EXTI*_IRQn 、EXTI*_IRQHandler の * は同じ種別番号です。5.3.3 プログラム
全てmain.cに記載しました。割り込みの㉑㉒㉓は 割り込みプログラムを記載した ch32v20x_it.c にまとめて記載した方が良いかも?
5.3.4 割り込みプログラム説明
プログラムの①〜⑦については、2.I/Oの設定 2.2記述方法で説明しているので割愛する。
※ただし③で PB2を出力に追加しているが、これは最後の※注意!で出た異常状態を見るために追加した。
割り込み処理することによりスイッチと同じ動作をすることが可能になった。

以下プログラムは一部(関係するところ)を抜粋している
GPIext_InitStructure.GPIO_Pin = GPIO_Pin_*;
GPIO_EXTILineConfig (GPIO_PortSourceGPIOB, GPIO_PinSource*);
EXTI_InitStructure.EXTI_Line = EXTI_Line*;
NVIC_InitStructure.NVIC_IRQChannel = EXTI*x_IRQn;
void EXTI*_IRQHandler (void)
// 上記プログラム中の * 及び *x を 0 以外にすると割り込み処理が出来るようになるが
// 特に*x を0以外にすると main プログラムで下記異常がでる。
int main (void) {
GPIO_INIT();
while (1) {
GPIOA->BSHR |= 0x00000001 ; //A
GPIOA->BSHR |= 0x00000004 ; //B← B,Cが動作しない
GPIOA->BSHR |= 0x00010000 ; //C
} //←ただしWhileを含めコメントアウトするとプログラムは終了してしまう
// ので While がある場合は Bで止まっているのでは無いようだ。
}
※ただし③で PB2を出力に追加しているが、これは最後の※注意!で出た異常状態を見るために追加した。
割り込み処理することによりスイッチと同じ動作をすることが可能になった。

※注意! 調査必要
割り込みは現在 pin 0 以外は異常動作している。以下プログラムは一部(関係するところ)を抜粋している
GPIext_InitStructure.GPIO_Pin = GPIO_Pin_*;
GPIO_EXTILineConfig (GPIO_PortSourceGPIOB, GPIO_PinSource*);
EXTI_InitStructure.EXTI_Line = EXTI_Line*;
NVIC_InitStructure.NVIC_IRQChannel = EXTI*x_IRQn;
void EXTI*_IRQHandler (void)
// 上記プログラム中の * 及び *x を 0 以外にすると割り込み処理が出来るようになるが
// 特に*x を0以外にすると main プログラムで下記異常がでる。
int main (void) {
GPIO_INIT();
while (1) {
GPIOA->BSHR |= 0x00000001 ; //A
GPIOA->BSHR |= 0x00000004 ; //B← B,Cが動作しない
GPIOA->BSHR |= 0x00010000 ; //C
} //←ただしWhileを含めコメントアウトするとプログラムは終了してしまう
// ので While がある場合は Bで止まっているのでは無いようだ。
}
5.3.5 割り込み優先順位説明
1.NVIC_IRQChannelPreemptionPriority
2.NVIC_IRQChannelSubPriority
3.具体的な設定値
4.実装時の注意点
5.例
Preemption (横取り):Sub (同一優先度内順位)
役割: 実行中の割り込みを「中断させて」割り込むことができる優先度です。
動作: この値が小さい(優先度が高い)割り込みが発生すると、現在処理中の優先度が低い割り込みを一時停止し、即座に新しい割り込み処理を開始します(割り込みネスト)。
CH32V003の制約: ハードウェアの制限により、割り込みネストは最大 2レベル までです。
動作: この値が小さい(優先度が高い)割り込みが発生すると、現在処理中の優先度が低い割り込みを一時停止し、即座に新しい割り込み処理を開始します(割り込みネスト)。
CH32V003の制約: ハードウェアの制限により、割り込みネストは最大 2レベル までです。
2.NVIC_IRQChannelSubPriority
役割: 同じ「先取り優先度」を持つ複数の割り込みが「同時に」発生した際の実行順序を決めます。
動作: すでに実行中の割り込みを中断させることはできません。あくまで待ち状態の割り込みの中での優先順位です
動作: すでに実行中の割り込みを中断させることはできません。あくまで待ち状態の割り込みの中での優先順位です
3.具体的な設定値
| 項目 | 値 | 備考 |
|---|---|---|
| Preemption | 0 ~ 1 | 0が最高優先、1が最低優先 |
| Sub | 0 ~ 1 | 同上 |
4.実装時の注意点
割り込みを設定する前に、2ビットの優先度領域をどのように「先取り」と「サブ」に振り分けるかを NVIC_PriorityGroupConfig() で指定する必要があります。
(1) NVIC_PriorityGroup_1
(1) NVIC_PriorityGroup_1
先取り優先度(0-1)、サブ優先度(0-1) に1ビットずつ割り当てます(一般的)。
(2) NVIC_PriorityGroup_2
すべてを先取り優先度(0-3)に割り当てます(サブ優先度は使わない)。
※CH32V003には NVIC_PriorityGroup_2 はありません。
5.例
| 割り込み | Preemption | Sub | 理由 |
|---|---|---|---|
TIM1 | 0 | 0 | 最も高い。PWM生成や高速サンプリングなど、厳密な同期が必要なため。 |
USART1 Rx | 0 | 1 | 受信漏れを防ぐため高めに設定。 USART1のボーレートが非常に高い(115200bps以上など)場合は、優先度を上げることを検討。 |
TIM2 | 1 | 0 | 汎用タイマー処理。USART受信よりは許容度がある場合が多い。 |
USART1 Tx(DMA) | 1 | 1 | 送信は多少遅延してもデータの整合性に影響しないため、優先度を下げます。 |
