Arduinoを使ったUSB-シリアル変換

Arduino UnoではUSB-シリアル変換を担っているAVRと外部向けのTX/RXピンが直結されているので、Arduino単体で内部でデータ受け渡しの処理を書かずにUSB-シリアル変換ができるのでは?と思って試してみました。

Arduino Uno R3回路図
http://arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf

はじめは書き込み時にTX/RXピンに書き込みの信号が出てきて嫌だなぁとだけ思っていたのですが、これを逆手に取って変換器として使えないかとやってみたところ予想通りできました。


注意点はArduino自体には通信の邪魔をして欲しくないので以下のようにTX/RXピンをHi-Zに設定するくらいです。
(5/9追記)もしかしたら無設定ならこのピンはHi-Zになるから設定不要?
あとPCへの入力はTXピン(pin1)、PCからの出力はRXピン(pin0)になるのでここもちょっと間違えそうになりました。回路図見れば一目瞭然なのですが^^;

void setup() {
  pinMode(0,INPUT);
  pinMode(1,INPUT);
}

void loop() {
  // Nothing to do
}

シリアル出力リアルタイムクロック(RTC-4543SA)を使ってみた

秋月で売っているシリアル出力方式のRTC(リアルタイムクロック)、RTC-4543SA(エプソントヨコム社製)を使ってみました。

概要

  • 年、月、日、曜日、時、分、秒をカウント
  • データ読み書きはシリアル通信(ただしちょっと特殊)
  • 通信には4本の通信線を使用


IC単体だと250円
http://akizukidenshi.com/catalog/g/gP-04019/


DIP変換基板に載ってると450円。ブレッドボードで実験したかったのでこちらの基板付き版を買いました。
http://akizukidenshi.com/catalog/g/gI-04593/


RTCと言えばI2Cの以下のをよく見ますが、今回はI2Cが利用できない条件を想定しているので上記のシリアル通信タイプのものを使っています。
http://akizukidenshi.com/catalog/g/gI-00233/

データシート
http://www.epsondevice.com/docs/qd/ja/DownloadServlet?id=ID000713

アプリケーションノート
http://www.epsondevice.com/docs/qd/ja/DownloadServlet?id=ID000516


また、kyuuzyuu9yenさんのブログに解説とArduinoの分かりやすいソースコードが掲載されています。

RTC-4543(RTC-4543SA/SB)を使う(前編)
http://d.hatena.ne.jp/kyuuzyuu9yen/20100803/1280828753

RTC-4543(RTC-4543SA/SB)を使う(後編)
http://d.hatena.ne.jp/kyuuzyuu9yen/20100912/1284283555

プロトコル

CE(Chip Enable入力)、WR(入出力切り替え)、CLK(クロック)、DATA(データ入出力共用)の4ピンを使用します。
データはDATA端子にクロックと同期してLSBファーストで1bitずつ読み書きします。ちなみにデータの中身はBCDコードです。
BCDコードは例えば「34」だったら十の位の3と一の位の4に分けて0011 0100とする形式です。


データを書き込む時は決められた順で52bitを連続で送ります。読む時も同様に52bit連続で読みます。実際は必要なデータに応じて途中まででもOKなようですが、ここでは全て読み書きすることにします。
52bitの内容は以下通りです。

FDTは電圧監視ビットで規定電圧より低くなるとセットされます。TMビットはテスト用だそうで、共に書き込む時は0を指定します。アスタリスクはユーザ定義ビットで好きに使っていいようです。
表を見て分かるように、3bit〜8bitで読み書きするようになっています。マイコン内蔵のUARTモジュールなどは使えないのでソフトで実装します。


送受信は以下のような手順です。

  • 書き込み
    • WR端子を1
    • CE端子を1
    • クロックと同期して1bitずつ送信
    • CE端子を0
    • WR端子を0
  • 読み込み
    • WR端子を0
    • CE端子を1
    • マイコン側のDATA端子を入力ピンに設定
    • クロックと同期して1bitずつ受信
    • CE端子を0
    • WR端子を0
    • マイコン側のDATA端子を出力ピンに設定


CE端子を0にすると他の通信用の端子はハイインピーダンスになります。ハイインピーダンスの端子同士を繋ぐのは良くないとどこかで見たことがあるので、最後に一応出力に設定していますが、実際はどうなのかよく分かっていません。

