PSoC – CY8C29466の14ビットADコンバータを内蔵マルチプレクサで切り替えて複数ピンで使う

2010 年 7 月 28 日 shokai コメントはありません

14ビットのADコンバータ1つと、AMUX4(4チャンネルアナログマルチプレクサ)を組み合わせると複数のピンからAD変換が使える。
というかPSoC Designer4.xでADCINC12を使っていた頃は、ADCINC12を複数置くことができたのにPSoC Designer5.0では1つしか配置できなくて困った。
でもよく考えたらマルチプレクサ使う方が自然だな。


アナログブロックの接続
マルチプレクサを切り替えるとPort0の0,2,4,6番ピンに接続できる
cfab931ebc6fd7a15179e8377e1a847c


ピン配置
右上のADC以外は関係ない
e9c55cbf49c94421f1ef6f24400f6531


PGAの設定
1.0倍で、AMUXがあるアナログマルチプレクサ1に接続する。
e838b8bcea333aaa21f7249301632629


ADCINCの設定
Negative Inputのソースは一応指定するが、GainをDisconnectedにすると使わないようにできる。
5f959013ed6c5d34154921a08c1cf097


AMUX4の設定
Interconnect Viewには表示されないけど、配置はされてる。アナログマルチプレクサ1として設定する。
ff18fb9db0458d481244adfc38113cfb


AMUX4の他にも8もあるので、まあたくさん増やせる。


UARTのTX8で、14bitADCINCの値を送る。
TX8だけを使う設定は橋本商会 ≫ PSoC – TX8モジュールに書いた

void wait(int n){
    while(n--);
}

// AMUX4_PORT0_0 => 0x00
// AMUX4_PORT0_2 => 0x01
// AMUX4_PORT0_4 => 0x02
// AMUX4_PORT0_6 => 0x03
int get_adc(BYTE amux_channel){
    AMUX4_InputSelect(amux_channel);
    wait(10);
    ADCINC_GetSamples(0);
    while(!ADCINC_fIsDataAvailable());
    return ADCINC_iClearFlagGetData();
}

int ad;
BYTE ad_pin;
char buf[6];

void main(void){
    M8C_EnableGInt;
    AMUX4_Start();
    PGA_1_Start(PGA_1_HIGHPOWER);
    ADCINC_Start(ADCINC_HIGHPOWER);
    TX8_Start(TX8_PARITY_NONE);

    for(;;){
        for(ad_pin = 0; ad_pin < 4; ad_pin++){
            ad = get_adc(ad_pin);
            TX8_PutChar(ad_pin+'0');
            TX8_CPutString(":");
            TX8_PutString(intToStr(ad,buf));
            TX8_PutCRLF();
        }
    }
}
AMUX4を切り替えた直後にAD変換を開始するのがなんとなく嫌だった(物理的に回路が切り替わっているわけだし)ので、一瞬waitを入れてからAD変換するようにした。



string.hのitoa関数がおかしいので代わりを作った
// intの桁数を返す
char getDigit(int n){
    char i;
    i = 0;
    while(n>0){
        n /= 10;
        i++;
    }
    return i;
}

// int->String変換
// char buf[6]
char *intToStr(int n, char *buf){ // 変換する数、作業領域
    int i, digit;
    digit = getDigit(n); // 桁数
    for(i = digit-1; i >= 0; i--){ // intは最大5桁
        buf[i] = n%10+'0';
        n /= 10;
    }
    buf[digit] = '\0'; // 行末
    return buf;
}

OpenCVで画像サイズを求めるgearman workerをdaemontoolsで管理する

2010 年 7 月 26 日 shokai コメントはありません
カテゴリー: 未分類 タグ: , , , , , ,

OpenCVで画像のサイズを求めるgearman workerを作って、Rubyから呼ぶで作ったworkerをpreforkさせて、そいつらをdaemontoolsで管理できるようにした。あらかじめCPU個数+いくつかforkしておくと、CPUが複数あるマシンを生かせるし、解析前にlibcurlで画像を取得している時のI/O待ちが少なくなって良い。(この記事のworkerはlibcurl使ってない版だけど)
あと、返り値は自分で作ったjson_builder.hを使って返すようにした。

なにげに大量の画像の中からダウンロード失敗した破損画像を見つけるのに重宝している。

まずdaemontoolsをインストールしておく

