このページで示したロボットの倒立振子の検討の記録の続きのページです。
なお、EEPROMの内容は、このページで作成した内容を使っている。
で示したように振動を止めることができませんでした。| 項目 | Master | Slave |
|---|---|---|
| クロック | 生成する(出力端子) | 受け取る(受信端子) |
| CS制御 | 制御する(出力端子) | 選択される(受信端子) |
| 通信開始 | できる | できない |
| 通信速度 | 決定する | 従う |
| MOSI(Master Out Slave In) | 送信 | 受信 |
| MISO(Master In Slave Out) | 受信 | 送信 |
| CN10 端子番号 | PIC32MXの端子番号 | 変更前のUART1 | 変更後のSPI(スレーブ) | 拡張基板内 コネクタ番号 | Raspberry ピン番号とSPI端子(マスター) |
|---|---|---|---|---|---|
| 1 | 18 RB9 | RTS(出力) | RB9を SS2 イネーブル入力端子にPPSで変更 | 1 | Pin24 GPIO8 (CE0) |
| 2 | 28 | Vcc(3.3v) | Vcc(3.3v) | ||
| 3 | 27 | GND | GND | 2 | GND |
| 4 | 12 RA4 | RX(入力) | RA4をSDI2(入力)にPPSで変更 | 3 | Pin19 GPIO10 (MOSI) |
| 5 | 11 RB4 | TX(出力) |
RB4を単なる入力端子に変更 SCLK2 (PIC32のRB15をPPSで変更した26番ピンにジャンパー接続) | 4 | Pin23 GPIO11 (SCLK) |
| 6 | 17 RB8 | CTS(入力) | RB8をSDO2(出力)にPPSで変更 | 5 | Pin21 GPIO9 (MISO) |
|
|
[Raspberry Pi 3 Model A+]と[UMEHOSHI ITA]を乗せたモータ付き台車で、 拡張ボードでSPI用のコネクタを追加した回路結線図&配置図 ![]() |
suzuki@raspberrypi:~ $ ls /dev/spidev* /dev/spidev0.0 /dev/spidev0.1 suzuki@raspberrypi:~ $ suzuki@raspberrypi:~ $ lsmod | grep spi spidev 20480 0 spi_bcm2835 20480 0 suzuki@raspberrypi:~ $/dev/spidev0.0と /dev/spidev0.1は、それぞれが SPIバス0のCE0がGPIO8(Pin24)で使えることと、CE1のGPIO7(Pin26)が使えることを示している。
import spidev
import time
spi = spidev.SpiDev() # SPI操作オブジェクト生成
spi.open(0, 0) # bus=0 device=0 (CE0) でSPIオープン
spi.max_speed_hz = 7812500 # 7.8 MHzSPIクロック
spi.mode = 0 # クロック信号(SCLK)はLow待機、LowからHighに立ち上がる瞬間でデータを読み取り
v=0 # 送信データ(0,1,2・・・・と増やした値を Enterキー操作で少しずつ送る)
while True:
send_data = [v] # 例えばビッグエンディアンで0x15AFを送るなら[0x15, 0xAF]のリストで送る。決まってないが一般の受信側のオーダーで送る。
input("Enter>") # Enterで送信へ進む
print("Send :", send_data)
recv_data = spi.xfer2(send_data)# 送受信
# print("Send :", send_data) # (send_dataの内容は、受信データ置き換わる挙動もある)
print("Recv :", recv_data)
time.sleep(0.1)
v += 1 # 送信データの後進
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include <proc/p32mx270f256b.h>
#include <sys/attribs.h>//割込み関係の定義
int spi_out_v = 0x0ff; // SPI出力用データ
/*
PIC32MX270F256B-50I/SP (SP は 28pin SPDIP パッケージ)のUART1を、
18番ピンをRTS(出力)、12ピンをRX(入力)、11ピンをTX(出力)、17ピンRB8をCTS(入力)
として使っているが、 これを、次のSPI2のスレーブモードに設定し直す
18番ピンをCS GPIO入力、12ピンをSDI2(入力)、11ピンRB5をSDO2(出力)、17ピンをデジタル入力
そのためのPPSは「ピン配線設定」関数。つまり **「配線設定」**です。
*/
void PPS_Init_SPI2_Slave(void)
{
/* ---------- UART1のフロー制御(CTS/RTS)を解除 ---------- */
U1MODEbits.UEN = 0b00; // UART1はTX/RXのみ使用(CTS/RTSピンを開放)
U1MODEbits.ON = 0; // UART1自体を使わない場合は停止
ODCBbits.ODCB9 = 0; // RB9デフォルト設定のオープンドレイン無効
CNPUBbits.CNPUB9 = 0;//CNPUB (Change Notice Pull-up B) レジスタの第9ビットを0
//(これにより、RB9ピンの内部プルアップ抵抗を無効)
CNPDBbits.CNPDB9 = 0;//CNPDB (Change Notice Pull-down B)レジスタの第9ビットを 0
//(これにより、RB9ピンの内部プルダウン抵抗を無効)
LATBbits.LATB9 = 1; // RB9のRTS(出力)をHiにする
TRISBbits.TRISB9 = 1;// RB9を入力ピンにする (基板のパターンが繋がっているため)
LATBbits.LATB4 = 1; // RB4を1にする。(必要ないかも?)
TRISBbits.TRISB4 = 1; // RB4を入力用に指定
ODCBbits.ODCB4 = 0; // デフォルト設定のオープンドレイン無効
CNPUBbits.CNPUB4 = 0;//内部プルアップ抵抗を無効
CNPDBbits.CNPDB4 = 0;//内部プルダウン抵抗を無効
TRISAbits.TRISA4 = 1; // RA4 (pin12) SDI2入力へ
ANSELBbits.ANSB15 = 0; // 26番ピンのRB15をデジタルモードに設定
TRISBbits.TRISB15 = 1; // スレーブの場合は入力、マスターなら0(出力)に設定
TRISBbits.TRISB8 = 0; // RB8を出力用に指定
CNPUBbits.CNPUB8 = 0;//内部プルアップ抵抗を無効
/* ---------- PPS設定アンロック ---------- */
SYSKEY = 0xAA996655; // 書き込み保護解除キー1
SYSKEY = 0x556699AA; // 書き込み保護解除キー2
CFGCONbits.IOLOCK = 0; // PPSレジスタのロック解除
/* ---------- 入力PPS設定 ---------- */
SS2R = 0b0100; // SS2入力をRB9へ
SDI2R = 0b0010; // SDI2入力をRA4へ
/* ---------- 出力PPS設定 ---------- */
RPB8R = 0b0100; // SDO2へ
asm("NOP");
/* ---------- PPS再ロック ---------- */
CFGCONbits.IOLOCK = 1;
SYSKEY = 0; // 保護再有効
}
/*
SPI2に関する周辺機能設定の初期設定
* クロック極性、スレーブ設定、割り込みなど、ここだけ変更する可能性を考慮して
* 「ピン配線設定」のPPS_Init_SPI2_Slaveを別関数にしている。
*/
void Init_SPI2_Slave(void)
{
_RB5 = 1; // D1 LED の初期点灯設定
PPS_Init_SPI2_Slave(); // PIC32MX270F256B-50I/SP のSPI2スレーブ用端子にピン割り当てを変更
SPI2CON = 0; // SPI2制御レジスタ初期化
SPI2STAT = 0; // ステータス初期化
/* ---------- SPIモード設定 ---------- */
SPI2CONbits.MSTEN = 0; // 0 = スレーブモード
SPI2CONbits.SSEN = 1; // SS有効 Slave Select Enable (Slave mode) bit
SPI2CONbits.MODE16 = 0; // 8bit通信
SPI2CONbits.MODE32 = 0; // 32bit通信無効
/* SPIモード0設定 (多くのマスターが使用) */
SPI2CONbits.CKP = 0; // クロックアイドルLow
SPI2CONbits.CKE = 1; // クロック立上りでデータ更新
SPI2CONbits.SMP = 0; // スレーブでは通常0
SPI2BUF; // バッファをダミー読み出し
SPI2STATbits.SPIROV = 0; // 受信オーバーフロークリア
// SPI用Enhanced Buffer(ENHBUF)を0(デフォルト)に指定(重要)
SPI2CONbits.ENHBUF = 0;
SPI2CONbits.SRXISEL = 1; //01受信バッファが1byteに指定
SPI2CONbits.ON = 1; // SPI2モジュール有効
SPI2BUF = spi_out_v;// 最初の送信データのセット(重要)
}
void timer4() // SPIスレーブ のポーリング処理の確認用タイマー処理
{
static int count = 0;
if(++count % 1000 != 0) return;
// count が 1000の倍数時だけ(0.5秒ごと)に以下を実行
_RB5 = ! _RB5;// このタイマーの動作確認用の D1 LED 点滅の出力を反転
if (SPI2STATbits.SPIRBF == 1) { // SPI受信バッファが一杯になったら
// ここを通るなら、SPI受信ができている(配線とSPI設定はOK)
uint8_t rxData = SPI2BUF; // 受信データを読み出す(これでSPIRBFが0に戻る)
_clear_beep_code();
_debug_hex8(0, rxData , 1); // 受信データ確認
spi_out_v-=1;
while(SPI2STATbits.SPITBE == 0); // 送信バッファ空待ち
SPI2BUF = spi_out_v;// 次の送信データのセット
}
if (SPI2STATbits.SPIROV == 1) { // エラーフラグが立っていないか
SPI2STATbits.SPIROV = 0; // 一旦クリア
_debug_hex16(0, SPI2BUF, 1);
}
}
// UMEHOSHI ITA のRAMプログラムの設定希望プログラム(0x80005000番地より起動)
__attribute__((address( 0x80005000 ))) void start (void);
void start()
{
_clear_beep_code(); // _debug_hex? 関数利用の初期化
_UM_PTR_GOTO_BEEP = NULL; // debug_hex?の出力をループしない設定
Init_SPI2_Slave();// SPI2の初期化
// デフォルトで0.00005秒ごとに呼び出される関数に、SPIスレーブ のポーリング処理のtimer4を設定
_HANDLES[_IDX_TIMER_4_FUNC] = timer4;
T4CONbits.ON = 1;// timer4割込みオン(SPIスレーブ のポーリングの起動)
_RB5 = 0; // D1 LED の消灯
_debug_hex4(0,0x0f,1);// _debug_hex? の動作確認用
_send_string("start END\r\n");//起動関数終了の表示
}
上記はTimer4のインターバルタイマーの割り込みを使い、この繰り返しの中でSPIの受信を監視して、受信処理をする例です。
void __ISR(_SPI2_VECTOR, IPL3SOFT) SPI2_Handler(void)
{
((void (**)(void) )_PTR_HANDLERS)[_IDX_SPI2_FUNC]();
}
つまり、_PTR_HANDLERS)[_IDX_SPI2_FUNC]に、ユーザー作成の割り込み処理のポイントを記憶しておいて、それを実行させるコードです。#define _IDX_PPS_INIT_SPI2_SLAVE 70 // ★ SPI2のPPS 設定 PPS_Init_SPI2_Slave #define _PPS_Init_SPI2_Slave() (((void (**)(void) )_PTR_HANDLERS)[_IDX_PPS_INIT_SPI2_SLAVE]()) #define _IDX_INIT_SPI2_SLAVE 71 // ★ SPI2の初期設定 割り込みを含む初期化 Init_SPI2_Slave #define _Init_SPI2_Slave() (((void (**)(void) )_PTR_HANDLERS)[_IDX_INIT_SPI2_SLAVE]()) #define _IDX_SPI2_FUNC 72 // ★ SPI2割り込みで呼ぶ関数記憶用の添え字そして、上記の_PTR_HANDLERS[_IDX_PPS_INIT_SPI2_SLAVE]に記憶する次のデフォルト関数を次のように定義しています。
/*
PIC32MX270F256B-50I/SP (SP は 28pin SPDIP パッケージ)のUART1を、
18番ピンをRTS(出力)、12ピンをRX(入力)、11ピンをTX(出力)、17ピンRB8をCTS(入力)
として使っているが、 これを、次のSPI2のスレーブモードに設定し直す
18番ピンをCS GPIO入力、12ピンをSDI2(入力)、11ピンRB5をSDO2(出力)、17ピンをデジタル入力
そのためのPPSは「ピン配線設定」関数。つまり **「配線設定」**です。
*/
void PPS_Init_SPI2_Slave(void)
{
/* ---------- UART1のフロー制御(CTS/RTS)を解除 ---------- */
U1MODEbits.UEN = 0b00; // UART1はTX/RXのみ使用(CTS/RTSピンを開放)
U1MODEbits.ON = 0; // UART1自体を使わない場合は停止
ODCBbits.ODCB9 = 0; // RB9デフォルト設定のオープンドレイン無効
CNPUBbits.CNPUB9 = 0;//CNPUB (Change Notice Pull-up B) レジスタの第9ビットを0
//(これにより、RB9ピンの内部プルアップ抵抗を無効)
CNPDBbits.CNPDB9 = 0;//CNPDB (Change Notice Pull-down B)レジスタの第9ビットを 0
//(これにより、RB9ピンの内部プルダウン抵抗を無効)
LATBbits.LATB9 = 1; // RB9のRTS(出力)をHiにする
TRISBbits.TRISB9 = 1;// RB9を入力ピンにする (基板のパターンが繋がっているため)
LATBbits.LATB4 = 1; // RB4を1にする。(必要ないかも?)
TRISBbits.TRISB4 = 1; // RB4を入力用に指定
ODCBbits.ODCB4 = 0; // デフォルト設定のオープンドレイン無効
CNPUBbits.CNPUB4 = 0;//内部プルアップ抵抗を無効
CNPDBbits.CNPDB4 = 0;//内部プルダウン抵抗を無効
TRISAbits.TRISA4 = 1; // RA4 (pin12) SDI2入力へ
ANSELBbits.ANSB15 = 0; // 26番ピンのRB15をデジタルモードに設定
TRISBbits.TRISB15 = 1; // スレーブの場合は入力、マスターなら0(出力)に設定
TRISBbits.TRISB8 = 0; // RB8を出力用に指定
CNPUBbits.CNPUB8 = 0;//内部プルアップ抵抗を無効
/* ---------- PPS設定アンロック ---------- */
SYSKEY = 0xAA996655; // 書き込み保護解除キー1
SYSKEY = 0x556699AA; // 書き込み保護解除キー2
CFGCONbits.IOLOCK = 0; // PPSレジスタのロック解除
/* ---------- 入力PPS設定 ---------- */
SS2R = 0b0100; // SS2入力をRB9へ
SDI2R = 0b0010; // SDI2入力をRA4へ
/* ---------- 出力PPS設定 ---------- */
RPB8R = 0b0100; // SDO2へ
asm("NOP");
/* ---------- PPS再ロック ---------- */
CFGCONbits.IOLOCK = 1;
SYSKEY = 0; // 保護再有効
}
同様で、my_option.cに_PTR_HANDLERS[__IDX_PPS_INIT_SPI2_SLAVE]に記憶する次のデフォルト関数を次のように定義しています。
/*
SPI2に関する周辺機能設定の初期設定
* クロック極性、スレーブ設定、割り込みなど、ここだけ変更する可能性を考慮して
* 「ピン配線設定」のPPS_Init_SPI2_Slaveを別関数にしている。
*/
void Init_SPI2_Slave(void)
{
_RB5 = 1; // D1 LED の初期点灯設定
PPS_Init_SPI2_Slave(); // PIC32MX270F256B-50I/SP のSPI2スレーブ用端子にピン割り当てを変更
SPI2CON = 0; // SPI2制御レジスタ初期化
SPI2STAT = 0; // ステータス初期化
/* ---------- SPIモード設定 ---------- */
SPI2CONbits.MSTEN = 0; // 0 = スレーブモード
SPI2CONbits.SSEN = 1; // SS有効 Slave Select Enable (Slave mode) bit
SPI2CONbits.MODE16 = 0; // 8bit通信
SPI2CONbits.MODE32 = 0; // 32bit通信無効
/* SPIモード0設定 (多くのマスターが使用) */
SPI2CONbits.CKP = 0; // クロックアイドルLow
SPI2CONbits.CKE = 1; // クロック立上りでデータ更新
SPI2CONbits.SMP = 0; // スレーブでは通常0
SPI2BUF; // バッファをダミー読み出し
SPI2STATbits.SPIROV = 0; // 受信オーバーフロークリア
// SPI用Enhanced Buffer(ENHBUF)を0(デフォルト)に指定(重要)
SPI2CONbits.ENHBUF = 0;
SPI2CONbits.SRXISEL = 1; //01受信バッファが1byteに指定
/* ---------- 割り込み設定 ---------- */
IFS1bits.SPI2RXIF = 0; // 受信割り込みフラグクリア
IEC1bits.SPI2RXIE = 1; // SPI2受信割り込み許可
//IEC1bits.SPI2TXIE = 1; // SPI2送信割り込み許可
IEC1bits.SPI2EIE = 1; // SPI2エラー割り込み許可
IPC9bits.SPI2IP = 3; // 優先度(Priority)を 3 に設定 (1?7)
IPC9bits.SPI2IS = 0; // 副優先度(Sub-priority)を 0 に設定 (0?3)
SPI2CONbits.ON = 1; // SPI2モジュール有効
}
以上の関数定義は、my_sys.cのinit_handle_area関数内に、次のコード追加で行っています。
extern void PPS_Init_SPI2_Slave(void);
extern void Init_SPI2_Slave(void);
extern void SPI2_Slave_func(void);
handlers[_IDX_PPS_INIT_SPI2_SLAVE]=(void *)PPS_Init_SPI2_Slave; // SPI2利用のためのPPS設定
handlers[_IDX_INIT_SPI2_SLAVE]=(void *)Init_SPI2_Slave; // SPI2の初期化デファルト関数登録
handlers[_IDX_SPI2_FUNC]=(void *)SPI2_Slave_func; // SPI2受信割り込みデファルト
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include <proc/p32mx270f256b.h>
#include <sys/attribs.h>//割込み関係の定義
int spi_out_v = 0x0ff; // SPI出力用データ
void SPI2_Slave_func(void)// スレーブ用割り込み処理から呼ばれる
{
/* ---- 受信割り込み ---- */
if (IFS1bits.SPI2RXIF)// SPI2RXIF = 受信バッファにデータが入った
{
uint8_t data;
data = SPI2BUF; // SPI受信データを読む
// 読まないとSPIROV(オーバーフロー)が発生する
/* ここで data を処理する */
_clear_beep_code();
_debug_hex8(0, data, 1); // 受信データ確認
spi_out_v-=1;
while(SPI2STATbits.SPITBE == 0); // 送信バッファ空待ち
SPI2BUF = spi_out_v;// 次の送信データのセット
}
/* ---- エラー割り込み ---- */
if (IFS1bits.SPI2EIF)
{
SPI2STATbits.SPIROV = 0; // 受信オーバーフロー解除
IFS1bits.SPI2EIF = 0; // エラーフラグクリア
}
}
// UMEHOSHI ITA のRAMプログラムの設定希望プログラム(0x80005000番地より起動)
__attribute__((address( 0x80005000 ))) void start (void);
void start()
{
_clear_beep_code(); // _debug_hex? 関数利用の初期化
_UM_PTR_GOTO_BEEP = NULL; // debug_hex?の出力をループしない設定
_HANDLES[_IDX_SPI2_FUNC] = (void *)SPI2_Slave_func;// SPI割り込み関数の登録
((void (**)(void) )_PTR_HANDLERS)[_IDX_INIT_SPI2_SLAVE]();// SPI2の初期化
SPI2BUF = spi_out_v;// 最初の送信データのセット(重要)
_RB5 = 0; // D1 LED の消灯
_debug_hex4(0,0x0f,1);// _debug_hex? の動作確認用
_send_string("start END\r\n");//起動関数終了の表示
}