著者:米田 聡
小型コンピュータボード「Raspberry Pi」(ラズパイ)向けにさまざまな拡張ボードが発売されています。その拡張ボードとラズパイを組み合わせれば、ラズパイでいろいろなことが簡単に試せます。本企画では、前回に引き続き、ラズパイと連携して使用するマイコンボードを扱います。
シェルスクリプトマガジン Vol.84は以下のリンク先でご購入できます。
図8 I★2★CアドレスとGPIOピン番号の設定(ADRS2040U_i2c.h)
1 2 3 4 5 6 |
// ADRS2040U I2Cアドレス #define I2C0_SLAVE_ADDR 0x41 // I2Cで使うGPIO #define GPIO_SDA0 0 #define GPIO_SCK0 1 |
図9 I2CとI2C Slaveライブラリの初期化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <stdio.h> #include <pico/stdlib.h> #include "hardware/i2c.h" ← hardware_i2cライブラリのヘッダーファイル #include "pico/i2c_slave.h" ← I2C Slaveライブラリのヘッダーファイル (略) #include "ADRS2040U_i2c.h" (略) static void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) ← I★★2C割り込みコールバック関数 { (略) } void i2c_setup(void) { i2c_init(i2c0, 100 * 1000); ← I★2★Cコントローラの初期化(通信速度100Kビット/秒) gpio_set_function(GPIO_SDA0, GPIO_FUNC_I2C); ← I★2★Cで利用するGPIOピンの設定 gpio_set_function(GPIO_SCK0, GPIO_FUNC_I2C); // ADRS2040Uでは基板上でプルアップされているので // プルアップを無効化する gpio_disable_pulls(GPIO_SDA0); gpio_disable_pulls(GPIO_SCK0); (略) // I2Cスレーブ初期化 i2c_slave_init(i2c0, I2C0_SLAVE_ADDR, &i2c_slave_handler); } |
図10 コールバック関数「i2c_slave_handler()」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
static void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { (略) switch(event) { // I2Cデータ受信 case I2C_SLAVE_RECEIVE: (略) break; // I2Cデータ要求 case I2C_SLAVE_REQUEST: (略) break; // STOP or RESTARTコンデション case I2C_SLAVE_FINISH: break; default: break; } } |
図11 ADCの初期化で呼び出す関数
1 2 3 4 |
adc_init(); adc_gpio_init(26); adc_select_input(0); |
図12 adc_fifo_setup()関数の引数パラメータ
1 2 3 4 5 6 7 |
adc_fifo_setup( en, dreq_en, dreq_threash, error_in_fifo, byte_shift ); |
図13 ADCの受信FIFOバッファ割り込み処理のサンプルコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
uint16_t adc_buffer[1024]; static int p = 0; (略) // ADC FIFO割り込み関数 void adc_interrupt(void) { // FIFOが空になるまでデータをadc_bufferに読み取る while(! adc_fifo_is_empty()) { adc_buffer[p++] = adc_fifo_get() & 0xFFF; } } void main(void) { (略) adc_fifo_setup(true, false, 1, true, false); (略) // 排他的割り込みの設定 irq_set_exclusive_handler(ADC_IRQ_FIFO, adc_interrupt); // 割り込みの有効化 irq_set_enabled(ADC_IRQ_FIFO, true); // フリーランスタート adc_run(true); (略) } |
図14 サンプリングレート設定のサンプルコード
1 2 3 4 |
int sample_rate = 1000; // 1000 サンプリング/秒 adc_clk = clock_get_hz(clk_adc); adc_set_clkdiv(adc_clk/sample_rate); |
図15 リングバッファのサンプルコード
1 2 3 4 5 6 7 8 9 |
uint16_t buffer[0x100]; int write_pointer, read_pointer; write_pointer = read_pointer = 0; // バッファの読み出し data = buffer[(read_pointer++) & 0xFF]; // バッファへの書き込み buffer[(write_pointer++) & 0xFF] = data; |
図16 クリティカルセクションの保護
1 2 3 4 5 |
mutex_enter_blocking(&my_mutex); ここがクリティカルセクション mutex_exit(&my_mutex); |
図17 サンプルファームウエアのレジスタ番号定義(ADRS2040U_i2c.h)
1 2 3 4 5 6 7 8 9 |
// I2Cレジスタ enum ADRS2040_CMD { ADRS2040_CMD_INVALID, // 無効 ADRS2040_CMD_ADC_START, // ADCフリーラン開始 ADRS2040_CMD_ADC_STOP, // ADCフリーラン停止 ADRS2040_CMD_SET_RATE, // サンプリングレート設定 ADRS2040_CMD_GET_COUNT, // バッファデータ数 ADRS2040_CMD_GET_VALUE, // ADCデータ読み取り }; |
図18 I2Cスレーブ動作時に使えるFIFOバッファ読み書き関数
1 2 3 4 5 6 7 8 |
// 1バイト読みだし i2c_read_byte_raw(i2c_inst_t *); // 指定バイト数の読み出し i2c_read_raw_blocking(i2c_inst_t *, uint8_t *, size_t); // 1バイト書き込み i2c_write_byte_raw(i2c_inst_t *, uint8_t); // 指定バイト数の書き込み i2c_write_raw_blocking(i2c_inst_t *, uint8_t *, size_t); |
図19 サンプルファームウエアのi2c_slave_handler()関数とその関連個所(main.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
// ADCドライバ ADC_Driver adcd; typedef union { uint16_t d; uint8_t b[2]; } I2C_WORD_t; static void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { static uint8_t ADRS2040U_cmd = ADRS2040_CMD_INVALID; switch(event) { // I2Cデータ受信 case I2C_SLAVE_RECEIVE: if(ADRS2040U_cmd == ADRS2040_CMD_INVALID) { ADRS2040U_cmd = i2c_read_byte_raw(i2c); } if(ADRS2040U_cmd == ADRS2040_CMD_ADC_START) { DEBUG_PRINT("ADC START\n"); adcd.run(true); ADRS2040U_cmd = ADRS2040_CMD_INVALID; } else if(ADRS2040U_cmd == ADRS2040_CMD_ADC_STOP) { DEBUG_PRINT("ADC STOP\n"); adcd.run(false); ADRS2040U_cmd = ADRS2040_CMD_INVALID; } else if(ADRS2040U_cmd == ADRS2040_CMD_SET_RATE) { I2C_WORD_t rate; i2c_read_raw_blocking(i2c, rate.b, sizeof(I2C_WORD_t)); DEBUG_PRINT("Rate = %d\n", rate.d * 10); adcd.set_sample_rate(rate.d * 10); ADRS2040U_cmd = ADRS2040_CMD_INVALID; } break; // I2Cデータ要求 case I2C_SLAVE_REQUEST: I2C_WORD_t sdata; sdata.d = 0; if(ADRS2040U_cmd == ADRS2040_CMD_GET_COUNT) { sdata.d = adcd.count(); } else if(ADRS2040U_cmd == ADRS2040_CMD_GET_VALUE) { int value = adcd.get_value(); if( value >= 0) { sdata.d = value & 0xFFF; } else { sdata.d = 0xFFFF; } } i2c_write_raw_blocking(i2c, sdata.b, sizeof(I2C_WORD_t)); ADRS2040U_cmd = ADRS2040_CMD_INVALID; break; // STOP or RESTARTコンデション case I2C_SLAVE_FINISH: break; default: break; } } (略) int main(void) { stdio_init_all(); i2c_setup(); while (true) { ; } } |
図20 受信FIFOバッファからデータを取り出すための前処理
1 2 3 4 5 6 7 |
i2c_hw_t *i2chw = i2c_get_hw(i2c0); uint32_t datacmd = i2chw->data_cmd; // FIFOからデータを取り出す uintu_t value = datacmd & I2C_IC_DATA_CMD_DAT_BITS; // 下位8ビットがデータ本体 if(datacmd & I2C_IC_DATA_CMD_FIRST_DATA_BYTE_BITS) { // I2Cアドレスに続く最初の書き込みバイト // つまりレジスタ番号 } |
図22 ADCへアクセスするサンプルプログラム(adcsample.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from smbus2 import SMBus ADRS2040_ADDR=0x41 ADRS2040_CMD_INVALID = 0 ADRS2040_CMD_ADC_START = 1 ADRS2040_CMD_ADC_STOP = 2 ADRS2040_CMD_SET_RATE = 3 ADRS2040_CMD_GET_COUNT = 4 ADRS2040_CMD_GET_VALUE = 5 with SMBus(1) as i2c: # 500 spsで初期化・スタート self.i2c.write_word_data(ADRS2040_ADDR, ADRS2040_CMD_SET_RATE, 50) self.i2c.write_byte(ADRS2040_ADDR, ADRS2040_CMD_ADC_START) while True: nod = 0 nod = self.i2c.read_word_data(ADRS2040_ADDR,ADRS2040_CMD_GET_COUNT) for i in range(nod): value = self.i2c.read_word_data(ADRS2040_ADDR,ADRS2040_CMD_GET_VALUE) print(value) |
図23 ADCから取得したデータをグラフ化するサンプルプログラム(adctest.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
from smbus2 import SMBus import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui import numpy as np import sys import time # 定数 ADRS2040_ADDR=0x41 ADRS2040_CMD_INVALID = 0 ADRS2040_CMD_ADC_START = 1 ADRS2040_CMD_ADC_STOP = 2 ADRS2040_CMD_SET_RATE = 3 ADRS2040_CMD_GET_COUNT = 4 ADRS2040_CMD_GET_VALUE = 5 class ADCGraph: def __init__(self): # グラフウィンドウ self.win = pg.GraphicsWindow() self.win.setWindowTitle('ADC Input') self.plt = self.win.addPlot() self.plt.setYRange(0,1024) self.curve = self.plt.plot(pen=(0, 0, 255)) # グラフデータを用意 self.data = np.zeros(100) self.i2c = SMBus(1) # 500 spsで初期化・スタート self.i2c.write_word_data(ADRS2040_ADDR, ADRS2040_CMD_SET_RATE, 50) self.i2c.write_byte(ADRS2040_ADDR, ADRS2040_CMD_ADC_START) self.timer = QtCore.QTimer() self.timer.timeout.connect(self.update) # 10msごとに更新 self.timer.start(10) # グラフ更新 def update(self): nod = 0 nod = self.i2c.read_word_data(ADRS2040_ADDR,ADRS2040_CMD_GET_COUNT ) for i in range(nod): value = self.i2c.read_word_data(ADRS2040_ADDR,ADRS2040_CMD_GET_VALUE) self.data = np.delete(self.data, 0) self.data = np.append(self.data, value) self.curve.setData(self.data) if __name__ == "__main__": graphWin = ADCGraph() if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_() |