gearmandもdaemontoolsで自動起動するようにしておく。


daemontoolsで管理できるようにする。
普通にforkしただけだと、daemontoolsでsvc -dしてプロセスを止めようとしてもforkした子プロセスの方が止まらない。

Perlの場合の良い例があった。
How to manage Gearman worker processes. – TokuLog 改メ tokuhirom’s blog
Parallel::Preforkを使っている。Parallel::Preforkのソースを読んでみたら、trap_signalsオプションで親プロセスがSIGTERMとSIGHUPをフックして、子プロセスにkillを送っていた。
よく考えたら普通のforkで親が子を殺すというやつだった。


Parallel::Preforkと同じ様にやる。
forkした後親が子のpidのリストを持っておいて、SIGTERM/SIGHUPをフックして、子を全部killする処理を追加した。

daemontoolsのrunスクリプトはこれ
#!/bin/sh
exec 2>&1
exec setuidgid sho /Users/sho/src/gearmand-study/imgsize/imgsizeWorker -s localhost -p 7003 --fork 5
起動すると5個にプロセスが増える。親はdaemontoolsのsuperviseが管理してくれる。
これでsvc -dとか-uとかすればまとめて起動終了するようになった。

imgsizeWorker.cpp
// 画像サイズを返すgearman worker
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include <boost/program_options.hpp>
#include <boost/regex.hpp>
#include <boost/format.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/any.hpp>
#include <libgearman/gearman.h>
#include "json_builder.h"

using namespace boost;
using namespace std;

tuple<int, int> get_size(const string& fileName); // 画像のwidth,heightを返す
map<string,any> imgsize(const string& fileName); // gearman workerとしてclientに返すためのJSON Objectを作る
void *job_imgsize(gearman_job_st *job, void *cb_arg, size_t *result_size, gearman_return_t *ret_ptr);
void on_exit_signal(int sig);
vector<int> pids;

int main(int argc, char* argv[]) {
  program_options::options_description opts("options");
  opts.add_options()
    ("help,h", "helpを表示")
    ("server,s", program_options::value<string>(), "gearmanサーバーのアドレス")
    ("port,p", program_options::value<int>(), "gearmanサーバーのport番号")
    ("fork", program_options::value<int>(), "preforkする数")
    ("test,t", program_options::value<string>(), "gearman worker単体テスト用query");
  program_options::variables_map argmap;
  program_options::store(parse_command_line(argc, argv, opts), argmap);
  program_options::notify(argmap);

  if(!argmap.count("help")){
    if(argmap.count("test")){
      cout << "---test---" << endl;
      string gearman_param = argmap["test"].as<string>();
      cout << json_builder::toJson(imgsize(gearman_param)) << endl; // 単体でworkerとしてのテスト
      return 0;
    }else if(argmap.count("server") && argmap.count("port")){
      if(argmap.count("fork")){
int i, pid;
for(i = 1; i < argmap["fork"].as<int>(); i++){
  pid = fork();
  if(pid == 0){ // 子プロセス
    pids.clear();
    break;
  }
  else{ // 親プロセス
    pids.push_back(pid);
    cout << str(format("fork:%d - parent:%d child:%d") % 
i %
getpid() %
pid) << endl;
  }
}
      }
      if(pids.size() > 0){ // 親プロセスの終了シグナルをフックする
signal(SIGTERM, on_exit_signal);
signal(SIGHUP, on_exit_signal);
      }
      gearman_worker_st worker;
      gearman_worker_create(&worker);
      string g_server = argmap["server"].as<string>();
      int g_port = argmap["port"].as<int>();

      struct hostent *g_host = gethostbyname((char*)g_server.c_str());
      string g_server_addr = str(format("%d.%d.%d.%d") %
 (uint)(uchar)g_host->h_addr[0] %
 (uint)(uchar)g_host->h_addr[1] %
 (uint)(uchar)g_host->h_addr[2] %
 (uint)(uchar)g_host->h_addr[3]);

      gearman_worker_add_server(&worker, g_server_addr.c_str(), g_port);
      gearman_worker_add_function(&worker, "img_size", 0, job_imgsize, NULL);
      cout << str(format("---start worker (%s:%d)---") %
  g_server_addr % g_port) << endl;
      while(true) gearman_worker_work(&worker); // workerとして待機
      return 0;
    }
  }
  cerr << "server,portが必要です" << endl;
  cerr << opts << endl;
  return 1;
  
}

