آموزش STM32 با توابع LL قسمت ششم: GPIO-Input

آموزش STM32 با توابع LL قسمت ششم: GPIO-Input

آموزش STM32 با توابع LL قسمت ششم: GPIO-Input
آموزش STM32 با توابع LL قسمت ششم: GPIO-Input

در قسمت پنجم از آموزش STM32 با توابع LL، در رابطه با GPIO در حالت خروجی صحبت کردیم و به بررسی جزئیات رجیسترهای GPIO پرداختیم که همین بررسی جزئیات و البته یک سری توضیحات دیگر، درک ما را از تفاوت سرعت فاحش بین توابع HAL و LL بیشتر کرد.

در این قسمت قصد داریم GPIO در حالت ورودی را راه‌اندازی کنیم.

شاید از قسمت قبل برایتان سوال پیش آمده باشد که اگر بخواهیم چندین پین از میکروکنترلر را در حالت خروجی راه‌اندازی کنیم باید چه کار کنیم؟ (چون در قسمت قبل فقط یک پین را در حالت خروجی راه‌اندازی کردیم) این کار راه‌های متفاوتی دارد، اما ما بهترین و اصولی‌ترین راه را در این قسمت برای شما شرح خواهیم داد.

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

کلیت کار به این صورت است که وضعیت ورودی با استفاده از یک کلید بررسی می‌شود و هر زمان که کلید فشرده شد متغیری در درون برنامه یک واحد افزایش پیدا می‌کند (این متغیر وقتی به عدد 9 رسید دوباره 0 می‌شود) و مقدار این متغیر که عددی بین 0 تا 9 است با استفاده از دیکدر، بر روی سون سگمنت نمایش داده خواهد شد.

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

ما تا الان در قسمت GPIO، رجیسترهای BSRR و BRR را بررسی کردیم و تنها دو رجیستر دیگر باقی می‌ماند که در این قسمت بررسی می‌شود. (البته یک سری رجیستر دیگر برای پیکره‌بندی اولیه مانند این که در حالت ورودی باشیم یا خروجی، یا اینکه سرعت پین چقدر باشد، نیز وجود دارد که ما به بررسی این رجیسترها نخواهیم پرداخت و این رجیسترها در نرم‌افزار STM32CubeMX زمانی که به صورت گرافیکی تنظیمات اولیه را انجام می‌دهیم مقداردهی می‌شوند)

همانطور که در قسمت قبل گفتیم رجیستر BSRR هم برای 1 کردن و هم برای 0 کردن پین‌های خروجی به کار می‌رود، اما رجیستر BRR تنها برای 0 کردن پین‌های خروجی به کار می‌رود.

دو رجیستری که می‌خواهیم در این قسمت بررسی کنیم رجیسترهای ODR و IDR خواهند بود. رجیستر ODR برای 0 یا 1 کردن پین خروجی و رجیستر IDR برای خواندن پین‌های ورودی به کار می‌رود.

شاید بپرسید فرق رجیستر ODR با رجیستر BSRR و BRR در چیست؟ یا وقتی رجیستر ODR وجود دارد چه نیازی به رجیسترهای BSRR و BRR است؟

جواب این سوال در داکیومنت‌های ST به صراحت ذکر شده است، برای اینکه تنها یک بیت را به صورت مستقیم بخواهیم 0 یا 1 کنیم باید از رجیسترهای BSRR و BRR استفاده کنیم، چون این کار با استفاده از رجیستر ODR به صورت مستقیم امکان‌پذیر نیست. با هر بار خواندن یا نوشتن رجیستر ODR به کل بیت‌ها با هم دسترسی داریم نه یک بیت تنها.

به همین دلیل است که در قسمت قبلی که می‌خواستیم فقط یک پین را 0 و 1 کنیم از رجیسترهای BSRR و BRR استفاده کردیم، اما در این قسمت که می‌خواهیم چندین پین را 0 و 1 کنیم از رجیستر ODR استفاده می‌کنیم.

پس نتیجه این است که برای تغییر یک بیت بهتر است از رجیسترهای BSRR و BRR استفاده کنیم، اما برای تغییر چندین بیت باهم بهتر است که از رجیستر ODR استفاده بکنیم.

خب بهتر است که به موضوع اصلی این مقاله یعنی تشریح رجیسترهای ODR و IDR برگردیم.

