だるしむダイアリー

ゆるーくマイペースに

男性育児休暇 Arduino Dueを使い倒す ~レジスタ直接制御SPI通信(Slave)編

今回はArduino DueでSPI通信(Slave)をレジスタ直接制御にて実現します。

実際にはあまり使うことが少ないかもしれませんが、Master側から送信されたクロックをトリガーとし、データを更新して制御するノードです。

電子工作だとICが載ることもあると思いますが、この場合は、マイコン側がMaster側、IC側がSlave側となると思います。

まぁMaster側を作ったのでSlave側もやってみただけの自己満足です。

 

・前回のSPI通信(Master)の記事

na-daru.hatenablog.com

 

Arduino DueのSPI有効化手順(大きく4つです)

①クロックの供給(Masterの設定と同じ)

Arduino Due搭載のATSAM3X8EでSPIはPID24,PID25となっています。

今回はSPI0(PID24)を使っていきます。

 

マイコン端子の設定(Masterの設定とほぼ同じ)

続いて、SPIで使用するマイコン端子の設定を行います。

MISO(PA25), MOSI(PA26), SPCK(PA27),NPCS0(PA28)

NPCSは通信対象を決めるチップセレクトですが、今回はマイコンにあらかじめ準備されている機能を使用します。(GPIOでやるとGPIO側の割り込みを設定したりだるいので・・・)

マイコンの端子名で書かれているのでこれをArduino Dueのピン番号に変換します。

Arduinoのピン配置を確認するとマイコンが搭載されているすぐ下のコネクタに配置されていることが分かります。NPCS0(PA28)は10pinのことでMaster設定の時と同じです。

  

 

設定するためにはこれまでの記事にも書きましたがPIO Controllerの設定を行います。

大きな変更点として、GPIOの時は汎用入出力のため、PIO_ABSRの設定を行っていませんでしたが、今回はSPIで周辺機能を使うため、設定が必要となります。あとは、PIO_PSR[0]もPIO_PERではなく、PIO_PDRの方に値を書き込む必要があります。

③SPI関連レジスタの設定

SPIのモードとか通信速度をはじめ、通信に必要なレジスタは最低限設定は必要です。Masterの時と異なるのは、SPIのフレームを受信したときの割り込みを有効化したり、チップセレクト関係のレジスタの値の書き込みが必須となることです。

ソースコード内のコメントを参照ください。

④割り込み設定(NVICの設定)

GPIOの割り込みの時にも行いましたが、

NVICにてSPI0の割り込みを有効化する必要があります。

具体的にはSPI0はPID24なので、PID24の割り込みを有効化します。

NVIC->ISERで割り込み許可します。 

後は、割り込みが入ったときの処理をSPI0のHandler関数の中に書いていくことになります。

 

・GPIO割り込みの記事

na-daru.hatenablog.com

 

ソースコード

 


