著者:米田 聡
小型コンピュータボード「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ライブラリの初期化
		
		
			
			
			
			
				
					
				
					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の初期化で呼び出す関数
		
		
			
			
			
			
				
					
				| 
					
				 | 
						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バッファ割り込み処理のサンプルコード
		
		
			
			
			
			
				
					
				
					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 サンプリングレート設定のサンプルコード
		
		
			
			
			
			
				
					
				| 
					
				 | 
						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)
		
		
			
			
			
			
				
					
				
					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バッファからデータを取り出すための前処理
		
		
			
			
			
			
				
					
				| 
					
				 | 
						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_()  |