AVR پروتکل i2c سال ۱۴۰۵

saalek110

Well-Known Member
سالک: یک هدر i2c کدویژن دارد...هدر یعنی فایلی که بالای برنامه اینکلود می کنیم.
و میشه از اون هدر استفاده کرد. ولی من اصرار داشتم با رجیسترها کار کنم. ولی وقتی بود که اینترنت ملی بود و گپ gpt ابزار کارم بود...خیلی اوقات کد اتمل استودیو را به جای کدویژن بهم میداد و یا کدهای چیز دیگه را می داد و مقداری اذیت شدم تا توانستم از گپ gpt کدها را بگیرم.....

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

saalek110

Well-Known Member
سالک ، داستان: من می دونستم که هر دیوایس یا وسیله یک شماره داره که وقتی مستر یا ارباب که میکروکنترلر است اینجا ، می حواهد صداش بزنه باید شماره اش را بگه.....مثل معلمی که سر کلاس اسم ۴۰ نفر را می تونه صدا بکنه و دانش آموزان دونه به دونه بلند بشن و بگن حاضر.....

سالک: و من یک برنامه اسکنر از هوش مصنوعی گرفتم تا همه شماره ها را تست کنه تا ببینه ویوایس من شماره اش چیه....

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

برنامه ای هم برای راه اندازی این سون سگمنت ساده باید در میکروکنترلر بریزیم...
من توصیه نمی کنم به شما از سون سگمنت ساده استفاده کنید. ماژولش را بگیرید تا با سیم کم وصل بشه به میکروکنترلر.....

خلاصه سون سگمنت ، چند روزی طول کشید تا راه بیافته....

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


سالک: من دو دیواس مختلف را شماره شان را پیدا کردم ، یکی ماژول ساعت بود و دیگری مبدل برای lcd کارکتری 16*2 ......
پس می خواستم lcd کاراکتری را راه بیاندازم با i2c ....
ولی عاقلانه این بود که وقتی lcd با تعداد زیادی سیم بدون i2c وصل بود و کار میکرد اول شماره مبدل را پیدا می کردم ولی من اول lcd را سیم هایش را کشیدم و بعد مجبور شدم سون سگمنت راه بیاندازم....البته تجربه خوبی بود ولی وقت گرفت....
 

saalek110

Well-Known Member
پس ، نتیجه داستان ، هر دیوایس در پروتکل i2c یک شماره داره که با نوشتن یک کد اسکن می توانید شماره اش را پیدا کنید....اگر هم نمایشگر ندارید که کد اسکنر براتون شماره را چاپ کند...
افراد قبلی که با اون دیوایس کار کردند ، در آموزشهایشان گفته اند هر دیوایس چه شماره ای دارد....

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

ولی مبدل نمایشگر کاراکتری ، فقط یک شماره داشت...۲۷ بود فکر کنم و من دو تا داشتم ، پورت a0 را لحیم کاری کردم دومی را که کدش شد ۲۶...شما اگر لحیم کاری نکردید روی برد را..همون ۲۷ است.
 

saalek110

Well-Known Member
حالا که شماره دیوایس را داریم ، مستر می تونه ، کلی دیوایس را صدا بزنه...مثلا بیست تا نمایشگر که پشتشان مبدل لحیم کردید ، همگی با سیم i2c که دو تاست به میکروکنترلر وصل میشن.....البته ۳ تا جای لحیم کاری داره و شاید بیست جور آدرس نشه باهاش ساخت....ولی مثلا کلی سنسور و چیزهای دیگر که با پروتکل i2c کار می کنند ، می تونن با همون دو سیم وصل بشن به میکروکتترلر...یعنی دهها وسیله ، همگی می آیند روی دو پایه میکروکنترلر وصل میشن...میشه مثل اتوبان....کلی آدرس داخل این اتوبان هست که مستر یعنی میکروکنترلر با صدا زدن هر آدرس ، با اون دیوایس خاص ارتباط برقرار می کند.
برد اتوبان i2c:
من اومدم روی یک برد هزار سوراخ هدر بورد لحیم کردم که هر چی دیوایس i2c ئی داریم از اونجا تغذیه بشن....دو تا مثبت و منفی هم براشون گذاشتم، سیم های i2c خوبه پول آپ یا پول داون ، الان یادم نیست ، یکی از اینها بشه...از هوش مصنوعی عدد مقاومتش را بپرسید....دقیق الان یادم نیست. ۴.۷ کیلو بود یا ۴۷ کیلو...یادم نیست چی بود....
 

