برق و الکترونیک *دانلود پروژه رایگان
2.29K subscribers
784 photos
96 videos
350 files
642 links
بسمه تعالی
دانلود پروژه و پی سی بی رایگان
Download Telegram
🟠 سعدبن‌عبداللَّه قمی حدیثی طولانی از امام مهدي عجل الله تعالی فرجه الشریف نقل می‌کند و گوید: از امام صاحب‌الزمان صلوات الله علیه[که آن وقت در دامن پدر نشسته بودند] پرسیدم: به چه دلیل مردم نمی‌توانند برای خود امام انتخاب کنند؟ فرمودند: امام خوب یا بد؟ گفتم: امام خوب و شایسته. فرمودند: امکان دارد انتخابی که می‌کنند به جای خوب بد از کار درآید با اینکه هیچ‌کس اطّلاع از دل دیگری ندارد که چه چیز به خاطرش می‌گذرد، فکر خوب یا فکر بد؟ گفتم: آری ممکن است. فرمودند: همین موجب نداشتن چنین اختیاری است که با دلیلی برای تو توجیه کردم که عقلت بپذیرد. فرمودند: بگو ببینم پیامبرانی که خداوند آن‌ها را برگزیده و کتاب آسمانی بر آن‌ها نازل‌کرده و ایشان را به وحی ممتاز نموده و عصمت بخشیده چون برجسته‌ترین افراد مردم‌اند و از همه بهتر می‌توانند انتخاب نمایند که از جمله‌ی این پیامبران موسی و عیسی علیهما السلام نیز هستند، اگر به ایشان اختیار بدهند با توجه به کمال عقل و دانشی که این دو داشتند آیا ممکن است انتخاب آن‌ها در مورد کسی که خیال می‌کردند مؤمن است منافق از کار در آید؟ گفتم: نه. فرمودند: همین موسی علیه السلام با کمال عقل و دانشی که داشت و به او وحی شد: از میان قوم خود برای میقات خدا هفتادنفر را انتخاب کن. و او با اینکه یقین داشت منتخبان او مؤمن و مخلص هستند این انتخاب بر خلاف تصوّر او، بر منافقین قرارگرفت. خداوند در این آیه به همین مطلب اشاره می‌کند: موسی از قوم خود، هفتاد تن از مردان را برای میعادگاه ما برگزید ... (اعراف/۱۵۵)؛ وقتی انتخاب پیامبری که خدا او را برگزیده بر شخص فاسدی قرار گیرد با اینکه او خیال می‌کرد صالح است می‌فهمیم اجازه‌ی انتخاب فقط به کسی داده شده که از راز دلها و افکار پنهان و آینده‌ی اشخاص خبر دارد، داده شده، با این حال انتخاب مهاجرین و انصار چه اهمیتی دارد وقتی که انتخاب بهترین پیامبران وقتی قصد انتخاب صالحان را داشتند، اهل فساد از آب درآمد؟!

📚تفسیر اهل بیت علیهم السلام ج۱۱، ص۲۶۰
📚الإحتجاج؛ ج۲، ص۴۶۴
یک تابع در زبان C مینویسیم که دو رشته را دریافت کرده و تعداد تکرارهای رشته دوم در رشته اول و موقعیتهای آنها را مشخص میکند. در این راهحل، از الگوریتم جستجوی ساده برای بررسی زیررشتهها استفاده میشود و موقعیتها با استفاده از حافظه پویا ذخیره میشوند.

### کد نهایی

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int find_substrings(const char *str1, const char *str2, int **positions) {
if (str1 == NULL || str2 == NULL) {
*positions = NULL;
return 0;
}

int len1 = strlen(str1);
int len2 = strlen(str2);
int count = 0;
int i, j;

// بررسی شرایطی که زیررشته نمیتواند وجود داشته باشد
if (len2 == 0 || len1 < len2) {
*positions = NULL;
return 0;
}

// حداکثر تعداد ممکن موقعیتها
int max_positions = len1 - len2 + 1;
*positions = (int *)malloc(max_positions * sizeof(int));
if (*positions == NULL) {
return -1; // خطای تخصیص حافظه
}

// بررسی هر موقعیت ممکن در رشته اول
for (i = 0; i <= len1 - len2; i++) {
for (j = 0; j < len2; j++) {
if (str1[i + j] != str2[j]) {
break; // عدم تطابق، بررسی موقعیت بعدی
}
}
if (j == len2) { // اگر تمام کاراکترها مطابقت داشتند
(*positions)[count++] = i;
}
}

// تنظیم مجدد اندازه آرایه به تعداد واقعی موقعیتهای یافت شده
if (count == 0) {
free(*positions);
*positions = NULL;
} else {
int *temp = (int *)realloc(*positions, count * sizeof(int));
if (temp != NULL) {
*positions = temp;
}
}

return count;
}

