この記事はJLCPCBの提供を受けています。

 

こんにちは。Yukiです。

今回はPCで使うマイクを自作したのでここに書こうと思います。

 

 

 

 

  欲しかった理由

 

今まで電池式のマイクを使っていたのですが、PCの電源を切るたびにON/OFFするのが面倒になったのが要因です。それならもう音質を犠牲にしてUSB電源化してしまえばいいのでは?と思い設計しました。

 

ということで最低限したいことで、

  • USB電源で動作する
  • そこそこ良い音質
  • 安価(部品代合わせて1000円以内)
を目標に作ることにします。
 

 

  回路図

 

USB電源はノイズまみれで汚いということで、まずはLDOレギュレータで5V→3.3Vにします。ここで少しでもノイズを減らせればという作戦でもあります。

 

 

次に、基準電圧生成部です。

ここは贅沢に2回路どちらも使い中間電位を生成しています。
分圧だけでも良かった気もしますが、少しだけ贅沢ということで…。

 

最後に増幅部です。増幅部は2段にしてあります。一応ですがこれでGain11-150まで行けるようになっています。

 

 

  シミュレーション

 

あまりアナログよりの回路は詳しくないため、一度LTSpiceにてシミュレーションをかけます。

 

 

問題なく増幅できているようです。

 

 

歪率も0.000129%とほぼ無視できる値です。これで良しとしましょう。

 

 

 

  部品表

 

 

なんと合計金額が658円に収まっています。

 

ちなみにWA-61A互換品は持っている思っていたら、手元になかったので、C9767を代わりに入れています。(大きさが少し違うが、無理やり入れました)

 

 

  アートワーク

 

できるだけ最短&きれいを目指しました。

オーディオ系のアートワーク設計難しいですね。

 

 

 

 

 

  JLCPCBに発注

 

今回はJLCPCB様で発注させていただきました。

 

 

ステンシル板もきれいです。

 

 

基板自体も二層基板なら 100mm x 100mm までで500円ちょっとで発注できます。ぜひご利用ください。

 

あと、今回はもう一つのブログの方でも紹介しています。

そちらもぜひご覧ください。

 

 

 

  実際に作る

 

今回もリフローで実装です。もうリフロー楽すぎてやめられないっす。

 

 

部品を載せて焼きます。

 

 

  実際に使う

 

今回はUSB電源から給電できるということで、こんな感じで使えます。

 

後はAUXケーブルでつなぐだけです。

 

 

 

 

 

  良くない点

 

思ったよりノイズが多いです。

 

ホワイトノイズというかピンクノイズのようなものが聞こえます。

最初は電源が悪いのかと思い、電池に変えてみたりもしましたが、変化なし。

 

 

FFTの様子もこんな感じで、RMS-42dBはかなり大きい部類に入るのではないでしょうか。

 

 

そして、音響系の回路を作っているのにも関わらず、抵抗によって音が変化することを知りませんでした。

今回使った抵抗は厚膜抵抗と呼ばれるもので、これは一般的に安価・ノイズ大という事らしいです。(確かにLCSCで5000個500円とかで買ったやつなんで…)

 

デジタル回路のプルアップとかだったら厚膜抵抗で十分だそうです。

 

一般的にノイズを嫌う音響回路の場合は薄膜抵抗か金属皮膜抵抗のどちらかを使うことが多いらしく、チップ抵抗だと実質薄膜抵抗一択です。(金属皮膜抵抗は高い)

 

ということで次回以降、薄膜チップ抵抗に切り替えてみて、音が変化するか試してみたいです。

 

それと、ECMももう少しお高いものを買ってみました。

 

今回はECM界隈ではそこそこ有名なPUI Audioのものです。本当は-80dB品を買いたかったのですが、Mouserでも1つ900円とかで予算オーバーすぎたので、今回は不採用としました。

POM-3535Pもデータシート値的には68dBと比較的高い値なので、どれくらい変わるのか試してみたいと思っています。

CMEJ-0627-42-Pは安かったため購入してみました。10kHzということでもしかしたらボイスチャットより特化なのかなと思いますが、S/N比が60dBなのでそこまで期待しないことにしておきます。(なにかのおもちゃとかに搭載してもいいと思っています)

 