saalek110

Well-Known Member
سالک: من با زبون فارسی و به شکل داستان شرح ما وقع را گفتم تا مشکلی در فهم قضیه نداشته باشید.
من خودم کد کار با رجیسترها را کار کردم ولی شاید با هدر کار کردن عاقلانه تر و راحت تر بود...

کدهای کار با رجیسترهای i2c که شاید باید بگیم رجیسترهای twi ، خیلی مزخرف بود....
مثل بونجوق در فرانسوی است که هم معنی سلام می دهد ، هم معنی خوش آمد ، هم معنی دیگر...
در این رجیسترها هم مدام دو تا رجیستر هی تنظیم میشد ولی هر بار برای کار دیگری بود....من که دیدم کدها ، همش یکی است ولی کارهای مختلف انجام میشه....فکر کنم مقصر سازنده میکروکنترلر بوده که نخواسته رجیستر زیاد مصرف کنه و باعث افزایش شدید پیچیدگی شده...
 

saalek110

Well-Known Member
پس twi مربوط به میکروکنترلرهای avr است.. اسم
رجیسترهای مربوطه را بگو


بله، درست است. TWI (Two-Wire Interface) یک نام اختصاصی برای پیاده‌سازی پروتکل I2C در میکروکنترلرهای AVR است.

رجیسترهای اصلی مربوط به TWI در میکروکنترلرهای AVR که برای کنترل و تبادل داده در پروتکل I2C استفاده می‌شوند، عبارتند از:

1. **TWBR (TWI Bit Rate Register):**
* این رجیستر برای تنظیم نرخ بیت (سرعت ارتباط) I2C استفاده می‌شود. مقدار آن با توجه به فرکانس کلاک میکروکنترلر و نرخ بیت مورد نظر محاسبه می‌شود.

2. **TWCR (TWI Control Register):**
* این رجیستر برای کنترل عملیات TWI و وضعیت آن استفاده می‌شود. بیت‌های مختلف این رجیستر وظایف متفاوتی دارند، از جمله:
* `TWINT`: پرچم پایان عملیات TWI.
* `TWEA`: فعال/غیرفعال کردن پاسخ به آدرس TWI (در حالت Slave).
* `TWSTA`: ارسال شرط START.
* `TWSTO`: ارسال شرط STOP.
* `TWEN`: فعال کردن ماژول TWI.
* `TWIE`: فعال کردن وقفه TWI.

3. **TWAR (TWI Address Register):**
* این رجیستر آدرس 7 بیتی میکروکنترلر را در حالت Slave تنظیم می‌کند.

4. **TWDR (TWI Data Register):**
* این رجیستر برای خواندن و نوشتن داده‌هایی که از طریق باس TWI ارسال یا دریافت می‌شوند، استفاده می‌شود.

با دسترسی و تنظیم این رجیسترها، می‌توانید ارتباط I2C را به صورت Master یا Slave در میکروکنترلرهای AVR مدیریت کنید. در محیط کدویژن، معمولاً توابع کتابخانه‌ای سطح بالاتری (مانند `twi_init()`, `twi_start()`, `twi_write()`, `twi_read()`, `twi_stop()`) این رجیسترها را برای شما مدیریت می‌کنند تا کار با آن‌ها ساده‌تر شود.
 

saalek110

Well-Known Member
یک سری توابع در twi.h کدویژن داریم ، آیا نمیشه به جای رجیسترهای twi با اون توابع کار کرد؟

گپ gpt:
کاملاً می‌شود. درواقع **هدف اصلی توابع موجود در twi.h کدویژن این است که شما مجبور نباشید مستقیماً با رجیسترهای TWI کار کنید.**

یعنی:

- اگر بخواهید **سطح پایین** و دقیق کار کنید → از رجیسترهای TWI مثل TWBR، TWCR، TWDR استفاده می‌کنید.
- اگر بخواهید **ساده و سریع** کار کنید → از **توابع سطح بالای CodeVision** در فایل twi.h استفاده می‌کنید.

کدویژن دقیقاً برای همین، مجموعه‌ای از توابع TWI/I2C آماده کرده تا دیگر نیازی به دست‌کاری رجیسترها نباشد.

