著者:米田 聡
小型コンピュータボード「Raspberry Pi」(ラズパイ)向けにさまざまな拡張ボードが発売されています。その拡張ボードとラズパイを組み合わせれば、ラズパイでいろいろなことが簡単に試せます。本企画では、前回に引き続き、ラズパイと連携して使用するマイコンボードを扱います。
シェルスクリプトマガジン Vol.84は以下のリンク先でご購入できます。![]()
![]()
図8 I★2★CアドレスとGPIOピン番号の設定(ADRS2040U_i2c.h)
// ADRS2040U I2Cアドレス
#define I2C0_SLAVE_ADDR 0x41
// I2Cで使うGPIO
#define GPIO_SDA0 0
#define GPIO_SCK0 1
図9 I2CとI2C Slaveライブラリの初期化
#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()」
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の初期化で呼び出す関数
adc_init();
adc_gpio_init(26);
adc_select_input(0);
図12 adc_fifo_setup()関数の引数パラメータ
adc_fifo_setup(
en,
dreq_en,
dreq_threash,
error_in_fifo,
byte_shift
);
図13 ADCの受信FIFOバッファ割り込み処理のサンプルコード
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 サンプリングレート設定のサンプルコード
int sample_rate = 1000; // 1000 サンプリング/秒
adc_clk = clock_get_hz(clk_adc);
adc_set_clkdiv(adc_clk/sample_rate);
図15 リングバッファのサンプルコード
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 クリティカルセクションの保護
mutex_enter_blocking(&my_mutex);
ここがクリティカルセクション
mutex_exit(&my_mutex);
図17 サンプルファームウエアのレジスタ番号定義(ADRS2040U_i2c.h)
// 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バイト読みだし
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)
// 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バッファからデータを取り出すための前処理
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)
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)
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_()