//SPIの動作モード設定用のパラメータ/////////////////////////////////////////////////////////////// #define SPI_MODE 0 //SPIのモード選択(SPIモード0~3)制御対象のSPIプロトコルを確認して決定する。 #define SPI_FREQUENCY 2000 //SPIの周波数を選択(kHz単位で記入する。例:500KHz⇒500, 2MHz⇒2000) #define SPI_BITS 16 //SPIの同時送信 or 受信データ数の設定。(最小8bit,最大16bitで1bit刻みで変更可能) #define SPI_READ_BYTE 50 //SPI読み出し時に読み出す数 ////////////////////////////////////////////////////////////////////////////////////////////// //SPI信確認用のフラグとデータ格納用の変数//////////////////////////////////////////////////////////////////////////// volatile bool gSPI_Received = false;//SPI受信フラグ volatile int gReceivedData = 0;//受信データの格納 ////////////////////////////////////////////////////////////////////////////////////////////// int scbr;//SPIの周波数を決めるための変数 //SPIモード設定用の変数 int cpol; int ncpha; int data_filter; //以下、SPIのSlave設定////////////////////////////////////////////////////////////////////////////// void spi_slave_resister_setting(){  
PMC->PMC_PCER0 = 1<<24 (SPI0, PID24) にクロック供給    
//PIOA25:MISO, PIOA26:MOSI, PIOA27:SPCK, PIOA28:NPCS0 //PIOA25,26,27,28はGPIOではなくPeripheralを使用する。 PIOA->PIO_PDR = 0b1111<<25; //PIOA25,26,27,28のPeripheral A/B選択はAとする。 PIOA->PIO_ABSR = PIOA->PIO_ABSR&(~(0b1111<<25)); //PIOA25,26,27,28の出力はオープンドレインにしない。 PIOA->PIO_MDDR = 0b1111<<25; // SPI0を使用不可にする。(この状態でレジスタ設定) SPI0->SPI_CR = 2; //スレーブモード時は、0bit目を0にする以外の他設定は不要。 SPI0->SPI_MR = 0; //選択されたSPI_MODEに基づいて、SPIクロックの極性と同期のタイミングを決める。 if(SPI_MODE == 0){ cpol = 0; ncpha = 1; } else if(SPI_MODE == 1){ cpol = 0; ncpha = 0; } else if(SPI_MODE == 2){ cpol = 1; ncpha = 1; } else if(SPI_MODE == 3){ cpol = 1; ncpha = 0; } data_filter = 0b1111111111111111 >> (16 - SPI_BITS); //周波数の設定をレジスタに書き込むための演算
 //SPIの周波数計算 //(84MHz)/SCBR = SPI周波数  
 scbr = 1000*((double)84 / SPI_FREQUENCY); SPI0->SPI_CSR[0] = (3<<24)+(scbr<<16)+(scbr<<8)+((SPI_BITS - 8)<<4)+ (ncpha << 1) + cpol; //いったん全ての割り込みを禁止にする。 SPI0->SPI_IDR = 0xffffffff; SPI0->SPI_IER = 1;//フレーム受信時に割り込みを発生させる。
 NVIC->ISER[0] = 1<<24;//SPI0(Peripheral ID:24)の割り込み発生を許可。
// チップセレクトがアクティブの場合は起動を待つ。 // (フレームの途中から通信し始めてしまうのを防ぐ) while (((PIOA->PIO_PDSR >> 28)&1) == LOW){} SPI0->SPI_CR = 1;//SPI0を起動する。 } //SPIデータ受信時の割り込み処理(Master側のCSがLoの場合、データを受信するたびに割り込みが入る) void SPI0_Handler(){ gSPI_Received = true;//SPI受信フラグを立てる。 gReceivedData = (SPI0->SPI_RDR & data_filter); } //データの受信がある場合、受信したデータを読み出す void SPI_receive(bool *spi_read_flag){ static int spi_read_counter = 0; if(gSPI_Received == true){ if (*spi_read_flag == true){ spi_read_counter+=1; // 受信した文字を出力する。 Serial.print(gReceivedData,HEX); if(spi_read_counter < SPI_READ_BYTE){ Serial.println(","); } if(spi_read_counter == SPI_READ_BYTE){ *spi_read_flag = false; spi_read_counter = 0; } } gSPI_Received = false; } else{ //Serial.println("SPI_No_message"); } }

Masterの時と同様にspi_slave_resister_setting()はsetup()で読み出し、SPI_receive(bool *spi_read_flag)をmain()から呼び出してもらうと呼び出したタイミングで受信データレジスタの値が読み出されるプログラムになっていると思います。

 

育休中の一日スケジュールと分担

最近は電子工作ネタばっかりだったので今日は育休のスケジュールです。

 

我が家は里帰りもしないで僕が3カ月の育休を取得しています。

妻と自分が一日どのように過ごしているかを書こうと思います。

 

  • 目次

一日のスケジュール

特にシフト制とかではないのですが元々、自分が夜型人間なので勝手にこうなりました。

7:00~11:00 妻1オペ育児

子供が大体7時くらいに起きるのでそれに合わせて妻が起きてきます。

ゴミ出しとか洗濯とか色々やってくれています。感謝。

僕は自分の1オペが終わった時間次第で9時~11時くらいに起きていました。

11:00~23:00 2人育児

子供は大体2時間半~3時間寝てそのあと1時間半~2時間くらい起きるを繰り返しています。

起きている間は授乳(妻)やミルク(だるしむ)の後、子供と三人で遊んでいます。