// opencvで画像サイズを取得
tuple<int, int> get_size(const string& fileName){
  IplImage *img = cvLoadImage(fileName.c_str());
  if(!img){
    return make_tuple(-1, -1);
  }
  else{
    int width = img->width;
    int height = img->height;
    cvReleaseImage(&img);
    return make_tuple(width, height);
  }
}

// 画像サイズを取得してgearman serverに返すJSON Objectを作る
map<string,any> imgsize(const string& fileName){
  map<string,any> result_m;
  int width, height;
  tie(width, height) = get_size(fileName);
  if(width > 0 && height > 0){
    result_m["width"] = width;
    result_m["height"] = height;
  }
  else{
    result_m["error"] = string("image load error");
  }
  return result_m;
}

// gearman worker job
void *job_imgsize(gearman_job_st *job, void *cb_arg, size_t *result_size, gearman_return_t *ret_ptr){
  string fileName = (char*)gearman_job_workload(job);
  cout << fileName << endl;
  string result_str = json_builder::toJson(imgsize(fileName));
  cout << " => " << result_str << endl;
  char *result = (char*)strdup(result_str.c_str());
  *result_size = result_str.size();
  *ret_ptr = GEARMAN_SUCCESS;
  return result;
}

void on_exit_signal(int sig){
  for(int i = 0; i < pids.size(); i++){
    cout << str(format("kill (pid:%d)") % pids[i]) << endl;
    if(kill(pids[i], SIGKILL) < 0){
      cerr << str(format("kill failed (pid:%d)") % pids[i]) << endl;
    }
  }
  exit(0);
}

Android – アプリの設定画面を作る

2010 年 7 月 23 日 shokai コメントはありません
カテゴリー: 未分類 タグ: , , ,

画面遷移するアプリを作ったこと無かったのでやってみた。
AndroidではIntentで別のActivityを呼び出して画面遷移する。遷移ついでにIntentオブジェクトに値を入れて渡したり、返り値を受け取ったりする。

作った物はgithubに置いた



■動作
名前設定ボタンを押したらIntentで別のActivityに遷移して、そこで名前を設定する。保存(終了)すると前のActivityに戻る。もう一度やると、設定ActivityのEditTextの中身がさっき保存した値になっている。
device device2 device3



■Activityを追加する
  1. android.app.Activityを継承した新しいJavaクラスを追加する。ConfigActivity.javaにした。
  2. res/layout/ の中にレイアウト用XMLファイルを作る。既にあるmain.xmlをコピー&リネームでいい。config.xmlにして中身を適当に編集
  3. レイアウトを.javaに読み込む。config.xmlを作った時点でresourceが更新されているので、onCreate関数の中で setContentView(R.layout.config); すればいい。
  4. AndroidManifest.xmlを編集、<application></application>の中に新しいActivity名を書く
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="org.shokai"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".MainActivity"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity android:name=".ConfigActivity" android:label="@string/app_name">
            </activity>

        </application>
        <uses-sdk android:minSdkVersion="7" />

    </manifest> 
    名前は .java ファイルの名前に合わせる。
これでConfigActivityを呼び出す準備ができた。



■ソースコード
返り値が必要な場合はstartActivityForResultで呼び出す。第二引数の数値はrequest codeで、onActivityResultで返り値を受け取る時に同じ値が戻ってくるので本物かどうかチェックするのに使う。
request codeは毎回違う値を生成した方がいいのかもしれない
MainActivity.java
package org.shokai;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.*;

public class MainActivity extends Activity implements OnClickListener{
    
    private Button buttonOpen;
    private Logger logger;
    private TextView textViewName;
    private String name;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        logger = new Logger(this.getResources().getString(R.string.app_name));
        textViewName = (TextView)findViewById(R.id.TextViewName);
        buttonOpen = (Button)findViewById(R.id.ButtonOpen);
        buttonOpen.setOnClickListener(this);
    }

    public void onClick(View arg0) {
        switch(arg0.getId()){
        case R.id.ButtonOpen:
            logger.v("click : ButtonOpen");
            Intent it = new Intent(this, ConfigActivity.class);
            if(name != null) it.putExtra("name", name);
            this.startActivityForResult(it, 1234);
            break;
        }
    }
    
    protected void onActivityResult(int reqCode, int resCode, Intent it) {
        logger.v("responseCode:"+resCode+", requestCode:"+reqCode);
        switch(reqCode){
        case 1234:
            setName(it.getStringExtra("name"));
            break;
        }
    }
    
    private void setName(String name){
        this.name = name;
        this.textViewName.setText(name);
    }
}


