<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های اشکان انوشه</title>
        <link>https://virgool.io/feed/@ashkan.anousheh</link>
        <description>مهندس دیجیتالی !</description>
        <language>fa</language>
        <pubDate>2026-07-08 06:12:12</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/34198/avatar/uup0Cf.png?height=120&amp;width=120</url>
            <title>اشکان انوشه</title>
            <link>https://virgool.io/@ashkan.anousheh</link>
        </image>

                    <item>
                <title>نکات طراحی نرم افزار برای امبددکارها</title>
                <link>https://virgool.io/@ashkan.anousheh/design-firmware-for-micro-controllers-resw2k1ydzqn</link>
                <description>قبل از هر چیزی منظورم از واژه امبددکارها بیشتر فریمور دولوپرها ( Firmware Developers)  یا به عبارت دیگر میکرو کار ها و مخصوصا ARM کارها هست. البته میتواند برای طرفداران رزبری  و آردوینو نیز مفید باشد.نکات ذکر شده بیشتر خلاصه ای از کتاب Reusable Firmware Development اثر آقای Jacob Beningo و همچنین تجربه شخصی خودم هست.حتما شما هم وقتی شروع کردید به یادگیری یک میکروکنترلر با کشمکش های عجیبی رو به رو شدید که هر کسی میکرویی که خودش کار میکنه خیلی خفنه ! یکی میگه STM32 یکی دیگه از NXP میگه و ... ! بحث من یک ذره کلی تره. چه میشود اگر یک برنامه بنویسیم و با حداقل تغییر در میکرو های متفاوت استفاده بشود ؟اگر شما با سخت افزار و دنیای رجیستری پشت میکروکنترلر درگیر شده باشید متوجه میشوید که همه ی میکرو کنترلر ها در بسیاری از موارد مشابه هم هستند و عمده ترین تفاوت آنها در نام گذاری رجیسترها و شاخ و برگ دادن به پریفرال ها هست. مثلا پریفرال ( Peripheral ) ارتباطی UART را در نظر بگیرید. ویژگی های اصلی پروتکل همه جا بصورت استاندارد ثابت است همینطور در راه اندازی اکثر میکروها به اینصورت عمل میکنیم - صبر میکنیم که فلگ ( Flag ) خاصی فعال بشه.(نشان دهنده خالی بودن بافر ارسال است)رجیستر مربوط به بافر ارسال را با دیتایی که قرار هست ارسال شود مقداردهی میکنیمصبر میکنیم که فلگ خاصی فعال بشود.( ارسال انجام شده و بافر آماده دیتای جدید است)کلیت ماجرا ثابت هست.خب پس اگه همشون در باطن یک کار و انجام میدن پس میشه فریموری نوشت که در میکرو های مختلف استفاده کرد ؟ جواب تقریبا بلللله هست ! چرا تقریبا ؟‌ چون لایه ی درایور ( لایه ای که آدرس پریفرال ها و اسم رجیسترها) در آن ثبت شده برای هر شرکت سازنده منحصر به خودش است. و شما فقط میتوانید لایه بالا تر را (HAL) بصورت reusable پیاده کنید.حالا نکات طلایی طراحی نرم افزارماژولار برنامه بنویسید ! به جای اینکه یک فایل main.c به اندازه ۶۰۰۰ خط کد درست کنید کد هایتان را عاقلانه و در فایل های مختلف دسته بندی کنید. حتما برای هر ماژول یک فایل h و یک فایل c در نظر بگیرید. در اولی فقط اینترفیس و در دومی پیاده سازی و کامنت گذاری کنید.از زبان C استفاده کنید !! راجع به خوب و بد بودن این زبان اصلا حرف نمیزنم چون که خودتون میدونید چقدر خوبه ! مخصوصا از استاندارد C99 به بعد ! نیم نگاهی به استاندارد های کد نویسی C مثل MISRA داشته باشید.از کتابخونه stdint.h استفاده کنید !به منظور صراحت و دقت بیشتر از قالب های int16_t ، uint16_t ، uint32_t و ... به جای int , short int و ... استفاده کنید.از بیت فیلدها ( ‌Bit Field ) استفاده نکنییید !!بیت فیلد ها امکان دسترسی به یک بیت از یک دیتا تایپ را میدهند( مثلا یک بیت از بایت) اما پیاده سازی این دسترسی برای کامپایلر دردسر دارد ! و اغلب با کاهش کارایی و افزایش حجم برنامه همراه است.از یک استاندارد برای نام گذاری متغیرها ، توابع و ماکروها پیروی کنید.این استاندارد میتواند حاصل تجربه خودتان باشد اما می بایست نام های با مسما و به یادماندنی باشند.در ماژول ها بیشتر از ۱۰ الی ۱۵ تابع نداشته باشید.اگر هم داشتید تلاش کنید به دو ماژول تقسیم اش کنید.از معماری ساده ی لایه ای استفاده کنید.به عنوان مثال معماری فریمور زیر را زیر ببینید.تا جای امکان از متد non-blocking استفاده کنید !به عنوان مثال اگر میخواهید رشته ای از LOG را به پورت UART میکرو منتقل کنید، با در نظر گرفتن نرخ انتقال پایین این پروتکل بهتر است از اینتراپت و یا DMA برای ارسال و دریافت استفاده شود و میکرو زمان اش را با انتظار برای تک تک کاراکتر ها هدر ندهد.تفاوت کلیدواژه های extern و static را در زبان C بخاطر بیاورید.بصورت پیشفرض تمام متغیر ها و توابع بصورت extern هستند. یعنی قابلیت دسترسی global دارند و اغلب برای اطلاع دادن به کامپایلر که متغیر مورد نظر در فایل دیگری تعریف شده است آن را بصورت extern  در فایل جدید تعریف می کنیم. با استفاده از کلیدواژه static برای توابع ، دسترسی آن هارا به فایل محدود میکنیم.اهمیت ویژه کلیدواژه volatile را بخاطر داشته باشیم.بیشتر از هر جای دیگری در میکروکنترلر ها این کلیدواژه را میبینیم ! شاید برایتان پیش آمده باشد که برنامه بدرستی کار نکند و با غیر فعال کردن بهینه سازی کامپایلر مشکل تان برطرف شود. علت چیست ؟ به کد زیر از دید کامپایلر بنگرید.#define GPIOA_BASE            0x40020000
