آموزش STM32 با توابع LL قسمت سی‌ و دوم: راه‌اندازی یک wave player

آموزش STM32 با توابع LL قسمت سی‌ و دوم: راه‌اندازی یک wave player

آموزش STM32 با توابع LL قسمت سی‌ و دوم: راه‌اندازی یک wave player
آموزش STM32 با توابع LL قسمت سی‌ و دوم: راه‌اندازی یک wave player

در قسمت قبلی راه‌اندازی و استفاده از کارت‌ حافظه SD را آموختیم و در این قسمت می خواهیم راه‌اندازی یک wave player را به شما همراهان سیسوگ آموزش دهیم.

همان‌طور که می‌دانید فرمت wave (فایل‌های با پسوند.wav) یکی از فرمت‌های رایج برای فایل‌های صوتی است. در این بخش از آموزش STM32 قصد داریم یک wave player با استفاده از قابلیت‌های این میکرو بسازیم. با سیسوگ همراه باشید.

 

 

قبل از این‌که وارد روند توسعه پروژه wave player شویم، فرمت wav را با جزییات بیشتری بررسی می‌کنیم. این فرمت از ساختار فرمت فایل RIFF (مخفف Resource Interchange File Format) استفاده می‌کند. همچنین معمولاً از این فرمت برای ذخیره اطلاعات صوتی بدون فشرده‌سازی و اتلاف استفاده می‌شود. هرچند امکان ذخیره اطلاعات به‌صورت فشرده نیز در فرمت wav وجود دارد.

 

فرمت wav

هر فایل wav عموماً از سه بخش تشکیل می‌شود. قسمت RIFF، قسمت Format و قسمت Data در حالتی که فایل wav به‌صورت فشرده ساخته‌شده باشد، شامل بلاک‌های fact نیز می‌شود. ساختار معمول فرمت wav در شکل زیر قابل‌مشاهده است:

هرکدام از بخش‌های هدر یک فایل wav، اطلاعاتی راجع به‌ویژگی‌های فایل می‌دهد.  بخش Chuck ID شامل کد اسکی مربوط به حروف “RIFF” است. Chunksize اندازه فایل را براساس تعداد بایت نشان می‌دهد(اندازه فایل منهای 8 بایتی که تا اینجای هدر برای ذخیره این دو بخش به‌کاررفته است). Format کد اسکی مربوط به حروف “WAVE” را شامل می‌شود. در بلوک fmt، جزییات مربوط به فرمت فایل قرار دارد. همان‌طور که احتمالاً انتظار دارید فیلد Subchunk1ID شامل کاراکترهای ” fmt” می‌شود. فیلد بعدی یعنی Subchunk1Size اندازه ادامه این بلوک را مشخص می‌کنم که درصورتی‌که فایل در قالب PCM باشد، عدد 16 خواهد بود. فیلد AudioFormat نیز مشخص می‌کند که فایل PCM است یا اینکه نوعی فشرده‌سازی در آن به‌کاررفته است. NumChannels نیز همان‌طور که اسم آن مشخص است، تعداد کانال‌ها را مشخص می‌کند که برای Mono، ‏1 و برای Stereo، ‏2 خواهد بود. SampleRate سرعت پخش نمونه‌ها را مشخص می‌کند و ByteRate سرعت پخش براساس بایت را بیان می‌کند. حوزه بعدی BlockAlign است که برابر است با تعداد کانال‌ها ضربدر تعداد بیت‌های هر نمونه تقسیم‌بر 8. فیلد BitsPerSample که در حالت PCM آخرین فیلد این بلاک است نیز تعداد بیت‌های هر نمونه را مشخص می‌کند. بلوک بعدی بلوک داده است که شامل سه فیلد می‌شود. فیلد اول که Subchunk2ID است کد اسکی حروف “data” را در خود نگه می‌دارد. فیلد Subchunk2Size تعداد بایت‌های داده‌های صوتی را مشخص می‌کند و بالاخره آخرین فیلد خود داده صوتی است.  توضیحات کامل مربوط به بخش‌های مختلف هدر در جدول زیر آورده شده است (برای توضیح بیشتر به این لینک مراجعه کنید).

بنابراین برای خواندن صحیح یک فایل wav و طراحی wave player، باید بتوانیم این اطلاعات را به‌درستی از هدر فایل استخراج‌کنیم و براساس آن، پارامترهایی مثل اندازه فایل، سرعت پخش، اندازه هر نمونه را تنظیم کنیم. یک مثال از مشخصات فایل wav مربوط به یک موج سینوسی (تک فرکانس) و نمایش هگز اطلاعات آن را در تصاویر زیر می‌بینیم:

 

 

 