onCreate関数内でgetIntent()すると、MainActivityから呼び出したIntentオブジェクトが手に入る。この中から値を取り出す。
Intent.getStringExtra(key) も Intent.getExtras().getString(key) も同じ意味の関数だが、getExtras().getString(key) に存在しないkeyを与えると強制終了するので getStringExtra(key) の方を使った。こちらはnullが返ってくる。
finish()するとMainActivityに戻るが、戻る時にsetResultしておいて値を戻す。setResultは終了コード的な意味で、MainActivityのonActivityResultの第二引数に入る。
ConfigActivity.java
package org.shokai;

import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.os.Bundle;
import android.widget.*;

public class ConfigActivity extends Activity implements OnClickListener{

    private EditText editTextName;
    private Button buttonSave;
    private Logger logger;
    
    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        logger = new Logger(this.getResources().getString(R.string.app_name));
        setContentView(R.layout.config);
        editTextName = (EditText)findViewById(R.id.EditTextName);
        buttonSave = (Button)findViewById(R.id.ButtonSave);
        buttonSave.setOnClickListener(this);
        Intent it = getIntent();
        String name = it.getStringExtra("name");
        if(name != null) editTextName.setText(name);
    }

    public void onClick(View v) {
        switch(v.getId()){
        case R.id.ButtonSave:
            logger.v("click : ButtonSave");
            Intent it = new Intent();
            it.putExtra("name", this.editTextName.getText().toString());
            this.setResult(0, it);
            this.finish();
            break;
        }
    }

}


■参考

PSoC CY8C29466のI2CHWモジュールで通信

2010 年 7 月 21 日 shokai コメントはありません
カテゴリー: 未分類 タグ: , , , , ,

PSoC CY8C29466のI2Cモジュールを試した。
I2Cは2本の信号線の上にmaster/slaveの2種類のノードを合計100個以上載せて相互に通信できるプロトコル。動作速度が違う部品同士をより少ない結線数で相互接続させる事を目的として、1992年にPhilipsが作って2004年に特許が切れている。今ではほとんどのマイコンにI2C通信をするモジュールがついているし、外部メモリやセンサーなどもI2Cで値を返す物がある。linuxのkernelにも入っていて(/usr/include/linux/i2c.hとか)マザーボード上の温度センサとかに使われているらしい。

このへんに詳しく書いてある。

特に仕様書pdfはちゃんと読んでみたらかなり勉強になったのでオススメ。
プログラムはCypress公式のUser Module Datasheetのp.39,40ものを参考にした。


■作った物
PSoC Designer 5.0+SP6で作ったプロジェクトまるごとgithubに置いた
git clone git://github.com/shokai/i2c-uart-cy8c29466.git
で取得できる。


3つ並んでいるPSoCマイコンのうち一番下がmasterで、上2つがslaveデバイス。デバイスが接続されている2つの線は2kΩの抵抗(1kΩ2つ)を介してVCCと接続されpull upされている。I2Cの接続構成とプルアップ抵抗に適切なプルアップ抵抗の計算方法が書かれている。電源5Vで100kbpsか400kbpsなら2kΩ〜5kΩが最適値とのこと。
R0015277.JPG


masterに付いているボタンを押すと、2つのslaveにLED点灯命令を送る。ボタンを離すと消灯する。
R0015274.JPG


slave側のボタンは、それぞれmasterの2つのLEDの点灯/消灯に対応している。
R0015275.JPG
R0015276.JPG


masterはslaveをコントロールするだけでなく、I2Cとシリアル通信のブリッジになっている。
slaveからのデータをslaveのアドレス名と共にUARTでパソコンに中継する。また、パソコンからmasterマイコンに’U'か’D'の文字を送る事と、masterは全slaveにLED点灯/消灯を送る。
UARTとRS232Cのレベル変換にはADM3202を使った。


だいたいそういう内容のビデオ




■デバイス設定
master側。
I2C master device setting
パソコンとのシリアル通信で9600bpsを作るためにVC1,VC3を設定している。動作電圧やCPUクロックなどはデフォルト値。

