ユーザスイッチによるLEDの点灯消灯

20140530 coskx
T.SATOH

1.はじめに

マイコンで,基本的な入力は,ディジタル電気信号(0Vまたは電源電圧の2値)を, 変数の1ビットの値(0または1)として取り込むことである。また,基本的な出力は,変数の1ビットの値(0または1)をディジタル電気信号(0Vまたは 電源電圧の2値)として出力することである。この2つの動作ができれば,コンピュータは外界の状態を取り込み,また外界への働きかけができる。

ここでは基本的な入力として,スイッチの状態(ON-OFF)を電気回路上でディジタル電気信号(0Vまたは電源電圧の2値)に変換し,それを変数の1 ビットの値(0または1)として取り込む方法について考える。また,変数の1ビットの値(0または1)をディジタル電気信号(0Vまたは電源電圧の2値)として出力し,LEDの点灯・消灯を行うことを考える。

STM32F3Discoveryには,ユーザスイッチと8個のプログラムで点滅可能なLEDが搭載されており,基本的なディジタルIOを試せるようになっている。
STM32F3Discoveryの青いユーザスイッチはGPIOのPA0ピンに接続されており,内部的にはGPIO Port A bit 0に対応している。
LEDは8個ある。これらはそれぞれ,GPIOのPE8,PE9,・・・,PE15(GPIO Port E bit 8からGPIO Port E bit 15)に接続されている。(ユーザマニュアルDM00063382.pdf(UM1570) Figure 14参照)
(GPIOとは General-purpose Input/Output のこと)


8個のLED,青のユーザスイッチは画面左側




スイッチがOFFの時は,PA0に0Vが
出力される。
スイッチがONの時には,PA0にVDD
(3V)が出力される。

ユーザースイッチとLED (ユーザマニュアルDM00063382.pdf(UM1570) Figure 14 より引用)

自分でスイッチを付ける場合は,この回路が参考になる。


LED点灯中に,LED両端の電圧を測定してみた。

LED電圧降下
Vled [V]
抵抗での電圧降下
Vr = 2.89V-Vled [V]
保護抵抗
R [Ω]
LEDを流れる電流
= ポート出力電流 [mA]
LED green
1.88
1.01
510
2.0
LED blue
2.62
0.27
680
0.4
LED red
1.77
1.12
680
1.6
LED orange
1.86
1.03
680
1.5

ポート出力電流は緑色LEDが2.0mA,青色LEDが0.4mA,
赤色LEDが1.6mA,橙色LEDが1.5mAであった。




参考1 ポート出力電流
ポート出力電流については DM00058181.pdf の 6.3.14 I/O port characteristics の Output driving current に 通常GPIOは±8mA/ピンまで使えるとなっている。ただし,6.2にある最大値はこえるなと書いてあり,6.2のtable20 . Current characteristicsには各ピンは±25mA(GPIOの方が厳しい規格)までで,各ピン合計で±80mAまでとなっている。

参考2 ポート入力電圧
DM00058181.pdf の Table 22. General operating conditions  によれば,GPIOピンへの入力電圧は,通常は-0.3Vから電源電圧+0.3Vまでが許される。 Table 11. Pin definitions 中にFTと書いてあるのは「5 V tolerant I/O」でTTLレベルを直接入力可能なピンである。






ここで制作するプログラムの仕様
  ユーザスイッチを押すとLED4,7,9,6が点灯し,離すとLED3,5,10,8が点灯する。


2.プログラムが行うべき作業内容

(1)GPIOのPort AとPort Eを使うため,これらにクロックを供給しなければならない。
GPIOのPortAのクロックを有効にする(RCC_AHBENRのbit17と21を1にする)
(リファレンスマニュアル
DM00043574.pdf(RM0316) 8.4.6 参照)

