STM32 ゼロから始めるローパワーマイコン ADCを使ってスイッチを読み分ける 後編

STM32 ゼロから始めるローパワーマイコン ADCを使ってスイッチを読み分ける 後編

皆さま こんにちは。

今回はADCを使ったスイッチの読み分け(判定)の後編です。

投稿時の開発環境を記しておきます。

PC:Windows10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.6.0
マイコン: STM32L010F4P6
Board: 自作のボード

コーディングする

ファイル gueue.h を追加・編集する

スイッチが押された情報をキューに格納するので、キューのコードを書きます。
キューの詳しい説明は省きますが、データ構造のひとつで先に入れたデータを先に取り出す FiFoバッファです。

FiFo(ファイフォー) は First in First Out の略です。

File – New – Header File から queue.h を作成し、以下の内容を記述します。

queue.h は Project Explorer – STM32L-UART – Core – INc にあるのでダブルクリックしてファイルを開いて編集します。

#ifndef __QUEUE_H
#define __QUEUE_H

#define SIZE 4

#include <stdint.h> // uint8_t

typedef enum {
	SW_NONE,
	SW1,
	SW2,
	SW3,
	SW4,
} Sw;

typedef struct {
	uint8_t head;
	uint8_t tail;
	Sw qbuf[SIZE+1];
} data;

#define THRESHOLD43 3584
#define THRESHOLD32 2566
#define THRESHOLD21 1537
#define THRESHOLD10 508

void init(data *d);
_Bool isEmpty(data *d);
_Bool isFull(data *d);
void enqueue(data *d, Sw sw);
Sw dequeue(data *d);
void sort(data *d, uint16_t ad);

#endif /* __QUEUE_H */

ファイル queue.c を追加・編集する

File – New – Source File から queue.c を作成し、以下の内容を記述します。

queue.c は Project Explorer – STM32L-UART – Core – Src にあるのでダブルクリックしてファイルを開いて編集します。

#include "queue.h"
#include "stm32l0xx_hal.h" // UART

data qd;
UART_HandleTypeDef huart2;

void init(data *d)
{
	d->head = d->tail = 0;
}

_Bool isEmpty(data *d)
{
	return (d->head == d->tail);
}

_Bool isFull(data *d)
{
	return (d->head == (d->tail + 1) % (SIZE + 1));
}

void enqueue(data *d, Sw sw)
{
	d->qbuf[d->tail++] = sw;
	if ((SIZE + 1) == d->tail)
	{
		d->tail = 0;
	}
}

Sw dequeue(data *d)
{
	Sw ret = d->qbuf[d->head];
	d->head++;
	if ((SIZE + 1) == d->head)
	{
		d->head = 0;
	}
	return ret;
}

void sort(data *d, uint16_t ad)
{
	static uint16_t sw1 = 0;
	static uint16_t sw2 = 0;
	static uint16_t sw3 = 0;
	static uint16_t sw4 = 0;

	if (ad >= THRESHOLD43)	// SW_NONE
	{
		sw1 = sw2 = sw3 = sw4 = 0;
	}
	else if (THRESHOLD43 > ad && ad >= THRESHOLD32)	// SW4
	{
		sw4++;
		sw3 = sw2 = sw1 = 0;
		if (sw4 == 3)
		{
			if (!isFull(d))
			{
				enqueue(d, SW4);
				HAL_UART_Transmit(&huart2, (uint8_t*)"SW4 was pressed.\r\n", 18, 10);
			}
			else
			{
				HAL_UART_Transmit(&huart2, (uint8_t*)"Queue is full.\r\n", 16, 10);
			}
		}
	}
	else if (THRESHOLD32 > ad && ad >= THRESHOLD21)	// SW3
	{
		sw3++;
		sw4 = sw2 = sw1 = 0;
		if (sw3 == 3)
		{
			if (!isFull(d))
			{
				enqueue(d, SW3);
				HAL_UART_Transmit(&huart2, (uint8_t*)"SW3 was pressed.\r\n", 18, 10);
			}
			else
			{
				HAL_UART_Transmit(&huart2, (uint8_t*)"Queue is full.\r\n", 16, 10);
			}
		}
	}
	else if (THRESHOLD21 > ad && ad >= THRESHOLD10)	// SW2
	{
		sw2++;
		sw4 = sw3 = sw1 = 0;
		if (sw2 == 3)
		{
			if (!isFull(d))
			{
				enqueue(d, SW2);
				HAL_UART_Transmit(&huart2, (uint8_t*)"SW2 was pressed.\r\n", 18, 10);
			}
			else
			{
				HAL_UART_Transmit(&huart2, (uint8_t*)"Queue is full.\r\n", 16, 10);
			}
		}
	}
	else	// SW1
	{
		sw1++;
		sw4 = sw3 = sw2 = 0;
		if (sw1 == 3)
		{
			if (!isFull(d))
			{
				enqueue(d, SW1);
				HAL_UART_Transmit(&huart2, (uint8_t*)"SW1 was pressed.\r\n", 18, 10);
			}
			else
			{
				HAL_UART_Transmit(&huart2, (uint8_t*)"Queue is full.\r\n", 16, 10);
			}
		}
	}
}