I2C master I2CHW module setting
single masterモードでモジュールを置いた。
100kbpsでPORT1の5番と7番ピンをI2Cに使う。bufferはプログラム中で自分で用意したBYTE配列を使ってもらうように設定。

I2C master UART module setting
シリアル通信で9600bpsが出るようにUARTモジュールを設定。
橋本商会 » PSoC – CY8C29466でUART受信割り込みでくわしく書いた。

I2C master Pinout setting
3つのLEDに使うピンは出力をStrongにして3V出るようにした。
タクトスイッチにつなぐPort2の2番ピンは両エッジ入力割り込みを設定しつつ内部でプルアップして、ブレッドボード上でも100Ωの抵抗でプルダウンする。こうするとチャタリングが起きない。


I2C master Pin layout
ピン配置図

I2C master modules connection
アナログブロックは使っていないので上の方だけ。
UARTがPort2の4,5番ピンから出るように引き出した。



slave側。
I2C slave device setting
Global Resourceは初期値のまま


I2C slave I2CHW module setting
I2CHWモジュールををslaveで置いた。速度はmasterに合わせる。
slaveデバイスのアドレスは7bitだが、0×00~0×10までの16個のアドレスはプロトコルに予約されているので、0×11からの112個が使える。
今回はmasterのプログラムで0×11~0×20のデバイスがいるか確認しながら動作するようにしているので、アドレス17~32のどれかを使えばいい。


I2C slave Pinout setting
masterと同じ様にLEDとタクトスイッチ用のピンを設定。LEDは1つだけ。


I2C slave Pin layout


I2C slave modules connection
なにも結線していない


■プログラム
master側
slaveアドレス0×11から0×20までのデバイスに、順にTXバッファを書き込み、RXバッファに読み込む。送受信を待っている間、timeout_countをカウントアップし続けて、閾値以上になるとタイムアウトするようにしている。これで存在するかわからないslaveとやりとりできるし、動作中にslaveデバイスが増えても問題なくネットワークに参加させられる。

UARTの受信やピン入力の検出は割り込みで処理している(橋本商会 » PSoC – CY8C29466でUART受信割り込みで書いた)のだが、I2Cはメインループの中で処理している。I/OからのイベントでUARTぐらいなら使ってもいいけど、16個のslaveとのやりとりを割り込みの中でやると多重割り込みが起こりやすいので、各ルーチンからbufferを読み書きしてそれを定期的にslaveと共有するという方式にした。
本番ではdigital blockを1つ消費するだけで使える8bit Timerモジュールで定期的に回すようにすると良いと思う。

main.c
// I2C-UART master
// CY8C29466-24PXI(DIP Package)
// PSoC Designer 5.0 + SP6
// IMAGECRAFT C Compiler
#include <m8c.h>        // part specific constants and macros
#include <I2CHW_1Common.h>
#include <I2CHW_1Mstr.h>
#include "PSoCAPI.h"    // PSoC API definitions for all User Modules
#define _BV(BIT) (1<<BIT)
#define sbi(BYTE,BIT) (BYTE |= _BV(BIT))
#define cbi(BYTE,BIT) (BYTE &= ~_BV(BIT))

#define LED_ON() sbi(PRT2DR, 0) // LED
#define LED_OFF() cbi(PRT2DR, 0)
#define LED2_ON() sbi(PRT1DR, 6)
#define LED2_OFF() cbi(PRT1DR, 6)
#define LED3_ON() sbi(PRT1DR, 4)
#define LED3_OFF() cbi(PRT1DR, 4)

#define BTN_PORT PRT2DR // push button
#define BTN_BIT _BV(2)

#define BUF_SIZE 8
BYTE buf_tx[BUF_SIZE]; // I2C buffer
BYTE buf_rx[BUF_SIZE];
BYTE status; // I2C status
BYTE slave; // slave address
#define I2C_TIMEOUT 128 // 長時間応答が返ってこないslaveデバイスを無視する
BYTE timeout_count, i;

