آموزش میکروکنترلر STM32 قسمت دوم: نرم‌افزار Keil

آموزش میکروکنترلر STM32 قسمت دوم: نرم‌افزار Keil

آموزش میکروکنترلر STM32 قسمت دوم: نرم‌افزار Keil
آموزش میکروکنترلر STM32 قسمت دوم: نرم‌افزار Keil

سلام دوستان! در قسمت قبل، تنظیمات GPIO را برای میکروکنترلر STM32 در محیط نرم‌افزار CubeMX انجام دادیم. در این قسمت از آموزش قصد داریم تا این تنظیمات را به محیط نرم‌افزار Keil منتقل کنیم و برنامه‌نویسی برای میکروکنترلر STM32 را در این محیط آغاز کنیم. پس با سیسوگ همراه باشید.

 

باز کردن پروژه در نرم‌افزار Keil

پروژه ذخیره‌شده‌ی خود را باز می‌کنیم و از منو project گزینه setting را انتخاب می‌کنیم تا صفحه‌ای مطابق عکس زیر باز شود:

محیط نرم افزار cubeMx برای میکروکترلر STM32

 

همان‌طور که در عکس بالا مشاهده می‌کنید، باید نام پروژه و محل ذخیره آن و همچنین نام کامپایلر موردنظرمان (keil) را انتخاب کنیم و سپس دکمه OK را بزنیم و بعدازاین عمل از همان منو Project گزینه generate code را بزنیم تا کدهای مربوط میکروکنترلر تولید شود و سپس دیالوگ مشابه عکس دوم برای ما ظاهر شود:

خرویجی نرم افزار CubeMx برای نرم افزار Keil

 

باز کردن کد تولید شده در نرم افزار Keil

 

حالا باید دکمه open project را بزنیم تا برنامه ما در محیط نرم‌افزار Keil مطابق عکس زیر باز شود:

 

محیط نرم افزار keil

 

همین ابتدای کار یک نکته خیلی مهم را اعلام می‌کنیم:

توجه داشته باشید موقع اضافه کردن کد حتماً کدهای خودتان رابین USER CODE BEGIN و USER CODE END های مشخص‌شده در برنامه قرار بدهید. در غیر این صورت اگر دوباره با CubeMX تغییراتی اعمال و سپس دوباره خروجی بگیرید خودبه‌خود کدهایی که خارج از این کادرها قرار داده‌اید، پاک می‌شوند!!!

در ابتدا دکمه F7 یا دکمه Build را می‌زنیم تا برنامه یک‌بار در محیط نرم‌افزار Keil کامپایل شود. پس از اتمام کامپایل برنامه، اگر به سمت چپ برنامه Keil توجه کنید، در کادر Project و در شاخه Drivers می‌توانید کتابخانه‌های HAL را که برای راه‌اندازی ادوات مختلف این میکروکنترلر قرار داده‌شده‌اند، مشاهده کنید:

کتابخانه HAL در محیط نرم افزار Keil

 

قرار است توابع کتابخانه‌های  stm32f1xx_hal_gpio.c و stm32f1xx_hal.c را در نرم‌افزار Keil موردبررسی قرار دهیم و از آنها استفاده کنیم. همان‌طور که در عکس زیر مشاهده می‌کنید اگر در پایین همین کادر روی تب Functions کلیک کنید می‌توانید توابع بکار رفته در هر فایل را ببینید.

توابع GPIO میکروکنترلر STM32

 

توابع موجود در کتابخانه HAL میکروکنترلر STM32 :

با توضیح کتابخانه stm32f1xx_hal_gpio.c آغاز می‌کنیم:

این تابع همان‌طور که از نامش برمی‌آید برای برگرداندن تنظیمات یک پین مشخص از یک پورت مشخص به حالت پیش‌فرض است.

در کتابخانه  stm32f1xx_it.c تابع زیر راداریم که در زمان رخ دادن وقفه خارجی به داخل آن می‌رویم.

منظور از آرگومان GPIO_Pin پین از پورت موردنظر است که اینتراپت خارجی روی آن فعال‌شده و از داخل همین تابع هم هست که ابتدا وقفه اینتراپت پاک می‌شود و سپس تابع HAL_GPIO_EXTI_Callback فراخوانی می‌شود به‌عنوان‌مثال به‌عکس‌های زیر توجه کنید:

 

 

 

