C言語

タクタルスイッチでLED点灯②(応用編)

スポンサーリンク

こんにちは!駆け出しエンジニアのまっきーです。

引き続き、タクタル(タクト)スイッチ操作の復習と応用問題に取り組みたいと思います。

前回の記事はこちらです。

新たに、割り込み処理を実装するので、そこは詳しく解説します!

使用言語はC言語です。

課題一覧

  1. タクタルスイッチでLEDを点滅させる(その1)
  2. タクタルスイッチでLEDを点滅させる(その2)

電子回路

回路図は前回と全く同じです

スイッチは右からSW0~SW7

LEDは右からLED0~LED3とします

以下、GPIOの対応表です

スイッチ,LED番号GPIO
LED0GPIO23
LED1GPIO24
LED2GPIO20
LED3GPIO21
SW0GPIO25
SW1GPIO17
SW2GPIO27
SW3GPIO5
SW4GPIO6
SW5GPIO13
SW6GPIO19
SW7GPIO26

タクタルスイッチでLEDを点滅させる(その1)

SW0 を押すと LED0 が 1 秒間隔で点滅し、SW1 を押すと LED0 の点滅が一時停止し、SW0 を押すと再スタートするプログラムを、割込み処理を使わないで作成します。

ソースコード

#include <stdio.h>
#include <wiringPi.h>

int led[4] = {23,24,20,21};//LED0,LED1,LED2,LED3
int sw[8] = {25,17,27,5,6,13,19,26};//SW0~SW7

int main(void){
	int i;
	//wiringPi初期化処理(エラー対策込み)
	if(wiringPiSetupGpio() == -1) return 1;

	//led,swの初期設定 swはプルアップ抵抗を使用
	for(i = 0;i < 4;i++){
		pinMode(led[i],OUTPUT);}
	for(i = 0;i < 8;i++){
		pinMode(sw[i],INPUT);
		pullUpDnControl(sw[i],PUD_UP);
	}
	while(1){
		if(digitalRead(sw[0]==LOW)){
            while(1){
                if(digitalRead(sw[1])==LOW) break;
                digitalWrite(led[0],HIGH);
                delay(1000);
                digitalWrite(led[0],LOW);
                delay(1000);
            }
        }
	}
	return 0;
}

これを実行してみると、

なぜか最初から点灯してしまいます。SW1を押して止まるところはいいのですが、、、