نمونه‌ای از توابع موجود در CodeVision (twi.h):

- twi_init
- twi_start
- twi_stop
- twi_readAck / twi_readNoAck
- twi_write
- twi_master_transmit
- twi_master_receive
- twi_slave_init
- twi_slave_receive

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

پس:

**بله؛ کاملاً می‌توانید فقط از توابع TWI کدویژن استفاده کنید و اصلاً سراغ رجیسترهای TWI نروید.**

اگر خواستی، می‌توانم:

- یک مثال واقعی Master I2C با توابع twi.h بنویسم
یا
- تفاوت دقیق بین مدل رجیستری و مدل تابعی را توضیح بدهم.
 

saalek110

Well-Known Member
برویم سراغ توابع و رجیسترهای twi .....ما می خواهیم یک کار دقیق و گام به گام و علمی با شرح وقیق هر مرحله انجام دهیم.....ابتدا قسمت آماده سازی را بگو ، یعنی روشن کردن و شروع و ارسال اولین بسته اطلاعات . فعلا برنامه کامل نمی خوام ، چند خط چند خط ، بده کدها را و شرح اون چند خط را بده...از رجیسترها استفاده کن....کاری با توابع موجود در twi.h کد ویژن نداریم...خودن توابعی بساز که با استفاده از رجیسترها ، کار را انجام می دهد

شروع می‌کنیم با **پایه‌ای‌ترین و دقیق‌ترین شکل ممکن**:
کار با **رجیسترهای TWI** در AVR و نوشتن **توابع کاملاً دستی (Low‑Level)**.

در این مرحله فقط هدف ما این‌هاست:

1. فعال‌سازی سخت‌افزار TWI
2. تنظیم سرعت SCL
3. تولید پالس START
4. ارسال اولین بایت (مثلاً آدرس Slave)

پله‌پله جلو می‌رویم و هر بار فقط چند خط کد + شرح آنها را می‌دهم.

------------------------------------

مرحله 1
تنظیم سرعت کلاک SCL و روشن کردن ماژول TWI

کد:
%%%%%%%%%%%% az boof rooy scanner emtehan shod%%%%%%%%%%%%%
شروع ارتباط I2C
TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
while (!(TWCR & (1 << TWINT)));

// ارسال آدرس + بیت نوشتن
TWDR = (addr << 1); // آدرس + بیت W (0)
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));

// بررسی ACK
if ((TWSR & 0xF8) == 0x18) {
// دستگاه پیدا شد!
// می‌توانی آدرس را چاپ کنی یا ذخیره کنی
}

// توقف
TWCR = (1 << TWSTO) | (1 << TWINT) | (1 << TWEN);

%%%%%%%%%%%%%%%%%%% payane az boof %%%%%%%%%%%%%%%%%%%


```c
void TWI_Init(void)
{
// Set SCL frequency
TWBR = 32; // Bit Rate → تنظیم سرعت
TWSR &= ~((1<<TWPS1) | (1<<TWPS0)); // Prescaler = 1

// Enable TWI module
TWCR = (1<<TWEN); // روشن کردن TWI
}
```

شرح کامل:

• رجیستر TWBR
مقدار آن، همراه با پری‌اسکیلر، سرعت خط I2C را تعیین می‌کند.
فرمول سرعت:

SCL = F_CPU / (16 + 2*TWBR*4^Prescaler)

با TWBR=32 و پری‌اسکیلر=1 و F_CPU=8MHz → حدود 100kHz.

• رجیستر TWSR
دو بیت TWPS0 و TWPS1 پری‌اسکیلر هستند.
ما آنها را صفر کردیم تا Prescaler = 1 شود.

• رجیستر TWCR
بیت TWEN را 1 کردیم تا سخت‌افزار TWI فعال شود.
در این مرحله ارتباط هنوز شروع نشده، فقط سخت‌افزار روشن شده.

------------------------------------

مرحله 2
ارسال پالس START روی خط I2C

کد:

```c
void TWI_Start(void)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

// Wait for start to be transmitted
while(!(TWCR & (1<<TWINT)));
}
```

شرح کامل:

• TWINT
با نوشتن 1 در این بیت، وقفه‌ی داخلی TWI پاک می‌شود و عملیات شروع می‌شود.
(در TWI، برای پاک شدن این فلگ باید 1 بنویسیم، نه 0!)

