ch
Feedback
برق و الکترونیک *دانلود پروژه رایگان

برق و الکترونیک *دانلود پروژه رایگان

前往频道在 Telegram

بسمه تعالی دانلود پروژه و پی سی بی رایگان

显示更多
2 293
订阅者
无数据24 小时
无数据7 天
+330 天
帖子存档
### ۱۰. وقفه TIMEOUT (Timeout) - کاربرد: زمانی فعال می‌شود که زمان انتظار برای یک رویداد (مثل دریافت داده) بیش از حد مجاز طول بکشد. - پیکربندی:
  HAL_I2C_EnableListen_IT(&hi2c1); // فعال‌سازی Timeout
  
--- ### جمع‌بندی: - ترتیب اولویت وقفه‌ها: خطاها (مثل BERR، ARLO، AF) > وقفه‌های معمولی (مثل ADDR، STOPF، RxNE، TxE). - فعال‌سازی وقفه‌ها: با استفاده از توابع __HAL_I2C_ENABLE_IT() یا HAL_I2C_Enable_IT(). - پاک کردن فلگ وقفه: همیشه پس از پردازش وقفه، فلگ مربوطه را با __HAL_I2C_CLEAR_FLAG() پاک کنید. --- ### مثال کاربردی (ترکیب چند وقفه):
void I2C1_EV_IRQHandler(void) {
    // پردازش خطاها
    if (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_BERR)) {
        __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_BERR);
        // خطای باس
    }

    if (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_AF)) {
        __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_AF);
        // خطای NACK
    }

    // پردازش وقفه STOP
    if (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_STOPF)) {
        __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_STOPF);
        // پایان انتقال
    }

    HAL_I2C_EV_IRQHandler(&hi2c1); // پردازش باقی وقفه‌ها توسط HAL
}
با مدیریت صحیح این وقفه‌ها، می‌توانید ارتباطات I2C را با قابلیت اطمینان بالا و مصرف بهینه منابع پیاده‌سازی کنید!

(Due to technical issues, the search service is temporarily unavailable.) در رابط I2C میکروکنترلرهای STM32، وقفه‌های متعددی وجود دارد که برای مدیریت ارتباطات، تشخیص رویدادها و خطاها استفاده می‌شوند. در ادامه، مهم‌ترین وقفه‌های I2C و عملکرد هر کدام را به زبان ساده توضیح می‌دهم: --- ### ۱. وقفه ADDR (Address Match) - کاربرد: زمانی فعال می‌شود که آدرس ارسالی توسط Master با آدرس Slave (میکروکنترلر) مطابقت داشته باشد. - مثال:
  void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) {
      // TransferDirection: جهت انتقال (ارسال یا دریافت)
  }
  
- نکات: - برای حالت Slave استفاده می‌شود. - می‌تواند جهت انتقال داده (I2C_DIRECTION_TRANSMIT یا I2C_DIRECTION_RECEIVE) را مشخص کند. --- ### ۲. وقفه RxNE (Receive Data Register Not Empty) - کاربرد: زمانی فعال می‌شود که داده‌ای در ثبات دریافت (RXDR) موجود باشد. - مثال:
  void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) {
      // پردازش داده دریافتی
  }
  
- نکات: - در حالت Slave یا Master (دریافت) استفاده می‌شود. - معمولاً با DMA ترکیب می‌شود تا داده مستقیماً به حافظه منتقل شود. --- ### ۳. وقفه TxE (Transmit Data Register Empty) - کاربرد: زمانی فعال می‌شود که ثبات انتقال (TXDR) خالی باشد و آماده دریافت داده جدید. - مثال:
  void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c) {
      // آماده‌سازی داده بعدی برای ارسال
  }
  
- نکات: - در حالت Slave یا Master (ارسال) استفاده می‌شود. - اگر داده جدیدی در ثبات TXDR نوشته نشود، خطای Underrun رخ می‌دهد. --- ### ۴. وقفه BTF (Byte Transfer Finished) - کاربرد: زمانی فعال می‌شود که انتقال یک بایت کامل انجام شده باشد. - مثال:
  if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF)) {
      __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_BTF);
      // انجام عملیات پس از انتقال بایت
  }
  