一旦著者のソースコードを見てみます。(引用元

著者は正論理(スイッチオンでHIGH)なので注意してください。

#include <stdio.h>          //入出力
#include <stdlib.h>         //一般ユーティリティ
#include <wiringPi.h>       //wiringPi

const int ledGpio[4] ={23,22,25,24};            //LED GPIOを配列で定義
const int swGpio[8] = {4,5,6,26,17,27,20,21};   //SW GPIOを配列で定義

int main (void){
    int i;
    wiringPiSetupGpio();        //BCMのGPIO番号を使用
    for(i=0;i<4;i++){           //LED0-LED3を出力に設定
        pinMode(ledGpio[i], OUTPUT);}
    for(i=0;i<8;i++){           //SW0-SW7を入力に設定
        pinMode(swGpio[i], INPUT);}
    for(i=0;i<8;i++){           //SW0-SW7をプルダウン抵抗をつける
        pullUpDnControl(swGpio[i],PUD_DOWN);}
 
    for(;;){                    //永久ループ
        if(digitalRead(swGpio[0])==HIGH){ //SW0がHならLED0を点滅させます
            while(1){
                digitalWrite(ledGpio[0],HIGH);
                delay(1000);
                digitalWrite(ledGpio[0],LOW);
                delay(1000);
                if(digitalRead(swGpio[1])==HIGH){ //SW1がHならwhileループ文から抜けます
                    break;}
            }
        }
    }
    return EXIT_SUCCESS;
}

私のコードとあまり変わったところはありません。

実際にこのソースコードで試してみても、最初から点灯されてしまいます。

原因は何なのでしょうか…

ハード側の問題かとも思い、スイッチを変えてみたりしたのですが、解決しませんでした。

放置して次の問題に進んでしまいます。

タクタルスイッチでLEDを点滅させる(その2)

先ほど のプログラムでは、SW1 を長押ししないと一時停止しない欠点がありました。割込み処理を使用して、SW1を長押ししなくても一時停止するプログラムに改良します。

割り込み処理という新しい言葉が出てきました。割り込み処理について説明します。

割り込み処理の実装

割り込み処理とは

割り込み処理は,CPUのハードウェア機能として実現され,通常のプログラムの実行を強制的に中断し,別のプログラム処理を割り込ませることができる. 割り込みした処理が終了した後は,何もなかったかのように元のプログラムをそのまま続行する.(こちらのサイトから引用)

私たちの世界でも割り込みはよく行われています。例えば、

Aさんが仕事をしている最中に電話がかかってきました。
お客さんからのクレームです。
Aさんはクレーム電話に対応しています。
やっと電話が終わり、元の作業に戻ろうとしてAさんは思いました。
「あれ、さっき何をやってたんだっけ?」
そういえば電話に出る直前にメモを残していました。
メモを見て思い出したAさんは仕事を再開しました。

上の例でいうと、電話に出て、クレーム対応をする。が割り込みにあたる部分です。

何となくイメージしていただけたでしょうか。

では、実際にラズパイで実装する際はどうなるか説明します。

割り込み処理の実装方法

wiringPiSR(GPIO番号,エッジタイプ,割り込み処理関数)

・エッジタイプは3種類あります

INT_EDGE_FALLING立ち下がりエッジを検出
INT_EDGE_RISING立ち上がりエッジを検出
INT_EDGE_BOTH両方のエッジを検出

立ち上がりはLOW→HIGH

立下りはHIGH→LOWになるタイミングのことです

・割り込み処理関数

(void*)function(void) 注void*は割り込み処理関数のアドレスを指します

上記のように関数を定義します。ポインタが使われていますが、そんなに心配しなくても大丈夫です

実際のプログラムを見た方がわかりやすいと思うので、ソースコードを見てください

ソースコード

#include <stdio.h>
#include <wiringPi.h>

int led[4] = {23,24,20,21};//LED0,LED1,LED2,LED3
int sw[8] = {25,17,27,5,6,13,19,26};//SW0~SW7
int mode;

void sw_start(void);
void sw_stop(void);

int main(void){
	int i;
	//wiringPi初期化処理(エラー対策込み)
	if(wiringPiSetupGpio() == -1) return 1;

	//led,swの初期設定 swはプルアップ抵抗を使用
	for(i = 0;i < 4;i++){
		pinMode(led[i],OUTPUT);}
	for(i = 0;i < 8;i++){
		pinMode(sw[i],INPUT);
		pullUpDnControl(sw[i],PUD_UP);
	}
	//割り込み処理
	wiringPiISR(sw[0],INT_EDGE_FALLING,(void*) sw_start);
	wiringPiISR(sw[1],INT_EDGE_FALLING,(void*) sw_stop);
    	
	while(1){
		if(mode == HIGH){
		digitalWrite(led[0],HIGH);
		delay(500);
		digitalWrite(led[0],LOW);
		delay(500);
	}
}       
	
	return 0;
}

void sw_start(void){
		mode = HIGH;
}
void sw_stop(void){
	mode = LOW;
}

最後の方にあるsw_start(),sw_stop()の二つが割り込み関数になります。

割り込み関数で負荷が大きいと、作業効率の悪いプログラムになる可能性があるそうです。

なのでできるだけ長い処理時間を要するプログラムはmain関数に任せ、割り込み関数はシンプルに記述することを心がけましょう!

まとめ・反省

wiringPiSR(GPIO番号,エッジタイプ,割り込み処理関数)と割り込み関数を用意することで割り込み処理を行うことができる。

・割り込み処理関数の中身は、できるだけシンプルに書く

初めの課題の、最初に点滅が始まってしまう問題はまだわかっていません。もしわかる方がいらっしゃいましたら教えて頂きたいです。

原因を突き止めることはコードを記述するよりも大切なことだと思うので、粘り強くやっていきたいと思います。

今回はこれで終了です。ありがとうございました。

スポンサーリンク

-C言語