STM32، رابط سریال I2C. STM32، رابط سریال I2C Stm32 رابط i2c شرح ادامه داد

برخی از افراد پای را دوست دارند، برخی نه.

رابط i2c به طور گسترده ای گسترده و مورد استفاده قرار می گیرد. در stm32f4 سه ماژول وجود دارد که این پروتکل را پیاده سازی می کند.
طبیعتا با حمایت کاملاین همه چیز

کار با ماژول به طور کلی مانند سایر کنترلرها است: شما به آن دستور می دهید، آنها را اجرا می کند و نتیجه را گزارش می کند:
I> ما شروع کردیم.
S> باشه فرستادم.
من> باحال، همین الان آدرس را بفرست. مانند این: 0xXX.
S> باشه فرستادم به من گفته شد که ACK. بیایید ادامه دهیم.
من> هنوز زنده ام، خوب. در اینجا شماره ثبت است: 0xYY، - بیایید برویم.
S> ارسال، دریافت ACK.
I> اکنون داده ها را برای او ارسال کنید، این بایت است: 0xZZ.
S> ارسال شد، او با موارد بیشتری موافقت می کند: ACK.
من> لعنت به او، هنوز نه. آنها STOP رفتند.
S> باشه.

و همه چیز تقریباً در این روحیه است.

که در این کنترلرپین های i2c در سراسر پورت ها به صورت زیر پراکنده شده اند:
PB6: I2C1_SCL
PB7: I2C1_SDA

PB8: I2C1_SCL
PB9: I2C1_SDA

PB10: I2C2_SCL
PB11: I2C2_SDA

PA8: I2C3_SCL
PC9: I2C3_SDA
به طور کلی، نگاه کردن به پین ​​اوت تجهیزات جانبی در صفحه 59 راحت است.

با کمال تعجب، برای کار با i2c به همه رجیسترهای آن نیاز دارید، خوشبختانه تعداد کمی از آنها وجود دارد:
I2C_CR1- دستورات به ماژول برای ارسال دستورات/حالت ها و انتخاب حالت های عملیاتی.
I2C_CR2- تنظیم DMA و نشان دادن فرکانس کاری ماژول (2-42 مگاهرتز).
I2C_OAR1- تنظیم آدرس دستگاه (برای برده)، اندازه آدرس (7 یا 10 بیت)؛
I2C_OAR2- تنظیم آدرس دستگاه (در صورت وجود دو آدرس)؛
I2C_DR- ثبت داده ها؛
I2C_SR1- ثبت وضعیت ماژول؛
I2C_SR2- ثبت وضعیت (اگر پرچم‌های ADDR یا STOPF در SR1 تنظیم شده‌اند، Slave باید خوانده شود).
I2C_CCR- تنظیم سرعت رابط؛
I2C_TRISE- تنظیم زمان بندی لبه ها

با این حال، نیمی از آنها از نوع "آن را بنویس و فراموش کن" هستند.

برد STM32F4-Discovery قبلاً یک دستگاه I2C دارد که می توانید با آن تمرین کنید: CS43L22، DAC صوتی. به پین ​​های PB6/PB9 متصل می شود. نکته اصلی این است که فراموش نکنید که سطح بالایی را برای پین PD4 اعمال کنید (~RESET در آنجا قرار می گیرد)، در غیر این صورت DAC پاسخ نمی دهد.

روش نصب تقریباً به شرح زیر است:
1 . اجازه کلاک کردن پورت ها و خود ماژول را بدهید.
ما به پین‌های PB6/PB9 نیاز داریم، بنابراین باید بیت 1 (GPIOBEN) را در رجیستر RCC_AHB1ENR تنظیم کنیم تا پورت فعال شود.
و بیت 21 (I2C1EN) را در رجیستر RCC_APB1ENR تنظیم کنید تا ماژول I2C فعال شود. برای ماژول دوم و سوم، اعداد بیت به ترتیب 22 و 23 هستند.
2 . سپس، پین‌ها پیکربندی می‌شوند: خروجی Oped Drain (GPIO->OTYPER)، حالت عملکرد جایگزین (GPIO->MODER)، و شماره عملکرد جایگزین (GPIO->AFR).
در صورت تمایل، می توانید یک pull-up (GPIO->PUPDR) را پیکربندی کنید، در صورتی که روی برد نباشد (و به هر شکلی که باشد نیاز به کشیدن منبع تغذیه هر دو خط است). شماره I2C همیشه یکسان است: 4. خوب است که یک شماره جداگانه برای هر نوع لوازم جانبی وجود دارد.
3 . فرکانس ساعت فعلی دستگاه جانبی Fpclk1 (بیان شده در مگاهرتز) در رجیستر CR2 نشان داده شده است. همانطور که من متوجه شدم، این برای محاسبه زمان بندی پروتکل های مختلف مورد نیاز است.
ضمناً برای حالت عادی باید حداقل دو و برای حالت سریع حداقل چهار باشد. و اگر به سرعت کامل 400 کیلوهرتز نیاز دارید، باید بر 10 (10، 20، 30، 40 مگاهرتز) نیز تقسیم شود.
حداکثر فرکانس ساعت مجاز: 42 مگاهرتز.
4 . سرعت رابط در رجیستر CCR پیکربندی می‌شود و حالت (عادی/سریع) انتخاب می‌شود.
معنی این است: Tsck = CCR * 2 * Tpckl1، یعنی. دوره SCK متناسب با CCR است (برای حالت سریع همه چیز کمی پیچیده تر است، اما در RM توضیح داده شده است).
5 . حداکثر زمان لبه افزایشی در ثبات TRISE تنظیم شده است. برای حالت استاندارد این زمان 1 میکرو ثانیه است. در رجیستر باید تعداد چرخه های اتوبوس متناسب با این زمان را بعلاوه یک بنویسید:
اگر چرخه Tpclk1 125 ns طول بکشد، بنویسید (1000 ns / 125 ns) + 1 = 8 + 1 = 9.
6 . تولید سیگنال های وقفه (خطا، وضعیت و داده) به صورت اختیاری فعال است.
7 . ماژول روشن می شود: پرچم PE در رجیستر CR1 روی 1 تنظیم شده است.

سپس ماژول همانطور که باید کار می کند. فقط باید ترتیب صحیح دستورات را اجرا کنید و نتایج را بررسی کنید. به عنوان مثال، یک ورودی ثبت نام:
1 . ابتدا باید START را با تنظیم پرچمی با همان نام در رجیستر CR1 ارسال کنید. اگر همه چیز خوب باشد، پس از مدتی پرچم SB در ثبات SR1 تنظیم می شود.
من می خواهم به یک نکته توجه کنم - اگر در خط کشش وجود نداشته باشد (و آنها در 0 هستند)، ممکن است این پرچم اصلا منتظر نباشد.
2 . اگر پرچم دریافت شد، آدرس را ارسال می کنیم. برای یک آدرس هفت بیتی، ما به سادگی آن را در DR دقیقاً همانطور که در خط خواهد بود می نویسیم (7 بیت آدرس + بیت جهت). برای ده بیت، یک الگوریتم پیچیده تر.
اگر دستگاه به آدرس با ACK پاسخ دهد، پرچم ADDR در رجیستر SR1 ظاهر می شود و اگر نه، پرچم AF (Acknowledge شکست) ظاهر می شود.
اگر ADDR ظاهر شد، باید رجیستر SR2 را بخوانید. لازم نیست به چیزی در آنجا نگاه کنید، فقط خواندن متوالی SR1 و SR2 این پرچم را بازنشانی می کند. و در حالی که پرچم تنظیم شده است، SCL توسط Master پایین نگه داشته می شود، که در صورتی مفید است که از دستگاه راه دور بخواهید قبل از ارسال داده منتظر بماند.
اگر همه چیز خوب باشد، بسته به کمترین بیت آدرس ارسال شده، ماژول به حالت دریافت یا انتقال داده می رود. برای نوشتن باید صفر باشد، برای خواندن باید یک باشد.
اما ما به رکورد نگاه می کنیم، بنابراین فرض می کنیم که در آنجا یک صفر وجود دارد.
3 . سپس آدرس ثبت نام مورد علاقه خود را ارسال می کنیم. به همین ترتیب، آن را در DR یادداشت کنید. پس از ارسال، پرچم های TXE (بافر انتقال خالی است) و BTF (انتقال کامل) تنظیم می شوند.
4 . سپس داده‌هایی می‌آیند که می‌توانند در حالی که دستگاه با ACK پاسخ می‌دهد ارسال شود. اگر پاسخ NACK باشد، این پرچم ها تنظیم نمی شوند.
5 . پس از اتمام انتقال (یا در صورت بروز یک وضعیت غیرمنتظره)، STOP را ارسال می کنیم: پرچمی به همین نام در رجیستر CR1 تنظیم می شود.