رجیستر ODR یک رجیستر 32 بیتی است که ما فقط به 16 بیت اول آن دسترسی داریم و این 16 بیت هم خواندنی هستند و هم نوشتنی. رجیستر IDR هم یک رجیستر 32 بیتی است که ما فقط به 16 بیت اول آن دسترسی داریم، اما این 16 بیت فقط خواندنی هستند و ما قادر به نوشتن در این بیت‌ها نیستیم.

اجازه بدهید که جزئیات بیشتر را در نرم‌افزار و هنگام برنامه‌نویسی توضیح بدهم تا موضوع برایتان ملموس‌تر باشد.

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

همانند تصویر زیر پین‌های PA0 تا PA6 را برای سون سگمنت در نظر می‌گیریم و آن‌ها را به عنوان خروجی تعریف می‌کنیم. همینطور پین PB0 را برای کلید و به عنوان ورودی تعریف می‌کنیم.

 

نرم‌افزار STM32CubeMX

 

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

حالت اول این است که پین ورودی ابتدا 1 منطقی باشد و با فشردن کلید 0 منطقی شود. در این حالت ما باید با استفاده از شرط، 0 شدن پین را بررسی کنیم.

و حالت دوم این است که پین ورودی ابتدا 0 منطقی باشد و با فشردن کلید 1 منطقی شود. در این حالت ما باید با استفاده از شرط، 1 شدن پین را بررسی کنیم.

هر دو حالت بالا عملکرد مشابهی دارند، فقط با دو منطق متفاوت.

در حالت اول معمولا یک مدار بسیار ساده به نام مدار Pull-Up و در حالت دوم مدار ساده دیگری به نام مدار Pull-Down را در کنار میکروکنترلر قرار می‌دهند تا قبل از زدن کلید، پین در یکی از دو منطق 0 یا 1 باشد و بعد از زدن کلید منطق عوض شود.

برای درک بهتر پاراگراف بالا به مدار زیر توجه کنید:

 

Pull-up and Pull-down Resistor

 

اما خبر خوب اینکه معمولا این مدارات به صورت پیش‌فرض، از قبل در میکروکنترلرها قرار داده شده است و نیازی به مدار خارجی نیست. و خبر خوب‌تر اینکه میکروکنترلرهای ARM بر خلاف میکروکنترلرهای AVR که فقط مدار Pull-Up را دارند، هم مدار Pull-Up و هم مدار Pull-Down را به صورت داخلی دارند.

در اینجا ما قصد داریم که از حالت اول استفاده بکنیم، یعنی پین ورودی ابتدا 1 منطقی باشد و با فشردن کلید 0 منطقی بشود. پس باید از مدار Pull-Up استفاده بکنیم. برای این کار نیاز است که در نرم‌افزار STM32CubeMX مشخص کنیم که پین PB0 در حالت Pull-Up داخلی قرار دارد.

برای اینکه پین به صورت داخلی Pull-Up شود، باید در نرم‌افزار تنظیمات زیر را انجام داد:

 

نرم‌افزار STM32CubeMX

 

اکنون از پروژه خروجی می‌گیریم و برای نوشتن کد به نرم‌افزار Keil می‌رویم.

در ابتدای برنامه یک آرایه به طول 10، برای 10 عدد مختلفی که باید بر روی سون سگمنت نمایش داده شود و یک متغیر برای شمارنده در نظر خواهیم گرفت:

آرایه‌ای که برای سون سگمنت در نظر گرفتیم، مطابق تصویر زیر که همان دیکدر BCD به سون سگمنت است، نوشته شده است:

 

BCD to seven segment

 

پس از تعریف کردن متغیرها برای اینکه سون سگمنت به صورت مستمر شروع به شمارش کند، باید کد زیر درون حلقه‌ی while نوشته شود:

در ادامه، قطعه کد بالا را با جزئیات کامل مفصلا شرح خواهیم داد. 

در اولین خط، از تابع LL_GPIO_WriteOutputPort استفاده کردیم و در ورودی‌های آن به ترتیب از GPIOA و SevenSegNumber[i] استفاده کردیم. اجازه بدهید به تعریف تابع برویم تا ببینیم که چه اتفاقی بر روی این دو ورودی خواهد افتاد.

به تعریف تابع توجه کنید:

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

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