این تابع هم همان روتین وقفه خارجی است و همان‌طور که پیش‌ازاین گفته شد از داخل تابع HAL_GPIO_EXTI_IRQHandler فراخوانی می‌شود که با توجه به اینکه کلمه “weak __”  در ابتدا آن بکار رفته به این معنی است که شما می‌توانید همین تابع را به فایل main خودتان انتقال دهید و در آنجا کدهای خودتان را داخل آن جاسازی کنید.  توضیحات بیشتر در مورد وقفه خارجی را موکول می‌کنیم به بخش آموزش کار با وقفه خارجی که در قسمت‌های بعدی بیان خواهم کرد فقط توجه داشته باشید که همین قضیه کلمه  “weak __” را در مورد وقفه‌های ادوات دیگر میکروکنترلر مثل رابط‌های سریال و تایمرها و غیره نیز داریم.

 

این تابع هم برای پیکره‌بندی یک پین یا پین‌های خاص از یک پورت مشخص کاربرد دارد پیکره‌بندی  اعم از ورودی و یا خروجی بودن پورت، پوش پول یا کلکتورباز بودن آن، حداکثر سرعت پورت، اینکه ورودی آنالوگ یا غیره باشد است. البته توجه داشته باشد کدهای مربوط به پیکره‌بندی پورت‌ها در تابع MX_GPIO_Init داخل main برنامه تولید می‌شود.

 

توسط این تابع می‌توانیم تنظیمات پیکره‌بندی را روی پین یا پین‌های موردنظر از یک پورت قفل کنیم که دیگر با تغییر مقادیر رجیسترهای پیکره‌بندی تغییری حاصل نشود.

 

توسط این تابع می‌توانیم اطلاعات ورودی یک پین از پورت مشخص را در حالتی که به‌صورت ورودی پیکره‌بندی شده  به شکل صفر و یا یک بخوانیم.

 

این تابع همان‌طور که از نامش برمی‌آید وظیفه  بالعکس کردن وضعیت یک یا چند پین از پورت مشخص که به‌صورت خروجی پیکره‌بندی شده‌اند را بر عهده دارد.

 

این تابع وظیفه تغییر وضعیت یک یا چند پین از پورت مشخص که به‌صورت خروجی پیکره‌بندی شده‌اند به صفر یا یک را بر عهده دارد.

تا اینجا با توابع کتابخانه gpio آشنایی مختصری پیدا کردیم ولیکن قرار بر این شد که برای برنامه‌نویسی اصولی تا آنجا که می‌توانیم کمتر از توابع کتابخانه hal استفاده کنیم از این‌رو در ادامه به بررسی رجیسترهای رابط gpio می‌پردازیم.

 

رجیسترهای رابط GPIO میکروکنترلر STM32:

  • رجیسترهای GPIOx->CRH و GPIOx->CRL

دو رجیستر 32 بیتی خواندنی و نوشتنی به همین نام‌ها برای هر پورت میکروکنترلر در نظر گرفته‌شده، که به دلیل اینکه هر پورت در میکروکنترلر STM32 حداکثر 16 بیتی است برای هر پین از پورت، 4 بیت تنظیمات خواهیم داشت که توسط رجیسترهای GPIOx->CRH و GPIOx->CRL  اعمال می‌گردد. در اسامی رجیسترها منظور از x همان نام پورت خاص است که از A شروع می‌شود تا B و C و … برای فهم بیشتر به‌عکس‌های زیر در مورد مشخصات این رجیسترها در رفرنس منوال توجه نمایید:

 

 

نرم‌افزار keil

 

  • رجیستر GPIOx->IDR

این‌یک رجیستر خواندنی 32 بیتی هست که 16 بیت اول آن برای ما قابل‌استفاده است و از طریق این 16 بیت می‌توانیم مقدار لاجیک صفر و یک روی پین‌های پورت موردنظرمان را بخوانیم. توجه داشته باشید اگر پورت موردنظر بجای ورودی به‌صورت خروجی پیکره‌بندی شده باشد در هرلحظه مقدار این رجیستر مساوی مقدار رجیستر GPIOx->ODR خواهد بود. برای فهم بیشتر به‌عکس زیر در مورد مشخصات این رجیستر در رفرنس منوال میکروکنترلر توجه نمایید:

 

نرم‌افزار keil

 

  • رجیستر GPIOx->ODR

