LoginSignup
1
1

More than 3 years have passed since last update.

TWELITE で SPI を試す

Last updated at Posted at 2020-08-23

前回の
「TWELITE で Lチカの続き(シリアルUART出力)」
https://qiita.com/nanbuwks/items/7194db5680b65c9cc2bd
を元に、SPI 接続の ADコンバータ、MCP3008 を接続してみます。

無線通信などは行わず、先の Lチカ+シリアル UART 出力プログラムに SPI 接続に必要な最低限の記述のみ加えます。

接続

IMG_20200824_065326144.jpg

IMG_20200824_063529806.jpg

MCP3008 はアナログ入力ポートが CH0 〜 CH7 の 8チャンネルあります。それぞれ 10 bit の分解能で AD 変換を行い、SPI でマイコンなどに取り込むことができます。テストのため、CH0 に 1kΩ の抵抗をつけて、VDD につないでいます。
右側に配置しているスイッチは TWELITE の書き込みのために PRG と R (RESET) につなげています。

また、ブレッドボード外にLEDを接続する配線として、ピン番号15番(シルク表記12)とGNDにLEDを接続しています。

SPI信号名 MCP3008 PIN MCP3008 信号名 ケーブル TWELITE DIP基板ピン番号 TWELITE DIP基板上のシルク表記 TWELITE IO名
VDD 16 VDD オレンジ 28 VCC VCC
15 VREF VDDに接続
14 AGND DGNDに接続
SCK 13 CLK 黄色 6 C DO0
MISO 12 DOUT 7 I DO1
MOSI 11 DIN 5 18 DIO18
CS 10 CS/SHDN 8 19 DIO19
GND 9 DGND GND GND GND

TWELITE の SPI関数

以下のサイトから、必要な設定をピックアップします。

https://sdk.twelite.info/hw-api-rifurensu/perifuraru/spi
https://mono-wireless.com/jp/tech/Programming/PERIPH_SPI.html

ピックアップした主要関数は以下の通りです。

  • vAHI_SpiConfigure()
  • vAHI_SpiSelect()
  • vAHI_SpiStartTransfer()
  • bAHI_SpiPollBusy()
  • u32AHI_SpiReadTransferXX()
  • vAHI_SpiStop
  • vAHI_SpiDisable();

SPI 主要関数を MCP3008 に合わせて使う

MCP3008についての詳細は
「ESP32 と MCP3008 で SPI の勉強」
https://qiita.com/nanbuwks/items/9a9169a2afbad95ae7ca
を参照してください。

vAHI_SpiConfigure

SPIリソースを確保し、初期化します。
必要な引数について、設定値は以下のようにします。

設定値 設定値の意味
u8SlaveEnable 1 SPI_CSEL として DIO19 のみ使用する(SPI_CSEL0のみの使用)
bLsbFirst FALSE MSBから転送開始
bPolarity FALSE クロックを反転しない(SPI MODE0)
bPhase TRUE 立ち上がりエッジを用いる(SPI MODE0)
u8ClockDiviser 8 クロックを1MHzで駆動
bInterruptEnable FALSE SPI転送終了時の割り込みを使用しない
bAutoSlaveSelect FALSE CSELピンの制御を vAHI_SpiSelect を使って行う

vAHI_SpiSelect , vAHI_SpiStop

SPIのCSEL は TWELITE では SPI_CSEL0,SPI_CSEL1,SPI_CSEL2 の3つが使え、それぞれ DIO19, DIO14(DIO0に変更可能),DIO15(DIO1 に変更可能)に割り当てられています。
今回は SPI_CSEL0 を使うことにし、必要な引数について、設定値は以下のようにします。

設定値 設定値の意味
u8SlaveEnable 1 SPI_CSEL0 ( DIO19 ) ピンを使ってSPIをセレクト状態にする

また、セレクト解除には以下のようにするか、vAHI_SpiStop を呼び出します。

設定値 設定値の意味
u8SlaveEnable 0 SPI_CSELピンの解除

vAHI_SpiStartTransfer , bAHI_SpiPollBusy()

vAHI_SpiStartTransfer で SPI スレーブにデータを送信します。
必要な引数について、設定値は以下のようにします。