هنگام خواندن همه چیز یکسان است. فقط پس از نوشتن آدرس ثبت تغییر می کند.
به جای نوشتن داده، START دوباره ارسال می شود (راه اندازی مجدد) و آدرس با کمترین مجموعه بیت (علامت خواندن) ارسال می شود.
ماژول منتظر داده های دستگاه خواهد ماند. برای تشویق آن به ارسال بایت های بعدی، باید قبل از دریافت، پرچم ACK را در CR1 تنظیم کنید (به طوری که پس از دریافت ماژول همان ACK را ارسال کند).
وقتی از آن خسته شدید، پرچم را بردارید، دستگاه NACK را می بیند و بی صدا می شود. پس از آن STOP را به روش معمول ارسال می کنیم و از داده های دریافتی خوشحال می شویم.

در اینجا همان چیزی در قالب کد وجود دارد:
// راه‌اندازی ماژول void i2c_Init(void) (uint32_t ساعت = 16000000UL؛ // فرکانس ساعت ماژول (system_stm32f4xx.c استفاده نمی‌شود) uint32_t سرعت = 100000UL؛ // 100 kHz قابل قفل کردن GCC1-R/// |= RCC_AHB1ENR_GPIOBEN؛ // پین‌های PB6، PB9 را تنظیم کنید // تخلیه را باز کنید! به رجیستر GPIOB->PUPDR مراجعه کنید // شماره تابع جایگزین GPIOB ->AFR &= ~(0x0FUL<< (6 * 4)); // 6 очистим GPIOB->AFR |= (0x04UL<< (6 * 4)); // В 6 запишем 4 GPIOB->AFR &= ~(0x0FUL<< ((9 - 8) * 4)); // 9 очистим GPIOB->AFR |= (0x04UL<< ((9 - 8) * 4)); // В 9 запишем 4 // Режим: альтернативная функция GPIOB->MODER &= ~((0x03UL<< (6 * 2)) | (0x03UL << (9 * 2))); // 6, 9 очистим GPIOB->MODER |= ((0x02UL<< (6 * 2)) | (0x02UL << (9 * 2))); // В 6, 9 запишем 2 // Включить тактирование модуля I2C1 RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // در این مرحله I2C باید خاموش شود // همه چیز را بازنشانی کنید (SWRST == 1، تنظیم مجدد) I2C1->CR1 = I2C_CR1_SWRST; // PE == 0، این چیز اصلی است I2C1->CR1 = 0. // فرض می‌کنیم که از RC (16 مگاهرتز) در حال اجرا هستیم // هیچ پیش‌اسکیل‌کننده‌ای در سیستم ساعت وجود ندارد (همه 1) // به روشی دوستانه، باید همه اینها را از // فرکانس ساعت واقعی ماژول محاسبه کنیم. I2C1->CR2 = ساعت / 1000000UL; // 16 مگاهرتز // تنظیم فرکانس ( // Tclk = (1 / Fperiph)؛ // ران = Tclk * CCR؛ // Tlow = ران؛ // Fi2c = 1 / CCR * 2؛ // CCR = Fperiph / (Fi2c * 2)؛ uint16_t مقدار = (uint16_t) (ساعت / (سرعت * 2))؛ // مقدار حداقل: 4 if(Value< 4) Value = 4; I2C1->CCR = ارزش; ) // تنظیم زمان افزایش حد // در حالت استاندارد، این زمان 1000 ns است // ما به سادگی یک را به فرکانس بیان شده در مگاهرتز اضافه می کنیم (به RM صفحه 604 مراجعه کنید). I2C1->TRISE = (ساعت / 1000000UL) + 1; // فعال کردن ماژول I2C1->CR1 |= (I2C_CR1_PE); // اکنون می توانید کاری انجام دهید ) // ارسال یک بایت bool i2c_SendByte(uint8_t آدرس، uint8_t ثبت نام، داده uint8_t) ( if(!i2c_SendStart()) false را برگرداند؛ // آدرس تراشه if(!i2c_SendAddress(Address_S) را برگردانید i2c ()؛ // ثبت آدرس if(!i2c_SendData(Register)) return i2c_SendStop(); // داده if(!i2c_SendData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); بازگشت true;) // دریافت بایت bool i2c_ReceiveByte(uint8_t آدرس، uint8_t ثبت نام، uint8_t * داده) ( if(!i2c_SendStart()) false; // آدرس تراشه if(!i2c_SendAddress(Address)) بازگشت i2c_SendSendStop آدرس if(!// (! if(!i2c_ReceiveData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) استفاده: ( uint8_t ID = 0; i2c_Init(); // فرض می کنیم که PD4 روی سطح بالایی تنظیم شده است و DAC کار می کند (این کار باید به نحوی انجام شود) // یک بایت به دستگاه با آدرس 0x94 ارسال کنید تا 0x00 با مقدار 0x00 ثبت شود. i2c_SendByte (0x94، 0x00، 0x00)؛ // دریافت یک بایت از دستگاه با آدرس 0x94 از ثبات 0x01 (ID) در بافر متغیر i2c_ReceiveByte(0x94, 0x01, &ID); )
البته جز در یک مثال آموزشی نمی توانید این کار را انجام دهید. انتظار برای تکمیل عمل برای چنین کنترلر سریعی بسیار طولانی است.

(راهنمای توسعه دهنده برای میکروکنترلرهای خانواده HCS08)

برای کنترل ماژول I2C از 6 رجیستر تابع ویژه استفاده می شود:

  • IICC - اولین ثبت کنترل ماژول I2C.
  • IICC2 - ثبت کنترل دوم ماژول I2C؛
  • IICS - ثبت وضعیت ماژول I2C.
  • IICF - ثبت نرخ باود ماژول I2C.
  • IICA - ثبت آدرس ماژول I2C.
  • IICD ثبت داده ماژول I2C است.

MCU سری QE شامل 2 ماژول I2C و بر این اساس، دو رجیستر کنترل از هر نوع است. برای مثال اولین ثبت وضعیت IIC1S و دومین ثبت وضعیت IIC2S است.

11.2.8.1. ثبت کنترل IICC

برای AC سری MK. AW، Dx، EL، GB، GT، JM، LC، QE. Q.G. SG، SH، SL
ثبت نام حالت D7 D6 D5 D4 D3 D2 D1 D0
IICC خواندن IICEN IICIE MST TX TXAK 0 0 0
رکورد RSTA
بازنشانی کنید 0 0 0 0 0 0 0 0
توضیحات بیت:
اسم بیت شرح نماد در زبان C
IICEN بیت فعال ماژول I2C:
0 — کنترلر I2C غیرفعال است.
1 - کنترلر I2C فعال است.
bIICEN
IICIE بیت فعال کننده وقفه ماژول از I2C:
0 - وقفه های درخواست I2C غیرفعال هستند.
1 - وقفه های درخواست I2C فعال هستند.
bHCIE
MST بیت انتخاب حالت عملکرد کنترلر I2C:
0 - کنترلر I2C در حالت Slave کار می کند.
1 - کنترلر I2C در حالت Master کار می کند.
هنگامی که این بیت از 0 به 1 تغییر می کند، یک حالت Start ایجاد می شود. برعکس، وقتی یک بیت از 1 به 0 تغییر می کند، یک شرط Stop ایجاد می شود.
bMST
TX بیت انتخاب جهت انتقال در خط داده SDA:
0 - خط برای ورودی کار می کند.
1 - خط برای خروجی کار می کند.
bTX
TXAK بیت تصدیق در حالت دریافت:
0 - یک بیت تصدیق پس از دریافت یک بایت تولید می شود.
1 - یک بایت بیت تایید نشده تولید می شود.
این بیت تولید یک بیت تصدیق را پس از دریافت بایت داده، بدون توجه به اینکه یک Slave یا Master باشد، کنترل می کند.
bTXAK
RSTA اگر ماژول I2C در حالت اصلی کار می کند، نوشتن یک عدد 1 در این بیت باعث می شود حالت Start - "Restart" دوباره تولید شود. bRSTA

11.2.8.2. ثبت وضعیت IICS

