2017年7月
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          

Amazonウィジェット

  • miniPC
  • 最近買った本
  • Raspberry Pi
  • クアッドコプター
  • 書籍ランキング

AdSense

  • 広告
無料ブログはココログ

2017年7月15日 (土)

PIC32MX220のADコンバータで、複数chをスキャンモードで使う

PIC32MX220は、秋月電子で売っている32bitのPICマイコン。
このPICのADコンバータは、1Mspsというとても高速な(PIC16Fシリーズから入った自分からみて)ものを搭載している。これを使ってアナログ入力数本を、自動で順番にAD変換できるようにしてみた。

とりあえずADCが動いているかを確認する手段を用意するため、シリアルの送信から手をつけた。
ここのブログを参考にさせてもらった。
http://miu-robo.blogspot.jp/2012/09/pic32mx220f032buartprintf.html

ソースを見ていると、
ペリフェラルのヘッダファイルの使い方がなんとなくわかる。

PIC32インストールフォルダを見て、ペリフェラルのヘッダファイルを探す。
adc10.hというのがADC用のファイルになっている。



適宜MicrochipJapanにある日本語リファレンスを見る。
シングルエンド入力で、スキャンモード(chを順番に自動で変換していく)を使うときは、
MUXのAだけを使うようにする。



<ちょっとマイコン知識>
マイコンのCコンパイラでビットをセットしたいときは、大抵ヘッダファイルにビットやレジスタ名を#defineしたものが記述されているのでそれらを使う。

ビットを立ててほしい#define定数をOR演算子でつなげて書いて、それを何か新しい#define定数に入れる。すると、セットしたいビットだけが1になった定数が完成する。

下記の例で行くと、ADC_MODULE_OFF,ADC_FORMAT_INTG16, ・・・ ADC_SAMP_ONのビットを立てて、PARAM1というのに入れたことになる。

#define PARAM1 ADC_MODULE_OFF | ADC_FORMAT_INTG16 | ADC_CLK_MANUAL | ADC_AUTO_SAMPLING_ON |ADC_SAMP_ON

そしてOpenADC10に、それぞれのレジスタに入れるべきビット列を書いた#defineの定数を渡すと、あとはヘッダファイルに書かれたマクロがPICの持つレジスタ、ビットに直して書き込んでくれるという仕組み。

OpenADC10(AD1CON1R, AD1CON2R, AD1CON3R, AD1PCFGR, AD1CSSLR);

ヘッダファイルを読むのが面倒だからとPICの仕様書に書いてあるレジスタ名を使って、

AD1CON1 = 0x020F;
などのように直に書いても構わないのだが、それだと各ビットが何を意味していたか後でわからなくなるというデメリットがある。
<ちょっとマイコン知識 おわり>


ピンは4,5,6ピンが空いているので、AN2,3,4を使うことにした。

SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN2 | ADC_CH0_POS_SAMPLEA_AN3 | ADC_CH0_POS_SAMPLEA_AN4 );

あとは変換開始してループするだけ。
    while(1)
    {
        ConvertADC10();                // 変換開始
        while(BusyADC10());            // 変換完了待ち(1チャネル分)

        int i;
        for(i=0;i<3;i++){
            Result[i] = ReadADC10(i);            // 変換結果読み出し
            printf("%4u", Result[i]);

        }
        printf("\r\n");
    }

ReadADC10(int offset)
この関数にバッファ0からのオフセットを入れれば、

chごとの変換結果が読める。バッファは16個あり、指定した変換モードによってバッファの使われ方が変化する。同じchを3回と指定したら、バッファには3回測定した結果が順番に入るし、5chをスキャンするように指定すれば、バッファ0から4まで順番に各AN入力の変換結果が入る。
#define AD1CSSLR (0xFFFF ^ (SKIP_SCAN_AN2 | SKIP_SCAN_AN3 | SKIP_SCAN_AN4))
スキャンするときにスキップするchの指定をするレジスタ。
SKIPに指定したポートはスキャンするときに読まなくなる。
上記ソースの例では、XOR演算子を使っているので、AN2,3,4だけがスキップ対象から除外される。