使ってみる

おなじみのmbedを使って、先ほどのkyuuzyuu9yenさんのソースコードを眺めつつ制御プログラムを組んでみました。kyuuzyuu9yenさんありがとうございました。
mbed向けに書いたプログラムを下の方に掲載しました。


実際の動作の様子です。はじめに書きこみをして、その後1秒ごとに読み出してStarBoard OrangeのLCDに表示しています。
精度はまだ検証できていませんが、とりあえず正常に動作させることができました。

ハマったところ

  • データの上下ビットがひっくり返る

単純にLSBファーストなことを忘れてMSBから読んでしまっただけでした

  • データが化ける

読み込みは正常にできるのに書き込みで全然違う数値が入ってしまう問題にしばらく悩まされました。
データ送出間隔が短すぎるのではと思って、3.3V駆動から5Vにしてみたところ正常に動作。
アプリケーションノート上では、5V時のクロック周期が最小0.75us、3V時では最小1.5usとなっています。どうやらmbedが速すぎてこれを下回っていたようです。
クロックを立ち上げた後に1usのwaitを入れたところ3.3Vでも正常に動作しました。

まとめ

はじめは面倒かなと思ってましたが実際はそうでもありませんでした。これを使えば正確な時計機能を簡単に実装できそうです。1つ250円という価格も魅力的だと思います。
時刻や日付と連携したものを作ってみたいと思います。

プログラム全文

#include "mbed.h"
#include "TextLCD.h"

DigitalOut cePin(p5);
DigitalInOut dataPin(p6);
DigitalOut clkPin(p7);
DigitalOut wrPin(p8);
TextLCD lcd(p24, p26, p27, p28, p29, p30);

class RTC4543
{
    private:
        void rtcIdle(void)
        {
            cePin = 0;
            wrPin = 0;
            clkPin = 0;
            dataPin.output();   
            dataPin = 0;     
        }
        
        void rtcWriteMode(void)
        {
            wrPin = 1;
            cePin = 1;
            dataPin.output();
        }
        
        void rtcReadMode(void)
        {
            wrPin = 0;
            cePin = 1;
            dataPin.input();
        }
        
        void clk(void)
        {
            clkPin = 1;
            wait_us(1);
            clkPin = 0;
        }
        
        void setBit(unsigned char in)
        {
            if((in & 0x01) == 0x01) {
                dataPin = 1;
            } else {
                dataPin = 0;
            }
            
            clk();
        }
        
        unsigned char readBit(void)
        {
            clk();
            
            return(dataPin & 0x01);
        }
        
        void setData(unsigned char inData, int inCount)
        {
            for(int i = 0; i < inCount; i++) {
                setBit(inData & 0x01);
                inData = inData >> 1;
            }
        }
        
        unsigned char readData(int inCount)
        {
            unsigned char inData = 0;
            unsigned char temp = 0;

            for(int i = 0; i < inCount; i++) {
                temp = readBit() << i;
                inData += temp;
            }
            
            return(inData);
        }
        
    public:
        int sec;
        int min;
        int hour;
        int date;
        int day;
        int month;
        int year;
        
        //constructor
        RTC4543()
        {
            sec = 0;
            min = 0;
            hour = 0;
            date = 0;
            day = 0;
            month = 0;
            year = 0;
        }
    
        void writeRtc(void)
        {
            rtcWriteMode();
            
            setData(sec % 10, 4);
            setData(sec / 10, 3);
            setData(0, 1);          // FDT bit(always set to 0)
            
            setData(min % 10, 4);
            setData(min / 10, 3);
            setData(0, 1);          // user bit(0)
            
            setData(hour % 10, 4);
            setData(hour / 10, 2);
            setData(0, 2);          // user bit(0)
            
            setData(date, 3);
            setData(0, 1);          // user bit(0)
            
            setData(day % 10, 4);
            setData(day / 10, 2);
            setData(0, 2);          // user bit(0)
            
            setData(month % 10, 4);
            setData(month / 10, 1);
            setData(0, 2);          // user bit(0);
            setData(0, 1);          // TM bit(always set to 0)
            
            setData(year % 10, 4);
            setData(year / 10, 4);
            
            rtcIdle();
        }
        
