2014年1月29日水曜日

SPIをDMAで

DMAでSPIの出力です

送信するデータはconst uint8_t *Buffで与えられます
1回に512バイトをDMAで出力し、出力が終わるまでwhileでループします

エディタからソースコードをコピペしてきたので行番号がついてるのはご容赦ください
    173     DMA_InitTypeDef DMA;
    174
    175     DMA.DMA_PeripheralBaseAddr = (uint32_t)(&SPI1->DR);
    176     DMA.DMA_MemoryBaseAddr = (uint32_t)Buff;
    177     DMA.DMA_DIR = DMA_DIR_PeripheralDST;
    178     DMA.DMA_BufferSize = 512;
    179     DMA.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    180     DMA.DMA_MemoryInc = DMA_MemoryInc_Enable;
    181     DMA.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    182     DMA.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    183     DMA.DMA_Mode = DMA_Mode_Normal;
    184     DMA.DMA_Priority = DMA_Priority_Medium;
    185     DMA.DMA_M2M = DMA_M2M_Disable;
    186
    187     DMA_DeInit(DMA1_Channel3);
    188     DMA_Init(DMA1_Channel3, &DMA);
    189     DMA_Cmd(DMA1_Channel3, ENABLE);
    190     SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
    191
    192     while (DMA_GetCurrDataCounter(DMA1_Channel3));
    193     while (!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));

メモリ2メモリとほぼ同じ感じでできます
違いとしては
1. ペリフェラルアドレスを適切に設定する
2. ペリフェラルのインクリメントはdis
3. M2Mもdis
4. DMA_Cmdを実行した後にペリフェラルのDMACmdを実行
5. ウエイトはDMA_GetCurrDataCounterだけではダメ
こんな感じです

まずペリフェラルアドレスについて
STM32F1のSPIはSPI_I2S_SendData関数を使って出力しますが
DMAの場合はレジスタアドレスを指定する必要があります
レジスタはSPIx->DR(DataRegister)です

データレジスタは配列ではないのでインクリメントも無効にしておきます
メモリ間転送ではないのでM2MもDisableにします

あとはDMA_Initの後にDMA_Cmdですが、M2Mと違い自動的に始まるわけではありません
SPI_I2S_DMACmdでDMAのトリガーをかけてやる必要があります
このトリガーにより適切なタイミングでSPIのレジスタにデータが転送されるようになります

また、メモリ間転送の場合、転送終了はGetCurrDataCounterで検出できますが
SPIの場合はちょっと違っていて、転送が終了してもSPIの送信が終了しているとは限りません
そのため、ソフトウェアでCS端子を操作している場合は、SPIの転送が終わらないうちにCSがネゲートされてしまいます
それを防ぐためにSPI_I2S_GetFlagStatusで送信完了フラグを確認する必要があります


SPIでマスタから一方的にデータを送る場合は比較的簡単に実現できますが
Webの情報を見た感じ、SPIの受信は一工夫必要な感じがあるようです

DMAを使ったデータ転送はSDSPIライブラリではなく、SPIライブラリに書いたほうがいろいろ使い回しができて便利だったかもしれません
例えば送信と受信の双方をDMAで行う関数を作っておけばセンサからのデータを読む場合に簡単になります

低レベルのAPIを書き換えると高レベル側全てに影響するため、結構面倒そうですが 試してみる価値はあるかも


STM32F1のDMAは実質的にDMA1の7chしかなく、その7chも様々なペリフェラルが共存しているので、データ転送をDMAだけでやることはほぼ不可能でしょう
例えばSPI1の受信とUSART3の送信 SPI1の送信とUSART3の受信は同じチャネルを使っています
これからの基板設計はGPIOの配置だけではなく、DMAのことも考慮する必要がありそうです
(例えばSDカードはSPI1を使うためUSART3はDMAが使えない のような)
GPIOのリマップのようにDMAのチャネル入れ替えも欲しい機能ですね

もっとも、USARTは送信・受信割り込みを使った送受信バッファを作ってしまったので、DMAを使う必要はあまり感じておらず、その点ではSPI以外にDMAが必要なIOも無いので気にしなくてもいいという気もしますが

2014年1月28日火曜日

SDカードのマルチブロックライト

無事SPIのDMA転送が動くようになったので
SDカードにマルチブロック転送を仕掛けてみました
(SPI-DMAは後で別のエントリに書きます)