どっちにしても、届くのが7月10日以降くらいだと思うので、時間が空いたらやってみたいと思います。

 

こんにちは。Yukiです。

今日は、BIOSアップデートしたらWoLできなくなったことについて書いていこうと思います。

 

 

  そりゃ当たり前

 

まず言っておきますが、BIOSアップデートしたらBIOSは初期状態になるものが多いので、WoLできなくなって当たり前です。

 

それを知らずに、しばらく使ってて、ルーターの調子でも悪いのかな?なんて思っていました。

 

 

 

  環境

 

frontierさんのセールで買ったPCなので、BTOですが、若干一般品と違うところがあります。(マザーボードがカスタマイズ品で、WiFi端子がなかったりなど)

 

 

  • マザーボード MSI PRO B760-P DDR4
  • CPU Intel Core i7 14700F
  • メモリ DDR4 64GB
  • 裏のRJ45端子にイーサネット接続
 
 
我が家はこんな感じになっています。ONUは共用品なのでこちらからは管理できません。
 
OpenWrtルータにWoL用のソフトウェアが入っていて、それを操作してWoLパケットを送信しています。(実際にはNASやらいろいろありますが省略)
 

 

  設定変更

 

まずはBIOS画面に入ります。

 

 

Shiftキーを押しながら再起動をクリックします。

 

 

こんな画面(Windows RE環境)が出てきたら、トラブルシューティング

 

詳細オプション

 

UEFI ファームウェアの設定

 

再起動 これだけでBIOSに入れます。F2とかDELとか連打しなくていいんですね

 

 

ここからはあくまでも私の環境の場合ですが、 上のAdvanced(F7)

 

SETTINGS をクリック

 

 

Advenced を選択

 


Wake Up Event Setup をクリック

 

 

Resume By PCI-E … みたいなのをEnableにすればOKです。

 

次に、Settingsまで戻って、Bootをクリック

 

 

Fast Boot を Disabledにすれば完了です。このメニューが見つけられなくて苦労しました。

 

BIOSから出るには、Saveみたいなの探す or ESCキー押せば、保存しますか?と出ますので、OK押せば保存されます。

 

最後に、ちゃんとWoLで起動するか確認して終了です。

 

 

  最近はすごい

 

Core i の4世代くらいからですかね?BIOS画面(正確にはUEFI BIOS)にもマウスが使えたりとか。本当に簡単になりました。

 

今だメーカーPC(特にPanasonicとか)はいまだにキーボード操作オンリーだったりしますが、HPとかのPCも最近はマウス操作できるのが当たり前になっているのがすごいなと思いますね。(その代わりBIOS製作者は苦労しているのかもしれませんが)

 

 

これでWoLが有効化できたのでよかったです。

WoLないと、外出先で家に入っているPCのデータが欲しい時困ることがあるので。

 

またBIOSアップデートしたらやります。

 

こんにちは。Yukiです。

今回は、モータードライバを作る際に、どういう電流が流れているのか気になったので、書いていこうと思います。

 

今回は、電源OFF→ON時の電流と、PWM波形を与えているときの電流どちらも見てみたいと思います。

 

 

  今回使う物品

 

今回は実験っぽい内容なので、ちゃんと使ったもの書いておきます。

  • オシロスコープ Hantek 6254BD
  • オシロスコープ ISDS205A
  • 電源 ALIENTEK DP100
  • PWM生成 LA1010
  • モーター① TG-85B-KU-36-KA
  • モーター② FA-130RA-2270
今回は電気的時定数の算出もしたいのですが、モーター①だと軸をロックできなかったので、モーター②で算出をしています。
 
モータ①の時は10Vで、モータ②の時は3Vにしています。

 

(なんでこんなモータあったんだろう?買った記憶がない…)

 

 

 

 

  電源OFF→ONの波形

 

 

OFF→ONなので、今回はこんな感じでやります。

 

 

これがモーター①の波形です。TG-85B-KU-36-KAは最大電流が3800mA(12V)くらいなので、内部抵抗Raは3.15Ωくらいだと思います。出力部に1Ω抵抗をつけているので、今回は実質Ra=4.15Ω 電源電圧は10Vなので、最大電流は2.4Aくらいになります。

 