همان‌طور که در تصویر بالا مشخص است، چهار بایت اول کد اسکی “RIFF” را مشخص می‌کند و به همین ترتیب اطلاعات مربوط به هدر قرارگرفته‌اند، تا جایی که به بلوک داده می‌رسیم. در این بلوک پس از قسمت‌های ID (که کد اسکی data است) و Subchunk2Size، داده‌های مربوط به فایل صوتی قرارگرفته‌اند. نمونه‌های این فایل صوتی 16 بیتی و دو کاناله (Stereo) هستند. یعنی اینکه هر نمونه 2 بایت فضا اشغال کرده است و داده‌های کانال چپ و کانال راست به‌صورت یکی در میان قرارگرفته‌اند.

حال که با ساختار فایل wav آشنا شده‌ایم، به سراغ طراحی wave player می‌رویم.

 

ایجاد پروژه

در این پروژه از میکروکنترلر STM32F103RET6 استفاده می‌کنیم. برای ایجاد پروژه wave player، مانند بخش قبلی، یعنی پروژه SD Card، بااینکه نیاز به واحد SPI داریم اما فعلاً آن را فعال نمی‌کنیم و توابع مربوط به این واحد و فایل سیستم را بعد به پروژه اضافه می‌کنیم. پس از فعال و تنظیم کردن بخش‌های دیباگ، کلاک و USART1، واحد DAC را نیز برای تولید خروجی فعال می‌کنیم و کانال DMA مربوط به خروجی 1 آن را در حالت Circular تنظیم می‌کنیم؛

 

 

همان‌طور که در تصویر مربوط به تنظیم DAC مشخص است، ترگیر این واحد را به‌وسیله تایمر 2 تنظیم کرده‌ایم. درواقع سرعت پخش نمونه‌ها توسط DAC به‌وسیله تریگر تایمر 2 تنظیم می‌شود. پس فرکانس پخش را باید با فرکانس تایمر 2 تنظیم کنیم.

اکنون به سراغ تنظیم تایمر 2 می‌رویم؛

 

تنها بخش‌های مهم در تنظیم تایمر، انتخاب منبع کلاک (Internal Clock) و Trigger Event Selection هستند. بقیه بخش‌ها را بعداً در کد برنامه تنظیم می‌کنیم.

اکنون کلاک میکرو را روی 72MHz تنظیم می‌کنیم و توابع LL را برای بخش‌های فعال‌شده انتخاب می‌کنیم و سپس پروژه را ایجاد می‌کنیم.

 

نوشتن کد پروژه

در این پروژه، مانند پروژه SD Card، فایل‌های کتابخانه‌ای fatfs و mmc_stm32f1_spi.c و دیگر فایل‌های مورد نیاز آن‌ها که در آن پروژه استفاده کردیم را به مسیر پروژه wave player اضافه می‌کنیم. برای منظم شدن پروژه و جلوگیری از شلوغ شدن فایل اصلی برنامه، دو فایل جدید WAV_Handler.c و WAV_Handler.h را به ترتیب در قسمت‌های src و inc پروژه ایجاد می‌کنیم.

قبل از نوشتن فایل‌های جدید، به سراغ فایل mmc_stm32f1_spi.c می‌رویم تا واحد spi مورد استفاده را از spi1 به spi2 تغییر دهیم. زیرا در این پروژه برای فایل‌های stereo به خروجی دوم DAC نیاز پیدا می‌کنیم که با پین‌های مورد استفاده توسط spi1 تداخل دارد؛

در فایل WAV_Handler.h کتابخوانه‌ها و ثابت‌های مورد نیاز را تعریف می‌کنیم، همچنین تایپ استراکچر مربوط به اطلاعات هدر را در این فایل تعریف می‌کنیم؛

دلیل تعریف استراکچر به‌صورت بالا، این است که ممکن است میان این بخش از اطلاعات فاصله وجود داشته باشد و بین آن‌ها اطلاعات دیگری قرار بگیرد. با این تعریف مطمئن می‌شویم که اطلاعات به‌درستی خوانده می‌شوند. همچنین در بین فایل‌های اضافه‌شده، SD_Utility.h مربوط به توابع تعریف‌شده در پروژه قبل هستند که محتویات آن را در ادامه بررسی می‌کنیم.