• TWSTA
این بیت باعث ارسال پالس START روی باس I2C می‌شود.

• TWEN
در تمام عملیات TWI باید روشن باشد.

• حلقه while
منتظر می‌ماند تا سخت‌افزار عملیات START را کامل کند.
وقتی TWINT=1 شد یعنی START ارسال شده و ما می‌توانیم ادامه دهیم.

------------------------------------

مرحله 3
ارسال اولین بایت روی باس (مثلاً آدرس Slave)

کد:

```c
void TWI_Write(uint8_t data)
{
TWDR = data; // Load data into data register

TWCR = (1<<TWINT) | (1<<TWEN); // Start transmission

while(!(TWCR & (1<<TWINT))); // Wait until done
}
```

شرح کامل:

• TWDR
رجیستر داده‌ی TWI است. هرچه داخل آن بنویسید، روی باس ارسال می‌شود.

• تنظیم TWCR
دوباره با نوشتن 1 روی TWINT می‌گوییم یک عملیات جدید شروع شود.
TWEN هم باید همیشه فعال باشد.

• while
منتظر می‌ماند تا ارسال کامل شود.

این تابع می‌تواند برای هر بایت استفاده شود؛
اولین بایت معمولاً **آدرس Slave + بیت R/W** است.

------------------------------------

اگر بخواهی، در مرحله بعد می‌توانیم موارد زیر را اضافه کنیم:

• بررسی وضعیت‌ها از طریق رجیستر TWSR (کدهای Status)
• ارسال STOP
• نوشتن چند بایت پشت‌سرهم
• دریافت داده (READ) با ACK و NACK
• ساخت یک کتابخانه mini-TWI با چند تابع کامل

بگو مرحله بعد را شروع کنیم؟
 

saalek110

Well-Known Member
مرحله قبل فقط «ارسال» را فعال کرد.
اکنون می‌رویم سراغ **کنترل علمی و دقیق عملیات با بررسی TWSR** و سپس «STOP».

گام 4
بررسی وضعیت (Status Codes) بعد از START
ما باید مطمئن شویم که **START درست ارسال شده**. برای این کار از رجیستر وضعیت TWSR استفاده می‌کنیم.

کد کوچک:

```c
uint8_t TWI_GetStatus(void)
{
return (TWSR & 0xF8); // ماسک 11111000 برای گرفتن کد وضعیت
}
```

شرح علمی:

• بیت‌های 0،1،2 در TWSR مربوط به پری‌اسکیلر هستند و نباید روی وضعیت اثر بگذارند
بنابراین ماسک 0xF8 (باینری 11111000) فقط کد وضعیت واقعی را نگه می‌دارد.

• بعد از ارسال START، انتظار داریم مقدار زیر دریافت شود:

0x08 → START condition transmitted

اگر مقدار دیگری بود یعنی مشکل داریم.

مثال استفاده:

```c
TWI_Start();
if (TWI_GetStatus() != 0x08)
{
// error: START not transmitted
}
```

گام 5
ارسال آدرس Slave و بررسی نتیجه
در I2C آدرس 7 بیتی است و بیت 0 تعیین می‌کند خواندن است یا نوشتن.

مثلاً برای نوشتن:

```c
void TWI_WriteAddress(uint8_t address)
{
TWDR = address; // SLAVE ADDRESS + R/W BIT
TWCR = (1<<TWINT) | (1<<TWEN);

while(!(TWCR & (1<<TWINT))); // Wait
}
```

پس از ارسال آدرس، باید وضعیت را بخوانیم:

• اگر یک Slave Acknowledge داد، انتظار داریم:

0x18 → SLA+W transmitted and ACK received
(یعنی نوشتن و Slave پاسخ داده)

کد کامل نمونه:

```c
TWI_WriteAddress(0x50<<1); // مثال: آدرس 0x50 برای EEPROM
if (TWI_GetStatus() != 0x18)
{
// error: SLA+W not acknowledged
}
```

شرح علمی:

• در I2C آدرس 7 بیتی است، بنابراین باید آن را یک بیت به چپ شیفت بدهیم
تا بیت صفر برای R/W آزاد شود.

• مقدار 0x18 یکی از مهم‌ترین کدهای TWI است و نشان‌دهنده این است که:
"آدرس نوشته شده و Slave تأیید کرده"