(2)GPIOの各ピンは入力にも出力にも使うことが出来,どちらに使うかは,プログラムの冒頭で指定しておく必要がある。
GPIOのPortA_Bit0を入力に設定(GPIOA_MODERのbit0,1を00)
GPIOのPortE_Bit8を出力に設定(GPIOE_MODERのbit16,17を01)
GPIOのPortE_Bit9を出力に設定(GPIOE_MODERのbit18,19を01)
GPIOのPortE_Bit10を出力に設定(GPIOE_MODERのbit20,21を01)
GPIOのPortE_Bit11を出力に設定(GPIOE_MODERのbit22,23を01)
GPIOのPortE_Bit12を出力に設定(GPIOE_MODERのbit24,25を01)
GPIOのPortE_Bit13を出力に設定(GPIOE_MODERのbit26,27を01)
GPIOのPortE_Bit14を出力に設定(GPIOE_MODERのbit28,29を01)
GPIOのPortE_Bit15を出力に設定(GPIOE_MODERのbit30,31を01)
(リファレンスマニュアル  DM00043574.pdf(RM0316) 9.4.6 参照)


準備が終わったら,PushSwitchの状態を調べて,LED点灯・消灯作業を行えば良い。


3.実際のプログラミング
同じことをするのに,様々な記述方法があるので,ここではそれらを比較しながら,
3つの方法でプログラムを書いてみる。
どれを使うかは,プログラミングをCPUのどの切り口で考えるかによる。

(1)ポートの設定をリファレンスマニュアル通りにプログラムする。
   勉強のためには必要だが,実用的ではない。
   → 4.リファレンスマニュアル通りのプログラム
(2)ライブラリ(STM32F30x_StdPeriph_Driver関数)を使用する。
   実用的だが,マニュアルが完備していないので,各関数の使い方をプログラムサンプル
   あるいはヘッダーファイルから使い方を学ぶ。
   → 5.STM32F30x_StdPeriph_Driver関数利用
(3)stm32f3discoveryのカード上に配置されているLED,スイッチ類なら,専用関数があるので
   これを利用する。
   汎用性はないが,便利に使えるし,自作関数を作る際の見本にもなる。
   stm32f3discoveryのカード上に配置されているLED,スイッチ類を使う時だけ,これらの関数を使うという考え方もできる。
   → 6.STM32F3Discovery専用関数利用


4.リファレンスマニュアル通りのプログラム

フレームワークプロジェクトを使って,以下のプログラムを行う。
SMSISのファイルstm32f30x.hで定義された構造体を使ってリファレンスマニュアル通りにプログラムすると次のようになる。
Sw_Led.zip

main.c
#include <stm32f30x.h>
#include <stdint.h>
#include "stm32f30x_conf.h"
#include "stm32f3_discovery.h"
#include <stdio.h>

// User Button is checked as Port A Pin0 input
// LEDs are controlled by Port E Pin8-15 output

int main(void)
{
    RCC ->AHBENR |= 0x20000;   //Enable CLK for Port A
    RCC ->AHBENR |= 0x200000;   //Enable CLK for Port E
    GPIOA -> MODER &= ~3;      //Enable Port A Pin0 as a digital input
    GPIOE -> MODER |= 0x55550000; //Enable Port E Pin8-15 as a digital output
    while(1) {
        if (((GPIOA ->IDR)&1) ==1 ) {  //if user's button pushed
            GPIOE ->ODR |= 0x9900; //Turn GPIOE Pin8,11,12,15 On
            GPIOE ->ODR &= ~0x6600; //Turn GPIOE Pin9,10,13,14 Off
        } else {
            GPIOE ->ODR &= ~0x9900; //Turn GPIOE Pin8,11,12,15 Off
            GPIOE ->ODR |= 0x6600; //Turn GPIOE Pin9,10,13,14 On
        }
    }
}



5.STM32F30x_StdPeriph_Driver関数利用

STM32F30x_StdPeriph_Driver関数を利用してプログラムすると,より一般性のあるプログラムができるが,
マイクロコントローラの型番ごとに異なる関数体系なのでマニュアル・資料が追いつかないのが現状のようだ。
しかし,近い型番のマニュアル(例えばSTM32F10x Standard Peripherals Library)を参考に類推し,対応する.c
ファイル,.hファイルを検討する必要がある。

