温湿度・気圧センサーから測定データを取得する前に、簡単なプログラムを作成してSPI通信できるか確認します。
SPI通信確認方法
温湿度・気圧データを取得するプログラムは、データ校正部分がかなり複雑です。最初からそのプログラムを作成すると全体の見通しが悪くなってしまいます。
そこで、最初はSPI通信の動作確認を行うためのシンプルなプログラムを作成することにします。
このセンサーのメモリマップを見ると、0xD0番地にセンサのIDが保存されています。
ここに保存されている値は0x60で固定となっています。

そこで、0xD0番地のデータをSPI通信で読み取るプログラムを作成し、0x60が正しく取得できるか確認することによりSPI通信の動作確認を行います。
ところで、プログラムで0xD0番地のデータを読み取るのはいいとしても、その値を確認方法がありませんよね。
LCDモジュールを利用する方法がありますが、まだ動作させていませんので、読み取った値をLCDモジュールに表示して確認することはできません。
そこでセンサから取得した値を確認するために、MPLAB X IDEの「デバッグ機能」を使用します。
デバッグ機能については、実際に使用する際に詳しく説明しますので、ここでは「デバッグ機能」とはどのようなものか簡単に説明します。
今まで、PICマイコンを動作させる場合の手順は、MPLAB X IDE上でプログラムを作成→ビルド→PICマイコンに書き込み→動作、というステップでした。
このような手順では、PICマイコンが思った通りに動作する場合は問題ないですが、うまく動かない場合、PICマイコンの中で何が起こっているのか知りたいですよね。
MPLAB X IDEのデバッグ機能を使うと、PICマイコン内部で動作しているプログラムを指定した行で動作を止めて、変数の内容を確認しながらプログラムを1行ずつ動作させることができます。
このあとの説明は次のように進めます。
最初に動作確認プログラムでどのようなSPI通信を行うか、概要を説明します。
実際にSPI通信の動作確認用のプログラムを作成します。
デバッグ機能を利用して、SPI通信で取得したIDの値を確認します。
MPLAB X IDEのデバッグ機能の基本的な使い方も習得します。
使用するピンの確認
SPI通信を行うために、PICマイコンとセンサモジュールは以下のように接続しました。

このように接続すると、PICマイコン側のSPI通信用のピンは以下のように割り当てることになります。
ピン番号 | ポート名 | 機能 |
---|---|---|
15 | RC4 | CSB(チップセレクト) |
16 | RC5 | MOSI(PICマイコン ➡︎ センサー) |
17 | RC6 | MISO(センサー ➡︎ PICマイコン) |
18 | RC7 | SCK(クロック) |
SPI通信は、RC4 〜 RC7のピンをデジタル制御しながら行うことになります。
プログラムでは次のように記述して制御することになります。
LATCbits.LATC4 = 0;
ただ、このように記述すると、どのピンがどの信号線か分かりづらくなりますので、次のように名前定義することにします。
// SPI通信ピンの名前定義
#define SPI_SCK LATCbits.LATC7 // SCK (クロック)
#define SPI_MISO PORTCbits.RC6 // MISO (センサーからPICマイコン)
#define SPI_MOSI LATCbits.LATC5 // MOSI (PICマイコンからセンサー)
#define SPI_CSB LATCbits.LATC4 // CSB (チップセレクト)
なお、PICマイコンのピン入出力設定は次のようになります。
ポート名 | 機能 | 入出力 |
---|---|---|
RC4 | CSB(チップセレクト) | 出力 |
RC5 | MOSI(PICマイコン ➡︎ センサー) | 出力 |
RC6 | MISO(センサー ➡︎ PICマイコン) | 入力 |
RC7 | SCK(クロック) | 出力 |
出力制御はLATレジスタ、入力値読み取りはPORTレジスタを使用しますので、上のように名前定義しています。
0xD0番地の読み出し手順
SPI通信で0xDO番地の読み出す場合、次のように通信を行います。

また、SPI通信手順は以下のようになります。(クリックすると拡大できます)

SPI通信の基本関数作成
SPI通信手順が確認できたところで、プログラムを作成します。
SPI通信を行うために、「8ビット送信」と「8ビット受信」の部分を関数として作成しておくことにします。(クリックすると拡大できます)

8ビット送信関数
8ビット送信関数として、次の部分をプログラム作成します。(クリックすると拡大できます)