1マス500mAで、大体4マス程度、まあ2Aくらい。誤差の範囲でしょう。(実際にはモータードライバ内の抵抗値とかもあると思うので)

 

 

次に、軸ロックした時の波形を見ます。

波形ロック時の波形はISDS205Aのデータロガー機能で保存します。

 

 

これはあくまでも軸をロックしているという前提です。(軸ロックさせないと逆起電力が発生してしまうため)

 

FA-130RA-2270も最大電流が2.2A(1.5V)らしいので、内部抵抗Raは682mΩくらいになります。

今回も外部に1Ωつけているので、Ra = 1.682Ω 電源電圧は3Vだったので、1.78Aくらいになります。が、実際には800mAくらいしか流れてないですね。(おそらくですが、電源電圧が低いので、モータードライバICのON抵抗も上がっているのだと思います)

 

電気的時定数を求めるには、0→63.2%になる時間を測定します。今回だと172.9usですね。

モータードライバの内部抵抗などを考慮すると、Ra = 3V / 800mA = 3.75Ω L=Rt = 3.75Ω × 172.9us = 648uH と求まります。

 

 

  PWM波の電流

 

この実験はすべてモーター①を使っています。

 

 

今回はこのような感じで電流測定します。

さすがに差動プローブは持っていないので、受動プローブ2本を使って、MATHモードで差分を出します。

 

オシロスコープは紫(CH3)が入力PWM波で、赤がMATH値です。各2ポイントの差分を出して電流値を出しています。

 

 

見えにくくてすみません。急に上がって、そこからはじわじわと上がる感じっぽいですね。

 

ちなみにこれは1kHzで駆動させていて、非連続電流モードになっています。

 

 

 

次には100kHz駆動です。(本当はモータードライバの限界超えてますが、まあいけました)

 

こんな感じで結構なだらかな波形になるんですね。驚きました。

 

 

  なぜこんな実験したのか

 

今度作るモータードライバ基板で、電流カット方法をどうしようかなと考えていたためです。なんとなく予想はできていましたが、RCフィルタを組み合わせれば作れそうだなと思いました。

 

  アメブロの仕様変更

 

前からPVの仕様が変更されたらしく、明らかに少なくなってしまい悲しいです。

おそらくですが、AI(LLMのRAG等)から参照されたPV数もカウントされないのでしょうか?そこら辺はわかりませんが、まあしょうがないですね。

 

こんにちはYukiです。

今回はUbuntuに完全移行できなかったことについて書いていこうかと思います。

中身はなんにも内容がないポエムみたいな記事です。

 

  完全移行できなかった理由

 

まあ簡単です。アプリケーションが対応していないことです。

 

もともとOSはなんでもよいと思ってる私ですが、機械学習やらそういうことをやろうとするとなると、やはりLinux(特にDebian系)がやはり有利です。これはやはりAI開発者がLinuxを使うことが多いからでしょう。(無料だし、オープンソースなのでいろいろ楽なのだと思われる)

 

しかし逆に、CADソフトであったりはWindowsのみの対応であることが多いです。

私は使ってませんが、FusionなんかはLinux非対応です。また、組み込み系のIDEもLinuxに対応していなかったりします。

 

MounRiver Studio (CH32V/CH32F系のIDE)だとLinuxにも対応しているみたいですが、LinkUtilityなどが使えなかったりするみたいです。(もしかしたら今は変わっているかもしれません)

 

まあこんなことで使えないのかなしいんですが、しょうがないですね。

 

一応Wineとかのエミュレータを使うという方法もありますが、できればやりたくないですね。(落ちた時萎えるし)

 

 

  Linuxの便利だなと思うところ

 

やはりbashでしょう。もともと改造したルーターとかで遊ぶ時もbashに近い構文を使えます。(lsとかpwdとかmkdirとか)

確かopenWrtとかだとashだったかな?

 

実際ワンライナーでなんでもやる職人がいたり、使えこなせれば最強に近づくかもしれません。(まだ私はその領域には達していませんが)

 

まあ使えないものはしょうがない。

 

最近気づいたのですが、lsとかであればpowershellならできるので、powershellを使いこなせるようになりたいですね。powershell芸人 なんちゃって。

 

  やはり使いたいソフトで選ぶ

 