- نکات: - برای همگام‌سازی دقیق انتقال داده مفید است. - معمولاً در ارتباطات بدون DMA استفاده می‌شود. --- ### ۵. وقفه STOPF (Stop Condition Detected) - کاربرد: زمانی فعال می‌شود که یک شرط توقف (STOP) روی باس I2C تشخیص داده شود. - مثال:
  void I2C1_EV_IRQHandler(void) {
      if (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_STOPF)) {
          __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_STOPF);
          // پایان انتقال داده
      }
  }
  
- نکات: - برای تشخیص پایان انتقال توسط Master استفاده می‌شود. - در پروتکل I2C، Master با ارسال STOP به انتقال پایان می‌دهد. --- ### ۶. وقفه NACK (Not Acknowledge) - کاربرد: زمانی فعال می‌شود که دستگاه مقصد (Slave یا Master) داده را تأیید نکرده باشد (NACK ارسال کند). - مثال:
  if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF)) {
      __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF);
      // مدیریت خطای NACK
  }
  
- نکات: - فلگ این خطا به نام AF (Acknowledge Failure) نیز شناخته می‌شود. - معمولاً نشاندهنده خطای اتصال یا آدرس نامعتبر است. --- ### ۷. وقفه BERR (Bus Error) - کاربرد: زمانی فعال می‌شود که یک خطای باس (مانند سیگنال ناخواسته روی SDA/SCL) تشخیص داده شود. - مثال:
  if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BERR)) {
      __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_BERR);
      // مدیریت خطای باس
  }
  
--- ### ۸. وقفه ARLO (Arbitration Lost) - کاربرد: زمانی فعال می‌شود که میکروکنترلر در آربیتراسیون باس شکست بخورد (دو Master همزمان بخواهند داده ارسال کنند). - مثال:
  if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_ARLO)) {
      __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ARLO);
      // مدیریت خطای آربیتراسیون
  }
  
--- ### ۹. وقفه OVR (Overrun/Underrun) - کاربرد: - Overrun: داده جدید دریافت شده، قبل از خواندن داده قبلی (دریافت). - Underrun: داده جدیدی برای ارسال نوشته نشده، در حالی که باس منتظر است (ارسال). - مثال:
  if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_OVR)) {
      __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_OVR);
      // مدیریت خطای Overrun/Underrun
  }
  
---

داخل همین سایت بگردید خیلی پروژه های رایگان عالی گذاشته

این نرم افزار را از اینجا میتونید دانلود کنید

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