// مثال استفاده از تابع
int main() {
const char *str1 = "ababa";
const char *str2 = "aba";
int *positions;
int count = find_substrings(str1, str2, &positions);

if (count > 0) {
printf("تعداد تکرارها: %d\nموقعیتها: ", count);
for (int i = 0; i < count; i++) {
printf("%d ", positions[i]);
}
printf("\n");
free(positions);
} else {
printf("زیررشته یافت نشد.\n");
}

return 0;
}

### شرح کد

1. بررسی ورودیهای نامعتبر: اگر هر یک از رشتههای ورودی NULL باشند یا طول رشته دوم از رشته اول بیشتر باشد، تابع بلافاصله خروجی را صفر برمیگرداند.

2. تخصیص حافظه پویا: آرایهای برای ذخیره موقعیتهای احتمالی با حداکثر اندازه ممکن (تفاضل طول دو رشته به علاوه یک) ایجاد میشود.

3. جستجوی زیررشته: با استفاده از دو حلقه تو در تو، هر موقعیت در رشته اول بررسی میشود تا مشخص شود آیا زیررشته مورد نظر از آنجا شروع میشود یا خیر.

4. ذخیره موقعیتها: در صورت یافتن زیررشته، موقعیت آن در آرایه ذخیره شده و شمارنده افزایش مییابد.

5. تنظیم مجدد اندازه آرایه: پس از اتمام جستجو، اگر موقعیتی یافت شده باشد، اندازه آرایه به تعداد واقعی موقعیتها کاهش مییابد. در غیر این صورت، حافظه آزاد میشود.

6. نمونه استفاده: در تابع main، مثال سادهای از استفاده از تابع نشان داده شده است که نتیجه را چاپ کرده و حافظه اختصاص داده شده را آزاد میکند.

این کد به گونهای طراحی شده است که کارایی مناسبی داشته باشد و شرایط مرزی مختلف را به درستی مدیریت کند.
# قابلیت Oversampling در ADC میکروکنترلر STM32

Oversampling یکی از قابلیت‌های ADC در میکروکنترلرهای STM32 است که باعث افزایش دقت (Resolution) و کاهش نویز (Noise) در تبدیل آنالوگ به دیجیتال می‌شود. این روش با جمع‌آوری چندین نمونه از سیگنال ورودی و پردازش آن‌ها، باعث افزایش کیفیت داده‌های ADC می‌شود.

---

## ۱. Oversampling چیست و چگونه کار می‌کند؟
در ADC معمولی، هر مقدار ولتاژ آنالوگ تنها یک بار خوانده شده و به مقدار دیجیتال تبدیل می‌شود. در حالی که در Oversampling**، ADC چندین نمونه از ورودی را جمع‌آوری کرده و سپس با اعمال **میانگین‌گیری یا فیلترینگ دیجیتال**، مقدار دقیق‌تری را به‌دست می‌آورد.

**فرآیند کلی به این صورت است:

1. نمونه‌برداری چندگانه: ADC به جای گرفتن یک مقدار، 2^N نمونه از سیگنال را جمع‌آوری می‌کند.
2. جمع کردن مقادیر: تمامی مقادیر نمونه‌برداری شده جمع می‌شوند.
3. انتخاب روش پردازش: این داده‌ها می‌توانند به‌صورت جمع ساده (Accumulation) ذخیره شوند یا با تقسیم بر یک مقدار توان ۲ (Shifting Right) پردازش شده و مقدار میانگین گرفته شود.
4. افزایش دقت مؤثر (Effective Resolution): با افزایش تعداد نمونه‌ها، دقت مؤثر ADC تا 16 بیت می‌تواند افزایش یابد، حتی اگر ADC اصلی 12 بیتی باشد.

---