最初に動かしたときは、全chの変換結果が2とか3しか出力されず、入力信号としてつないだ半固定抵抗をいくら動かしても変化しなかった。
ANピンを一個ずつ読ませているときはなんとも無かったのだが、スキャンモードにするとちっとも動かない。

#define AD1CON3_REG ADC_SAMPLE_TIME_3 | ADC_CONV_CLK_SYSTEM | ADC_CONV_CLK_6Tcy

原因は、
AD1CON3に設定している、ADC_SAMPLE_TIME_NとかADC_CONV_CLK_NTcyだった。
これらはchの切り替えタイミングやらを制御していて、スキャンモードで時間を短く設定しすぎると、AD変換している途中のピンの信号を放置して次のchに切り替えてしまう。
つまり切り替えタイミングが速すぎになっていたのだ。

ch1個だけなら、ADCの切り替えタイミングがどれだけ短くても、
変換スタートまでにprintfやらなにやらを挟んでいれば問題なく変換できる。
このため、変換タイミングの設定の間違いはわからないまま動作してしまう。
ch1個が動いた設定がそのままマルチchにしたときも動くとは限らない。

<想>
しばらくPICを触らない間に、Cコンパイラも標準で使えるようになった。アセンブラから入門した自分としてはちょっと寂しいけれど、その分面白い機能がモリモリと搭載されるようになった。

やはりPICはユーザーが多いし、日本語の情報も多いし、詳しい本も出ているので割ととっつきやすい。

2016年12月11日 (日)

C++ VisualStudio2013を使ってopenCVのプロジェクトを簡単に試す

最初にVisualStudioに出会った頃はCDが入ったパッケージ版しかなく(6.0とかそれくらいのころ)、もっぱらLCCとかcygwinに入っているgccとかを使ってチマチマとしたプログラムを書いていました。

今は全部VisualStudio+たまにVimとか使うのですが、一旦VisualStudioの便利さを知ってしまうと、なかなか他のツールには手を出そうとは思わなくなりました。

VisualStudioを使っていてもちょっと大変なのは、openCVを使ったプログラムで、これのライブラリの設定については沢山HPがあるのですが、どれもこれもバージョンが少し違ったりするだけで画面が変わったり、パスがちょっと変更になったりで、なかなか載っている通りにはいかんです。
いまはNuGetパッケージという大変便利なものがあって、ボタンひとつでセットアップが完了します。
この記事は、このサイトを見て試した結果を書いています。

そのまんまopenCV3.0を入れてみたのですが、
なぜかopenCVの中で使っている関数がエラーを起こしてうまく行きませんでした。

そこで2.4.10を入れなおしてみました。
インクルードのパスが少し変わっていて、下記のように書くといけました。

#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>

このサイト
の、5.1のソースを使ってテストしました。




2016年12月 4日 (日)

C# イベントを発生させる続編 イベントを使って値を受け渡しする

C#のフォームアプリケーションを作っていると、描画の更新などイベントを使わなければ回りくどかったり、難しい場合がある。イベントを使いこなせても、今度は値を渡したいとか、どこで発生したイベントかを知らせたり、渡した値でハンドラの振る舞いを変えたいとかいろいろ出てくる。

イベントに値を追加して、イベントハンドラでその値を受け取らせたい場合は、クラスを作ってそれを入れ物として値を受け渡せばよい。じつはそういう例がMouseイベントでも見られる。マウスMoveはEventArgsではなく、マウスの座標をイベントオブジェクトの中に入れ込んだMouseEventArgsという型をやり取りしている。

MouseEventArgsをMSDNを調べてみると、EventArgsというクラスを継承して作られていることがわかる。