چون در شروع برنامه مقدار متغیر i صفر است، پس اولین عضو از آرایه SevenSegNumber که معادل همان عدد صفر است بر روی سون سگمنت نمایش داده می‌شود.

اکنون ما باید کنترل کنیم که در هر لحظه از زمان چه مقداری بر روی پورت قرار بگیرد. می‌توانستیم این کار را با یک زمان‌بندی و توالی خاص انجام بدهیم که پس از پروگرام کردن برنامه، سون سگمنت شروع به شمارش کند، اما همانطور که در ابتدای مقاله ذکر کردیم قصد داریم که با GPIO در حالت ورودی آشنا بشویم، پس این کار را با استفاده از فشردن یک کلید انجام خواهیم داد.

فشردن کلید را با استفاده از ساختار شرطی if بررسی می‌کنیم. برای اینکه بدانیم دقیقا باید چه عبارتی را درون شرط بنویسیم، لازم است که با رجیستر IDR آشنا باشیم و عملکرد آن را وقتی پین در حالت ورودی است بررسی کنیم.

اگر پین در حالت ورودی باشد، هر مقدار منطقی که خارج از میکروکنترلر بر روی پین اعمال شود، در بیت متناظر با همان پین در رجیستر IDR ذخیره می‌شود. مثلا اگر ما پین PB8 را به سطح ولتاژ 3.3 ولت یا همان 1 منطقی متصل کنیم، هشتمین بیت از رجیستر IDR به صورت خودکار 1 می‌شود.

چون ما کلید را به پین PB0 وصل کردیم تنها کاری که باید بکنیم این است که بیت صفرم رجیستر IDR از GPIOB را بخوانیم و هر موقع این مقدار، 0 منطقی شد، شرط برقرار شود و عملیات مورد نظرمان را انجام بدهیم.

اما به صورت مستقیم خواندن بیت صفرم یا هر بیت دیگر از رجیستر IDR امکان‌پذیر نیست، چرا؟

چون که طبق مستندات ST ما نمی‌توانیم تنها یک بیت از این رجیستر را بخوانیم و با هر بار خواندن این رجیستر باید 16 بیت آن باهم خوانده شود.

برای خواندن رجیستر IDR از تابع LL_GPIO_ReadInputPort استفاده می‌کنیم و اگر به تعریف تابع LL_GPIO_ReadInputPort نیز توجه کنید متوجه خواهید شد که کل بیت‌های رجیستر IDR باهم خوانده می‌شوند:

پس با این تفاسیر راه‌حل چیست؟

راه‌حل این است که ابتدا کل 16 بیت را باهم بخوانیم و سپس مقدار بیت موردنظر را از آن جدا کنیم.

قبل از اینکه کدی که برای خواندن یک بیت نوشتیم، را بررسی کنیم، به این نکته توجه کنید که در مستندات ST گفته شده است که مقدار پیش‌فرض بیت‌های رجیستر IDR معلوم نیست و می‌تواند هر مقداری باشد (Reset value: 0x0000 XXXX)، مگر اینکه پین متناظر با آن بیت مشخصا به یک سطح ولتاژ متصل باشد.

خب ما تنها پین صفرم را Pull-Up کردیم، پس تنها بیت صفرم رجیستر IDR مقدارش مشخص است و سایر بیت‌ها می‌توانند هر مقداری داشته باشند.

بیت صفرم چون Pull-Up شده است، در حالت عادی مقدارش 1 منطقی است. و زمانی که کلید فشرده شود این بیت مقدارش 0 منطقی خواهد شد.

با توجه به توضیحات بالا؛ ما در شرط if، از عبارت ((LL_GPIO_ReadInputPort(GPIOB)) & (1<<0)) استفاده کردیم، یعنی همه‌ی بیت‌ها به جز بیت صفرم را با 0 منطقی AND کردیم تا بیت‌هایی که مقدارشان مشخص نبود، همگی صفر شوند. و بیت صفرم را با 1 منطقی AND کردیم تا اگر مقدار بیت 0 بود همان 0 و اگر 1 بود همان 1 را داشته باشیم.