今回はSPIの送信だけをDMA化しました
受信処理はソフトウェアループです
それとコマンドの送信などもソフトウェアで行っています

基本的に僕はロガーでしか使わないので、高速な読み取りが不要というのが大きいです
どちらかと言えば4KiBとか8KiBのバッファを一気にカードに書き込みたい感じなので


FatFsを使ってバッファをSDカードに書き込んだ時の波形です

茶色がクロック オレンジがMOSI 赤がMISO 黄色がCSです
書き込みのバッファはXorshiftで初期化しているため乱数が入っています

1ブロック目の後はビジーが出力されますが
2ブロック目以降はビジーがほとんど発生していません
おそらく1ブロック目で内部バッファ等を初期化して、2ブロック目以降はバッファが溜まるまでとりあえず貯めこんでいく みたいな感じだと思います

上のキャプチャはクロック周波数18MHzですが
36MHzの場合は更にビジーの比率が大きくなります
(ちなみにSDカードはSPIで25MHzくらいまでらしいので、36MHzはかなりのオーバークロックです)

36MHzクロックでは10240バイトの転送に20mSecほどかかりました
およそ520kByte/secといったところです

ロガーでデータを書き込む場合、8KiBくらいのバッファを確保しておいて、6-7kByte程度でカードに書き込みを試行するとして、およそ30ミリ秒もあれば書き込めそうです(理想状態のカードと仮定して)
内部処理をうまくやれば十分現実的な値でしょう
そもそもいままではソフトウェアループで書いていたので、おそらくDMA化して2倍から3倍にはなっていると思います

コレ以上の高速化はSPIを使っている限りほぼ不可能でしょう
あとはバックグラウンド処理で、SDカード周りの専有時間を減らす等ですが さすがにそれは面倒なのでやらないかな。。
SDIOとかも結構面倒な気がするのと とりあえず現状ではSPIアクセスでも十分な速度なため、ちまちまと最適化して完了 という感じになりそうです

2014年1月27日月曜日

STM32F1 DMA mem2mem

STM32F1のDMAを使ってメモリ間コピーをしてみました

uint8_t Buff1[1024];
uint8_t Buff2[1024];
int i;

for (i = 0; i < sizeof(i); i++) { Buff1[i] = i & 0xFF; }
memset(Buff2, 0, sizeof(Buff2));

Buff1からBuff2へコピーします
コピーが成功したかどうかはBuff2の内容を表示することにより確認できます
すべてのバッファが0の場合はコピー失敗 連続した値が表示されたら成功です

DMA転送の方法

まずクロックを有効にします
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
Periph1やPeriph2ではないので注意が必要です
(1や2にDMAが無いのでDMAはデフォルトでクロックが有効 と思い込んで悩みました)

つぎにDMAの初期化をします
DMA_InitTypeDef DMA;

DMA.DMA_PeripheralBaseAddr = (uint32_t)Buff1;
DMA.DMA_MemoryBaseAddr = (uint32_t)Buff2;
DMA.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA.DMA_BufferSize = sizeof(Buff2);
DMA.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA.DMA_Mode = DMA_Mode_Normal;
DMA.DMA_Priority = DMA_Priority_VeryHigh;
DMA.DMA_M2M = DMA_M2M_Enable;

DMA_Init(DMA1_Channel1, &DMA);

PeripheralBaseAddrにペリフェラルのメモリアドレスを
MemoryBaseAddrにRAMのメモリアドレスを設定します
ペリフェラルとメモリは便宜上の名前のため、メモリ間転送の場合はどちらに設定しても構いません
双方のアドレスはuint32_t型で、何らかのポインタでは無いので注意してください(キャスト無しでポインタを突っ込むと警告が出ます)

次にDIRですが、これはどちらが転送先か を設定します
DMA_DIR_PeripheralDSTで転送先がペリフェラルになります
DMA_DIR_PeripheralSRCで転送元がペリフェラルになります
今回は転送元のBuff1をペリフェラルに 転送先のBuff2をメモリに設定したので
ペリフェラル→メモリ となりDMA_DIR_PeripheralSRCとしてあります

BufferSizeは転送する要素数です メモリサイズではないので注意してください
ザックリ説明すると sizeof(Buff) ではなく sizeof(Buff) / sizeof(Buff[0]) の方です