void main(void)
{
    M8C_EnableGInt; // enable global interrupt
    M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);
    UART_1_CmdReset(); // uart init
    UART_1_IntCntl(UART_1_ENABLE_RX_INT); // enable receive interrupt
    UART_1_Start(UART_1_PARITY_NONE);
    LED_ON();
    UART_1_CPutString("start");
    LED_OFF();
    I2CHW_1_Start();
    I2CHW_1_EnableMstr();
    I2CHW_1_EnableInt();
    for(;;){
        for(slave = 0x11; slave < 0x21; slave++){
            I2CHW_1_bWriteBytes(slave, buf_tx, BUF_SIZE, I2CHW_1_CompleteXfer); // master->slave
            timeout_count = 0;
            for(;;){
                if(I2CHW_1_bReadI2CStatus() & I2CHW_WR_COMPLETE ||
                   timeout_count++ > I2C_TIMEOUT) break;
            }
            I2CHW_1_ClrWrStatus();
            
            I2CHW_1_fReadBytes(slave, buf_rx, BUF_SIZE, I2CHW_1_CompleteXfer); // slave->master
            timeout_count = 0;
            for(;;){
                if(I2CHW_1_bReadI2CStatus() & I2CHW_RD_COMPLETE ||
                   timeout_count++ > I2C_TIMEOUT) break;
            }
            I2CHW_1_ClrRdStatus();

            switch(slave){
            case 0x11:
                if(buf_rx[0] == 'u') LED2_OFF();
                else if(buf_rx[0] == 'd') LED2_ON();
                break;
            case 0x12:
                if(buf_rx[0] == 'u') LED3_OFF();
                else if(buf_rx[0] == 'd') LED3_ON();
                break;
            }
            
            while(!(UART_1_bReadTxStatus() & UART_1_TX_BUFFER_EMPTY)); // slaveからの受信データをシリアル通信出力
            UART_1_CPutString("I2C:");
            UART_1_PutSHexByte(slave); // slaveアドレス
            UART_1_CPutString(",");
            UART_1_PutString(buf_rx); // slaveからの受信データ
            UART_1_PutCRLF();
            for(i = 0; i < BUF_SIZE-1; i++) buf_rx[i] = '\0'; // 受信バッファを初期化
        }
    }
}


// UART受信割り込み
#pragma interrupt_handler INT_UART_RX
void INT_UART_RX(void){
    char recv_data;
    recv_data = UART_1_cGetChar(); // read UART
    UART_1_PutChar(recv_data); // echo
    switch(recv_data){
    case 'U':
        LED_ON();
        buf_tx[0] = 'A'; // slaveにLED点灯を指示
        UART_1_CPutString("LED:ON\r\n");
        break;
    case 'D':
        LED_OFF();
        buf_tx[0] = 'B'; // slaveにLED消灯を指示
        UART_1_CPutString("LED:OFF\r\n");
        break;
    }
}

// I/Oピン状態変化割り込み
#pragma interrupt_handler INT_GPIO
void INT_GPIO(void){
  if(BTN_PORT & BTN_BIT){ // ボタンが押されている時
    LED_ON();
    buf_tx[0] = 'A'; // slaveにLED点灯を指示
    UART_1_CPutString("LED:ON\r\n");
  }
  else{
    LED_OFF();
    buf_tx[0] = 'B'; // slaveにLED消灯を指示
    UART_1_CPutString("LED:OFF\r\n");
  }
}




slave側
slaveもmasterと同じ様にglobalにバッファを用意して、そこに適当に現在の状態を読み書きし、定期的にメインループ内でmasterと通信し共有している。この方が素直に書きやすい。
LEDとボタンの状態を結びつける情報を1byte目に置いているが、それ以外の情報をやりとりする場合もシリアライズとかして通信するのではなく2byte目以降を使う方がいい。あくまで必要な変数を定期的に同期させられる、分散オブジェクト風に書いた方がすっきりする(PSoCのstring.hがおかしいという理由もあるけど)


あと、ややこしいのがslaveなのでI2CHW_1_InitWrite関数を呼ぶとmasterからslaveへの書き込みが起こる。受信する。送信ではない。また、I2CHW_1_bReadI2CStatus() での状態チェックも、dataに使っている信号線1本の状態のチェックのためなので呼び出すタイミングがmasterと逆になる。このへんややこしいのであまり触りたくないから、通信は隔離された別ループでやってglobalに置いた共用bufferを同期させるという方法にした。

main.c
// I2C-UART slave
// CY8C29466-24PXI(DIP Package)
// PSoC Designer 5.0 + SP6
// IMAGECRAFT C Compiler

