در قسمت پنجم از آموزش 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 را برای کلید و به عنوان ورودی تعریف میکنیم.
همانطور که گفتیم ما قصد داریم با هر بار فشردن کلید یک واحد به متغیری در درون برنامه اضافه شود و مقدار این متغیر بر روی سون سگمنت نشان داده شود. برای این کار از یک شرط استفاده خواهیم کرد و خود این شرط در دو حالت میتواند نوشته شود.
حالت اول این است که پین ورودی ابتدا 1 منطقی باشد و با فشردن کلید 0 منطقی شود. در این حالت ما باید با استفاده از شرط، 0 شدن پین را بررسی کنیم.
و حالت دوم این است که پین ورودی ابتدا 0 منطقی باشد و با فشردن کلید 1 منطقی شود. در این حالت ما باید با استفاده از شرط، 1 شدن پین را بررسی کنیم.
هر دو حالت بالا عملکرد مشابهی دارند، فقط با دو منطق متفاوت.
در حالت اول معمولا یک مدار بسیار ساده به نام مدار Pull-Up و در حالت دوم مدار ساده دیگری به نام مدار Pull-Down را در کنار میکروکنترلر قرار میدهند تا قبل از زدن کلید، پین در یکی از دو منطق 0 یا 1 باشد و بعد از زدن کلید منطق عوض شود.
برای درک بهتر پاراگراف بالا به مدار زیر توجه کنید:
اما خبر خوب اینکه معمولا این مدارات به صورت پیشفرض، از قبل در میکروکنترلرها قرار داده شده است و نیازی به مدار خارجی نیست. و خبر خوبتر اینکه میکروکنترلرهای ARM بر خلاف میکروکنترلرهای AVR که فقط مدار Pull-Up را دارند، هم مدار Pull-Up و هم مدار Pull-Down را به صورت داخلی دارند.
در اینجا ما قصد داریم که از حالت اول استفاده بکنیم، یعنی پین ورودی ابتدا 1 منطقی باشد و با فشردن کلید 0 منطقی بشود. پس باید از مدار Pull-Up استفاده بکنیم. برای این کار نیاز است که در نرمافزار STM32CubeMX مشخص کنیم که پین PB0 در حالت Pull-Up داخلی قرار دارد.
برای اینکه پین به صورت داخلی Pull-Up شود، باید در نرمافزار تنظیمات زیر را انجام داد:
اکنون از پروژه خروجی میگیریم و برای نوشتن کد به نرمافزار Keil میرویم.
در ابتدای برنامه یک آرایه به طول 10، برای 10 عدد مختلفی که باید بر روی سون سگمنت نمایش داده شود و یک متغیر برای شمارنده در نظر خواهیم گرفت:
1 2 | uint8_t SevenSegNumber[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; uint8_t i =0; |
آرایهای که برای سون سگمنت در نظر گرفتیم، مطابق تصویر زیر که همان دیکدر BCD به سون سگمنت است، نوشته شده است:
پس از تعریف کردن متغیرها برای اینکه سون سگمنت به صورت مستمر شروع به شمارش کند، باید کد زیر درون حلقهی while نوشته شود:
1 2 3 4 5 6 7 8 9 | LL_GPIO_WriteOutputPort(GPIOA, SevenSegNumber[i]); if (((LL_GPIO_ReadInputPort(GPIOB)) & (1<<0)) == 0) { i++; LL_mDelay(200); if(i > 9) i = 0; } |
در ادامه، قطعه کد بالا را با جزئیات کامل مفصلا شرح خواهیم داد.
در اولین خط، از تابع LL_GPIO_WriteOutputPort استفاده کردیم و در ورودیهای آن به ترتیب از GPIOA و SevenSegNumber[i] استفاده کردیم. اجازه بدهید به تعریف تابع برویم تا ببینیم که چه اتفاقی بر روی این دو ورودی خواهد افتاد.
به تعریف تابع توجه کنید:
1 2 3 4 | __STATIC_INLINE void LL_GPIO_WriteOutputPort(GPIO_TypeDef *GPIOx, uint32_t PortValue) { WRITE_REG(GPIOx->ODR, PortValue); } |
همانطور که از تعریف تابع مشخص است، این تابع ورودی دوم، که همان مقدار پورت است را در ورودی اول خود، که یکی از پورتهای 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 باهم خوانده میشوند:
1 2 3 4 | __STATIC_INLINE uint32_t LL_GPIO_ReadInputPort(GPIO_TypeDef *GPIOx) { return (READ_REG(GPIOx->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 یا همان وقفه صحبت خواهیم کرد.
خیلی متشکر از مطالب مفیدتون. بعضی از مطالبی که مطرح کردید در سایت نه تنها در میان سایت های فارسی که در سایت های خارجی هم نمونش نیست. مثل مقایسه ای که بین LL و HAL داشتید.
با تمام قوت ادامه دهید لطفا
سلامت باشید و برقرار
واقعا کمپانی ST خیلی عملکرد ضعیفی داره ارائه میده مخصوصا با ارائه STMcube، شما یه مقایسه سرعت بین کدنویسی رجیستری و کتابخانهHal انجام بدید تا متوجه ضعف این کتابخانه ها بشید.معماری ARM برای سرعت بالا طراحی شده و این نوع کدنویسی مغایر با این تکنولوژی هست. متاسفانه کمپانی ST فرت فرت میکرو بیرون میده و دریغ از رفرنس یا یوزر منوال و تو مثالهاش همش از cube و توابع hal مثال اورده که واقعا جای شرمندگی داره، من میکرو Cortex M7 خریدم و هیچ منوال ازش نبود حتی تو سایت کمپانی، تمام مثالهاش با cube بود. اخه کدوم ادمی میاد میکرو ۴۰۰مگاهرتزی میخره که باهاش LED چشمک زن بسازه، همین مشکل با ARM9 کمپانی اتمل هم داشتم، پیام کلی من به دوستان اینه که توابع اماده و کدنویسی گرافیگی از شما یه مصرف کننده میسازه که تو صنعت هیچ جایی نخواهید داشت و فقط کمپانی از این مسئله سود میبره، امیدوارم موفق باشید
سلام دوست عزیز
من منوال های شرکت ST رو مطالعه کردم، و البته مثال های ارائه شده همراه کتابخانه HAL رو همچنین البته قبول دارم که مثالهاش برای کتابخانه LLکم هستند ولی فکر نمیکنم اونطور هم که شما میگی باشد
من مشکلی به لحاظ داکیومنتی باهاش نداشتم، اپلیکیشن نوت های خیلی خوبی داره، و توی خیلی از موارد جزییات خوبی رو بهش اشاره کرده است، اگر داکیومنت های میکروهای چینی رو ببینید اون موقع مصداق کامل یه داکیومنت ناقصه!!
اما بله تعداد و تنوع میکروهای اس تی خیلی زیاده و به نظر منم این کارش زیاد جالب نیست
واقعا ممنونم از اموزشهای مفیدتون.من مشتاقانه این امووزش ها رو دنبال میکنم و نمونه اش رو ندیدم چون بطور بیسیک توضیح میدید و همچنین نحوه استفاده از منوال ها رو هم ذکر میکنید .امیدوارم به این اموزشها اضافه بشه هرچند ممکنه خاستم نابجا باشه
و یک سوال داشتم.اونجا که کد (1<<0) هست بطور کلی میخاستم بدونم اگر مقداری رو بخایم بخونیم اگه معادل عددیش رو بنویسیم کد نهایی سریعتر و کمحجمتر میشه؟در کل میخاستم بدونم که کامپایلر این عبارت رو قبل از کد خروجی به عدد تبدیل میکنه یا نه و خود این کد تو میکرو اجرا میشه؟؟؟پیشاپیش ممنون از پاسختون
سلام امیدوارم حالتون خوب باشه.
این عملگر: -> چه عملی انجام میده؟
در این عبارت استفاده شده:
return (READ_REG(GPIOx->IDR));
زیرمجموعه رو نشون میده؟ کاری که نقطه در زبانهای دیگه انجام میدن انجام میده؟
همین طور کاراکتر ستاره (*) که در این عبارت استفاده شده:
__STATIC_INLINE uint32_t LL_GPIO_ReadInputPort(GPIO_TypeDef *GPIOx)
ممنونم.
سلام دوست عزیز
ببینید برای دسترسی به اعضای یک ساختار ما از نقطه (.) استفاده میکینم این برای وقتی هست که خود استراکچر را در اختیار داشته باشیم
ولی وقتی که اشاره گر به ساختار رو در اختیار داشته باشیم برای دسترسی به اعضای آن از ->استفاده میکنیم.
عالی میشد اگر 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 بیت در نظرش میگیرد.
سرعت کدوم بیشتره llیاhal
سرعت LL بیشتر است. در قسمت پنجم مفصلا این موضوع را شرح دادم، به قسمت پنجم مراجعه کنید.
فکر کنم این مجموعه آموزش ی 2-3سالی طول بکشه 🙂
شوخی کردم
عالی بود
ممنون
سه سال که یکم زیاده، ولی دو سال رو فکر کنم طول بکشه. ?
سپاس از توجهتان.
سلام آقای جلیلی وقت تون بخیر
بنده لطیفی هستم دانشجوی الکترونیک و مخابرات دریایی و علاقه مند به موضوعات الکترونیکی برای یه پروژه مهم به کمک شما نیاز داریم. هم بحث استفاده از توابع LL در میونه تا پروژه سریعتر اجرا بشه، هم بحث کنش ها و واکنش های سیستم. بنده خیلی خوشحال میشم بتونم باهاتون در مورد پروژه صحبت کنم و اگر امکانش باشه بتونیم باهم همکاری داشته باشیم.
سلام مهندس جان. در جواب به همین کامنت لطفا کامنت بگذارید و شماره تماستون رو قرار بدهید تا باهاتون تماس بگیرم. البته قبلش به دوستان سیسوگ میگم که برای حریم خصوصی شما، کامنت رو در دید دیگران قرار ندهند تا شماره شما خصوصی بماند.