برای سری MK AC، AW، Dx، EL، GB، GT، JM، LC، QE، QG، SG، SH، SL
ثبت نام حالت D7 D6 D5 D4 D3 D2 D1 D0
IICS خواندن TCF IAAS مشغول ARBL 0 SRW IICIF RXAK
رکورد
بازنشانی کنید 0 0 0 0 0 0 0 0
توضیحات بیت:
اسم بیت شرح نماد در زبان C
TCF بیت تکمیل تبادل. پس از اتمام مبادله یک بایت تنظیم کنید:
0 - تعویض کامل نشده است.
1 - تعویض انجام شد.
پرچم TCF زمانی که ثبت داده IICD خوانده می شود (در حالت دریافت) یا زمانی که ثبت داده IICD نوشته می شود (در حالت انتقال) روی 0 پاک می شود.
bTCF
IAAS پرچم آدرس برده. اگر دستگاه در حالت برده کار می کند و آدرس ارسال شده در پیام اصلی برابر است با آدرس برده که در ثبات آدرس IICA ذخیره می شود، تنظیم کنید.
هنگام نوشتن در ثبت IICC، پرچم پاک می شود.
bIAAS
مشغول پرچم خط مشغول این پرچم در صورتی تنظیم می شود که ماژول I2C بیت شروع را روی خط تشخیص داده باشد. هنگامی که ماژول یک بیت توقف را در خط تشخیص دهد، پرچم پاک می شود.
0 - اتوبوس I2C رایگان است.
1 - اتوبوس I2C مشغول است.
bUSY
ARBL پرچم ضرر داوری:
0 - عدم تخلف در عملکرد اتوبوس I2C.
1- ضرر در داوری وجود دارد. ماژول I2C باید مدتی صبر کند و سپس دوباره عملیات انتقال را آغاز کند.
bARBL
SRW بیت جهت انتقال برد. این بیت وضعیت بیت R/W را در فیلد آدرس نشان می دهد:
0 - برده می پذیرد. رهبر به غلام منتقل می کند;
1 - برده انتقال می دهد. رهبر از غلام دریافت می کند.
bSRW
IICIF پرچم درخواست‌های وقفه بدون سرویس برای ماژول I2C. اگر یکی از پرچم‌ها تنظیم شده است: TCF، IAAS یا ARBL را روی 1 تنظیم کنید.
0 - بدون وقفه بدون سرویس
1 - وقفه های بدون سرویس وجود دارد.
پرچم با نوشتن 1 روی آن بازنشانی می شود.
biICIF
RXAK استاد کمی تصدیق:
0-داده های دریافتی تایید شده توسط برده؛
1 - برده دریافت داده را تأیید نکرد.
این بیت وضعیت فیلد ASK را در نمودار زمان بندی تبادل نشان می دهد.
bRXAK

11.2.8.3. ثبت آدرس ICA

ثبت نام حالت D7 D6 D5 D4 D3 D2 D1 D0
IICA خواندن ADDR
رکورد
بازنشانی کنید 0 0 0 0 0 0 0 0

این رجیستر آدرس برده 7 بیتی را که توسعه دهنده اختصاص داده ذخیره می کند این دستگاههنگام توسعه سیستم این آدرس به طور خودکار با کد آدرسی که Slave در قسمت آدرس در گذرگاه I2C دریافت کرده است مقایسه می شود. اگر آدرس ها مطابقت داشته باشند، بیت IAAS در ثبت وضعیت IICS تنظیم می شود.

11.2.8.4. IICF Baud Rate Register

برای سری MK AC، AW، Dx، EL، GB، GT، JM، LC، QE، QG، SG، SH، SL
ثبت نام حالت D7 D6 D5 D4 D3 D2 D1 D0
IICF خواندن MULT ICR
رکورد
بازنشانی کنید 0 0 0 0 0 0 0 0
توضیحات بیت:

مقادیر ضرایب SCL_DIV و SDA_HV

ISR SCL_DIV SDA_HV ISR SCL_DIV SDA_HV
0x00 20 7 0x20 160 17
0x01 22 7 0x21 192 17
0x02 24 8 0x22 224 33
0x03 26 8 0x23 256 33
0x04 28 9 0x24 288 49
0x05 30 9 0x25 320 49
0x06 34 10 0x26 384 65
0x07 40 10 0x27 480 65
0x08 28 7 0x28 320 33
0x09 32 7 0x29 384 33
0x0A 36 9 0x2A 448 65
0x0B 40 9 0x2B 512 65
0x0C 44 11 0x2C 576 97
0x0D 48 11 0x2D 640 97
0x0E 56 13 0x2E 768 129
0x0F 68 13 0x2F 960 129
0x10 48 9 0x30 640 65
0x11 56 9 0x31 768 65
0x12 64 13 0x32 896 129
0x13 72 13 0x33 1024 129
0x14 80 17 0x34 1152 193
0x15 88 17 0x35 1280 193
0x16 104 21 0x36 1536 257
0x17 128 21 0x37 1920 257
0x18 80 9 0x38 1280 129
0x19 96 9 0x39 1536 129
0x1A 112 17 0x3A 1792 257
0x1B 128 17 0x3B 2048 257
0x1C 144 25 0x3C 2304 385
0x1D 160 25 0x3D 2560 385
0x1E 192 33 0x3E 3072 513
0x1F 240 33 0x3F 3840 513

این ثبات دو فیلد بیتی را ذخیره می کند که پارامترهای سرعت و زمان تبادل I2C را تعیین می کند. فرکانس سیگنال های همگام سازی با فرمول تعیین می شود:

زمان تسویه داده SDA_hold_time در گذرگاه I2C فاصله زمانی بین لحظه ای که سیگنال SCL روی 0 تنظیم می شود و داده های روی خط SDA تغییر می کند. توسط پارامتر SDA_HV (SDA_Hold_Value) از جدول برای ضریب ICR ثبت نرخ باود اختصاص داده شده است:

.

11.2.8.5. ثبت داده های IICD

برای سری MK AC، AW، Dx، EL، GB، GT، JM، LC، QE، QG، SG، SH، SL
ثبت نام حالت D7 D6 D5 D4 D3 D2 D1 D0
IICD خواندن داده های I2C
رکورد
بازنشانی کنید 0 0 0 0 0 0 0 0

اگر ماژول I2C در حالت اصلی کار می کند، یک عملیات نوشتن در این رجیستر ارتباط I2C را آغاز می کند (اما تنها در صورتی که بیت جهت ارتباط در ثبات کنترل IICC به درستی تنظیم شده باشد، یعنی TX = 1). اولین بایت بعد از حالت Start که برنامه در رجیستر داده می نویسد، توسط بردها به عنوان آدرس دستگاه تفسیر می شود. بنابراین، برنامه باید محتویات اولین بایت را به درستی تشکیل دهد. عملیات خواندن ثبت آخرین بایت دریافت شده از طریق I2C را برمی گرداند. عملیات خواندن رجیستر نیز شروع دریافت بایت بعدی را آغاز می کند، اما تنها در صورتی که بیت جهت ارتباط در رجیستر کنترل IICC به درستی تنظیم شده باشد، یعنی. در TX = 0! با TX = 1، عملیات خواندن رجیستر باعث دریافت بایت جدید از طریق I2C از Slave نمی شود.

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

11.2.8.6. ثبت کنترل IICC2

برای سری MK AC، Dx، EL، JM، QE، SG، SH، SL
ثبت نام حالت D7 D6 D5 D4 D3 D2 D1 D0
IICC خواندن GCAEN ADEXT 0 0 0 10 میلادی AD9 AD8
رکورد
بازنشانی کنید 0 0 0 0 0 0 0 0

مراحل اول با کامپایلر STM32 و mikroC برای معماری ARM - قسمت 4 - اتصال LCD مبتنی بر I2C، pcf8574 و HD4478

من می خواهم مقاله بعدی را به کار با رابط مشترک i2c اختصاص دهم که اغلب در ریزمدارهای مختلف متصل به یک میکروکنترلر استفاده می شود.

I2C اتوبوسی است که روی دو اتصال فیزیکی (علاوه بر سیم مشترک) کار می کند. در اینترنت چیزهای زیادی در مورد آن نوشته شده است؛ مقالات خوبی در ویکی پدیا وجود دارد. علاوه بر این، الگوریتم عملیات اتوبوس بسیار واضح توضیح داده شده است. به طور خلاصه، اتوبوس یک باس سنکرون دو سیمه است. حداکثر 127 دستگاه می توانند همزمان در اتوبوس باشند (آدرس دستگاه 7 بیتی است، بعداً به این موضوع خواهیم پرداخت). در زیر یک نمودار معمولی برای اتصال دستگاه ها به گذرگاه i2c با MK به عنوان دستگاه اصلی ارائه شده است.