子供が寝ている間は二人ともやりたいことをやります。

僕は走ったり、ジムにいったり、電子工作用のプログラム作ったり、ゲームしたり、株の売買をはじめ色々しています。後は日用品の買い出しに行ったりもします。

大体20時くらいに子供をお風呂に入れています。冬で寒いからずっと沐浴です。

23:00~翌5:00 だるしむ1オペ育児

妻が寝た後は僕が一人で子供を見ています。

ミルクやったり、おむつを替えたり、遊んだり。

日にもよりますが寝ない時もあります。

生後2~3カ月の時は理由が分からない泣き方をしたり、1~2時間抱っこしていることもありました。

よく寝てくれるときは資格の勉強をしていました。(回路設計者だけどIPAのレベル4取りたい!)

子供が妻が次に起きてくるまで寝そうだなというタイミングで眠りについていました。ここでバトンタッチ。

大まかな家事の分担について

・妻

ゴミ出し、洗濯、昼食準備

・だるしむ

夕食準備、掃除、日用品買い出し

⇒料理は元々ある程度出来ましたが、育休中になんでも作れるようになりました。

・二人

洗濯物の取り込み

 

考えていたこと

取りあえず、妻は寝ないと調子が悪い人なので、普段通り寝させてあげられるようにしていました。

家事は分担はしていましたが、自分が出来るときは妻の分もやったり、逆に出来ない時はやってもらったり、お互いの体調次第で臨機応変に対応していました。

僕のが先に職場復帰してしまうので僕の休み中は楽させてあげたかったです。

 

幸い、このスケジュールで育休中に喧嘩はありませんでした。

ただただ、子供が可愛く、二人で笑いながら育児をしていました。

夜泣きは最初はしんどかったですがすぐ慣れました。

ぶっちゃけ仕事をしている方が僕は楽なときもあると思ったので、これを1オペでやってるお母さんはすげぇなって思います。

育児してない自慢おじさんは家庭の立場が無くなっていくのがよくわかりました(笑)

男性育児休暇 Arduino Dueを使い倒す ~レジスタ直接制御SPI通信(Master)編

今回はSPI通信についてまとめます。

最近、回路設計においてICを多数使うことが多く、SPI通信が必要なものが多いです。

事前にICの動作を確認したくても、メーカーのGUI開発が追い付いていないこともあるのでそういった場合は自分で環境を構築する必要があります。

よく使うのはMasterモードだと思うのでレジスタ直接制御で実現していきます。

 

SPI通信(Master)の概略

SPIのプロトコルについては説明は割愛しますが、クロックに同期させてデータの通信を行う同期式シリアル通信のひとつです。一つのMasterに対して複数のSlaveと通信をすることができます

 

目次

Arduino DueのSPI有効化手順(大きく3つです)

①クロックの供給

Arduino Due搭載のATSAM3X8EでSPIはPID24,PID25となっています。

今回はSPI0(PID24)を使っていきます。

 

 

マイコン端子の設定

続いて、SPIで使用するマイコン端子の設定を行います。

MISO(PA25), MOSI(PA26), SPCK(PA27)

NPCSは通信対象を決めるチップセレクトですが、複数のICと通信することを想定してGPIOで実現することとします。

マイコンの端子名で書かれているのでこれをArduino Dueのピン番号に変換します。

Arduinoのピン配置を確認するとマイコンが搭載されているすぐ下のコネクタに配置されていることが分かります。GPIOは10pinを割り当てることにします。

  

 

設定するためにはこれまでの記事にも書きましたがPIO Controllerの設定を行います。

大きな変更点として、GPIOの時は汎用入出力のため、PIO_ABSRの設定を行っていませんでしたが、今回はSPIで周辺機能を使うため、設定が必要となります。あとは、PIO_PSR[0]もPIO_PERではなく、PIO_PDRの方に値を書き込む必要があります。

③SPI関連レジスタの設定

以下がSPIに関連するレジスタです。大量にありますね・・・

SPIのモードとか通信速度をはじめ、通信に必要なレジスタは最低限設定は必要です。

(ソースコードに少しコメントは残しました)

ソースコード


