備忘録: ヒューズ抵抗 購入

秋葉原ラジオデパート2F 山王電子さんで ヒューズ抵抗 1/2W 1KΩ 購入 @60円
店頭の陳列棚には無いが、キョロキョロしていたら「何かお探し?」と言われ、聞いたら「ああ、ある」と、箱から出してくれた。


親切に「普通のRと間違えるといけねえ」と袋にマジックで書いてくれた。

ARM用GCCと64bitINTと8バイト境界とFAULT

CoreSourcery G++ 4.5.1 (GCC 4.3.2ベース)で、64bit int値を使うとどんなコードが出るのか見てみた。CPUはSTM32(Cortex-M3)。

; volatile uint64_t a,b;

	push	{r4, r5, r6, lr}
	ldr	r6, .L5+8

; a = 0;
	movs	r0, #0
	movs	r1, #0
	strd	r0, [r6]

; a++;
	movs	r0, #1
	ldrd	r2, [r6]
	movs	r1, #0
	adds	r2, r2, r0
	adc	r3, r3, r1
	strd	r2, [r6]

; a = a + 2;
	movs	r4, #2
	ldrd	r2, [r6]
	movs	r5, #0
	adds	r2, r2, r4
	adc	r3, r3, r5
	strd	r2, [r6]

こんなふうにLDRD/STRD命令を使って、割とコンパクトなコードになってるし、なにより、ロード/ストアが atomic だ(だよね・・・? →後述。アトミックじゃなかった)。
(某社オリジナルコア32bitCPU用のGCCでは、64bit値使ったら、いちいちライブラリをコールしてやがったので、不安になって、チェックしたのです…)

しかし、LDRD/STRD命令は、8バイト境界でないところに使ってしまうと、ARMコアがFAULTするので注意が必要だ。

Cortex-M3(ARM v7-Mアーキテクチャ)は、奇数アドレスからの16bit/32bitデータアクセスが出来る(サイクルは余計にかかるが。なお、ARM7は出来ない)ので、油断していた・・・すこし、ハマった・・・。

注:アンアラインドデータアクセスは単一ロード/ストア命令(LDR/STR)でしか利用できない。コンパイラによっては単純なポインタアクセスでLDM/STM命令を使ってくれたりしてFAULTするので(KEIL…)、この機構に期待しない方が安全だ・・・。

・・・Cortex-M3でも、LDRD/STRD命令は、アトミックってことでいいんだろうか?

マニュアルには、LDM/STMは割り込み中断・再開できるのでレイテンシが改善してると書いてあるけど、LDRD/STRDは割り込まれないんだろうか?
・・・マニュアルに書いてあるのはLDM/STM, PUSH/POPについてだけだから、LDRD/STRDは大丈夫なんじゃないかと思うんだけど・・・。

→うーんでも http://www.yokogawa-digital.com/arm/support/faq/index.php?VER-B-09 に 「ARM v7M 及び v7EM のコアに於いて、LDRD/STRD 命令はシングルコピーの最小単位(atomic)ではありません」[SDCOMP-14812]って書かれててるなあ…アトミックじゃないのかなあ。

→Cortex-M3 テクニカルマニュアルの ETMの項に、 ETMCANCEL ポートの説明として、
現在実行されているオペコードがキャンセルされました。割り込みされたオペコードは、この実行コンテキストに復帰したときに再始動または続行されます。これには、次のオペコードが含まれます。
LDR/STR
LDRD/STRD
LDM/STM
U/SMULL
MLA
U/SDIV
MSR
CPSID
とあるな…。じゃあ、LDRD/STRDもアトミックじゃないのか…。がっかり。

USBデバイス作成時はMicrosoft OS ディスクリプタに注意

人に指摘してもらって気がついたのだが、Windows XP SP1以降のWindowsは、USBデバイスから、Microsoft OS Descriptor なるものを取得しようとするらしい。

USB に関する FAQ: 中級レベル (microsoft.com)
Microsoft OS ディスクリプター (microsoft.com)

具体的には Windowsは index 0xEE で String Descriptor を取得しようとしてくる。

そんなindexのStringDescriptorは持ってません、と無視してしまえば問題はないのだが、たとえば、STM32の、最初期(2007年頃)のUSBサンプルコードでは、

こんな String_Descriptor配列を

ONE_DESCRIPTOR String_Descriptor[4] =
  {
    {(u8*)Joystick_StringLangID, JOYSTICK_SIZ_STRING_LANGID},
    {(u8*)Joystick_StringVendor, JOYSTICK_SIZ_STRING_VENDOR},
    {(u8*)Joystick_StringProduct, JOYSTICK_SIZ_STRING_PRODUCT},
    {(u8*)Joystick_StringSerial, JOYSTICK_SIZ_STRING_SERIAL}
  };
u8 *Joystick_GetStringDescriptor(u16 Length)
{
  u8 wValue0 = pInformation->USBwValue0;
  return Standard_GetDescriptorData(Length, &String_Descriptor[wValue0]);
}

こうやってindexノーチェックで引いて返信していた。