#include <m8c.h>        // part specific constants and macros
#include <I2CHW_1Common.h>
#include "PSoCAPI.h"    // PSoC API definitions for all User Modules
#define _BV(BIT) (1<<BIT)
#define sbi(BYTE,BIT) (BYTE |= _BV(BIT))
#define cbi(BYTE,BIT) (BYTE &= ~_BV(BIT))

#define LED_ON() sbi(PRT2DR, 0) // LED
#define LED_OFF() cbi(PRT2DR, 0)
#define BTN_PORT PRT2DR // push button
#define BTN_BIT _BV(2)

#define BUF_SIZE 8
BYTE buf_rx[BUF_SIZE]; // I2C buffer
BYTE buf_tx[BUF_SIZE] = {'x'};
BYTE status; // I2C status

void main(void)
{
    M8C_EnableGInt;
    M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);
    I2CHW_1_Start();
    I2CHW_1_EnableSlave();
    I2CHW_1_EnableInt();
    for(;;){
        status = I2CHW_1_bReadI2CStatus();
        if(status & I2CHW_WR_COMPLETE){ // master->slave
            I2CHW_1_ClrWrStatus();
            I2CHW_1_InitWrite(buf_rx, BUF_SIZE);
        }
        if(status & I2CHW_RD_COMPLETE){ // slave->master
            I2CHW_1_ClrRdStatus();
            I2CHW_1_InitRamRead(buf_tx, BUF_SIZE);
        }
        if(buf_rx[0] == 'A') LED_ON(); // masterからの指示でLEDの点灯/消灯を切り替え
        else if(buf_rx[0] == 'B') LED_OFF();
    }
}

#pragma interrupt_handler INT_GPIO
void INT_GPIO(void){
    if(BTN_PORT & BTN_BIT){ // ボタンを押している時
        buf_tx[0] = 'd'; // 押下をmasterに通知
    }
    else{
        buf_tx[0] = 'u';
    }
}

ShinagawaSeaside

2010 年 7 月 5 日 shokai コメントはありません

tokyo tyrantのサーバーを起動したり終了したりするrake taskを作った。

名前は、tokyotyrantの周辺のライブラリがmiyazaki resistanceとかそういう名前ばかりだったのでそういう作法なのかなと思って天王洲アイルと迷いつつ品川シーサイドに決めた。


■インストール

sudo gem isntall shinagawaseaside


■使う
Rakefile
require 'rubygems'
require 'shinagawaseaside'

ttdb = [ { :name => 'users', :port => 20010},
         { :name => 'videos',:port => 20011},
         { :name => 'comments', :port => 20012} ]

ShinagawaSeaside::set_tasks(ttdb, :basedir => File.dirname(__FILE__)+'/ttdb')
ShinagawaSeaside::set_tasks するとrake taskが追加される。

Rakefileのあるディレクトリの下に ttdb というディレクトリが作られて、
その中にusers.tch, videos.tch, comments.tch というDBができる。pidはusers.pid, videos.pid, comments.pidの中に入る。


% rake -T
rake ttrestart # restart TokyoTyrant server
rake ttstart # start TokyoTyrant server
rake ttstop # stop TokyoTyrant server


中身はRakeでTokyoTyrant serverを起動/終了 – 橋本詳解と大体同じ。(複数サーバー起動できるようにした)
tokyotyrantをソースからインストールすると一緒に入る ttservctl を参考にした。



タスクの名前は初期値がttstart, ttstopだけど、変更もできる
ShinagawaSeaside::set_tasks(ttdb,
                            :basedir => File.dirname(__FILE__)+'/ttdb',
                            :start => 'start', # set task name
                            :stop => 'stop',
                            :restart => 'restart'
                            )



俺はyamlで設定ファイルを書いてそこから読み込むようにしている。そうするとアプリからも、どのDBがどのportにあるか見つけやすい。

config.yaml
ttdb : 
     - name : users
       port : 23240
     - name : videos
       port : 23241

Rakefile
require 'rubygems'
require 'yaml'
require 'shinagawaseaside'

begin
  conf = YAML::load open(File.dirname(__FILE__)+'/config.yaml')
rescue
  STDERR.puts 'config.yaml load error'
  exit 1
end

ShinagawaSeaside::set_tasks(conf['ttdb'], :basedir => File.dirname(__FILE__)+'/ttdb')



■ソースコード
githubに置いた

track feed