PeripheralIncとMemoryIncはインクリメントするか否かです
今回は両方共RAMバッファなのでインクリメントする設定です

PeripheralDataSizeとMemoryDataSizeは配列のメモリサイズです
8bit 16bit 32bitでそれぞれByte HalfWord Wordです

ModeはNormalとCircularがあり、Circularの場合はメモリ転送を繰り返します
これはおそらくADCからどんどんメモリを読む場合などに便利な機能だと思います

Priorityは優先度です
ソフトウェアで設定できる優先度は下からLow Medium High VeryHighの4段階です
ソフトウェアで設定できる優先度以外にハードウェア出固定された優先度があり、これは同じソフトウェア優先度の転送が複数有る場合は番号の小さいチャネルが優先されます
例えばDMA1Channel1がMedium DMA1C2がHigh DMA1C3がHighの場合
DMA1C2→DMA1C2→DMA1C1の順に処理されます(おそらく
DMA2が有効の場合は どっかで読んだ気がしましたが忘れました
(RM0008とかあのあたりに書いてあります)

最後にM2Mですが
これはメモリ間転送モードの場合はEnable 非メモリ間はDisableです

構造体の初期化が完了したらDMA_Initで初期化します
この際に設定するチャネルを指定子ます


そして
 DMA_Cmd(DMA1_Channel1, ENABLE);
で転送を開始します

残りのデータ量はDMA_GetCurrDataCounter(DMA1_Channel1)で確認できます
転送が完了するまで待つ場合は
while (DMA_GetCurrDataCounter(DMA1_Channel1));
のようになります
DMAを使うとメモリ転送中に他の処理をすることができるので、whileで待つのは無駄に思えますが
CPUで転送するよりDMAのほうが早い場合があります


次はペリフェラルの転送を試してみたいと思います
これができればSPI転送などが高速化できます

2014年1月25日土曜日

SPIとI2C

SPIとI2Cの通信速度の差について

I2Cは標準モードで100kHz 高速モードで400kHzまでクロック周波数を設定できます
しかし400kHzでは信号がかなり鈍ってしまうため、実際には200kHzが限界と考えたほうがいいです
(ちゃんと動く環境なら400kHzでも動きますが)

対してSPIは、基本的に周波数の上限はなく、それぞれ適当に決めてやる必要があります
例えば、ST社のL3GD20というジャイロセンサでは10MHzまで可能です

そしてSPIでは1バイト8クロックですが、I2CはACKがあるため1バイト9ビットです
しかもSPIはスレーブ選択信号がありますが、I2Cはそれが無いのでスレーブ選択のために追加で1バイト必要です

3軸センサのデータを読む場合、手順としてはステータスレジスタを読んでから、ステータスの内容に応じてデータを読み込みます
ステータスは1バイトでデータは6バイトです

I2Cで読む場合は
→スレーブアドレスを送信
→レジスタアドレスを送信
←レジスタデータを受信

→スレーブアドレスを送信
→レジスタアドレスを送信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
の手順で、11バイトのやりとりが必要です
1バイトは9クロック 1クロックは5uSなので、495マイクロ秒の時間が必要です
スタートコンディションやストップコンディション等があるため、更に追加時間が必要で、がんばっても500マイクロ秒を切ることはできません


次にSPIで読む場合ですが
→レジスタアドレスを送信
←レジスタデータを受信

→レジスタアドレスを送信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
←レジスタデータを受信
の手順で読むことができ、9バイトになります
1バイト8クロックで、1クロックは0.1uSのため、7.2uSで終えることができます

単純に比較した場合、I2CはSPIの68倍の時間が必要になります
SPIを10MHzでウエイト無しに実行できるマイコンも少ないので、実際にはSPIはこの倍以上の時間が必要な場合もありますが、それでもI2Cに比べて十分に高速です


なお、I2Cは200kHzを超えるとかなりアナログ的になりますが、SPIも同様で、10MHzとかになるとかなり滑らかな正弦波に見えたりします
必要な速度と、回路を考えた上で選択する必要があるでしょう

2014年1月21日火曜日

オールSTなIMU

次のロガーの構成を考えつつ、手持ちのセンサで遊んでいます

とりあえずすでに実績のあるセンサは優先度を下げて、放置していたLSM303DLHCのライブラリを作成したりしました

LSM303DLHCはSTMicro製のデジタルコンパスモジュールで
加速度は2/4/8/16から 地磁気は1.3-8.1ガウスくらいの範囲で選ぶことができます
このセンサを放置していた理由として、このセンサはI2Cアクセスオンリーで、SPIアクセスができないというのが大きな理由です
データシートにはSPIの設定(3ワイヤor4ワイヤ)があるので、元々はSPIアクセスもできるように想定していたと思われますが、現在の製品ではSPIに非対応です

内部レジスタの配置にまんまと引っかかってしまい、やっと正常なデータを得られるようになりました
1つのチップ内にLSBファーストとMSBファーストが混在してるとかマジでやめてほしい。。。


で、デジタルコンパスモジュール以外にもいくつかセンサを載せてみました

3軸加速度 3軸角速度 3軸地磁気 気圧 気温 の計11種類のデータが得られます
LSM303DLHCのみI2Cアクセスで、L3GD20とLPS331APはSPIアクセスです

ちなみにこのブレッドボードに乗っているデバイス
CPUはSTMicro製
コンパスもSTMicro製
ジャイロもSTMicro製
気圧センサもSTMicro製です
マジでSTMicroオンリーで慣性計測装置を作ることができます
あとはSTMicro製GPSモジュールが出てくれば完璧ですがw さすがにそれは無いでしょう…

いくつか試してみたいセンサもありますが、その前に買うものがいろいろ溜まっていて、その予算も出てないので、基板を作るのはまだまだ先になりそうです

2014年1月20日月曜日

北海道スポーツシューティングクラブ 14年1月例会に参加してきた

1月18日に行われた北海道スポーツシューティングクラブ例会に参加してきました

内容としては
1)APSの練習
2)電動ガンのモーター交換方法
3)フルオートトレーサーを使用した夜戦
の3本です