در آخر، تابع‌هایی که قرار است در فایل WAV_Handler.c تعریف کنیم را اعلان می‌کنیم.

محتویات فایل‌های SD_Utility.h و SD_Utility.c را به ترتیب به‌صورت زیر تعریف می‌کنیم:

حالا باید به سراغ فایل WAV_Handler.c برویم و توابع اعلان‌شده برای خواندن و پخش فایل wav را در اینجا تعریف کنیم. قبل از هر چیز WAV_Handler.h را به این فایل اضافه می‌کنیم و متغیرهای گلوبال موردنیاز را تعریف می‌کنیم؛

 اکنون به سراغ توابع می‌رویم. تابع اول، برای فعال سازی SD Card و واحد DAC است؛

 تابع بعدی را به منظور باز کردن فایل و برگرداندن خطا در صورت بروز مشکل تعریف می‌کنیم؛

 حالا باید مهم‌ترین تابع این پروژه، یعنی خواندن هدر فایل و برداشتن اطلاعات موردنیاز جهت تنظیم بخش‌های مختلف را بنویسیم؛

همان‌طور که می‌بینید، بخش دوم Structure، یعنی بخش Data، بعد از خواندن بخش اول و با توجه به مقدار Subchunk1size پرشده است. زیرا همان‌طور که پیش‌تر اشاره شد ممکن است میان این دو بخش فاصله وجود داشته باشد. اکنون با توجه به مقدار Subchunk2Size می‌دانیم که چند بایت را باید برای پخش کامل فایل صوتی بخوانیم. همچنین با توجه به تعداد کانال‌ها Mono یا Stereo بودن تنظیم می‌شود و سرعت پخش را از SampleRate می‌فهمیم.

بنابراین اکنون باید به سراغ تنظیم واحدهای DMA و Timer با توجه به مقدارهای خوانده‌شده برویم؛

   در ادامه تابع جداگانه‌ای برای فعال‌سازی درخواست DAC از DMA و صدا زدن دو تابع قبلی تعریف می‌کنیم؛

  یک تابع نیز برای شروع به پخش، یعنی فعال کردن شمارنده تایمر می‌نویسیم؛

  حالا باید تابعی تعریف کنیم که از تابع‌های تنظیم و شروع به خواندن که نوشتیم، استفاده کند، اسم فایل مورد نظر را دریافت کند و آن را پخش کند؛

تابعی که در حلقه while(1) به‌کاررفته است را هنوز تعریف نکرده‌ایم. کاربرد این تابع، به‌روزرسانی بافر DMA از اطلاعات فایل صوتی موجود در SD Card و همچنین فعال کردن پرچم انتهای فایل است. این تابع را به‌صورت زیر می‌نویسیم؛

تقریبا به انتهای پروژه رسیده‌ایم. فقط باید تابع‌های موردنیاز برای تغییر متغیر BufferAction که عملیات موردنیاز برای تغییر بافر را مشخص می‌کند بنویسیم و در وقفه‌های مناسب آن‌ها را صدا بزنیم. ابتدا تابع‌ها را می‌نویسیم؛

 حالا باید از فایل stm32f1xx_it.c تابع مربوط به روال وقفه DMA را به فایل WAV_Handler.c منتقل کنیم و تابع‌های بالا در آن صدا بزنیم؛

اکنون فایل‌های کتابخانه ما تکمیل‌شده‌اند. تنها کاری که برای استفاده از wave player نیاز است، قرار دادن یک فایل با فرمت wav در کارت حافظه، اتصال آن به میکرو و صدازدن توابع wave_init و wave_play، در فایل main.c است. این توابع را در int main فراخوانی می‌کنیم؛

 فراموش نشود که باید WAV_Handler.h را نیز به این فایل اضافه کنیم؛

در صورتی که همه کارها به درستی انجام شده باشند، می‌توانیم فایل صوتی را به وسیله یک بلندگو با مقاومت حدود 25ohm پخش کنیم. یا اینکه شکل موج فایل در حال پخش را روی یک اسیلوسکوپ مشاهده کنیم. اگر مقدار Debug در فایل هدر را 1 قرار داده باشیم، مشخصات فایلی که برای پخش قرار دادیم، در ترمینال سریال چاپ می‌شود:

 

 

    لینک فایل مربوط به این پروژه در گیت‌هاب

 

 

1 نفر

پــــســنــدیـده انـد

توجه

سیاوش
سیاوش

کارشناس ارشد الکترونیک دیجیتال، علاقه‌مند به یادگیری