در برنامهنویسی میکروکنترلرهای STM32، __attribute__ کاربردهای مختلفی دارد. در اینجا چند مثال رایج و کاربردی با توضیحات کامل ارائه میشود: --- ### ۱. تراز کردن دادهها برای DMA (Alignment) #### مثال: انتقال داده از حافظه به DAC با استفاده از DMA
uint16_t dac_buffer[256] __attribute__((aligned(4))); // تراز ۴ بایتی
- هدف: DMA در بسیاری از میکروکنترلرهای STM32 نیاز دارد آدرس شروع بافر حافظه مضربی از ۴ باشد (۳۲ بیتی). اگر تراز رعایت نشود، DMA ممکن است بهدرستی کار نکند یا دادهها خراب شوند. - کاربرد: - تولید سیگنالهای آنالوگ با DAC. - نمونهبرداری از ADC با DMA. - نکته: در STM32CubeIDE، اگر از توابع HAL مانند HAL_DAC_Start_DMA() استفاده کنید، نیاز به تراز دستی دادهها دارید. --- ### ۲. قراردادن متغیر در حافظه خاص (مثلاً CCM RAM) #### مثال: ذخیرهسازی داده در حافظه سریع CCM برای پردازش
uint8_t high_speed_buffer[1024] __attribute__((section(".ccmram")));
- هدف: - حافظه CCM (Core Coupled Memory) در برخی STM32ها (مثل STM32F4/H7) بهصورت مستقیم توسط پردازنده قابل دسترسی است و سرعت بالاتری دارد. - مناسب برای دادههای حیاتی یا توابع وقفه. - تنظیمات Linker Script: باید مطمئن شوید بخش .ccmram در فایل STM32XXXX_FLASH.ld تعریف شده است. --- ### ۳. تعریف هندلر وقفه (Interrupt Handler) #### مثال: تابع سرویس وقفه تایمر
void __attribute__((interrupt("IRQ"))) TIM2_IRQHandler(void) {
    HAL_TIM_IRQHandler(&htim2);
}
- هدف: - اطمینان از اینکه کامپایلر کد مناسب برای ذخیره/بازیابی رجیسترها در ورود/خروج از وقفه را ایجاد میکند. - عدم استفاده از این Attribute ممکن است باعث Crash سیستم شود. - تفاوت با توابع عادی: توابع وقفه باید با __attribute__((interrupt)) مشخص شوند تا از صحت اجرا اطمینان حاصل شود. --- ### ۴. ساختارهای فشرده (Packed Structures) #### مثال: ارسال داده از طریق UART بدون Padding
typedef struct __attribute__((packed)) {
    uint8_t header;
    uint32_t value;
    uint16_t checksum;
} SensorData;
- هدف: - حذف Padding بین فیلدهای ساختار برای جلوگیری از افزایش حجم داده. - مناسب برای پروتکلهای ارتباطی (مثل UART، SPI یا Ethernet). - مشکل بدون `packed`: کامپایلر ممکن است بین فیلدها Padding اضافه کند و ساختار ۸ بایت (۱ + ۴ + ۲ + ۱ Padding) شود. --- ### ۵. جلوگیری از حذف تابع توسط Linker #### مثال: تابعی که در کد صریحاً فراخوانی نمیشود
void __attribute__((used)) SystemBootLogger(void) {
    // لاگ اطلاعات بوت سیستم
}
- هدف: - کامپایلر ممکن است توابعی که مستقیماً فراخوانی نمیشوند را حذف کند. با `used`، تابع حتی اگر استفاده نشده باشد، حفظ میشود. - مناسب برای توابع دیباگ یا Hookهای سیستم. --- ### ۶. بهینهسازی توابع بحرانی #### مثال: تابع پردازش سیگنال با بهینهسازی سطح بالا
void __attribute__((optimize("O3"))) ProcessSignal(int16_t* data) {
    // پردازش سنگین سیگنال
}
- هدف: - اعمال بهینهسازی خاص روی یک تابع (مثلاً `O3` برای حداکثر بهینهسازی یا Os برای بهینهسازی اندازه). - مناسب برای توابعی که نیاز به اجرای سریع دارند. - هشدار: بهینهسازی بالا ممکن است باعث افزایش مصرف حافظه یا مشکلات زمانبندی شود. --- ### ۷. ذخیرهسازی ثابتها در Flash بهجای RAM #### مثال: جدول لوکآپ (Lookup Table) بزرگ
const uint32_t lookup_table[1024] __attribute__((section(".rodata")));
- هدف: - ذخیرهسازی دادههای ثابت (مثل جداول ریاضی) در حافظه Flash برای آزادسازی RAM. - پیشفرضاً، ثابتها در STM32 در Flash ذخیره میشوند، اما این Attribute برای کنترل دقیقتر مفید است. --- ### ۸. تعریف Weak Symbol برای Override توابع #### مثال: تعریف Weak Callback برای HAL
void __attribute__((weak)) HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    // پیادهسازی پیشفرض (میتوان در فایل اصلی Override کرد)
}
- هدف: - ایجاد توابعی که میتوانند در فایلهای دیگر بازنویسی شوند (مفید برای کتابخانههای HAL). - اگر تابعی با همین نام در جای دیگری تعریف شود، نسخه غیر-Weak جایگزین میشود. --- ### نکات پایانی: - وابستگی به کامپایلر: __attribute__ مختص کامپایلرهای GCC/Clang است و در کامپایلرهای دیگر (مثل IAR) ممکن است با سینتکس متفاوتی (#pragma) پیادهسازی شود. - جایگزینهای استاندارد: در C++11 میتوان از alignas یا [[gnu::packed]] استفاده کرد، اما در محیطهای Embedded معمولاً از __attribute__ استفاده میشود. - مستندات سختافزار: قبل از استفاده، دیتاشیت STM32 و راهنمای کامپایلر (مثلاً ARM GCC) را بررسی کنید.