APS

APSはAPS-3と電子ターゲットを使用して、一番得点が多い人には景品がでていました
得点の計算には電子ターゲットが使用されました


まずAPSとはなんぞや という説明



APS-3とペーパーターゲットを使用して練習
(写真の銃はAPS-3リミテッドエディション)


モーター交換



電動ガンの簡単なカスタムとして、モーターの交換方法も取り上げられました
モーターを交換することにより、単位時間あたりの発射回数が増えるため、僅かなチャンスにより多く弾を撃つことができるようになります



ノーマルのモーター
秒14発くらい



モーター交換後
秒20発くらい


夜戦

最後にフルオートトレーサーを使用して夜戦を行いました
この写真には3人写っており、奥の2人と、手前に1人います
長時間露出のため、フルオートで撃った蓄光弾が複数うつりこんでいます
もっと綺麗に写り込むかと思っていましたが、実際には蓄光BBはそんなに明るくなく、ほとんど映りこみません


ちなみに近距離で撃った場合はこんなかんじに写ります
これくらい明るく写り込むと綺麗なんですけども。。。


この時に使用したのはP90とMP5で、P90は給弾不良が多発し、ほとんど撃てませんでした
場所も駐車場を使ったため、障害物が全くなく、ジャムったら速攻で撃たれます ちょっと運の要素が大きすぎる感じがしました


今回は撮影班としてかなり後悔の残る感じになってしまいました
夜戦の撮影はかなり難しいです

次回

次の例会は3月15日(土)の予定のようです
実際に集まる以外にも、Facebookでも活動しているので、北海道在住で興味の有る方はぜひ参加してみてください

2014年1月15日水曜日

北海道スポーツシューティングックラブの忘年会に参加してきた

もう1月も15日ですが、北海道スポーツシューティングクラブ(HSSC)の忘年会で撮影した写真など紹介します
忘れてたわけじゃないんだからねっ

ちなみに北海道スポーツシューティングクラブはFacebookグループで活動しています
昨年11月から活動を始め、11月に1回目の例会を、12月に忘年会を、そして今年1月18日にも例会を行う予定です

開催場所は赤平市の植松電気で、エアガン関係は知る人ぞ知る秘境という感じです



Eotechのホロサイトの比較
左がレプリカ 右が純正です



Mk20狙撃銃
実銃はSCAR-Hをベースにした7.62x51mm弾を使用する狙撃銃です
SCAR-Hとの外観上の大きな違いはハンドガードが延長されていることです