دیدگاه ها

0 دیدگاه

پر بحث ترین ها

مسابقه دوم : چالش برنامه نویسی به زبان C

مسابقه دوم : چالش برنامه نویسی به زبان C

مسابقه اول سیسوگ (مسابقه اول: درک سخت افزار) انتقادهای زیادی رو در پی داشت تا جایی که حتی خودمم به نتیجه مسابقه...

Zeus ‌ Zeus ‌
  • 3 سال پیش
راه اندازی LCD گرافیکی Nokia 1661

راه اندازی LCD گرافیکی Nokia 1661

LCD گرافیکی یکی از مهم ترین پارامترهای موجود در طراحی انواع مدارات الکترونیکی پیچیده و حتی ساده است ، نمایش وضعیت و...

Zeus ‌ Zeus ‌
  • 4 سال پیش
ریموت کدلرن و چکونگی دکد کردن آن به همراه سورس برنامه

ریموت کدلرن و چکونگی دکد کردن آن به همراه سورس برنامه

ریموت کنترل امروزه کاربرد زیادی پیدا کرده است؛ از ریموت‌های درب بازکن تا ریموت‌های دزدگیر و کنترل روشنایی همه از یک اصول اولیه پیروی می‌کنند و آن‌هم ارسال اطلاعات به‌صورت بی‌سیم است....

Zeus ‌ Zeus ‌
  • 5 سال پیش
همه چیز درباره ریموت کنترل‌های هاپینگ

همه چیز درباره ریموت کنترل‌های هاپینگ

امنیت همیشه و در همه‌ی اعصار، مقوله‌ی مهم و قابل‌توجه‌ ای بوده و همیشه نوع بشر به دنبال امنیت بیشتر، دست به ابداعات و اختراعات گوناگونی زده است. ریموت کنترل یکی از این اختراعات است. در این مقاله، به بررسی امنیت انواع ریموت‌های کنترل خواهیم پرداخت....

Zeus ‌ Zeus ‌
  • 5 سال پیش
مسابقه سوم: استخراج داده از رشته ها در زبان C

مسابقه سوم: استخراج داده از رشته ها در زبان C

نزدیک به 5 ماه از مسابقه دوم سیسوگ می‌گذره و فکر کردم که بد نیست یک چالش جدید داشته باشیم! البته چالش‌ها...

Zeus ‌ Zeus ‌
  • 2 سال پیش
مسابقه ششم: بزن میکروکنترلر را بسوزون!

مسابقه ششم: بزن میکروکنترلر را بسوزون!

بزنم میکروکنترلر را بسوزونم اونم تو  این شرایط!، طراحی مسابقه از اون چیزی که به نظر می‌رسه سخت‌تر است، باید حواست باشه...

Zeus ‌ Zeus ‌
  • 12 ماه پیش
آموزش قدم به قدم راه اندازی NRF24L01

آموزش قدم به قدم راه اندازی NRF24L01

آموزش قدم به قدم راه اندازی +NRF24L01  با کتابخانه سازگار با انواع میکروکنترلرها و کامپایلرها قبل از اینکه قسمت بشه با ماژول...

رسول خواجوی بجستانی رسول خواجوی بجستانی
  • 3 سال پیش
ساخت ماینر با FPGA و ARM

ساخت ماینر با FPGA و ARM

چند ماهی هست که تب بیت کوین و ارزهای دیجیتال خیلی بالا رفته! چه شد که این پست را نوشتم همانطور که...

Zeus ‌ Zeus ‌
  • 3 سال پیش
کار با ماژول تمام عیار mc60 – قسمت دوم – راه اندازی OpenCPU

کار با ماژول تمام عیار mc60 – قسمت دوم – راه اندازی OpenCPU

در قسمت اول به یکسری اطلاعات کلی ماژول mc60 پرداختیم، با نرم افزار QNavigator کار کردیم و یک هدربرد هم برای کار...

Mahdi.h   Mahdi.h  
  • 3 سال پیش
مسابقه چهارم: کدام حلقه سریع‌تر است؟

مسابقه چهارم: کدام حلقه سریع‌تر است؟

حدود ۷ ماه پیش، مسابقه سوم سیسوگ رو برگزار کردیم و کلی نکته در مورد خواندن رشته‌های ورودی را بررسی کردیم. فکر...

Zeus ‌ Zeus ‌
  • 2 سال پیش
سیـــســـوگ

مرجع متن باز آموزش الکترونیک