این‌یک رجیستر خواندنی و نوشتنی 32 بیتی هست که 16 بیت اول آن برای ما قابل‌استفاده است و از طریق این 16 بیت می‌توانیم مقدار لاجیک صفر و یک روی پین‌های پورت موردنظرمان را تنظیم کنیم. دقیقاً مشابه رجیستر PORT در میکرو کنترولر AVR که هرکدام از بیتها را که صفر کنیم پین متناظر آن صفر منطقی می‌شود و هرکدام از بیتها را که یک نماییم پین متناظر آن یک منطقی خواهد شد. برای فهم بیشتر به‌عکس زیر در مورد مشخصات این رجیستر در رفرنس منوال توجه نمایید:

 

 

  • رجیستر GPIOx->BSRR

این‌یک رجیستر خواندنی و نوشتنی 32 بیتی هست که هر 32 بیت آن برای ما قابل‌استفاده است و از طریق 16 بیت اول می‌توانیم پین یا پین‌های مشخص از پورتمان را یک منطقی و از طریق 16 بیت دوم می‌توانیم پین یا پین‌های مشخص از پورتمان را صفر منطقی کنیم. برای فهم بیشتر مسئله به‌عکس زیر توجه کنید:

 

نرم‌افزار keil

 

کادر قرمزرنگ برای یک کردن پین موردنظر استفاده می‌شود و کادر بنفش‌رنگ برای صفر کردن پین موردنظر استفاده می‌شود. نمونه مثال عملی برای کاربرد این رجیستر را می‌توانیم در متن تابع HAL_GPIO_WritePin هم ببینم برای فهم بیشتر به‌عکس زیر توجه کنید:

 

 

کاربرد این رجیستر خیلی ساده و جالب است در عکس بالا اگر توجه کنید از همان 16 بیت اول برای یک کردن استفاده کرده ولی برای صفر کردن با 16 بیت شیفت دادن به سمت چپ و بیتهای پرارزش‌تر از 16 بیت دوم استفاده می‌کنیم. توجه داشته باشید در اینجا منظور از GPIO_Pin که پین موردنظر را مشخص می‌کند عدد 0 و 1 و 2 و … تا 15 نیست بلکه همان 1 و 2 و 4 و 8 و … تا 32768 است در شکل زیر قضیه قابل‌فهم می‌شود که چطور ماکرو پین‌های مختلف نوشته‌شده‌اند:

 

نرم‌افزار keil

 

  • رجیستر GPIOx->BRR

این یک رجیستر خواندنی و نوشتنی 32 بیتی هست که 16 بیت اول آن برای ما قابل‌استفاده است و از طریق این 16 بیت می‌توانیم مقدار لاجیک صفر و یک روی پین‌های پورت موردنظرمان را صفر کنیم. ما اگر هرکدام از بیتها را یک کنیم پین متناظر آن صفر منطقی خواهد شد. برای فهم بیشتر به‌عکس زیر در مورد مشخصات این رجیستر در رفرنس منوال توجه نمایید:

 

نرم‌افزار keil

 

 

  • رجیستر GPIOx->LCKR

این یک رجیستر خواندنی و نوشتنی 32 بیتی است که 17 بیت اول آن برای ما قابل استفاده است و از طریق 16 بیت اول با یک کردن هر کدام از بیت های مورد نظر، می‌توانیم پیکره بندی پین های متناظر رجیستر شده را قفل کنیم. بعد از این عمل تا زمانی که بیت مورد نظر با آن پین را در رجیستر GPIOx->LCKR  صفر نکنیم، نمی‌توانیم پیکره بندی آن پین ها را عوض کنیم. بیت هفدهم  برای این است که محتویات 16 بیت اول تا زمان ریست شدن میکروکنترلر STM32 قفل شود. برای فهم بیشتر به عکسهای زیر در مورد مشخصات این رجیستر در رفرنس منوال توجه نمائید:

 

نرم‌افزار keil

 

نرم‌افزار keil

 

 

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

حمایت از مهدی عبدالهی

خوشحال میشیم برای تداوم و کیفیت ما رو حمایت کنید.

0 نفر

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

توجه

مهدی عبدالهی
مهدی عبدالهی

کانال میکروالکترونیک @microstm32

دیدگاه ها