uint32_t *  GPIOA =               ((uint32_t *) GPIOA_BASE);
while(*GPIOA &amp; 0x01 == 1);     همانطور که شاید حدس زده باشید در صورتی که کامپایلر کد بالا را بهینه سازی کند، فقط یکبار مقدار رجیستر GPIOA را می خواند چراکه اشاره گر GPIOA در برنامه هیچ تغییری نکرده پس کامپایلر مقادیر داخل while را ثابت در نظر میگیرد. اما مشکل کجاست ؟ مشکل اینجاست که کامپایلر از امکان تغییر محتوای حافظه ای که GPIOA به آن اشاره می کند بی خبر است. این محتوا ممکن است فلگی (Flag) باشد که توسط سخت افزار تغییر کند ! پس نباید ثابت در نظر گرفته شود.راه حل چیست ؟ فقط کافیست با اضافه کردن واژه volatile به کامپایلر بگوییم که خانه حافظه GPIOA امکان تغییر توسط سخت افزار را دارد.uint32_t volatile *  GPIOA =               ((uint32_t *) GPIOA_BASE);توجه داشته باشید که حتما کلید واژه volatile  سمت چپ ستاره ی اشاره گر باشد.( این نکات را در مقاله دیگری مفصل توضیح خواهم داد.)استفاده از کلمه const تا جای امکاناین واژه بیشتر برای برنامه نویس است که نمایانگر این است که برنامه نویس نباید در توابع این متغیر را تغییر دهد !داکیومنتینگ و کامنتینگ !!ترجیحا با Doxygen و graphviz.در کامنت نویسی همیشه چرای کد را توضیح دهید نه چگونگی را.ابتدا کامنت کد را بنویسید سپس خود کد !در طراحی HAL موارد زیر را به ترتیب انجام دهید.۱. دیتاشیت قطعه و رجیستر مپ آن پریفرال را بصورت مروری مطالعه کنید.۲. ویژگی های ضروری و اضافی آن پریفرال را از هم سوا کرده و یادداشت کنید.۳. اینترفیس ویژگی های ضروری پریفرال را طراحی کنید.۴. کامنت گذاری۵. پیاده سازی توابع برای آن میکروکنترلر خاص۶. تست !۷. تکرار عملیات بالا برای پریفرال بعدی.نکات طراحی HAL* در ماژول اصلی فقط ویژگی های اصلی را پیاده کنید.* از نوشتن کد همه منظوره برای همه میکرو ها پرهیز کنید !( بخاطر پیچیدگی و باگ های احتمالی خطرناک.)* پیاده سازی توابع دسترسی مستقیم به رجیسترها.* از شخص دیگری بخواهید کدتان را مطالعه کند.* از یک شیوه نام گذاری استاندارد پیروی کنید.* در توابع initialize حتما از یک پارامتر استفاده کنید.منابع:Reusable Software Development , Jacob Beningo.</description>
                <category>اشکان انوشه</category>
                <author>اشکان انوشه</author>
                <pubDate>Fri, 12 Apr 2019 17:27:06 +0430</pubDate>
            </item>
                    <item>
                <title>ساخت سنسور تشخیص رنگ</title>
                <link>https://virgool.io/@ashkan.anousheh/%D8%B3%D8%A7%D8%AE%D8%AA-%D8%B3%D9%86%D8%B3%D9%88%D8%B1-%D8%AA%D8%B4%D8%AE%DB%8C%D8%B5-%D8%B1%D9%86%DA%AF-sgxq4uatqjq0</link>
                <description>وجود رنگ های متنوع در زندگی علاوه بر زیبایی، نشاط و القای حس خوب کاربرد دیگری هم دارند که برای مهندس ها همیشه جذابیت ویژه ای داشته ! آن هم قابلیت متمایز کردن اشیاء است..دوازده رنگ نسبتا متمایزطبق عرف همیشگی، گرانی های بازار دامان الکترونیکی ها را هم گرفته و هزینه ساخت محصولات با محاسبه ی حداکثر انصاف، به سه برابر رسیده ! که برای همین می خواهم راجع به روشی برای تشخیص رنگ از نوع ارزون، تماسی و با قابلیت اطمینان بالا صحبت کنم. شاید کسب و کار های غولی که محصولات گران قیمت تولید می کنن، سنسور رنگ ۱۰ هزار تومانی برایشان ایده آل باشد چرا که هم دردسر کمتری دارد و هم قابلیت اطمینان بالایی دارد.راجع به نحوه کار سنسورهای آشکارساز رنگ ملموس ترین مثالی که می تونم مطرح کنم دوربین گوشی شماست ! تاحالا فکر کردین این دوربین چطوری یک عکس میگیره که کلی رنگ داخلش هست ؟ در واقع سنسور دوربین عکاسی متشکل از میلیون ها سنسور رنگ هست که بصورت آرایه ای کنار هم چیده شدند. و با ذخیره رنگ هر پیکسل باعث بوجود آمدن عکس های دیجیتالی میشود. البته تا اینجای کار من همه ی پیچیدگی های داستان (مثل لنز و ...) را بخاطر بی ربط بودن به موضوع دور زدم.سنسور دوربین عکاسی (CCD)حالا این سنسورها چطور رنگ را تشخیص می دهند؟ چرا دوربین سیب قرمز را قرمز و سیب سبز را سبز نشان میدهد !؟ مسئله غم انگیزی که وجود داره اینکه اصلا یک سیب قرمز و یک سیب سبز وجود نداره ! در واقع یکی از سیب ها شدت نور قرمز را بیشتر و سیب دیگری شدت نور سبز را بیشتر بازتابش می کند ! ( علمی تر: طول موج متفاوتی را بازتابش می کنند).اینجاست که مهندس ها سریعاً بساط سوء استفاده را پهن می کنند !! در واقع سنسور رنگ چیزی نیست جز سه عدد سنسور شدت نور (مثل سنسورهای فتورزیست LDR) + سه عدد فیلتر رنگ های اصلی (قرمز، سبز، آبی) + مبدل ADC .. پس نوری که به هر پیکسل دوربین میرسد از فیلترهای رنگی عبور کرده سپس شدت نورش توسط ADC دیجیتالی شده و ذخیره میشود. به عنوان مثال اگر نور قرمز وارد سلول دوربین شود فیلتر های سبز و آبی آن را جذب کرده و شدت نور کمتری را به سنسور می تابانند همینطور فیلتر قرمز نور قرمز را کامل از خود عبور داده و حسگر متناظرش مقدار بزرگتری از نور را دریافت می کند. سنسور ساده شده دوربین عکاسی( چیدمان سبز قرمز سبز)خب ! بعد از کلی ماجرا و داستان سرایی بریم که ما هم از زیبایی مسئله استفاده کنیم ! حالا از آنجایی که ما درحال ساخت سنسور تماسی هستیم و دسترسی به فیلترهای رنگی با کیفیت نداریم (که احتمالا گرون هم هستن) پس دوگان قضیه را پیاده سازی میکنیم. به اینصورت که به جای ۳ عدد فیلتر، ۳ عدد LED قرمز، سبز، آبی و همچنین بجای ۳ عدد LDR (فتوسل) از یک عدد فتوسل استفاده میکنیم.برای اینکه نور محیط دقت سنجش را کم نکنه از یک محفظه مات پلاستیکی استفاده می کنیم.یک LED سه رنگ + فتوسل + محفظه پلاستیکیروش کار به این صورت هست که هر کدام از رنگ های اصلی به مدت معقولی (توضیح میدم) روشن شود، به سطح رنگی مورد نظر بتابد سپس بازتابش توسط LDR اندازه گیری شود. حال درجه ای از شدت نور ها برای قرمز سبز و آبی بدست آوردیم، که این اعداد شناسنامه رنگ آن سطح برای ما محسوب میشود !سنسور در حال تشخیص رنگحالا منظور از مدت معقول روشن شدن ال ای دی ها چیست ؟ در واقع فتوسل های موجود در بازار یک مولفه rise time و fall time دارند که بیانگر زمان واکنش خروجی LDR به نور دریافتی هست که معمولا هر کدام حدود ۳۰ میلی ثانیه میباشد که میتوان مدت معقول را همین مقدار برای روشن شدن و خاموش شدن در نظر گرفت.(از مدت روشن شدت LED ها صرف نظر شد).من برای تست از چه چیزایی استفاده کردم ؟بورد STM32F407 سری کاتالیست شرکت ECAدیباگر/ پروگرمر st-link v2یک ورق A4 برچسبی که رنگ های مختلف رویش پرینت شده است.کامپایلر گنو آرم + CUBEMXمیرسیم به بخش جذاب توضیحات نرم افزاری که اول از همه یک نوع داده به اسم rgb_color_t تعریف می کنیم که در واقع تمام خواسته های ما را به زیبایی بیان می کند.چون برای من تشخیص دوازده رنگ متمایز کافی و حتی زیاد هم بود از ADC در رزولوشن ۸ بیتی استفاده کردم و برای همین هم دیتاتایپ های uint8_t اختیار شده است. فکر میکنم ممبرهای struct نیازی به توضیح نداشته باشد، چون اسم های با مسمایی دارند.حالا باید سنسور را روی رنگ های مختلف قرار داد و مقادیر را ثبت کنیم. می توان این کار را هم اتوماتیک توسط برنامه انجام داد که من برای راحتی فعلا اطلاعات را دستی ذخیره کردم.حالا که مقادیر رنگ ها همراه با آیدی و اسم ۴حرفی را در ساختمان داده ی خودمان مقداردهی کردیم میریم سراغ تابعی که رنگ مجهول را با رنگ های از قبل ثبت شده مقایسه کنه و نتیجه را اعلام کنه.خب این تابع یکم نیاز به توضیح داره. اول اینکه دقت کنید منابع در سیستم های میکروکنترلری محدود هستن برای همین در تابع main آدرس ساختمان داده به تابع تشخیص رنگ ارسال شده است. خط ۱۱ تابعی را صدا میزند که مسئول روشن کردن LED ها و نمونه گرفتن از LDR توسط ADC می باشد.خطوط ۱۵ تا ۳۰ رنگ مجهول نمونه برداری شده را با رنگ های از قبل ثبت شده مقایسه می کند.این مقایسه به اینصورت است که قدرمطلق رنگ دیده شده از فتوسل را از رنگ های تعریف شده کم کرده و نتیجه را با یک حاشیه مقایسه می کند. این حاشیه ها بصورت تجربی بدست آمدن که در خطوط ۱ تا ۵ ثبت شده اند. خطوط ۳۱ تا ۳۶ برای این منظور می باشد که در صورت نیافتن رنگ مورد نظر عبارت UNKW به مفهوم مجهول را در struct ثبت کند.خطوط ۱ تا ۵: متوجه شدم که حاشیه تغییر ADC در نمونه های متوالی برای رنگ قرمز بیشتر از آبی و آبی نیز بیشتر از سبز بود.(یعنی سبز کمترین تغییرات را داشت) لذا برای دقیق تر شدن، حاشیه رنگ ها را از هم متمایز در نظر گرفتم.آخرین تابع هم که مربوط به نمونه گیری از LDR و روشن کردن LED ها هست در شکل زیر قرار دادم.( این تابع به گونه ای نوشته شده که باعث سادگی و قابل فهم شدن منطق برنامه شود و ابدا بهینه نیست !)امیدوارم مطلب برایتان مفید بوده باشه بزودی برنامه بهینه شده را در گیت ام آپلود میکنم و لینک اش را اینجا قرار می دهم.اشکان انوشهashkan.anousheh@gmail.com </description>
                <category>اشکان انوشه</category>
                <author>اشکان انوشه</author>
                <pubDate>Sat, 16 Mar 2019 04:49:43 +0330</pubDate>
            </item>
            </channel>
</rss>