## ۲. مزایای استفاده از Oversampling
افزایش دقت موثر (Effective Resolution)
کاهش نویز تصادفی (Random Noise Reduction)
افزایش کیفیت اندازه‌گیری برای سیگنال‌های کم‌دامنه
ایده‌آل برای کاربردهای حساس مانند پردازش سیگنال‌های حسگرها

---

## ۳. نحوه استفاده از Oversampling در STM32 (HAL Library)
### ۱) مقداردهی اولیه ADC
ابتدا باید ADC را پیکربندی کنیم و قابلیت Oversampling را فعال کنیم.

ADC_HandleTypeDef hadc1;

void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};

/** تنظیمات اولیه ADC **/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B; // دقت اصلی 12 بیت
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
HAL_ADC_Init(&hadc1);

/** تنظیمات کانال ADC **/
sConfig.Channel = ADC_CHANNEL_0; // کانال 0 برای نمونه‌برداری
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}


---

### ۲) فعال‌سازی Oversampling
برای فعال کردن Oversampling در STM32، از ساختار ADC_OversamplingTypeDef استفاده می‌شود.

void Configure_ADC_Oversampling(void) {
ADC_OversamplingTypeDef sOversamplingConfig = {0};

sOversamplingConfig.OversamplingMode = ENABLE; // فعال‌سازی Oversampling
sOversamplingConfig.RightBitShift = ADC_RIGHTBITSHIFT_4; // تقسیم مقدار کل به 16 (۲^۴)
sOversamplingConfig.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER; // شروع با یک تریگر
sOversamplingConfig.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE; // ادامه تبدیل‌ها

sOversamplingConfig.OversamplingRatio = ADC_OVERSAMPLING_RATIO_16; // جمع‌آوری 16 نمونه

HAL_ADCEx_OversamplingConfig(&hadc1, &sOversamplingConfig);
}


> نکته:
> - RightBitShift تعیین می‌کند که داده‌های جمع‌شده چند بیت به راست شیفت شوند تا مقدار میانگین گرفته شود.
> - OversamplingRatio مشخص می‌کند که چند نمونه برای افزایش دقت جمع‌آوری شود. مقدار 16 به معنای جمع‌آوری 16 نمونه پشت سر هم است.
> - TriggeredMode تعیین می‌کند که همه نمونه‌ها در یک تریگر گرفته شوند یا هر نمونه با یک تریگر جداگانه انجام شود.

---

### ۳) شروع تبدیل ADC
پس از تنظیم ADC و Oversampling، باید تبدیل آنالوگ به دیجیتال را شروع کنیم.

void Start_ADC(void) {
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint32_t adc_value = HAL_ADC_GetValue(&hadc1);

printf("مقدار ADC با Oversampling: %lu\n", adc_value);
}


---
## ۴. افزایش دقت مؤثر ADC با Oversampling
با توجه به نسبت Oversampling Ratio**، دقت ADC به صورت زیر افزایش پیدا می‌کند:

| **Oversampling Ratio
| افزایش دقت مؤثر |
|------------------|-----------------|
| 2 | +0.5 بیت |
| 4 | +1 بیت |
| 8 | +1.5 بیت |
| 16 | +2 بیت |
| 32 | +2.5 بیت |
| 64 | +3 بیت |
| 128 | +3.5 بیت |
| 256 | +4 بیت |

> مثال:
> اگر ADC اصلی 12 بیتی باشد و Oversampling Ratio = 16 انتخاب شود، دقت مؤثر آن 14 بیت (12+2) خواهد شد.

---

## ۵. کاربردهای Oversampling
سنجش سنسورهای کم‌دامنه و حساس به نویز (مانند ترموکوپل و سنسورهای فشار)
بهبود دقت داده‌های ADC بدون نیاز به سخت‌افزار اضافی
پردازش سیگنال در تجهیزات پزشکی و صنعتی
فیلترینگ نویز در سیگنال‌های کم‌قدرت

---

## ۶. جمع‌بندی
- Oversampling یک روش پردازش دیجیتال است که با نمونه‌برداری چندگانه و میانگین‌گیری، دقت ADC را افزایش داده و نویز را کاهش می‌دهد.
- در STM32، می‌توان Oversampling Ratio را از 2 تا 256 تنظیم کرد.
- با استفاده از Oversampling + Right Bit Shift می‌توان ADC را تا 16 بیت مؤثر افزایش داد.
- این قابلیت برای پردازش سیگنال‌های دقیق، حذف نویز و افزایش دقت خواندن سنسورها بسیار مفید است.