23 دیدگاه

  • حمید
    ۹ دی ۱۳۹۹

    سلام. متشکر از سایت خوبتون مختصر و مفید بود واقعا

  • امیرسینا
    ۱۵ شهریور ۱۳۹۹

    سلام. میخواستم بدونم اطلاعات مربوط به رجیسترهای یه میکرو از این سری (مثلا STM32L052R8) رو از کجا میتونم ببینم؟ دیتاشیتش رو دارم ولی بر خلاف AVR و PIC خبری از رجیسترها نیست. اگه میشه زودتر پاسخ بدین یه مقدار عجله دارم. ممنون.

    • Zeus ‌
      زئوس Zeus
      ۱۸ شهریور ۱۳۹۹

      آها شما باید فایل رفرنس منوال این میکروکنترلر رو دانلود کنید از خود سایت st میشه این کار رو انجام داد.

      • امیرسینا
        ۱۸ شهریور ۱۳۹۹

        به قول شاعر «آمدی جانم به قربانت ولی حالا چرا؟»
        جواب شما حکم نوشدارو بعد از «درمان» سهراب رو داشت. چون اون موردی که براش عجله داشتم خودش رفع شد! 🙂
        به هر حال، ممنون از جوابتون. این مورد رو پیدا کردم که به سری STM32L0x1 مربوط میشه:
        https://www.st.com/resource/en/reference_manual/dm00108282-ultra-low-power-stm32l0x1-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
        ولی برای STM32L052 یا در کل STM32L0x2 چیزی پیدا نکردم. همون pdf بالا برای L052 جواب میده یا باید دنبال چیز دیگه‌ای بگردم؟
        یه سوال دیگه: برد خاصی بر پایه STM32L052 وجود داره؟ منظورم مثلا برد آموزشی یا development board و اینجور چیزاست. یه نفر یه چیزایی در مورد چنین بردی بهم گفته بود.

        • Zeus ‌
          زئوس Zeus
          ۱۹ شهریور ۱۳۹۹

          سلام
          باید بگم خوشحالم که مشکلتون حل شده :)، برای دیر شدن پاسخ پوزش میخوام – این روزها درگیری ها خیلی زیاد شده
          + الان من نگاه کردم شرکت st این مدل میکرویی که میگید رو تولید نکرده !!! تو نرم افزار stmcube چک کردم سری ۵۱ رو داره ولی سری ۵۲ چیزی نیست :/
          فکر کنم یه جای رو اشتباه میکنید

          • امیرسینا
            ۱۹ شهریور ۱۳۹۹

            من الان توی STM32CubeMX دارم ۸ تا میکرو از سری STM32L052 میبینم! مثلا STM32L052T8. از قسمت Line گزینه‌ی STM32L0x2 رو تیک بزنید.
            فایل‌های مربوطه رو توی Cube پیدا کردم. بعد انتخاب یکی از میکروهای این سری (همون 52T8) رفتم قسمت Docs & Resources این دو تا لینک به دردم خورد:
            https://www.st.com/resource/en/datasheet/stm32l052t8.pdf
            این همون دیتاشیته که قبلا دیدم و رجیسترها رو نداشت.

            https://www.st.com/resource/en/reference_manual/DM00108281-.pdf
            این یکی reference manual برای سری L0x2 هست.

            در مورد اون برد که گفتم چی؟ توی Cube دو تا برد بر پایه L053 دیدم ولی L052 نه!

            0
          • Zeus ‌
            زئوس Zeus
            ۱۹ شهریور ۱۳۹۹

            اها – ببخشید اشتباه از من بود به جای l میزدم f ، اینقدری که با سری جنرال کار کردم ،
            البته هفته پیش سری ۵۱ رو داشتم روش کار میکردم هر کاری میکردم پروگرام نمیشد نگو اونجام اشتباهی stm32f051 رو انتخاب کرده بودم :دی کلی وقت از دست دادم
            سری های l توان مصرفیش خیلی مناسبه
            در مورد برد هم برای من همون یک سری ۵۳ میآد ولی فکر میکنم اینا همخوان باشن – مثلا الان فرق سری ۵۱ و ۵۲ چیه ؟ – برنماه ۵۱ احتمال خیلی زیاد روی سری ۵۲ جواب میده

            0
  • احد
    ۵ مرداد ۱۳۹۹

    سلام
    در برنامه keil با تعریف کردن تابع delay هنگام debug کردن وقتی دیباگر به خط delay میرسه با ارور undefined line number مواجه میشم تو اموزشی که دارم کد رو اجرا میکنه ولی برد وصله و من برد رو ندارم و در محیط سیمولیشن اجرا میکنم.
    ایا امکان داره که تابع delay به سخت افزار نیاز داشته باشه؟
    این در صورتی که وقتی تابع delay رو کامنت یا حذف میکنم، برنامه راحت debug میشه و اون ارور رو نمیده.

    • Zeus ‌
      زئوس Zeus
      ۱۸ شهریور ۱۳۹۹

      اونطور که من متوجه شدم شما برنامه رو دیباگ نمی کنید – سیمولیت میکنید- یعنی از سخت افزار استفاده نمیکنید –
      توی سیمولیت کردن بسته به شکل تاخیری که ایجاد شده (با حلقه بوده یا تایمر) میتونه جواب های غیر دقیقی دریافت کنید و اینو دقیقا نمیدونم راه حلی داره یا نه باید توی سایت keil ببینید.

  • حامد
    ۲۹ خرداد ۱۳۹۹

    با سلام
    ببخشید در شبیه سازی کامپایلر keil قسمت لاجیک آنالایزر هر سیگنال و متغیری را که اضافه میکنم خطا میده و میگه سیگنال شناخته نمی شود.
    می تونید راهنماییم کنید
    با تشکر

  • مهسا
    ۳۰ اردیبهشت ۱۳۹۹

    چطوری میتونیم از تابع write pin برای مقدار دهی همزمان چند پایه استفاده کنیم؟
    مثلا میخام عدد ۳ روی پایه های pa0 تا pa1 قرار بگیره
    چجوری با write pin این کارو بکنیم؟!

    • Zeus ‌
      زئوس Zeus
      ۶ خرداد ۱۳۹۹

      تنها وقتی میتونید از این روش استفاده کنید که پین های مروبطه روی یک gpio باشه.
      با سادگی or کردن بایه هایی که مد نظر دارید.

  • علی عطاران
    ۲۷ بهمن ۱۳۹۸

    سلام و خسته نباشید . مهندس با stm 32 میشه مستقیم با keil کار کرد و نیازی به cube mx نباشه؟

    • Zeus ‌
      زئوس Zeus
      ۳۰ بهمن ۱۳۹۸

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

  • حسین
    ۲۹ آذر ۱۳۹۸

    من همه مراحل رو انجام دیدم ولی
    کد ها generate نمیشه
    البته jtag ها قرمز میشود که شاید به منزله ارور هستش نمیدونم
    که البته این قسمت در نرم افزار من ورژن 4.7.1 کمی با مال شما فرق داره

    • Zeus ‌
      زئوس Zeus
      ۱ دی ۱۳۹۸

      شاید کتابخونه های مورد نیاز رو نتونسته دانلود کنه – متاسفانه st ایران رو تحریم کرده و این مشکل از این جهته که نرم افزارش کار نمیکنه
      با ف… وصل بشید و آپدیت کنید.

  • Ebbex
    ۶ شهریور ۱۳۹۸

    خدا خیرتون بده
    همین

  • reza
    ۲۴ اردیبهشت ۱۳۹۸

    بسیار عالی

  • محمد امین
    ۱۷ اسفند ۱۳۹۷

    با سلام خدمت دوستان
    من بعد از generate کد در cube mx و اجرای برنامه keil صفحه مربوط به pack installer ران می شه وقتی می بندمش ارور زیر ظاهر می شه
    one or more device familly pack device are not peresent
    بعدش چند تا ارور دیگه می اد
    کسی می دونه مشکل از کجاست؟؟؟؟

    • Zeus ‌
      زئوس Zeus
      ۲۱ اسفند ۱۳۹۷

      سلام دوست عزیز ،
      اگر از کیل 5 به بعد استفاده می کنید احتمالا با این خطا مواجه میشید ؛ دلیلش هم اینه که توی این نسخه های شرکت ARM بسته های نصبی برای هر پکیج قرار داده به این صورت حجم فایل نصبی کم میشه و بهینه تر خواهد بود ؛ برای رفع این خطا تنها لازمه پکیج مورد نیاز رو نصب کنید – روی پک منیجر که باز میشه پکیج هایی رو که لازم دارید مثل STM32Fxxx رو نصب کنید CmSIS و…

پر بحث ترین ها

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

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

Zeus ‌ Zeus ‌
  • 2 سال پیش

راه اندازی LCD گرافیکی Nokia 1661 و دانلود کتابخانه آن

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

Zeus ‌ Zeus ‌
  • 4 سال پیش

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

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

Zeus ‌ Zeus ‌
  • 5 سال پیش

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

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

Zeus ‌ Zeus ‌
  • 5 سال پیش

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

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

Zeus ‌ Zeus ‌
  • 2 سال پیش

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

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

Zeus ‌ Zeus ‌
  • 11 ماه پیش

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

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

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

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

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

Zeus ‌ Zeus ‌
  • 3 سال پیش

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

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

Mahdi.h   Mahdi.h  
  • 3 سال پیش

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

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

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

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