//SPIの動作モード設定用のパラメータ///////////////////////////////////////////////////////////////
#define SPI_MODE 0 //SPIのモード選択(SPIモード0~3)制御対象のSPIプロトコルを確認して決定する。
#define SPI_FREQUENCY 2000 //SPIの周波数を選択(kHz単位で記入する。例:500KHz⇒500, 2MHz⇒2000)
#define SPI_BITS 16 //SPIのデータ長の設定。(最小8bit,最大16bitで1bit刻みで変更可能)

//////////////////////////////////////////////////////////////////////////////////////////////


int scbr;//SPIの周波数を決めるための変数


//SPIモード設定用の変数
int cpol;
int ncpha;
int data_filter;


//SPIのMaster設定
void spi_master_resister_setting(){
 PMC->PMC_PCER0 = 1<<24 (SPI0, PID24) にクロック供給 
 //PIOA25:MISO, PIOA26:MOSI, PIOA27:SPCK //PIOA25,26,27はGPIOではなくPeripheralを使用する。 PIOA->PIO_PDR = 0b0111<<25; //PIOA25,26,27のPeripheral A/B選択はAとする。 PIOA->PIO_ABSR = PIOA->PIO_ABSR&(~(0b0111<<25)); //PIOA25,26,27の出力はオープンドレインにしない。 PIOA->PIO_MDDR = 0b0111<<25; //SPIのCSはGPIOにて制御する。(10pin) PIOC->PIO_OER = 0x20000000; PIOC->PIO_PER = 0x20000000; PIOC->PIO_MDDR = 0x20000000; PIOC->PIO_SODR = 0x20000000;//10番ピンはイニシャルHiにしておく(CSはイニシャルHiにしておく。) //SPI設定 SPI0->SPI_CR = 0x00000010;//レジスタ設定中はSPI通信無効化 SPI0->SPI_MR = (84<<24)+0b10011;//マスターモード、chip selectは1つ。 //割り込みは禁止する SPI0->SPI_IDR = 0xFFFFFFFF; //チップセレクトの割り込みはしない。 SPI0->SPI_IER = 0x00000000; //選択されたSPI_MODEに基づいて、SPIクロックの極性と同期のタイミングを決める。 if(SPI_MODE == 0){ cpol = 0; ncpha = 1; } else if(SPI_MODE == 1){ cpol = 0; ncpha = 0; } else if(SPI_MODE == 2){ cpol = 1; ncpha = 1; } else if(SPI_MODE == 3){ cpol = 1; ncpha = 0; } //通信速度をレジスタに書き込むための演算
//SPIの周波数計算 //(84MHz)/SCBR = SPI周波数  
 scbr = 1000*((double)84 / SPI_FREQUENCY);  
//チップセレクトレジスタ設定(送信データ長は16bit)  
 SPI0->SPI_CSR[0] = (3<<24)+(84<<16)+(scbr<<8)+((SPI_BITS - 8)<<4)+ (ncpha << 1) + cpol;//DLYBCTおよびDLYBSはCSをGPIOで実現するため、設定は不要。 //下位2bitはクロックの極性0:inactive Lo,1:inactive Hi //その先の2bitは1:立ち上がりor 0:立ち下がり同期 SPI0->SPI_CR = 0x00000001;//レジスタ設定が完了したのでSPI通信有効化 } //以下、送信設定をする関数/////////////////////////////////////////////////////////////////////////////////// //SPI送信設定(凝ったデータを送信したければ、ここを改造する) //デフォルトではspi_send_dataを送信するデータズバリの変数となっている void SPI_transfer(int spi_send_data){ PIOC->PIO_CODR = 0x20000000;//10Pin(CS)Low出力 delayMicroseconds(2); //SPI通信用のデータを送信する。 SPI0->SPI_TDR = spi_send_data; //データ送信完了まで待つ while(((SPI0->SPI_SR >> 9)&1) == 0){}; PIOC->PIO_SODR = 0x20000000;//10Pin(Cs)HIGH出力 delayMicroseconds(2); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

コードの中に出てくるSPIのモードはデータの更新タイミングに関わってくるもので、Slave側の仕様によって変わります。制御対象のデータシートを確認して設定を行います。

Arduino Dueの仕様上、データは最大で16bitまでしか送信が出来ないため、それ以上のデータを送信したい場合は分けて送信する必要があるのでプログラムの改造が必要になります。(25bitだったら16bit+9bitに分けるとかね)

 

上記のプログラムはSPI通信(Master)の関数部分しか添付していないので、実際には

void setup()中でvoid spi_master_resister_setting()を呼び出してもらい、void main()でSPI_transfer(int spi_send_data)を呼び出してもらえれば通信可能だと思います。

 

ICによってチップセレクトをActiveにしてからクロックを受け取るまでの時間やデータの書き込み時間に制約があったりするので、必要に応じてdelay()の値を見直したりも必要に応じて行います。今回のプログラムはSlave数が1を想定して作りましたので、通信バスに複数のSlaveが存在する場合はチップセレクトを複数準備する必要があります。

 

長くなりましたが、今回はこのあたりで。

男性育児休暇 Arduino Dueを使い倒す ~レジスタ直接制御GPIO割り込み編

今回はArduino Dueをレジスタ直接制御で割り込み処理を行います。

前回まで、GPIOの入出力を行ってきました。

 

過去の記事

  • GPIO出力

na-daru.hatenablog.com

  • GPIO入力

na-daru.hatenablog.com

 

GPIO入力に割り込み処理を追加します。

通常、Arduinoの外部割り込みにはattachInterruptを使うと思います。

これをレジスタ直接制御で実現していくことになります。

 

目次

Arduino Dueで外部割込みを実現する流れ

単純に前回のGPIO入力の延長です。

前回でマイコン端子のHi/Lo電圧はPIO_PDSRに格納されることは書きましたが、外部割り込みを設定する際には以下の割り込み関連ブロックの設定を行います。

Arduino Due搭載のSAM3XのGPIOでは、入力電位を読んだ後にEVENT DETECTORを通じて割り込み条件の成立を判定し、次に割り込み可否設定(PIO_IMR)を参照し

割り込みが許可されていれば、割り込み要求を(NVICへと)出力します。

 

①EVENT DETECTORの設定

まず、EVENT DETECTORで割り込みの種類の設定を行います。

EVENT DETECTORの中身は以下のようになっています。

割り込み成立条件は以下の5種類設定出来ます。

(Rise発生, Fall発生, RiseまたはFall発生, High電位時, Low電位時)

 

具体的なレジスタ設定方法として、Rise発生時に割り込みを有効とする場合は

以下のようになります。

Arduino Dueの3Pin(PIOC 28)を使う場合、ソースコード

	
void setup() {
  // put your setup code here, to run once:
  PIOC->PIO_REHLSR = 1<<28;//Rising EdgeまたはHigh Levelを選択。
  PIOC->PIO_ESR = 1<<28;//Edge検出を選択。(上と併せてRising Edge検出)
  PIOC->PIO_AIMER = 1<<28;//両Edge検出は使用しない。
}

となります。

②割り込み可否の設定

EVENT DETECTORで設定した割り込みを有効化させるにはPIOコントローラの割り込みを有効化する必要があります。

PIO_IMRレジスタで割り込みを有効化します。PIO_IMRレジスタ自身はRead-onlyなので、割り込みを有効化させる場合はPIO_IERに値を書き込みます。

①のコードに一行追加する形です。

	
void setup() {
  // put your setup code here, to run once:
  PIOC->PIO_REHLSR = 1<<28;//Rising EdgeまたはHigh Levelを選択。
  PIOC->PIO_ESR = 1<<28;//Edge検出を選択。(上と併せてRising Edge検出)
  PIOC->PIO_AIMER = 1<<28;//両Edge検出は使用しない。
 PIOC->PIO_IER = 1<<28;//  3番端子(PIOC28)からの割り込み発生を許可
}
③NVICの設定

②でPIOコントローラにて割り込みを有効となりましたが、最後にNVICにてPIOCの割り込みを有効化する必要があります。

具体的にはPIOCはPID13なので、PID13の割り込みを有効化します。

少しややこしいので詳細は割愛しますが、

NVIC->ISERで割り込み許可。NVIC->ICERで割り込み禁止となります。 

	
void setup() {
  // put your setup code here, to run once:
  PIOC->PIO_REHLSR = 1<<28;//Rising EdgeまたはHigh Levelを選択。
  PIOC->PIO_ESR = 1<<28;//Edge検出を選択。(上と併せてRising Edge検出)
  PIOC->PIO_AIMER = 1<<28;//両Edge検出は使用しない。
 PIOC->PIO_IER = 1<<28;//  3番端子(PIOC28)からの割り込み発生を許可
 NVIC->ISER[0] = 1<<13;//PIOC(Peripheral ID:13)の割り込み発生を許可。
}

これで割り込みの設定は完了です。

 

割り込み発生時の処理の記入

上記で設定した割り込みが発生した際は、PIO_ISRレジスタに"1"が格納されます。

割り込みが発生した時の処理はHandler関数の中に書きます。

GPIOの割り込み時の処理はPIO_Handler関数として記載するのですが、

Arduinoインストール時のデフォルトのライブラリに既に定義されているため、

自分で作ったソースコードにもPIOC_Handler()等を書くと、関数名が重複してエラーになってしまいます。

なので、レジスタ直接制御で割り込みを有効化させる場合には、ライブラリを無効化する必要があります。(少しめんどくさいですね・・・)

PIO_Handler関数はWInterrupts.cに書かれているので一時的にコメントアウトしておきます。Arduino15に格納されています。

(\Arduino15\packages\arduino\hardware\sam\1.6.12\cores\arduino\WInterrupts.c)

 

ソースコード例(3Pinを外部割り込みとして使う)
	
void setup() {
  // put your setup code here, to run once:
  Serial.begin(250000);//シリアル通信開始 (250kbps)
  
  //3Pinを入力設定する(GPIO入力設定の記事参照)
  PIOC->PIO_PUER = 1<<28;//PIOC28のプルアップ抵抗を有効にする。
  PIOC->PIO_SCIFSR = 1<<28;//PIOC28の内部フィルタ選択クロックをMCKにする。
  PIOC->PIO_IFER = 1<<28;//PIOC28の内部フィルタを有効にする。
  
  //3Pinの割り込み設定をする
  PIOC->PIO_REHLSR = 1<<28;//Rising EdgeまたはHigh Levelを選択。
  PIOC->PIO_ESR = 1<<28;//Edge検出を選択。(上と併せてRising Edge検出)
  PIOC->PIO_AIMER = 1<<28;//両Edge検出は使用しない。
 PIOC->PIO_IER = 1<<28;//  3番端子(PIOC28)からの割り込み発生を許可
 NVIC->ISER[0] = 1<<13;//PIOC(Peripheral ID:13)の割り込み発生を許可。 
  
  
}

void PIOC_Handler() {//3番端子はPIOC28のためPIOC_Handler()を使用。
 if ((PIOC->PIO_ISR >> 28) & 1 == 1){//PIO_ISRが1の場合
 //※ここに割り込み発生時の処理を書く
 }
}


void loop() {

}

 

ここまで出来ればあとはPIO_ISRレジスタの中身で判定して色々な処理を拡張していってもいいですね。

 

次回はSPI通信のことでも書こうかな・・・覚えているうちに色々書きます。

 

男性育児休暇 Arduino Dueを使い倒す ~レジスタ直接制御GPIO編#2

前回はArduino Dueをレジスタ直接制御にてGPIO出力を行いましたが、今回は入力を行っていきます。

 

・前回記事はコチラ

na-daru.hatenablog.com

 

GPIO入力なのでArduinoのwrapperで言うとdigitalreadです。

外部からの入力に対してHi/Loを返します。

また次回書こうと思いますが、割り込み時のトリガーとして使うことが多いです。

 

目次

レジスタ直接制御にてGPIO入力を行う手順

おなじみのArduino Dueのピン配列です。

前回はArduinoの2pin(PIOB25)のGPIO出力を行うためのプログラムを作成しましたが、今回は、3Pin(PIOC28)で入力として受けて結果を表示するプログラムを作成します。

 

GPIOの入力は以下のPIO_PDSR(Pin Data Status Register)で"1","0"管理されます。

なので、前回のGPIOの時と同様に関連するレジスタの設定を行います。

データシートから抜粋しました。

PIO出力設定の部分は前回の記事で設定した出力設定のレジスタ部です。今回は入力設定なので無視です。

設定する必要があるのはPIO_PUSR, PIO_IFSCR, PIO_IFSRの3種類です。

PIO_PUSRはプルアップ抵抗の有効、無効

PIO_IFSCR, PIO_IFSRは内部フィルタの設定関係です。

PIO_PUSRはプルアップ有効、内部フィルタの設定は用途に応じて設定すればいいと思います。

 

今回は以下のように設定しました。

内部フィルタも有効とし、除去するスパイクノイズ幅はMCK(84MHz)としています。

ソースコード

前回のGPIO出力に対してGPIO入力のコードを追加した形になります。

Arduino Dueの2pin, 3pinをショートさせてプログラムを実行します。

(入力で使用する3PinはPIOCなのでPID = 13にクロックの供給も行う必要があるので注意!)

		    
void setup() {
  // put your setup code here, to run once:
  Serial.begin(250000);//シリアル通信開始 (250kbps)
  PMC->PMC_PCER0 = (1<<12) + (1<<13);//PIOB,PIOCにクロックを供給。
  PIOB->PIO_PER = 1<<25;//PIOB25を汎用入出力端子として使う。周辺機能OFF。
  PIOB->PIO_OER = 1<<25;//PIOB25の出力を許可する。
  PIOC->PIO_PUER = 1<<28;//PIOC28のプルアップ抵抗を有効にする。
  PIOC->PIO_SCIFSR = 1<<28;//PIOC28の内部フィルタ選択クロックをMCKにする。
  PIOC->PIO_IFER = 1<<28;//PIOC28の内部フィルタを有効にする。
}

void loop() {
  // put your main code here, to run repeatedly:
  int theLogicVoltage;
  PIOB->PIO_SODR = 1<<25;//PIOB25(2番端子)からHIGH電圧を出力。
  delay(1);
  theLogicVoltage = (PIOC->PIO_PDSR >> 28)&1;//PIOC28の電圧を読み取り※
  Serial.println(theLogicVoltage);//読み取った電圧をシリアル出力
  delay(999);
  
  PIOB->PIO_CODR = 1<<25;//PIOB25(2番端子)からLOW電圧を出力。
  delay(1);
  theLogicVoltage = (PIOC->PIO_PDSR >> 28)&1;//PIOC28の電圧を読み取り※
  Serial.println(theLogicVoltage);//読み取った電圧をシリアル出力
  delay(999);
}

このプログラムを実行すると約1秒ごとにHi/Loが切り替わり、シリアルモニタに出力されます。

3pin(PIOC28)の電圧レベル情報はPIOCのPIO_PDSRレジスタの28ビット目に格納されているのでレジスタにアクセスし、ビットシフトして値を取り出しています。

 

今回でGPIO入出力の設定が出来る様になったので、次回は割り込み機能を使っていきたいと思います。

男性育児休暇 Arduino Dueを使い倒す ~レジスタ直接制御GPIO編

今回もArduinoネタです。

前回の記事のようにArduino Dueをレジスタ直接制御で動かしていきます。

今回はGPIO編です。

 

・前回記事

na-daru.hatenablog.com

 

前回、軽く触れたようにArduinoのピン配列は以下のようになっています。

Arduino Dueには、色々と番号がふられていますが、レジスタ直接制御を行う場合にはマイコン側のピン番号を使うことになります。

例えば、Arduinoの2ピンであればArduino Due搭載マイコン(ATSAM3X8E)ではB25です。

 

目次

ArduinoでGPIO出力するための手順

マイコン機能への電源(クロック)供給

消費電力を抑えるため、マイコンは周辺機能の電源を最初はOFFにしています。

従って、マイコンの各種機能を使用するには、まず電源 (クロック) を供給してやる必要があります。

 

ATSAM3X8Eのデータシートを見ると以下の様な機能を持っています。

ATSAM3X8Eのデータシート

先ほどのArduinoの2ピン(B25)であればInstance ID(PID) = 12(PIOB)となります。

PIOBへクロックを供給するためには以下のPMC_PCERレジスタに書き込みが必要になります。

PMC_PCERにはPMC_PCER0とPMC_PCER1がありますが、PID = 12はPMC_PCER0側にあります。

クロックの供給が完了すると、次はGPIOの出力をさせるための設定を行います。

 

以下がPIOポートの概要図です。

GPIOの出力を行うためには、以下のレジスタに値を書き込んでいくことになります。

例えば、PIO Status Register(PIO_PSR[0])に値を書き込む方法

PIO_PER[0]に1を書き込むと、PIO_PSR[0]が1になる。(上側だと"1")

PIO_PDR[0]に1を書き込むと、PIO_PSR[0]が0になる。(下側だと"0")

同様にPIO_OSR, PIO_ODSR, PIO_MDSR, PIO_PUSRも設定し、マイコン端子まで繋いでいきます。

 

GPIO出力を行うまでのレジスタ設定は以下です。

汎用の入出力機能を使うため、周辺機能は使わないのでPIO_ABSR[0]は無関係です。

ここまでの流れをプログラムに書くと以下です。

B25なのでPIOBで各レジスタの25ビット目に値を書き込むことになります。

 

ソースコード

 

void setup() {
  // put your setup code here, to run once:
  PMC->PMC_PCER0 = 1<<12;//PIOB (Peripheral ID:12)にクロックを供給する。
  PIOB->PIO_PER = 1<<25;//PSR (PIO Status) を1にする。
  PIOB->PIO_OER = 1<<25;//OSR (Output Status) を1にする。
  PIOB->PIO_MDDR = 1<<25;//MDSR (Multi-Driver Status)を0にする。
}

void loop() {
  // put your main code here, to run repeatedly:
  PIOB->PIO_SODR = 1<<25;//PIOB25をHIGHレベルにする。
  delay(500);//500ミリ秒待つ。
  PIOB->PIO_CODR = 1<<25;//PIOB25をLOWレベルにする。
  delay(500);//500ミリ秒待つ。
}

分かりやすくビットシフト演算していますが、直接書き込んだ方が演算処理が無くなるのでより早くなると思います。(1 << 25 ⇒ 0x02000000)

 

上記を設定することでdigitalWriteをマイコン直接制御で実現することができます。

電子工作で使うだけだと、こんなにマニアックなことはしなくても問題ないと思いますが、高速制御にこだわる人は一度試してはいかがでしょうか。

男性育児休暇 Arduino Dueを使い倒す ~レジスタ直接制御導入編

育児休暇3カ月経過。あっという間です。

ただただ、子供が可愛い・・・

 

育児休暇も終わりを迎えているのでやったことの中身を書いていこうかと思います。

・先回記事

na-daru.hatenablog.com

ネタはいっぱいありますが、気分で電子工作から書きます。

 

私は電気設計をしているエンジニアです。

育児休暇で電子工作に触れようと思い、Arduino Due, Raspberry pi Pico, Arduino Uno R4などを買いました。

全部がっつり触りましたがArduino Dueをレジスタ直接制御で色々動かしたので半分備忘録で残そうと思います。

 

Arduino Dueに関して

ピン配の詳細

CANコントローラが内蔵されており、外付けのコントローラが不要です。

CANをゴリゴリ使うため、Dueに決めました。

 

Arduino IDEのダウンロードや、ボードマネージャーなどの最低限のセットアップはネットで検索すると色々出てきたのですぐに出来ました。

ArduinoにはWrapperと呼ばれる、代理の関数のようなものが準備されています。

 

<Wrapperとレジスタ直接制御のイメージ>

よく見られるdigitalWriteやanalogreadなどもレジスタ直接制御で実施する場合には煩雑なコードになるわけですね。

 

少し調べてみましたが、Arduino Dueの能力をフルで使うためにはレジスタ制御でないとダメだとわかりました。

 

Arduino Dueに搭載されているマイコンを調べてみると、ATSAM3X8Eというマイコンが搭載されています。

データシートを見てみると・・・1500ページくらいある・・・

 

一度に書くと量が膨大になるため一旦ここまで!

次回はGPIO入出力をレジスタ直接制御で実現してみようと思います。

(育休から復帰に向けて文章書くリハビリ頑張ろう)