SPI通信確認方法
温湿度・気圧データの取得するプログラムは、データ校正部分が結構複雑になります。最初からそのプログラムを作成すると全体の見通しが悪くなってしまいます。そこで、最初はSPI通信の動作確認を行うためのシンプルなプログラムを作成することにします。
センサのメモリマップを見ると、0xD0番地にセンサのIDが保存されています。ここに保存されている値は0x60で固定となっています。
そこで、0xD0番地のデータをSPI通信で読み取るプログラムを作成し、0x60が正しく取得できるか確認することによりSPI通信の動作確認を行いたいと思います。
ところで、プログラムで0xD0番地のデータを読み取るのはいいとしても、まだLCDモジュールは動いていませんので、読み取った値をLCDモジュールに表示して確認することはできません。
そこでセンサから取得した値を確認するために、MPLABX IDEの「デバッグ機能」を使用します。デバッグ機能については、実際に使用する際に詳しく説明しますので、ここでは簡単に説明します。
今まで、PICマイコンを動作させる場合の手順は、MPLABX IDE上でプログラムを作成→ビルド→PICマイコンに書き込み→動作、というステップでした。
このような手順では、PICマイコンが思った通りに動作する場合は問題ないですが、うまく動かない場合、PICマイコンの中で何が起こっているのか知りたいですよね。
MPLABX IDEのデバッグ機能を使うと、PICマイコン内部で動作しているプログラムを指定した行で動作を止めたりして、その結果を確認しながら動作させることができます。
言葉ではなかなかイメージがつかないと思いますので、プログラム作成後、実際に手を動かして確認してみてください。
使用するピンの確認
SPI通信を行うために、PICマイコンとセンサモジュールは以下のように接続しました。
このように接続すると、PICマイコン側のSPI通信用のピンは以下のように割り当てることになります。
ピン番号 | ポート名 | SPI信号線 | PICマイコン入出力設定 |
---|---|---|---|
15 | RC4 | CSB(チップセレクト) | 出力 |
16 | RC5 | MOSI(マスター→スレーブ) | 出力 |
17 | RC6 | MISO(スレーブ→マスター) | 入力 |
18 | RC7 | SCK(クロック) | 出力 |
SPI通信は、RC4〜RC7のピンをデジタル制御しながら行います。プログラムではこれらのピンは、
LATC4、RC6 (またはLATCbits.LATC4、PORTCbits.RC6)
などの記述になりますが、どのピンがどの信号線か分かりづらくなりますので、以下のように#defineすることにします。
// SPI通信ピン定義
#define SPI_SCK LATCbits.LATC7 // クロック
#define SPI_MISO PORTCbits.RC6 // スレーブ→マスター
#define SPI_MOSI LATCbits.LATC5 // マスター→スレーブ
#define SPI_CSB LATCbits.LATC4 // チップセレクト
PICマイコンのピン入出力設定は、SCK(クロック)、MOSI(マスター→スレーブ)、SS(スレーブセレクト/チップセレクト)は出力、MISO(スレーブ→マスター)は入力になります。PICマイコン全般に言えることですが、出力値制御はLATレジスタ、入力値制御はPORTレジスタを使用します。そのため、MISOのみPORTCレジスタ、それ以外はLATCレジスタを使用しています。
0xD0番地の読み出し
SPI通信で0xDO番地の読み出す場合、以下のように通信を行います。
また、SPI通信手順は以下のようになります。
SPI通信の基本関数作成
SPI通信手順が確認できたところで、プログラムを作成します。
SPI通信は以下の部分の8ビット送信と8ビット受信の部分を関数として作成しておきます。
8ビット送信関数
8ビット送信の部分は、以下のようにプログラムを作成します。8ビットの送信と受信の関数内部では、チップセレクト信号の制御は行わないことにします。チップセレクト信号はこの関数を使用する側で制御することにします。
この図のように、
- SCKピン(クロック)を0にする
- MOSIピン(マスター→データ)に送信するビットデータをセットする
- SCKピン(クロック)を1にする
という制御を8回繰り返せばSPIデータ送信ができます。プログラムとしては以下のように作成してみました。
//
// SPIデータ8ビット書き込み
// SCK/MOSI制御のための関数であるため
// スレーブセレクト信号はこの関数の前後で制御すること
void spiSend8bit(uint8_t data) {
// 8ビット分繰り返す
for (int8_t i=7; i>=0; i--) {
// (1)クロックを0にする
SPI_SCK = 0;
// (2)MOSIにデータをセットする
if( data & (1<<i) ) {
SPI_MOSI = 1;
} else {
SPI_MOSI = 0;
}
// (3)クロックを1にする
SPI_SCK = 1;
}
}
2点補足いたします。
1点目は変数型についてです。基礎編と応用編では、変数の宣言として一般的なC言語(ANSI C)に沿って「unsigned char」などを使用して変数宣言していました。実践編では、変数のビット数が分かりやすいint_t型を使用することにしました。なお、この変数型を使用する場合、stdint.hをインクルードする必要があります。
以下はその変数型になります。
変数型 | 意味 |
---|---|
int8_t | 符号付き8ビット |
uint8_t | 符号なし8ビット |
int16_t | 符号付き16ビット |
uint16_t | 符号なし16ビット |
int32_t | 符号付き32ビット |
uint32_t | 符号なし32ビット |
符号付きが「int」(integer)、符号なしが「uint」(unsigned integer)、数字がビット数を表しますので、かなり分かりやすいと思います。またArduinoではこの変数型を使用することが一般的なようです。
2点目はビット演算についてです。最近のiOSやAndroidなどのプログラミングではあまりビット演算が必要な場面は出てこないと思います。一方で電子工作で使用するPICマイコンやArduinoでは、意外にビット演算が必要な場合があります。
実践編のプログラムの中では、「<<」や「|」などの記号がよく出てきますので、よくわからない場合はもう一度ビット演算全般を復習されてみてください。
8ビット受信関数
8ビット受信の部分は、以下のようにプログラムを作成します。
この図のように、
- SCKピン(クロック)を0にする
- SCKピン(クロック)を1にする(この段階でスレーブのデータが読めるようになっている
- MISO(スレーブ→マスター)のデータを読む
という制御を8回繰り返せばSPIデータ送信ができます。プログラムとしては以下のように作成してみました。
//
// SPIデータ8ビット読み込み
// SCK/MOSI制御のための関数であるため
// スレーブセレクト信号はこの関数の前後で制御すること
//
uint8_t spiReceive8bit(void) {
// 受信データ格納変数
uint8_t read_data = 0;
// 8ビット分繰り返す
for (int8_t i=7; i>=0; i--) {
// 受信データ変数を1ビット左シフト
read_data <<= 1;
// (1)クロックを0にする
SPI_SCK = 0;
// (2)クロックを1にする
SPI_SCK = 1;
// (3)この時点でセンサからのデータを読めるので、MISOのピン状態を読む
if(SPI_MISO){
read_data |= 1;
}
}
// 受信したデータを返す
return read_data;
}
8ビットの送信、受信関数ができましたので、これらの関数を利用して0xD0番地のデータを読み取るプログラムを作成します。
更新履歴
日付 | 内容 |
---|---|
2018.4.27 | 新規投稿 |
2019.4.20 | ソースコード文字化け修正 |
お世話になります
// 受信データ変数を1ビット左シフト
read_data <<= 1;
の個所ですが
1ビットシフトしたら2番目に行くのではと思って
シフト無しで確認しても結果は同じでした
初期値は0でも1シフトでも同じという事で良いんですかね
デバックが使えだして理解が深まりそうです
すみません、ご質問の内容が的確にわからないのでうまく回答できませんが、
以下のコードでBME280からSPI通信でビットデータ受信しますので、
次のコードで次のビットデータを読むために左シフトする必要があります。
これを8回繰り返してBME280からSPI通信で8ビット受信することになります。
返信ありがとうございます
昨日やってたことが今日見たら分からない状況で
酒飲みながら朦朧としながらやってるんで
物凄い勘違いしてたような気がします
それでは何か質問ありましたらコメントいただければと思います。
読んでいて自己解決しました。
ビット位置をずらすためですね。
申し訳ありませんでした。
上記プログラム(8ビット受信関数)で初歩的ではありますが質問があります。
14行名
// 受信データ変数を1ビット左シフト
read_data <<= 1; と記載がありますがこの理由を詳しく聞きたく。