いつも参考にさせて頂いています。
SPIの割込みについてご教授いただけたらと思い、投稿させていただきました。よろしくお願いします。
現在、STM32-LCDとWIZnet製のイーサネットモジュールWIZ812MJを使用してTCPで通信を行おうとしています。
PCと通信が出来たので、受信時に割込みを使って処理をしようと考えたのですが上手く動きません。
SPI1_IRQnを設定して、SPI1_IRQHandlerに飛ぶようにしたらいいと思い次のような設定を組み込みました。
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);
実行するとSPI_IRQHandlerに飛ぶには飛ぶのですが、
ずっと受信割込みと送信割込みがきているようで、
TCPの初期化のすらうまく行かない状態になりました。
レジスタデータを処理しないと割込みが解消されないと思い、
レジスタデータの読み込みも行って見ましたが、正常な処理には戻りませんでした。
因みに上記の設定をコメントアウトすると、正常に通信できます。
SPIでの通信の割込みには何か気をつける落とし穴みたいなものがあるのでしょうか?
設定の不備の指摘や、ドツボにはまった経験などありましたら
アドバイス頂ければと思います。
ポートAのSPI1を使用するように設定しています。
以下に設定している部分を抜粋します。
-------------------------------------------------------------
RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1 |
RCC_APB2Periph_AFIO |
RCC_APB2Periph_GPIO,
ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
/* SCK, MISO, MOSI */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* NSS */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* SPI設定 */
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI, &SPI_InitStructure);
/* 割り込み設定(次の行からSPI_I2S_ITConfig()の行までを無効にすると通信は正常に動作する)*/
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);
SPI_Cmd(SPI1, ENABLE);
-------------------------------------------------------------
長文で失礼しました。
よろしくお願い致します。