استفاده از Double Buffer Mode در DMA امکانات و مزایای مهمی را فراهم میکند که برای برنامههای نیازمند انتقال دادههای پیوسته و بلادرنگ حیاتی است. در اینجا فواید اصلی این روش توضیح داده شده است: --- ### ۱. جلوگیری از تداخل داده (Data Corruption): - مشکل: در حالت عادی (بدون Double Buffer)، وقتی DMA در حال انتقال داده از یک بافر است، اگر CPU همزمان بخواهد همان بافر را پردازش یا تغییر دهد، ممکن است دادهها خراب شوند. - راهحل: با Double Buffer، دو بافر جداگانه دارید: - هنگامی که DMA در حال انتقال داده از Buffer 1 است، CPU میتواند Buffer 2 را پردازش کند. - پس از اتمام انتقال، DMA بهصورت خودکار به Buffer 2 سوئیچ میکند و CPU میتواند Buffer 1 را آپدیت کند. --- ### ۲. کاهش تأخیر (Latency) و افزایش کارایی: - بدون Double Buffer: پس از هر انتقال DMA، باید صبر کنید تا CPU دادهها را پردازش کند و سپس بافر جدیدی را برای DMA بارگذاری کند. این باعث ایجاد وقفه در انتقال پیوسته داده میشود. - با Double Buffer: انتقال دادهها بهصورت چرخشی بین دو بافر انجام میشود، بنابراین هیچ وقفهای برای بارگذاری مجدد بافر وجود ندارد. این برای سیستمهای بلادرنگ (Real-Time) مانند پردازش صدا، تصویر یا کنترل موتور ضروری است. --- ### ۳. مدیریت سادهتر پردازش داده: - مثال: فرض کنید از ADC برای نمونهبرداری پیوسته از یک سیگنال استفاده میکنید: - DMA نمونهها را در Buffer 1 ذخیره میکند. - هنگامی که Buffer 1 پر شد، DMA بهصورت خودکار به Buffer 2 سوئیچ میکند. - در همین حین، CPU میتواند دادههای Buffer 1 را فیلتر، تحلیل یا ذخیره کند. - این چرخه بهصورت پیوسته تکرار میشود. --- ### ۴. کاهش بار پردازشی CPU: - در این روش، CPU فقط زمانی درگیر میشود که نیاز به پردازش دادههای یک بافر کامل داشته باشد. - DMA بهصورت مستقل و بدون نیاز به دخالت CPU، دادهها را بین بافرها و Peripheral جابهجا میکند. --- ### ۵. کاربردهای کلیدی: - پردازش سیگنالهای صوتی/تصویری: انتقال پیوسته دادههای میکروفون، اسپیکر یا دوربین بدون وقفه. - کنترل موتور و سیستمهای بلادرنگ: خواندن موقعیت انکودر یا ارسال PWM با زمانبندی دقیق. - ذخیرهسازی دادهها: نمونهبرداری از سنسورها و ذخیره دادهها در حافظه خارجی (مثلاً SD Card). - ارتباطات سریال پرسرعت: انتقال دادههای UART، SPI یا Ethernet بدون از دست رفتن فریمها. --- ### مثال عملی: سیستم ضبط صدا - Buffer 1: در حال ضبط صدا از میکروفون via DMA. - Buffer 2: در حال پردازش (فشردهسازی) دادههای ضبطشده توسط CPU. - هنگام پر شدن Buffer 1، DMA به Buffer 2 سوئیچ میکند و CPU شروع به پردازش Buffer 1 میکند. - این چرخه تضمین میکند که هیچ نمونه صوتی از دست نمیرود! --- ### جمعبندی: - Double Buffer مانند داشتن دو خط تولید در یک کارخانه است: - وقتی یک خط در حال تولید است، خط دیگر در حال بستهبندی محصولات نهایی است. - این روش برای سیستمهایی که نیاز به پردازش موازی و انتقال بدون وقفه داده دارند، ضروری است. - با استفاده از این تکنیک، همزمانی (Concurrency) بین DMA و CPU بهینه میشود و از مشکلاتی مانند از دست رفتن دادهها یا تأخیر جلوگیری میکند.