https://msdn.microsoft.com/ja-jp/library/system.windows.forms.mouseeventargs(v=vs.110).aspx

つまり、これと同様にしてEventArgsを継承したクラスを用意し、そのクラスに好きな値を持たせればよいことになる。

※マウスイベントの試し方:pictureBoxやパネルの中にあるマウス系イベントのMouseMoveイベントを、フォームデザイナのプロパティで設定してみると、MouseEventHandlerというハンドラが追加されることがわかる。

※手順:pictureBoxをフォームに追加して、プロパティ画面の雷マークを押すと、イベントのプロパティが現れる。この中に、マウスという項目がある。このなかのMouseDownという項目の右をダブルクリックすると、pictureBox1_MouseDownという関数がフォームデザイナでForm内に自動的に追加される。

//色が変化したときにイベントで飛ばすプロパティを入れたクラス
    class ColorEventArgs : EventArgs
    {
        Color c;
        public ColorEventArgs(Color c)
        {
            this.c = c;
        }
        public Color color
        {
            get { return c; }
        }
    }

上記入れ物をイベントに載せて飛ばすためには、
・その型を持ったイベントの宣言
・その型を持ったオブジェクトを受けられるデリゲートの宣言
の2つが必要。

        //イベント
        public event ColorEventHandler CurrentColorChange;

        //ColorEventArgs型のオブジェクトを返すようにする
        public delegate void ColorEventHandler(object sender, ColorEventArgs e);

デリゲートに、イベントが発生したときにやってほしい関数を渡すのを忘れずに。

//イベントで処理する関数を登録
          ceditor.CurrentColorChange += new ColorEditor.ColorEventHandler(this.currentColorChanged);

イベントを発生させるには

        public void setCurrentColor(Color c)
        {
            currentColor = c;
            if (CurrentColorChange != null)
            {
                CurrentColorChange(this, new ColorEventArgs(currentColor));
            }
        }

という風にして、先ほどのイベントを関数のようにして呼ぶ。 そのときに引数として先ほど作ったデータの入れ物型のオブジェクトを作って渡す。

すると、イベントハンドラ先で入れ物を受け取り、そのなかのメンバにアクセスして データを取り出すことができる。この中には、イベントの送り先とかデータを何でも入れられる。 これによりどこが発生させたイベントかを判別することで振る舞いを変えたりできる。

イベントハンドラはこんな風にかける。

        private void currentColorChanged(object sender, ColorEventArgs e)
        {
            colorNum_t num = e.color;
        }

ちなみに、他のイベントハンドラと共用で使いたい場合は、一旦基底クラスのEventArgsで受けておいて、 ほしいクラスのキャストをしてやってもよい。

        private void currentColorChanged(object sender, EventArgs e)
        {
            colorNum_t num = ((ColorEventArgs)e).getColorNum();
        }

イベントを使うとかなり色々なことが出来るようになった気になるが、あまり使いすぎたりあちこちからイベントを発生させたりすると、プログラムの流れが全く読めなくなって困る。

2016年11月27日 (日)

C言語 CSVを読み込む関数をsscanfとstrchrで作ってみた。

CSVを読み取る機会は多いものの、C言語の入門本やWebにはなかなかそういうのが書いていなくて、学習の最初のほうは難儀しました。たしか最初に作ったアプリは単語帳ソフトで、CSVで保存しようとしていたのですが、どうにも動作が安定せず挫折したような記憶があります。最初から文字列処理が得意なperlないしはpythonでも使っていればもう少し簡単だったのかもしれませんが。

今までにもC言語で色々な方法でCSVを読み取る関数を作ってきましたが、今回はsscanfを使って作ってみました。


とりあえずべた書きで処理を書いてみた。

#include 

char str[] = "1,2,3,4,5,6,7,8";

