#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int strpos(char *haystack, char *needle, int offset);
int main()
{
char *p = "Hello there all y'al, hope that you are all well";
int pos = strpos(p, "all", 0);
printf("First all at : %d\n", pos);
pos = strpos(p, "all", 10);
printf("Second all at : %d\n", pos);
}
int strpos(char *hay, char *needle, int offset)
{
char haystack[strlen(hay)];
strncpy(haystack, hay+offset, strlen(hay)-offset);
char *p = strstr(haystack, needle);
if (p)
return p - haystack+offset;
return -1;
}
#include <stdlib.h>
#include <string.h>
int strpos(char *haystack, char *needle, int offset);
int main()
{
char *p = "Hello there all y'al, hope that you are all well";
int pos = strpos(p, "all", 0);
printf("First all at : %d\n", pos);
pos = strpos(p, "all", 10);
printf("Second all at : %d\n", pos);
}
int strpos(char *hay, char *needle, int offset)
{
char haystack[strlen(hay)];
strncpy(haystack, hay+offset, strlen(hay)-offset);
char *p = strstr(haystack, needle);
if (p)
return p - haystack+offset;
return -1;
}
یک تابع به زبان C مینویسیم که تعداد مشخصی از کاراکترها را از ابتدا و انتهای یک رشته حذف کرده و رشته جدید را برگرداند. این تابع مواردی مانند مقادیر منفی یا بزرگتر از طول رشته را نیز مدیریت میکند.
### توضیحات:
1. ورودی تابع: رشته اصلی (
2. بررسی مقادیر منفی: اگر
3. محدود کردن مقادیر: اگر
4. محاسبه طول جدید: طول رشته جدید پس از حذف کاراکترها محاسبه میشود.
5. اختصاص حافظه: حافظه لازم برای رشته جدید همراه با نال ترمیناتور اختصاص مییابد.
6. کپی بخش مورد نظر: بخش مربوطه از رشته اصلی به رشته جدید کپی میشود.
7. خروجی: رشته جدید برگردانده میشود که باید توسط کاربر آزاد شود.
### مثال خروجی:
این تابع تمام حالات مرزی مانند رشته خالی، مقادیر منفی، یا مقادیر بزرگتر از طول رشته را به درستی مدیریت میکند.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* trim_string(const char* str, int n, int m) {
if (str == NULL) {
return NULL;
}
int len = strlen(str);
// اطمینان از عدم منفی بودن n و m
n = (n < 0) ? 0 : n;
m = (m < 0) ? 0 : m;
// محدود کردن n و m به طول رشته
if (n > len) {
n = len;
}
if (m > (len - n)) {
m = len - n;
}
int new_len = len - n - m;
char* result = (char*)malloc(new_len + 1); // +1 برای نال ترمیناتور
if (result == NULL) {
return NULL;
}
// کپی بخش مورد نظر از رشته اصلی
memcpy(result, str + n, new_len);
result[new_len] = '\0'; // پایان رشته
return result;
}
// مثال استفاده از تابع
int main() {
const char* str = "Hello, World!";
int n = 3;
int m = 4;
char* trimmed = trim_string(str, n, m);
if (trimmed != NULL) {
printf("Original: %s\n", str);
printf("Trimmed (%d from start, %d from end): '%s'\n", n, m, trimmed);
free(trimmed); // آزادسازی حافظه
}
// تست موارد خاص
char* test1 = trim_string("Example", 0, 0);
printf("\nTest 1: '%s'\n", test1);
free(test1);
char* test2 = trim_string("Test", 5, 2);
printf("Test 2: '%s'\n", test2);
free(test2);
char* test3 = trim_string("Hello", 1, 1);
printf("Test 3: '%s'\n", test3);
free(test3);
return 0;
}
### توضیحات:
1. ورودی تابع: رشته اصلی (
str
)، تعداد کاراکترهای حذف از ابتدا (n
)، و تعداد کاراکترهای حذف از انتها (m
).2. بررسی مقادیر منفی: اگر
n
یا m
منفی باشند، به صورت خودکار صفر در نظر گرفته میشوند.3. محدود کردن مقادیر: اگر
n
یا m
از طول رشته بیشتر باشند، به حداکثر مقدار ممکن محدود میشوند.4. محاسبه طول جدید: طول رشته جدید پس از حذف کاراکترها محاسبه میشود.
5. اختصاص حافظه: حافظه لازم برای رشته جدید همراه با نال ترمیناتور اختصاص مییابد.
6. کپی بخش مورد نظر: بخش مربوطه از رشته اصلی به رشته جدید کپی میشود.
7. خروجی: رشته جدید برگردانده میشود که باید توسط کاربر آزاد شود.
### مثال خروجی:
Original: Hello, World!
Trimmed (3 from start, 4 from end): 'lo, Wo'
Test 1: 'Example'
Test 2: ''
Test 3: 'ell'
این تابع تمام حالات مرزی مانند رشته خالی، مقادیر منفی، یا مقادیر بزرگتر از طول رشته را به درستی مدیریت میکند.
برای پیادهسازی تابع
### توضیحات:
1. شمارش جداکنندهها: ابتدا تعداد جداکنندهها در رشته اصلی شمارش میشود.
2. محاسبه تعداد توکنها: تعداد توکنها برابر با تعداد جداکنندهها بهعلاوه یک است. اگر رشته خالی باشد، تعداد توکنها صفر در نظر گرفته میشود.
3. ذخیره اندیس جداکنندهها: اندیسهای تمام جداکنندهها در یک آرایه ذخیره میشوند.
4. استخراج توکنها: توکنها بین اندیسهای جداکنندهها استخراج میشوند. هر توکن شامل کاراکترهای بین دو جداکننده متوالی است.
5. آزادسازی حافظه: بعد از استفاده از توکنها، باید حافظه اختصاصدادهشده توسط تابع
### مثال خروجی:
برای رشته ورودی
این تابع تمام حالات از جمله توکنهای خالی در ابتدا، انتها یا بین جداکنندهها را بهدرستی پردازش میکند.
split
در زبان C که یک رشته را بر اساس یک جداکننده تقسیم میکند، میتوان از روش زیر استفاده کرد. این تابع تمام توکنها از جمله توکنهای خالی را در نظر میگیرد و آرایهای از رشتههای تقسیمشده را برمیگرداند.#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char** split(const char* str, char separator, int* count) {
int len = strlen(str);
int num_separators = 0;
// شمارش تعداد جداکنندهها
for (int i = 0; i < len; i++) {
if (str[i] == separator) {
num_separators++;
}
}
// محاسبه تعداد توکنها
int num_tokens = (len == 0) ? 0 : num_separators + 1;
if (num_tokens == 0) {
*count = 0;
return NULL;
}
// ذخیره اندیسهای جداکنندهها
int* separator_indices = malloc(num_separators * sizeof(int));
if (!separator_indices) {
return NULL;
}
int idx = 0;
for (int i = 0; i < len; i++) {
if (str[i] == separator) {
separator_indices[idx++] = i;
}
}
// اختصاص حافظه برای آرایه توکنها
char** tokens = malloc((num_tokens + 1) * sizeof(char*));
if (!tokens) {
free(separator_indices);
return NULL;
}
int token_index = 0;
int start = 0;
// استخراج توکنها بین جداکنندهها
for (int i = 0; i < num_separators; i++) {
int end = separator_indices[i] - 1;
int token_length = end - start + 1;
char* token = malloc(token_length + 1); // +1 برای نال ترمیناتور
if (!token) {
// آزادسازی حافظه در صورت خطا
for (int j = 0; j < token_index; j++) {
free(tokens[j]);
}
free(tokens);
free(separator_indices);
return NULL;
}
if (token_length > 0) {
strncpy(token, str + start, token_length);
}
token[token_length] = '\0';
tokens[token_index++] = token;
start = separator_indices[i] + 1;
}
// آخرین توکن بعد از آخرین جداکننده
int end = len - 1;
int token_length = end - start + 1;
char* token = malloc(token_length + 1);
if (!token) {
for (int j = 0; j < token_index; j++) {
free(tokens[j]);
}
free(tokens);
free(separator_indices);
return NULL;
}
if (token_length > 0) {
strncpy(token, str + start, token_length);
}
token[token_length] = '\0';
tokens[token_index++] = token;
// پایان آرایه با NULL
tokens[token_index] = NULL;
free(separator_indices);
*count = num_tokens;
return tokens;
}
// تابع برای آزاد کردن حافظه اختصاصدادهشده توسط split
void free_split_result(char** tokens) {
if (tokens) {
for (int i = 0; tokens[i] != NULL; i++) {
free(tokens[i]);
}
free(tokens);
}
}
// مثال استفاده از تابع
int main() {
const char* str = "hello,,world";
char separator = ',';
int count;
char** tokens = split(str, separator, &count);
if (tokens) {
printf("تعداد توکنها: %d\n", count);
for (int i = 0; i < count; i++) {
printf("توکن %d: '%s'\n", i, tokens[i]);
}
free_split_result(tokens);
}
return 0;
}
### توضیحات:
1. شمارش جداکنندهها: ابتدا تعداد جداکنندهها در رشته اصلی شمارش میشود.
2. محاسبه تعداد توکنها: تعداد توکنها برابر با تعداد جداکنندهها بهعلاوه یک است. اگر رشته خالی باشد، تعداد توکنها صفر در نظر گرفته میشود.
3. ذخیره اندیس جداکنندهها: اندیسهای تمام جداکنندهها در یک آرایه ذخیره میشوند.
4. استخراج توکنها: توکنها بین اندیسهای جداکنندهها استخراج میشوند. هر توکن شامل کاراکترهای بین دو جداکننده متوالی است.
5. آزادسازی حافظه: بعد از استفاده از توکنها، باید حافظه اختصاصدادهشده توسط تابع
split
با استفاده از تابع free_split_result
آزاد شود.### مثال خروجی:
برای رشته ورودی
"hello,,world"
با جداکننده ','
، خروجی به صورت زیر خواهد بود:تعداد توکنها: 3
توکن 0: 'hello'
توکن 1: ''
توکن 2: 'world'
این تابع تمام حالات از جمله توکنهای خالی در ابتدا، انتها یا بین جداکنندهها را بهدرستی پردازش میکند.
بیایید با جزئیات کامل به آموزش تابع
---
### **۱. ساختار تابع
- **پارامترها:**
-
-
-
- تعداد متغیرهایی که با موفقیت خوانده شدهاند.
- اگر خطایی رخ دهد،
-۲. مثالهای پایهی پایه**
#### مثال ۱: خواندن یک عدد صحیح از رشته
#### مثال ۲: خواندن چند متغیر با انواع مختلف
-۳. خواندن دادههای پیچیدهترچیدهتر**
#### مثال ۳: خواندن تاریخ با فرمت مشخص (dd/mm/yyyy)
#### مثال ۴: خواندن بخشی از رشته با استWidth Specifiercifier**
-۴. کار با کاراکترهای خاصای خاص**
#### مثال ۵: خواندن تا رسیدن به یک کاراکتر خاص (با استفاده از
#### مثال ۶: نادیده گرفتن کاراکترهای خاص (با استفاده از
-۵. کنترل خطاها و اعتبارسنجیارسنجی**
#### مثال ۷: بررسی موفقیت عملیات با مقدار بازگشتی
-۶. خواندن دادههای پیشرفتهیشرفته**
#### مثال ۸: خواندن اعداد هگزادسیمال (با
#### مثال ۹: خواندن با استScansetcanset** (
#include <stdio.h>
int main() {
char input[] = "Username: User123!";
char username[20];
// خواندن فقط حروف و اعداد (تا رسیدن به کاراکتر غیرمجاز)
sscanf(input, "Username: %[A-Za-z0-9]", username);
sscanf
در زبان C بپردازیم. این تابع برای خواندن دادههای قالببندیشده از یک رشته (String) استفاده میشود و شباهت زیادی به تابع scanf
دارد، با این تفاوت که بهجای خواندن از ورودی استاندارد (مانند صفحهکلید)، از یک رشته بهعنوان منبع داده استفاده میکند.---
### **۱. ساختار تابع
sscanf
**int sscanf(const char *str, const char *format, ...);
- **پارامترها:**
-
str
: رشته ورودی که دادهها از آن خوانده میشوند.-
format
: رشته فرمت که مشخص میکند چگونه دادهها خوانده شوند.-
...
: متغیرهای آدرسی که دادههای خواندهشده در آنها ذخیره میشمقدار بازگشتی:زگشتی:**- تعداد متغیرهایی که با موفقیت خوانده شدهاند.
- اگر خطایی رخ دهد،
EOF
برمیگرداند.-۲. مثالهای پایهی پایه**
#### مثال ۱: خواندن یک عدد صحیح از رشته
#include <stdio.h>
int main() {
char input[] = "42";
int num;
int result = sscanf(input, "%d", &num);
printf("خوانده شده: %d\n", num); // خروجی: 42
printf("تعداد متغیرهای خواندهشده: %d\n", result); // خروجی: 1
return 0;
}
#### مثال ۲: خواندن چند متغیر با انواع مختلف
#include <stdio.h>
int main() {
char input[] = "Ali 25 75.5";
char name[50];
int age;
float score;
int result = sscanf(input, "%s %d %f", name, &age, &score);
printf("نام: %s\n", name); // Ali
printf("سن: %d\n", age); // 25
printf("امتیاز: %.1f\n", score); // 75.5
printf("تعداد خواندهشده: %d\n", result); // 3
return 0;
}
-۳. خواندن دادههای پیچیدهترچیدهتر**
#### مثال ۳: خواندن تاریخ با فرمت مشخص (dd/mm/yyyy)
#include <stdio.h>
int main() {
char input[] = "31/12/2023";
int day, month, year;
int result = sscanf(input, "%d/%d/%d", &day, &month, &year);
printf("تاریخ: %d-%d-%d\n", day, month, year); // 31-12-2023
printf("تعداد خواندهشده: %d\n", result); // 3
return 0;
}
#### مثال ۴: خواندن بخشی از رشته با استWidth Specifiercifier**
#include <stdio.h>
int main() {
char input[] = "1234567890";
char first_part[5];
int num;
// خواندن ۴ کاراکتر اول و سپس یک عدد
sscanf(input, "%4s%d", first_part, &num);
printf("بخش اول: %s\n", first_part); // 1234
printf("عدد: %d\n", num); // 567890
return 0;
}
-۴. کار با کاراکترهای خاصای خاص**
#### مثال ۵: خواندن تا رسیدن به یک کاراکتر خاص (با استفاده از
%[^]
)#include <stdio.h>
int main() {
char input[] = "apple,banana,cherry";
char fruits[3][20];
// خواندن تا ویرگول
sscanf(input, "%[^,],%[^,],%s", fruits[0], fruits[1], fruits[2]);
printf("میوه ۱: %s\n", fruits[0]); // apple
printf("میوه ۲: %s\n", fruits[1]); // banana
printf("میوه ۳: %s\n", fruits[2]); // cherry
return 0;
}
#### مثال ۶: نادیده گرفتن کاراکترهای خاص (با استفاده از
*
)#include <stdio.h>
int main() {
char input[] = "Product: 1234 Price: 99.99$";
int id;
float price;
// نادیده گرفتن کلمه "Product:" و خواندن عدد
sscanf(input, "Product: %d Price: %f$", &id, &price);
printf("شناسه: %d\n", id); // 1234
printf("قیمت: %.2f\n", price); // 99.99
return 0;
}
-۵. کنترل خطاها و اعتبارسنجیارسنجی**
#### مثال ۷: بررسی موفقیت عملیات با مقدار بازگشتی
#include <stdio.h>
int main() {
char input[] = "Error: Invalid Data";
int code;
int result = sscanf(input, "Error: %d", &code);
if (result == 1) {
printf("کد خطا: %d\n", code);
} else {
printf("فرمت رشته نادرست است!\n"); // این خط اجرا میشود
}
return 0;
}
-۶. خواندن دادههای پیشرفتهیشرفته**
#### مثال ۸: خواندن اعداد هگزادسیمال (با
%x
)#include <stdio.h>
int main() {
char input[] = "Color: #FF5733";
unsigned int color;
sscanf(input, "Color: #%x", &color);
printf("مقدار هگز: %X\n", color); // FF5733
return 0;
}
#### مثال ۹: خواندن با استScansetcanset** (
%[]
)`
c#include <stdio.h>
int main() {
char input[] = "Username: User123!";
char username[20];
// خواندن فقط حروف و اعداد (تا رسیدن به کاراکتر غیرمجاز)
sscanf(input, "Username: %[A-Za-z0-9]", username);
یک تابع در زبان C مینویسیم که تعداد متغیری از رشتهها را دریافت کرده و آنها را به هم متصل میکند. برای مدیریت پارامترهای متغیر از کتابخانه
### کد نهایی:
### شرح کد:
1. محاسبه طول کل رشتهها: با استفاده از
2. اختصاص حافظه: با توجه به طول کل محاسبهشده، حافظه مورد نیاز را اختصاص میدهیم.
3. الحاق رشتهها: دوباره از
4. اتمام رشته: در انتها کاراکتر نول (
### نکات مهم:
- اگر اولین رشته
- کاربر باید آخرین آرگومان را
- حافظه بازگشتی توسط تابع باید توسط کاربر آزاد شود (
stdarg.h
استفاده میکنیم. در این روش، لیست رشتهها با یک NULL
خاتمه مییابد تا تابع تشخیص دهد چه زمانی باید پردازش را متوقف کند.### کد نهایی:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* concatenate(const char* first, ...) {
if (first == NULL) {
char* result = malloc(1);
if (result) *result = '\0';
return result;
}
va_list args;
size_t total_length = 0;
const char* current = first;
// محاسبه طول کل تمام رشتهها
va_start(args, first);
do {
total_length += strlen(current);
current = va_arg(args, const char*);
} while (current != NULL);
va_end(args);
// اختصاص حافظه برای رشته نهایی
char* result = (char*)malloc(total_length + 1);
if (!result) return NULL;
char* ptr = result;
// الحاق رشتهها
va_start(args, first);
current = first;
do {
size_t len = strlen(current);
memcpy(ptr, current, len);
ptr += len;
current = va_arg(args, const char*);
} while (current != NULL);
va_end(args);
*ptr = '\0'; // پایاندهی رشته
return result;
}
// مثال استفاده از تابع
int main() {
char* str = concatenate("Hello", " ", "World!", NULL);
if (str) {
printf("%s\n", str); // خروجی: Hello World!
free(str);
}
str = concatenate("C", " ", "Programming", " ", "Language", NULL);
if (str) {
printf("%s\n", str); // خروجی: C Programming Language
free(str);
}
return 0;
}
### شرح کد:
1. محاسبه طول کل رشتهها: با استفاده از
va_list
و حلقه do-while
، طول تمام رشتهها را جمع میزنیم تا حافظه لازم را اختصاص دهیم.2. اختصاص حافظه: با توجه به طول کل محاسبهشده، حافظه مورد نیاز را اختصاص میدهیم.
3. الحاق رشتهها: دوباره از
va_list
استفاده کرده و هر رشته را به ترتیب به رشته نهایی اضافه میکنیم.4. اتمام رشته: در انتها کاراکتر نول (
'\0'
) را اضافه میکنیم تا رشته به درستی خاتمه یابد.### نکات مهم:
- اگر اولین رشته
NULL
باشد، تابع یک رشته خالی برمیگرداند.- کاربر باید آخرین آرگومان را
NULL
قرار دهد تا پایان لیست رشتهها مشخص شود.- حافظه بازگشتی توسط تابع باید توسط کاربر آزاد شود (
free()
).یک تابع در زبان C مینویسیم که دو رشته را دریافت کرده و بررسی میکند آیا رشته دوم درون رشته اول وجود دارد یا خیر. اگر وجود داشت، موقعیت اولین وقوع آن را برمیگرداند. در غیر این صورت مقدار -1 را بازمیگرداند.
### کد نهایی:
### شرح کد:
1. بررسی حالتهای خاص:
- اگر رشته دوم خالی باشد (
- اگر طول رشته دوم از رشته اول بیشتر باشد، بلافاصله
2. حلقه بیرونی:
- تمام موقعیتهای ممکن در رشته اول را بررسی میکند (تا جایی که جای کافی برای رشته دوم باقی مانده باشد).
3. حلقه داخلی:
- اگر کاراکتر اول رشته دوم با موقعیت فعلی در رشته اول تطابق داشت، بقیه کاراکترها را مقایسه میکند.
- اگر تمام کاراکترهای رشته دوم با بخشی از رشته اول مطابقت داشت، موقعیت آن را برمیگرداند.
4. مقدار بازگشتی:
- اگر زیررشته یافت نشد، مقدار
### مثالهای تست:
-
-
-
-
### نکات مهم:
- این تابع حسّاس به بزرگی و کوچکی حروف است.
- موقعیتها از ایندکس 0 شمارش میشوند.
- اگر رشته دوم چندبار در رشته اول تکرار شده باشد، موقعیت اولین وقوع برگردانده میشود.
### کد نهایی:
#include <stdio.h>
#include <string.h>
int findSubstring(const char *str1, const char *str2) {
// اگر رشته دوم خالی باشد، موقعیت 0 برگردانده میشود
if (*str2 == '\0') {
return 0;
}
int len1 = strlen(str1);
int len2 = strlen(str2);
// اگر طول رشته دوم از رشته اول بیشتر باشد، قطعا وجود ندارد
if (len2 > len1) {
return -1;
}
// بررسی تمام موقعیتهای ممکن در رشته اول
for (int i = 0; i <= len1 - len2; i++) {
// اگر کاراکتر اول تطابق داشت، بقیه را بررسی کن
if (str1[i] == str2[0]) {
int match = 1;
for (int j = 1; j < len2; j++) {
if (str1[i + j] != str2[j]) {
match = 0;
break;
}
}
// اگر تمام کاراکترها تطابق داشتند
if (match) {
return i;
}
}
}
// اگر هیچ تطابقی یافت نشد
return -1;
}
// مثال استفاده از تابع
int main() {
const char *str1 = "hello world";
const char *str2 = "world";
int position = findSubstring(str1, str2);
if (position != -1) {
printf("زیررشته در موقعیت %d یافت شد.\n", position); // خروجی: 6
} else {
printf("زیررشته یافت نشد.\n");
}
return 0;
}
### شرح کد:
1. بررسی حالتهای خاص:
- اگر رشته دوم خالی باشد (
str2 == ""
)، موقعیت 0
برگردانده میشود.- اگر طول رشته دوم از رشته اول بیشتر باشد، بلافاصله
-1
برگردانده میشود.2. حلقه بیرونی:
- تمام موقعیتهای ممکن در رشته اول را بررسی میکند (تا جایی که جای کافی برای رشته دوم باقی مانده باشد).
3. حلقه داخلی:
- اگر کاراکتر اول رشته دوم با موقعیت فعلی در رشته اول تطابق داشت، بقیه کاراکترها را مقایسه میکند.
- اگر تمام کاراکترهای رشته دوم با بخشی از رشته اول مطابقت داشت، موقعیت آن را برمیگرداند.
4. مقدار بازگشتی:
- اگر زیررشته یافت نشد، مقدار
-1
برگردانده میشود.### مثالهای تست:
-
findSubstring("hello", "ll")
→ 2
-
findSubstring("abc", "abcd")
→ -1
-
findSubstring("", "")
→ 0
-
findSubstring("apple", "app")
→ 0
### نکات مهم:
- این تابع حسّاس به بزرگی و کوچکی حروف است.
- موقعیتها از ایندکس 0 شمارش میشوند.
- اگر رشته دوم چندبار در رشته اول تکرار شده باشد، موقعیت اولین وقوع برگردانده میشود.
یک تابع در زبان C مینویسیم که دو رشته را دریافت کرده و تعداد تکرارهای رشته دوم در رشته اول و موقعیتهای آنها را مشخص میکند. در این راهحل، از الگوریتم جستجوی ساده برای بررسی زیررشتهها استفاده میشود و موقعیتها با استفاده از حافظه پویا ذخیره میشوند.
### کد نهایی
### شرح کد
1. بررسی ورودیهای نامعتبر: اگر هر یک از رشتههای ورودی
2. تخصیص حافظه پویا: آرایهای برای ذخیره موقعیتهای احتمالی با حداکثر اندازه ممکن (تفاضل طول دو رشته به علاوه یک) ایجاد میشود.
3. جستجوی زیررشته: با استفاده از دو حلقه تو در تو، هر موقعیت در رشته اول بررسی میشود تا مشخص شود آیا زیررشته مورد نظر از آنجا شروع میشود یا خیر.
4. ذخیره موقعیتها: در صورت یافتن زیررشته، موقعیت آن در آرایه ذخیره شده و شمارنده افزایش مییابد.
5. تنظیم مجدد اندازه آرایه: پس از اتمام جستجو، اگر موقعیتی یافت شده باشد، اندازه آرایه به تعداد واقعی موقعیتها کاهش مییابد. در غیر این صورت، حافظه آزاد میشود.
6. نمونه استفاده: در تابع
این کد به گونهای طراحی شده است که کارایی مناسبی داشته باشد و شرایط مرزی مختلف را به درستی مدیریت کند.
### کد نهایی
#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
، مثال سادهای از استفاده از تابع نشان داده شده است که نتیجه را چاپ کرده و حافظه اختصاص داده شده را آزاد میکند.این کد به گونهای طراحی شده است که کارایی مناسبی داشته باشد و شرایط مرزی مختلف را به درستی مدیریت کند.
## آموزش تایمر 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 را در هنگام اجرا تغییر داد:
🔹 با این تابع، مقدار PWM را تغییر میدهیم تا بتوانیم سیگنال را تنظیم کنیم.
---
## مثال ۲: استفاده از LPTIM بهعنوان شمارنده خارجی
در این مثال، LPTIM ورودی رویدادهای خارجی (External Events) را میشمارد.
### 🛠 تنظیمات در CubeMX:
1. در CubeMX**، تایمر **LPTIM1 را در مد External Counter Mode تنظیم کنید.
2. یک پین ورودی را برای LPTIM1_IN1 تنظیم کنید.
3. کد را تولید کنید و تغییرات زیر را اعمال کنید.
---
### 💻 کدنویسی برای استفاده از LPTIM بهعنوان شمارنده خارجی
🔹 تحلیل عملکرد:
✅ LPTIM بهعنوان شمارنده خارجی عمل میکند.
✅ میتوان تعداد پالسهای ورودی را با خواندن مقدار شمارنده LPTIM مشاهده کرد.
✅ مناسب برای اندازهگیری فرکانس و تعداد پالسهای دریافتی از سنسورها و سایر منابع خارجی است.
---
# ۳. مقایسه LPTIM با سایر تایمرها
| ویژگی | LPTIM | TIMx (Advanced/General-Purpose) |
|-----------|----------|----------------------------------|
| مصرف توان کم | ✅ بله | ❌ خیر |
| پشتیبانی از حالت STOP و Standby | ✅ بله | ❌ خیر |
| رزولوشن تایمر | 16 یا 32 بیت | 16 یا 32 بیت |
| قابلیت PWM | ✅ بله | ✅ بله |
| قابلیت شمارش رویداد خارجی | ✅ بله | ❌ خیر |
| کاربرد در کنترل توان و مدیریت مصرف انرژی | ✅ بله | ❌ خیر |
---
# ۴. جمعبندی
💡 LPTIM تایمری کممصرف و کاربردی در میکروکنترلرهای STM32 است که برای PWM، شمارندههای خارجی و کنترل توان استفاده میشود.
💡 در این آموزش، دو مثال برای تولید سیگنال PWM و شمارنده خارجی ارائه شد.
💡 در مقایسه با تایمرهای معمولی، LPTIM حتی در حالت کممصرف هم کار میکند و انرژی زیادی صرفهجویی میکند.
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 و تایمر
#### ۲) راهاندازی در تابع `main.c`
🔹 تحلیل عملکرد:
✅ مقدار PWM به صورت متناوب تغییر میکند.
✅ مقدار CCR1 با استفاده از DMA بروزرسانی میشود.
✅ CPU هیچ پردازشی انجام نمیدهد، زیرا انتقال دادهها به تایمر مستقیماً توسط DMA انجام میشود.
---
### مثال ۲: ذخیره مقدار Capture در حافظه با استفاده از DMA
در این مثال، مقدار ورودی ثبتشده توسط تایمر را در یک آرایه حافظهای ذخیره میکنیم.
### 🛠 تنظیمات در CubeMX:
1. فعالسازی TIM2 در مد Input Capture
2. فعالسازی DMA برای TIM2_CH1
3. Memory to Peripheral Mode را انتخاب کنید
---
### 💻 کدنویسی برای ذخیره مقدار Capture با DMA
#### ۱) مقداردهی اولیه
#### ۲) اجرای کد در `main.c`
🔹 تحلیل عملکرد:
✅ مقدار Capture در آرایه
✅ هر بار که لبه سیگنال ورودی رخ دهد، مقدار آن ثبت خواهد شد.
✅ CPU پردازش خاصی انجام نمیدهد و این وظیفه به DMA سپرده شده است.
---
## ۴. جمعبندی
💡 DMA یکی از ویژگیهای مهم تایمرهای STM32H743IIT6 است که باعث کاهش بار پردازنده و افزایش کارایی میشود.
💡 با استفاده از DMA، میتوان PWM با مقادیر متغیر، ثبت دادههای ورودی و کنترل پیشرفته موتور را انجام داد.
💡 در این آموزش دو مثال عملی برای انتقال دادههای PWM و ذخیره مقادیر Capture ارائه شد.
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`:
---
### کد کامل برای 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);
}