設定値 設定値の意味
u8CharLen 4 転送長のビット長(5ビット) - 1 を指定。
u32Out 0x00000018 32bitバッファに送信するデータをLSB詰めで格納する

スタートビットとして1ビット、それに続く4ビットでMCP3008の測定モードとチャンネルを設定、合計5ビット送信します。

bit4 bit3 bit2 bit1 bit0
start bit SGL/IFF D2 D1 D0
1 1 0 0 0

MCP3008へ入力するbit3~bitについて、SGL/DIFFは シングルエンド入力と作動入力のビットです。1ならシングルエンド、0なら作動入力となります。
今回はシングルエンド入力を使います。その場合、D2/D1/D0 はチャンネル指定、CH0なら D2=0,D1=0,D0=0。CH1ならD2=0,D1=0,D0=1となります。
スタートビットを含めて 0b11000 となるので、32ビットに直し 0x00000018 として転送します。

転送終了を待たずに制御が帰るので、bAHI_SpiPollBusy() でチェックします。転送完了になると bAHI_SpiPollBusy() が false になります。

u32AHI_SpiReadTransferXX

SPIスレーブデバイスから読み出します。
vAHI_SpiStartTransfer の結果の読み出しデータのサイズごとに

  • uint32 u32AHI_SpiReadTransfer32(void);
  • uint16 u16AHI_SpiReadTransfer16(void);
  • uint8 u8AHI_SpiReadTransfer8(void);

が用意されています。データが満たない場合はLSB側に詰められます。
今回は以下のように 11bit を読み出すので、

bit10 bit9 bit8 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
0 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0

uint16 u16AHI_SpiReadTransfer16(void);

を使えばいいのですが、デバッグなどの経緯で

uint32 u32AHI_SpiReadTransfer32(void);

を使い、下位10bitを使うことにしました。

vAHI_SpiDisable

vAHI_SpiConfigure() で確保した SPI のリソースを開放します。

コード

上記のコードをまとめて以下のようにしました。


#include <AppHardwareApi.h>
#include "utils.h"
#include "ToCoNet.h"
#include "ToCoNet_mod_prototype.h"
#include "serial.h" 
#include "fprintf.h"
#define LED 12

tsFILE sSerial0Stream;
static tsSerialPortSetup sSerial0;


static void vProcessEvCore(tsEvent *pEv, teEvent eEvent, uint32 u32evarg)
{
    static int counter=0;
    uint32 mcp3008result;
    int SPIresult=0;
    if (eEvent == E_EVENT_TICK_TIMER) {      // 4ms timer
        counter++;
        if (  0 == counter % 500 ) {
            vPortSetHi(LED);
            vfPrintf(&sSerial0Stream, "LED High\n\r");
            // SPI リソース初期化
            vAHI_SpiConfigure(1,     // use SPISEL0
                              FALSE, // MSB FIRST
                              FALSE, // SPI mode 0
                              FALSE,  // SPI mode 0
                              8,     // 1MHz clock
                              FALSE, // no use interrupt SPI transfer end
                              FALSE);  // CSEL control by vAHI_SpiSelect
            vAHI_SpiSelect(1); // SPISEL0 スタート
            vAHI_SpiStartTransfer(
                                4, // 5bit - 1 
                                0x0000018L ); // 0b1 (スタートビット) + 0b1000 (シングルエンド入力、CH0)
            int i;

            for ( i=0;  bAHI_SpiPollBusy(); i++) // timeout 付 Busy チェック
            {
                if ( 1000 < i )
                {
                    vfPrintf(&sSerial0Stream, "error! MCP3008 SPI timeout\n\r");
                    break;
                }
                vWait(1);
            }
            vAHI_SpiStartTransfer(
                                11, // 12bit - 1 ( 1 : 1 bit + 0 : 2bit + ADvalue : 10bit ) 
                                0x00000000L );
            for ( i=0;  bAHI_SpiPollBusy(); i++) // timeout 付 Busy チェック
            {
                if ( 1000 < i )
                {
                    SPIresult=-1;
                    break;
                }
                vWait(1);
            }
            if ( 0 == SPIresult ) {
                mcp3008result = u32AHI_SpiReadTransfer32();
                vfPrintf(&sSerial0Stream, "SPI result=%b ADvalue=%d\n\r",mcp3008result,mcp3008result & 0x3FF);
            } else {
                vfPrintf(&sSerial0Stream, "error! MCP3008 SPI timeout\n\r");
            }
            vAHI_SpiSelect(0); // SPISEL0 処理終了
            vAHI_SpiDisable(); // SPI リソース開放

        }
        if ( 250 == counter % 500 ) {
            vPortSetLo(LED);
            vfPrintf(&sSerial0Stream, "LED Low\n\r");
        }
    }
}