پس مقدار درون شرط if تا زمانی که کلید فشرده نشود “0x0001” است، به مجرد اینکه کلید فشرده شود این مقدار به “0x0000” تغییر می‌کند و ما با استفاده از شرط if تعیین کردیم که اگر عبارت ((LL_GPIO_ReadInputPort(GPIOB)) & (1<<0)) برابر با “0x0000” شد، دستورات درون شرط if اجرا شود.

ما با استفاده از تکنیک‌های ساده‌ای موفق شدیم که بر محدودیت‌هایی که ذکر شد غلبه کنیم و شرط موردنظرمان را توصیف کنیم. حال وقت آن است که دستورات درون شرط را توضیح بدهیم.

درون شرط if گفتیم ابتدا متغیر یک واحد افزایش پیدا کند و پس از 200 میلی ثانیه تاخیر، اگر متغیر از عدد 9 بزرگ‌تر بود به صفر برگردانده شود و در نهایت از شرط خارج شود. پس از خارج شدن از شرط، برنامه به ابتدای حلقه برمی‌گردد و مقدار پورت را با توجه به مقدار جدید i آپدیت می‌کند.

در این برنامه به صورت مستمر چک می‌شود که آیا کلید فشرده شده است یا خیر و اگر کلید فشرده شد، به متغیر یک واحد اضافه شود و بر روی سون سگمنت نمایش داده شود.

شاید برایتان سوال پیش آمده باشد که چرا از تاخیر 200 میلی ثانیه استفاده کردیم! به این دلیل که وقتی ما کلید را فشار می‌دهیم، با توجه به سرعت بالای برنامه متغیر سریعا افزایش می‌یابد و دوباره به ابتدای شرط برمی‌گردد و چون ما هنوز دستمان بر روی کلید است دوباره متغیر افزایش می‌یابد. در واقع وقتی ما دستمان را بر روی دکمه گذاشتیم تا بخواهیم آن را برداریم متغیر n بار افزایش پیدا می‌کند. به همین علت است که ما در برنامه از تاخیر استفاده کردیم.

البته استفاده از تاخیر در برنامه راهی اصولی نیست، اما چون هنوز در ابتدای مسیر هستیم و با سایر پریفرال‌ها آشنا نیستیم، استفاده از تاخیر در برنامه مانعی ندارد.

در قسمت هفتم در رابطه با Interrupt یا همان وقفه صحبت خواهیم کرد.

1 نفر

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

توجه

دیدگاه ها

