Raspberry Pi 4のGPIOの速度を測定する
概要
Raspberry Pi 4とマイコンを使って、GPIOでどのくらいの通信速度が出せるかを実験・測定しました。
使用機材・言語
データ受信側:Raspberry Pi 4 (Raspberry OS、C++(better Cとして))
データ送信側:Renesas RX231 RSK(C言語、システムクロック: 54MHz)
通信環境・シーケンス
通信シーケンスは以下としました。
マイコン側 | 1. DATA書き込み 2. SYN: High書込み 4. SYNACK: High検出後、SYN: Low書込み 5. SYNACK: Low検出後、最初に戻り次のデータを書き込み |
RasPi側 | 2. SYN: High検出、DATA読み込み 3. DATA読み込み完了にて、SYNACK: High書込み 5. SYN: Low検出後、SYNACK: Low書込み、SYN: High待ち受けに戻る |
マイコン後側ピンアサイン
SYN: PE2 – D10_RXD12 – J4.1 →OUTPUT、高駆動出力
SYNACK: PE1 – D9_TXD12 – J4.2 →INPUT
DATA: PE0 – D8_SCK12 – J4.3 →OUTPUT、高駆動出出力
Raspberry Pi 4側ピンアサイン
SYN: GPIO 17 (INPUT)
SYNACK: GPIO 27 (OUTPUT)
DATA: GPIO 22 (INPUT)
マイコン側ソース
CS+ CCを使い作成。システム設定やペリフェラルはスマート・コンフィグレータで設定。
プロジェクト名はGPIOBurstTest
UserConfig.h
#ifndef USER_CONFIG_H
#define USER_CONFIG_H
//////////////////////////////////////////////////////////////////////////
// ピンアサイン
//////////////////////////////////////////////////////////////////////////
// 入力LOW
#define UC_LOW 0
// 入力HIGH
#define UC_HIGH 1
// RSK LED
#define UC_LED0 PORT1.PODR.BIT.B7
#define UC_LED0_ON UC_LOW
#define UC_LED0_OFF UC_HIGH
#define UC_LED1 PORT5.PODR.BIT.B0
#define UC_LED1_ON UC_LOW
#define UC_LED1_OFF UC_HIGH
#define UC_LED2 PORT5.PODR.BIT.B1
#define UC_LED2_ON UC_LOW
#define UC_LED2_OFF UC_HIGH
#define UC_LED3 PORT5.PODR.BIT.B2
#define UC_LED3_ON UC_LOW
#define UC_LED3_OFF UC_HIGH
// Burst通信用
// SYN - PE2 - D10_RXD12 - J4.1
#define UC_SYN PORTE.PODR.BIT.B2
#define UC_SYN_ON UC_HIGH
#define UC_SYN_OFF UC_LOW
// SYNACK - PE1 - D9_TXD12 - J4.2
#define UC_SYNACK PORTE.PIDR.BIT.B1
#define UC_SYNACK_ON UC_HIGH
#define UC_SYNACK_OFF UC_LOW
// DATA - PE0 - D8_SCK12 - J4.3
#define UC_DATA PORTE.PODR.BIT.B0
#define UC_DATA_ON UC_HIGH
#define UC_DATA_OFF UC_LOW
#endif
GPIOBurstTest.c
#ifdef __cplusplus
//#include <ios> // Remove the comment when you use ios
//_SINT ios_base::Init::init_cnt; // Remove the comment when you use ios
#endif
// Use Common Fit
#include "platform.h"
#include "r_cg_macrodriver.h"
// Use Config
#include "UserConfig.h"
void main(void);
#ifdef __cplusplus
extern "C" {
void abort(void);
}
#endif
void main(void)
{
UC_LED0 = UC_LED0_OFF;
UC_LED1 = UC_LED1_OFF;
UC_LED2 = UC_LED2_OFF;
UC_LED3 = UC_LED3_OFF;
UC_DATA = UC_DATA_ON;
unsigned char data1 = 1;
while(1)
{
// データ設定 SYN
UC_DATA = data1 ^= 1;
UC_SYN = UC_SYN_ON;
UC_LED0 = UC_LED0_ON;
// SYNACK待ち受け
while (1)
{
if (UC_SYNACK == UC_SYNACK_ON)
{
// UC_LED1 = UC_LED1_ON;
break;
}
nop();
}
// ACK処理
UC_SYN = UC_SYN_OFF;
// データ送信OK待ち受け
while (1)
{
if (UC_SYNACK == UC_SYNACK_OFF)
{
// UC_LED2 = UC_LED2_ON;
break;
}
nop();
}
// UC_LED0 = UC_LED0_OFF;
// UC_LED1 = UC_LED2_OFF;
// UC_LED2 = UC_LED2_OFF;
}
while(1)
{
nop();
}
}
#ifdef __cplusplus
void abort(void)
{
}
Raspberry Pi 4側ソース
Visual Studio 2019を使用し、Windowsからリモートビルドで使用。
GPIOにはWiringPiを使用。
通信時間の測定として、1,000,000万回ごとに時間を計測してコンソールに出力する。Printfが反応しなかったので、std::coutを使用。
待ち受けにusleep(0)をいれてみたが、速度が異常に遅くなったので、コメントアウト。
#include <wiringPi.h>
#include <iostream>
#include <unistd.h>
#include <time.h>
#define SYN 17
#define SYNACK 27
#define DATA 22
int main(void)
{
std::cout << "APP Start" << std::endl;
//wiringPiSetupSys();
wiringPiSetupGpio();
pinMode(SYN, INPUT);
pinMode(SYNACK, OUTPUT);
pinMode(DATA, INPUT);
digitalWrite(SYNACK, 0);
// 通信して取得したデータ
int data = 0;
// 時間計測関連
unsigned int sec;
int nsec;
double d_sec;
struct timespec start_time, end_time;
int timeCounter = 0;
const int timeCounterConst = 1000000;
while (true)
{
// 開始時間測定
if (timeCounter == 0)
{
timespec_get(&start_time, TIME_UTC);
}
//std::cout << "Wait SYN" << std::endl;
// SYNまち
while (true)
{
if (digitalRead(SYN) == 1)
{
//std::cout << "SYN OK" << std::endl;
break;
}
// NOP
}
// Data読み取り
data = digitalRead(DATA);
//std::cout << "SYN OK" << std::endl;
// SYNACK
digitalWrite(SYNACK, 1);
//std::cout << "Wait ACK" << std::endl;
// ACKまち
while (true)
{
if (digitalRead(SYN) == 0)
{
//std::cout << "ACK OK" << std::endl;
break;
}
// NOP
}
// SYNACK取り下げ
digitalWrite(SYNACK, 0);
timeCounter++;
// 終了時間測定
if (timeCounter >= timeCounterConst - 1)
{
// カウンタの初期化
timeCounter = 0;
timespec_get(&end_time, TIME_UTC);
sec = end_time.tv_sec - start_time.tv_sec;
nsec = end_time.tv_nsec - start_time.tv_nsec;
d_sec = (double)sec
+ (double)nsec / (1000 * 1000 * 1000);
std::cout << "time:" << d_sec << std::endl;
}
}
return 0;
}
実行結果
1,000,000回通信の実行時間は、平均で概ね1.66秒。通信速度にして、602,409 bpsとなりました。
ちなみにRaspberry側の待ち受けにusleep(0)をいれると、平均134.8秒で7,418 bpsとなります。この場合はCPU使用率はほぼ0になります。
usleep(0)の待ち時間は額面どおりではない模様です。
このソースは1bitの通信ですが、Dataとして4bitのピン書込みをすると、2.41秒まで落ちます。この時の通信速度は、1,659,751 bpsです。
8bitで書込みすると、3.19秒で、この時の通信速度は、2,507,836 bpsとなります。
この時のデータ幅の増加はマイコンのみで、Raspberry Pi 4側は1bit読み込みです。さらに、データもbit反転しているだけですので、実際の速度はもう少し遅くなると思います。
補足
Raspberry Pi 4、RX231単体でのGPIOの最大速度をオシロスコープで測定してみました。
プログラムはどちらもHigh/Lowべた書きです。
Raspberry Pi 4: 約26 MHz
RX231: 約2.7 MHz
この測定結果から、Raspberry Pi 4のGPIOはCPUのクロック周波数の1.5 GHzに比べて多少遅いとはいえ、そこそこ十分な速度が出ています。
対して、RX231の方はクロック周波数の54 MHzに対して、GPIOを一回動作させるのに10クロックかかっていますので、マイコンのクロック周波数的には妥当な値です。
通信速度をGPIOの通信速度を速めるには、RX231よりもクロック周波数の高いMCUを使うのが良いように推測できます。
作成:松林雄一