گام 6
ارسال STOP
برای پایان تراکنش:

```c
void TWI_Stop(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
// بعد از نوشتن STOP معمولاً نیازی به انتظار نیست
}
```

شرح:
بیت TWSTO باعث آزاد شدن باس و ارسال STOP می‌شود.

---------------------------------------

این مرحله از کار الان این‌ها را پوشش داد:

• بررسی وضعیت اجرای عملیات
• ارسال آدرس و بررسی ACK
• ارسال STOP

اگر اجازه بدهی، گام بعد شامل موارد زیر می‌شود:

مرحله بعد
• ارسال داده با بررسی ACK
• تابع دقیق WriteData با مدیریت خطا
• شروع خواندن و دریافت داده (READ) با ACK و NACK
• ساخت یک کتابخانه کامل TWI (سطح پایین)

بگو ادامه بدهم؟
 

saalek110

Well-Known Member
در ادامه می‌رویم سراغ **نوشتن داده (Write Byte)** و **دریافت داده (Read Byte)** همراه با بررسی وضعیت و سپس ساخت یک **کتابخانه کامل سطح پایین TWI** مخصوص AVR.

مرحله 7
ارسال یک بایت داده با بررسی ACK

هدف: بعد از ارسال آدرس، بتوانیم یک بایت به Slave بفرستیم و از وضعیت برگشتی مطمئن شویم.

کد تابع:

```c
uint8_t TWI_WriteData(uint8_t data)
{
TWDR = data; // قرار دادن داده در بافر
TWCR = (1<<TWINT) | (1<<TWEN); // شروع ارسال
while(!(TWCR & (1<<TWINT))); // انتظار برای پایان انتقال

uint8_t status = TWI_GetStatus();

if (status == 0x28) // DATA transmitted + ACK received
return 1; // موفق
else
return 0; // خطا
}
```

شرح کامل:

• مقدار 0x28 نشانه این است که:
"داده ارسال شد و Slave آن را ACK کرد"

• این تابع برای هر بایت کاربرد دارد: تنظیم رجیستر، نوشتن در EEPROM، ارسال CMD و...

-------------------------------------

مرحله 8
تابع خواندن یک بایت با ACK

این حالت زمانی استفاده می‌شود که انتظار داریم **بایت بعدی را هم بخوانیم**.

```c
uint8_t TWI_ReadACK(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); // فعال کردن ACK
while(!(TWCR & (1<<TWINT)));

return TWDR; // برگرداندن بایت خوانده شده
}
```

شرح:

• بیت TWEA = 1 یعنی:
"بعد از دریافت این بایت، یک ACK ارسال کن"
یعنی بگو: «بزن بعدی را هم بفرست!»

-------------------------------------

مرحله 9
تابع خواندن یک بایت با NACK

زمانی استفاده می‌شود که **آخرین بایت** است.

```c
uint8_t TWI_ReadNACK(void)
{
TWCR = (1<<TWINT) | (1<<TWEN); // بدون TWEA → ارسال NACK
while(!(TWCR & (1<<TWINT)));

return TWDR;
}
```

شرح:

• اگر TWEA=0 باشد، پس از دریافت بایت، NACK به Slave داده می‌شود
(یعنی «کافیه!»)
• معمولاً بعد از آن STOP فرستاده می‌شود.

-------------------------------------

مرحله 10
ساخت یک مینی‌کتابخانه TWI کاملاً سطح پایین

یک فایل twi_lowlevel.c فرض می‌کنیم:

```c
#include <avr/io.h>

void TWI_Init(void)
{
TWBR = 32;
TWSR &= ~((1<<TWPS1) | (1<<TWPS0));
TWCR = (1<<TWEN);
}

uint8_t TWI_GetStatus(void)
{
return (TWSR & 0xF8);
}

void TWI_Start(void)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
}

void TWI_Stop(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
}

void TWI_Write(uint8_t data)
{
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
}

uint8_t TWI_WriteData(uint8_t data)
{
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));

return (TWI_GetStatus() == 0x28);
}

uint8_t TWI_ReadACK(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while(!(TWCR & (1<<TWINT)));
return TWDR;
}

uint8_t TWI_ReadNACK(void)
{
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
return TWDR;
}
```

مرحله 11
نمونه کد کامل برای نوشتن در یک Slave (مثلاً EEPROM)