18 دیدگاه

  • محمد حسین
    محمد حسین
    ۲۵ تیر ۱۴۰۱

    سلام امیدوارم حالتون خوب باشه.
    این عملگر: -> چه عملی انجام می‌ده؟
    در این عبارت استفاده شده:
    return (READ_REG(GPIOx->IDR));
    زیرمجموعه رو نشون میده؟ کاری که نقطه در زبان‌های دیگه انجام می‌دن انجام می‌ده؟

    همین طور کاراکتر ستاره (*) که در این عبارت استفاده شده:
    __STATIC_INLINE uint32_t LL_GPIO_ReadInputPort(GPIO_TypeDef *GPIOx)

    ممنونم.

    • Zeus ‌
      Zeus ‌
      ۲۹ تیر ۱۴۰۱

      سلام دوست عزیز
      ببینید برای دسترسی به اعضای یک ساختار ما از نقطه (.) استفاده میکینم این برای وقتی هست که خود استراکچر را در اختیار داشته باشیم
      ولی وقتی که اشاره گر به ساختار رو در اختیار داشته باشیم برای دسترسی به اعضای آن از ->استفاده می‌کنیم.

  • داستایوسکی
    ۳ فروردین ۱۴۰۱

    عالی میشد اگر PDF مطلب هم قرار میدادین. گاهی دوست دارم پرینت کنم و خط بکشم زیر کلمات.
    وقتی خودم میخوام سایت رو بهpdf تبدیل کنم یه مقدار جالب درنمیاد، حتی وقتی روی حالت reader view میذارم مرورگر رو.
    عالیه این سری آموزش.

  • صادق
    ۲۵ فروردین ۱۴۰۰

    سلام
    خسنه نباشید
    توی تعریف تابع LL_GPIO_ReadInputPort فقط از عبارت return (READ_REG(GPIOx->IDR)) استفاده شده است.
    سوالم اینه که اگه به جای فراخوانی اون تابع ، از عبارت return (READ_REG(GPIOx->IDR)) استفاده کنیم، مشکلی پیش میاد؟ و آیا از لحاظ سرعت اجرا با هم متفاوتن؟ اگر بله، کدوم سریعتره؟

    • کامین جلیلی
      ۲۶ فروردین ۱۴۰۰

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

  • محمدرضا
    ۳ اسفند ۱۳۹۹

    دلیل استفاده از تاخیر 200 میلی ثانیه در شکل زیر مشخص است. این مورد را در همه جا حتی مدارهای آنالوگ در نظر بگیرید.
    debounce circuit به صورت آنالوگ مانند تاخیر 200 میلی ثانیه است و از قطع و وصل مکانیکی(که به چشم ما نمیاید) جلوگیری میکند.

    https://www.nuvation.com/sites/default/files/blog/Switch%20Debouncing%20for%20Electronic%20Product%20Designs/Switch_Debouncing_Circuit_Waveform.jpg

    ممنون که شما هم توضیح دادین جناب مهندس

    • کامین جلیلی
      ۴ اسفند ۱۳۹۹

      محمد رضا جان این موضوع که می‌گید درسته، اما فقط بخشی از کل ماجراست. یعنی حتی اگه کلید‌ها ایده‌آل بودند و بانس مکانیکی هم نداشتند ما در اینجا باید تاخیر را می‌گذاشتیم. البته همانطور که گفتم تاخیر راه درستی نیست.

  • بهاره
    ۲۱ بهمن ۱۳۹۹

    سلام
    من سوال خیلی ساده ای داشتم شرمنده از مطرح کردنش
    این عبارت (LL_GPIO_ReadInputPort(GPIOB)) & (1<>1) چطور این کار را انجام می دهد؟ علامت >> چه معنی دارد و چه کاری انجام می دهد؟

    • کامین جلیلی
      ۲۱ بهمن ۱۳۹۹

      سلام بهاره. نه سوال پرسیدن شرمندگی دارد و نه ندانستن، پس هیچوقت از پرسیدن ابایی نداشته باشید.

      تابع LL_GPIO_ReadInputPort کل بیت‌های رجیستر IDR را با هم می‌خواند، اما ما فقط مقدار بیت صفرم از این رجیستر را می‌خواهیم. همانطور که در مقاله گفتم مقدار پیش فرضی که توسط تابع LL_GPIO_ReadInputPort برگشت داده می‌شود معلوم نیست و ممکن است هر بیت آن 0 یا 1 باشد، البته به جز بیت صفرم آن که ما پین را پول آپ کردیم و مقدارش مشخص است. حالا کاری که ما کردیم این است که مقدار همه‌ی بیت‌های برگشتی تابع، به جز بیت صفرم را به دلیل نامشخص بودن 0 می‌کنیم. این کار هم با استفاده از اند (&) بیتی مقدار برگشتی تابع با عبارت (1<<0) انجام شده است. که این کار همه‌ی بیت‌های برگشتی تایع به جز بیت صفرم را با 0 اند می‌کند که مقدارش همان 0 می‌شود بیت صفرم را هم با مقدار 1 اند می‌کند. عبارت (1<<0) هم به این معناست که 1 را به اندازه 0تا به سمت چپ شیفت می‌دهد، البته اینجا عبارت (1<<0) چپه شده است، این عبارت را داخل کد درون مقاله ببینید که باعث اشتباه نشود. اگر جایی را متوجه نشدید یا سوالی داشتید دوباره بپرسید من جواب می‌دهم.

      • بهاره
        ۲۱ بهمن ۱۳۹۹

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

        1- چرا به صورت شیفت نوشته شده است وقتی که اصلا شیفت هم داده نمی شود. نمیشد به صورت 0x0001 نوشت؟

        2- مقدار یک متناظر با بیت صفرم است که باید با آن اند شود ولی بیت های دیگر که باید با صفر اند شوند آن صفر ها کجا و چطور نوشته شده است. یعنی عبارت “صفربار شیفت یک به سمت چپ” یک مقدار 16 بیتی تولید میکند که بیت صفرم آن یک و بقیه بیت ها صفرند؟

        • کامین جلیلی
          ۲۴ بهمن ۱۳۹۹

          در پاسخ به سوال اولتون باید بگم که دلیل این کار خوانایی برنامه است. اجازه بدید یه مثال براتون بزنم:

          مثلا فرض کنید می‌خواهیم عدد 1 رو به اندازه 13 واحد شیفت بدیم یا بیت سیزدهم از یک رجیستر 16 بیتی را 1 کنیم، راحت‌تر و خواناتر است که بنویسیم (1<<13) تا بخواهیم 0010000000000000 یا هگز آن را به صورت 0x2000 بنویسیم. سوال دومتون هم به سمت چپ عبارت 0 اضافه میشه. اگه دقت کرده باشید بعضی وقتا اعداد را با پسوند می‌نویسن که این پسوند نشان‌دهنده چند بیتی بودن اون عدد هستش. مقداری که ما تعیین می‌کنیم مثلا همین 1 را در بیت اول می‌نویسد و سایر بیت‌ها به اندازه اینکه اون عدد رو چند بیتی تعریف کردیم 0 قرار می‌دهد. و اگر عدد رو مثل الان بدون پسوند بنویسیم مثلا در اینجا به صورت دیفالت 32 بیت در نظرش می‌گیرد.

  • Aaa
    ۱۵ آبان ۱۳۹۹

    سرعت کدوم بیشتره llیاhal

    • Kamin Jalili
      ۱۵ آبان ۱۳۹۹

      سرعت LL بیشتر است. در قسمت پنجم مفصلا این موضوع را شرح دادم، به قسمت پنجم مراجعه کنید.

  • VHD
    ۸ آبان ۱۳۹۹

    فکر کنم این مجموعه آموزش ی 2-3سالی طول بکشه 🙂
    شوخی کردم
    عالی بود
    ممنون

    • Kamin Jalili
      ۱۰ آبان ۱۳۹۹

      سه سال که یکم زیاده، ولی دو سال رو فکر کنم طول بکشه. ?

      سپاس از توجه‌تان.

      • عباس لطیفی پاکدهی
        عباس لطیفی پاکدهی
        ۱۱ آبان ۱۳۹۹

        سلام آقای جلیلی وقت تون بخیر
        بنده لطیفی هستم دانشجوی الکترونیک و مخابرات دریایی و علاقه مند به موضوعات الکترونیکی برای یه پروژه مهم به کمک شما نیاز داریم. هم بحث استفاده از توابع LL در میونه تا پروژه سریعتر اجرا بشه، هم بحث کنش ها و واکنش های سیستم. بنده خیلی خوشحال میشم بتونم باهاتون در مورد پروژه صحبت کنم و اگر امکانش باشه بتونیم باهم همکاری داشته باشیم.

        • Kamin Jalili
          ۱۱ آبان ۱۳۹۹

          سلام مهندس جان. در جواب به همین کامنت لطفا کامنت بگذارید و شماره تماس‌تون رو قرار بدهید تا باهاتون تماس بگیرم. البته قبلش به دوستان سیسوگ می‌گم که برای حریم خصوصی شما، کامنت رو در دید دیگران قرار ندهند تا شماره شما خصوصی بماند.