void cbAppColdStart(bool_t bStart)
{
    if (!bStart) {
    } else {
        vPortAsOutput(LED);  // GPIO set
        ToCoNet_Event_Register_State_Machine(vProcessEvCore); // add Event handler
        // serial init

        static uint8 au8SerialBuffTx[32];
        static uint8 au8SerialBuffRx[16];

        sSerial0.pu8SerialRxQueueBuffer = au8SerialBuffRx;
        sSerial0.pu8SerialTxQueueBuffer = au8SerialBuffTx;
        sSerial0.u32BaudRate = 115200;
        sSerial0.u16AHI_UART_RTS_LOW = 0xffff;
        sSerial0.u16AHI_UART_RTS_HIGH = 0xffff;
        sSerial0.u16SerialRxQueueSize = sizeof(au8SerialBuffRx);
        sSerial0.u16SerialTxQueueSize = sizeof(au8SerialBuffTx);
        sSerial0.u8SerialPort = E_AHI_UART_0;
        sSerial0.u8RX_FIFO_LEVEL = E_AHI_UART_FIFO_LEVEL_1;
        SERIAL_vInit(&sSerial0);

        sSerial0Stream.bPutChar = SERIAL_bTxChar;
        sSerial0Stream.u8Device = E_AHI_UART_0;

        vfPrintf(&sSerial0Stream, "Hello World!\n\r");

    }
}

void cbAppWarmStart(bool_t bStart) { return; }
void cbToCoNet_vRxEvent(tsRxDataApp *psRx) { return; }
void cbToCoNet_vTxEvent(uint8 u8CbId, uint8 bStatus) { return; }
void cbToCoNet_vNwkEvent(teEvent eEvent, uint32 u32arg) { return; }
void cbToCoNet_vHwEvent(uint32 u32DeviceId, uint32 u32ItemBitmap) { return; }
uint8 cbToCoNet_u8HwInt(uint32 u32DeviceId, uint32 u32ItemBitmap) { return FALSE; }
void cbToCoNet_vMain(void) { return; }


最初は vAHI_SpiStartTransfer で 5bit 送信した後にすぐに u32AHI_SpiReadTransfer32(void) で読み込みをしたのですが、有効な値が取れませんでした。あれれー?? と思っていろいろ試したら、

  1. vAHI_SpiStartTransfer で 5bit 送信して動作モードやチャンネルなどを送る
  2. bAHI_SpiPollBusy()で処理が終わるのを待つ
  3. vAHI_SpiStartTransfer で 12bit の 0 データを送信する
  4. bAHI_SpiPollBusy()で処理が終わるのを待つ
  5. u32AHI_SpiReadTransfer32() で 12bit 取得する

というようにしないといけませんでした。

image.png

MCP3008のデータシートでは10bits のAD変換データの頭に0が追加されて11bitsになるはずですが、TWELITEでは10が頭に追加されて12bitになっていました。

なお、bAHI_SpiPollBusy() の処理は Timeout 判定を入れています。SPIライブラリがブロッキングしっぱなしになる可能性があるかどうかまだ確認ができていないので、必要ないかも知れませんが念の為。

結果


LED Hello World!
LED Low
LED High
SPI result=101111111111 ADvalue=1023
LED Low
LED High
SPI result=101111111111 ADvalue=1023
LED Low
LED High
SPI result=101111111111 ADvalue=1023
LED Low
LED High
SPI result=101110110101 ADvalue=949
LED Low
LED High
SPI result=101111010101 ADvalue=981
LED Low
LED High
SPI result=100000000001 ADvalue=1
LED Low
LED High
SPI result=100000000000 ADvalue=0
LED Low
LED High

LEDの点滅と同時に CH0 の電圧をADの変換した値を出力します。CH0に接続した抵抗を、VDDからGNDに繋ぎ変えた時の値です。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1