M249 MINIMI
実銃は5.56x45mm弾を使用した分隊支援火器です
本来の使い方はベルトリンクを使用したボックスマガジンですが
ベルトリンクの弾がなくなった場合はSTANAGマガジンを使うことができます
この電動ガンもSTANAGマガジンに対応し、通常のM4用マガジンを使用することができます



SCAR-LとMk13グレネードランチャー
FPSゲームとかだと心強い装備ですが、重いっす



冬の北海道ではバリケードの作成が簡単にできます



サバゲ後はコンプレッサーで雪などの水分を吹き飛ばします
ちなみに僕が使用したのは一番手前のサプレッサーとACOGを付けたM4です


サバゲの後は場所を移動しての食事をしつつ色々話したり とかという感じでした


1月の例会はAPSの電動ターゲットを使用した精密射撃や、電動ガンのカスタム、フルオートトレーサーを使用した射撃 等をするようです

Contourカメラを買ったので、普通に使った場合や、フルオートトレーサーを使用した場合にどういうふうに写るかを確認してみたいと思っています

2014年1月12日日曜日

OpenCV

最近 実践OpenCV 2.4―映像処理&解析(AA) という本を買いました
ちょっとOpenCVでやってみたいことがあったためです

現状、僕が使えるOpenCV環境はMacのParallersに入っているUbuntu12か、RasPiに入っているRaspbianのどちらかになります
Nexus7にもUbuntuが入っていますが、ARM版のOpenCVはちょっとややこしそうなのでまだ入れていません

また、OpenCVはWindowsやMacOSにも入れることができますが、コレもちょっと面倒っぽいのでやってません
Windowsはともかく、いずれはMacOSも必要になりそうですが、とりあえずUbuntuで遊んでいる次第です

この本はVisualC++の環境を前提としているため、ソースコードも微妙に不慣れな感じです
ただ大きな違いはないため、ほぼ同じコードをUbuntuでビルドすることができます

付録にはOpenCVをソースコードからビルドする方法も書いてあるため、とりあえず色々つかえそうです

2014年1月6日月曜日

エアガンのターゲットを作る(下調べ1)

エアガンのターゲットを作ってみようと思ったので、ちょっといろいろ調べてみます
今回作るものはプレートに弾があたったことを検出するだけの簡単なものです
どこにあたったか等は検出しません

なんで市販品を買わないかって?
手持ちの部品で作れそうなのでやってみようかと


センサに使うのは圧電素子です
アナログ出力なので処理が面倒ですが、衝撃を検出するのには向いています

今回使用したのはストリナで売っているエナジーハーベスティング用の圧電素子です
http://strawberry-linux.com/catalog/items?code=12019
元々は振動発電をやってみたかったので購入したのですが、結局何も試さずに放置していました


とりあえず試しに大きさ10x30で厚さ1.0mmのアルミ板に貼り付けてみました
貼付け面は裏で、撃たれる方の面は何もついていない状態です

そして撃った時の出力をオシロで覗いてみました
縦軸5V 横軸5mSecです

ピークピークで20Vくらいで、非常に高速に振動しているっぽいです

撃ったのはマルイ10禁Mk23で、0.12gBB弾です
計測はしていませんがマズルエネルギーは0.1J程度だと思います
距離は2mですが
このようにヘコミができていました(裏側は膨らんでいました)
アルミ柔らかすぎです

とりあえず手っ取り早く検出したいなら適当に分圧してマイコンのADCに突っ込んで高速に監視すれば検出可能だと思います
しかし様々な距離やエネルギーになることや、風などの外乱を考えるともう一工夫必要そうです

次はステンレスの板で試してみようと思います
プレートが大きく、厚くなればその分重くなり、材質も変われば重さも変わります
プレート自体が重くなるとBB弾があたった時にプレートに加わる力が少なくなり、圧電素子に伝わるエネルギーも小さくなってしまうため、適切な厚さを調べる必要がありそうです


ちなみに30cm*10cmのプレートがどれくらいの大きさなのか
男性の平均的な身長は170cmなので、30cmの5.6倍くらいです
平均的な肩幅は45cmくらいなので、10cmの4.5倍くらいです
そのため、おおよそ平均的な男性の5分の1くらいの大きさ と考えることができます
距離2mで狙った場合は5倍の10m先の人間くらいの大きさ ということになります
30mで狙った場合は換算で150m先くらいになるので、射撃訓練にはちょうどいいかも