پر بحث ترین ها

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

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

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

Zeus ‌ Zeus ‌
  • 3 سال پیش
راه اندازی LCD گرافیکی Nokia 1661

راه اندازی LCD گرافیکی Nokia 1661

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

Zeus ‌ Zeus ‌
  • 4 سال پیش
ریموت کدلرن و چکونگی دکد کردن آن به همراه سورس برنامه

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

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

Zeus ‌ Zeus ‌
  • 5 سال پیش
همه چیز درباره ریموت کنترل‌های هاپینگ

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

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

Zeus ‌ Zeus ‌
  • 5 سال پیش
مسابقه سوم: استخراج داده از رشته ها در زبان C

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

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

Zeus ‌ Zeus ‌
  • 2 سال پیش
مسابقه ششم: بزن میکروکنترلر را بسوزون!

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

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

Zeus ‌ Zeus ‌
  • 1 سال پیش
آموزش قدم به قدم راه اندازی NRF24L01

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

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

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

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

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

Zeus ‌ Zeus ‌
  • 3 سال پیش
کار با ماژول تمام عیار mc60 – قسمت دوم – راه اندازی OpenCPU

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

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

Mahdi.h   Mahdi.h  
  • 3 سال پیش
مسابقه چهارم: کدام حلقه سریع‌تر است؟

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

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

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

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