🐙

STM32でZephyrRTOS入門~ADCでボリューム抵抗の値をポーリング~

に公開

はじめに

これまでWindows上にZephyrRTOSの開発環境を構築し、NucleoF103RBボードを用いて学習を進めてきた。
https://zenn.dev/gotoooo/articles/894213f95372bd

今回は、NucleoF103RBのADCでボリューム抵抗の値を読み取るサンプルを作成し、仕組みの理解に取り組みたい。
なおボリューム抵抗はPA0に接続された状態を想定している。

コード

今回もmain.cの実装に加えて、zephyr/prj.confの編集が必要である。

main.cのコード全体
main.c
#include <zephyr/drivers/adc.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define ADC_NODE DT_NODELABEL(adc1)

static const struct device *adc_dev = DEVICE_DT_GET(ADC_NODE);

static int16_t sample_buffer;

struct adc_channel_cfg channel_cfg = {
    .gain             = ADC_GAIN_1,
    .reference        = ADC_REF_INTERNAL,
    .acquisition_time = ADC_ACQ_TIME_DEFAULT,
    .channel_id       = 0,              // ADC1_IN0
    .differential     = 0,
};

int main(void)
{
    if (!device_is_ready(adc_dev)) {
        printk("ADC device not ready\n");
        return -1;
    }

    adc_channel_setup(adc_dev, &channel_cfg);

    struct adc_sequence sequence = {
        .channels    = BIT(0),
        .buffer      = &sample_buffer,
        .buffer_size = sizeof(sample_buffer),
        .resolution  = 12,
    };

    while (1) {
        int ret = adc_read(adc_dev, &sequence);
        if (ret == 0) {
            printk("ADC raw: %d\n", sample_buffer);
        }
        k_sleep(K_MSEC(500));
    }

	return 0;
}

zephyr/prj.conf
CONFIG_ADC=y

デバイスツリーでのADC設定

#define ADC_NODE DT_NODELABEL(adc1)

static const struct device *adc_dev = DEVICE_DT_GET(ADC_NODE);

デバイスツリーでは以下のように定義されている。
ADC1_In0がPA0に接続されていることがわかる。

ADCチャネルの設定と初期化

struct adc_channel_cfg channel_cfg = {
    .gain             = ADC_GAIN_1,
    .reference        = ADC_REF_INTERNAL,
    .acquisition_time = ADC_ACQ_TIME_DEFAULT,
    .channel_id       = 0,              // ADC1_IN0
    .differential     = 0,
};

    adc_channel_setup(adc_dev, &channel_cfg);

ここではADCチャネルの設定を行っている。
gainなどのメンバ変数に設定する値はZephyr側のマクロで定義されている。
ただしマイコンボードによってサポートしている値が異なる。
そのため、サポートされていない値が設定された状態で初期化処理が呼び出された場合、以下のようにエラーを返す仕組みを備えている。

サンプリングシーケンスの設定

    struct adc_sequence sequence = {
        .channels    = BIT(0),
        .buffer      = &sample_buffer,
        .buffer_size = sizeof(sample_buffer),
        .resolution  = 12,
    };

ここでは使用するチャンネル、読み取った値の格納先、バッファサイズ、分解能を指定している。

ADCのポーリング

    while (1) {
        int ret = adc_read(adc_dev, &sequence);
        if (ret == 0) {
            printk("ADC raw: %d\n", sample_buffer);
        }
        k_sleep(K_MSEC(500));
    }

adc_readを呼び出すことで、設定したサンプリングシーケンスを渡すとともにADCの変換が開始される。
そして変換が完了すると指定したバッファにデータを格納する。

なお、resolutionにサポートされていない値を指定すると以下のようにエラーが返される。

おわりに

ボリューム抵抗の値を読み取るサンプルを通じて、ZephyrRTOSにおけるADCの仕組みを理解した。
STM32F103のリファレンスマニュアルを読む限りADCは他にもまだまだ多くの機能を備えている。
DMAやタイマと連携した変換などにも機会があればトライしてみたい。

Discussion