フレームワークプロジェクトを使って,以下のプログラムを行う。
stm32f30x_conf.hがSTM32F30x_StdPeriph_Driver関数に関するすべてのヘッダファイルをインクルードしているので,STM32F30x_StdPeriph_Driver関数はすべて使用可能である。
Sw_Led2.zip

main.c
/* Includes ------------------------------------------------------------------*/
#include <stm32f30x.h>
#include <stdint.h>
#include "stm32f30x_conf.h"
#include "stm32f3_discovery.h"
#include <stdio.h>

/* Private function prototypes -----------------------------------------------*/
void init_Clocks(void);
void init_GPIO(void);

int main(void)
{
    init_Clocks();
    init_GPIO();
    while(1) {
        if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)) {  //if user's button pushed
            GPIO_SetBits(GPIOE,GPIO_Pin_8); //Turn GPIOE Pin 8 On
            GPIO_SetBits(GPIOE,GPIO_Pin_11); //Turn GPIOE Pin 11 On
            GPIO_SetBits(GPIOE,GPIO_Pin_12); //Turn GPIOE Pin 12 On
            GPIO_SetBits(GPIOE,GPIO_Pin_15); //Turn GPIOE Pin 15 On
            GPIO_ResetBits(GPIOE,GPIO_Pin_9); //Turn GPIOE Pin 9 Off
            GPIO_ResetBits(GPIOE,GPIO_Pin_10); //Turn GPIOE Pin 10 Off
            GPIO_ResetBits(GPIOE,GPIO_Pin_13); //Turn GPIOE Pin 13 Off
            GPIO_ResetBits(GPIOE,GPIO_Pin_14); //Turn GPIOE Pin 14 Off
        } else {
            GPIO_ResetBits(GPIOE,GPIO_Pin_8); //Turn GPIOE Pin 8 On
            GPIO_ResetBits(GPIOE,GPIO_Pin_11); //Turn GPIOE Pin 11 On
            GPIO_ResetBits(GPIOE,GPIO_Pin_12); //Turn GPIOE Pin 12 On
            GPIO_ResetBits(GPIOE,GPIO_Pin_15); //Turn GPIOE Pin 15 On
            GPIO_SetBits(GPIOE,GPIO_Pin_9); //Turn GPIOE Pin 9 Off
            GPIO_SetBits(GPIOE,GPIO_Pin_10); //Turn GPIOE Pin 10 Off
            GPIO_SetBits(GPIOE,GPIO_Pin_13); //Turn GPIOE Pin 13 Off
            GPIO_SetBits(GPIOE,GPIO_Pin_14); //Turn GPIOE Pin 14 Off
        }
    }
}

void init_Clocks(void)
{
    /* GPIOA Periph clock enable */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOE, ENABLE);
}

void init_GPIO(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    /* Configure PA0 in input pushpull mode */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    /* Configure PC8 and PC9 in output pushpull mode */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOE, &GPIO_InitStructure);
}


ここに出てきた関数をCoIDEで検索してみると,次のように見つかる。
関数名
定義されているファイル
意味
RCC_AHBPeriphClockCmd stm32f30x_rcc.h
stm32f30x_rcc.c
指定したユニットにクロックを供給する設定
GPIO_Init
GPIO_ReadInputDataBit
GPIO_SetBits
GPIO_ResetBits
stm32f30x_gpio.h
stm32f30x_gpio.c
GPIOを操作する

GPIO_Init でGPIOを初期化するには,構造体GPIO_InitTypeDefを使うことになる。
そうすると構造体要素をすべて設定しなければならないため,stm32f30x_gpio.hを参照しながら意味を考えておく。

