基本事項を説明し、LEDを点滅させたら、より複雑なタスクの実装に進むことができます。 幸いなことに、microCコンパイラには、特に初心者のプログラマーの生活を大幅に簡素化する多くの便利な機能があります。
最後の部分では、たとえば、MKポートに接続されたボタンをポーリングするために、MKポートのステータスのポーリングを実装する方法について説明しました。 一般に、STM32 MKのポートは非常に複雑です。 マニュアルのGPIOポート図は次のとおりです。
ポートピンは、デジタルポートとして直接いくつかのモードで動作でき、さらに、多くのピンをADC入力、PWM変調器出力、外部割り込み入力、ハードウェアシリアルSPIおよびI2Cインターフェイスとして使用できます。 (たとえば、PA6はポートAの6番目のピン、6番目のADC入力、3番目のPWM変調器/タイマーの1番目のチャネル出力、および1番目のSPIインターフェースのMISO信号です)。
MicroCには、高度なGPIO構成の機能があります
GPIO_Set_Pin_Mode(&port_base, pin, config);
- &port_base -GPIOポートへのポインター( ポートEの場合は&GPIOE_BASE、またはポートAの場合は&GPIO_BASEなど )。
- pin-作業しているポートのピン( 5ピンの場合は_GPIO_PIN_5、ポートのすべてのピンの場合は_GPIO_PIN_ALLなど )。
- config-ポート操作モード( |演算子を介していくつかのパラメーターを書き込みます );
- _GPIO_CFG_MODE_INPUT-入力用に構成。
- _GPIO_CFG_PULL_UP-プルアップ抵抗がオンになり+ Vcc。
- _GPIO_CFG_PULL_DOWN -GNDのプルアップ抵抗はオンです;。
- _GPIO_CFG_PULL_NO-プルアップ抵抗とプルアップ抵抗は無効です。
- _GPIO_CFG_MODE_ALT_FUNCTION-出力は、統合された周辺モジュール(たとえば、UARTまたはSPI)の入力/出力として使用されます。
- _GPIO_CFG_MODE_ANALOG-出力は、ADC入力として使用されます(内蔵DACを備えたマイクロコントローラーでは、DACの出力として)。
- _GPIO_CFG_MODE_OUTPUT-出力用に構成されています。
- _GPIO_CFG_OTYPE_OD- 「オープンドレイン」動作モードがオンになっています。
- _GPIO_CFG_OTYPE_PP- 「プッシュプル」動作モードが有効になっています(「プッシュプル出力」、最も頻繁に使用されます)。
- _GPIO_CFG_SPEED_MAX -GPIO、フルスピードで動作するための最大速度。APBは400KHZ、2MHZ、10MHZ、40MHZ、50MHZ、100MHZを使用できます。
- _GPIO_CFG_MODE_INPUT-入力用に構成。
最後の部分では、ポート出力を入力に構成しました
GPIO_Digital_Input (&GPIOb_BASE, _GPIO_PINMASK_8);
別の方法で、これを行うことができます:
GPIO_Set_Pin_Mode(&GPIOb_BASE, _GPIO_PINMASK_8, _GPIO_CFG_MODE_INPUT | _GPIO_CFG_PULL_NO);
同じですが、パワーリフトをオンにします。
GPIO_Set_Pin_Mode(&GPIOb_BASE, _GPIO_PINMASK_8, _GPIO_CFG_MODE_INPUT | _GPIO_CFG_PULL_UP);
デフォルトでは、入力ポート( GPIO_Digital_Input() )を初期化するmicroCはプルアップ抵抗をMKピンに接続しません。そのため、必要な場合は、追加のパラメーターでGPIO_Set_Pin_Mode()を使用します。
最後の部分では、MKを使用して、ボタンが接続されている入力ステータスをポーリングする方法を既に説明しました。 実際には、ボタンを使用する場合、接触の跳ね返り(Webの広大な領域で読むことができます)からの保護を導入することが非常に望ましいです。 一言で言えば、これはランダムなパルスの単一パケットではなく、機械的接触の発行です。 MKはそれらを読み取って、正しく処理できません。 チャター(たとえば、ボタンまたはシュミットトリガーと並列の静電容量)に対するハードウェア保護方法と、非常に多くの方法の両方があります。 連絡先のチャタリングを抑制するmicroCには特別な機能があります
Button(&port_base, pin, delay, state)
* port_baseをこの関数に渡します(たとえば、&GPIOb_BASE)。 pin -MKポートのピン番号。 遅延 -時間、バウンスと見なされるより短い持続時間のパルス(たとえば1ms)および状態 -アクティブ状態(関数はポート入力の高レベルまたは低レベルを制御します)。
ボタンのガタガタに対するソフトウェア保護を導入することにより、プログラムを最初の部分からわずかに変更します。
void main() { GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_1); GPIO_Digital_Input(&GPIOb_BASE, _GPIO_PINMASK_8); // PB8 GPIOb_ODR.b1 = 0; while(1) { if (Button(&GPIOb_IDR, 8, 1, 1)) // PB(GPIOb), №8, 1 , ( ) - . 1 1 , - , { GPIOb_ODR.b1=~GPIOb_ODR.b1; Delay_ms(500); // 500 } else { GPIOb_ODR.b1 = 0; // , } } }
ボタンを押すと、LEDが点滅します。 では、最初に押されたときにLEDを点滅させ、次に停止するときにLEDを点滅させます。
unsigned short state = 0; // , , 2 (2 - 1 - .) void main() { GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_1); GPIO_Digital_Input(&GPIOb_BASE, _GPIO_PINMASK_8); GPIOb_ODR.b1 = 0; while(1) { if (Button(&GPIOb_IDR, 8, 1, 1)) // state.b2=1; // if (state.b2 && Button(&GPIOb_IDR, 8, 1, 0)) // , , / { state.b2 = 0; // state.b1= ~state.b1; // } if (state.b1) // { GPIOb_ODR.b1=~GPIOb_ODR.b1; Delay_ms(250); } else { GPIOb_ODR.b1 = 0; } Delay_ms(1); } }
さて、ボタンが押されると、LEDが点滅し始め、次に停止します(どのような場合でも)常にそうなるとは限りません...理由は、 Delay_ms(250)は本質的にMKカーネルによる空のサイクルのソートの250 msであるためです 現時点では、彼はこれで忙しく、外部からの影響を完全に無視しています(もちろん、リセットと電源オフを除き、ユーモアのジョーク:))
時間間隔の形成でマイクロコントローラの貴重なクロックを使用しないように、タイマーと呼ばれる特別なブロックがあります。 STM32の数は14に達します。設定はわずかに異なり、期間のカウントだけでなく、たとえばPWMの生成にも使用できます。 実際、各タイマーはTIMx_CNTレジスタ(STM 32の場合は16ビット)であり、一定量のMKクロック(より正確には、接続先のAPB1バス( タイマーTimer1-APB2 ))によって発生します。 タイマーレジスタの値を1増加させるためにAPBサイクルが何回通過する必要があるかは、クロックバス周波数が分割される16ビット値のプリセレクターのレジスタTIMx_PSCを示します。
STM32F103には7つのタイマーがあり、
- TIM1-高度な制御タイマー 、高度なパラメーター設定を可能にするタイマー(たとえば、ドライブで使用される周波数コンバーターの動作に必要なパルスの形成)。 将来的には、記事でそれに戻ります。 TIM1には4チャネルのPWM変調器があります。
- TIM2、TIM3、TIM4-汎用タイマー - 汎用タイマー 。 最もよく使用されるのは、TIM2、TIM3、TIM4に4チャネルのPWM変調器がある場合です。
- WDT-Wachdogタイマー-MKの凍結を防ぐために使用されるタイマー。
- SysTickタイマー -24ビットMKカーネルクロックカウンター、たとえば、カーネルの負荷を推定するために使用されます。
タイマーのセットアップには多くのレジスタが使用されます(タイマーのパラメーターも非常に優れています)。 特定の問題を解決するときに必要なパラメーターを参照します。
各タイマーのメインレジスタ:
- TIMx_CNT-実際にカウントしているレジスタ。
- TIMx_PSC-周波数スプリッター、スプリッターファクターはTIMx_PSC + 1です (状況に陥らないように、これを忘れないでください:「どのように動作しますか?」:));
- TIMx_ARR-カウントレジスタの値。これに達すると、タイマーがリセットされ、割り込みが生成されます(以下を参照)。
- TIMx_CR1-タイマーパラメーターのレジスタ。
- TIM_DIER-チンキの中断とDMAタイマーを登録します。
- TIM_SR-タイマーステータスレジスタ。たとえば、割り込みがトリガーされたことを示すフラグがあります。
タイマーがリセットされ、有効になっている場合、プログラムによって割り込みが生成されます。 割り込みは、特定のイベントに対するプログラムコードの特定のセクションへの呼び出しです。 割り込みソースは、ビルトインMKブロック、割り込み(int)を作成する機能を持つ入力、およびDMAブロックにすることができます。 実際、割り込みが到着すると、MKはメインコードの実行から特定のサブルーチンの実行に切り替わり、その後メインコードに戻ります(1つの割り込みの処理中に別の割り込みが発生したときの特別な話ですが、まだこれに焦点を合わせません)。 STM32には70を超えるいわゆる割り込みベクター(サブプログラムの実行をトリガーするイベント)-割り込みハンドラーがあります。 デフォルトでは、すべての割り込みが無効になっています。 1つまたは別のブロックからの割り込みを有効にするには、1つまたは別のMKブロックの制御レジスタの構成ビットを変更する必要があります。 MicroCはコマンドを使用して割り込みを有効にします
NVIC_IntEnable(NVIC_name) //NVIC_name -
一般に、microCには「コードアシスタント」と呼ばれる非常に便利な機能があります。 Ctrl + Spaseのキーの組み合わせを押すと呼び出されます。 パラメータの正確な名前を覚えていない場合に役立ちます。 初期文字を入力すると、コンパイラーは、可能な値から選択してコマンドを完了することを提案します。 たとえば、「NVIC IntEnable(NVIC "b」と入力して「 コードアシスタント」を呼び出すと、次のようになります。
制御レジスタのパラメータに似ています。 たとえば、「 TIM4_CR1bits。 」と入力すると、次のようになります。
気配りのある読者は、レジスタの別のビットにアクセスするために、たとえばREGx.by型の式を使用していることに気付きました。
GPIOb_ODR.b1=0; \\1 GPIOb_ODR TIM4_CR1.CEN \\ CEN TIM4_CR1
同じアクションを記録する別の形式があり、 「コードアシスタント」は通常それでのみ動作します
GPIOb_ODRbits.ODR1=1; \\1 GPIOb_ODR TIM4_CR1bits.CEN \\ CEN TIM4_CR1
タイマーを構成するには、次の手順を実行します。
RCC_APB1ENR.TIM2EN = 1; // APB1 (1/2 , 72/2=36 ) 2, TIM2_CR1.CEN = 0; // 2, TIM2_PSC = 7199; // , 7199+1 = 7200, 72 10 K TIM2_ARR = 5000; // NVIC_IntEnable(IVT_INT_TIM2); // TIM2_DIER.UIE = 1; // TIM2_CR1.CEN = 1; // 2
* 重要なお知らせ。 TIM2、TIM3、TIM4がAPB1バスに接続されているという事実にもかかわらず、このケースでは半分の周波数(APB1の最大周波数は36 MHz)HCLKで動作しますが、APB1分周器をインストールするときのタイマーは1以外のバス周波数の2倍でクロックされます。周波数APB1 = HCLK / 2でTIMxCLK = APB1 x 2、実際、この場合、コア周波数。
500ミリ秒ごとに割り込みを生成するようにタイマーを設定します。
ここで、タイマー割り込みが到着したときに呼び出される関数を作成します。
void Timer2_interrupt() iv IVT_INT_TIM2 { TIM2_SR.UIF = 0; // , !!! // }
これですべてがLEDで点滅する準備ができました。次にタイマーを使用します
unsigned short state = 0; void main() { GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_1); GPIO_Digital_Input(&GPIOb_BASE, _GPIO_PINMASK_8); RCC_APB1ENR.TIM2EN = 1; TIM2_CR1.CEN = 0; TIM2_PSC = 7199; TIM2_ARR = 5000; NVIC_IntEnable(IVT_INT_TIM2); TIM2_DIER.UIE = 1; TIM2_CR1.CEN = 1; while(1) { } } void Timer2_interrupt() iv IVT_INT_TIM2 { TIM2_SR.UIF = 0; GPIOb_ODR.b1=~GPIOb_ODR.b1; }
ボタンを使用してタイマーのオンとオフを切り替えましょう。ボタンは正常に機能します。
unsigned short state = 0; void main() { GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_1); GPIO_Digital_Input(&GPIOb_BASE, _GPIO_PINMASK_8); RCC_APB1ENR.TIM2EN = 1; TIM2_CR1.CEN = 0; TIM2_PSC = 7199; TIM2_ARR = 5000; NVIC_IntEnable(IVT_INT_TIM2); TIM2_DIER.UIE = 1; while(1) { if (Button(&GPIOb_IDR, 8, 1, 1)) state.b2=1; if (state.b2 && Button(&GPIOb_IDR, 8, 1, 0)) { state.b2 = 0; state.b1= ~state.b1; } if (state.b1) { TIM2_CR1.CEN = 1; // } else { TIM2_CR1.CEN = 0; // GPIOb_ODR.b1 = 0; // } Delay_ms(1); } } void Timer2_interrupt() iv IVT_INT_TIM2 { TIM2_SR.UIF = 0; GPIOb_ODR.b1=~GPIOb_ODR.b1; }
* 継続する