برای i2c، همه دستگاه ها (اعم از master و slave) از خروجی های open-drain استفاده می کنند. به عبارت ساده، آنها فقط می توانند تایر را به زمین جذب کنند. سطح بالای اتوبوس توسط مقاومت های کششی تضمین می شود. مقدار این مقاومت ها معمولاً در محدوده 4.7 تا 10 کیلو اهم انتخاب می شود. i2c به خطوط فیزیکی متصل کننده دستگاه ها کاملاً حساس است، بنابراین اگر از یک اتصال با ظرفیت بزرگ استفاده شود (مثلاً یک کابل بلند نازک یا محافظ)، تأثیر این ظرفیت می تواند لبه های سیگنال را تار کند و با آن تداخل ایجاد کند. عملکرد عادیلاستیک ماشین. هرچه مقاومت pull-up کوچکتر باشد، این ظرفیت تأثیر کمتری بر ویژگی های لبه های سیگنال دارد، اما بار روی ترانزیستورهای خروجی در رابط های i2c بیشتر است. مقدار این مقاومت ها برای هر پیاده سازی خاص انتخاب می شود، اما نباید کمتر از 2.2 کیلو اهم باشد، در غیر این صورت می توانید به سادگی ترانزیستورهای خروجی را در دستگاه هایی که با گذرگاه کار می کنند رایت کنید.

گذرگاه از دو خط تشکیل شده است: SDA (خط داده) و SCL (سیگنال ساعت). ساعت دستگاه bus Master، معمولا MK ما. هنگامی که SCL بالا است، اطلاعات از گذرگاه داده خوانده می شود. وضعیت SDA را فقط زمانی می توان تغییر داد که سیگنال ساعت کم باشد.. هنگامی که SCL بالا است، سیگنال در SDA هنگام تولید سیگنال تغییر می کند شروع کنید (وقتی SCL بالا است سیگنال SDA از زیاد به پایین تغییر می کند) و متوقف کردن - هنگامی که سطح SCL بالا باشد، سیگنال SDA از پایین به بالا تغییر می کند.

به طور جداگانه باید گفت که در i2c آدرس به صورت یک عدد 7 بیتی مشخص شده است. 8 - بیت کم اهمیت نشان دهنده جهت انتقال داده است 0 - به این معنی است که برده داده ها را منتقل می کند، 1 - دریافت می کند.. به طور خلاصه الگوریتم کار با i2c به شرح زیر است:

  • سطح بالا در SDA و SCL- اتوبوس رایگان است، می توانید شروع به کار کنید
  • استاد بالابر SCLبه 1 و تغییر حالت می دهد S.D.A.از 1 تا 0 - آن را به زمین جذب می کند - یک سیگنال تشکیل می شود شروع کنید
  • Master یک آدرس slave 7 بیتی را با یک بیت جهت (داده در S.D.A.زمانی به نمایش گذاشته می شوند SCLبه زمین کشیده شود و غلام هنگام رها شدن آن را بخواند). اگر برده زمان "چاپ کردن" بیت قبلی را نداشته باشد، جذب می کند SCLبه زمین، و به استاد روشن می کند که وضعیت گذرگاه داده نیازی به تغییر ندارد: "من هنوز دارم قبلی را می خوانم." بعد از اینکه استاد لاستیک را رها کرد، چک می کند آیا غلام او را رها کرد؟.
  • پس از انتقال 8 بیت از آدرس، Master سیکل ساعت 9 را تولید می کند و گذرگاه داده را آزاد می کند. اگر غلام آدرس او را شنید و آن را پذیرفت، پس او فشار خواهد داد S.D.A.به زمین. به این ترتیب سیگنال تشکیل می شود پرسیدن- قبول، همه چیز اوکی است. اگر برده چیزی نفهمد، یا به سادگی آنجا نباشد، کسی نیست که لاستیک را فشار دهد. استاد منتظر تایم اوت می ماند و متوجه می شود که او متوجه نشده است.
  • پس از انتقال آدرس، اگر جهت را تنظیم کرده باشیم از ارباب تا برده(8 بیت از آدرس برابر با 1 است)، سپس Master داده ها را به Slave منتقل می کند، فراموش نمی کند که وجود آن را بررسی کند. پرسیدناز Slave، منتظر است تا دستگاه Slave اطلاعات دریافتی را پردازش کند.
  • وقتی Master داده‌ها را از Slave دریافت می‌کند، Master خودش یک سیگنال تولید می‌کند پرسیدنپس از دریافت هر بایت، و Slave حضور آن را کنترل می کند. ممکن است استاد به طور خاص ارسال نکند پرسیدنقبل از ارسال فرمان متوقف کردن، معمولاً به Slave روشن می کند که نیازی به ارائه داده های بیشتری نیست.
  • اگر پس از ارسال داده ها توسط master (حالت نوشتن)، نیاز به خواندن داده ها از Slave باشد، سپس Master دوباره سیگنال را تولید می کند شروع کنید ، آدرس برده را با پرچم خوانده شده ارسال می کند. (اگر قبل از دستور شروع کنیدمنتقل نشد متوقف کردنسپس یک تیم تشکیل می شود راه اندازی مجدد). این برای تغییر جهت ارتباط master-slave استفاده می شود. به عنوان مثال، ما آدرس ثبت نام را به Slave ارسال می کنیم و سپس داده ها را از آن می خوانیم.)
  • پس از اتمام کار با برده، Master یک سیگنال تولید می کند متوقف کردن- در سطح بالایی از سیگنال ساعت، یک انتقال گذرگاه داده از 0 به 1 را تشکیل می دهد.
STM 32 دارای فرستنده گیرنده های باس i2c با سخت افزار است. ممکن است 2 یا 3 ماژول از این دست در یک MK وجود داشته باشد. برای پیکربندی آنها، از ثبات های خاصی استفاده می شود که در مرجع برای MK مورد استفاده توضیح داده شده است.

در MicroC، قبل از استفاده از i2c (و همچنین هر وسیله جانبی)، باید به درستی مقداردهی اولیه شود. برای این کار از تابع زیر (Initialization as master) استفاده می کنیم:

I2Cn_Init_Advanced(طولانی بدون علامت: I2C_ClockSpeed، const Module_Struct *module);

  • n- برای مثال تعداد ماژول استفاده شده I2C1یا I2C2.
  • I2C_ClockSpeed- سرعت اتوبوس، 100000 (100 کیلوبایت، حالت استاندارد) یا 400000 (400 کیلوبایت، حالت سریع). دومی 4 برابر سریعتر است، اما همه دستگاه ها از آن پشتیبانی نمی کنند
  • *مدول- برای مثال اشاره گر به یک ماژول جانبی &_GPIO_MODULE_I2C1_PB67، در اینجا فراموش نکنیم که دستیار کد (ctrl-space ) کمک زیادی می کند.
ابتدا، بیایید بررسی کنیم که آیا اتوبوس رایگان است یا خیر؛ یک تابع برای این وجود دارد I2Cn_Is_Idle();در صورت رایگان بودن اتوبوس، عدد 1 را برمی‌گرداند و در صورت تعویض 0.

I2Cn_Start();
جایی که n- شماره ماژول i2c استفاده شده میکروکنترلر ما. اگر خطایی در اتوبوس وجود داشته باشد، این تابع 0 و اگر همه چیز درست باشد، 1 را برمی‌گرداند.

برای انتقال داده به Slave از تابع استفاده می کنیم:

I2Cn_Write( char slave_address بدون علامت، char بدون علامت *buf، تعداد طولانی بدون امضا، حالت طولانی بدون امضا END_mode);

  • n- تعداد ماژول استفاده شده
  • برده_آدرس- آدرس برده 7 بیتی.
  • * بوف- یک اشاره گر به داده های ما - یک آرایه بایت یا بایت.
  • شمردن- تعداد بایت های داده منتقل شده
  • حالت END_- پس از انتقال داده ها به برده چه باید کرد، END_MODE_STOP - انتقال سیگنال متوقف کردن، یا END_MODE_RESTART دوباره بفرست شروع کنید، یک سیگنال تولید می کند راه اندازی مجددو به بخش روشن می کند که جلسه با او تمام نشده است و اکنون داده ها از او خوانده می شود.