```c
TWI_Start();
TWI_Write(0xA0); // آدرس SLA+W → برای EEPROM
if (TWI_GetStatus() != 0x18) goto error;

TWI_WriteData(0x00); // آدرس داخلی حافظه
TWI_WriteData(0x55); // دیتای مورد نظر

TWI_Stop();
```

برای خواندن:

```c
TWI_Start();
TWI_Write(0xA1); // SLA+R
if (TWI_GetStatus() != 0x40) goto error;

uint8_t data = TWI_ReadNACK();
TWI_Stop();
```

-------------------------------------

اگر موافق باشی مرحله بعد شامل این‌هاست:

• پیاده‌سازی Read/Write چندبایتی
• هندل خطاها و ری‌استارت خودکار
• طراحی کتابخانه حرفه‌ای‌تر TWI با timeout
• آموزش Sniffer کردن I2C با Logic Analyzer برای تست عملی

بگو ادامه بدهم.
 

saalek110

Well-Known Member
سالک: قضیه مال دو ماه پیش است و یادم نیست کدهای پستهای بالا را استفاده کردم یا نه ولی چند تا پوشه دیگه هست ، بایل بروم ببینم اونها چیه
 

saalek110

Well-Known Member
در مورد دیوایس هایی که با i2c کار می کنند مثل مبدل pcf8574 یا ماژول ساعت......اینها داخل آی سی شون دستورات خاصی براشون تعریف شده؟ ما میگیم time_get در rtc و ساعت را می گیریم . و نمی رویم داخل ماژول ساعت به عنوان slave برنامه slave بدهیم....قضیه چیه؟



اینجا نکتهٔ مهمی وجود دارد: **دیوایس‌های I2C مثل PCF8574 یا DS3231 (RTC) خودشون یک میکروکنترلر کوچکِ از قبل برنامه‌ریزی‌شده داخل چیپ دارند.**
یعنی *داخل آی‌سی یک «برنامهٔ Slave» دائمی و غیرقابل‌تغییر وجود دارد* که توسط کارخانه نوشته شده است.

به همین دلیل:

- تو **هیچ‌وقت برنامه‌ای داخل آن‌ها نمی‌نویسی**
- تو فقط از **دستورات و رجیسترهایی** که کارخانه تعریف کرده استفاده می‌کنی
- مثل این است که از قبل یک “پروتکل گفتگو” نوشته شده و تو فقط به آن دستور می‌دهی یا داده می‌خوانی

این دقیقاً دلیل راحت بودن استفادهٔ آن‌هاست.

---

## چرا یک RTC یا PCF8574 بدون کدنویسی Slave کار می‌کند؟

### 1. **چیپ خودش یک Slave کامل I2C است**
داخل چیپ:

- آدرس I2C ثابت یا قابل‌تنظیم وجود دارد
- منطق ACK، NACK، استارت، استاپ
- رجیسترها و حافظهٔ داخلی
- منطق پاسخ‌دهی

همه از قبل ساخته شده‌اند.

### 2. **کارخانه مجموعه‌ای از «رجیسترها» تعیین می‌کند**
مثلاً **DS3231** رجیسترهای آماده دارد:

- ثانیه
- دقیقه
- ساعت
- تاریخ
- ماه
- آلارم
- کنترل
- استاتوس

وقتی تو می‌گویی:
`rtc.getTime()`
این تابع در واقع این کارها را پشت پرده انجام می‌دهد:

1. ارسال آدرس اسلیو (مثلاً 0x68)
2. نوشتن شماره رجیستر (مثلاً 0x00 = ثانیه)
3. درخواست خواندن چند بایت
4. تبدیل BCD به عدد معمولی
5. تحویل وقت به برنامهٔ تو

پس تو فقط «درخواست» می‌کنی، اما چیپ خودش «پاسخ» را طبق طراحی خودش می‌دهد.

---

## آیا داخل این چیپ‌ها واقعاً کدی وجود دارد؟
نوع طراحی بسته به چیپ فرق دارد:

### برای IC‌های ساده مثل PCF8574
- میکروکنترلر واقعی ندارند
- ترکیبی از منطق دیجیتال و رجیسترهای داخلی هستند
- ولی همچنان یک «ماشین حالت» I2C داخلی دارند