10x30cmの金属板は東急ハンズ等の一般向けホームセンターで容易に入手できると思うので、エアガンの射撃訓練にちょうどいいかもしれません(当たると金属音がするのでわかりやすいし)

2014年1月2日木曜日

XBeeAPIから送られてくるパケットを受信する

追記:上記関数内return(1);の前に"sRcvCount = 0;"を追加してください

この関数は
引数1:受信したパケット配列へのポインタ
引数2:受信したパケットのデータ数
引数3:チェックサムが有効か否か
戻り値:パケット受信が完了したか
という機能を持っています
他に定数"XBeeAPI_RCV_BUFF_SIZE"という整数が必要で
さらに下位関数として
int16_t Read(void);
が必要です
Read関数はシリアル受信バッファが空の場合には負の値を返すことにより、その旨を知らせます


この関数ではパケットの中身には関知しません
上位の関数で適切に処理する必要があります

また想定されるよりも大きなデータを受信することも想定していません
バッファオーバーフローのような危険性が有ることも理解しておく必要があります

この関数はデータ受信1バイトごとに呼び出すようなことは想定しておらず、少なくとも数十バイトから数百バイトの受信バッファが下位に存在することを要求しています
場合によっては数キロバイトのバッファが必要になるかもしれません
さらにこの関数自体で数百から数キロバイトのバッファを持つため、非常に冗長な関数となっています

2014年1月1日水曜日

ソードアートオンライ ファントム・バレット

祝 ソードアートオンライアニメ第2期
SAO EE後の予告ではシノンさんが狙撃銃を撃っておりました

ということで、SAO5巻及び6巻の途中までに出てくる銃の一覧です
SAO6巻は前半で大体のキャラが出尽くしてしまう感じがあるので途中までしか調べていません



名称種類弾薬
L115狙撃銃.338ラプアマグナム
ヘカートII対物狙撃銃12.7x99mm NATO
ミニガン(M134)ガトリング銃7.62x51mm NATO
FN MINIMI(M249)軽機関銃5.56x45mm NATO
SIG SG550アサルトライフル5.56x45mm NATO
M4アサルトライフル5.56x45mm NATO
M16アサルトライフル5.56x45mm NATO
ノリンコCQアサルトライフル5.56x45mm
H&K UMP短機関銃.45ACP
H&K MP7短機関銃/PDW4.6x30mm
FN P90短機関銃/PDW5.7x28mm
ヤティ短機関銃9x19mm
キャリコM900A短機関銃9x19mm
FN Five-seveNハンドガン5.7x28mm
FN SCAR(*)N/AN/A
ヴィッカース重機関銃7.7x56mm
AR-17ショットガンN/A
*)SCARは作中でL/Hの記述なし そのため弾薬及び種類を不明とする

おおよそ紛らわしい分類として
9x19mm弾(単射):ハンドガン
9x19mm弾(連射):短機関銃
5.56x45mm弾(連射可):アサルトライフル
7.62x51mm弾(連射可):バトルライフル
7.62x51mm弾(連射不可):狙撃銃
みたいな分類にしています
SCARはLかHかの記述が無いため弾薬は不明です
自動的に5.56x45か7.62x51かで分類するため、アサルトライフルとバトルライフルの分類ができません

SCAR使いにはSCAR-Hを持たせて数少ないバトルライフル使いを出して欲しいところです

AK系が無いので古今東西と言うには少し違和感がありますが、古くは第一次世界大戦の頃からの銃も出てきているあたり非常に幅広いジャンルの銃が登場する物語です


追記:2014/01/03
BS11で放送されたソードアートオンライン1期1話後の予告を見る限りシノンさんのメインウェポンはライフルのままでしたが、サブウェポンがグロック18C?に変更になっていそうです
原作ではPDWを持っていただけに拳銃に変わったのはちょっと残念では有りますが、まぁ機関拳銃とも言えるこの銃なら大きな違いは無いかも?(ただし弾丸が違い、原作版のほうが貫通力の高い弾を使う銃です)
上記の表を見ても短機関銃がやたら多く、ハンドガンが少ないので その辺りのバランス調整があったのかも