STM32からロシアのマイクロコントローラーK1986BE92QIに渡します。 音を生成して再現します。 パート1

エントリー



前回の記事で、マイクロコントローラーのクロック周波数の設定について説明しました。 次に、サウンドを操作するためのオプション、つまり生成と再生を検討したいと思います。 最初は、すべてが考慮される1つの大きな記事を書きたいと思いました。 矩形パルスの生成から、microSDカードからのFLACの再生まで。 しかし、この記事は巨大なものでした。 そこで、私はそれをいくつかの小さな記事に分割することにしました。 それぞれで、1つの周辺モジュールを分解します。



記事のタスクの実装が気に入らない人向け。
すぐに予約します。 すでに述べた知識の助けを借りてのみ、記事で提起された問題を解決します。 DMAをどこかで、タイマーをどこかで、などを使用する方が真実であることを理解しています。 しかし、これはすべて後で検討されます。





単純なサウンド生成方法の一般的なアイデア。



この記事を書くまでは、音がどのように生成されるかについて漠然とした考えしかありませんでしたが、 この記事を読んだ後、すべてが適切に配置されました。 上記の記事から、最も重要なことを区別することができます-原則。

音を作成するには、スピーカーの膜を特定の周波数で振動させる必要があります。 各音には固有の周波数があります。たとえば、最大1オクターブの音は、261Hzの周波数に対応します。 つまり スピーカーに接続されたマイクロコントローラーの足を毎秒261回の速度でけいれんさせると、この音が聞こえます。 音楽理論が苦手な人にとっては、1 kHz以上に近い音ほどきしみ、300 Hz未満では低音になります。


ボードには、ジャック3.5コネクタとそのためのアンプがあります。 回路図を検討してください。







ご覧のとおり、アンプはPE0ピンを介してコントローラーに接続されています。 ボードの「DAC_OUT_SEL」ジャンパーを切り替えた後、たとえばヘッドフォンをジャックに接続することで聞こえる音を生成できます。



シンプルなトゥイーターの実現



理論に基づいて、最初のオクターブまでのノートの頻度できしむプログラムを書くことができます。

ピンを設定します。 ポートにはアンプ以外は何もないという修正を加えてコードが記述されていることにすぐに同意します。 その後、普遍的な機能を作ることは難しくありません。



//--------------------------------------------------------- // ,   . //--------------------------------------------------------- #define PER_CLOCK_PORTE (1<<25) //    E. #define PORT_OE_OUT_PORTE_0 (1<<0) //    PORTE_0  "". #define ANALOG_EN_DIGITAL_PORTE_0 (1<<0) //     PORTE_0. #define PWR_MAX_PORTE_0 (3<<0) //    PORTE_0    . #define PORT_RXTX_PORTE_0_OUT_1 (1<<0) //    "1"  . void Buzzer_out_init (void) { RST_CLK->PER_CLOCK |= PER_CLOCK_PORTE; //   E. PORTE->OE |= PORT_OE_OUT_PORTE_0; //. PORTE->ANALOG |= ANALOG_EN_DIGITAL_PORTE_0; //. PORTE->PWR |= PWR_MAX_PORTE_0; //  ( 10 ). }
      
      





次に、「きしむ」頻度を決定する必要があります。 この周波数表はこれで私を助けました。 わかりやすくするために、以下に示します。







最初のオクターブの(C)の前の音の周波数は261.63ヘルツです。 これは、1秒間に261.63期間が経過することを意味します。 それぞれ、ビットの状態を2回変更します。 そして、ビットの状態を毎秒523.26回変更する必要があります。 1秒を523.26で割ると0.0019110958223445になります。これは、およそ191 * 10 ^(-5)秒に相当します。 これは、切り替え間の「一時停止」です。