برای خواندن داده ها از Slave، از تابع استفاده کنید:

I2Cn_Read(char slave_address، char *ptrdata، تعداد طولانی بدون علامت، حالت END_مدت طولانی بدون علامت)؛

  • n- تعداد ماژول استفاده شده
  • برده_آدرس- آدرس برده 7 بیتی.
  • * بوف- یک اشاره گر به یک متغیر یا آرایه که در آن داده ها را دریافت می کنیم، تایپ کنید char یا short int
  • شمردن- تعداد بایت های داده دریافتی
  • حالت END_- پس از دریافت اطلاعات از برده چه باید کرد - END_MODE_STOP - انتقال سیگنال متوقف کردن، یا END_MODE_RESTART یک سیگنال ارسال کنید راه اندازی مجدد.
بیایید سعی کنیم چیزی را به MK خود متصل کنیم. برای شروع: میکرو مدار گسترده PCF8574(A) که یک گسترش دهنده پورت های ورودی/خروجی است که از طریق گذرگاه i2c کنترل می شود. این تراشه فقط یک رجیستر داخلی دارد که پورت ورودی/خروجی فیزیکی آن است. یعنی اگر یک بایت را به او منتقل کنید، بلافاصله در معرض نتیجه گیری او قرار می گیرد. اگر یک بایت از آن را بشمارید (Transmit شروع کنیدآدرس با پرچم خوانده شده، سیگنال RESTERT،داده ها را بخوانید و در نهایت یک سیگنال تولید کنید متوقف کردن) سپس حالت های منطقی را روی خروجی های خود منعکس می کند. بیایید ریز مدار خود را مطابق با دیتاشیت وصل کنیم:


آدرس ریز مدار از حالت پین ها تشکیل می شود A0، A1، A2. برای ریز مدار PCF8574آدرس خواهد بود: 0100A0A1A2. (به عنوان مثال، ما A0، A1، A2 در سطح بالایی داریم، بنابراین آدرس میکرو مدار ما 0b0100 خواهد بود. 111 = 0x27). برای PCF8574A - 0111A0A1A2، که با نمودار اتصال ما آدرس 0b0111 را می دهد 111 = 0x3F. اگر مثلا A2 به زمین متصل است، آدرس برای PCF8574Aاراده 0x3B. در مجموع، 16 میکرو مدار را می توان به طور همزمان بر روی یک باس i2c، 8 PCF8574A و PCF8574 نصب کرد.

بیایید سعی کنیم چیزی را انتقال دهیم، گذرگاه i2c را مقداردهی اولیه کنیم و چیزی را به PCF8574 خود منتقل کنیم.

#define PCF8574A_ADDR 0x3F //آدرس PCF8574 void ما I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // ایجاد سیگنال START I2C1_Write(PCF85D,Op_MOD,END_DF,PCF85,PCF85,PCF85,100/1/4/4/1/1) 1 بایت داده و STOP تولید کنید سیگنال) char PCF8574A_reg ; // متغیری که در PCF8574 void main () می نویسیم ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67)؛ // شروع I2C delay_ms(25)؛ // کمی صبر کنید PCF8574A_reg.b0 کمی صبر کنید. PCF8574A_reg_b0 =0 = 1؛ // چراغ دوم را خاموش کنید در حالی که (1) (delay_ms(500)؛ PCF8574A_reg.b0 = ~PCF8574A_reg.b0؛ PCF8574A_reg.b1 = ~PCF8574A_reg.b1؛ // معکوس کردن وضعیت LED ها ؛ // انتقال داده به PCF8574 ما ))
ما برنامه خود را کامپایل و اجرا می کنیم و می بینیم که LED های ما به طور متناوب چشمک می زند.
من به دلیلی کاتد LED را به PCF8574 خود وصل کردم. موضوع این است که وقتی یک 0 منطقی به خروجی داده می شود، ریز مدار صادقانه خروجی خود را به زمین می کشد، اما وقتی یک منطقی 1 اعمال می شود، آن را از طریق منبع جریان 100 میکروآمپر به برق + وصل می کند. یعنی نمی توانید یک منطقی 1 "صادقانه" را در خروجی دریافت کنید. و نمی توانید LED را با 100 میکروآمپر روشن کنید. این کار به منظور پیکربندی خروجی PCF8574 در ورودی بدون رجیسترهای اضافی انجام شد. ما به سادگی در رجیستر 1 خروجی می نویسیم (در اصل حالت های پین را روی Vdd قرار می دهیم) و به سادگی می توانیم آن را به زمین کوتاه کنیم. منبع فعلی اجازه نمی دهد که مرحله خروجی توسعه دهنده ورودی/خروجی ما "سوخته شود". اگر پا به زمین کشیده شود، پتانسیل زمین روی آن است و 0 منطقی خوانده می شود. هنگام کار با این ریز مدارها باید همیشه این را به خاطر بسپارید.


بیایید سعی کنیم وضعیت پین های تراشه گسترش دهنده خود را بخوانیم.

#define PCF8574A_ADDR 0x3F //آدرس PCF8574 ما void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // ایجاد سیگنال START I2C1_Write(PCFMODAT/END_ST) 1 بایت داده و STOP تولید کنید signal) void I2C_PCF8574_ReadReg (char rData بدون علامت) (I2C1_Start(); // سیگنال START I2C1_Read را ایجاد کنید(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // خواندن 1 بایت علامت PCF و 1 بایت char STOPF; //متغیری که در PCF8574 char PCF8574A_out می نویسیم. // متغیری که در آن خواندیم و PCF8574 char lad_state; //LED ما روشن یا خاموش است اصلی خالی () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67)؛ // شروع I2C delay_ms(25)؛ // کمی صبر کنید PCF8574A_reg.b0 = 0؛ // reg51 اولین LED PC را روشن کنید. 1؛ / / خاموش کردن LED دوم PCF8574A_reg.b6 = 1؛ // پین های 6 و 7 را به برق بکشید. PCF8574A_reg.b7 = 1؛ در حالی که (1) (delay_ms(100); I2C_PCF8574_WriteReg (PCF8574A/data)؛ به PCF8574 I2C_PCF8574_READREG (PCF8574 A_OUT) ؛ // از PCF8574 IF (~ PCF8574A_OUT.B6) PCF8574A_REG.B0 = ~ PCF8574A_REG.B0 ؛ // بخوانید. LED ما را روشن/خاموش کنید) اگر (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1؛ // مشابه برای 2 دکمه و 2 LED ) )
حالا با فشار دادن دکمه ها LED خود را روشن یا خاموش می کنیم. میکرو مدار خروجی دیگری دارد INT. هر بار که وضعیت پین های منبسط کننده ورودی/خروجی ما تغییر می کند، یک پالس روی آن تولید می شود. با اتصال آن به ورودی وقفه خارجی MK ما (در یکی از مقالات زیر به شما خواهم گفت که چگونه وقفه های خارجی را پیکربندی کنید و چگونه با آنها کار کنید).

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


دیتاشیت آن و کنترلر HD44780 را می توان به راحتی در اینترنت پیدا کرد. بیایید صفحه نمایش خود را به PCF8574 و صفحه نمایش او را به ترتیب به STM32 وصل کنیم.

HD44780از یک رابط دروازه ای موازی استفاده می کند. داده ها توسط 8 پالس گیت (در یک سیکل ساعت) یا 4 (در 2 سیکل ساعت) در خروجی ارسال می شود. E. (خوانده شده توسط کنترل کننده نمایشگر در لبه نزولی، انتقال از 1 به 0). نتیجه R.S.نشان می دهد که آیا ما داده ها را به صفحه نمایش خود ارسال می کنیم ( RS = 1) (کاراکترهایی که باید نمایش دهد در واقع کدهای ASCII هستند) یا دستور ( RS = 0). RWجهت انتقال داده، نوشتن یا خواندن را نشان می دهد. معمولاً داده ها را روی صفحه نمایش می نویسیم، بنابراین ( RW=0). مقاومت R6 کنتراست نمایشگر را کنترل می کند. شما نمی توانید به سادگی ورودی تنظیم کنتراست را به زمین یا برق وصل کنید، در غیر این صورت چیزی نمی بینید.. VT1 برای روشن و خاموش کردن نور پس زمینه نمایشگر طبق دستورات MK استفاده می شود. MicroC کتابخانه ای برای کار با چنین نمایشگرهایی از طریق یک رابط موازی دارد، اما معمولاً صرف 8 پایه روی نمایشگر گران است، بنابراین من تقریباً همیشه از PCF8574 برای کار با چنین صفحه نمایش هایی استفاده می کنم. (اگر کسی علاقه مند است، مقاله ای در مورد کار با نمایشگرهای مبتنی بر HD44780 که در MicroC از طریق یک رابط موازی تعبیه شده است، خواهم نوشت.) پروتکل تبادل به خصوص پیچیده نیست (ما از 4 خط داده استفاده خواهیم کرد و اطلاعات را در 2 سیکل ساعت انتقال خواهیم داد). با نمودار زمان بندی زیر به وضوح نشان داده شده است:


