SVX日記
2005-03-07(Mon) FT232BM専用基板上に起つ!!
今日は朝から再び花粉症対策の注射を打ちに行く。前回は注射のタイミングが早すぎたのか、花粉が飛ばないうちに薬が切れてしまったような気もするが、今はシッカリと鼻はズルズル、目はカユカユなのである。今度こそ薬の効き目が明らかになるであろ……ビャックショイ!! ズズーッ……くそー、花粉が憎けりゃ、杉山、杉本、杉良太郎、ケイン・コスギまで憎いわ。がるるる。
帰りがけにつくば市内で対向車線をチラッと横切る、黄色のひらべったい……SVXッ!! 発見ッ!! 黄色という色も目に鮮やかだけど、視界の隅は「ひらべったい」という形状に反応したのであった。天井とトランクの上が黒だったので、余計にひらべったく感じるのかもしれん。そーか、SVXはひらべったかったのか。なるほどぉ。なんにせよ、もう少し観察したかったなぁ。
それはそうと昨日、見事なタイミングで役者が揃ったので、今日はFT232BMを専用基板上に取り付けてみるのである。QFP-32というパッケージはピン間0.8mmという狭さであり、これだけの精密なハンダ付けはオイラの未経験の領域である。やはり昨日手配した新兵器のハンダゴテCXR-30を得物にとりゃーッ!!
意外とイケるじゃん!! というか、PCBにあらかじめ塗布してあるハンダを活用しつつ、コテ先に少しハンダを含ませ、各ピンをウリウリしただけである。ひととおり作業が終わった時点では、それでも何ピンか浮いていたが、ルーペで確認しては、ウリウリ、テスターでチェックしては、ウリウリするうち、そこそこキレイ(?)にハンダ付けができたようだ。
ちなみに、オイラはカメラも趣味なので、ルーペにはNikonのAi50mm1.8Fを逆からノゾいて代用にした。また、今回は基板の撮影に三脚とマクロスライダを動員してみた。道具が揃っていれば安いデジカメでもかなり本格的なマクロ撮影ができるってモンよ。
難関であるFT232BMの取り付けを完了したら、あとは残りの部品をチャチャっと取り付けて完成である。わぉ。なかなかシンプルかつコンパクトで美しい。初めて設計したPCBにしては、かなりよいデキだといえるのではないだろうか。
……カリカリカリ……オゴソかにノートPCのHDDが回転し「新しいハードウェアが見つかりました」出ましたッ!! デバイス名は「USB <-> Serial」ということで、とりあえず成功であろうッ!! 完全に動作確認したとはいえないが、ココまでは実にあっけなく到達である。いやー、楽しい。明日からは実際に通信をさせてみる予定である。うほほぃ。
ちなみに、基板は一度に16枚もできてしまうので、たくさん余っている。欲しい人があったら必要な部品とセットで実費+手数料程度でお分けしようとも考えている。希望者はココまで……って、まだ動作確認すら終わってないのに気が早いっすか、そうすか……。
2006-03-07(Tue) 555を味見してみる
帰りが遅いやらなんやらで、なかなか工作して遊ぶ時間がとれないのだが、それでも先日買ってきたLMC555を使ってみたいのである。電子工作を始めるに当たって3年弱ほど前に購入した「図解・わかる電子回路」だが、その中に「555は便利だよ」と笑っている犬のイラストが強烈に印象に残っていて、いつか555を使ってみたかったのである。
しかし、実際の本の記述は、なんだかいろいろと式が出てくるわ、実際のピンアサインが表に出てこないわで、どうも実感がなくて拒否反応を感じていたのだが、よくよく読んでみれば、そんなに難しいわけでもない。というか、本を読むより、データシートの回路サンプルを見つつ、事前知識として時定数、つまり抵抗とコンデンサを直列につないだ時に、与えた電圧の63%まで電圧が上がるまでの時間がrとcの積で求められる……具体的には1MΩと0.1uFだったら0.1秒とか……という知識を下敷きにしたらすぐに理解できた。
2007-03-07(Wed) USBメモリが熱いッ!!
ずーっと前に購入した赤いUSBメモリ。デザインが痛く気に入っており、モバイルなcvsリポジトリを主な用途に、今日も毎日愛用している。愛用のあまり、キズだらけになっており、チェーンもブッちぎれてしまっているが、そのイモータルな感じは今も健在である。
しかし、ずぅーッと気になっているコトがあった。それは発熱である。USBメモリっつったって、たかがメモリだ。それが挿してあるだけなのに、かなりホットになるのだ。持てないほどではないが、かなり熱い。外装で40度を超えている感じ。
ずっと「PCとの相性が悪いのかな」などと思ってたが……ちょっと待てぇぃ!! んなワケがあるかッ!! USBメモリの温度を上げるPC側の要因なんて、電圧と読み書き動作の頻度くらいなもんだろう。USB端子の電圧は5V固定なのは言うまでもなく、たいして読み書きさせてないのも確認済み。つーことは、USBメモリに要因があるってことじゃねーか。だいたい、こちらも痛く気に入っており、愛用しているワイヤレスマウスのレシーバモジュールは、さっぱり熱くなったりはしない。
なんで今まで気づかなかったんだろう。サクッとバッファローのサイトで仕様を確認してみる。ふむん。平均250mA以下ね。なるほ……
……そりゃ、高すぎだろうッ!? だって、一般的なUSBの給電容量はポートあたり500mAだ。最大値の半分が平均だなんて……ポータブルHDDみたいな回転モノで平均300mAってなら、まぁ納得もするケドもさ。そんな電力を消費して、コトリとも動きもしないUSBメモリなら、そりゃ発熱するしかないだろうさ。
USB機器ということで、関連して調べてみる。わが社で扱っている(!?)USBシリアル変換チップの消費電流だ。松林コースのFT232BM、松原BコースのFT245BMでTyp.25mA。一世代前の松原コースのFT8U245AMでもMax.50mAだ。そりゃ、USB1.1のフルスピードに出さない仕様だし、メモリ自体の消費電力も入ってないが、チップ単体で25mAというのは、USB接続を前提とするチップとして常識の範囲内と思える。
だいたい、ノートPCの電力は有限なのだ。250mAが稼働時間に及ぼす影響を考えてみる。オイラの使っているノートPCのバッテリの容量は定格11.1Vの5200mAhである。標準消費電力は18Wとあるので、18W / 11.1V = 1621mA、5200mAh / 1621mA = 3.207h、3時間チョイの稼働時間となり、だいたい計算が合う。
ここで5Vで250mAなUSBメモリが挿さりっぱなしだとどうなるか。5V x 250mA = 1.25W……って、デカいなぁ、おい。19.25W / 11.1V = 1734mA、5200mAh / 1734mA = 2.998h、0.2時間。12分の短縮である。うーむ、思ったよりは小さいが、決して12分は小さくはないぞ。最初から12分短縮されるコトを知っていれば、挿しっぱなしにすることはタメラわれよう。
こうなれば、消費電力の観点からUSBメモリを選んでみよう。しかし、そんな項目で製品を一覧することは価格.comでもできない……うまくググるのも困難……こんな時は人力検索はてなに質問してみようッ!!
おぉ!! すごいッ!! 前回は回答が付かなかったが、今回はスゴく的確な回答が付いたッ!! 低消費電力を謳って消費電力が75mA以下なんていう製品があったんだねぇ。しかもアルミ外装。これで赤いバージョンがあってストラップ穴が付いている最高だが、この低消費電力の前には何も言うまい。
2009-03-07(Sat) XPort動き出します
というわけで、調子の悪いヤツ、どうしてくれよう。面倒くさいが、仕方ない。ダメモトで、Windows機を立ち上げ、シリアル経由でのアップデートを試そう。アドホックに、シリアル接続する配線を加え、ヘンなWindows用ユーティリティを立ち上げ、アップデートしよ……うにも、ウンともスンともいわない。というか、通信速度が115200固定に見えるんですけどね。ユーティリティには変更できるような設定項目は見あたらない。
……えぇい、なんかわからんが、同時にLANからも接続ッ!! ……って、アレ? もしかして、IPアドレスが付いてない? 特に何もしてないが動き出した。なんつぅ不安定な。トラブルシュートのために計測機をつなぐと、その時だけちゃんと動く、なんて事例を思い出した。telnetで10001番につなぐと、WindowsのTERATERMからのシリアル出力が受け取れてしまう。逆はダメだけど。
なんでもいいや。動いているなら、その間に、せめて、ファームだけでも最新にしておこう。ファームウェアアップデートはtftpで行う。tftpなんて滅多に使わんから入ってない。入れる。
# yum install tftp
Example command for firmware upgrade from a DOS command prompt type:
1. C:\ tftp -i put xpt01upg.rom X1
2. Wait 20 seconds
3. C:\ tftp -i put xpte_6100.rom X4
Example command for the new Web Manager upgrade from a DOS command prompt type:
1. C:\ tftp -i put xpt_webm_1300.cob WEB4
$ tftp 192.168.7.100 -m binary -c put xpt01upg.rom X1
$ tftp 192.168.7.100 -m binary -c put xpte_6503.rom X4
$ telnet 192.168.7.100 9999
Trying 192.168.7.100...
Connected to 192.168.7.100.
Escape character is '^]'.
MAC address 00204A805218
Software version V6.5.0.3 (070403) XPTE
Press Enter for Setup Mode
*** basic parameters
Hardware: Ethernet TPI
IP addr - 0.0.0.0/DHCP/BOOTP/AutoIP, no gateway set,netmask 128.0.0.0
DHCP device name : not set
*** Security
SNMP is enabled
SNMP Community Name: public
Telnet Setup is enabled
TFTP Download is enabled
Port 77FEh is enabled
Web Server is enabled
Web Setup is enabled
ECHO is disabled
Enhanced Password is disabled
Port 77F0h is enabled
*** Channel 1
Baudrate 9600, I/F Mode 4C, Flow 00
Port 10001
Connect Mode : C0
Send '+++' in Modem Mode enabled
Show IP addr after 'RING' enabled
Auto increment source port disabled
Remote IP Adr: --- none ---, Port 00000
Disconn Mode : 00
Flush Mode : 00
*** Expert
TCP Keepalive : 45s
ARP cache timeout: 600s
Monitor Mode @ bootup : enabled
HTTP Port Number : 80
SMTP Port Number : 25
MTU Size: 1400
Alternate MAC: disabled
Ethernet connection type: auto-negotiate
*** E-mail
Mail server: 0.0.0.0
Unit :
Domain :
Recipient 1:
Recipient 2:
- Trigger 1
Serial trigger input: disabled
Channel: 1
Match: 00,00
Trigger input1: X
Trigger input2: X
Trigger input3: X
Message :
Priority: L
Min. notification interval: 1 s
Re-notification interval : 0 s
- Trigger 2
Serial trigger input: disabled
Channel: 1
Match: 00,00
Trigger input1: X
Trigger input2: X
Trigger input3: X
Message :
Priority: L
Min. notification interval: 1 s
Re-notification interval : 0 s
- Trigger 3
Serial trigger input: disabled
Channel: 1
Match: 00,00
Trigger input1: X
Trigger input2: X
Trigger input3: X
Message :
Priority: L
Min. notification interval: 1 s
Re-notification interval : 0 s
Change Setup:
0 Server
1 Channel 1
3 E-mail
5 Expert
6 Security
7 Defaults
8 Exit without save
9 Save and exit Your choice ? 8
exiting without save !
Connection closed by foreign host.
$ tftp 192.168.7.100 -m binary -c put xpt01_webm_1602.cob WEB4
2013-03-07(Thu) ついにはロードブラスター
ロードブラスターといえば、ダライアスと並んで、オイラの一番好きなゲームなのである。中学の頃だと思うが、ゲームセンターでオープニングデモを一目見て魅了されたのだ。やや、ゲーム性には疑問を感じながらも、それには熱中し、かなりつぎ込んだ記憶がある。なんというか、あの車、あのステージに魅力がありすぎるのだよな。
しかしながら、最終面である9面は、難度が飛び抜けていて、何度も9面に到達するも、ゲームセンターでは遂にエンディングを拝めなかった記憶がある。うまい人のプレイを後ろで見ていてエンディングを拝んだが、ある程度は覚えないと無理だと思ったな、あれは。
が、ここに来てiOS版の登場だ。いままでと何が違うって、夢中になって、一緒に遊んでくれそうなガキが、ちょうど隣にいることだ。これはダウンロードしなくてはならない。iPodTouchはカミさんのものなので、コンビニでアプリのプリペイドカードを買ってきて、許可を得てインストール。
懐かしい! 画面がキレイだ。ちょっとトリミングされていたり、画質をリファインしすぎている気がするが、悪くない。なかなかの好移植だ。あまり一度に遊んでしまうともったいない。今日は1面だけクリアしておこう。
VHDプレーヤ | VP-2400 | 149,800 |
インタフェイスユニット | VO-20PC | 55,000 |
VHDコントローラボード | VO-20PS | 27,000 |
VHD言語インタープリタ | VO-20IP | 5,000 |
VHDソフト | 6,800 | |
PC用ソフト | 3,000 |
2025-03-07(Fri) ALSAでPulseAudioで音を鳴らす
しかし情報が少ない。Cで書くのは面倒だからRubyで書きたい。GEMにALSAのバインディングくらいあんじゃないの? えぇい、AIに聞いちゃえ。「RubyでALSAで音を鳴らすプログラムを書いてください」。そのまんまだ。
出てきた……て、なんだこれ。GEMを使ってない。FFIてナニ? 調べたら「Foreign function interface」とある。え。Ruby上でバインディングを書けるってこと!? そういうのもあるのか! 知らんかった。
前にもあったが、サンプルコード的なものを書かせると、AIはちゃんとしたものを出してくる。ほぼそのまま動いたが、あちこちオレ風にリライトして仕上げたのが以下。その作業を通じてコードはオレの血肉となるのだ。
#!/usr/bin/env ruby
# $ bundle add ffi
require 'bundler/setup'
require 'ffi'
include Math
module ALSA
extend FFI::Library
ffi_lib 'asound'
# https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html
attach_function :snd_pcm_open, [:pointer, :string, :int, :int], :int
attach_function :snd_pcm_set_params, [:pointer, :int, :int, :uint, :uint, :int, :uint], :int
attach_function :snd_pcm_avail, [:pointer], :int
attach_function :snd_pcm_writei, [:pointer, :pointer, :ulong], :long
attach_function :snd_pcm_close, [:pointer], :int
SND_PCM_STREAM_PLAYBACK = 0
SND_PCM_FORMAT_S16_LE = 2
SND_PCM_ACCESS_RW_INTERLEAVED = 3
end
p_pcm = FFI::MemoryPointer.new(:pointer)
name = 'default' # 'hw:2,0', 'plughw:2,0'
ALSA.snd_pcm_open(p_pcm, name, ALSA::SND_PCM_STREAM_PLAYBACK, 0) == 0 or raise('snd_pcm_open failed.')
pcm = p_pcm.read_pointer
channels = 1
rate = 44100
ALSA.snd_pcm_set_params(pcm, ALSA::SND_PCM_FORMAT_S16_LE, ALSA::SND_PCM_ACCESS_RW_INTERLEAVED, channels, rate, 1, 500000) == 0 or raise('snd_pcm_set_params failed.')
freq = 440
duration = 2
p_buffer = FFI::MemoryPointer.new(:int16, frames = 2048)
omega = 2.0 * PI * freq / rate
theta = 0
(duration * rate).times {|p|
p_buffer.put_int16(p % frames * 2, (sin(theta += omega) * 32700).to_i)
if((p + 1) % frames == 0)
a0 = ALSA.snd_pcm_avail(pcm)
ALSA.snd_pcm_writei(pcm, p_buffer, frames) < 0 and raise('snd_pcm_writei failed.') # blocking
puts('%s: %5d -> %5d' % [Time.now.strftime('%H:%M:%S.%N'), a0, ALSA.snd_pcm_avail(pcm)])
end
}
ALSA.snd_pcm_close(pcm)
引き続き、PulseAudio版。こっちも情報が少ない。Simple APIのサンプルは見つかったが、発声中にブロッキングされる仕様では、ゲームに使えない。Asynchronous APIを使うべきだが……無闇に複雑な前処理が要るんだなぁ。結局、CのサンプルをRubyのFFI向けに書き直した。
#!/usr/bin/env ruby
# $ bundle add ffi
require 'bundler/setup'
require 'ffi'
include Math
module PulseAudio
extend FFI::Library
ffi_lib 'pulse'
# https://freedesktop.org/software/pulseaudio/doxygen/mainloop_8h.html
# pa_mainloop *pa_mainloop_new(void);
attach_function :pa_mainloop_new, [], :pointer
# pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
attach_function :pa_mainloop_get_api, [:pointer], :pointer
# pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
attach_function :pa_context_new, [:pointer, :string], :pointer
# void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
callback :pa_context_notify_cb, [:pointer, :pointer], :void
# void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
attach_function :pa_context_set_state_callback, [:pointer, :pa_context_notify_cb, :pointer], :void
# int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
attach_function :pa_context_connect, [:pointer, :string, :int, :pointer], :int
# pa_context_state_t pa_context_get_state(const pa_context *c);
attach_function :pa_context_get_state, [:pointer], :int
# pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
attach_function :pa_stream_new, [:pointer, :string, :pointer, :pointer], :pointer
# void (*pa_stream_request_cb_t)(pa_stream *p, size_t nbytes, void *userdata);
callback :pa_stream_request_cb, [:pointer, :int, :pointer], :void
# void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
attach_function :pa_stream_set_write_callback, [:pointer, :pa_stream_request_cb, :pointer], :void
# int pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume, pa_stream *sync_stream);
attach_function :pa_stream_connect_playback, [:pointer, :string, :pointer, :int, :pointer, :pointer], :int
# int pa_stream_write(pa_stream *p, const void *data, size_t nbytes, pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek);
attach_function :pa_stream_write, [:pointer, :pointer, :int, :pointer, :int, :int], :int
# int pa_mainloop_run(pa_mainloop *m, int *retval);
attach_function :pa_mainloop_run, [:pointer, :pointer], :int
# int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
attach_function :pa_mainloop_iterate, [:pointer, :int, :pointer], :int
PA_CONTEXT_NOAUTOSPAWN = 1
PA_CONTEXT_UNCONNECTED = 0
PA_CONTEXT_CONNECTING = 1
PA_CONTEXT_AUTHORIZING = 2
PA_CONTEXT_SETTING_NAME = 3
PA_CONTEXT_READY = 4
PA_CONTEXT_FAILED = 5
PA_CONTEXT_TERMINATED = 6
PA_SAMPLE_U8 = 0
PA_SAMPLE_ALAW = 1
PA_SAMPLE_ULAW = 2
PA_SAMPLE_S16LE = 3
PA_SAMPLE_S16BE = 4
PA_SEEK_RELATIVE = 0
PA_SEEK_ABSOLUTE = 1
PA_SEEK_RELATIVE_ON_READ = 2
PA_SEEK_RELATIVE_END = 3
end
class Pa_sample_spec < FFI::Struct
layout(
:format, :int,
:rate, :uint32,
:channels, :uint8,
)
end
class Userdata < FFI::Struct
layout(
:gain, :int,
:omega, :double,
:theta, :double,
:data, :pointer,
)
end
def pa_context_notify
FFI::Function.new(:void, [:pointer, :pointer]) {|c, p_userdata|
state = PulseAudio.pa_context_get_state(c)
puts('state: %d' % state)
case(state)
when PulseAudio::PA_CONTEXT_UNCONNECTED
when PulseAudio::PA_CONTEXT_CONNECTING
when PulseAudio::PA_CONTEXT_AUTHORIZING
when PulseAudio::PA_CONTEXT_SETTING_NAME
when PulseAudio::PA_CONTEXT_READY
ss = Pa_sample_spec.new
ss[:format] = PulseAudio::PA_SAMPLE_S16LE
ss[:rate] = 44100
ss[:channels] = 1
stream = PulseAudio.pa_stream_new(c, 'SineWave', ss, nil)
puts('stream: 0x%016X' % stream)
PulseAudio.pa_stream_set_write_callback(stream, pa_stream_request, p_userdata)
r = PulseAudio.pa_stream_connect_playback(stream, nil, nil, 0, nil, nil)
puts('connect_playback: %d' % r)
when PulseAudio::PA_CONTEXT_FAILED
when PulseAudio::PA_CONTEXT_TERMINATED
end
}
end
def pa_stream_request
FFI::Function.new(:void, [:pointer, :int, :pointer]) {|p, nbytes, p_userdata|
userdata = Userdata.new(p_userdata)
data = userdata[:data] # ループの外にある必要!?
nbytes.times {|t|
v = userdata[:gain] * sin(userdata[:theta] += userdata[:omega])
data.put_int16(t * 2, v.to_i)
# userdata[:data].put_int16(t * 2, v.to_i) # これだとなぜか SEGV...
}
r = PulseAudio.pa_stream_write(p, userdata[:data], nbytes, nil, 0, PulseAudio::PA_SEEK_RELATIVE)
puts('stream_write(%d): %d' % [nbytes, r])
}
end
#-------------------------------------------------------------------------------
#
# Main
#
mainloop = PulseAudio.pa_mainloop_new()
puts('mainloop: 0x%016X' % mainloop)
mainloop_api = PulseAudio.pa_mainloop_get_api(mainloop)
puts('mainloop_api: 0x%016X' % mainloop_api)
context = PulseAudio.pa_context_new(mainloop_api, 'SineWaveAsync')
puts('context: 0x%016X' % context)
userdata = Userdata.new
userdata[:gain] = 32700
userdata[:omega] = 2.0 * PI * 440 / 44100
userdata[:theta] = 0
userdata[:data] = FFI::MemoryPointer.new(:int16, 32768)
PulseAudio.pa_context_set_state_callback(context, pa_context_notify, userdata)
r = PulseAudio.pa_context_connect(context, nil, PulseAudio::PA_CONTEXT_NOAUTOSPAWN, nil)
puts('context connect: %d' % r)
#r = PulseAudio.pa_mainloop_run(mainloop, nil)
loop {
r = PulseAudio.pa_mainloop_iterate(mainloop, 0, nil)
print('.')
sleep(0.01)
}
■ すーぱーたーぼ [始めまして。 X1用VHDシステムの検索でたまたま見つけたのでコメントさせていただきます。 お子様が一緒に遊んでく..]