LTSpiceとかもLinux非対応なので、もうしばらくはWindows+WSLまたはdual bootでUbuntuを使おうと思います。

 

仮想PCでもいいっちゃいいんですけど、正直あまり好きではないので。

 

ここまでUbuntu使いたい!とか書いておいてあれですが、ぶっちゃけOSというよりかは使いたいソフトウェアで考えたほうが良い気はします。機械学習系はLinux 組み込み系はWindows ゲームはWindows みたいな感じですかね。

 

 

こんにちは!Yukiです。

今回はCH32V203を使って、SD+FAT32のファイルの読み書きをしてみたいと思います。

 

 

 

 

  使ったもの

 

 

 

今回は信頼と安心のKIOXIAのMicroSDカードを使いました。(株価も上がってr(ry)

 

 

 

今回私は秋月のものを買いましたが、これであれば抵抗なども実装されているので便利かもしれません。(保証はしません)

 

 

  FATとは

 

File Allocation Tableの略で、ディスクのファイルの位置情報や日付などに関する情報を保存しておくための領域です。

 

これらを使ったファイルシステムを FATファイルシステムと呼びます。

しかし、ほとんどの人はFAT = FATファイルシステムと呼びます。

 

FATには様々ながあり、

  • FAT12
  • FAT16
  • FAT32
  • exFAT
に分類されます。
 
FAT12は最大ファイルサイズ32MiB 最大ボリュームサイズ32MiBという今では小さすぎてなにもできない子です。フロッピーディスク(1.44MiB)に多く使われていました。
 
次にFAT16です。こちらは最大ファイルサイズ 2GiB 最大ボリュームサイズ 2GiBとなっておいて、こちらもまた今ではあまり見かけられないものになりました。
ちなみにWindows11とかのフォーマットで出てくるFATはこいつらしいです。
 
次にFAT32です。最大ファイルサイズ 4GiB 最大ボリュームサイズ2TiBとなっています。いきなりボリュームサイズが大きくなりました。ちなみにWindows標準のフォーマットツールだと32GiBまでしかフォーマットできません。
 
最後にexFATです。こちらはFAT32以前のシステムと大きくかわっており、exFATは少し異種です。最大ファイルサイズは16EiBで、最大ボリュームサイズは512TiBとなっています。ちなみに、ボリュームサイズはあくまでも推奨で、理論上は64ZiBまで扱えます。(まさかのPとYぬかしてのZですね…)
 
次に、ファイル名についてです。exFATには関係ない話です。
FATにはファイル名に8.3形式を採用しています。(ファイル名8文字以下 拡張子3文字以下) なおファイル名はすべて大文字という規則になっています。
HELLO.TXT とか NOTEPAD.EXE みたいな感じですね。
この形式をFATではSFN (Short File Name)といいます。
 
しかしこれだと不便だと思ったのか、MAX255文字までファイル名を拡張できるように仕様変更されました。これがLFN (Long File Name)です。
 
なのでFAT32でフォーマットされたUSBメモリなどでも長い名前で使えます。
ちなみにLFNを使わないと日本語も使えないので注意です。
 
 

 

  FatFsについて

 

FATは規格が複雑で、自分で実装するにはハードルが高いです。

そこでFatFsというChaN様が製作されたライブラリを組み込んで使うことが多いです。

 

 

 

 

簡単に言うと、SDにアクセスする方法さえ自力で書ければ、あとはFatFsがファイル書き込みや読み込みなどを行ってくれるようになります。

 

もちろんSDを取り外しPCにさせば、マイコン側で書き込んだファイルを見たり編集することだってできます。

 

 

このシステムを作っていただいたChaN様にお礼申し上げます。本当に素晴らしいシステムです。

 

 

  早速実装

 

 

前回の記事でSDの書き込み・読み込みは実装できているので、これを使います。

 

FatFsは本当は複数ブロックまとめて書き込み・読み込みをするマルチブロック書き込み・読み込みに対応しているので、非効率ではあるのですが、今回はシングルブロック書き込み・読み込みだけで実装していきます。

(また個人的に困ったことがあれば実装したいと思います)

 

 

実装するといっても、下位レイヤーを軽く書いてあげるだけです。

 

diskio.cを新規作成し、そこに書いていきます。

 

必要な関数は

  1. disk_initalize
  2. disk_status
  3. disk_read
  4. disk_write
  5. disk_ioctl
  6. disk_fattime
です。
 
まずインクルード・宣言・グローバル変数等です。
 

#include "diskio.h"

#include "../CH32V203_Mylib/SPI_Mylib.h"

 

#define DEV_SD_DEBUG 0

#define DEV_SD 1

 

static DSTATUS Stat_DEV[5] = {STA_NOINIT};

 
DEV_SD_DEBUGやDEV_SDはファイルアクセス時に使うことになるドライブ番号のようなものです。(WindowsでいうC:\みたいなもの)
 
DEBUGはUARTにログが出てきます。それだけの違いです。
 
Stat_DEV[5] はそのデバイスをマウントしたかどうか記録しておくための変数です。
 
 
次に、disk_initialize関数です。

DSTATUS disk_initialize (BYTE pdrv){

 

    DSTATUS stat;

    int result;

 

    if(pdrv == DEV_SD_DEBUG){

        result = SD_init_debug();

 

        if(result == 0){

            Stat_DEV[DEV_SD_DEBUG] = RES_OK;

            stat = RES_OK;

        }else{

            stat = RES_ERROR;

        }

 

        return stat;

 

    }else if(pdrv == DEV_SD){

        result = SD_init();

 

        if(result == 0){

            Stat_DEV[DEV_SD] = RES_OK;

            stat = RES_OK;

        }else{

            stat = RES_ERROR;

        }

 

        return stat;

 

    }

 

    return STA_NOINIT;

}

 
ここはSDの初期化について書くところです。
(なんかinitializeみてたら、急にマテリアライズを思い出しましたw)
 
次に、disk_statusです。

DSTATUS disk_status (BYTE pdrv){

    if(pdrv == DEV_SD){

        if(Stat_DEV[DEV_SD] == STA_NOINIT){

            return STA_NOINIT;

        }

        return RES_OK;

    }else if(pdrv == DEV_SD_DEBUG){

        if(Stat_DEV[DEV_SD_DEBUG] == STA_NOINIT){

            return STA_NOINIT;

        }

        return RES_OK;

    }

    return RES_ERROR;

}

 
ここはドライブが初期化されているかどうか・メディアがセットされているか・メディアにライトプロテクト(書き込み禁止)がされていないかを返すところです。
 
今回はメディアセットとライトプロテクトに関しては無視しています。
 
ドライブ初期化はdisk_initializeが正常に動作した際に代入される値で見ています。
(本当は適当なCMDから帰ってくるR1レスポンスを見るべきなのかもしれません)
 
 
次に、disk_read関数です。

DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count){

    if(pdrv == DEV_SD){

        int res = RES_OK;

        for(int i = 0; i < count; i++){

            res = SD_Read(sector + i, buff + (i * 512));

        }

 

        if(res != 0){

            return RES_ERROR;

        }

 

        return res;

 

    }else if(pdrv == DEV_SD_DEBUG){

        int res = RES_OK;

        for(int i = 0; i < count; i++){

            res = SD_Read_debug(sector + i, buff + (i * 512));

        }

 

        if(res != 0){

            return RES_ERROR;

        }

 

        return res;

    }

 

    return RES_ERROR;

}

 
ややこしいのですが、sectorは512byteずつで進んでいき、buffは1byteずつですすんでいくのでこのような記述になります。後はcount関数繰り返すだけです。
 
 
次に、disk_write関数です。

DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count){

    if(pdrv == DEV_SD){

        int res = RES_OK;

 

        for(int i = 0; i < count; i++){

            res = SD_Write(sector + i, buff + (i * 512));

        }

 

        if(res != 0){

            return RES_ERROR;

        }

 

        return RES_OK;

 

    }   else if (pdrv == DEV_SD_DEBUG){

        int res = RES_OK;

 

        for(int i = 0; i < count; i++){

            res = SD_Write_debug(sector + i, buff + (i * 512));

        }

 

        if(res != 0){

            return RES_ERROR;

        }

 

        return RES_OK;

    }

 

    return RES_ERROR;

}

 
こちらもRead関数と同様の書き方です。
 
次に、disk_ioctl関数です。

DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff){

    switch(pdrv)

    {

        case DEV_SD:

           

            if(cmd == CTRL_SYNC){

                return RES_OK;

            }else if(cmd == GET_SECTOR_SIZE){

                *(WORD*)buff = 512;

                return RES_OK;

            }else if(cmd == GET_SECTOR_COUNT){

                *(WORD*)buff = SD_AllBlockCnt_debug();

                return RES_OK;

            }else if(cmd == GET_BLOCK_SIZE){

                *(WORD*)buff = 1;

                return RES_OK;

            }else{

                return RES_ERROR;

            }

 

            break;

        case DEV_SD_DEBUG:

 

            if(cmd == CTRL_SYNC){

                return RES_OK;

            }else if(cmd == GET_SECTOR_SIZE){

                *(WORD*)buff = 512;

                return RES_OK;

            }else if(cmd == GET_SECTOR_COUNT){

                *(WORD*)buff = SD_AllBlockCnt();

                return RES_OK;

            }else if(cmd == GET_BLOCK_SIZE){

                *(WORD*)buff = 1;

                return RES_OK;

            }else{

                return RES_ERROR;

            }

 

            break;

        default:

           

            return RES_ERROR;

 

            break;

    }

 

    return RES_ERROR;

}

 
こちらが少し面倒です。
cmd引数で来たコマンドに応じて返却しなくてはいけません。
 