کلمه `__attribute__` یک ویژگی (Attribute) خاص در کامپایلرهای خانواده GCC/Clang است که به برنامهنویس امکان میدهد ویژگیهای اضافی برای متغیرها، توابع، ساختارها و غیره تعیین کند. این دستور یک ساختار غیراستاندارد است و مختص کامپایلرهایی است که از سینتکس GCC پشتیبانی میکنند (مثل ARM GCC برای برنامهنویسی میکروکنترلرها). --- ### هدف اصلی `__attribute__`: این دستور برای بهینهسازی یا تعیین رفتار خاص در سطح سختافزار/کامپایلر استفاده میشود. مثالهایی از کاربردها: - تنظیم تراز حافظه (Alignment) برای متغیرها. - تعیین محل ذخیرهسازی یک متغیر در حافظه (مثلاً در SRAM یا Flash). - تغییر رفتار توابع (مثلاً فعالسازی بهینهسازی خاص). - کنترل اندازه یا چیدمان ساختارها در حافظه. --- ### مثال کاربردی از `__attribute__((aligned(n)))`: در کد قبلی از __attribute__((aligned(4))) استفاده شد تا مطمئن شویم آدرس شروع آرایه در حافظه، مضربی از ۴ باشد:
uint16_t buffer1[BUFFER_SIZE] __attribute__((aligned(4)));
- چرا این مهم است؟ برخی سختافزارها (مثل DMA، پردازندههای ARM یا کنترلرهای حافظه) برای دسترسی به دادهها نیاز دارند آدرس حافظه ترازشده (Aligned) باشد. اگر دادهها تراز نباشند، ممکن است سیستم crash کند یا عملکرد کاهش یابد. --- ### انواع رایج `__attribute__` در برنامهنویسی میکروکنترلرها: #### ۱. تراز حافظه (Alignment):
   // تراز ۴ بایتی برای متغیر
   int my_var __attribute__((aligned(4)));
   
#### ۲. ذخیرهسازی در حافظه خاص (مثلاً SRAM سریع):
   // ذخیرهسازی متغیر در بخش ".fast_data" از حافظه
   uint8_t buffer[100] __attribute__((section(".fast_data")));
   
#### ۳. بهینهسازی توابع:
   // عدم بهینهسازی این تابع توسط کامپایلر
   void __attribute__((optimize("O0"))) my_func() { ... }
   
#### ۴. ساختارهای فشرده (Packed Structures):
   // حذف padding بین فیلدهای ساختار
   struct __attribute__((packed)) {
       uint8_t a;
       uint32_t b;
   } my_struct;
   
#### ۵. توابع وقفه (Interrupt Handlers):
   // تعریف تابع به عنوان هندلر وقفه
   void __attribute__((interrupt("IRQ"))) TIM2_IRQHandler() { ... }
   
--- ### چرا از `__attribute__` استفاده میشود؟ - کنترل سطح پایین: در برنامهنویسی سیستمهای تعبیهشده، گاهی نیاز است رفتار کامپایلر یا سختافزار را دقیقتر کنترل کنید. - بهینهسازی: بهبود عملکرد یا مصرف حافظه. - سازگاری با سختافزار: الزامات خاص سختافزاری (مثل DMA یا دسترسی به رجیسترها). --- ### تفاوت با استانداردهای C/C++: - __attribute__ بخشی از استاندارد C/C++ نیست و فقط در کامپایلرهای سازگار با GCC/Clang کار میکند. - برای جایگزینهای استاندارد، میتوان از #pragma یا کلمات کلیدی مثل alignas (در C++11) استفاده کرد:
  // C++11
  alignas(4) uint8_t buffer[100];
  
--- ### نکات مهم: - استفاده از __attribute__ ممکن است پورتابل بودن (قابلیت انتقال) کد را کاهش دهد. - حتماً به دیتاشیت سختافزار مراجعه کنید تا بدانید چه ویژگیهایی نیاز است. - در صورت استفاده نادرست، ممکن است برنامه با خطاهای عجیب مواجه شود (مثلاً خطای Alignment Fault در ARM).



