大気圧センサ(LPS331AP)より測定データを得るにはI2CとSPIの二つの方法があります。最初にI2Cで実験します。
I2Cを選ぶにはLPS331APのCSをハイにし、SCLとSDAでCPUとやりとりします。
CPUのPC4, 5は汎用ポートとしてI2Cのマスターの動作をさせます。(AVRのI2Cは使わない) なお、LPS331APは電源は3.3VですのでPCA9306でレベル変換します。 I2Cを使用する場合 コントロール・バイトは書き込みは0xb8、読み出しは0xb9です。(SDOはロー) @レジスタ0x0fを読むと0xbbが返ってくれば大成功です。 Aレジスタ0x20に0x90または0x94を書き込めば、気圧、温度を一秒毎に取ります。 Bデータを取るタイミングはレジスタ0x27のビット0、1を利用すると良いでしょう。 気圧データがそろうとビット1が1、温度データがそろうとビット0が1になります。 レジスタ0x29を読むとビット1が0、レジスタ0x2bを読むとビット0が0となります。 Cレジスタ0x28、0x29、0x2a、0x2b、0x2cを読み込み(I2Cの場合、オートインクリメントの場合は0x28を0xa8にする) 計算すると気圧、温度が得られます。 気圧 = ((レジスタ0x2aの値)×65536 + (レジスタ0x29の値)×256 + (レジスタ0x28の値)) / 4096 温度 = 42.5 + ((レジスタ0x2cの値)×256 + (レジスタ0x2bの値)) / 480 SPIを使用する場合 LPS331APのCS-----CS(CPUのPC5) LPS331APのSPC----SCK(CPUのPC4) LPS331APのSDI----SDO(CPUのPC3) LPS331APのSDO----SDICPUのPC2) のように接続します。SPIは高速のため、ロジックレベル変換用ICは TXS0108E を使用します。 あとは、I2Cを使用する場合の@ABCのようになります。 |
回路図 (I2Cを使用)
回路図 (SPIを使用)
表示画面
実験風景 (I2Cを使用)
How to Compile (コンパイルのしかた) I2Cを使用のプログラム /* * LPS331AP_i2c.c * 大気圧センサ「LPS331AP」のレジスタをI2Cで読み書きし、 * 得られた気圧、温度をRS-232C(115200 8-N-1)で出力する。 * ATmega328P * * * Fuses(システムクロック:20MHz) * * 水晶 * EXTENDED 0xFD * HIGH 0xD9 * LOW 0xF7 */ #include <stdio.h> #include <stdlib.h> /* abs() */ #include <avr/io.h> #include <avr/interrupt.h> #define F_CPU 20.0E6 /* 20 MHz */ #include <util/delay.h> #define SCL_OUT DDRC |= _BV(5) #define SDA_OUT DDRC |= _BV(4) #define SCL_IN DDRC &= ~_BV(5) #define SDA_IN DDRC &= ~_BV(4) #define SCL_H PORTC |= _BV(5) #define SDA_H PORTC |= _BV(4) #define SCL_L PORTC &= ~_BV(5) #define SDA_L PORTC &= ~_BV(4) #define SCL (PINC & _BV(5)) #define SDA (PINC & _BV(4)) /* 変更無し */ #define ACK 0 /* ハードに合わせて変更する (SDAのビット) */ #define NAK _BV(4) #define PS_WRITE 0xb8 #define PS_READ 0xb9 #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) void delay_us(unsigned short t){while(t--) _delay_us(1);} ///void delay_ms(unsigned short t){while(t--) _delay_ms(1);} void initUSART0(void); void Send0(char *str); int ps_write_i2c(unsigned char addr, unsigned char *data, unsigned char size); int ps_read_i2c(unsigned char addr, unsigned char *data, unsigned char size); int main(void) { int result; unsigned char buffer[5]; char buffer2[16]; unsigned char status; long tmp; short tmp2; short press, temp; /* ATmega328P */ DDRB = 0b00000000; /* XTAL2 XTAL1 SCK MISO MOSI ----- ----- ----- */ DDRC = 0b00100000; /* xxxxx RESET SCL SDA ----- ----- ----- ----- */ DDRD = 0b00000000; /* ----- ----- ----- ----- ----- ----- TXD RXD */ PORTB = 0b00111111; PORTC = 0b00001111; PORTD = 0b11111100; initUSART0(); sei(); Send0("START\r"); /* レジスタ0x0fを読むと0xbbが返ってくれば大成功です。 */ result = ps_read_i2c(0x0f, buffer, 1); if(result != -1){ sprintf(buffer2, "WHO AM I = 0x%02x\r", buffer[0]); Send0(buffer2); } else Send0("Error1\r"); /* レジスタ0x20に0x90または0x94を書き込めば、気圧、温度を一秒毎に取ります。 */ buffer[0] = 0x94; result = ps_write_i2c(0x20, buffer, 1); if(result == -1) Send0("Error2\r"); while(1){ /* データを取るタイミングはレジスタ0x27のビット0、1を利用すると良いでしょう。 */ /* 気圧データがそろうとビット1が1、温度データがそろうとビット0が1になります。 */ /* レジスタ0x29を読むとビット1が0、レジスタ0x2bを読むとビット0が0となります。 */ result = ps_read_i2c(0x27, &status, 1); if(result == -1){ Send0("Error3\r"); status = 0; } if((status & 0x03) == 0x03){ /* レジスタ0x28、0x29、0x2a、0x2b、0x2c読み込み込む。 */ /* (オートインクリメントの場合は0x28を0xa8にする) */ result = ps_read_i2c(0xa8, buffer, 5); if(result != -1){ /* 気圧 = ((レジスタ0x2aの値)×65536 + (レジスタ0x29の値)×256 */ /* + (レジスタ0x28の値)) / 4096 */ tmp = buffer[2]; tmp <<= 8; tmp += buffer[1]; tmp <<= 8; tmp += buffer[0]; press = (short)round(tmp / 4096.0); /* 温度 = 42.5 + ((レジスタ0x2cの値)×256 + (レジスタ0x2bの値)) / 480 */ tmp2 = buffer[4]; tmp2 <<= 8; tmp2 += buffer[3]; temp = (short)round(425.0 + tmp2 / 48.0); sprintf(buffer2, "%d hPa %d.%d℃\r", press, temp / 10, abs(temp % 10)); Send0(buffer2); } else Send0("Error4\r"); } } } void initUSART0(void) { UBRR0 = 21; /* U2X0 = 1, 115200ボー/20MHzクロック */ UCSR0A = 0b00000010; /* U2X0 = 1 */ UCSR0B = 0b00001000; /* 送信イネーブル */ UCSR0C = 0b00000110; /* 8ビット, パリティなし, ストップ・ビット1 */ } /* * 1データ送信 */ void Tx0(char data) { while((UCSR0A & _BV(5)) == 0) ; UDR0 = (unsigned char)data; } /* * 文字列を送信 */ void Send0(char *str) { while(*str != '\0'){ Tx0(*str); str++; } } void start_i2c(void) { SCL_OUT; SDA_OUT; SCL_L; SDA_H; SCL_H; delay_us(1); /* TSU:STA = 600ns */ SDA_L; delay_us(1); /* THD:STA = 600ns */ SCL_L; SCL_OUT; SDA_IN; } void stop_i2c(void) { SCL_OUT; SDA_OUT; SCL_L; SDA_L; SCL_H; delay_us(1); /* TSU:STO = 600ns */ SDA_H; delay_us(1); /* TBUF = 1300ns */ delay_us(1); SCL_L; SCL_OUT; SDA_IN; } /* * 戻り値 ACK:0, NAK:SDA */ unsigned char writebyte(unsigned char data) { unsigned char i; SCL_OUT; SDA_OUT; for(i=0; i<8; i++){ if(data & 0x80) SDA_H; else SDA_L; SCL_H; delay_us(1); /* THIGH = 600ns */ SCL_L; delay_us(1); /* TLOW = 1300ns */ delay_us(1); data <<= 1; } SCL_OUT; SDA_IN; SCL_H; delay_us(1); /* THIGH = 600ns */ i = SDA; SCL_L; delay_us(1); /* TLOW = 1300ns */ delay_us(1); return i; } /* * 戻り値 読み込みデータ(8ビット) */ unsigned char readbyte(void) { unsigned char i, data; data = 0; for(i=0; i<8; i++){ data <<= 1; SCL_H; delay_us(1); /* THIGH = 600ns */ if(SDA) data++; SCL_L; delay_us(1); /* TLOW = 1300ns */ delay_us(1); } return data; } void ack(void) { SCL_OUT; SDA_OUT; SDA_L; SCL_H; delay_us(1); /* THIGH = 600ns */ SCL_L; delay_us(1); /* TLOW = 1300ns */ delay_us(1); SCL_OUT; SDA_IN; } void nak(void) { SCL_OUT; SDA_OUT; SDA_H; SCL_H; delay_us(1); /* THIGH = 600ns */ SCL_L; delay_us(1); /* TLOW = 1300ns */ delay_us(1); SCL_OUT; SDA_IN; } /* * 戻り値 0:成功, -1:失敗 * * コントロール・バイトの書き込み */ int send_ctrl(unsigned char ctrl) { unsigned char ret; start_i2c(); ret = writebyte(ctrl); if(ret == NAK) return -1; return 0; } /* * 戻り値 0:成功, -1:失敗 * * コントロール・バイトの書き込み以外はACKをチェックしてません */ int ps_write_i2c(unsigned char addr, unsigned char *data, unsigned char size) { int ret; unsigned char i; ret = send_ctrl(PS_WRITE); if(ret == -1) return -1; writebyte(addr); for(i=0; i<size; i++) writebyte(*data++); stop_i2c(); return 0; } /* * 戻り値 0:成功, -1:失敗 * * コントロール・バイトの書き込み以外はACKをチェックしてません */ int ps_read_i2c(unsigned char addr, unsigned char *data, unsigned char size) { int ret; unsigned char i; ret = send_ctrl(PS_WRITE); if(ret == -1) return -1; writebyte(addr); ret = send_ctrl(PS_READ); if(ret == -1) return -1; for(i=0; i<size-1; i++){ *data++ = readbyte(); ack(); } *data = readbyte(); nak(); stop_i2c(); return 0; } |
How to Compile (コンパイルのしかた) SPIを使用のプログラム /* * LPS331AP_spi.c * 大気圧センサ「LPS331AP」のレジスタをSPIで読み書きし、 * 得られた気圧、温度をRS-232C(115200 8-N-1)で出力する。 * ATmega328P * * * Fuses(システムクロック:20MHz) * * 水晶 * EXTENDED 0xFD * HIGH 0xD9 * LOW 0xF7 */ #include <stdio.h> #include <stdlib.h> /* abs() */ #include <avr/io.h> #include <avr/interrupt.h> ///#define F_CPU 20.0E6 /* 20 MHz */ ///#include <util/delay.h> #define CS_H PORTC |= _BV(5) #define SCK_H PORTC |= _BV(4) #define SDO_H PORTC |= _BV(3) #define CS_L PORTC &= ~_BV(5) #define SCK_L PORTC &= ~_BV(4) #define SDO_L PORTC &= ~_BV(3) #define SDI (PINC & _BV(2)) #define NOP() __asm__ __volatile__("NOP"::) #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) ///void delay_us(unsigned short t){while(t--) _delay_us(1);} ///void delay_ms(unsigned short t){while(t--) _delay_ms(1);} void initUSART0(void); void Send0(char *str); void ps_write_spi(unsigned char addr, unsigned char *data, unsigned char size); void ps_read_spi(unsigned char addr, unsigned char *data, unsigned char size); int main(void) { unsigned char buffer[5]; char buffer2[16]; unsigned char status; long tmp; short tmp2; short press, temp; /* ATmega328P */ DDRB = 0b00000000; /* XTAL2 XTAL1 SCK MISO MOSI ----- ----- ----- */ DDRC = 0b00111000; /* xxxxx RESET CS SCK SDO SDI ----- ----- */ DDRD = 0b00000000; /* ----- ----- ----- ----- ----- ----- TXD RXD */ PORTB = 0b00111111; PORTC = 0b00110011; PORTD = 0b11111100; initUSART0(); sei(); Send0("START\r"); /* レジスタ0x0fを読むと0xbbが返ってくれば大成功です。 */ ps_read_spi(0x0f, buffer, 1); sprintf(buffer2, "WHO AM I = 0x%02x\r", buffer[0]); Send0(buffer2); /* レジスタ0x20に0x90または0x94を書き込めば、気圧、温度を一秒毎に取ります。 */ buffer[0] = 0x94; ps_write_spi(0x20, buffer, 1); while(1){ /* データを取るタイミングはレジスタ0x27のビット0、1を利用すると良いでしょう。 */ /* 気圧データがそろうとビット1が1、温度データがそろうとビット0が1になります。 */ /* レジスタ0x29を読むとビット1が0、レジスタ0x2bを読むとビット0が0となります。 */ ps_read_spi(0x27, &status, 1); if((status & 0x03) == 0x03){ /* レジスタ0x28、0x29、0x2a、0x2b、0x2c読み込み込む。 */ ps_read_spi(0x28, buffer, 5); /* 気圧 = ((レジスタ0x2aの値)×65536 + (レジスタ0x29の値)×256 */ /* + (レジスタ0x28の値)) / 4096 */ tmp = buffer[2]; tmp <<= 8; tmp += buffer[1]; tmp <<= 8; tmp += buffer[0]; press = (short)round(tmp / 4096.0); /* 温度 = 42.5 + ((レジスタ0x2cの値)×256 + (レジスタ0x2bの値)) / 480 */ tmp2 = buffer[4]; tmp2 <<= 8; tmp2 += buffer[3]; temp = (short)round(425.0 + tmp2 / 48.0); sprintf(buffer2, "%d hPa %d.%d ℃\r", press, temp / 10, abs(temp % 10)); Send0(buffer2); } } } void initUSART0(void) { UBRR0 = 21; /* U2X0 = 1, 115200ボー/20MHzクロック */ UCSR0A = 0b00000010; /* U2X0 = 1 */ UCSR0B = 0b00001000; /* 送信イネーブル */ UCSR0C = 0b00000110; /* 8ビット, パリティなし, ストップ・ビット1 */ } /* * 1データ送信 */ void Tx0(char data) { while((UCSR0A & _BV(5)) == 0) ; UDR0 = (unsigned char)data; } /* * 文字列を送信 */ void Send0(char *str) { while(*str != '\0'){ Tx0(*str); str++; } } void ps_write_spi(unsigned char addr, unsigned char *data, unsigned char size) { unsigned char tmp; unsigned char i, j; CS_L; /* R /W[7], M /S[6], AD[5:0] */ tmp = addr; if(size != 1) tmp |= _BV(6); /* アドレス */ for(i=0; i<8; i++){ SCK_L; /* SCK パルス出力 */ if(tmp & 0x80){ /* 1 */ SDO_H; } else{ /* 0 */ SDO_L; } tmp <<= 1; SCK_H; } /* データ送信 */ for(j=0; j<size; j++){ tmp = *(data + j); for(i=0; i<8; i++){ SCK_L; /* SCK パルス出力 */ if(tmp & 0x80){ /* 1 */ SDO_H; } else{ /* 0 */ SDO_L; } tmp <<= 1; SCK_H; } } CS_H; } void ps_read_spi(unsigned char addr, unsigned char *data, unsigned char size) { unsigned char tmp; unsigned char i, j; CS_L; /* R /W[7], M /S[6], AD[5:0] */ tmp = addr; tmp |= _BV(7); if(size != 1) tmp |= _BV(6); /* アドレス */ for(i=0; i<8; i++){ SCK_L; /* SCK パルス出力 */ if(tmp & 0x80){ /* 1 */ SDO_H; } else{ /* 0 */ SDO_L; } tmp <<= 1; SCK_H; } /* データ受信 */ for(j=0; j<size; j++){ tmp = 0; for(i=0; i<8; i++){ SCK_L; /* SCK パルス出力 */ NOP(); NOP(); tmp <<= 1; if(SDI){ /* 1 */ tmp |= 1; } SCK_H; } *(data + j) =tmp; } CS_H; } |