در قسمت دهم از آموزش STM32 با توابع LL، ابتدا ADC را بررسی کردیم و گفتیم که یک ADC چه مشخصهها و پارامترهایی دارد و در نهایت واحد ADC در میکروکنترلرهای STM32 را به صورت عملی راهاندازی کردیم و ولتاژ میکروکنترلر را با استفاده از این واحد قرائت کردیم.
در این قسمت میخواهیم در رابطه با DAC یا همان مبدل دیجیتال به آنالوگ در میکروکنترلرهای STM32 صحبت کنیم و در دو حالت مختلف این واحد را راهاندازی کنیم.
از قبل میدانیم که ADC با ساز و کار خاصی یک ولتاژ آنالوگ را به یک ولتاژ دیجیتال تبدیل میکند. حال کاری که DAC انجام میدهد، دقیقا برعکس کاری است که ADC انجام میداد. یعنی تبدیل یک سیگنال دیجتال به یک سیگنال آنالوگ.
(DAC) digital-to-analog converter
مبدل دیجیتال به آنالوگ، یا همان DAC عنصری است که یک سیگنال دیجیتال را به عنوان ورودی دریافت میکند، و در خروجی معادل آنالوگ همان سیگنال دیجیتال را تولید میکند.
برای ساخت DAC چندین روش مختلف وجود دارد، اما عملکرد همهی این روشها یکسان است. یعنی تبدیل یک سیگنال دیجیتال به یک سیگنال آنالوگ.
R-2R و Binary Weighted دو روش معروف برای ساخت DAC هستند که روش R-2R به نسبت روش بهتر و کارآمدتری است. برای ساخت این دو روش از یک سری اصول پایهای ساده الکترونیکی استفاده شده است که برای درک عملکرد DAC بهتر است نحوهی ساخت این دو روش را در اینترنت بخوانید.
DACها هم مانند ADCها چندین مشخصه مهم و اصلی دارند که در ادامه این مشخصات را بررسی میکنیم.
در ADCها سه مشخصهی رزولوشن، فرکانس نمونهبرداری و مقدار زمانی که طول میکشید تا یک نمونه دیجیتال را در خروجی داشته باشیم، از مشخصات اصلی ADC بودند. این مشخصات را تقریبا با همان تعاریفی که در آنجا گفتیم، در DACها هم داریم. این تعاریف را در ادامه، زمانی که میخواهیم DAC را در میکروکنترلرهای STM32 شرح بدهیم، همره با مقادیرشان بررسی خواهیم کرد.
رابطه بین ولتاژ خروجی، ولتاژ رفرنس، رزولوشن و ورودی دیجیتال DAC به صورت زیر است:
برای بدست آوردن حداقل ولتاژ خروجی تولید شده توسط DAC، باید Digital Number Input را در فرمول بالا برابر با عدد 1 قرار بدهید.
کاربرد DAC
از کاربردهای DAC میتوان به پردازش سیگنال، کارهای صوتی و ویدئویی، رادیو نرمافزاری و … اشاره کرد.
مثلا زمانی که یک سیگنال آنالوگ را به یک سیگنال دیجیتال تبدیل میکنیم تا بر روی آن کارهای پردازشی انجام بدهیم، در نهایت ممکن است که بخواهیم سیگنال پردازش شده را به یک سیگنال آنالوگ تبدیل کنیم، در این مواقع باید از DAC استفاده کنیم و این یکی از کاربردهای DAC است.
DAC در میکروکنترلرهای STM32
ابتدا این نکته را بگویم که در این قسمت به دلیل اینکه میکروکنترلر STM32F103CBT6 فاقد واحد DAC است، پس از نمیتوانیم از برد blue pill که در قسمت چهارم معرفی کردیم استفاده کنیم، و باید از میکروکنترلری استفاده کنیم که دارای واحد DAC باشد. به همین جهت در این قسمت از میکروکنترلر STM32F103RET6 که دارای واحد DAC است، استفاده میکنیم و با استفاده از یک برد مبتنی بر این میکروکنترلر تستهای عملی را انجام خواهیم داد.
در میکروکنترلر STM32F103RET6 دو واحد DAC وجود دارد که هر کدام از این DACها دارای یک کانال خروجی هستند.
ولتاژ رفرنس یا مرجع DAC از پایه +VREF که با ADC مشترک است تامین میشود.
حداکثر sampling rate این DAC برابر با 1Ms/s و حداکثر کلاکی که میتوانیم به واحد DAC اعمال کنیم برابر با 36MHZ که همان حداکثر کلاک باس APB1 است.
واحد DAC دارای رزولوشن 12 بیت است که این 12 بیت میتواند به صورت right-aligned یا left-aligned درون رجیستر مربوطه قرار بگیرد. البته این DAC در حالت 8 بیتی هم میتواند پیکرهبندی بشود که این 8 بیت تنها میتواند به صورت right-aligned درون رجیستر مربوطه قرار بگیرد.
چینش right-aligned و left-aligned درون رجیسترها به صورت زیر است:
چون دیتا نمیتواند به صورت مستقیم در رجیستر خروجی نوشته شود، ابتدا باید به صورت right-aligned یا left-aligned درون رجیستر DHR نوشته بشود و سپس به صورت خودکار و یا با استفاده از تریگر که میتواند نرمافزاری یا سختافزاری باشد، درون رجیستر DOR قرار بگیرد.
سومین پارامتر مهم DAC هم مقدار زمانی است که طول میکشد تا معادل آنالوگ دیتای دیجیتالی که درون رجیستر DOR قرار دارد، بر روی پین میکروکنترلر مسقر شود. این زمان در دیتاشیت با نام زمان SETTLING آورده شده است که معادل فارسی آن زمان استقرار است.
این DAC علاوه بر تولید سیگنال دلخواه ما، قابلیت تولید خودکار سیگنال نویز و سیگنال مثلثی را هم دارد.
در ادامه هر کدام از این روشها را مفصلا شرح خواهم داد.
انواع سیگنالهای تولید شده توسط DAC در میکروکنترلرهای STM32
سیگنال دلخواه
فرض کنید میخواهیم با استفاده از DAC یک سیگنال متناوب دلخواه را تولید کنیم. برای این کار ابتدا نمونههای دیجیتال یک دوره تناوب را درون یک آرایه قرار داده و سپس عضوهای این آرایه را به ترتیب درون رجیستر DHR قرار میدهیم. پس از اینکه هر عضو آرایه را درون رجیستر DHR قرار دادیم، باید عملیاتی صورت بگیرد تا محتوای رجیستر DHR به رجیستر DOR منتقل و در نهایت بر روی پین خروجی مسقر بشود.
برای اینکه محتوای رجیستر DHR به رجیستر DOR منتقل بشود، بستگی به فعال بودن تریگر دارد. اگر تریگر فعال نباشد این کار به صورت خودکار مانند شکل زیر انجام خواهد شد.
همانطور که در شکل بالا مشاهده میکنید محتوای رجیستر DHR، پس از یک سیکل کلاک APB1 به رجیستر DOR منتقل میشود.
اما اگر تریگر فعال باشد دو حالت مختلف پیش میآید.
حالت اول این است که تریگر را بر روی حالت نرمافزاری تنظیم کرده باشیم. در این صورت هنگامی که ما به صورت نرمافزاری تریگر را فعال میکنیم، پس از یک سیکل کلاک APB1، محتوای رجیستر DHR به رجیستر DOR منتقل خواهد شد. در این حالت هم مانند زمانی که تریگر را کلا فعال نکرده باشیم، انتقال دیتا پس از یک سیکل کلاک APB1 انجام خواهد شد، اما با این تفاوت که برای انتقال دیتا باید تریگر به صورت نرمافزاری رخ بدهد.
در این حالت پس از اینکه ما تریگر را بر روی حالت نرمافزاری قرار دادیم، پس از قرار دادن محتوا درون رجیستر DHR باید یک بیت را در رجیستر SWTRIGR فعال بکنیم تا محتوای رجیستر DHR به رجیستر DOR منتقل بشود. پس از اینکه محتوای مربوطه منتقل شد، بیت SWTRIGR به صورت خودکار توسط سختافزار غیرفعال یا ریست خواهد شد.
برای درک بهتر عملکرد این حالت به شکل زیر توجه کنید:
حالت دوم هم این است که تریگر را بر روی حالت سختافزاری تنظیم کرده باشیم. تریگرهای سختافزاری شامل تایمرها و یک وقفه خارجی هستند. در این صورت هنگامی که با استفاده از تایمر یا وقفه خارجی، تریگر رخ بدهد، پس از سه سیکل کلاک APB1، محتوای رجیستر DHR به رجیستر DOR منتقل خواهد شد.
در شکل زیر منابع مختلف تریگر، که شامل تایمرها، یک وقفه خارجی و یک حالت نرمافزاری است را مشاهده میکنید:
برای تنظیم تریگر در هر کدام از حالتهای بالا، پس از فعال کردن بیت TEN در رجیستر CR، باید بیتهای [2:0]TSEL در رجیستر CR را مطابق حالت دلخواه تنظیم کنیم.
سیگنال نویز
دومین نوع سیگنالی که DAC قادر است برای ما تولید کند، سیگنال نویز است. واحد DAC با توجه به یک الگوریتم سختافزاری قادر است که یک سیگنال نویز را در خروجی خود برای ما تولید کند.
شکل زیر الگوریتمی است که باعث ایجاد سیگنال نویز میشود:
همچنین این سیگنال نویز در 12 حالت مختلف میتواند تولید بشود. این 12 حالت با توجه به تنظیماتی است که ما از قبل تعیین میکنیم. در این تنظیمات ما میتوانیم هر کدام از 12 بیت موجود را ماسک کنیم.
محتوای الگوریتم بالا در رجیستر LFSR ذخیره میشود که با رخ دادن تریگر با رجیستر HDR جمع میشود و به رجیستر ODR منتقل خواهد شد.
توجه کنید که اگر حاصلجمع دو رجیستر LFSR و HDR بیش از 12 بیت باشد، این مورد به صورت خودکار توسط سختافزار کنترل خواهد شد و سرریز رخ نمیدهد.
خب پس از اینکه تعیین کردیم الگوریتم بالا با چه پارامترهایی سیگنال نویز را تولید کند، کافی است تا تریگر آن به صورت نرمافزاری یا سختافزاری فعال شود تا دیتا به رجیستر ODR منتقل و در نهایت سیگنال نویز در پین خروجی تولید شود.
در حالت سیگنال نویز، چون به صورت خودکار محتوای رجیستر LFSR نمیتواند به رجیستر ODR منتقل بشود، پس حتما باید تریگر را بر روی یکی از حالتهایی که گفتیم تنظیم کنیم، یعنی باید بیت TEN در رجیستر CR فعال باشد.
سیگنال مثلثی
سومین نوع سیگنالی که DAC قادر است برای ما تولید کند، سیگنال مثلثی است. واحد DAC قادر است تا یک سیگنال موج مثلثی با دامنه کم را به یک سینال DC، یا یک سیگنال با تغییرات کم اضافه کند.
دامنه این سیگنال مثلثی هم مانند سیگنال نویز در 12 حالت مختلف قابل تنظیم است.
در حالت سیگنال مثلثی هم مانند توضیحاتی که در حالت سیگنال نویز دادیم، محتوا به صورت خودکار نمیتواند به رجیستر ODR منتقل بشود و تریگر حتما باید به صورت نرمافزاری یا سختافزاری فعال شود.
در حالت سیگنال مثلثی یک کانتر یا شمارنده وجود دارد که با هر بار رخ دادن تریگر، یک واحد به آن اضافه میشود و این اضافه شدن تا قبل از این که به ماکسیمم دامنهای که تعیین کردیم برسیم، ادامه خواهد داشت. پس از اینکه کانتر به ماکسیمم دامنه تعیین شده توسط ما رسید، روند نزولی پیدا خواهد کرد تا دوباره به صفر برسد.
محتوای کانتر در این حالت هم با رجیستر HDR جمع میشود و با هر بار تریگر به رجیستر ODR منتقل میشود بدون اینکه سرریزی رخ بدهد.
موج مثلثی که توسط DAC تولید میشود مانند شکل زیر است:
البته این شکل موج یک مرحله قبل از این است که بر روی پین میکروکنترلر مستقر شود. اگر رجیستر HDR برابر با 0 باشد دقیقا همین شکل موج، و در غیر این صورت، این شکل موج با رجیستر HDR جمع میشود و بر روی پین میکروکنترلر مستقر خواهد شد.
برای تعیین اینکه کدام یک از سه سیگنالی که در بالا ذکر کردیم فعال باشد، باید در رجیستر CR، بیتهای [1:0]WAVE را متناسب با حالتی که میخواهیم تنظیم کنیم.
همچنین برای ماسک کردن بیتهای الگوریتم سیگنال نویز و یا تعیین دامنه سیگنال موج مثلثی باید بیتهای MAMP2[3:0] را مانند زیر تنظیم کنیم:
- 0000: بیت 0 از LFSR بدون ماسک / دامنه موج مثلثی برابر با 1
- 0001: بیتهای [1:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 3
- 0010: بیتهای [2:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 7
- 0011: بیتهای [3:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 15
- 0100: بیتهای [4:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 31
- 0101: بیتهای [5:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 63
- 0110: بیتهای [6:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 127
- 0111: بیتهای [7:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 255
- 1000: بیتهای [8:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 511
- 1001: بیتهای [9:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 1023
- 1010: بیتهای [10:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 2047
- ≥1011: بیتهای [11:0] از LFSR بدون ماسک / دامنه موج مثلثی برابر با 4095
توجه کنید که این بیتها حتما باید قبل از فعال کردن DAC تنظیم بشوند تا موثر باشند.
یک نکته مهم دیگر هم مانده است، این نکته را بگویم و سایر مراحل را در نرمافزار شرح خواهم داد.
در واحد DAC این قابلیت وجود دارد که خروجی آنالوگ را قبل از اینکه بر روی پین میکروکنترلر مستقر بشود، بافر بکنیم. بافر کردن باعث کاهش امپدانس خروجی میشود و ما میتوانیم بار را به صورت مستقیم به پین میکروکنترلر متصل کنیم.
بافر خروجی در بیت BOFF رجیستر CR فعال میشود. توجه کنید که این بافر با نوشتن 0 در این بیت فعال و با نوشتن 1 غیر فعال خوهد شد.
برای تاثیر بافر بر ولتاژ خروجی ابتدا به شکل زیر توجه کنید:
همانطور که از قسمت بالا این تصویر قابل مشاهده است، اگر خروجی را بافر نکرده و آن را به بار متصل کنیم، ولتاژ در مقایسه با زمانی که خروجی به بار متصل نیست، به شدت افت خواهد کرد.
اما در قسمت پایین تصویر، وقتی خروجی را بافر میکنیم، حتی زمانی که خروجی به بار متصل است، ولتاژ در مقایسه با زمانی که خروجی به بار متصل نیست، بدون تغییر باقی میماند.
پس توصیه میکنیم که حتما بافر خروجی را در برنامه فعال کنید.
اکنون میخواهیم با استفاده از DAC یک سیگنال پالس و یک سیگنال مثلثی ایجاد کنیم.
نرمافزار STM32CubeMX را باز کرده و کلاک را مانند گذشته تنظیم میکنیم. در قسمت Analog، مانند تصویر زیر، DAC را برای سیگنال پالس تنظیم میکنیم:
در این حالت، یعنی حالت سیگنال پالس، ابتدا یکی از کانالهای خروجی DAC را فعال کرده و در تنظیمات کانال هم بافر خروجی را فعال میکنیم و تریگر را بر روی None قرار میدهیم.
همانطور که گفتیم در حالت سیگنال دلخواه، تریگر هم میتواند به صورت خودکار باشد و هم توسط یکی از منابع تریگر که قبلا گفتیم. وقتی تریگر را بر روی None قرار میدهیم، انتقال دیتا به صورت خودکار انجام میشود و نیازی به تریگ شدن توسط منابع تریگر نیست.
اما در حالت سیگنال نویز و سیگنال مثلثی، عمل تریگ حتما باید با استفاده از منابع تریگر رخ بدهد. برای همین وقتی تریگر را بر روی None قرار میدهیم، تنظیمات مربوطه برای ما نمایش داده نمیشود.
پس در اینجا ما تریگر را بر روی None قرار میدهیم تا انتقال دیتا به صورت خودکار انجام بشود.
پس از تنظیمات بالا، کد را برای نرمافزار Keil ایجاد میکنیم.
در نرمافزار Keil ابتدا باید در main برنامه DAC را توسط تابع LL_DAC_Enable فعال بکنیم:
LL_DAC_Enable(DAC1, LL_DAC_CHANNEL_1);
سپس در while برنامه کد زیر را مینویسیم:
LL_DAC_ConvertData12RightAligned(DAC1, LL_DAC_CHANNEL_1, 0xFFF); LL_mDelay(1); LL_DAC_ConvertData12RightAligned(DAC1, LL_DAC_CHANNEL_1, 0x000); LL_mDelay(1);
در کد بالا با استفاده از تابع LL_DAC_ConvertData12RightAligned مقدار 0xFFF که بیشترین مقدار، و مقدار 0x000 که کمترین مقدار است را در حالت 12-bit right aligned درون رجیستر DHR قرار میدهیم تا یک پالس ایجاد بشود. چون تریگر غیر فعال است نیاز به هیچ کار اضافهی دیگری نیست و دیتا به صورت خودکار از رجیستر DHR به درون رجیستر ODR منتقل میشود.
همچنین ما از تابع LL_mDelay که برای ایجاد تاخیر است هم استفاده کردهایم. ما در اینجا از تاخیر 1 میلی ثانیه استفاده کردیم اما شما برای تغییر فرکانس و دیوتی سایکل پالس خودتان، میتوانید این مقدار را تغییر بدهید.
کد را کامپایل و پروگرام میکنیم. پس از اجرای کد بر روی میکروکنترلر، نتیجهی زیر بر روی اسیلسکوپ قابل مشاهده است:
اکنون میخواهیم یک سیگنال مثلثی را با استفاده از DAC ایجاد کنیم.
ابتدا باید در نرمافزار STM32CubeMX کانال خروجی را مانند زیر تنظیم کنیم:
در این حالت مانند قبل کانال و بافر خروجی را فعال میکنیم. اما این بار تریگر را بر روی Software trigger قرار میدهیم. با فعال کردن تریگر مشاهده خواهید کرد که تنظیمات مربوط به سیگنال نویز و سیگنال مثلثی هم فعال خواهند شد.
در ادامه سیگنال مثلثی را انتخاب و ماکسیمم دامنه آن را عدد 4095 تعیین خواهیم کرد و در نهایت کد را برای نرمافزار Keil ایجاد میکنیم.
main برنامه شبیه قبل است، اما در while برنامه باید کد زیر را بنویسیم:
LL_DAC_TrigSWConversion(DAC1, LL_DAC_CHANNEL_1);
با هر بار اجرای تابع LL_DAC_TrigSWConversion به صورت نرمافزاری تریگر رخ میدهد و مجموع محتوای کانتر و رجیستر HDR، به رجیستر ODR منتقل میشود.
کد را کامپایل و پروگرام میکنیم. پس از اجرای کد بر روی میکروکنترلر، نتیجهی زیر بر روی اسیلسکوپ قابل مشاهده است:
در قسمت دوازدهم در رابطه با تایمرها صحبت خواهیم کرد.
منبع:سیسوگ