🚀 اگر سوالی دارید، بپرسید! 😊
## آموزش تایمر LPTIM در میکروکنترلر STM32 با مثال عملی

LPTIM (Low-Power Timer) یک تایمر کم‌مصرف در میکروکنترلرهای STM32 است که برای کاربردهایی که نیاز به مصرف انرژی پایین، دقت بالا و قابلیت اجرای وظایف در حالت کم‌مصرف (Low Power Mode) دارند، طراحی شده است.

---

# ۱. ویژگی‌های اصلی LPTIM
مصرف انرژی بسیار کم (قابل اجرا در حالت Stop و Standby)
دقت بالا برای زمان‌بندی‌های طولانی (32 بیتی)
امکان اجرای PWM، تایمر شمارشی و Encoder Interface
قابلیت ورودی خارجی برای شمارنده و Event Trigger
پشتیبانی از خروجی PWM در مدهای مختلف

موارد استفاده:
🔹 زمان‌بندی‌های طولانی با مصرف انرژی کم
🔹 تولید پالس PWM در حالت کم‌مصرف
🔹 اندازه‌گیری فرکانس و شمارش رویدادها
🔹 کنترل توان و بیدار کردن پردازنده

---

# ۲. نحوه راه‌اندازی و استفاده از LPTIM
در اینجا دو مثال کاربردی را بررسی خواهیم کرد:
1. تولید PWM کم‌مصرف
2. استفاده از LPTIM به‌عنوان شمارنده خارجی

---

## مثال ۱: تولید سیگنال PWM کم‌مصرف
### 🛠 مراحل راه‌اندازی در CubeMX:
1. CubeMX را باز کنید و میکروکنترلر خود را انتخاب کنید.
2. در قسمت Timers**، تایمر **LPTIM1 را در مد PWM Output فعال کنید.
3. یک پین GPIO را به LPTIM1_OUT متصل کنید.
4. مقدار Prescaler را روی DIV1 بگذارید (یا مقدار دلخواه).
5. مقدار Counter Period را تنظیم کنید.
6. روی Generate Code کلیک کنید تا کد HAL تولید شود.

---

### 💻 کدنویسی برای راه‌اندازی LPTIM در مد PWM
در main.c**، ابتدا مقداردهی اولیه LPTIM را انجام دهید:

```c
#include "main.h"

extern LPTIM_HandleTypeDef hlptim1; // تایمر LPTIM1

void LPTIM1_Init(void) {
HAL_LPTIM_PWM_Start(&hlptim1, 1000, 500); // مقدار 1000 دوره و 500 Duty Cycle (50%)
}
```

🔹 تحلیل عملکرد:
**PWM فعال می‌شود
و با دوره 1000 و Duty Cycle = 50% سیگنال تولید می‌کند.
این تایمر حتی در حالت کم‌مصرف هم کار خواهد کرد.

---

### 💡 تغییر مقدار Duty Cycle در زمان اجرا
می‌توان مقدار Duty Cycle را در هنگام اجرا تغییر داد:

void Set_PWM_DutyCycle(uint32_t duty) {
if (duty > 100) duty = 100; // محدود کردن مقدار به 100%
uint32_t compare_value = (1000 * duty) / 100; // محاسبه مقدار مقایسه‌ای
HAL_LPTIM_PWM_SetOnce(&hlptim1, 1000, compare_value);
}


🔹 با این تابع، مقدار PWM را تغییر می‌دهیم تا بتوانیم سیگنال را تنظیم کنیم.

---

## مثال ۲: استفاده از LPTIM به‌عنوان شمارنده خارجی
در این مثال، LPTIM ورودی رویدادهای خارجی (External Events) را می‌شمارد.

### 🛠 تنظیمات در CubeMX:
1. در CubeMX**، تایمر **LPTIM1 را در مد External Counter Mode تنظیم کنید.
2. یک پین ورودی را برای LPTIM1_IN1 تنظیم کنید.
3. کد را تولید کنید و تغییرات زیر را اعمال کنید.

---

### 💻 کدنویسی برای استفاده از LPTIM به‌عنوان شمارنده خارجی
void Start_LPTIM_ExternalCounter(void) {
HAL_LPTIM_Counter_Start(&hlptim1, 0xFFFF); // شمارنده را روی مقدار حداکثر قرار می‌دهیم.
}

uint32_t Get_LPTIM_Count(void) {
return HAL_LPTIM_ReadCounter(&hlptim1); // خواندن مقدار شمارش شده
}


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

---

# ۳. مقایسه LPTIM با سایر تایمرها
| ویژگی | LPTIM | TIMx (Advanced/General-Purpose) |
|-----------|----------|----------------------------------|
| مصرف توان کم | بله | خیر |
| پشتیبانی از حالت STOP و Standby | بله | خیر |
| رزولوشن تایمر | 16 یا 32 بیت | 16 یا 32 بیت |
| قابلیت PWM | بله | بله |
| قابلیت شمارش رویداد خارجی | بله | خیر |
| کاربرد در کنترل توان و مدیریت مصرف انرژی | بله | خیر |

---

# ۴. جمع‌بندی
💡 LPTIM تایمری کم‌مصرف و کاربردی در میکروکنترلرهای STM32 است که برای PWM، شمارنده‌های خارجی و کنترل توان استفاده می‌شود.
💡 در این آموزش، دو مثال برای تولید سیگنال PWM و شمارنده خارجی ارائه شد.
💡 در مقایسه با تایمرهای معمولی، LPTIM حتی در حالت کم‌مصرف هم کار می‌کند و انرژی زیادی صرفه‌جویی می‌کند.
# قابلیت DMA در تایمرهای میکروکنترلر STM32H743IIT6 و کاربردهای آن

DMA (Direct Memory Access) یکی از ویژگی‌های مهم در STM32H743IIT6 است که امکان انتقال داده‌ها بین تایمر (TIMx) و حافظه را بدون نیاز به مداخله CPU فراهم می‌کند. این ویژگی باعث افزایش کارایی پردازنده و کاهش مصرف انرژی می‌شود، به‌ویژه در پردازش سیگنال‌های سریع و کارهای بلادرنگ.

---

## ۱. کاربرد DMA در تایمرهای STM32H743IIT6
DMA در تایمرها می‌تواند در موارد زیر به کار رود:

به‌روزرسانی مقدار مقایسه‌ای PWM (Auto Update PWM)
ذخیره مقدار Capture در حافظه (Input Capture DMA Mode)
استفاده برای تولید امواج PWM با داده‌های متغیر (Waveform Generation)
ارسال خروجی به DAC یا پردازش داده‌های ورودی از ADC
کنترل موتور و مدولاسیون سیگنال‌های پیچیده

---

## ۲. انواع درخواست‌های DMA در تایمرها
در STM32H743IIT6**، تایمرها می‌توانند چندین درخواست **DMA داشته باشند:

| نوع درخواست DMA | توضیح |
|--------------------|-----------|
| Update DMA Request (UDE) | ارسال داده هنگام بروزرسانی تایمر (مثلاً برای PWM با داده‌های متغیر) |
| Capture Compare DMA Request (CCxDE) | ارسال داده هنگام مقایسه مقدار تایمر با مقدار تنظیم‌شده (مناسب برای ذخیره وقایع در حافظه) |
| Trigger DMA Request (TDE) | ارسال داده هنگام فعال شدن تریگر تایمر |
| Commutation DMA Request (COMDE) | مخصوص کنترل موتورهای الکتریکی |

---

## ۳. پیاده‌سازی مثال عملی
### مثال ۱: تولید PWM با مقادیر متغیر با استفاده از DMA
در این مثال، از DMA برای تغییر مقدار PWM در زمان اجرا استفاده می‌کنیم.

### 🛠 تنظیمات در CubeMX:
1. فعالسازی TIM1 در مد PWM
2. فعالسازی DMA Stream برای TIM1_CH1
3. انتخاب Memory to Peripheral برای انتقال داده به رجیستر مقایسه‌ای (CCR1)
4. تنظیم Circular Mode در DMA

---

### 💻 کدنویسی برای تغییر مقدار PWM با DMA
#### ۱) مقداردهی اولیه DMA و تایمر
#include "main.h"

extern TIM_HandleTypeDef htim1;
extern DMA_HandleTypeDef hdma_tim1_ch1;

uint32_t pwmValues[5] = {200, 400, 600, 800, 1000}; // مقادیر متغیر برای PWM

void TIM1_PWM_DMA_Init(void) {
// شروع انتقال داده از حافظه به تایمر با استفاده از DMA
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, pwmValues, 5);
}


