آموزش میکروکنترلر STM32 – قسمت سیزدهم ;
سیسوگ در قسمت دوازدهم از آموزش میکروکنترلر STM32 توابع عمومی رابط RTC و جزئیات ساختار RTC_TimeTypeDef و RTC_DateTypeDef را بیان نمود. در این قسمت از آموزش میکروکنترلر STM32 قصد دارد وقفه های رابط RTC و تبدیل تاریخ شمسی و میلادی و تبدیل تاریخ و زمان به عدد را آموزش دهد. با سیسوگ همراه باشید.
وقفه ثانیه
بعد از آموزش رابط RTC وارد آموزش قسمت وقفه های رابط RTC میشویم. اولین وقفه که میخواهیم در اینجا مورد بررسی قرار دهیم وقفه ثانیه است، یعنی با هر ثانیه اضافه شدن به زمان این وقفه فعال میگردد. ما میتوانیم این وقفه را با فرمان زیر فعال نمائیم.
1 | __HAL_RTC_ALARM_ENABLE_IT(&hrtc,RTC_IT_SEC); |
روتین مورد استفاده برای این وقفه هم تابع HAL_RTCEx_RTCEventCallback میباشد که ما میتوانیم آن را در main برنامه مورد استفاده قرار دهیم. به عنوان مثال ما در برنامه زیر در وقفه مربوطه زمان و تاریخ را نمایش میدهیم.
1 2 3 4 5 6 | void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc) { HAL_RTC_GetTime(hrtc, &time, RTC_FORMAT_BIN); HAL_RTC_GetDate(hrtc, &date, RTC_FORMAT_BIN); printf("TIME=%02d:%02d:%02d DATE=%02d/%02d/%02d\r\n", time.Hours, time.Minutes, time.Seconds, date.Year, date.Month, date.Date); } |
وقفه آلارم
وقفه مورد استفاده دیگر وقفه آلارم میباشد که ما میتوانیم بوسیله آن در یک زمان مشخص به روتین وقفه مورد نظر برویم. برای فعال کردن این وقفه ابتدا باید مثل برنامه زیر یک ساختار از نوع RTC_AlarmTypeDef تعریف کنیم و سپس زمان مورد نظرمان را در آن قرار دهیم و سپس از طریق فرمان HAL_RTC_SetAlarm_IT آن را به آلارم واحد RTC اعمال کنیم و همزمان وقفه آلارم را نیز فعال نمائیم.
1 2 3 4 5 | RTC_AlarmTypeDef atime; atime.AlarmTime.Hours = 1; atime.AlarmTime.Minutes = 0; atime.AlarmTime.Seconds = 10; HAL_RTC_SetAlarm_IT(&hrtc, &atime, RTC_FORMAT_BIN); |
روتین مورد استفاده برای این وقفه هم تابع HAL_RTC_AlarmAEventCallback میباشد که ما میتوانیم آن را در main برنامه مورد استفاده قرار دهیم. به عنوان مثال ما در برنامه زیر در وقفه مربوطه رویداد آلارم را گزارش میدهیم.
1 2 3 4 | void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) { printf("Active Alarm !!! \r\n"); } |
تبدیل تاریخ شمسی و میلادی
خوب بعد از فراگیری این قسمتها میخواهیم ببینیم که چطور میتوانیم تاریخ را از میلادی به شمسی و از شمسی به میلادی تبدیل کنیم. برای این امر از توابع مربوطه استفاده میکنیم. ابتدا تابع تبدیل تاریخ میلادی به شمسی را معرفی میکنیم.
1 | miltoshmcv(unsigned char ym,unsigned char mm,unsigned char dm); |
و تابع زیر هم برای تبدیل تاریخ شمسی به میلادی کاربرد دارد.
1 | shmtomilcv(unsigned char ys ,unsigned char ms,unsigned char ds); |
تبدیل تاریخ و زمان به عدد
حال میخواهیم توابع تبدیل Unix Time Stamp به DateTime را بررسی کنیم، این توابع برای تبدیل یک تاریخ و زمان به عدد Unix Time Stamp یا بالعکس مورد استفاده قرار میگیرند. unix timestamp یا به اختصار timestamp یک پروتکل (قراردادی) است که تعداد ثانیه ها از تاریخ ۱ ژانویه ۱۹۷۰ ساعت ۱۲ بامداد (به وقت GMT) تا هر لحظه را نشان میدهد. یکی از بهترین راه ها برای ذخیره تاریخ و زمان، ذخیره نمودن timestamp میباشد. چرا که به راحتی میتوانیم عملیات ریاضی را روی آن انجام دهیم، به سادگی آن را به تقویمهای دیگر تبدیل کنیم و یا حتی آن را به time zone های دیگر تبدیل کرد و با همین یک عدد میتوان به روز و ماه و سال و ساعت و دقیقه و ثانیه و فصل و روز هفته و شماره هفته از سال و… پی برد. یکی از کاربردهای این روش زمانی است که میخواهید به عنوان مثال فاصله بین دو زمان مشخص را متوجه شوید، راه حل این است که تاریخ و زمان ابتدا و انتها را به Unix Time Stamp تبدیل کنید و سپس عدد ابتدا را از عدد انتها کم کنید و عدد بدست امده فاصله این دو بر مبنای ثانیه است. کاربرد دیگر Unix Time Stamp ، روش ساده ذخیره سازی آن است یعنی اینکه زمانی که شما بخواهید یک عدد را ذخیره کنید خیلی ساده تر از این است که بخواهید تک تک متدهای ثانیه و دقیقه و ساعت و روز و ماه و سال را ذخیره کنید. یک کاربرد دیگر Unix Time Stamp هم این است که در بعضی مقولهها مثل خروجی پیامهای تلگرام زمان و تاریخی که برای شما به همراه پیام ارسال میگردد با فرمت Unix Time Stamp میباشد که شما میتوانید از طریق توابع تبدیل که داخل مثال همراه برنامه آمده آن را به حالت DateTime تبدیل کنید.حال بهتر است توابع مربوط به Unix Time Stamp را بررسی کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | uint32_t RTC_ToEpoch(RTC_TimeTypeDef *time, RTC_DateTypeDef *date) { uint8_t a; uint16_t y; uint8_t m; uint32_t JDN; // These hardcore math's are taken from http://en.wikipedia.org/wiki/Julian_day // Calculate some coefficients a = (14 - date->RTC_Month) / 12; y = (date->RTC_Year + 2000) + 4800 - a; // years since 1 March, 4801 BC m = date->RTC_Month + (12 * a) - 3; // since 1 March, 4801 BC // Gregorian calendar date compute JDN = date->RTC_Date; JDN += (153 * m + 2) / 5; JDN += 365 * y; JDN += y / 4; JDN += -y / 100; JDN += y / 400; JDN = JDN - 32045; JDN = JDN - JULIAN_DATE_BASE; // Calculate from base date JDN *= 86400; // Days to seconds JDN += time->RTC_Hours * 3600; // ... and today seconds JDN += time->RTC_Minutes * 60; JDN += time->RTC_Seconds; return JDN; } |
از طریق تابع بالا میتوانیم یک تاریخ و زمان را که از طریق دو آرگومان تابع وارد میشود را به یک عدد Unix Time Stamp تبدیل کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | void RTC_FromEpoch(uint32_t epoch, RTC_TimeTypeDef *time, RTC_DateTypeDef *date) { uint32_t tm; uint32_t t1; uint32_t a; uint32_t b; uint32_t c; uint32_t d; uint32_t e; uint32_t m; int16_t year = 0; int16_t month = 0; int16_t dow = 0; int16_t mday = 0; int16_t hour = 0; int16_t min = 0; int16_t sec = 0; uint64_t JD = 0; uint64_t JDN = 0; // These hardcore math's are taken from http://en.wikipedia.org/wiki/Julian_day JD = ((epoch + 43200) / (86400 >>1 )) + (2440587 << 1) + 1; JDN = JD >> 1; tm = epoch; t1 = tm / 60; sec = tm - (t1 * 60); tm = t1; t1 = tm / 60; min = tm - (t1 * 60); tm = t1; t1 = tm / 24; hour = tm - (t1 * 24); dow = JDN % 7; a = JDN + 32044; b = ((4 * a) + 3) / 146097; c = a - ((146097 * b) / 4); d = ((4 * c) + 3) / 1461; e = c - ((1461 * d) / 4); m = ((5 * e) + 2) / 153; mday = e - (((153 * m) + 2) / 5) + 1; month = m + 3 - (12 * (m / 10)); year = (100 * b) + d - 4800 + (m / 10); date->RTC_Year = year - 2000; date->RTC_Month = month; date->RTC_Date = mday; date->RTC_WeekDay = dow; time->RTC_Hours = hour; time->RTC_Minutes = min; time->RTC_Seconds = sec; } |
از طریق تابع بالا میتوانیم فرمت Unix Time Stamp را به فرمت DateTime تبدیل کنیم.
در قسمت چهاردهم مجموعه آموزش میکروکنترلر STM32 سیسوگ قصد دارد رجیستر های پشتیبان و کاربردی RTC را آموزش دهد. با سیسوگ همراه باشید.
سلام در تبدیل تاریخ میلادی به شمسی سال میلادی را به شمسی درست تبدیل نمیکنه.ممنون میشم بررسی بفرمایید
سلام بر سیسوگ و دانشمندانش
مسئلتن !
مشکل عقب افتادن ساعت در پروژه هایی که قراره همزمان چک بشه و فعالیتی در تایم مشخصی انجام بشه رو از قدیم توسط avr کارها میشنیدم که چند میکروثانیه عقب افتادگی پیدا میکنه و به مرور این خطا بیشتر و بیشتر میشه و آنها برای اصلاح این مشکل استفاده از آیسی ساعت استفاده میکنند.
از اساتیدی شنیدم که در arm هم این مورد هست و بهتره برای این نوع پروژه ها از آی سی ساعت استفاده شه چون هزینش از تجهیز بخش rtc کمتره. نظر شما چیه؟
سلام و درود بر شما
خوب این مساله توی تمام شمارنده های RTC صادق هست، ولی خوب راهکار داره، البته یه سری آیسی RTC هست که کریستال و نوسان سازش داخلی هست ، اینا توی شرکت کالیبره میشن
برای آیسی های ST هم همین مساله صادقه، البته شرکت ST برای کالیبره کردن RTC داکیومنت خوبی داره و البته توی سری آموزش های LL قراره بهش بپردازیم، ولی جواب کوتاه به سوال شما این میشه که بله این مشکل هست و البته راه حلشم هست 🙂
سلام زئوسه بت شکن
از المپ چه خبر حال و هوا خوبه؟
بله منظورم قیاس استفاده بین آیسی ساعت های آماده که از 5 تومن الان در بازار یافت میشه تا 50 تومن حدودا و RTC کالیبره شده خود میکرو با در نظر گرفتن همون ملاحضات که فرمودید بود.
از نظر قیمتی و اقتصادی منظورمه و حجم وقت و دردسر و دقت عملش آیا توصیه شما باز کالیبره کردن RTC میکروست؟
اگر لینک مفیدی از ST در این مورد دارید ممنون میشم به اشتراک بگذارید.
سلام و درود بر شما دوست عزیز
بت شکن کجا بوده :/، والا خیلی خیلی شلوغه و اصلا خودمم هم نمی فهمم دارم چکار میکنم
ببینید ما حدود ۷ سال پیش ساعت درست میکردیم، خوب مهم ترین چیز توی ساعت اینه که تایم رو گم نکنه و عقب و جلو نشه، ما از RTC خود STM32 استفاده کردیم البته برای هر برد نیاز به کالیبره کردن داشت که یه فرکانس متر دقیق خیلی دقیق خریدیم و هر کدوم رو کالیبره میکردم بدون مشکل به کار خودش ادامه میداد و دقتش اگر بیشتر از آیسی های RTC نبود کمتر هم نبود
و ما تعداد زیادی ازش تولید کردیم و فروختیم، البته باید حواستان به جنس های غیر اورجینال هم باشد
سلام
حاج زئوس نفهمه چکار میکنه پس منه god of war تقلبی چی بگم ! 🙂
عاقا این توابع LL رو میشه مثل قیمه ها ریخت تو ماستای توابع hal ؟
یعنی میشه در کنار هم در پروژه استفاده کرد؟ :l
نکاتی که میفرمایید مطرح شده در آموزش LL قابل استفاده در دنیای HAL که من فعلا درگیرشم هست؟
راستی شما از بین CMSIS , LL و HAL بیشتر کدومو ترجیح میدی؟ من هیچی از اون دوتای دیگه بجز HAL نمیدونم چون ورود من به دنیای STM32F1 با HAL بوده و فعلا هست تا زمانی که توش به تسلط برسم. آیا لازمه اصلا یادگیری اونا ؟
سپاس
سلام
خوب باید بگم بله امکانش هست که هر دو سری کتابخانه رو توی یک پروژه استفاده کنید و بخش هایی رو با توابع LL پیش ببرید و بخش دیگری را با کتابخانه HAL
خود stmcube این امکان رو داره که موقع ساختن پروژه تعریف کنید کدام بخش ها با کدام کتابخانه هندل بشه
من خودم که از spl منسوخ شده استفاده میکنم ! اما نزدیکتر به ll هست ساختارش، این که کدام کتابخانه رو استفاده کنید بیشتر برمیگرده به این که دقیقا بخواید چکار کنید مثلا اگر قابلیت حمل کد براتون مهم باشه مثلا امروز با st راه انداختید فردا بخواید ببرید روی nxp استفاده از cmsis مزایای زیادی داره
سلام مجدد
لینک سورس تابع برای تبدیل rtc tp epoch
لطفا داخل مقاله قرار بدین برای استفاده ی دیگران.
سلام دوست عزیز
ممنونم برای تذکرتون – سورس توابع مذکور رو میتونید در لینک زیر پیدا کنید.
https://github.com/LonelyWolf/stm32/blob/master/stm32l-dosfs/RTC.c
سلام وقت بخیر
می بخشید من تابع RTC_TOepoch رو داخل کتابخونهhal پیدا نمی کنم. میشه راهنمایی کنید ؟
سلام دوست عزیز
تابع مریوطه توی مطلب قرار دادم
سلام ، تشکر از مطلب خوبتون
مشکل قبول نکردن تابع تبدیل میلادی به شمسی در C99 رو چطور میتونم در نرم افزار keil 5.15 حل کنم ارور invalid in C99 میده ، سپاس
سلام دوست عزیز
توی تنظیمات پروژه توی کیل یه تیک هست که به کامپایلر کیل امکان c99 رو اضافه میکنه
اونطور که یادم میآد توی تب c/c++ بود
سلام بر خدای خدایان زئوس(استغفرالله) 🙂
آقا تشکر اما تا زمانی که بدنه ی تابع رو که زحمت کشیدید پایین تر برا آقا سعید لینک گذاشتید استفاده نکرده بودم جواب نداد.
دو اشکال وجود داره یکی جزئی و یکی کلی !
اشکال جزئی در خط 56 لینکی که گذاشتید در خط دوم تایع تبدیل شمسی به میلادیه که نوشتید yss باید بشه ys
اما اشکال کلی و غیر قابل اغماض اینه که با ورود تابع به سال کبیسه شمسی مثلا 1404 یا 1408روزها در خروجی یکی می افتند جلو و بعبارتی یک روز جلوتر رو تقویم نمایش میده.
میشه راهنمایی بفرمایید چه میشود کرد که همه چی بهم نریزه؟:)
سلام و خدا قوت و سپاس بابت وقتی که گذاشتید
آقا من تایع miltoshmcv و shtomilmcv رو که مینویسم این خطا رو میده:
implicit declaration function is invalide in C99
چطور میتونم تو نرم افزار keil V5.15 این ارور رو رفع کنم ؟
تشکر
سلام دوست عزیز
ساده است مطابق عکس زیر عمل کنید
خیلی ممنون از زحمتی که کشیدید
سلام
طریقه استفاده از تابع تبدیل میلادی به شمسی چطور هست .
من اینطوری نوشتم ولی خطا میده . بدنه تابع رو هم به برنامه اضافه کردم .
unsigned char sal=”21″;
unsigned char mah=”1″;
unsigned char roz=”11″;
unsigned char datee;
datee=miltoshmcv( sal, mah, roz);
سلام
این آموزش خیلی خوب بود . ممنونم
خواهش میکنم دوست عزیز
سلام اگه میشه بدنه این تابع رو هم بزارین چک کردم نبود. ممنون
RTC_ToEpoch
سلام خسته نباشید
تو این قسمت فقط اسم و ورودی توابع رو گذاشتین؟ بدنه ندارن ؟ یا من نمیتونم پیدا کنم ؟
با تشکر
احتمالا باید داخل کتابخونه HAL باشه !
توابع اولیه که گفتین آره ماله هال هستن و داخل هدر فایلها میشه پیدا کرد.اما منظورم توابع تبدیل شمسی به میلادی و تبدیل تاریخ و زمان به عدد بودش..چون داخل توابع هال رو سرچ زدم نبودن
سلام بله درسته
بدنه رو تو لینک زیر قرار دادم
https://gist.github.com/Sisoog/592013ee188b4bd6abdb9fbba2c34578
این رو تست کردید درسته برا من که درست کار نمی کنه باید ورودی تابع bcd باشه
بله من با این تابع کار کردم فکر نمیکنم مشکل داشته باشه – خیر داده ها نباید bcd باشند
سلام زئوس جان، این تابع تا قبل از 1400 رو درست کار میکنه، کلا اکثر دیوایس های تقویم فارسی داری که من اطرافم میشناختم با ورود به قرن جدید به مشکل افتادن و رفتن کارخونه برای دریافت برنامه جدید !
احتمالا از همین برنامه استفاده میکردند 🙂
با ورود به 1400 یک روز عقب یا جلو می افتاد مخصوصا درسالهای کبیسه تا جایی که یادمه من با مکافات چون دقیق نفهمیدم توش داره اتفاقی میفته از بیرون اعداد دریافتی رو اصلاح کردم و با تقویم موبایل “باد صبا” تطبیق دادم اما از سورس کد صحیحش استقبال میکنم 🙂
سلام و درود بر شما ، اتفاقا این اتفاق برای دستگاه های ما هم افتاد، سالها پیش ما ساعت تولید میکردیم و این تکه کد از همون ساعته که من قرار داده بودم
اما واقعا چون مربوط به خیلی سال پیش بود به دنبال اصلاحش نرفتم ، اگر شما اصلاح کردید خوشحال میشم که کدتون رو با ما به اشتراک بذارید یا حتی خودتون در موردش مقاله ای بنویسید
میدونم نیاز تعداد زیادی از دوستان هست این مساله 🙂
سلام
خسته نباشید
من میخواهم در تابع وقفه ی آلارم, تنظیمات جدیدی برای آلارم ایجاد کنم. به همان صورت که در تابع main این تنظیمات رو ایجاد میکنیم, انجام میدهم ولی با ارور زیر مواجه میشم:
Error[Pe167]: argument of type “struct **” is incompatible with parameter of type “struct *”
این هم کد داخل تابع وقفه:
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc){
RTC_AlarmTypeDef atime;
aflag=0;
atime.AlarmTime.Hours = 0;
atime.AlarmTime.Minutes = 15;
atime.AlarmTime.Seconds = 0;
HAL_RTC_SetAlarm_IT(&hrtc, &atime, RTC_FORMAT_BIN); //برای این خط ارور میزند.
}
ممنون میشم راهنمایی کنید.
سلام دوست گرامی
خطایی که دارید دریافت میکنید – درواقع به این معنی است که پارامتر پاس داده شده به تابع که ظاهرا یه structure هست ِ هم خوانی نداره با اون چیزی که باید باشه
در واقع یعنی نوع اشتباهی رو دارید توی ورودی تابع پاس میدید.