void main(void){
       char *adr;
       adr = str;
       for (int i = 0; i < 10; i++){
              int res;
              int col;

              res = sscanf(adr, "%d", &col);
              if (res < 1 || res == EOF){
                     continue;
              }
              printf("%d\n", col);
              adr = strchr(adr, (int)',');
              if (adr == NULL){
                     continue;
              }
              else{
                     adr += 1;
              }
       }
}


関数にまとめてみた。

#include 

char str[] = "1,2,3,4,5,6,7,8";


//戻り値:読み取りに成功=0,取りこぼし=-1
//引数:読み取り対象文字列、読み取り値を入れる配列、読み取り要素数
int csvIntRead(const char *str, int *col, const int num){
	char *adr;

	int succNum = 0; //読み取り成功カウンタ

	//開始位置をセット
	adr = str;

	for (int i = 0; i < num; i++){
		int res;

		//数字を読み取る
		res = sscanf(adr, "%d", &col[i]);
		if (res < 1 || res == EOF){
			//読み取れなかったら終了
			break;
		}
		else{
			//読み取り成功回数をカウント
			succNum++;
		}
		
		//次のカンマを探す
		adr = strchr(adr, (int)',');
		if (adr == NULL){
			//見つからなかったら終了
			break;
		}
		else{
			//カンマがあればその次の文字を開始位置にセット。
			adr += 1;
		}
	}

	if (succNum == num){
		//指定した読み取り数と等しければ成功
		return 0;
	}
	else{
		//どこか取りこぼしがあった場合
		return -1;
	}
}


void main(void){

	int dat[8];

	int res = csvIntRead(str, dat, 2);
	for (int i = 0; i < sizeof(dat)/sizeof(int); i++){
		printf("%d\n",dat[i]);
	}
	printf("\n");

}

2016年11月20日 (日)

python小技 map関数の小技

map関数を使うと、リストの各要素に対して同じ操作を行うことができます。
もっともよく使いそうなのは、CSVから数字を読んでその内容を処理したいとき。
for文で一個ずつint関数に突っ込んでもいいのですけど、内包表記とかmap関数を使うと下記のようにコンパクトに書けます。

たとえばファイルからCSVを一行読み込んだ、こんなリストaがあるとする。
a = ['12','13','14','15','16']

数値の左右にシングルクォーテーションがあるのは、ファイルから数字を読み取ったままだと文字列があるから。それをintに直したいとしたら、直した結果をnに入れていくようにして

n = [ ]
for t in a:
  n.append(int(t))
というようなことを書くこともできるが、map関数を使えば

map(int, a)
とすることもできる。
これは、第一引数が処理関数の指定。第二引数がリストで、リストの要素一個ずつを処理関数に渡してリストの内容を更新していくということ。
C言語で言うところの関数へのポインタを使っているのに近い。

map関数に加えて、lambda式を使えたらこんなこともできる。

map(lambda x: chr(x), range(0x30,0x30+10))
これは、
0x30から10個分のintのリスト要素それぞれを、ASCII文字に変換する。

処理した結果はこうなる。
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

ASCIIで0x30は数字の0という文字になるので、1個目が0で9まで続いている。

lambda x
というのがラムダ式というもので、引数xをとるlambdaという名前の関数があると考えればうまくいく。HPからコピペするだけだったり、丸覚えしようとするとうまくいかなかった。

確かタプルのリストを作ってそれをsort関数でソートしようとしたときに覚えて、
それから手になじむようになった。使い道が無いのに覚えようとすると、これはなかなかに難しい。


2016年11月13日 (日)

C# イベントを発生させる方法

イベントを発生させる方法。
別スレッドに処理をさせている間に、なにか問題があったときに報告させたり、グラフィックの再描画が必要なタイミングをイベントから知るようにするときに使う。
コンソールアプリで実行できる簡単な例を記載。

メインループでは最初にYobidashiというクラスのインスタンスを作ってstartメソッドを呼び出す。あとはreadlineで入力待ちのため何もしない。