危ないのでindexはきちんとチェックしましょう・・・。

STM32 ライブラリ xxx_StructInit() は必ず行う

STM32のライブラリでSPIを使ったが、どうも動作が不安定なので悩んでいた。

初期化は

SPI_InitTypeDef SPI_InitStructure;

// 2線・全二重モード
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

// スレーブモード
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
// 8bitモード
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
// クロック極性: 通常時L
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
// クロックフェーズ: 1エッジ
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
// SPI_NSS_Hard: NSSピンはマスターからのCSとして使用しそれにより自動イネーブル
// SPI_NSS_Soft: 手動(SPI_CR1->SSIビットをNSSとして使用)
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
// MSBから送受信
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
// CRCは使用しないがデフォルトの多項式 7 を設定
SPI_InitStructure.SPI_CRCPolynomial = 7;

SPI_Init(SPI1, &SPI_InitStructure);

こんなコード。SPI_InitTypeDef 構造体メンバ中、 SPI_BaudRatePrescaler のみ、なにも設定していないが、スレーブモードなので、設定しなくていいと思っていた。

でも、SPI_Init() のソースを見ると、構造体メンバの値を単純にorしたものを、SPI_CR1レジスタに設定していた。

tmpreg |= (uint16_t)((uint32_t)SPI_InitStruct->SPI_Direction | SPI_InitStruct->SPI_Mode |
SPI_InitStruct->SPI_DataSize | SPI_InitStruct->SPI_CPOL |
SPI_InitStruct->SPI_CPHA | SPI_InitStruct->SPI_NSS |
SPI_InitStruct->SPI_BaudRatePrescaler | SPI_InitStruct->SPI_FirstBit);
/* Write to SPIx CR1 */
SPIx->CR1 = tmpreg;

構造体メンバに1つでも未初期化のものがあると、めちゃくちゃな値がCR1レジスタ全体に設定されてしまうのだった…それで不安定だったのかorz。

debug時はassert_param()でパラメータチェックが入るからいいのだが、ズボラにズボラを重ねていきなりreleaseで作っていると当然ノーチェック。

構造体メンバはまず

SPI_StructInit(&SPI_InitStructure);

しておけば、必要な構造体メンバの初期化がされるので、必ず行うべきなのだった…。

XPortで、不特定多数の相手先とUDP通信する方法

LANTRONIXのXPortで、不特定多数の相手先とUDP通信する方法をググっていたら、以下のページが見つかった。

Sending UDP Datagrams to and from a Cobox, Xport, or WiPort

Undocumentedなモードがあって、

connectModeを 0xCC に設定
DatagramTypeを 0x00 に設定

すると、以下の形式のパケットをシリアルポートで送受信することで、UDPデータグラムを送受信するモードになる。
使用されるポートは送受信時共に 設定項目の Port No で設定したポートになるようだ。
(DatagramType 0x00 に設定すると、 Remote Port は設定できなくなる)

(余談: WordPress はなんで 0x00 … この x を全角に変換してしまうんだ??→いや全角エックスじゃないわ、&#215 の乗算記号か…計算式じゃないっちゅうの! → wp-includes/formatting.php の 55行目あたりの $dynamic_characters と $dynamic_replacements という置換用arrayの中(末尾)に、この余計なお世話な変換があったので、削った。 → 後記。WordPress3.4でも未だにこの変換が入ってるけど、96行目をコメントアウトすれば良くなった。わあ便利…)

■UDP送信

0x02 (スタートバイト)
0xC0 (相手先IPアドレス 1バイト目 例: 192)
0xA8 (相手先IPアドレス 2バイト目 例: 168)
0x00 (相手先IPアドレス 3バイト目 例: 0)
0x01 (相手先IPアドレス 4バイト目 例: 1)
0x00 (データバイト数 BigEndian H)
0x04 (データバイト数 BigEndian L 例: 4バイト)
0x30 (データ)
0x31 (データ)
0x32 (データ)
0x33 (データ)

上記の例の11バイトをXPortのシリアルポート RxDに受信させると、
XPortは 192.168.0.1:(Port Noで設定したポート) に “0123” の4バイトを入れたデータグラムを送信する。

■UDP受信

また、LAN上の、たとえば 192.168.0.2 のマシンから XPortのIPアドレス:(Port Noで設定したポート) へUDPデータグラムが届き、
中にたとえば”ABC” の3バイトが入っていたとすると、XPortは

0x02 (スタートバイト)
0xC0 (送信元IPアドレス 1バイト目 例: 192)
0xA8 (送信元IPアドレス 2バイト目 例: 168)
0x00 (送信元IPアドレス 3バイト目 例: 0)
0x02 (送信元IPアドレス 4バイト目 例: 2)
0x00 (データバイト数 BigEndian H)
0x03 (データバイト数 BigEndian L 例: 3バイト)
0x41 (データ)
0x42 (データ)
0x43 (データ)

の10バイトのパケットをシリアルポートのTxDから送出する。

これで、不特定多数の相手との双方向UDP通信ができる。