ARM, STM32, آموزش, آموزش STM32 با توابع LL, توصیه شده, مقاله های سیسوگ

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

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

در قسمت قبلی راه‌اندازی و استفاده از کارت‌ حافظه 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 تعداد بایت‌های داده‌های صوتی را مشخص می‌کند و بالاخره آخرین فیلد خود داده صوتی است.  توضیحات کامل مربوط به بخش‌های مختلف هدر در جدول زیر آورده شده است (برای توضیح بیشتر به این لینک مراجعه کنید).

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

بنابراین برای خواندن صحیح یک فایل 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 می‌رویم؛

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

تنها بخش‌های مهم در تنظیم تایمر، انتخاب منبع کلاک (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 قرار داده باشیم، مشخصات فایلی که برای پخش قرار دادیم، در ترمینال سریال چاپ می‌شود:

 

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

 

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

 

 

انتشار مطالب با ذکر نام و آدرس وب سایت سیسوگ، بلامانع است.

شما نیز میتوانید یکی از نویسندگان سیسوگ باشید.   همکاری با سیسوگ

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

  1. Avatar for حمید حمید گفت:

    سلام
    از آموزش خوبتون ممنونم
    امکانش هست آموزش و راهنمای نحوه پخش فایل صوتی با فرمت WAV توسط DMA رو هم بزارین
    چون اکثر میکرو های سطح پایین و حتی متوسط بازار هم DAC ندارن…

    ممنونم

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *