ユーザスイッチによる2つの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の点灯・消灯を行うことを考える。

STM32F0Discoveryには,ユーザスイッチと2つのプログラムで点滅可能なLEDが搭載されており,基本的なディジタルIOを試せるようになっている。
STM32F0Discoveryの青いユーザスイッチはGPIOのPA0ピンに接続されており,内部的にはGPIO Port A bit 0に対応している。
LEDは2つ有り,青と緑のLEDである。これらはそれぞれ,GPIOのPC8,PC9(GPIO Port C bit 8,GPIO Port C bit 9)に接続されている。(ユーザマニュアルDM00050135.pdf(UM1525) Figure 16参照)
(GPIOとは General-purpose Input/Output のこと)


青のLED(左下),青のユーザスイッチは画面左側 緑のLED(画面左上)




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

ユーザースイッチとLED (ユーザマニュアルDM00050135.pdf(UM1525) Figure 16 より引用)

自分でスイッチを付ける場合は,この回路が参考になる。
スイッチのところにはコンデンサが実装されていない。

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

LED電圧降下
Vled [V]
抵抗での電圧降下
Vr = 2.93V-Vled [V]
保護抵抗
R [Ω]
LEDを流れる電流
= ポート出力電流 [mA]
LED green
1.90
1.01
330
3.1
LED blue
2.65
0.27
660
0.4
LED red
1.76
1.17
1000
1.2

ポート出力電流は青色LEDが0.4mA,緑色LEDが3.1mAであった。




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

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




ここで制作するプログラムの仕様
  ユーザスイッチを押すと青LEDのみが点灯し,離すと緑LEDが点灯する。


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

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

(2)GPIOの各ピンは入力にも出力にも使うことが出来,どちらに使うかは,プログラムの冒頭で指定しておく必要がある。
GPIOのPortA_Bit0を入力に設定(GPIOA_MODERのbit0,1を00)
GPIOのPortC_Bit8を出力に設定(GPIOC_MODERのbit16,17を01)
GPIOのPortC_Bit9を出力に設定(GPIOC_MODERのbit18,19を01)
(リファレンスマニュアル DM00031936.pdf(RM0091) 9.1 9.4.1 参照)

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


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

どれを使うかは,プログラミングをCPUのどの切り口で考えるかによる。

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


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

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

main.c
/* Includes ------------------------------------------------------------------*/
#include <stm32f0xx.h>
#include <stdint.h>
#include "stm32f0xx_conf.h"
#include "stm32f0_discovery.h"
#include <stdio.h>

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void init_Clocks(void);
void init_GPIO(void);
int checkPortA(int32_t bit);
void setPortC(int32_t bit);
void resetPortC(int32_t bit);
/* Private functions ---------------------------------------------------------*/

int main(void)
{
    init_Clocks();
    init_GPIO();
    while(1) {
        if (checkPortA(0)) {  //if user's button pushed
            setPortC(8); //Turn GPIOC Pin 8 On
            resetPortC(9); //Turn GPIOC Pin 9 Off
        } else {
            resetPortC(8); //Turn GPIOC Pin 8 Off
            setPortC(9); //Turn GPIOC Pin 9 On
        }
    }
}

void init_Clocks(void)
{
    RCC ->AHBENR |= (1<<17);   //Enable CLK for Port A
    RCC ->AHBENR |= (1<<19);   //Enable CLK for Port C
}

void init_GPIO(void)
{
    GPIOA -> MODER &= ~3;      //Enable Port A Pin0 as a digital input
    GPIOC -> MODER &= ~(3<<16);
    GPIOC -> MODER |= (1<<16); //Enable Port C Pin8 as a digital output
    GPIOC -> MODER &= ~(3<<18);
    GPIOC -> MODER |= (1<<18); //Enable Port C Pin9 as a digital output
}

//PortAの指定したビットを検査し,そのビットのみ返す。 
int checkPortA(int32_t bit)
{
    return ((GPIOA ->IDR)&(1<<bit));
}

//PortCの指定したビットを1にする
void setPortC(int32_t bit)
{
    GPIOC ->ODR |= (1<<bit);
}

//PortCの指定したビットを0にする
void resetPortC(int32_t bit)
{
    GPIOC ->ODR &= ~(1<<bit);
}



5.STM32F0xx_StdPeriph_Driver関数利用

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

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

main.c
/* Includes ------------------------------------------------------------------*/
#include <stm32f0xx.h>
#include <stdint.h>
#include "stm32f0xx_conf.h"
#include "stm32f0_discovery.h"
#include <stdio.h>

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void init_Clocks(void);
void init_GPIO(void);
/* Private functions ---------------------------------------------------------*/

int main(void)
{
    init_Clocks();
    init_GPIO();
    while(1) {
        if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)) {  //if user's button pushed
            GPIO_SetBits(GPIOC,GPIO_Pin_8); //Turn GPIOC Pin 8 On
            GPIO_ResetBits(GPIOC,GPIO_Pin_9); //Turn GPIOC Pin 9 Off
        } else {
            GPIO_ResetBits(GPIOC,GPIO_Pin_8); //Turn GPIOC Pin 8 Off
            GPIO_SetBits(GPIOC,GPIO_Pin_9); //Turn GPIOC Pin 9 On
        }
    }
}

//
GPIOA,GPIOCへクロックの供給
void init_Clocks(void)
{
    /* GPIOA,GPIOC Periph clock enable */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOC, ENABLE);
}

void init_GPIO(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
//GPIO Pprtを設定するための構造体
     /* 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); //上記構造体をGPIO Pprt Aに設定

    /* Configure PC8 and PC9 in output pushpull mode */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
    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(GPIOC, &GPIO_InitStructure);
//上記構造体をGPIO Pprt Cに設定
}

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

GPIO_Init でGPIOを初期化するには,構造体GPIO_InitTypeDefを使うことになる。
そうすると構造体要素をすべて設定しなければならないため,stm32f0xx_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   プルダウン

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




GPIO IOピンの構造        

6.STM32F0Discovery専用関数利用

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

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

main.c
#include <stm32f0xx.h>
#include <stdint.h>
#include "stm32f0xx_conf.h"
#include "stm32f0_discovery.h"
#include <stdio.h>

int main(void)
{
    /* Configure LED3 and LED4 on STM32F0-Discovery */
    STM_EVAL_LEDInit(LED3);
    STM_EVAL_LEDInit(LED4);
    /* Initialize User_Button on STM32F0-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_LEDOff(LED3); //Turn LED3 Off
        } else {
            STM_EVAL_LEDOn(LED3); //Turn LED3 On
            STM_EVAL_LEDOff(LED4); //Turn LED4 Off
        }
    }
}