قبل از انتقال داده ها به صفحه نمایش ما، باید با عبور دادن دستورات سرویس مقداردهی اولیه شود. (توضیح داده شده در دیتاشیت، در اینجا فقط موارد استفاده شده را ارائه می دهیم)

  • 0x28- ارتباط با نشانگر از طریق 4 خط
  • 0x0C- خروجی تصویر را فعال کنید، نمایش مکان نما را غیرفعال کنید
  • 0x0E- فعال کردن خروجی تصویر، فعال کردن نمایش مکان نما
  • 0x01- نشانگر را پاک کنید
  • 0x08- غیر فعال کردن خروجی تصویر
  • 0x06- پس از نمایش نماد، مکان نما به اندازه 1 مکان آشنا حرکت می کند
از آنجایی که ما باید اغلب با این نشانگر کار کنیم، یک کتابخانه افزونه ایجاد خواهیم کرد "i2c_lcd.h" . برای این منظور در مدیر پروژه فایل های سرصفحه و انتخاب کنید افزودن فایل جدید . بیایید فایل هدر خود را ایجاد کنیم.

#define PCF8574A_ADDR 0x3F //Address of our PCF8574 #define DB4 b4 // مطابقت بین پین های PCF8574 و نشانگر #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 bdefin #define BL b0 // کنترل نور پس زمینه #define displenth 20 // تعداد کاراکترها در خط نمایش ما static unsigned char BL_status; // متغیر ذخیره کننده وضعیت نور پس زمینه (روشن/خاموش) void lcd_I2C_Init(void); // نمایش و تابع مقداردهی اولیه PCF8574 void lcd_I2C_txt(char *pnt); // یک خط متن را نمایش می دهد، پارامتر یک اشاره گر به این خط است void lcd_I2C_int(int pnt); // مقدار یک متغیر عدد صحیح را نمایش می دهد، پارامتر مقدار خروجی void lcd_I2C_Goto است(ردیف کوتاه بدون علامت، col کوتاه بدون علامت); // مکان نما را به موقعیت مشخص شده حرکت می دهد، پارامترهای سطر - خط (از 1 تا 2 یا 4 بسته به نمایشگر) و col - (از 1 تا displenth)) void lcd_I2C_cls(); // فضای خالی صفحه نمایش lcd_I2C_backlight (وضعیت int کوتاه بدون علامت) را پاک می کند. // فعال می کند (هنگام ارسال 1 و غیرفعال کردن - هنگام انتقال 0 نور پس زمینه نمایشگر)
حالا بیایید توابع خود را شرح دهیم، دوباره به سراغ آن می رویم مدیر پروژهروی پوشه کلیک راست کنید منابع و انتخاب کنید افزودن فایل جدید . یک فایل ایجاد کنید "i2c_lcd.c" .

#include "i2c_lcd.h" //include header file char lcd_reg; //ثبت ذخیره موقت برای داده های ارسال شده به PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) //عملکرد ارسال داده از طریق i2c به تراشه PCF8574 ( I2C1_Start(); I2C1_Write(PCF_AD,MOid_DE,END_DA); فرمان (شخصیت com) / /عملکرد ارسال یک فرمان به صفحه نمایش ما ( lcd_reg = 0؛ //0 را در ثبت موقت بنویسید lcd_reg.BL = BL_status.b0؛ //پایه نور پس زمینه را مطابق با مقدار متغیر ذخیره کننده نور پس زمینه تنظیم کنید. حالت lcd_reg.DB4 = com.b4؛ // 4 بیت مهم دستور خود را روی گذرگاه داده نشانگر تنظیم کنید lcd_reg.DB5 = com.b5؛ lcd_reg.DB6 = com.b6؛ lcd_reg.DB7 = com.b7؛ lcd_reg .EN = 1؛ //خروجی بارق را روی 1 تنظیم کنید I2C_PCF8574_WriteReg ( lcd_reg)؛ //نوشتن در رجیستر PCF8574، در واقع داده ها را به نشانگر delay_us (300) ارسال می کند. پالس بارق را روی 0 تنظیم مجدد کنید، نشانگر داده‌های I2C_PCF8574_WriteReg (lcd_reg) را می‌خواند؛ delay_us (300)؛ lcd_reg = 0؛ lcd_reg.BL = BL_status.b0؛ lcd_reg.DB4 = کمترین مقدار برای com//sa.b. بیت lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.EN = 1; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); ) void LCD_CHAR (char com بدون علامت) //ارسال داده (کد کاراکتر ASCII) به نشانگر ( lcd_reg = 0؛ lcd_reg.BL = BL_status.b0؛ lcd_reg.EN = 1؛ lcd_reg.RS = 1؛ //ارسال یک کاراکتر با ارسال دستورات با تنظیم بیت RS روی 1 تفاوت دارد lcd_reg.DB4 = com.b4؛ //تنظیم 4 بیت مهم در ورودی ها = com.b7؛ I2C_PCF8574_WriteReg (lcd_reg)؛ delay_us (300)؛ lcd_reg.EN = 0؛ // پالس بارق را به 0 بازنشانی کنید، نشانگر داده ها را می خواند I2C_PCF8574_WriteReg (lcdreg_0); .BL = BL_status.b0؛ lcd_reg.EN = 1؛ lcd_reg.RS = 1؛ lcd_reg.DB4 = com.b0؛ //تنظیم 4 بیت کم اهمیت در ورودی ها lcd_reg.DB5 = com.b1؛ lcd_reg.DB6 = com.b2؛ lcd_reg.DB7 = com.b3؛ I2C_PCF8574_WriteReg ( lcd_reg)؛ delay_us (300)؛ lcd_reg.EN = 0؛ I2C_PCF8574_WriteReg (lcd_reg)؛ (20l_it) (20l) (voC) _Init_Advanced(400000، &_GPIO_MODULE_I2C1_PB67 // ماژول I2c ما را در M‎K delay_ms(200) راه اندازی کنید؛ lcd_Command(0x28)؛ // نمایش در 4 بیت در هر حالت ساعت delay_ms (5); lcd_Command(0x08); //غیرفعال کردن خروجی داده به صفحه نمایش delay_ms (5); lcd_Command(0x01); //پاک کردن صفحه نمایش delay_ms (5); lcd_Command(0x06); //فعال کردن تغییر خودکار مکان نما پس از نمایش نماد delay_ms (5); lcd_Command(0x0C); //روشن کردن نمایش اطلاعات بدون نمایش مکان نما delay_ms (25); ) void lcd_I2C_txt(char *pnt) // خروجی رشته ای از کاراکترها به نمایشگر (int کوتاه بدون علامت؛ // متغیر شاخص آرایه کاراکتر موقت char tmp_str؛ //آرایه موقت کاراکترها، طول 1 بزرگتر از طول نمایشگر خط، از آنجایی که خط باید сiv با یک کاراکتر ASCII NULL خاتمه یابد 0x00 strncpy(tmp_str, pnt, displenth)؛ //بیشتر از displenth کاراکترهای رشته اصلی را در رشته موقت ما برای (i=0; i) کپی کنید. حالا بیایید کتابخانه جدید ایجاد شده را با تابع اصلی خود به فایل متصل کنیم:

#include "i2c_lcd.h" //include header file unsigned int i; //متغییر موقت شمارنده void main() ( lcd_I2C_Init(); //آغاز کردن صفحه نمایش lcd_I2C_backlight (1)؛ //نور پس زمینه را روشن کنید lcd_I2C_txt ("سلام habrahabr")؛ //نمایش خط در حالی که (1) ( delay_ms( 1000)؛ lcd_I2C_Goto (2،1)؛ //رفتن به کاراکتر 1 از خط 2 lcd_i2c_int (i)؛ //نمایش مقدار i++؛ //افزایش شمارنده)