Yobidashiのstartでは、SleepClassが作られて、SleepClassの持つイベントにイベントハンドラが登録される。SleepClassのオブジェクトがStartを呼んだあとは、Startメソッドのなかで3秒間待つ。3秒たったらTime(this,EventArgs.Empty)が実行されて、ここでイベントが発行される。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace CS_eventTameshi
{
    class Program
    {
        static void Main(string[] args)
        {
            Yobidashi y = new Yobidashi();
            y.start();

            Console.ReadLine();
        }
    }

    class Yobidashi
    {
        public void start()
        {
            SleepClass clsSleep = new SleepClass();

            //イベントハンドラの追加
            clsSleep.Time += new EventHandler(this.SleepClass_Time);
            clsSleep.Start();
        }

        private void SleepClass_Time(object sender, System.EventArgs e)
        {
            //起動してから3秒後におkが表示される。エンター押すと終了
            Console.WriteLine("ok!");
        }
    }
    

    class SleepClass
    {
        public event EventHandler Time;

        public void Start()
        {
            System.Threading.Thread.Sleep(3000);
            if (Time != null)
            {
                Time(this, EventArgs.Empty);
            }
        }

        
    }
}

<自分の勝手な理解>
イベントにイベントハンドラをセットするのは、 C言語で言うところの、関数ポインタをセットしておくのに似ている。

+=という操作をすると、そこで指定した関数へのポインタがセットされる。 イベントを発生させる側で、イベントの関数を実行すると、 その中にセットされていたイベントハンドラへのポインタがロードされて、 今度はそれが実行されるようになる。

そのために、例示したソースの中で
if (Time != null)

というのがあるが、これはeventに何も入っていなければそこへ飛びようがないので、 中身がなければ処理しないということ。

2016年11月 6日 (日)

C# 図形の塗りつぶし色の変え方

C# Csharp グラフィック 塗りつぶし四角形の色の変え方。システム色以外のRGBによる設定

MSDNのドキュメントはどれもすさまじい日本語で、英語の原文を読んだほうがよほどましな気がした。キーワード検索すると必ず日本語のページが出てくるので、最後にenglishを入れて検索する。

Rectangle構造体 これはあくまでも構造体のようで、描画をするためのものではない。
サイズを指定するようなもの
https://msdn.microsoft.com/ja-jp/library/system.drawing.rectangle(v=vs.110).aspx

上のページから、備考の欄にFillRectangleメソッドへのリンクが貼ってある。
https://msdn.microsoft.com/ja-jp/library/yysstebh(v=vs.110).aspx

このページの構文のところで
BrushとRectangleを引数として受け取るということがわかる。

Brushのリンクを辿ると、
https://msdn.microsoft.com/ja-jp/library/system.drawing.brush(v=vs.110).aspx
四角形、楕円、扇形、多角形、およびパスなどのグラフィカル形状の内部を塗りつぶすに使用されるオブジェクトを定義します。
とあるが、abstractがついているのでこのままではオブジェクトを作れない。
このクラスを継承しているクラスが他にあるということ。

解説のところに、
これは抽象基本クラスであり、インスタンス化することはできません。 Brush オブジェクトを作成するから派生したクラスを使用して Brush, など SolidBrush, 、TextureBrush, 、および LinearGradientBrushです。
ということが書かれているので、SolidBrushのリンクを辿ってみた。

すると、ようやくやりたいことが見えてきた。
https://msdn.microsoft.com/ja-jp/library/system.drawing.solidbrush(v=vs.110).aspx

この中の例をみると、
Color customColor = Color.FromArgb(50, Color.Gray);
SolidBrush shadowBrush = new SolidBrush(customColor);

となっていたので、
Color.FromArgb
とVisualStudio内で打ち込んでみて、Intelisenseで表示される、
RGBをint値でそれぞれ指定するメソッドを選べばいいことがわかった。

最終的にコードはこんな風になった。