#### ۲) راه‌اندازی در تابع `main.c`
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM1_Init();

TIM1_PWM_DMA_Init(); // اجرای تابع مقداردهی اولیه

while (1) {
HAL_Delay(1000);
}
}

🔹 تحلیل عملکرد:
مقدار PWM به صورت متناوب تغییر می‌کند.
مقدار CCR1 با استفاده از DMA بروزرسانی می‌شود.
CPU هیچ پردازشی انجام نمی‌دهد، زیرا انتقال داده‌ها به تایمر مستقیماً توسط DMA انجام می‌شود.

---

### مثال ۲: ذخیره مقدار Capture در حافظه با استفاده از DMA
در این مثال، مقدار ورودی ثبت‌شده توسط تایمر را در یک آرایه حافظه‌ای ذخیره می‌کنیم.

### 🛠 تنظیمات در CubeMX:
1. فعالسازی TIM2 در مد Input Capture
2. فعالسازی DMA برای TIM2_CH1
3. Memory to Peripheral Mode را انتخاب کنید

---

### 💻 کدنویسی برای ذخیره مقدار Capture با DMA
#### ۱) مقداردهی اولیه
#include "main.h"

extern TIM_HandleTypeDef htim2;
extern DMA_HandleTypeDef hdma_tim2_ch1;

uint32_t captureValues[10]; // آرایه ذخیره مقادیر ورودی

void TIM2_Capture_DMA_Init(void) {
HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_1, captureValues, 10);
}


#### ۲) اجرای کد در `main.c`
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM2_Init();

TIM2_Capture_DMA_Init(); // مقداردهی اولیه تایمر و DMA

while (1) {
HAL_Delay(1000);
}
}


🔹 تحلیل عملکرد:
مقدار Capture در آرایه captureValues[] ذخیره می‌شود.
هر بار که لبه سیگنال ورودی رخ دهد، مقدار آن ثبت خواهد شد.
CPU پردازش خاصی انجام نمی‌دهد و این وظیفه به DMA سپرده شده است.

---

## ۴. جمع‌بندی
💡 DMA یکی از ویژگی‌های مهم تایمرهای STM32H743IIT6 است که باعث کاهش بار پردازنده و افزایش کارایی می‌شود.
💡 با استفاده از DMA، می‌توان PWM با مقادیر متغیر، ثبت داده‌های ورودی و کنترل پیشرفته موتور را انجام داد.
💡 در این آموزش دو مثال عملی برای انتقال داده‌های PWM و ذخیره مقادیر Capture ارائه شد.
در زیر یک برنامه کامل برای تبدیل تاریخ میلادی به هجری قمری و بالعکس برای میکروکنترلر STM32 (با استفاده از HAL Library و محیط STM32CubeIDE) آورده شده است. این کد شامل راهاندازی UART برای ارسال خروجی به کامپیوتر است.

---

### کد کامل برای STM32 (با استفاده از UART):

#### 1. فایل `main.c`:
#include "main.h"
#include <stdio.h>
#include <stdint.h>

UART_HandleTypeDef huart2;

// تعریف ساختارهای تاریخ
typedef struct {
uint16_t year;
uint8_t month;
uint8_t day;
} GregorianDate;

typedef struct {
uint16_t year;
uint8_t month;
uint8_t day;
} HijriDate;

// آرایه سالهای کبیسه هجری (در چرخه 30 ساله)
const uint8_t hijriLeapYears[] = {1, 4, 6, 9, 12, 15, 17, 20, 23, 25, 28};

// --------- توابع تبدیل تاریخ ----------
// [همان توابع قبلی برای JDN و تبدیل تاریخ]
// ---------------------------------------

// تابع محاسبه JDN از میلادی
uint32_t gregorian_to_jdn(GregorianDate date) {
uint16_t Y = date.year;
uint8_t M = date.month;
uint8_t D = date.day;
return (1461 * (Y + 4800 + (M - 14)/12))/4
+ (367 * (M - 2 - 12 * ((M -14)/12)))/12
- (3 * ((Y + 4900 + (M - 14)/12)/100))/4
+ D
- 32075;
}

