بررسی
اتصال قطعات آنالوگ به میکروکنترلرها بسیار رایج است. در عصر دیجیتال، هنوز دستگاههای زیادی وجود دارند که سیگنالهای آنالوگ تولید میکنند: سنسورها، پتانسیومترها، مبدلها و لوازم جانبی صوتی ، تنها نمونههایی از دستگاههای آنالوگ هستند که ولتاژ متغیری را در یک بازه زمانی ثابت تولید میکنند. با خواندن این ولتاژ، می توانیم آن را به عددی مفید برای پردازش توسط سیستم عامل تبدیل کنیم. به عنوان مثال، TMP36 سنسور دمایی است که متناسب با دمای محیط ، ولتاژ متغیری تولید میکند.
همه میکروکنترلرهای STM32 حداقل یک مبدل آنالوگ به دیجیتال (ADC) ارائه میکنند، یک واحد جانبی که میتواند چندین ولتاژ ورودی را از طریق I/O های اختصاصی دریافت و آنها را به عدد تبدیل کند. ولتاژ ورودی با یک ولتاژ مشخص و ثابت که ولتاژ مرجع نام دارد ، مقایسه می شود. ولتاژ مرجع می تواند از VDDA گرفته شود یا در میکروکنترلرهایی که تعداد بالایی پین دارند، توسط یک ولتاژ مرجع خارجی تامین شود (این میکروکنترلر ها یک پایه اختصاصی به نام VREF+ دارند). اکثر میکروکنترلر های STM32 دارای یک ADC 12 بیتی هستند. (برخی از نمونه های سری STM32F3 دارای ADC 16 بیتی هستند.)
بر خلاف سایر واحد های جانبی STM32 که تاکنون دیده ایم ،واحد های ADC میتوانند بین سریهای مختلف STM32 و حتی در یک خانواده معین ، تفاوت زیادی داشته باشند. به همین دلیل، تنها به معرفی این واحد جانبی مفید پرداخته و مسئولیت تجزیه و تحلیل عمیق واحد ADC ، در هر میکروکنترلر خاصی که در نظر دارد ، به عهده خواننده می گذاریم.
قبل از اینکه ویژگیهای ارائه شده ADC در میکروکنترلر های STM32 و کتابخانه CubeHAL مربوطه را تجزیه و تحلیل کنیم، بهتر است نگاهی سریع به نحوه عملکرد این واحد جانبی داشته باشیم.
مقدمه ای بر SAR ADC
تقریباً در همه میکروکنترلرهای STM32، ADC به عنوان یک رجیستر تقریب متوالی 12 بیتی successive Approximation Register ADC پیاده سازی می شود. با توجه به پکیج مورد استفاده، می تواند چندین کانال ورودی مالتی پلکس شده (معمولا بیش از ده کانال در اکثر MCU های STM32) داشته باشد ، که امکان اندازه گیری انواع سیگنال ها از منابع خارجی را فراهم می کند. علاوه بر این، برخی از کانال های داخلی نیز در دسترس هستند: یک کانال برای سنسور دمای داخلی (VSENSE)، یک کانال برای ولتاژ مرجع داخلی (VREF INT)، یکی برای نظارت بر منبع تغذیه VBAT خارجی و یک کانال برای نظارت بر ولتاژ LCD آن دسته از میکروهایی که کنترل کننده monochrome passive LCD را ارائه می دهند. (به عنوان مثال STM32L053 یکی از این موارد است).واحدهای ADC در سری STM32F3 و اکثر میکرو های STM32L4 قادر به تبدیل ولتاژ های ورودی به صورت دیفرانسیلی هستند. جدول زیر تعداد دقیق واحدهای جانبی ADC و تعداد کانال ورودی برای برد های Nucleo را نشان داده است:
تنظیمات HAL_ADC Module
پس از معرفی مختصر مهم ترین ویژگی های ارائه شده توسط واحد جانبی ADC در میکروکنترلرهای STM32، زمان مناسبی است که به API های CubeHAL مربوطه بپردازیم.
برای انجام تنظیمات واحد جانبی ADC، HAL ساختار ADC_HandleTypeDef را تعریف می کند که به صورت زیر تعریف می شود:
typedef struct {
ADC_TypeDef *Instance; /* Pointer to ADC descriptor */
ADC_InitTypeDef Init; /* ADC initialization parameters */
__IO uint32_t NbrOfCurrentConversionRank; /* ADC number of current conversion rank */
DMA_HandleTypeDef *DMA_Handle; /* Pointer to the DMA Handler */
HAL_LockTypeDef Lock; /* ADC locking object */
__IO uint32_t State; /* ADC communication state */
__IO uint32_t ErrorCode; /* Error code */
} ADC_HandleTypeDef;
مهم ترین بخش های این ساختار :
- Instance: اشاره گر به توصیف کننده ADC ایست که می خواهیم از آن استفاده کنیم. به عنوان مثال، ADC1 توصیف کننده اولین واحد جانبی ADC است.
- Init: نمونه ای از ساختار ADC_InitTypeDef است که برای پیکربندی ADC استفاده می شود.
- NbrOfCurrentConversionRank: مربوط به کانال i-امین فعلی (rank) در یک گروه تبدیل معمولی است.
- DMA_Handle: اشاره گر به کنترل کننده DMA است که برای انجام تبدیل A/D در حالت DMA پیکربندی شده است. به طور خودکار توسط ماکرو __HAL_LINKDMA() پیکربندی می شود.
تنظیمات ADC با استفاده از نمونه ای از ساختار ADC_InitTypeDef انجام می شود که به صورت زیر تعریف می شود:
typedef struct {
uint32_t ClockPrescaler; /* Selects the ADC clock frequency */
uint32_t Resolution; /* Configures the ADC resolution mode */
uint32_t ScanConvMode; /* The scan sequence direction. */
uint32_t ContinuousConvMode; /* Specifies whether the conversion is performed in Continuous or Single mode */ uint32_t DataAlign; /* Specifies whether the ADC data alignment is left or right */
uint32_t NbrOfConversion; /* Specifies the number of input that will be converted within the regular group sequencer */
uint32_t NbrOfDiscConversion; /* Specifies the number of discontinuous conversions in which the main sequence of regular group */
uint32_t DiscontinuousConvMode; /* Specifies whether the conversion sequence of regular group is performed in Complete-sequence/Discontinuous sequence */
uint32_t ExternalTrigConv; /* Select the external event used to trigger the start of conversion */
uint32_t ExternalTrigConvEdge; /* Select the external trigger edge and enable it */
uint32_t DMAContinuousRequests; /* Specifies whether the DMA requests are performed in one shot or in continuous mode */
uint32_t EOCSelection; /* Specifies what EOC (End Of Conversion) flag is used for conversion polling and interruption*/
} ADC_InitTypeDef;
ClockPrescaler: سرعت کلاک (ADCCLK) را برای بخش مدار آنالوگ ADC تعریف می کند. در بخش قبل دیدیم که ADC دارای یک واحد زمان بندی داخلی است که فرکانس سوئیچینگ کلید ورودی را کنترل می کند (شکل 2 ). ADCCLK سرعت این واحد زمانبندی را تعیین میکند و بر تعداد نمونهها در ثانیه تأثیر میگذارد، زیرا میزان زمان استفاده شده توسط هر چرخه تبدیل را مشخص میکند. این کلاک از کلاک جانبی و توسط یک prescaler قابل برنامه ریزی تولید می شود و به ADC اجازه می دهد تا در فرکانس های fP CLK/2,/4,/6/8 کار کند (برای اطلاع از حداکثر مقادیر ADCCLK و prescaler به دیتاشیت میکروکنترلر مراجعه کنید). در برخی از MCU های STM32، ADCCLK می تواند از نوسانگر HSI نیز نشات گیرد. مقدار این فیلد بر سرعت ADCCLK همه ADC های پیاده سازی شده در MCU تأثیر می گذارد.
- Resolution: به غیر از MCU های STM32F1 که ADC آنها اجازه انتخاب Resolution را نمی دهد ، با استفاده از این فیلد می توان وضوح تبدیل A/D را تعریف کرد. هر چه وضوح بالاتر باشد، تعداد تبدیل کمتری در یک ثانیه امکان پذیر است. اگر سرعت در کاربرد شما مناسب نیست، اکیداً پیشنهاد می شود که Resolution را روی حداکثر و سرعت تبدیل را روی حداقل تنظیم کنید.
ScanConvMode: این فیلد می تواند مقدار ENABLE یا DISABLE را در برگیرد و برای فعال/غیرفعال کردن حالت تبدیل اسکن استفاده می شود.
- ContinuousConvMode: این فیلد مشخص می کند که تبدیل در حالت تک یا پیوسته انجام میشود و می تواند مقدار ENABLE یا DISABLE را در برگیرد.
- NbrOfConversion: تعداد کانال های گروه معمولی که در حالت اسکن تبدیل می شوند را مشخص می کند.
- DataAlign: این فیلد نحوه align داده تبدیل شده را مشخص می کند. رجیستر داده ADC به صورت یک رجیستر half-word پیاده سازی می شود. از آنجایی که فقط 12 بیت برای ذخیره داده تبدیل استفاده می شود، این پارامتر نحوه تراز شدن این بیت ها در داخل رجیستر را مشخص می کند. مقادیر ADC_DATAALIGN_LEFT یا ADC_DATAALIGN_RIGHT میتوانند برای این فیلد استفاده شوند.
- ExternalTrigConvEdge: این فیلد منبع trigger خارجی را انتخاب می کند تا با استفاده از تایمر، تبدیل انجام شود.
- EOCSelection: بسته به حالت تبدیل (تبدیل های تک یا پیوسته)، ADC flag پایان تبدیل (EOC) را بر این اساس تنظیم می کند. این فیلد توسط API های ADC polling یا interrupt برای تعیین زمان تکمیل تبدیل استفاده میشود و میتواند مقادیر ADC_EOC_SEQ_CONV را برای تبدیل continuous و ADC_EOC_SINGLE_CONV برای تبدیلهای single در بر گیرد.
قبل از شروع یک مثال عملی ، باید دو موضوع دیگر را بررسی و تحلیل کنیم: نحوه پیکربندی کانال های ورودی و نحوه نمونه برداری از سیگنال های ورودی آنها.
حالت های تبدیل Conversion Modes
ADC های موجود در میکروکنترلرهای STM32 چندین حالت تبدیل مفید برای سناریوها و کاربردهای مختلف ارائه می دهند. اکنون به طور مختصر مرتبط ترین آنها را معرفی می کنیم: داکیومنت AN31166 شرکت ST همه حالت های تبدیل ارائه شده توسط ADC را توصیف می کند.
حالت تک کانال Single-Channel ، حالت تبدیل تکی Single Conversion
این ساده ترین حالت ADC است. در این حالت، ADC همانگونه که در شکل 5 نشان داده شده است، یک تبدیل تکی (single sample) را از یک کانال انجام می دهد و پس از پایان تبدیل متوقف می شود.
حالت اسکن تبدیل تکی Scan Single Conversion
این حالت که در برخی از اسناد ST به آن حالت (multichannel single mode) نیز می گویند، برای تبدیل برخی کانال ها به صورت متوالی در حالت مستقل استفاده می شود. با استفاده از ranks میتوانید از این حالت ADC تا 16 کانال متوالی را با زمانهای مختلف نمونهبرداری و به ترتیب دلخواه استفاده کنید. به عنوان مثال، می توانید مانند شکل 6 را انجام دهید. به این ترتیب، لازم نیست ADC را در طول فرآیند تبدیل متوقف کنید تا کانال بعدی را با زمان نمونه برداری متفاوت پیکربندی کنید. این حالت بار اضافی را از CPU و توسعه نرم افزار را کاهش می دهد.حالت اسکن تبدیل در حالت DMA انجام می شود.
به عنوان مثال، این حالت را می توان در هنگام راه اندازی یک سیستم که به برخی پارامترها مانند دانستن مختصات نوک دست در سیستم کنترل بازو بستگی دارد ، استفاده کرد. در این حالت، برای تعیین مختصات نوک دست ، باید موقعیت هر مفصل را در سیستم کنترل بازو هنگام روشن شدن بخوانید. این حالت همچنین میتواند برای اندازهگیری چندین سطوح سیگنال (ولتاژ، فشار، دما و غیره) برای تصمیمگیری در مورد راهاندازی یا عدم راهاندازی سیستم به منظور محافظت از افراد و تجهیزات استفاده شود.
حالت تک کانال Single-Channel ، حالت تبدیل مداوم Continuous
این حالت یک کانال را به طور مداوم و نامحدود تبدیل می کند. ویژگی حالت پیوسته به ADC اجازه می دهد تا در background کار کند. ADC کانال ها را به طور مداوم و بدون دخالت CPU تبدیل می کند. علاوه بر این، DMA را می توان در حالت circular استفاده کرد، درنتیجه بار CPU کاهش می یابد.
به عنوان مثال، این حالت ADC را می توان برای نظارت بر ولتاژ باتری، اندازه گیری و تنظیم دمای فر با استفاده از PID و غیره ، استفاده کرد.
حالت اسکن تبدیل مداوم Continuous
این حالت که به آن حالت (multichannel continuous mode) نیز می گویند، برای تبدیل برخی کانال ها به صورت متوالی در حالت مستقل استفاده می شود. با استفاده از ranks میتوانید از این حالت ADC تا 16 کانال متوالی را با زمانهای مختلف نمونهبرداری و به ترتیب دلخواه استفاده کنید. این حالت شبیه حالت multichannel single conversion mode است با این تفاوت که بعد از آخرین کانال دنباله ، تبدیل را متوقف نمی کند و انجام تبدیل مجدداً از کانال اول شروع شده و به طور نامحدود ادامه می یابد. تبدیل اسکن در حالت DMA انجام می شود.
این حالت ممکن است به عنوان مثال برای نظارت بر ولتاژها و دماهای متعدد در یک شارژر باتری چندگانه استفاده شود. ولتاژ و دمای هر باتری در طول فرآیند شارژ خوانده می شود. هنگامی که ولتاژ یا دمای هر یک به حداکثر سطح رسید، باتری مربوطه باید از شارژر جدا شود.
حالت تبدیل Injected Conversion Mode
این حالت برای استفاده زمانی در نظر گرفته شده است که تبدیل توسط یک رویداد خارجی یا توسط نرم افزار ایجاد می شود. گروه Injected نسبت به گروه کانال معمولی اولویت دارد یعنی گروه Injected ، تبدیل کانال فعلی در گروه کانال معمولی را ، قطع می کند.
برای مثال می توان از این حالت برای همگام سازی تبدیل کانال ها با یک رویداد استفاده کرد. در کاربردهای کنترل موتور که در آن سوئیچ شدن ترانزیستور نویز ایجاد می کند و بر اندازه گیری ADC تأثیر می گذارد ، بنابراین با استفاده از یک تایمر، حالت تبدیل Injected میتواند برای به تاخیر انداختن اندازهگیری ADC بعد از سوئیچ شدن ترانزیستور ، اجرا شود.
حالت دوگانه Dual Modes
حالت دوگانه Dual در میکروکنترلرهای STM32 ای موجود است که دارای دو ADC هستند: ADC1 master و ADC2 slave. تریگرهای ADC1 و ADC2 به صورت داخلی برای تبدیل کانال عادی و Injected هماهنگ می شوند. ADC1 و ADC2 با هم کار می کنند. در برخی از دستگاه ها تا 3 ADC وجود دارد: ADC1، ADC2 و ADC3. در این مورد ADC3 همیشه به طور مستقل کار می کند و با سایر ADC ها هماهنگ نیست.
حالت دوگانه به این صورت کار می کند که وقتی تبدیل به پایان رسید، نتیجه ADC1 و ADC2 به طور همزمان در رجیستر داده 32 بیتی ADC1 ذخیره می شود. با جدا کردن این دو نتیجه، میتوانیم دادههایی را که از دو کانال مجزا بهطور همزمان حاصل شده، بدست آوریم.
برای اطلاعات بیشتر در مورد حالت دوگانه، به داکیومنت AN31167 شرکت ST مراجعه کنید.
انتخاب کانال Channel Selection
بسته به خانواده STM32 و پکیج مورد استفاده، ADCها در MCU های STM32 می توانند سیگنال تعداد متغیری از کانال ها را تبدیل کنند. در خانواده های F0 و L0 تخصیص کانال ثابت است: اولی همیشه IN0، دومی IN1 بوده و غیره. کاربر فقط می تواند تصمیم بگیرد که آیا کانال فعال است یا خیر. یعنی در حالت اسکن اولین کانال نمونه برداری شده همیشه IN0، دومین کانال IN1 و غیره خواهد بود. در عوض، دیگر میکروکنترلر های STM32 مفهوم گروه را ارائه می دهند. یک گروه شامل رشته ای از تبدیل هاست که می تواند در هر کانال و به هر ترتیبی انجام شود. در حالی که کانالهای ورودی ثابت هستند و به پینهای خاصی از میکروکنترلر متصل هستند (یعنی IN0 اولین کانال است، IN1 دومین کانال و غیره)، میتوان آنها را بهطور منطقی (logically) دوباره مرتب کرد تا دنباله ای از کانال های نمونهگیری دلخواه را تشکیل دهند. مرتب سازی مجدد کانال ها با تخصیص عددی از 1 تا 16 به آنها انجام می شود. این عدد گذاری در CubeHAL ، rank نامیده می شود.
شکل 10 این مفهوم را نشان می دهد. اگرچه کانال IN4 ثابت است (به عنوان مثال، به پین PA4 در میکروکنترلر STM32F401RE متصل است)، می توان آن را به طور منطقی به rank 1 اختصاص داد تا اولین کانالی باشد که نمونه برداری می شود. میکروکنترلر هایی که این قابلیت را ارائه می دهند ، امکان انتخاب سرعت نمونه برداری به صورت جداگانه برای هر کانال را نیز فراهم می کنند.( به غیر از MCU های F0/L0 که در آن پیکربندی ADC به صورت گسترده است.)
پیکربندی channel/rank با نمونه ای از ساختار ADC_ChannelConfTypeDef انجام می شود که به صورت زیر تعریف شده است:
Channel: شناسه کانال را مشخص می کند. بسته به تعداد کانالهای مؤثر موجود ، میتواند مقادیر ADC_CHANNEL_0، ADC_CHANNEL_1…ADC_CHANNEL_N را در بر گیرد.
- rank : این فیلد اولویت نمونه برداری هر کانال را مشخص می کند و می تواند مقادیری از 1 تا 16 را که حداکثر rank قابل تعریف توسط کاربر است، در بر گیرد.
- SamplingTime: مقدار زمان نمونه برداری را که باید برای کانال انتخاب شده تنظیم شود، مشخص می کند و با تعداد یا سیکل های ADC مطابقت دارد. این عدد نمی تواند دلخواه باشد، اما بخشی از یک لیست مقادیر انتخاب شده است. همانطور که بعدا خواهیم دید، CubeMX کمک زیادی به ارائه لیست مقادیر قابل قبول برای میکروکنترلر خاصی که در نظر دارید، می کند.
برای هر ADC دو گروه وجود دارد:
- یک گروه regular group ساخته شده از حداکثر 16 کانال، که مربوط به دنباله کانال های نمونه برداری شده در طول حالت تبدیل اسکن است.
- یک گروه injected group ، ساخته شده از حداکثر 4 کانال، که در صورت انجام تبدیل injected ، با دنباله کانال های injected مطابقت دارد.
ADC Resolution and Conversion Speed
با کاهش resolution ADC تبدیل های سریع تری می توان انجام داد. زمان نمونه برداری در واقع با تعداد ثابتی از cycles ها (معمولاً 3) به اضافه تعداد متغیری از cycles که به وضوح A/D وابسته است ،تعریف می شود. حداقل زمان تبدیل برای هر وضوح به شرح زیر است:
- 12 bits: 3 + ∼12 = 15 ADCCLK cycles
- 10 bits: 3 + ∼10 = 13 ADCCLK cycles
- 8 bits: 3 + ∼8 = 11 ADCCLK cycles
- 6 bits: 3 + ∼6 = 9 ADCCLK cycles
با کاهش resolution ، می توان تعداد نمونه ها را در هر ثانیه افزایش داد و حتی در برخی از میکروکنترلرهای STM32 به بیش از 15 مگابایت بر ثانیه رسید. به یاد داشته باشید که ADCCLK از کلاک واحد جانبی نشات گرفته است: یعنی سرعت SYSCLK و PCLK بر حداکثر تعداد نمونه ها در ثانیه تأثیر می گذارد.
A/D Conversions in Polling Mode
مانند اکثر واحدهای جانبی STM32، ADC را می توان در سه حالت راه اندازی کرد: حالت polling ، interrupt و حالت DMA. همانطور که بعداً خواهیم دید، تایمر میتواند حالت DMA را به گونهای هدایت کند که تبدیلهای A/D در فواصل زمانی منظم انجام شود. این روش ، زمانی که ما نیاز به نمونه برداری از سیگنال ها در یک فرکانس معین، (مانند برنامه های صوتی) داریم ، بسیار مفید است .
کنترلر ADC با استفاده از نمونه ی ساختار ADC_InitTypeDef به وسیله تابع HAL_ADC_Init() پیکربندی میشود و با استفاده از تابع HAL_ADC_Start، واحد ADC شروع به کار می کند. بر اساس حالت تبدیل انتخاب شده، ADC هر ورودی انتخاب شده را به طور مداوم یا یک بار تبدیل می کند: در این مورد، برای تبدیل مجدد ورودی های انتخاب شده، باید قبل از فراخوانی مجدد HAL_ADC_Start () تابع HAL_ADC_Stop را فراخوانی کنیم.
در حالت polling از تابع زیر برای تعیین اینکه چه زمانی تبدیل A/D کامل شده و نتیجه در داخل رجیستر داده ADC موجود است، استفاده می کنیم:
این تابع یک اشاره گر به کنترل کننده ADC و یک مقدار Timeout را به عنوان آرگومان می پذیرد که نشان دهنده حداکثر زمانی (در میلی ثانیه) است که مایلیم منتظر بمانیم. همچنین میتوانیم از HAL_MAX_DELAY استفاده کنیم تا بهطور نامحدود منتظر بمانیم.
برای دریافت نتیجه تبدیل انجام شده، از تابع زیر استفاده می کنیم:
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
حال آماده تحلیل یک مثال کامل هستیم.این کار را با دیدن API هایی که برای انجام تبدیل در حالت polling استفاده می شوند، شروع خواهیم کرد. همانطور که خواهید دید، هیچ چیز جدیدی در مقایسه با آنچه تاکنون در سایر واحد های جانبی دیده ایم وجود ندارد.
مثالی که می خواهیم بررسی کنیم کار ساده ای را انجام می دهد: از سنسور دمای داخلی ، که در همه MCU های STM32 موجود است ، به عنوان منبع ADC استفاده می کند. سنسور دما به ورودی داخلی ADC متصل است. تعداد دقیق ورودی به خانواده و پکیج آن MCU خاص بستگی دارد. به عنوان مثال، در میکروکنترلر STM32F401RE، سنسور دما به IN18 واحد جانبی ADC1 متصل است. قبل از اینکه کد واقعی را تجزیه و تحلیل کنیم، بهتر است نگاهی گذرا به مشخصات الکتریکی سنسور دما داشته باشیم که در دیتاشیت MCU مورد نظر شما موجود است.
جدول 3 ویژگی های سنسور دما را در میکروکنترلر STM32F401RE نشان می دهد. دقت معمول آن 1 °C و شیب متوسط 2.5 mV/°C است. علاوه بر این، اتصال سنسور دما به گونه ای است که در دمای 25 درجه سانتی گراد افت ولتاژ 760 میلی ولت است. یعنی برای محاسبه دما می توانیم از فرمول زیر استفاده کنیم:
کد زیر نحوه انجام تبدیل A/D خروجی سنسور دمای داخلی را در STM32F401RE نشان می دهد:
6 /* Private variables ———————————————————*/
7 extern UART_HandleTypeDef huart2;
8 ADC_HandleTypeDef hadc1;
9
10 /* Private function prototypes ———————————————–*/
11 static void MX_ADC1_Init(void);
12
13 int main(void) {
14
15 HAL_Init();
16 Nucleo_BSP_Init();
17
18 /* Initialize all configured peripherals */
19 MX_ADC1_Init();
20
21 HAL_ADC_Start(&hadc1);
22
23 while (1) {
24 char msg[20];
25 uint16_t rawValue;
26 float temp;
27
28 HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
29
30 rawValue = HAL_ADC_GetValue(&hadc1);
31 temp = ((float)rawValue) / 4095 * 3300;
32 temp = ((temp – 760.0) / 2.5) + 25;
33
34 sprintf(msg, “rawValue: %hu\r\n“, rawValue);
35 HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
36
37 sprintf(msg, “Temperature: %f\r\n“, temp);
38 HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
39 }
40 }
41
42 /* ADC1 init function */
43 void MX_ADC1_Init(void) {
44 ADC_ChannelConfTypeDef sConfig;
45
46 /* Enable ADC peripheral */
47 __HAL_RCC_ADC1_CLK_ENABLE();
48
49 /* Configure the global features of the ADC (Clock, Resolution, Data Alignment and number
50 of conversion) */
51 hadc1.Instance = ADC1;
52 hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
53 hadc1.Init.Resolution = ADC_RESOLUTION_12B;
54 hadc1.Init.ScanConvMode = DISABLE;
55 hadc1.Init.ContinuousConvMode = ENABLE;
56 hadc1.Init.DiscontinuousConvMode = DISABLE;
57 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
58 hadc1.Init.NbrOfConversion = 1;
59 hadc1.Init.DMAContinuousRequests = DISABLE;
60 hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
61 HAL_ADC_Init(&hadc1);
62
63 /* Configure for the selected ADC regular channel its corresponding rank in the sequencer 64 and its sample time. */ 65 sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
66 sConfig.Rank = 1;
67 sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
68 HAL_ADC_ConfigChannel(&hadc1, &sConfig);
69 }
اولین قسمتی که باید تجزیه و تحلیل شود، تابع MX_ADC1_Init() است که واحد جانبی ADC1 را مقداردهی می کند. اول از همه، در خط 52 ADC طوری پیکربندی شده است که ADCCLK (یعنی کلاک قسمت آنالوگ ADC) نصف فرکانس PCLK باشد که در STM32F401RE که با حداکثر سرعت خود کار می کند 84 مگاهرتز است. سپس، رزولوشن ADC در حداکثر مقدار خود یعنی 12 بیت پیکربندی می شود. حالت scan conversion mode غیرفعال (خط 54)، و حالت تبدیل پیوسته فعال شده است (خط 55) تا بتوانیم به طور مکرر یک تبدیل را بدون توقف انجام دهیم و سپس ADC را دوباره راه اندازی کنیم. بنابراین، فلگ EOC در خط 60 روی ADC_EOC_SEQ_CONV تنظیم شده است. توجه داشته باشید که پارامتر NbrOfConversion در خط 59 در این مورد کاملاً بی معنی و اضافی است، زیرا حالت تبدیل تکی به طور خودکار تعداد کانال های نمونه برداری شده را برابر با 1 فرض می کند.
خطوط [66:68] کانال سنسور دما را پیکربندی میکنند و به آن rank 1 را اختصاص میدهند: توجه داشته باشید حتی اگر scan conversion را انجام نمیدهیم، باید رتبه کانال مورد استفاده را مشخص کنیم. زمان نمونه برداری روی 480 سیکل تنظیم شده است: این بدان معنی است که با توجه به سرعت کلاک 84 مگاهرتز، و با در نظر گرفتن اینکه ADCCLK روی نصف سرعت PCLK تنظیم شده است، در هر μs10 یک تبدیل A/D انجام می شود.
اما چرا ما آن سرعت تبدیل را انتخاب می کنیم؟ جدول 3 ، بیان می کند که زمان نمونه برداری ADC، TS_temp، برابر با 10μs برای داشتن دقت 1 درجه سانتی گراد است. به عنوان مثال، اگر سرعت را به 3 سیکل افزایش دهید، با تنظیم فیلد SamplingTime روی ADC_SAMPLETIME_3CYCLES خواهید دید که نتیجه تبدیل شده اغلب کاملاً اشتباه است.
در همان جدول می توانید اطلاعات جالب دیگری پیدا کنید: زمان شروع سنسور دما (یعنی زمان لازم برای تثبیت ولتاژ خروجی در هنگام فعال بودن سنسور) بین 6 تا 10 میکرو ثانیه است. با این حال، نیازی به در نظر گرفتن این موضوع هنگام کدنویسی نداریم، زیرا تابع HAL_ADC_ConfigChannel() برای مدیریت صحیح زمان راه اندازی طراحی شده است. درواقع این تابع به مدت 10 میکرو ثانیه منتظر می ماند تا سنسور دما ثابت شود.
اکنون می توانیم روی main() تمرکز کنیم. هنگامی که واحد جانبی ADC1 راه اندازی شد (خط 21)، در یک حلقه بی نهایت به صورت چرخه ای از ADC برای تبدیل A/D تقاضا می کنیم. پس از اتمام، میتوان مقدار تبدیل شده را بدست آورده و از معادله [1] برای محاسبه دما بر حسب درجه سانتیگراد استفاده کنیم. نتیجه در نهایت بر روی رابط UART2 چاپ می شود.
ماژول HAL_ADC در کتابخانه HALدر CubeF1 کمی با HAL های دیگر متفاوت است. برای شروع تبدیلی که توسط نرمافزار انجام میشود، لازم است پارامتر hadc.Init.ExternalTrigConv = در طول مقداردهی اولیه ADC برابر با ADC_SOFTWARE_START شود. این موضوع به طور کامل با آنچه که HAL های دیگر انجام می دهند متفاوت است، و مشخص نیست چرا توسعه دهندگان ST این رویکرد متفاوت را اتخاذ کرده اند. علاوه بر این، حتی CubeMX نیز پیکربندی متفاوتی را برای تولید کد اولیه مربوطه ، ارائه می دهد.
A/D Conversions in Interrupt Mode
انجام تبدیل A/D در حالت وقفه تفاوت زیادی با آنچه تاکنون دیدید ندارد. طبق معمول، باید ISR متصل به وقفه ADC را تعریف ، اولویت وقفه مورد نظر را مشخص و IRQ مربوطه را فعال کنیم. مانند سایر واحدهای جانبی HAL، باید HAL_ADC_IRQHandler() را از ADC ISR فراخوانی کرده و تابع (HAL_ADC_ConvCpltCallback) را اجرا کنیم، که به طور خودکار هنگامی که یک تبدیل به پایان می رسد، توسط HAL فراخوانی می شود. در نهایت، تمام وقفه های مربوط به ADC با راه اندازی ADC با استفاده از تابع HAL_ADC_Start_IT() فعال می شوند.
مثال زیر نحوه انجام تبدیل در حالت وقفه را نشان می دهد. کد اولیه برای ADC همان است که در مثال قبلی استفاده شده است.
int main(void) {
HAL_Init();
Nucleo_BSP_Init();
/* Initialize all configured peripherals */
MX_ADC1_Init();
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
HAL_ADC_Start_IT(&hadc1);
while (1);
}
void ADC_IRQHandler(void) {
HAL_ADC_IRQHandler(&hadc1);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
char msg[20];
uint16_t rawValue;
float temp;
rawValue = HAL_ADC_GetValue(&hadc1);
temp = ((float)rawValue) / 4095 * 3300;
temp = ((temp - 760.0) / 2.5) + 25;
sprintf(msg, "rawValue: %hu\r\n", rawValue);
HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
sprintf(msg, "Temperature: %f\r\n", temp);
HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
}
A/D Conversions in DMA Mode
جالب ترین حالت برای درایو واحد جانبی ADC، حالت DMA است. این حالت امکان تبدیل بدون دخالت CPU را فراهم می کند و با استفاده از DMA در حالت circular ، می توانیم به راحتی ADC را طوری تنظیم کنیم که تبدیل را به صورت مداوم انجام دهد. علاوه بر این، همانطور که در ادامه متوجه خواهید شد، این حالت برای انجام تبدیل با استفاده از تایمر، یک روش عالی است و امکان نمونهبرداری از سیگنال ورودی با نرخ نمونهگیری ثابت را فراهم میکند. همچنین استفاده از واحد جانبی ADC در حالت DMA زمانی که می خواهیم چندین کانال را با استفاده از حالت اسکن انجام دهیم، الزامی است.
برای انجام تبدیل های A/D در حالت DMA، مراحل زیر طی می شود:
- واحد جانبی ADC را بر اساس حالت تبدیل مورد نظر (scan single, scan continuous, etc) تنظیم کنید.
- کانال DMA مربوط به کنترلر ADC را تنظیم کنید.
- با استفاده از ماکرو __HAL_LINKDMA() توصیفگر DMA handler را به ADC handler متصل کنید.
- DMA و IRQ مرتبط با کانال DMA مورد استفاده را فعال کنید.
- ADC را در حالت DMA با استفاده از HAL_ADC_Start_DMA() راه اندازی کنید و آرایه مورد استفاده برای ذخیره داده های به دست آمده از ADC را به آن ارجاع دهید.
- با تعریف HAL_ADC_ConvCpltCallback() برای رویداد EOC آماده باشید.
مثال زیر که برای میکروکنترلر STM32F401RE طراحی شده است، نحوه انجام تبدیل اسکن تکی single scan با استفاده از حالت DMA را نشان می دهد. اولین بخشی که می خواهیم آنالیز کنیم مربوط به راه اندازی هر دو کنترلر ADC و DMA است.
44 }
45
46 /* ADC1 init function */
47 void MX_ADC1_Init(void) {
48 ADC_ChannelConfTypeDef sConfig;
49
50 /* Enable ADC peripheral */
51 __HAL_RCC_ADC1_CLK_ENABLE();
52
53 /**Configure the global features of the ADC
54 */
55 hadc1.Instance = ADC1;
56 hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
57 hadc1.Init.Resolution = ADC_RESOLUTION_12B;
58 hadc1.Init.ScanConvMode = ENABLE;
59 hadc1.Init.ContinuousConvMode = DISABLE;
60 hadc1.Init.DiscontinuousConvMode = DISABLE;
61 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
62 hadc1.Init.NbrOfConversion = 3;
63 hadc1.Init.DMAContinuousRequests = DISABLE;
64 hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
65 HAL_ADC_Init(&hadc1);
66
67 /**Configure for the selected ADC regular channels */
68 sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
69 sConfig.Rank = 1;
70 sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
71 HAL_ADC_ConfigChannel(&hadc1, &sConfig);
72
73 sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
74 sConfig.Rank = 2;
75 HAL_ADC_ConfigChannel(&hadc1, &sConfig);
76
77 sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
78 sConfig.Rank = 3;
79 HAL_ADC_ConfigChannel(&hadc1, &sConfig);
80 }
81
82 void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) {
83 if(hadc->Instance==ADC1) {
84 /* Peripheral clock enable */
85 __HAL_RCC_ADC1_CLK_ENABLE();
86
87 /* Peripheral DMA init*/
88 hdma_adc1.Instance = DMA2_Stream0;
89 hdma_adc1.Init.Channel = DMA_CHANNEL_0;
90 hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
91 hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
92 hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
93 hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
94 hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
95 hdma_adc1.Init.Mode = DMA_NORMAL;
96 hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
97 hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
98 HAL_DMA_Init(&hdma_adc1);
99
100 __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
101 }
102 }
103
104 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
تابع MX_ADC1_Init() ADC را به گونه ای پیکربندی می کند که یک single scan از سه ورودی انجام دهد. ADCCLK روی کمترین مقدار تنظیم شده است (خط 55) و حالت اسکن فعال است (خط 58). همانطور که می بینید، ADC به گونه ای پیکربندی شده است که همیشه یک تبدیل از سنسور دمای داخلی انجام دهد: این کار مفید نیست، اما متأسفانه بردهای Nucleo قطعات جانبی آنالوگ را برای کار با آنها تعبیه نمی کنند.
تابع HAL_ADC_MspInit() بهطور خودکار توسط HAL وقتی که روتین HAL_ADC_Init() در خط 65 فراخوانی شود، اجرا می شود. این تابع به سادگی DMA2 Stream0/Channel0 را به گونهای پیکربندی میکند که وقتی ADC یک تبدیل را کامل میکند، انتقال peripheralto-memory انجام شود. واضح است که ترتیب تبدیل با RANKاختصاص داده شده به هر کانال مشخص می شود. از آنجایی که رجیستر داده ADC 16 بیت دارد، DMA را طوری پیکربندی می کنیم که یک انتقال half-word انجام شود. در نهایت، تابع HAL_ADC_ConvCpltCallback() به طور خودکار زمانی که تبدیل اسکن به پایان می رسد، توسط HAL فراخوانی می شود (تماس با این تابع توسط HAL_DMA_IRQHandler() فراخوانی شده از DMA2_Stream0_IRQHandler()، که در اینجا نشان داده نشده است، انجام می شود.). در callback از یک متغیر گلوبال را برای سیگنال پایان تبدیل استفاده می شود.
7 extern UART_HandleTypeDef huart2;
8 ADC_HandleTypeDef hadc1;
9 DMA_HandleTypeDef hdma_adc1;
10 volatile uint8_t convCompleted = 0;
11 12 /* Private function prototypes -----------------------------------------------*/
13 static void MX_ADC1_Init(void);
14 15 int main(void) {
16 char msg[20];
17 uint16_t rawValues[3];
18 float temp;
1920 HAL_Init();
21 Nucleo_BSP_Init();
22 23 /* Initialize all configured peripherals */
24 MX_ADC1_Init();
25 26 HAL_ADC_Start_DMA(&hadc1, (uint32_t*)rawValues, 3);
27 28 while(!convCompleted);
29 30 HAL_ADC_Stop_DMA(&hadc1);
31 32 for(uint8_t i = 0; i < hadc1.Init.NbrOfConversion; i++) {
33 temp = ((float)rawValues[i]) / 4095 * 3300;
34 temp = ((temp - 760.0) / 2.5) + 25;
35 36 sprintf(msg, "rawValue %d: %hu\r\n", i, rawValues[i]);
37 HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
38 39 sprintf(msg, "Temperature %d: %f\r\n",i, temp);
40 HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
8-10. Convert Multiple Times the Same Channel in DMA Mode
برای انجام تعداد معینی تبدیل از یک کانال (یا ترتیبی از کانالها) در حالت DMA، باید به روش زیر عمل کنید:
- فیلد hadc.Init.ContinuousConvMode را روی ENABLE قرار دهید.
- یک بافر با اندازه کافی اختصاص دهید.
- تعداد نتایج مورد نظر را به HAL_ADC_Start_DMA() ارسال کنید.
Multiple and not Continuous Conversions in DMA Mode
برای انجام چندین تبدیل در حالت DMA، باید به روش زیر عمل کنید:
- فیلد hadc.Init.DMAContinuousRequests را روی ENABLE تنظیم کنید.
- برای شروع تبدیل در حالت DMA، HAL_ADC_Start_DMA() را فراخوانی کنید.
اگر در عوض، فیلد hadc.Init.DMAContinuousRequests روی DISABLE تنظیم شده باشد، باید HAL_ADC_Stop_DMA() را در پایان هر رشته از تبدیل و قبل از فراخوانی مجدد HAL_ADC_Start_DMA() فراخوانی کنید. در غیر این صورت تبدیل شروع نمی شود.
Continuous Conversions in DMA Mode
برای انجام تبدیل مداوم در حالت DMA، باید مراحل زیر را انجام دهید:
- فیلد hadc.Init.ContinuousConvMode را روی ENABLE قرار دهید.
- فیلد hadc.Init.DMAContinuousRequests را روی ENABLE تنظیم کنید، در غیر این صورت ADC پس از تکمیل اولین توالی اسکن، DMA را دوباره راه اندازی نمی کند.
- جریان/کانال DMA را در حالت DMA_CIRCULAR پیکربندی کنید.
Errors Management
واحد جانبی ADC این توانایی را دارد که در صورت انجام نشدن تبدیل، به توسعه دهندگان اطلاع دهد. این شرایط خطا زمانی اتفاق میافتد که یک تبدیل حالت مداوم یا اسکن در حال انجام است و رجیستر داده ADC قبل از خواندن توسط یک تبدیل متوالی بازنویسی میشود. هنگامی که این اتفاق می افتد یک بیت خاص در رجیستر ADC_SR یک شده و وقفه ADC ایجاد می شود.
ما می توانیم خطای overrun را با اجرای callback زیر دریافت کنیم:
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc);
Timer-Driven Conversions
برای تغییر این متن بر روی دکمه ویرایش کلیک کنید. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است.
One thought on “مبدل آنالوگ به دیجیتال در میکروکنترلرهای STM32”
عالی