SolidBrush brush = new SolidBrush(Color.FromArgb(150,0,30));
g.FillRectangle(brush, 0,0,100,100);

SolidBrushにColorを渡してまずはブラシをつくり、
それを図形を描画するメソッドに渡せば、その色で塗りつぶしが実行できるということ。

2016年10月30日 (日)

Python lsコマンドからファイルのリストを受け取るやりかた

ファイルのリストを受け取ってpythonで処理しようとするときに、lsでファイルのリストを一旦ファイルに作ってから、それを読ます方法がありますが、ちょっと面倒なので直接やる方法を考えていました。

subprocessというのを使うと、外部のコマンドやプログラムを走らせることができる。

しかしlsからの結果を受け取ろうとして、
a = subprocess.call("ls -l")
とやってしまうと何も受け取れない。

これをやるくらいならpythonで用意されている、
import glob
files = glob.glob("*.JPG")

を使ったほうが速いし覚えやすい。
globのimportのやり方を間違うと少し嵌りそう。

次はパイプを使ってやる方法。


from subprocess import *

output = Popen( ["ls","-l"],stdout=PIPE)

while True:
    line = output.stdout.readline()
    if not line:
        break
    print line,

ちなみに、"ls -1"とすれば、ファイル名のリストが一行ずつで出力される。

インタラクティブシェルでいろいろ試せば見えてくる。

from subprocess import *
でとりあえずインポートしたら、

output = Popen(["ls","-l"],stdout=PIPE)
をつかって出力を受け取る。
ただし、ここまでだとstdoutとstderrが混ざったオブジェクトの状態なので、
stdoutだけを取り出すようにする。

output.stdout.read()
を使えば、すべての中身を読みだせる。
これを何かで受け取って、

a = output.stdout.read()
としたあと、
lines = a.split('\n')
とすれば、
簡単に行ごとの中身が手に入る。

ほかにも、aからそのままの状態で、
a.count(".py")
とすれば、この単語が何個あるか数えられたり、

len(lines)
とすれば何行出力されたかわかる。ただしls -lの一行目は、ファイル名ではない。




2016年10月23日 (日)

C言語 関数ポインタを使って遊ぶ

可変長引数を使うと、いくらでも引数を渡せるということがわかったので、それを使って関数ポインタを渡したらどうなるかやってみました。まずは関数ポインタというのがどんな風に使われるかの基本をおさらいしてから、この記事の本題に移っていきます。




まず、九九の表を作れといわれたとする。これから何が起こるのか知らされもせず。

#include 

int mult(int a, int b){        return a*b; } void dispCalc(void){        for (int i = 0; i < 10; i++){               for (int j = 0; j < 10; j++){                      int res = mult(i, j);                      printf("%3d,", res);               }               puts("");        } } void main(void){        dispCalc();        getchar(); }

じゃあ足し算の表は?となったとき、mult関数とは別にdispCalcAddでも作ればよいのだが、 もっと良い方法に、計算方法を書いた関数へのポインタを渡すというのがある。

#include 

int mult(int a, int b){        return a*b; } int add(int a, int b){        return a+b; } void dispCalc(int (*func)(int,int)){        for (int i = 0; i < 10; i++){               for (int j = 0; j < 10; j++){                      int res = func(i, j);                      printf("%3d,", res);               }               puts("");        } } void main(void){        dispCalc(mult);        dispCalc(add);        getchar(); }


ここまでが関数ポインタの概要。同じ機能を実現する関数は、振る舞いに応じて別々に関数を作らなくても、どのような振る舞いをするかを関数ポインタで渡してやればよいということ。

ここからが本題。じゃあ九九の表と足し算の表のあと、もう一回九九の表を書け。とか、ファイルに何の表をどの順番で書くか指示が書いてあるからその順番で書けとか、もうむちゃくちゃ色々な指示が出たらどうするか。そこで可変長引数を使ってみたら、

#include 

#include