この図のように、次の処理を8回繰り返せばSPIデータ送信ができます。
- SCKピンを0にする
- MOSIピンに送信するビットデータをセットする
- SCKピンを1にする
プログラムとしては以下のように作成してみました。
//
// SPIデータ8ビット書き込み
// チップセレクト信号はこの関数の前後で制御する
//
void spiSend8bit(uint8_t data) {
// 8ビット分繰り返す
for (int8_t i=7; i>=0; i--) {
// ❶SCKピンを0にする
SPI_SCK = 0;
// ❷MOSIに送信するビットデータをセットする
if( data & (1<<i) ) {
SPI_MOSI = 1;
} else {
SPI_MOSI = 0;
}
// ❸SCKピンを1にする
SPI_SCK = 1;
}
}
このプログラムについて、2点補足します。
1点目は変数型についてです。
基礎編と応用編では、変数を宣言するときの型指定はunsigned char
などを使用していました。
実践編では、変数のビット数が分かりやすいint_t
型を使用することにしました。
なお、この変数型を使用する場合、stdint.hをインクルードする必要があります。
int_t型には次の型があります。
変数型 | 意味 |
---|---|
int8_t | 符号付き8ビット |
uint8_t | 符号付き8ビット |
int16_t | 符号付き16ビット |
uint16_t | 符号なし16ビット |
int32_t | 符号付き32ビット |
uint32_t | 符号なし32ビット |
符号付きが「int」(integer・整数)、符号なしが「uint」(unsigned integer・符号なしの整数)、数字がビット数を表しますので、分かりやすい変数型かなと思います。
2点目はビット演算についてです。
関数内では、data & (1<<i)
として、iビット目の値を取り出しています。
(1<<i)
という部分で、1をi
回左にずらします。例えばi
が3のとき、0b00001000
になります。
さらに、この値をdata
と論理積(&
)を取ることによって、i
ビット目の値を取り出しています。
この結果をif
で判定しますので、0であればfalse
、1であればtrue
となり、送信するデータを設定しています。
(ちょっとわかりづらいですよね…。よく使われるテクニックですので、頭の片隅に置いておいていただければと思います)
8ビット受信関数
8ビット受信関数として、次の部分をプログラム作成します。(クリックすると拡大できます)

この図のように、次の処理を8回繰り返せばSPIデータ受信ができます。
- SCKピンを0にする
- SCKピンを1にする(この段階でセンサーのデータが読めるようになります)
- MISOのデータを読む
プログラムとしては以下のように作成してみました。
//
// SPIデータ8ビット読み込み
// スレーブセレクト信号はこの関数の前後で制御する
//
uint8_t spiReceive8bit(void) {
// 受信データ格納変数
uint8_t read_data = 0;
// 8ビット分繰り返す
for (int8_t i=7; i>=0; i--) {
// 受信データ変数を1ビット左シフト
read_data << 1;
// ❶SCKピンを0にする
SPI_SCK = 0;
// ❷SCKピンを1にする
SPI_SCK = 1;
// ❸MISOのピン状態を読む
if(SPI_MISO){
read_data |= 1;
}
}
// 受信したデータを返す
return read_data;
}
8ビットの送信と受信関数ができましたので、次回の記事で、これらの関数を利用して0xD0番地のデータを読み取るプログラムを作成します。
更新履歴
日付 | 内容 |
---|---|
2018.4.27 | 新規投稿 |
2019.4.20 | ソースコード文字化け修正 |
2025.7.29 | 「マスター」「スレーブ」を「ホスト」「デバイス」に変更 |
お世話になります
// 受信データ変数を1ビット左シフト
read_data <<= 1;
の個所ですが
1ビットシフトしたら2番目に行くのではと思って
シフト無しで確認しても結果は同じでした
初期値は0でも1シフトでも同じという事で良いんですかね
デバックが使えだして理解が深まりそうです
すみません、ご質問の内容が的確にわからないのでうまく回答できませんが、
以下のコードでBME280からSPI通信でビットデータ受信しますので、
次のコードで次のビットデータを読むために左シフトする必要があります。
これを8回繰り返してBME280からSPI通信で8ビット受信することになります。
返信ありがとうございます
昨日やってたことが今日見たら分からない状況で
酒飲みながら朦朧としながらやってるんで
物凄い勘違いしてたような気がします
それでは何か質問ありましたらコメントいただければと思います。
読んでいて自己解決しました。
ビット位置をずらすためですね。
申し訳ありませんでした。
上記プログラム(8ビット受信関数)で初歩的ではありますが質問があります。
14行名
// 受信データ変数を1ビット左シフト
read_data <<= 1; と記載がありますがこの理由を詳しく聞きたく。