GregorianDate hijri_to_gregorian(HijriDate hDate) {
    uint32_t jdn = hijri_to_jdn(hDate);
    return jdn_to_gregorian(jdn);
}

// تابع ارسال رشته از طریق UART
void send_uart(char *str) {
    HAL_UART_Transmit(&huart2, (uint8_t*)str, strlen(str), HAL_MAX_DELAY);
}

// بازنویسی تابع `printf` برای UART
int _write(int file, char *ptr, int len) {
    HAL_UART_Transmit(&huart2, (uint8_t*)ptr, len, HAL_MAX_DELAY);
    return len;
}

// تابع اصلی
int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_USART2_UART_Init();

    // مثال ۱: تبدیل میلادی به هجری
    GregorianDate gregDate = {2023, 10, 7}; // تاریخ میلادی ورودی
    HijriDate hijriDate = gregorian_to_hijri(gregDate);
    printf("Gregorian: %04u-%02u-%02u => Hijri: %04u-%02u-%02u\r\n",
           gregDate.year, gregDate.month, gregDate.day,
           hijriDate.year, hijriDate.month, hijriDate.day);

    // مثال ۲: تبدیل هجری به میلادی
    HijriDate hijriInput = {1445, 3, 21}; // تاریخ هجری ورودی
    GregorianDate gregResult = hijri_to_gregorian(hijriInput);
    printf("Hijri: %04u-%02u-%02u => Gregorian: %04u-%02u-%02u\r\n",
           hijriInput.year, hijriInput.month, hijriInput.day,
           gregResult.year, gregResult.month, gregResult.day);

    while (1) {}
}

// ------------ توابع راه‌اندازی HAL ------------
void SystemClock_Config(void) {
    // پیکربندی کلاک (متناسب با MCU شما)
}

void MX_USART2_UART_Init(void) {
    huart2.Instance = USART2;
    huart2.Init.BaudRate = 115200;
    huart2.Init.WordLength = UART_WORDLENGTH_8B;
    huart2.Init.StopBits = UART_STOPBITS_1;
    huart2.Init.Parity = UART_PARITY_NONE;
    huart2.Init.Mode = UART_MODE_TX_RX;
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    HAL_UART_Init(&huart2);
}
--- ### مراحل راه‌اندازی در STM32CubeIDE: 1. ایجاد پروژه جدید: - میکروکنترلر STM32 مورد نظر خود را انتخاب کنید (مثلاً STM32F103C8T6). - در CubeMX**، پین `USART2_TX` را به یک پین مناسب (مثلاً PA2) و `USART2_RX` را به PA3 وصل کنید. 2. **پیکربندی UART: - در تب Connectivity**، `USART2` را در حالت **Asynchronous فعال کنید. - Baud Rate را روی 115200 تنظیم کنید. 3. پیکربندی کلاک: - در تب Clock Configuration**، کلاک سیستم را متناسب با میکروکنترلر خود تنظیم کنید (مثلاً 72 MHz برای STM32F1). 4. **تولید کد: - پروژه را در STM32CubeIDE تولید کنید و کدهای بالا را در main.c جایگذاری کنید. 5. اتصال به کامپیوتر: - میکروکنترلر را از طریق USB به کامپیوتر وصل کنید. - از یک ترمینال سریال (مثل Putty یا Tera Term) با Baud Rate 115200 برای مشاهده خروجی استفاده کنید. --- ### خروجی مورد انتظار:
Gregorian: 2023-10-07 => Hijri: 1445-03-21
Hijri: 1445-03-21 => Gregorian: 2023-10-07
--- ### نکات مهم: - دقت: این الگوریتم یک تقریب است و ممکن است تا ۱-۲ روز اختلاف داشته باشد. - بهینه‌سازی: برای میکروکنترلرهای ضعیفتر، محاسبات را ساده کنید یا از Lookup Tables استفاده کنید. - پیکربندی UART: مطمئن شوید پینهای USART2 به درستی تنظیم شدهاند. - تست: تاریخهای مختلف را برای بررسی صحت تبدیل آزمایش کنید.