まず、CTRL_SYNCです。
これはドライブの書き込み処理が終わっているか確かめるコマンドです。
今回実装しているSD_write関数は、書き込みが終わらないと終わらない仕様になっているので、RES_OKを返しています。
 
次に、GET_SECTOR_SIZEです。
これはドライブのセクタサイズを返すコマンドです。
SDカードはブロック単位が512byteですから、512を返しています。
 
次に、GET_SECTOR_COUNTです。
これはドライブ上の総セクタを返す必要があります。ということでドライブの総セクタ数を返す関数を作りました。ということで、*(WORD*)buff = SD_AllBlockCnt();だけです。
 
次に、GET_BLOCK_SIZEです。
これは削除ブロックのサイズを返します。不明であれば1でよいとリファレンスマニュアルにありましたので、1を返しています。
 
最後にCTRL_ERASE_SECTORです。
これはフラッシュの一部領域を削除する際に使うようです。
これはよくわからなかったので実装していません。(正直マイコン内で削除処理を行う行為はやめた方がいいと思います。別のデータも飛びそうです)
 
 
最後に get_fattime関数です。

// とりあえず 2026年1月1日 00:00:00 を返す

DWORD get_fattime (void) {

    return ((DWORD)(2026 - 1980) << 25) /* Year 2026 */

         | ((DWORD)1 << 21)             /* Month 1 */

         | ((DWORD)1 << 16)             /* Mday 1 */

         | ((DWORD)0 << 11)             /* Hour 0 */

         | ((DWORD)0 << 5)              /* Min 0 */

         | ((DWORD)0 >> 1);             /* Sec 0 */

}

 
CH32V203マイコンにはRTCモジュールがついていますが、特に書き込み日時・読み込み日時についてどうでもいいということであれば、これでOKです。
 
ちゃんとやりたい場合は、RTCモジュールを使用し、UNIXタイムを返してあげましょう。
 
 

 

  CH32V203での制限

 

ROMの容量が足りなかった関係で、LFN(Long File Name)に関しては使えません。(対応表がとんでもなく重いみたいです)

 

ということで、このマイコンでは8.3表記しか扱えないことに注意が必要です。

(HELLO.TXTとかMINE.EXEとか)

 

  GitHub

 

今回のプログラムは長くなったので、GitHubに上げました。

少しですがサンプルのコードもあります。ぜひご活用ください。(つかう人がいればですが)

 

 

 

何かあったらここにコメントするか、issueに登録してください。(使う人いない気もしますが)

 

 

 

  次回までにやってみたいこと

 

FAT32ができたので、MP3プレイヤーもどきとか作ってみたいですね。

(ただ、ID3対応前提になると思うので、そこだけ面倒ですが)

 

どうせならアルバムアートなんかも表示して…
とか思ってると結構難しいかも…
 

チャレンジしたいですね。