GregorianDate jdn_to_gregorian(uint32_t jdn) {
GregorianDate date;
uint32_t l = jdn + 68569;
uint32_t n = (4 * l) / 146097;
l = l - (146097 * n + 3) / 4;
uint32_t i = (4000 * (l + 1)) / 1461001;
l = l - (1461 * i) / 4 + 31;
uint32_t j = (80 * l) / 2447;
date.day = l - (2447 * j) / 80;
l = j / 11;
date.month = j + 2 - 12 * l;
date.year = 100 * (n - 49) + i + l;
return date;
}

uint32_t hijri_to_jdn(HijriDate date) {
uint16_t year = date.year;
uint8_t month = date.month;
uint8_t day = date.day;
uint32_t totalYears = year - 1;
uint16_t cycles = totalYears / 30;
uint16_t remainingYears = totalYears % 30;

uint8_t leapCount = 0;
for (uint8_t i = 0; i < remainingYears; i++) {
for (uint8_t j = 0; j < 11; j++) {
if (i == hijriLeapYears[j]) {
leapCount++;
break;
}
}
}

uint32_t totalLeapYears = cycles * 11 + leapCount;
uint32_t totalDays = totalYears * 354 + totalLeapYears;

uint16_t cyclePos = (year - 1) % 30;
uint8_t isLeap = 0;
for (uint8_t j = 0; j < 11; j++) {
if (cyclePos == hijriLeapYears[j]) {
isLeap = 1;
break;
}
}

uint8_t monthLengths[] = {30,29,30,29,30,29,30,29,30,29,30, isLeap ? 30 : 29};

for (uint8_t m = 0; m < month - 1; m++) {
totalDays += monthLengths[m];
}

totalDays += day - 1;
return 1948440 + totalDays;
}

HijriDate jdn_to_hijri(uint32_t jdn) {
HijriDate date;
uint32_t daysSinceEpoch = jdn - 1948440;
uint32_t totalDays = daysSinceEpoch;
uint16_t year = 1;

while (1) {
uint16_t cyclePos = (year - 1) % 30;
uint8_t isLeap = 0;
for (uint8_t j = 0; j < 11; j++) {
if (cyclePos == hijriLeapYears[j]) {
isLeap = 1;
break;
}
}
uint32_t daysInYear = isLeap ? 355 : 354;
if (totalDays >= daysInYear) {
totalDays -= daysInYear;
year++;
} else {
break;
}
}

uint16_t cyclePos = (year - 1) % 30;
uint8_t isLeap = 0;
for (uint8_t j = 0; j < 11; j++) {
if (cyclePos == hijriLeapYears[j]) {
isLeap = 1;
break;
}
}

uint8_t monthLengths[] = {30,29,30,29,30,29,30,29,30,29,30, isLeap ? 30 : 29};
uint8_t month, day;

for (month = 1; month <= 12; month++) {
uint8_t length = monthLengths[month - 1];
if (totalDays < length) {
day = totalDays + 1;
break;
}
totalDays -= length;
}

date.year = year;
date.month = month;
date.day = day;
return date;
}

HijriDate gregorian_to_hijri(GregorianDate gDate) {
uint32_t jdn = gregorian_to_jdn(gDate);
return jdn_to_hijri(jdn);
}


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 به درستی تنظیم شدهاند.
- تست: تاریخهای مختلف را برای بررسی صحت تبدیل آزمایش کنید.
استفاده از 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 بهینه میشود و از مشکلاتی مانند از دست رفتن دادهها یا تأخیر جلوگیری میکند.
در برنامهنویسی میکروکنترلرهای 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) را بررسی کنید.
برق و الکترونیک *دانلود پروژه رایگان
https://developer.flythings.cn/en/system_introdoction.html
این نرم افزار شبیه تاچ جی اف ایکس که برای یکی از ای سی هایی که کاربرد داره t113_s3 شرکت الوینر FlyThings یک سیستم مبتنی بر لینوکس است که شامل یک فریمورک گرافیکی (GUI) برای توسعه رابط کاربری در دستگاه‌های اینترنت اشیا (IoT) و نمایشگرهای صنعتی است. این نرم‌افزار می‌تواند جایگزین اندروید در دستگاه‌های کم‌مصرف مانند لوازم خانگی و سیستم‌های کنترل دسترسی شود. FlyThings یک ابزار WYSIWYG برای طراحی UI ارائه می‌دهد و از خدمات چندرسانه‌ای، به‌روزرسانی از راه دور و APIهای وب پشتیبانی می‌کند.