//計算をする関数のポインタをtypedefする typedef int(*calc)(int, int); int mult(int a, int b){        return a*b; } int add(int a, int b){        return a + b; } void execList(int n,int a,int b, ...){        va_list argv;        va_start(argv, b);        for (int i = 0; i < n; i++){               calc pnt = va_arg(argv, calc);               int result = (pnt)(a, b);               printf("res %d\n", result);        }        va_end(argv); } void main(void){        //掛け算、足し算、掛け算        execList(3, 5, 2, mult, add, mult);        getchar(); }


関数のポインタをtypedefしておくと少しでも見やすくなる。また型チェックで警告も出してもらえるかもしれない。


ゲームのキャラクターの振る舞いをセットしたり、いろんなログを決まった順番で加工したり。色々使えそうだ。

2016年10月16日 (日)

C言語 sscanfの続きとvsscanfとの違い。それぞれの使いどころ

前回の記事でsscanfを使って文字列の読み込みをやりました。今日はもう少しsscanfを使ったテクニックを掘り下げます。

scanf、sscanfのほかに、vsscanfというのがあるのですが、これは可変長引数が受け取れるようになっています。可変長引数というのは、引数を何個でも受け取れるということ。しかしどこで使えばいいのか。一見sscanfとの違いがわからずもやもやしてしまいました。

以下、vsscanfを使ったときのメモです。

下記のような3種類のログがファイルの中に行毎に混ざって存在しているとする。

"channels  ,0(0),1(3),2(?),3(-),4(R),5(-)"
"answers   ,ss,0,sd,2,us,1"
"handles    ,-10         ,10         ,102    "

ログを読むための書式文字列は下記のようになった。読んだ後の値は構造体や配列に入れる。

int ret = sscanf(buff, "%10s ,%10d ,%10d ,%10d"
                     ,enc.name,&enc.left,&enc.right,&enc.time);
char ch[6];
int ret = sscanf(buff, "%s ,0(%c),1(%c),2(%c),3(%c),4(%c),5(%c),"
              , enc.name, &ch[0], &ch[1], &ch[2], &ch[3], &ch[4], &ch[5]);
       int res[3];
       char name[32];

       int ret = sscanf(buff, "%s ,2d,%d,3d,%d,us,%d"
              , name,&res[0],&res[1],&res[2]);

vsscanfを使うには可変長引数の使い方についての知識が必要で、それを使うにはstdarg.hをインクルード下上で、下記のようなラップ関数を用意する必要がある。そのラップ関数に書式文字列と、入れ物のアドレスを渡して使う。

//書式を指定してレコードを読みこむ
void input_vsscanf(char *s, char *format, ...){
	va_list argv;

	va_start(argv, format);
	vsscanf(s, format, argv);
	va_end(argv);

	return 0;
}

//この関数からvsscanfのラップ関数を呼び出す
void logReader(char *str){
	if (NULL != strstr(str, "answers")){
		input_vsscanf(str, "%s ,2d,%d,3d,%d,us,%d"
			, name
			, &result_log.res_ss, &result_log.res_sd, &result_log.res_us);
	}
}

やっているうちにvsscanfの強みには気がついたものの、引数については相変わらず一個ずつ指定しないといけないので、いい使い方例を書くまでにはいたりませんでした。今のところ用途は無いのです が、書式文字列を変えることによって、読み込みたい値が書いてある場所を任意に指定するとか。たとえば、同じ情報が書かれたログだけれど書式がちょっとず つ違っていて、そういうときに場合分けして読みたいとか。そういう用途なのかと思っていました。

追記
vsscanf以外のvprintfや他の関数についてみていたら、どうやらデバッグ用関数を作るときに有用なことがわかりました。たとえば読み込んだ文字列から値を取り出すと同時に、ファイルに書き出すとかUDPでどこかへ転送するとか。そういうデバッグ用関数を作るときに使えそうです。

今日のところはここまで。

«C言語 可変長引数の使いかた