ファイル stm32l0xx_it.c を編集する

ファイルの冒頭あたりで変数の宣言を追加します。

/* USER CODE BEGIN Includes */

#include "queue.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

extern ADC_HandleTypeDef hadc;
__IO uint16_t ad;
extern data qd;

/* USER CODE END TD */

TIM2の割り込みハンドラーを以下のように書きます。

void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */
  static uint16_t f = 0;

  HAL_StatusTypeDef s;

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  if (f & 1)
  {
    s =  HAL_ADC_PollForConversion(&hadc, 3);
    if( s == HAL_OK )
    {
      ad = HAL_ADC_GetValue(&hadc);
      sort(&qd, ad);
    }
    else
    {
//  ここには来ない
    }
  }
  else
  {
    HAL_ADC_Start(&hadc);
  }
  f ^= 1;
  /* USER CODE END TIM2_IRQn 1 */
}

ファイル main.c を編集する

ファイルの冒頭あたりで


/* USER CODE BEGIN PV */

#include "queue.h"
extern data qd;

/* USER CODE END PV */

while(1)ループの近辺

init(&qd); // queue initialize
  HAL_TIM_Base_Start_IT(&htim2);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  Sw readSw;

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

	if (!isEmpty(&qd))
	{
		readSw = dequeue(&qd);
		switch (readSw)
		{
		case SW1:
			HAL_UART_Transmit(&huart2, (uint8_t*)"Dequeue called by SW1\r\n", 23, 10);
			break;
		case SW2:
			HAL_UART_Transmit(&huart2, (uint8_t*)"Dequeue called by SW2\r\n", 23, 10);
			break;
		case SW3:
			HAL_UART_Transmit(&huart2, (uint8_t*)"Dequeue called by SW3\r\n", 23, 10);
			break;
		case SW4:
			HAL_UART_Transmit(&huart2, (uint8_t*)"Dequeue called by SW4\r\n", 23, 10);
			break;
		default:
			break;
		}
	}
	HAL_Delay(5000);

  }

  /* USER CODE END 3 */

ビルドして実行してみる

Project – Build All でビルドし、エラーがないことを確認して Run – Debug し実行します。

プログラムの概要

10msecのタイマー割り込みを使います。
2回に分けて処理していて、前半でAD変換をスタートし、後半で変換終了している値を読んでいます。

ということでAD変換のサンプリングは20msec毎に行っています。

sort()関数の中で、どのスイッチが押されたのか判定をしています。

sw1~sw4のstaticな変数をカウンターとして使って、数回同じ範囲にAD変換値があれば、そのスイッチが押されたと判定しています。

(カウンターが1周したら、もう1回押されたことになりますが、そこまで長い時間スイッチを押し続けない仕様としておきます^^)

押されたと判定したら、isFull()という関数でキューがいっぱいかどうか判定して、空いていれば押したスイッチの情報をキューに入れます。

main()の while(1)ループでは5秒毎にキューが空か確認しています。

通常はもっと素早く処理するはずですが、スイッチを素早く押し続けるとサイズ4のキューがいっぱいになるので、その挙動も確認できるようにしました。

以下の挙動について、UARTで確認できるようにしました。

・スイッチが押された
・キューがいっぱい
・スイッチの情報を取り出した

PCのTera Termで受信した様子の画像を貼っておきます。

割り込み処理の中にそこそこ時間のかかる処理を入れるのもどうかと思いますが、テスト用ということでご容赦ください。

キューを取り扱ったことがない方は、ぜひ動かしてみてください。

いかがでしたか?
うまく動作確認できましたか?

お疲れさまでした。

LowPowerカテゴリの最新記事