私も初心者でうまく説明できないかもしれませんが、気になった
私も初心者でうまく説明できないかもしれませんが、気になったので思ったことを書きます。
(1) 割り込みの中でSPI_I2S_ClearITPendingBit関数を呼んでPendingBitをクリアしないと
ずっと割り込みが入りっぱなしになります。
(2) ごぞんじのとおり、SPIでは送信するたびにデータも受信します。したがって、SPI送信のたびに受信が起こり、
割り込みが発生します。
(3) あと、SPI受信割込を設定してるようですが、割り込みハンドラの中でやりたいことがわからないと
回答できません。
(4) ちなみに、イーサネットモジュールの仕様が分からないので、何とも言えないですが、
SPIのプロトコルはマスターからクロックを供給しないとスレーブからマスターにデータは送れませんので、
マスターからスレーブにポーリングする方式、つまり常時、STM32側からSPIでイーサネットモジュールの
レジスタを読んでデータをLANから受信したかどうか判定する方式になるのではないかと思います。
norakuro3501999さん、色々な情報ありがとうご
norakuro3501999さん、色々な情報ありがとうございます。勉強になります。
>(1) 割り込みの中でSPI_I2S_ClearITPendingBit関数を呼んでPendingBitをクリアしないと
> ずっと割り込みが入りっぱなしになります。
資料で「RXNE ビットのクリアは、SPI_DR レジスタを読み出しすることによって行われます」とあったので、
そちらの方法ばかりに気を取られていました。SPI_I2S_ClearITPendingBitは知らなかったので調べてみます。
>(2) ごぞんじのとおり、SPIでは送信するたびにデータも受信します。したがって、SPI送信のたびに受信が起こり、
> 割り込みが発生します。
申し訳ないです知りませんでした orz
なるほど、送信の際の受信の処理も考えないといけないんですね。
>(3) あと、SPI受信割込を設定してるようですが、割り込みハンドラの中でやりたいことがわからないと
> 回答できません。
説明不足ですみません。
受信割込みが発生したらデータ受信を行って、目的のバイト分の受信をした後に応答として送信を行うことを考えています。
>(4) ちなみに、イーサネットモジュールの仕様が分からないので、何とも言えないですが、
> SPIのプロトコルはマスターからクロックを供給しないとスレーブからマスターにデータは送れませんので、
> マスターからスレーブにポーリングする方式、つまり常時、STM32側からSPIでイーサネットモジュールの
> レジスタを読んでデータをLANから受信したかどうか判定する方式になるのではないかと思います。
マスターからクロックを供給しないとスレーブからマスターにデータは送れないのは知りませんでした。もっと調べてみます。
すみません(1)は間違ってました
すみません(1)は間違ってました。おっしゃるとおり「RXNE ビットのクリアは、SPI_DR レジスタを読み出しすることによって行われます」が正しいです。
norakuro3501999さん、追加の情報ありがとう
norakuro3501999さん、追加の情報ありがとうございます。
DRでの読み出しが正当な方法だとすると、送信処理の仕方が悪いのか、
それともやっぱり設定に不備があるのかな。
色々と調査したところ、初期化時の読み込み処理の時に
Busyフラグがいつまでもクリアされずにループから抜けれない状態のようでした。
割込み処理の方でDRの読み込みなどはしているつもりなんですが。
また長くて恐縮ですが、初期化のあたりと
SPI1_Handlerの処理を記載させていただきます。
まだ理解していない部分が多く、詳しくコメントなどはないので見辛いかと思いますが、
アドバイスなど頂けたら幸いです。
よろしくお願い致します。
------------ 初期化処理の一部 ------------------------
w5100_write( W5100_MR, MR_RST ); /* S/Wリセット */
while( (w5100_read( W5100_MR ) & MR_RST) ) { /* リセット処理が終わるまで待つ(ここから戻ってこない) */
}
------------ レジスタ読み込み処理 ------------------------
int w5100_read(const uint16_t address, uint8_t *data)
{
uint8_t cmdbuf[4];
cmdbuf[0] = (address >> 8) & 0xFF;
cmdbuf[1] = address & 0xFF;
cmdbuf[2] = 0x00;
cmdbuf[3] = 0x01;
return spimaster_transfer(spiport, cmdbuf, 4, data, 1);
}
------------ レジスタ読み込み処理 ------------------------
int spimaster_transfer(uint32_t port,
uint8_t *txbuf,
uint32_t txcount,
uint8_t *rxbuf,
uint32_t rxcount)
{
SPI1_NSS = 0;
while (txcount--)
{
while (!(SPIx->SR & SPI_I2S_FLAG_TXE));
SPIx->DR = *txbuf++;
while (!(SPIx->SR & SPI_I2S_FLAG_RXNE));
(void) SPIx->DR;
}
while (rxcount--)
{
while (!(SPIx->SR & SPI_I2S_FLAG_TXE));
SPIx->DR = 0;
while (!(SPIx->SR & SPI_I2S_FLAG_RXNE));
*rxbuf++ = SPIx->DR;
}
/* 送信待ち(ここでずっと回り続けている orz) */
while (SPIx->SR & SPI_I2S_FLAG_BSY);
SPI1_NSS = 1;
return 0;
}
------------ 初期化処理ここまで ------------------------
------------ SPI1_IRQHandler ------------------------
void SPI1_IRQHandler(void)
{
uint8_t data;
/* 受信処理 */
if( SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == SET )
{
data = SPI_I2S_ReceiveData(SPI1);
}
/* 送信処理 */
if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == SET)
{
if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == RESET)
{
SPI_I2S_SendData(SPI1, data);
}
}
}
------------ SPI1_IRQHandlerここまで ------------------------
(1) 割り込み処理の中で受信したデータをそのまま送信して
(1) 割り込み処理の中で受信したデータをそのまま送信してますが、これでは無限に送受信が行われ、SPIが空きになる
ことは永遠にないと思います。
(2) また、メインから呼び出されていると思われるspimaster_transfer関数でもSPIの送受信を行っているので
割り込み処理と競合してしまいます。
(3) あの、いまさらですが、割り込みなしで作ってみませんか?
割り込みを使うと、シーケンシャルな処理がSPIの1バイト受信でぶった切られることになり、プログラムが煩雑で
分かりにくくなるような気がします。メインでシーケンシャルに組んではどうでしょうか?
(4) WIZNETのホームページにドライバソフトを見つけました。プロセッサはAVRですが、Cで書いてあります。
参考にしてはどうでしょうか? 「W5100 Driver Source」で検索すれば見つかります。
(5) またトランジスタ技術2009年3月号に同じくAVRでW5100を使ってUDPを実装した例が出ています。
ソースファイルはhttp://toragi.cqpub.co.jp/tabid/184/Default.aspxでダウンロードで来るようです。
これも参考になると思います。
norakuro3501999さん、今回も色々な情報ありが
norakuro3501999さん、今回も色々な情報ありがとうございます。助かります。
>(1) 割り込み処理の中で受信したデータをそのまま送信してますが、これでは無限に送受信が行われ、SPIが空きになる
> ことは永遠にないと思います。
失礼しました。dataを使いまわしているのでコードが誤解を招くようなことになってました。
現状の割込みの中の処理は確認のためのフラグに対する送受信処理を入れただけにしています。
なので、SPI_I2S_FLAG_RXNEがきたら受信、SPI_I2S_FLAG_TXEがきたら送信を捌いているだけの状態です。
なのでdataの中身は特に考えていなかったのですが、しかし、SPI_I2S_FLAG_RXNEとSPI_I2S_FLAG_TXEが
同時にくると仰るような無限状態になりそうです。
もう少し良く調べてみます。
>(2) また、メインから呼び出されていると思われるspimaster_transfer関数でもSPIの送受信を行っているので
> 割り込み処理と競合してしまいます。
むむ、ということは初期設定時のレジスタ操作でも発生するんだ。
初期設定時は割込みを無効にして、その後に有効にするような処理にしないいけないか。
>(3) あの、いまさらですが、割り込みなしで作ってみませんか?
> 割り込みを使うと、シーケンシャルな処理がSPIの1バイト受信でぶった切られることになり、プログラムが煩雑で
> 分かりにくくなるような気がします。メインでシーケンシャルに組んではどうでしょうか?
自分だけでは決定できない事項なのでなんとも難しいかもしれません。(TT)
>(4) WIZNETのホームページにドライバソフトを見つけました。プロセッサはAVRですが、Cで書いてあります。
> 参考にしてはどうでしょうか? 「W5100 Driver Source」で検索すれば見つかります。
>(5) またトランジスタ技術2009年3月号に同じくAVRでW5100を使ってUDPを実装した例が出ています。
> ソースファイルはhttp://toragi.cqpub.co.jp/tabid/184/Default.aspxでダウンロードで来るようです。
> これも参考になると思います。
色々な情報ありがとうございます。
参考にさせて頂きます。
管理人です
norakuroさん
情報&アドバイスありがとうございます。