        void readRtc(void)
        {
            rtcReadMode();
            
            sec = readData(4) + (readData(3) * 10);
            readData(1);            // FDT bit
            
            min = readData(4) + (readData(3) * 10);
            readData(1);            // user bit
            
            hour = readData(4) + (readData(2) * 10);
            readData(2);            // user bit
            
            date = readData(3);
            readData(1);            // user bit
            
            day = readData(4) + (readData(2) * 10);
            readData(2);
            
            month = readData(4) + (readData(1) * 10);
            readData(2);            // user bit
            readData(1);            // TM bit
            
            year = readData(4) + (readData(4) * 10);
            
            rtcIdle();
        }
};

int main()
{
    RTC4543 rtc;
    
    rtc.sec = 30;
    rtc.min = 48;
    rtc.hour = 1;
    rtc.date = 5;
    rtc.day = 24;
    rtc.month = 2;
    rtc.year = 12;
    
    rtc.writeRtc();

    wait(1);

    while(1) {
        rtc.readRtc();
    
        lcd.cls();
        lcd.printf("%d/%d/%d(%d)", rtc.year, rtc.month, rtc.day, rtc.date);
        lcd.locate(0, 1);
        lcd.printf("%d:%d:%d", rtc.hour, rtc.min, rtc.sec);
        
        wait(1);
    }
}

UARTで制御する液晶(AD-128160-UART)を使ってみた