اگر همه چیز به درستی مونتاژ شده باشد، باید متن روی نشانگر و شمارنده را ببینیم که هر ثانیه افزایش می یابد. به طور کلی، هیچ چیز پیچیده نیست :)

در مقاله بعدی به درک پروتکل i2c و دستگاه هایی که با آن کار می کنند ادامه خواهیم داد. بیایید کار با حافظه EEPROM 24XX و شتاب سنج/ژیروسکوپ MPU6050 را در نظر بگیریم.

امروز یک مهمان جدید روی میز عمل ما است، این یک محصول میکروچیپ، توسعه دهنده پورت MCP23008-E است. این مورد (همانطور که از نامش پیداست) برای افزایش تعداد پایه های ورودی/خروجی میکروکنترلر در صورت ناکافی شدن ناگهانی آنها در نظر گرفته شده است. البته، اگر به پاها و خروجی ها نیاز داریم، می توانیم آن را بگیریم و نگران آن نباشیم. اگر به پایه های ورودی نیاز دارید، پس راه حلی بر اساس منطق دقیق وجود دارد. اگر به هر دو ورودی و خروجی و همچنین یک pull-up کنترل شده برای ورودی ها نیاز داریم، در این صورت یک پورت گسترش دهنده شاید معمولی ترین راه حل باشد. در مورد قیمت دستگاه، بسیار کم است - حدود یک دلار. در این مقاله سعی خواهم کرد نحوه کنترل این ریزمدار با استفاده از میکروکنترلر AVR را به تفصیل شرح دهم.

ابتدا کمی در مورد ویژگی ها:

  • 8 پین پورت مستقل
  • رابط برای ارتباط با دنیای خارج - I2C (فرکانس تا 1.7 مگاهرتز)
  • کشش قابل تنظیم برای ورودی ها
  • ممکن است وقتی وضعیت برخی ورودی ها تغییر می کند، پای خود را تکان دهد
  • سه ورودی برای تنظیم آدرس ریز مدار (می توانید 8 دستگاه را در یک اتوبوس آویزان کنید)
  • ولتاژ کاری از 1.8 تا 5.5 ولت
  • مصرف جریان کم
  • انتخاب بزرگ محفظه ها (PDIP/SOIC/SSOP/QFN)

من ترجیح می دهم از رابط I2C استفاده کنم، با تعداد کمی سیم کشی من را جذب می کند:-) اما اگر به سرعت بسیار سریع (تا 10 مگاهرتز) نیاز دارید، باید از رابط SPI استفاده کنید که در MCP23S08 وجود دارد. تفاوت بین MCP23S08 و MCP23008، همانطور که من متوجه شدم، فقط در رابط و تعداد پاها برای تنظیم آدرس تراشه است. چه کسی چیز کوتاه تر دوست دارد؟ پین‌آوت میکروهی در دیتاشیت موجود است، به هیچ وجه جالب نیست و در اینجا مورد توجه قرار نخواهد گرفت. بنابراین، بیایید بلافاصله به نحوه شروع کار با این دستگاه بپردازیم. همه کارها به نوشتن و خواندن داده های خاص از رجیسترهای ریز مدار ختم می شود. برای خوشحالی من، اصلاً تعداد زیادی ثبت وجود نداشت - فقط یازده. نوشتن و خواندن داده ها از رجیسترها بسیار ساده است، در مورد آن پست کنید. درست است که ما در مورد رجیسترها صحبت نمی کنیم، اما اصل یکسان است. اکنون تنها چیزی که باقی می‌ماند این است که بفهمیم از کدام ثبات‌ها چه چیزی بخوانیم و چه رجیسترهایی را بنویسیم. البته دیتاشیت در این امر به ما کمک خواهد کرد. از برگه اطلاعات می آموزیم که ریز مدار دارای رجیسترهای زیر است:

ثبت IODIR
جهت جریان داده ها را مشخص می کند. اگر بیت مربوط به یک پایه خاص روی یک تنظیم شود، آن پا یک ورودی است. اگر بازنشانی به صفر رسید، خارج شوید. به طور خلاصه، این ثبات مشابه DDRx در AVR است (فقط در AVR 1 یک خروجی و 0 یک ورودی است).

ثبت IPOL
اگر یک بیت ثبت تنظیم شده باشد، وارونگی ورودی برای پای مربوطه فعال می شود. این به این معنی است که اگر شما یک چوب را به ساق پا اعمال کنید. صفر پس از ثبت GPIO یک در نظر گرفته می شود و بالعکس. سودمندی این ویژگی بسیار مشکوک است.

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

ثبت نام DEFVAL
مقدار فعلی پین های پیکربندی شده برای ورودی به طور مداوم با این ثبات مقایسه می شود. اگر به طور ناگهانی مقدار فعلی شروع به متفاوت شدن با آنچه در این ثبات است، آنگاه یک وقفه رخ می دهد. به بیان ساده، اگر بیت تنظیم شود، وقفه زمانی رخ می دهد که سطح از بالا به پایین در پایه مربوطه تغییر کند. اگر بیت پاک شود، وقفه در یک لبه در حال افزایش رخ می دهد.

ثبت نام INTCON
هر بیت از این ثبات مربوط به یک پین پورت خاص است. اگر بیت واضح باشد، هر گونه تغییر در سطح منطقی به سطح مخالف باعث وقفه می شود. اگر بیت تنظیم شود، وقوع وقفه تحت تأثیر ثبات DEFVAL قرار می گیرد (در غیر این صورت کاملاً نادیده گرفته می شود).

ثبت نام IOCON
این ثبت تنظیمات است. از چهار بیت تشکیل شده است:
SEQOP- افزایش خودکار آدرس کنترل های بیت. اگر نصب باشد، افزایش خودکار غیرفعال است، در غیر این صورت فعال می شود. اگر آن را خاموش کنیم، به دلیل اینکه مجبور نیستیم هر بار آدرس آن را ارسال کنیم، می توانیم مقدار همان ثبات را خیلی سریع بخوانیم. اگر نیاز دارید که هر 11 ثبت را به ترتیب به سرعت بخوانید، افزایش خودکار باید فعال شود. هر بار که یک بایت از رجیستر خوانده می شود، خود آدرس یک عدد افزایش می یابد و نیازی به ارسال نخواهد داشت.
DISSLW- چه کسی می داند این بیت چیست. مهم نیست که چگونه آن را بچرخانم، همه چیز هنوز کار می کند. اگه کسی توضیح بده خوشحال میشم
HAEN- بیت تنظیم خروجی INT. اگر تنظیم شود، پین به عنوان یک تخلیه باز پیکربندی می شود، اگر بیت تنظیم مجدد شود، سطح فعال در پای INT بیت INTPOL را تعیین می کند.
INTPOL- سطح فعال را در پای INT تعیین می کند. اگر تنظیم شود، سطح فعال یک و در غیر این صورت صفر است.

هنوز یه ذره مونده HAENاما در این تراشه استفاده نمی شود (پین های آدرس دهی سخت افزاری را در MCP23S08 روشن/خاموش می کند)

ثبت GPPU
خروجی ورودی را کنترل می کند. اگر بیت تنظیم شده باشد، یک pull-up به منبع تغذیه از طریق یک مقاومت 100 کیلو اهم روی پین مربوطه ظاهر می شود.

ثبت نام INTF
ثبت پرچم قطع شود. اگر بیت تنظیم شده باشد، به این معنی است که پایه پورت مربوطه باعث وقفه شده است. البته، وقفه برای پاهای مورد نیاز باید در رجیستر GPINTEN فعال شود

ثبت نام INTCAP
هنگامی که یک وقفه رخ می دهد، کل پورت در این ثبات خوانده می شود. اگر پس از این وقفه های بیشتری وجود داشته باشد، تا زمانی که آن را نخوانیم یا GPIO را نخوانیم، محتوای این ثبات با مقدار جدید بازنویسی نمی شود.

ثبت نام GPIO
هنگام خواندن داده ها از ثبات، سطوح منطقی روی پین های پورت را می خوانیم. هنگام ضبط داده ها، سطوح منطقی را تنظیم می کنیم. هنگام نوشتن در این ثبات، همان داده ها به طور خودکار در ثبات OLAT نوشته می شود