### برای IC‌های پیچیده‌تر مثل RTC یا سنسورها
- معمولاً *یک میکروکنترلر کوچک ماسک-رام* دارند
- این میکرو داخلش کد ثابت کارخانه‌ای ذخیره شده
- این کد قابل تغییر نیست
- این کد رفتار Slave را ۱۰۰٪ کنترل می‌کند

به همین خاطر مثل یک *دستگاه آماده و هوشمند* عمل می‌کنند.

---

## پس فرق میکروکنترلر Slave (که خودت برنامه‌نویسی می‌کنی) با IC آماده چیست؟

### زمانی که تو از یک **میکروکنترلر** به عنوان Slave استفاده می‌کنی:
- باید خودت برنامهٔ I2C Slave بنویسی
- باید مشخص کنی چه دستوراتی را قبول می‌کند
- باید مشخص کنی چه پاسخی بدهد
- دقیقاً شبیه ساختن یک ماژول جدید I2C است

### زمانی که از یک **IC مثل DS3231** استفاده می‌کنی:
- همه چیز آماده است
- رجیسترها ثابت‌اند
- فقط دستور خواندن/نوشتن می‌دهی
- هیچ برنامه‌نویسی داخلی لازم نیست

---

## یک تشبیه ساده
فرض کن:

- **میکروکنترلر Slave** مثل فردی است که باید اول زبان یاد بگیرد
- **RTC یا PCF8574** مثل فردی است که از قبل زبان بلد است و تو فقط ازش سؤال می‌پرسی

به همین دلیل ارتباط با آن‌ها بسیار ساده‌تر است.

---

اگر خواستی، می‌تونم برایت دقیقاً نشان بدهم که:

- رجیسترهای RTC کجا هستند
- هنگام `getTime()` یا `setTime()` چه دستوراتی دقیقاً روی باس I2C رد و بدل می‌شود
- و چه فرقی با برنامه‌نویسی یک میکرو Slave دارد

بگو تا ادامه بدهم.

جدول کدها:

jadvale code ha.jpg
 
آخرین ویرایش:

saalek110

Well-Known Member
در کدویژن ، آیا i2c توابع سطح بالای twi است؟

بله، در محیط کدویژن (CodeVision)، توابع سطح بالای TWI (Two-Wire Interface) همان توابع I2C هستند. TWI در میکروکنترلرهای AVR (که کدویژن معمولاً برای برنامه‌نویسی آن‌ها استفاده می‌شود) برای پیاده‌سازی ارتباط I2C به کار می‌رود.

بنابراین، وقتی در کدویژن با TWI سروکار دارید، در واقع با پروتکل I2C کار می‌کنید و توابع مربوط به TWI، توابع سطح بالای I2C محسوب می‌شوند.
 

saalek110

Well-Known Member
پس دو تا چیز جدا از هم i2c و twi نداریم ، و فقط توابع سطح بالاتر و توابع سطح پایین تر داریم

گپ gpt:
دقیقاً همین‌طور است. این جمع‌بندی کاملاً درست است:

• **I2C یک پروتکل است.**
• **TWI فقط نامی است که AVR برای همان I2C انتخاب کرده.**
• در نتیجه: I2C و TWI از هم «جدا» نیستند — یکی هستند.

• در AVR فقط دو سطح داریم:
1. **سطح پایین (Low‑Level):**
کار با رجیسترهای TWBR، TWCR، TWDR و …
2. **سطح بالا (High‑Level):**
کار با توابع آماده کتابخانه CodeVision در twi.h
مثل twi_init، twi_write، twi_read و …

بنابراین:

**I2C و TWI یک چیز هستند، فقط نحوه‌ی استفاده متفاوت است (رجیستری یا تابعی).**

اگر خواستی، یک مثال هم‌زمان از هر دو روش (یکی با رجیستر، یکی با توابع آماده) برای مقایسه می‌زنم.
 

saalek110

Well-Known Member
کدی که در میکروکنترلر ریختم برای i2c نمی دونم همین کدهای پستهای بالا بود یا نه...
فعلا روی تبلت هستم نه کامپیوتر ، وقتی رفتم روی کامپیوتر ، کدهایی که ریختم داخل میکروکنترلر را شاید اینجا پست زدم...

باز در این پست تکرار می کنم : شاید لازم نباشه با رجیسترها شما کار کنید ، هدر i2c توابع سطح بالا دارد.
 
بالا