aitendo(http://www.aitendo.co.jp/)で販売されている液晶モジュールのAD-128160-UARTが面白そうでしたので試してみました。

概要


  • 3.3V単一電源で駆動
  • 160x128pixel
  • 1.77インチ(表示領域 : 28.03x35.04mm)
  • 制御はUART(シリアル通信)
  • USB mini-B端子がありPCからデータの転送が可能(内蔵フラッシュメモリは4MB)
  • ARMのCortex-M3コア内蔵STM32F103搭載
  • 2,680円(aitendo)

他の概要は販売ページで確認することができます。
http://www.aitendo.co.jp/product/3119

しかしデータシート等の詳細な情報は提供されていないようです。
幸いなことにGingaXさん(http://www31.atwiki.jp/gingax/pages/1.html)が基本的な使用方法をまとめてくださっているので簡単に使用することができました。

高速1.77インチ液晶モジュール(128x160,SPI)[AD-128160-UART]
http://www31.atwiki.jp/gingax/pages/63.html

準備

今回は画像を表示したかったので最初から画像表示を試してみました。

画像を表示するには、

  1. 直接ピクセルを描画する方法
  2. PCから事前に転送してマイコンから呼び出す方法

の2種類があります。今回はPCから転送しておく方法をやってみます。


液晶モジュールに画像を転送するためにMrsToolというツールを使います。こちらもGingaXさんのページにて使い方がまとめられています。

高速1.77インチ液晶モジュール(128x160,SPI)[AD-128160-UART] MrsTool
http://www31.atwiki.jp/gingax/pages/64.html

こちらのページの「ダウンロード」よりソフトをダウンロードして解凍します。


ところがドライバの入手法がaitendoや他の方の説明と異なっていたので3時間ほど悩んでしまいました。
aitendoの説明には、

PCにUSBドライバをインストールする必要があるが、STM32のダウンロードページの「Software-PC」のコーナーから「CDC Driver」をダウンロードしてください。

とありますがSTのサイト構成が大きく変わって現在はアクセスできませんでした。
そこで色々辿ってみたらSTM32用のVirtual COM Port Driverを見つけたのでインストールしてみましたがなぜか本モジュールには適用できませんでした。
不思議なことに他サイトの説明を見る限りSTM32は「STM32 Virtual COM Port」という名前で認識されるようですが、自分の環境では「MzTH Virtual COM Port」と認識されます。
ググッてみると1件だけ中国語のサイトが引っかかりました。どうやらこのモジュールの開発元?のようです。
余談ですがそのサイトを見るとUARTで制御できる同シリーズのサイズ違い・解像度違いのモジュールも販売されていました。

それからサポートを辿るとMzTHなる名前が付いているドライバのようなものを発見。でもよくよく見るとMrsToolに同梱されている模様。そこでさっき落としてきたMrsToolのフォルダを見ると「Software Driver」フォルダがありました。もしやと思いこのフォルダを指定してドライバのインストールしてを試みたところあっさり成功。あの3時間は何だったんだろう....
とはいえ無事インストールできてPCからも認識されたのでよしとします。ちなみにフォルダ内には2つ.infファイルがありますがとりあえず「MzTH_FirmwareVr2000_L.inf」を指定したところ正常に動作しました。もう1つとどう違うのかは分かりません。

画像の転送

液晶モジュールをUSBケーブルで接続し、MrsToolを起動します。PCと接続した時点では液晶には何も映りません。

見事に中国語&文字化けです。

はじめにデバイスの選択をします。左上のボタンを選択します。

すると以下のような画面が開きます。

この液晶モジュールは「MzTH17 V10」という名前らしいので初期状態の「MzTH17_V10_V」のままで変更しなくてOKです。ちなみに「MzTH17_V10_W」というのもあり、こちらは画面を縦にするか横にするかの選択のようです。しかし「MzTH17_V10_W」を指定して画像を転送すると正しく表示されないという現象が起きたのでここでは「MzTH17_V10_V」を選択します。
液晶を横にして使用したい場合は90度傾けた画像を用意する必要があります。
次に左から3番目のボタンを選択します。

すると以下の通信ポートの設定画面が開きます。

自分の環境ではこのモジュールがCOM15に割り当てられたので、デバイスマネージャで確認した上でそのように指定しました。
ここで正常に通信ができると液晶モジュールに以下のような表示が出ます。

次に転送する画像を指定します。真ん中くらいのボタンを選択します。

ファイル選択のダイアログが開くのでビットマップファイルを指定します。ツール側で変換してくれるようで、元画像の大きさやビット数は特に気にしなくてもいいようです。

そして画面中央のボタンを押し、転送リストに追加します。

未確認ですが、リスト上部のバーはモジュール内に保存できるデータ容量を表している気がします。
ちなみにリストの一番左の項目は画像のIDで、マイコン側から呼び出しをする時に指定する番号になります。
そして右から2番目のボタンで転送を開始します。

完了すると以下のダイアログが出ます。

最後に右端のボタンで終了します。

以上で画像の転送は完了です。

マイコンから呼び出し

GingaXさんのサイトにて通信データについての詳しい解説がありますが、今回はその解説を元にnuchoさん(http://javatea.blog.abk.nu/)がmbed向けにライブラリを作成されていましたので利用させて頂きました。

2011/12/12(月) AD-128160-UARTのライブラリ作った
http://javatea.blog.abk.nu/064
mbed公開ライブラリ : AD128160
http://mbed.org/users/nucho/libraries/AD128160/m24mml

上記のクラスではmbedとはVCC、GND、受信、リセットの4本を接続するだけでOKです。

テストしたプログラムは以下の通りです。各命令の間に1秒のwaitを入れていますが実際はそんなに必要はありません。

#include "mbed.h"
#include "AD128160.h"

AD128160 lcd(p13, p5);

int main() {
    lcd.reset();     // モジュールリセット
    wait(1);
    lcd.cls();     // 描画範囲クリア
    wait(1);
    lcd.brightness(500);     // バックライト光量指定
    wait(1);
    lcd.bmp(0, 0, 0);     // x座標, y座標, 画像ID 
}

クラスリファレンスでは図形や文字を描画するメソッドについて記述がありましたが、画像呼び出しについては記述されていませんでした。しかしソースコードを読んでみるとbmp()というメンバがいたので使ってみたところ正常に転送した画像を表示してくれました。

そこそこきれいです。

まとめ

ライブラリのおかげもあり簡単に画像を表示させることができました。まだ試していませんが文字や図形の描画も簡単にできそうです。
ちょっと値段が張るのがネックかなと思いましたが、この扱いやすさを考えるとこのくらいでもいいかもしれません。

初めて液晶を扱いましたがLED、7セグLED、キャラクLCDに続く表示デバイスとして選択肢がまた1つ増えました。どんなものが作れるかいろいろ考えてみたいと思います。


最後に、サイトだけでなくTwitterでも直接いろいろと教えてくださったGingaXさん、nuchoさんにこの場を借りて御礼申し上げます。ありがとうございました。

XPortでシリアル通信-Ethernetの変換を試してみた

簡単にマイコンからLANを介した通信ができると聞いて前々から気になってたXPortを使ってみました。

XPortとは

Lantronix社が作ってる製品で、シリアル通信-Ethernetの変換ができます。製品的にはサーバを想定?詳しいことはまだよく分かっていません。

外見はRJ-45のジャックそのものです。
値段はお高めで7,000円弱のようです。(参考 : 若松通商 http://www.wakamatsu-net.com/cgibin/biz/pageshousai.cgi?code=38060095&CATE=3806)

DHCP環境ならばLANケーブルを接続するとIPアドレスが割り当てられ、あとはLANを介してXPortIPアドレス宛にデータを送ればXPortのシリアル通信線からそのデータが出てきます。逆にXPortにシリアル通信でデータを送るとEthernet経由で接続してる端末にそのデータが届きます。

面倒なEthernetを全く意識すること無く非常に簡単にシリアル-Ethernet変換ができるようになるモジュールです。

準備

準備は電源(3.3V)とシリアル通信用の送信・受信の計4本を繋ぐだけです。
これだけで動いてます。

足が縦・横共に2.54mmピッチなのですが、横方向が1.27mm上下で互い違いになってるので、以下のようにピンヘッダをはんだ付けして2.54mmピッチ環境で使いやすいようにしました。


ピン配置は以下の通り。

ピンNo. 機能
1 GND
2 VDD(3.3V)
3 外部リセット入力
4 シリアルデータOUT(出力ピン)
5 シリアルデータIN(入力ピン)
6〜8 拡張端子

基本的にはこのままで使えますが、固定IPにしたいなどいろいろ設定をしたい場合はLantronix社から提供されている設定ツール、DeviceInstallerを使います。(「デバイスインストーラ」なんていうから最初は設定ソフトとは思わず見逃してました。)
ダウンロードページ : http://ltxfaq.custhelp.com/app/answers/detail/a_id/644
ネットワーク上にXPortが検出されると一覧に出てきます。

ころころIPアドレスが変わるのも嫌なのでなんとなく192.168.11.11に固定しました。その他にもたくさんの設定項目があります。単体でメールも送れるとか...


それからmbedでUSB-シリアル通信変換のプログラムを書いて中継をさせました。

#include "mbed.h"

Serial pc(USBTX, USBRX);
Serial xPort(p28, p27);

int main() {
    while(1) {
        if(pc.readable()) {
            xPort.putc(pc.getc());
        }
        if(xPort.readable()) {
            pc.putc(xPort.getc());
        }
    }
}

使ってみる

XPortとはtelnetプロトコルで通信します。
コマンドプロンプトtelnetを起動します。

C:\Documents and Settings\Administrator>telnet

このままでは入力した文字が画面に反映されないので、起動したらまずローカルエコーを有効にします。

Microsoft Telnet クライアントへようこそ

エスケープ文字は 'CTRL+]' です

Microsoft Telnet> set localecho

次にXPortとの通信を開始します。仕様でシリアル通信でデータを送受信するにはポートの10001番を利用しないといけないようです。

Microsoft Telnet クライアントへようこそ

エスケープ文字は 'CTRL+]' です

Microsoft Telnet> set localecho
ローカル エコー: オン
Microsoft Telnet> open 192.168.11.11 10001
接続中: 192.168.11.11...

これで準備完了です。あとはキーボードで何か打ち込むと遅延することもなくXPortのシリアル通信ピンから出てきます。(telnetのローカルエコーは左上に出てしまいます)

その逆も然り。

まとめ

すごく簡単でした。
XPortにHTMLファイル置いておいて、ブラウザ経由で何かアクション起こしたらシリアル通信でデータが出てくるということをやりたかったのですがどうやらJavaアプレットを使う方法しかないようです。このJavaアプレット自体は公式で配布されているみたいです(未確認ですが)。
iPhoneのブラウザから操作をしたいのですがどうやら厳しそうです。PHPが動くサーバでも立てておいてそっち経由で、とかならできそうですがXPort単体では無理なようです。
ともあれ、用途を限定しなければLAN経由で様々なことができそうなのでいろいろと可能性を感じました。


それから、nyatlaさんが最近開発されたMiMicというものがちょっと気になってます。
http://nyatla.jp/mimic/wp/

MiMicは、TCP/IPをインタフェイスとして、WebAPIからハードウェアを制御する為のソフトウェアです。LPCXpresso1769で動作するファームフェアとそのSDK、ブラウザや他のWebプラットフォームで動作するスクリプトライブラリで構成されています。
ファームウェアをインストールしたLPCXpresso1769 評価ボードをイーサネットケーブルでネットワークに接続することで、Webブラウザを通して、電子デバイスMCUを制御することができます。

つい先日のver 1.2へのアップデートでmbedにも対応したようなので、XPortと同じくEthernet×マイコンの可能性を広げるツールとしてこちらもそのうち試してみたいと思います。

sparkfunのLiPo充電器の充電電流を可変できるようにしてみる

sparkfunのLiPo Charger Basic - Mini-USB(PRT-10401)(http://www.sparkfun.com/products/10401)を購入しましたが、購入時の状態では充電電流が500mAに設定されており手持ちの他の低容量のsparkfunのLiPoバッテリーに使用できないため少しばかり改造をしてみました。
ちなみにこの製品は国内ではスイッチサイエンス(http://www.switch-science.com/products/detail.php?product_id=550)等で入手できます。


この充電器にはMCP73831TというICが使われており、データシート(http://www.sparkfun.com/datasheets/Prototyping/Batteries/MCP73831T.pdf)を見ると5番ピンとVSSの間に接続された抵抗の値によって充電電流を15〜500mAの間で自由に設定できるようです。充電器の出荷時には2kΩ、すなわち500mA充電用の抵抗が予め付いています。

そこではじめは500mA用と決めうちで元々付いていた2kΩの代わりに100mAhのバッテリーの充電のために10kΩの抵抗を付けましたが、すぐに20mAのバッテリーも充電したくなってしまいました。どうしようかと考えた結果、抵抗をすぐに取り替えられるようにソケットを繋いでおくことにしました。

before(画像はメーカー詳細ページより)



after


元々チップ抵抗が付いていた場所からUEWでピンソケットと接続しました。そしてちょうど基板の中央に空きスペースがあったのでそこにソケットを固定しました。


これで充電時には容量に合った抵抗をセットするだけで、簡単に様々な容量のバッテリーに対応できるようになりました。
ちなみに20mAhのバッテリーを充電するには50kΩの抵抗が必要なようです。

VS1053bとXBeeを使って無線MIDI音源を作る

VLSI SolutionのオーディオIC、VS1053(http://www.vlsi.fi/en/products/vs1053.html)を扱う機会がありましたので、忘れないうちにその使い方をまとめておきたいと思いエントリーしました。

VS1053とは

秋月で700円で購入できるオーディオ関係の機能を載せたIC(http://akizukidenshi.com/catalog/g/gI-02407/)です。主な機能としては、

  • Ogg Vorbisデコード
  • MP3デコード
  • AACデコード
  • WMAデコード
  • WAV再生
  • リアルタイムMIDIデコード
  • オーディオDAC
  • ヘッドホンアンプ内蔵
  • 音場拡大エフェクト

などがあるようです。これだけの機能がこのような小さなICで実現されていて、かつ700円という低価格さです。

今回はVS1053のリアルタイムMIDIモードを使いました。MIDIの規格に基づいてシリアル通信でデータを送ってあげるとリアルタイムで発音するMIDI音源として使用することができます。最大同時発音数が64(※ただしクロック周波数やエフェクトの有無で変化)なのでそこそこ鳴らせそうです。

下準備

ICはLQFPパッケージで販売されておりこのままでは扱いづらいので2.54mmピッチへの変換基板(http://akizukidenshi.com/catalog/g/gP-03782/)も一緒に購入し、ユニバーサル基板で試作できるようにしました。

試作回路

データシートに基づいてIC周りに必要な回路をユニバーサル基板上で構成しました。その際、以下のページで詳細な解説がされていましたので参考にさせて頂きました。


回路図

GPIOの0〜7のH/Lで動作モードを指定するようで、リアルタイムMIDIモードで動作させるためにGPIO1のみLとし、残りはHとなるよう接続しました。
他のI/Oピンは以下のように接続しました。

ピン番号 ピン種類 機能 処理
8 Digital Out DREQ 未接続
13 Digital In XDCS / BSYNC H
23 Digital In XCS L
28 Digital In SCLK L
29 Digital In SI L
30 Digital Out SO 未接続

電源はアナログ電源に2.8〜3.6V、ディジタル電源に1.7〜1.85V、I/O電源に1.8〜3.6Vが必要なのでアナログ電源とI/O電源をまとめて3.3V、ディジタル電源を1.8Vにすることにしました。
外部からの供給電源は3.3Vのみとし、回路内での3.3V -> 1.8Vの降圧に三端子レギュレータTA48018Fを使用しています。
クロックは外部よりセラロックで12MHzを入力しています。


さらに今回はMIDIデータを無線で送受信する必要があったため、無線部分にはXBee(シリーズ2 : ZigBee版)を使いました。


ブレッドボード上で組んだ試作の回路です。

左上のユニバーサル基板がVS1053と必要なコンデンサ等の部品です。その右に8ピンのPSoC(CY8C24123)があり、このPSoCXBeeで受信したデータをMIDIの規格である31.25kbpsに変換してVS1053へ送っています。左下のXBeeが受信の役割であり、そこからPSoCへ19,200bpsで送られてきます。
右のXBeeはパソコンに接続され、パソコンで再生しているMIDIデータをそのまま送っています。
電源はACアダプタより3.3Vを供給し、音声出力はステレオでアクティブスピーカに接続しました。


PSoCは内蔵24MHz発振で動作させています。プログラムは以下のように非常に単純なものです。

#include <m8c.h>
#include "PSoCAPI.h"

void main(void)
{
     TX8_1_Start(TX8_PARITY_NONE);     // 送信モジュール(PSoC -> VS1053)スタート
     RX8_1_Start(RX8_PARITY_NONE);     // 受信モジュール(XBee -> PSoC)スタート
     
     while(1) {          
          TX8_1_PutChar(RX8_1_cGetChar());     // データを受信したら送信モジュールからそのまま送信
     }
}

この回路でテストしてみたところ心配していた遅延やデータの欠落も無く、テストに使用したいくつかの10chほどの曲のデータは全て正常に再生されました。

プリント基板製作

試作回路で動作を確認できたのでプリント基板を製作しました。
ほとんどの部品を表面実装部品にして省スペース化を図っています。


PCBEで基板作成



プリント基板(表)



プリント基板(裏)


部品実装後はこのようになりました。

一部配線ミスがありましたが、パターンカットとジャンパ線での接続で解決しました。全体の動作も問題ありませんでした。

バッテリー駆動

回路が完成したところで、完全な無線状態で使用するためにバッテリーでの駆動を検討しました。
はじめにアルカリ単4電池2本で試してみたところ10分程度しか持ちませんでした。それでいて電池の容積も大きいため、今回は小型で大容量なLiPoバッテリー(リチウムイオンポリマーバッテリー)を使用しました。


スイッチサイエンスでsparkfunの110mAhバッテリー(http://www.switch-science.com/products/detail.php?product_id=157)とUSB充電器(http://www.switch-science.com/products/detail.php?product_id=550)を購入しました。
※この充電器は購入状態では500mA仕様となっているので100mAhのバッテリーを充電するには改造が必要です。

LiPoバッテリーは出力電圧が3.7Vとなっていますが実際には4.2V〜2.7V程度と開きがあるので三端子レギュレータを入れて3.3Vに降圧しました。


このバッテリーを使用した結果、満充電状態で約50分間連続動作することを確認しました。この時の回路の消費電流は平均67mA程度でした。
また20mAh(http://www.switch-science.com/products/detail.php?product_id=479)のバッテリーでも試した結果、約15分間連続で動作しました。

まとめ

無線通信なのでデータの欠落や遅延を心配していましたが、MIDIキーボードを接続して演奏しても遅延やデータ抜けは全く気になりませんでした。
VS1053は安価なモジュールなのでそこまで性能は高くないのかな?と思っていましたが、同時発音数や遅延等が気になるようなことは無くちゃんと鳴らしてくれました。ただやはり音は載せている波形が貧弱なのかWindowsに標準搭載されているMicrosoft GS Wavetable SW Synthより少し劣るような感じです。
また48ピンのQFPパッケージであり、IC周辺に多少部品を追加する必要があり、さらに電源も複数用意しないといけないため取り扱いは少し面倒だと感じました。


しかしこれだけ小型なのにも関わらず立派なMIDI音源として使用できるので、いろいろなものに組み込んだりと様々な場面での利用価値があるのではないかと思います。

マイコン初心者講座その5 スイッチでLEDを制御する

前回のその4ではLEDを自動的に点滅させるプログラムを作りました。
今回はスイッチでLEDを点けたり消したりするプログラムを作ります。

回路

今回必要な部品は前回のLEDに加え、スイッチが必要となります。
LEDの接続ピンは前回同様PORTBの0番とし、追加するスイッチはPIC16F88の左上のピン、PORTAの2番ピンとします。
回路は以下のようになります。

回路の仕様としては、スイッチが押されていないときにはRA2には"1(5V)"が、押されたときには"0(0V)"が加わります。押されていないときが"0"なことに注意しないといけません。
また、RA2から電源に向かって接続されている10kΩの抵抗はプルアップ抵抗と呼ばれるものです。これがないとスイッチが押されていないときにPICのピンの状態が不定となり正常に動作しないだけでなく、PICを破壊してしまう可能性もあります。プルアップ抵抗について詳しくは調べてみてください。
タクトスイッチは以下の画像のようになっています。青のピン間は常に接続されており、赤のピン間はボタンを押したときに繋がります。接続の際はその点に注意してください。

プログラミング

新しくプロジェクトを作成するため、programmingフォルダの中に"switchLED"というフォルダを新しく作ります。
前回の手順を参考にしてswitchLEDというプロジェクトを作成します。今回もPIC16F88を8MHzで使用するので設定も同じです。

続いてプログラムを記述します。
初期状態では以下のようにmain関数に何も書かれていないものが作られています。

void main() {

}

まずクロックを設定するために以下を記述します。内蔵発振器を8MHzで使用する設定です。

     OSCCON = 0x70;

今回もアナログ入力機能を使用しないため、ANSELをクリアします。

     ANSEL = 0x00;

続いてポートの入出力の方向を設定します。今回はスイッチによってLEDを制御するので、マイコンに接続するスイッチのピンを入力に指定しないといけません。

ピンを入力に設定するにはTRISレジスタでそのピンに対応するビットを1にするだけです。
つまりこのようになります。

     TRISA = 0x04;
     TRISB = 0x00;

0x04というのは0b00000100と同じ意味です。ここで1になっているのはPORTAの2に対応する部分です。これでPORTAの2番ピンをディジタル入力ピンとして使う準備ができました。

その後全ポートをリセットします。

     PORTA = 0x00;
     PORTB = 0x00;

次にメインループの記述です。
まず外枠を用意します。

     while(1) {

     }

今回のメインとなる動作はスイッチが押されたかどうかを判定するということです。プログラムではif文でスイッチに接続されたピンの状態を判定することで実現します。
ポートに値を書き込む場合はPORTA = 0xXXというように書きました。mikcoCではポートを変数のように扱えるので、ポートの値を読み込むには"a = PORTA;"のような記述方法でOKです。
しかしポートの値は8bitごとにまとめてしか取得できないため、次のような方法を使います。

     if((PORTA & 0x04) != 0)

"&"は論理積を取る演算子です。論理積とは以下のような演算結果です。

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

つまりお互いが1でないとその出力は1にはならない演算です。
PORTAの2番ピンにスイッチが接続されているため、PORTAと0x04=0b00000100の論理積を取るとRA2の状態のみが分かります。
スイッチが押されていない状態、つまりRA2に"1"が入力されている状態ではPORTA = 0b00000100となります。ここで0x04と論理積を取ってみます。

PORTA 0000 0100
0x04  0000 0100
-----------------
      0000 0100

逆にスイッチが押されるとRA2が0になるため演算結果は以下のようになります。

PORTA 0000 0000
0x04  0000 0100
-----------------
      0000 0000

つまり論理積を取った結果が0なのかそうでないかを判定すればRA2のスイッチが押されたかどうかが分かるというわけです。

"if((PORTA & 0x04) != 0)"で0でなかった場合、すなわちスイッチが押されていない状態の時に実行される文はPORTBに0を出力するようなものとし、1だった場合、RB0に1を出力するような処理を書けばOKです。


以下がプログラムの全体像です。

void main() {
     OSCCON = 0x70;
     ANSEL = 0x00;
     TRISA = 0x04;
     TRISB = 0x00;
     PORTA = 0x00;
     PORTB = 0x00;

     while(1) {
          if((PORTA & 0x04) != 0) {
               PORTB = 0x00;
          } else {
               PORTB = 0x01;
          }
    }
}

このように記述することで、スイッチを押したときにLEDが光るということが実現できます。

  • 近日中

目次