GPIO_Pin   GPIOの指定するポートの設定対象のピン番号
GPIO_Mode  そのピンの利用モード モードには次の4つがある
           GPIO_Mode_IN     そのピンを入力として用いる(default)
           GPIO_Mode_OUT    そのピンを出力として用いる
           GPIO_Mode_AF     そのピンを別機能で用いる(例えばPWM出力,USARTの出力)
           GPIO_Mode_AN     そのピンをアナログ入出力として用いる
GPIO_OType そのピンを出力として用いるときに,2つの設定がある
           入力設定の場合には意味なし
           GPIO_OType_PP    プシュプル(1:3V or 0:0Vを出力)(default)
           GPIO_OType_OD    オープンドレイン(1:ハイインピーダンス 0:0V)
GPIO_Speed GPIOを駆動するクロックのスピード 3段階ある
           GPIO_Speed_50MHz,GPIO_Speed_10MHz(default),GPIO_Speed_2MHzが使える
GPIO_PuPd  そのピンが入力設定の時,プルアップ,プルダウン,何もしない
           のうちから1つ選ぶ。そのピンが出力設定の場合やアナログ入出力の場合は
           何もしないを設定する。
           GPIO_PuPd_NOPULL 何もしない(default)
           GPIO_PuPd_UP     プルアップ
           GPIO_PuPd_DOWN   プルダウン

これらの設定はリファレンスマニュアル
 DM00043574.pdf(RM0316) Fig.16 を見て,希望通りに設定すれば良い。




GPIO IOピンの構造 (DM00043574.pdf(RM0316) Fig.16より)       

6.STM32F3Discovery専用関数利用

STM32F3Discovery評価ボード専用関数が stm32f3_discovery.h stm32f3_discovery.c に与えられている。
これらの関数は,STM32F3Discovery評価ボードのプッシュスイッチと8個のLED専用の関数である。
この部分だけは,この関数群を使うと汎用性はないが,可読性よくプログラムを書くことができる。

フレームワークプロジェクトを使って,以下のプログラムを行う。

main.c
/* Includes ------------------------------------------------------------------*/
#include <stm32f30x.h>
#include <stdint.h>
#include "stm32f30x_conf.h"
#include "stm32f3_discovery.h"
#include <stdio.h>

int main()
{
    /* Configure LED3 - LED10 on STM32F3-Discovery */
    STM_EVAL_LEDInit(LED3);
    STM_EVAL_LEDInit(LED4);
    STM_EVAL_LEDInit(LED5);
    STM_EVAL_LEDInit(LED6);
    STM_EVAL_LEDInit(LED7);
    STM_EVAL_LEDInit(LED8);
    STM_EVAL_LEDInit(LED9);
    STM_EVAL_LEDInit(LED10);
    /* Initialize User_Button on STM32F3-Discovery */
    STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_GPIO);

    while(1) {
        if (STM_EVAL_PBGetState(BUTTON_USER)== SET) {  //if user's button pushed
            STM_EVAL_LEDOn(LED4); //Turn LED4 On
            STM_EVAL_LEDOn(LED7); //Turn LED7 On
            STM_EVAL_LEDOn(LED9); //Turn LED9 On
            STM_EVAL_LEDOn(LED6); //Turn LED6 On
            STM_EVAL_LEDOff(LED3); //Turn LED3 Off
            STM_EVAL_LEDOff(LED5); //Turn LED5 Off
            STM_EVAL_LEDOff(LED10); //Turn LED10 Off
            STM_EVAL_LEDOff(LED8); //Turn LED8 Off
        } else {
            STM_EVAL_LEDOn(LED3); //Turn LED3 On
            STM_EVAL_LEDOn(LED5); //Turn LED5 On
            STM_EVAL_LEDOn(LED10); //Turn LED10 On
            STM_EVAL_LEDOn(LED8); //Turn LED8 On
            STM_EVAL_LEDOff(LED4); //Turn LED4 off
            STM_EVAL_LEDOff(LED5); //Turn LED5 off
            STM_EVAL_LEDOff(LED10); //Turn LED10 off
            STM_EVAL_LEDOff(LED8); //Turn LED8 off
        }
    }
}