遅延のおおよその値がわかったので、SysTickタイマーとそれに遅延を設定できます。 10 ^(-5)秒ごとに中断するように設定する必要があります。 これを行うには、 この記事の関数を少し変更します。 以下が得られます。



 void Init_SysTick (void) //   10^(-5) . { SysTick->LOAD = (8000000/100000)-1; SysTick->CTRL |= CLKSOURCE|TCKINT|ENABLE; } volatile uint32_t Delay_dec = 0; //  SysTick . void SysTick_Handler (void) { if (Delay_dec) Delay_dec--; } void Delay (uint32_t Delay_Data) //    SysTick . { Delay_dec = Delay_Data; while (Delay_dec) {}; }
      
      





さて、最後に、上記のすべてを使用します。

 int main (void) { Buzzer_out_init(); //   . Init_SysTick(); //       10^(-5) . while (1) //    261.63 . { PORTE->RXTX |= PORT_RXTX_PORTE_0_OUT_1; // "1"  ,   . Delay(191); PORTE->RXTX = 0; Delay(191); } }
      
      





すべてがうまくいきますが、ここに最初の熊手があります。 お気づきかもしれませんが、メイン関数にはクロックを設定するためのコード行がありません。 コントローラはHSIからクロックされます。 この例では、これにより、最初のオクターブへのノートの代わりに、小さなオクターブ(2トーン全体)のノートが再生されます。 このエラーを修正するには、クロックソースをHSIからHSE(外部水晶振動子)に切り替える機能を追加します。



タイミングシステムはこのレッスンで説明されました



 int main (void) { HSE_Clock_ON(); //  HSE . HSE_Clock_OffPLL(); // ""      HSE . Buzzer_out_init(); //   . Init_SysTick(); //       10^(-5) . while (1) //    261.63 . { PORTE->RXTX |= PORT_RXTX_PORTE_0_OUT_1; // "1"  ,   . Delay(191); PORTE->RXTX = 0; Delay(191); } }
      
      





タスクを少し複雑にしましょう。 12個のハーフトーン(ピアノを見ると、7個の白鍵と5個の黒鍵)を再生するプログラムを作成します。 これを行うには、すべての遅延の継続時間で配列を作成します。 前のものと同じ方法でそれらを計算します:100000 / frequency_notes / 2 = duration_delay。 0.00001秒(10 ^(-5))ごとに割り込みがあるため、100000を分割します。



 const uint32_t MES[13] = {191, 180, 170, 161, 152, 143, 135, 128, 120, 114, 107, 101, 96};
      
      





それでは、メイン関数を少し変更しましょう。



 int main (void) { HSE_Clock_ON(); //  HSE . HSE_Clock_OffPLL(); // ""      HSE . Buzzer_out_init(); //   . Init_SysTick(); //       10^(-5) . while (1) //    261.63 . { for (uint32_t Nambe = 0; Nambe<13; Nambe++) //   . { for (uint32_t LoopN = 0; LoopN<MES[12-Nambe]*3; LoopN++) //     . { PORTE->RXTX |= PORT_RXTX_PORTE_0_OUT_1; // "1"  ,   . Delay(MES[Nambe]); PORTE->RXTX = 0; Delay(MES[Nambe]); } } } }
      
      





コードの簡単な説明。 各音のサンプル音の長さ(音波の1周期)が異なるため、各音の音をほぼ同じにするために、待機サイクルは次のように構成されました。 最も長い音符(遅延数が大きい音符)の数は、最短で「遅延時間」に聞こえました。 説明します。 最初のオクターブ(191)の前の音符は96 * 3回演奏され、2番目のオクターブ(96)の前の音符は191回* 3回演奏されました。 次に、遅延を測定するより正確な方法を検討します。



これが私たちの波のようです。







よく見ると、その不完全さがわかります。 矩形のインパルスに非常によく似ていません。







プロジェクトと、結果として得られるgithubのサウンドファイル



DACマスタリング



私たちの「波」のパターンを調べてみると、正弦波があることすらわかりません。 マイクロコントローラで正弦波と一般的にあらゆる形状の信号を作成する問題を研究して、 この記事に出会いました。 この記事は私に長い間出くわしました。 そして、私はただ楽しみのためにそれを読みました。 今では実用的な関心事です。 この記事では、シンプルなDAC(デジタル-アナログコンバーター)の作成と使用について説明します。 DACは、電圧値を直接アナログ出力電圧に変換するデバイスです。 すでに記事から図を収集したかったのですが、もう一度ドキュメントを調べて、ポイントを見つけました。

MDR_DACコントローラー...................... 326


これは私にとって非常に嬉しい驚きでした。 マイクロコントローラ自体にDACを直接見たことがありません。 ただし、最初にDACをアンプに接続できるかどうかを理解する必要があります。 これを行うには、回路基板を開き、次を参照してください。







アンプの出力は、DACが接続されているPE0に直接接続されています。 素晴らしい。 セットアップを開始できます。 しかし、その前に、DACについて少し勉強します。

マイクロコントローラーには2つのDACがあります。 DACを有効にするには、Cfg_ON_DACxビットを1に設定し、ポートEの使用されているDACピンをアナログとして構成し、内部ブレースを無効にする必要があります。 両方のDACは独立して、または共同で動作できます。 DACの独立動作中(ビットCfg_SYNC_A = 0)、データがDACx_DATAデータレジスタに書き込まれた後、記録された値に対応する電圧レベルがDACx_OUTの出力に形成されます。 同期動作中(ビットCfg_SYNC_A = 1)、両方のDACのデータは、DACx_DATAレジスタの1つの1つのレコードで更新できます。 DACは内部サポートCfg_M_REFx = 0で動作でき、DACは0〜電源電圧AUCCの範囲の出力信号を生成します。 外部サポートCfg_M_REFx = 1の動作モードでは、DACは0から値DACx_REFの範囲の出力電圧を生成します。


ここでは、セットアップ全体が記述されていると言えます。 レジスタを見てください。







ここには、2つのDACに対して3つのレジスタしかありません。 これらのうち、各DACの出力値を保存するための2つのレジスタ。 ケースの設定を検討してください。







セットアップを始めましょう。 また、DACクロッキングについても忘れないでください。 さて、テストのために、出力に最大電圧を設定しました(0xFFF = 4095)。



 //--------------------------------------------------------- //. //--------------------------------------------------------- #define PCLK_EN_DAC (1<<18) //   . #define CFG_Cfg_ON_DAC0 (1<<2) //  1. void ADC_Init (void) { RST_CLK->PER_CLOCK |= PCLK_EN_DAC; //  . DAC->CFG = CFG_Cfg_ON_DAC0; // 1. .   . }
      
      





さらに、出口についても忘れません。 前のレッスンでは、デジタル出力として構成しました。 次に、推奨事項に従って、アナログとして構成する必要があります。



 void Buzzer_out_DAC_init (void) { RST_CLK->PER_CLOCK |= PER_CLOCK_PORTE; //   E. PORTE->OE |= PORT_OE_OUT_PORTE_0; //. PORTE->ANALOG = 0; //. PORTE->PWR |= PWR_MAX_PORTE_0; //  ( 10 ). }
      
      





さて、これらすべてをメイン関数に追加してください。



 int main (void) { HSE_Clock_ON(); //  HSE . HSE_Clock_OffPLL(); // ""      HSE . Buzzer_out_DAC_init(); //   . ADC_Init(); // . Init_SysTick(); //       10^(-5) . DAC->DAC1_DATA = 0xFFF; //  (). while (1) { } }
      
      





ここで、ピンの電圧を測定すると、約3ボルトになるはずです。 しかし。 これは起きていません。 ピンには、約0.08ボルトがあります。 良くないもの。 整理しましょう。 まず、DACがカプセル化されているかどうかを確認しました。 デバッガーは、すべてのレジスタが正しく入力されたことを報告します。 次に、ピンテーブルを調べることにしましたが、次のことがわかりました。







ここにニュースがあります。 PE0はDAC1ではなくDAC2に接続されています! ここに別の間違いがあります... DACの機能を変更します。



 //--------------------------------------------------------- //. //--------------------------------------------------------- #define PCLK_EN(DAC) (1<<18) //   . #define CFG_Cfg_ON_DAC0 (1<<2) //  1. #define CFG_Cfg_ON_DAC1 (1<<3) void ADC_Init (void) { RST_CLK->PER_CLOCK |= PCLK_EN(DAC); //  . DAC->CFG = CFG_Cfg_ON_DAC1; // 2. .   . }
      
      





実行しようとしています。 今ではすべてが順調です。 出力は3.28ボルトです。 さて、簡単なツイーターの例に従って、矩形パルスで音を生成してみましょう。 これを行うには、前のプロジェクトのコードをわずかに変更します。



 const uint32_t MES[13] = {191, 180, 170, 161, 152, 143, 135, 128, 120, 114, 107, 101, 96}; int main (void) { HSE_Clock_ON(); //  HSE . HSE_Clock_OffPLL(); // ""      HSE . Buzzer_out_DAC_init(); //   . ADC_Init(); // . Init_SysTick(); //       10^(-5) . while (1) { for (uint32_t Nambe = 0; Nambe<13; Nambe++) //   . { for (uint32_t LoopN = 0; LoopN<MES[12-Nambe]*3; LoopN++) //     . { DAC->DAC2_DATA = 0xFFF; Delay(MES[Nambe]); DAC->DAC2_DATA = 0; Delay(MES[Nambe]); } } } }
      
      





純粋に、音は前の例よりもずっといい感じです。 はい、それははるかに大きく聞こえます。 githubのプロジェクトとサウンドファイル



そして、ここで、比較のために、私たちの波。











余談:ボード上のアンプは非常に高温です。 このモードで10分間放置すると、炉に変わります。そのため、音を聞いた後、ジャンパーをオフにします。 そのため、熱くなりません。



正弦波の生成。



さまざまなレベルの出力で電圧を生成する方法を理解したので、この電圧の値をどこで取得するのか疑問に思いました。 文字通り、検索の開始直後に、 この記事に出会いました。 その中で、私は最も重要なものを見つけました。 正弦波値を取得するためのコード。 コードを少し変更して、波長とサンプル周波数を要求し、コードの電圧値の配列を生成するプログラムを得ました。 githubのPascal ABCのコードを次に示します (すべて同じですが、試験の準備が必要であり、Pascalで記述することもあります)。



 Program Sin_wav; Var Real_Data, PR: double; // . samplerate: word; // . wavefrequency: double;// . Loop: word; //. Name: string; // . Begin write(' : '); readln(samplerate); // . write(' : '); readln(wavefrequency); write(' : '); readln(Name); write('const uint16_t ', Name, '[', samplerate, '] = {'); PR:=samplerate/2; // . for Loop:=0 to samplerate-1 do //-1, ..   0. Begin Real_Data := 2047*sin(Loop*pi/PR) + 2047; // sine-. //..       -  0  . //  2048-1 ( 0  4095) = 0,  2045 = -2. //2047 -    .  +,  -.  0. write(Round(Real_Data)); if (Loop<>samplerate-1) then write(', '); End; write('};') End.
      
      





少し説明します。 正弦波は正と負の両方の値を取ることができます。 DACは正電圧のみを生成できます。 大まかに言えば、変動は電圧の変化によるものなので、0はDACの分解能の半分になると判断しました。 つまり、2047 = 0、2045 = -2、2049 = 2です。合計振幅は4095です(0からアカウントを保持する場合)。 以下は、最初のオクターブまでの音の正弦波を生成するコードの実行例です(表によると、波の周波数は261.63ヘルツです)。 この正弦波を100個のセクションに分割します。



(C)最初のオクターブ、100セクションに注意してください。
  : 100  : 261.63  : C_4 const uint16_t C_4[100] = {2047, 2176, 2304, 2431, 2556, 2680, 2801, 2919, 3033, 3144, 3250, 3352, 3448, 3539, 3624, 3703, 3775, 3841, 3899, 3950, 3994, 4030, 4058, 4078, 4090, 4094, 4090, 4078, 4058, 4030, 3994, 3950, 3899, 3841, 3775, 3703, 3624, 3539, 3448, 3352, 3250, 3144, 3033, 2919, 2801, 2680, 2556, 2431, 2304, 2176, 2047, 1918, 1790, 1663, 1538, 1414, 1293, 1175, 1061, 950, 844, 742, 646, 555, 470, 391, 319, 253, 195, 144, 100, 64, 36, 16, 4, 0, 4, 16, 36, 64, 100, 144, 195, 253, 319, 391, 470, 555, 646, 742, 844, 950, 1061, 1175, 1293, 1414, 1538, 1663, 1790, 1918};
      
      









サイン波実験。



100個に分割された正弦波を受け取った後、この正弦波は261.63ヘルツの周波数で失われなければならないことを思い出してください。 割り込み間隔を計算しましょう。 秒/(100パーツ* 261、63)= 0.00003822191秒。 じゃあ すぐに言います。 私は音を得るために実験の海を実施しました。 それらについて簡単に説明します。 8 MHzの周波数は明らかにこのような速度には十分ではなかったので、私は自分自身を扱うことを決め、チップが80 MHzにオーバークロックし、これで間違いなく十分であることを期待しました。 しかし、そこにはありませんでした。 SysTickの割り込みを1秒あたり10,000,000回中断すると、コントローラーはデータが表示されるサイクルに到達することさえできませんでした。 その後、割り込みですぐにデータを出力する方がずっと簡単だと判断しました。 次のことが判明しました。



 void Init_SysTick (void) // 10000000   . { SysTick->LOAD = (80000000/10000000)-1; SysTick->CTRL |= CLKSOURCE|TCKINT|ENABLE; } const uint16_t C_4[100] = {2047, 2176, 2304, 2431, 2556, 2680, 2801, 2919, 3033, 3144, 3250, 3352, 3448, 3539, 3624, 3703, 3775, 3841, 3899, 3950, 3994, 4030, 4058, 4078, 4090, 4094, 4090, 4078, 4058, 4030, 3994, 3950, 3899, 3841, 3775, 3703, 3624, 3539, 3448, 3352, 3250, 3144, 3033, 2919, 2801, 2680, 2556, 2431, 2304, 2176, 2047, 1918, 1790, 1663, 1538, 1414, 1293, 1175, 1061, 950, 844, 742, 646, 555, 470, 391, 319, 253, 195, 144, 100, 64, 36, 16, 4, 0, 4, 16, 36, 64, 100, 144, 195, 253, 319, 391, 470, 555, 646, 742, 844, 950, 1061, 1175, 1293, 1414, 1538, 1663, 1790, 1918}; volatile uint16_t Loop = 0; volatile uint32_t Delay_dec = 0; //  SysTick . void SysTick_Handler (void) { Delay_dec++; if (Delay_dec==(382-1)) { DAC->DAC2_DATA = C_4[Loop]; if (Loop<99) Loop++; else Loop = 0; Delay_dec=0; } }
      
      





主な機能は次のとおりです。



 int main (void) { HSE_Clock_ON(); //  HSE . HSE_Clock_OffPLL(); // ""      HSE . Buzzer_out_DAC_init(); //   . ADC_Init(); // . HSE_PLL(10); //8  -> 80 . Init_SysTick(); //    . while (1) { } }
      
      





音は次のとおりです。







よく見ると次のことがわかります。







「正弦波」のおよその増加は次のとおりです。







大きなエラーが表示されます。 また、音は非常に低かった。 たぶん小さなオクターブのために。 高くない。 これは、割り込み内のコードに実行する時間がないことを示しています。 80 MHzの周波数でも。 私たちは異なった行動をします。 品質を少し下げます。 中断をもう少し少なくしましょう。 そして、割り込みの待機サイクルを一巡します。 以下が得られます。



 void Init_SysTick (void) // 10000000   . { SysTick->LOAD = (80000000/1000000)-1; SysTick->CTRL |= CLKSOURCE|TCKINT|ENABLE; } const uint16_t C_4[100] = {2047, 2176, 2304, 2431, 2556, 2680, 2801, 2919, 3033, 3144, 3250, 3352, 3448, 3539, 3624, 3703, 3775, 3841, 3899, 3950, 3994, 4030, 4058, 4078, 4090, 4094, 4090, 4078, 4058, 4030, 3994, 3950, 3899, 3841, 3775, 3703, 3624, 3539, 3448, 3352, 3250, 3144, 3033, 2919, 2801, 2680, 2556, 2431, 2304, 2176, 2047, 1918, 1790, 1663, 1538, 1414, 1293, 1175, 1061, 950, 844, 742, 646, 555, 470, 391, 319, 253, 195, 144, 100, 64, 36, 16, 4, 0, 4, 16, 36, 64, 100, 144, 195, 253, 319, 391, 470, 555, 646, 742, 844, 950, 1061, 1175, 1293, 1414, 1538, 1663, 1790, 1918}; volatile uint16_t Loop = 0; volatile uint32_t Delay_dec = 0; //  SysTick . void SysTick_Handler (void) { Delay_dec++; if (Delay_dec==(38-1)) { DAC->DAC2_DATA = C_4[Loop]; if (Loop<99) Loop++; else Loop = 0; Delay_dec=0; } }
      
      





これで、割り込みが処理されるようになりました。 音符とほぼ同じ音がします。 しかし、それでも耳で(ピアノと比較した場合)不正確さを聞くことができます。 githubのプロジェクトファイルとサウンドファイル。



音波の形式は次のとおりです。







「正弦波」の立ち上がりは次の形式になります。







ご覧のとおり、100個のパーツには意味がありません。 DACには、電圧を変更する時間がありません。 (研究の時点で私には思えた。)サイン波が20の部分で構成されるようにプロジェクトを変更する。 次の配列を取得します。



  : 20  : 261.63  : C_4 const uint16_t C_4[20] = {2047, 2680, 3250, 3703, 3994, 4094, 3994, 3703, 3250, 2680, 2047, 1414, 844, 391, 100, 0, 100, 391, 844, 1414};
      
      





ここで割り込み頻度を計算します。 秒/(20部* 261.63)= 0.00019110958秒〜191 * 10 ^(-6)。 これは以前よりも優れています。 割り込みと遅延を構成します。 以下が得られます。



 void Init_SysTick (void) // 1000000   . { SysTick->LOAD = (80000000/1000000)-1; SysTick->CTRL |= CLKSOURCE|TCKINT|ENABLE; } const uint16_t C_4[20] = {2047, 2680, 3250, 3703, 3994, 4094, 3994, 3703, 3250, 2680, 2047, 1414, 844, 391, 100, 0, 100, 391, 844, 1414}; volatile uint16_t Loop = 0; volatile uint32_t Delay_dec = 0; //  SysTick . void SysTick_Handler (void) { Delay_dec++; if (Delay_dec==(191-1)) { DAC->DAC2_DATA = C_4[Loop]; if (Loop<19) Loop++; else Loop = 0; Delay_dec=0; } }
      
      





Doにさらに近いサウンドが得られました。 サウンドファイルは、上記のリンクのプロジェクトでも入手できます。



波を見てください:









驚いたことに、目の前には再びほぼ長方形の衝動があります! 正弦波があったはずですが。 どこかで間違えられました...「でも、振動の振幅を小さくしたらどうなるの?」と思いました。 パスカルのプログラムのパラメーターを変更し、2047年から1500年に「波高」を「0」から「制限」に表示しました。しかし、これは何にもつながりませんでした。 そして、プログラムメニューをさらに詳しく見て、見ました。







-1ボルトから1ボルトまで! つまり、振幅は2ボルトです! そして、3 +のアンプがありました! アンプのドキュメントを探すのが面倒だったので、選択することで理想的な振幅は70 * 2であることがわかりました。



 Program Sin_wav; Var Real_Data, PR: double; // . samplerate: word; // . wavefrequency: double;// . Loop: word; //. Name: string; // . Begin write(' : '); readln(samplerate); // . write(' : '); readln(wavefrequency); write(' : '); readln(Name); write('const uint16_t ', Name, '[', samplerate, '] = {'); PR:=samplerate/2; // . for Loop:=0 to samplerate-1 do //-1, ..   0. Begin Real_Data := 70*sin(Loop*pi/PR) + 2047; // sine-. //..       -  0  . //  2048-1 ( 0  4095) = 0,  2045 = -2. //2047 -    .  +,  -.  0. write(Round(Real_Data)); if (Loop<>samplerate-1) then write(', '); End; write('};') End.
      
      





これが良い配列です:



 const uint16_t C_4[20] = {2047, 2069, 2088, 2104, 2114, 2117, 2114, 2104, 2088, 2069, 2047, 2025, 2006, 1990, 1980, 1977, 1980, 1990, 2006, 2025};
      
      







今、私たちの信号を見てみましょう(上記のリンクのgithubでも音声録音が可能です)。 最後に正弦波のようなもの!







20パートの正弦波を作成できたので、前述のコードを使用して100パートの正弦波を作成します。 結果の配列は次のとおりです。



  : 100  : 261.63  : C_4 const uint16_t C_4[100] = {2047, 2051, 2056, 2060, 2064, 2069, 2073, 2077, 2081, 2085, 2088, 2092, 2095, 2098, 2101, 2104, 2106, 2108, 2110, 2112, 2114, 2115, 2116, 2116, 2117, 2117, 2117, 2116, 2116, 2115, 2114, 2112, 2110, 2108, 2106, 2104, 2101, 2098, 2095, 2092, 2088, 2085, 2081, 2077, 2073, 2069, 2064, 2060, 2056, 2051, 2047, 2043, 2038, 2034, 2030, 2025, 2021, 2017, 2013, 2009, 2006, 2002, 1999, 1996, 1993, 1990, 1988, 1986, 1984, 1982, 1980, 1979, 1978, 1978, 1977, 1977, 1977, 1978, 1978, 1979, 1980, 1982, 1984, 1986, 1988, 1990, 1993, 1996, 1999, 2002, 2006, 2009, 2013, 2017, 2021, 2025, 2030, 2034, 2038, 2043};
      
      





古いコードを使用して配列を置き換え、これを取得します。







非常に高品質の正弦波が得られました!



githubのプロジェクトコードとオーディオ。



叙情的な余談



振幅が3ボルトを超えるサウンドを録音すると、同じラップトップサウンドカードに接続されたスピーカーに、デバイスからわずかに変更されたサウンドが現れました。 最初は、バックグラウンドプログラムがオンになったためだと思いました。 しかし、振幅を小さくすることが私に来るとすぐに、そうではないことに気付きました。 音がなくなっているので。 もう少し疑って、サウンドカードを焼いたと思います。



音声の録音と分析には、完全に無料のプログラムAudacityを使用しました。 これにより、制限なしに任意の高品質でサウンドを録音でき、任意の形式(FLACを含む)で保存できます。



また、オシロスコープを持っていないことも付け加えておきます。 このプログラムは私に取って代わりました。



結論の代わりに。



次の記事では、DMAモジュールとDACとの結合について説明します。






All Articles