ثبت OLAT
با نوشتن داده در این ثبات، داده ها را به پورت خروجی می دهیم. اگر داده ها را از آنجا بخوانید، آنچه را که نوشته شده است خواهید خواند، نه آنچه را که در ورودی های پورت وجود دارد. برای خواندن ورودی ها فقط از GPIO استفاده می کنیم.

توصیف رجیسترها به نظر من کاملاً عادی است و نباید ذهن کسی را به هم بزند. اکنون، فقط برای سرگرمی، اجازه دهید یک نسخه نمایشی کوچک بنویسیم که 4 دکمه متصل به گسترش دهنده پورت را نظرسنجی می کند و بسته به وضعیت آنها، 4 LED مربوطه را که به همان گسترش دهنده متصل هستند روشن یا خاموش می کند. مثال تا حد امکان ساده و بدون هیچ وقفه ای خواهد بود. ما به سادگی وضعیت دکمه ها را مدام می خوانیم و این حالت را روی LED ها نمایش می دهیم. نمودار دستگاه ما به صورت زیر خواهد بود:

پین وقفه لازم نیست وصل شود، ما در اینجا به آن نیازی نداریم، اما برای پایه تنظیم مجدد نیاز به pull-up است! من زمان زیادی را صرف کردم تا اینکه متوجه شدم که پورت توسعه دهنده من به دلیل سفت نشدن به طور دوره ای ریست می شود. حالا بریم سراغ کد. البته در مورد آن خواهیم نوشت. کد ساده:

برنامه rashiritel; const AdrR=%01000001; //آدرس تراشه با بیت خوانده شده AdrW=%01000000; //آدرس تراشه با رکورد بیت var r:byte; ///روند داده ها را از متغیر Dat به رجیستر در آدرس Adr Procedure WriteReg(Dat,Adr:byte) می نویسد. شروع TWI_Start(); TWI_Write (AdrW)؛ TWI_Write(Adr); TWI_Write(Dat); TWI_Stop(); پایان؛ ///این تابع مقدار ثبات را در آدرس Adr برمی‌گرداند. تابع ReadReg(Adr:byte):byte; var a:byte; شروع TWI_Start(); TWI_Write (AdrW)؛ TWI_Write(Adr); TWI_Start(); TWI_Write (AdrR)؛ a:=TWI_Read(0); TWI_Stop(); نتیجه:=a; پایان؛ شروع TWI_INIT(200000)؛ ///آغاز کردن i2c WriteReg(%00001111,0x00); //کمترین 4 بیت ورودی و 4 بیت باقیمانده خروجی WriteReg(%00001111,0x06); //بر pull-up برای 4 ورودی در حالی که TRUE شروع کنید r:=ReadReg(0x09); //خواندن وضعیت ورودی ها r:= NOT r; //لازم است بیت ها معکوس شوند، در غیر این صورت با رها شدن دکمه LED ها روشن می شوند r:= r shl 4; //Shift 4 بیت به چپ... WriteReg(r,0x0A); //نمایش وضعیت پایان دکمه ها. پایان.

تاریخ انتشار 1395/10/26

در مقاله قبلی به بررسی عملکرد STM32 با باس I 2 C به عنوان Master پرداختیم. یعنی رهبر بود و سنسور را بازجویی کرد. حالا بیایید STM32 را یک Slave کنیم و به درخواست ها پاسخ دهیم، یعنی خودش به عنوان یک سنسور کار می کند. ما 255 بایت حافظه را برای رجیسترهایی با آدرس‌هایی از 0 تا 0xFF اختصاص می‌دهیم و به Master اجازه می‌دهیم آنها را بنویسد/بخواند. و برای اینکه مثال خیلی ساده نباشد، اجازه دهید STM32 خود را تبدیل‌کننده آنالوگ به دیجیتال با رابط I 2 C کنیم. ADC 8 کانال را پردازش می‌کند. کنترل کننده هنگام خواندن از ثبات ها، نتایج تبدیل ها را به Master می دهد. از آنجایی که نتیجه تبدیل ADC 12 بیت است، برای هر کانال ADC به 2 ثبات (2 بایت) نیاز داریم.

i2c_slave.hشامل تنظیمات:

I2CSLAVE_ADDR- آدرس دستگاه ما؛

ADC_ADDR_START– آدرس شروع رجیسترهایی که مسئول نتایج تبدیل های ADC هستند.

در پرونده i2c_slave.cما بیشتر به توابع علاقه مندیم get_i2c1_ramو set_i2c1_ram. تابع get_i2c1_ramمسئول خواندن داده ها از رجیسترها است. داده ها را از آدرس مشخص شده برمی گرداند که به Master داده می شود. در مورد ما، داده ها از آرایه خوانده می شوند i2c1_ram، اما اگر Master آدرس های ثبت را از محدوده تخصیص یافته برای نتایج ADC بخواهد، داده های تبدیل ADC ارسال می شود.

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) (//داده های ADC اگر ((ADC_ADDR_START<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

تابع set_i2c1_ram- داده های دریافتی از Master را در رجیسترهایی با آدرس مشخص شده می نویسد. در مورد ما، داده ها به سادگی در یک آرایه نوشته می شوند i2c1_ram. اما این اختیاری است. برای مثال می‌توانید یک چک اضافه کنید و وقتی شماره مشخصی به آدرس خاصی رسید، برخی اقدامات را انجام دهید. به این ترتیب می توانید دستورات مختلفی را به میکروکنترلر ارسال کنید.

set_i2c1_ram:

Void set_i2c1_ram(uint8_t adr, uint8_t val) (i2c1_ram = val; return; )

مقداردهی اولیه بسیار ساده است:

Int main(void) (SetSysClockTo72(); ADC_DMA_init(); I2C1_Slave_init(); while(1) ())

ابتدا حداکثر فرکانس کاری کنترلر را تنظیم می کنیم. حداکثر سرعت زمانی لازم است که از تاخیر در گذرگاه I 2 C اجتناب شود. سپس عملیات ADC را با استفاده از DMA شروع می کنیم. در باره . در باره . و در نهایت گذرگاه I 2 C را به صورت مقداردهی اولیه می کنیم برده. همانطور که می بینید، هیچ چیز پیچیده ای نیست.

حالا بیایید ماژول STM32 خود را به Raspberry Pi متصل کنیم. بیایید پتانسیومترها را به کانال های ADC وصل کنیم. و ما نشانگرهای ADC را از کنترلر خود خواهیم خواند. فراموش نکنید که برای کارکرد گذرگاه I 2 C، باید در هر خط اتوبوس، مقاومت های کششی نصب کنید.

در کنسول Raspberry، بیایید بررسی کنیم که آیا دستگاه ما در گذرگاه I 2 C قابل مشاهده است (در مورد آن):

I2cdetect -y 1

همانطور که می بینید، آدرس دستگاه 0x27، اگرچه ما 0x4E را مشخص کردیم. وقتی وقت دارید، به این فکر کنید که چرا این اتفاق افتاد.

برای خواندن از رجیسترهای دستگاه I 2 C-Slave، دستور را اجرا کنید:

I2cget -y 1 0x27 0x00

جایی که:
0x27- آدرس دستگاه،
0x00- آدرس ثبت نام (0x00…0xFF).

برای نوشتن در رجیسترهای دستگاه I 2 C-Slave، دستور را اجرا کنید:

I2cset -y 1 0x27 0xA0 0xDD

د:
0x27- آدرس دستگاه،
0xA0- آدرس ثبت نام
0xDD-داده 8 بیتی (0x00…0xFF)

دستور قبلی عدد 0xDD را در رجیستر نوشت 0xA0(شما می توانید به 16 ثبت اول بنویسید، اما فایده ای ندارد، اما آنها برای ADC رزرو شده اند). حال بخوانیم:

I2cget -y 1 0x27 0xA0

برای ساده کردن روند خواندن داده های کانال ADC، یک اسکریپت نوشتم:

#!/usr/bin/env پایتون import smbus import time bus = smbus.SMBus(1) address = 0x27 while (1): ADC = (); برای i در محدوده (0، 8): LBS = bus.read_byte_data (آدرس، 0x00+i*2) MBS = bus.read_byte_data(آدرس، 0x00+i*2+1) ADC[i] = MBS*256 + LBS چاپ ADC time.sleep(0.2)

نظرسنجی و نتایج تمام 8 کانال ADC را به کنسول نمایش می دهد.

به روشی مشابه، می توانید چندین میکروکنترلر را با هم ترکیب کنید. یکی از آنها باید Master() و دیگری Slave باشد.

آرزو می کنم موفق شوی!




بالا