برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

В بخش اول من سعی کردم به مهندسان الکترونیک سرگرمی که از شلوار آردوینو بزرگ شده اند بگویم چگونه و چرا باید برگه های داده و سایر اسناد میکروکنترلرها را بخوانند. متن بزرگ بود، بنابراین قول دادم نمونه های عملی را در یک مقاله جداگانه نشان دهم. خب خودشو میگفت قارچ شیری...

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

متن دوباره بزرگ شد، بنابراین برای راحتی، محتوا را می‌سازم:

قرص آبی STM32: 16 عدد LED با درایور DM634
STM8: راه اندازی شش پین PWM
STM8: 8 LED RGB روی سه پین، وقفه

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

تمرکز این مقاله بر روی دیتاشیت ها است، نه پروژه ها، بنابراین ممکن است کد خیلی منظم و اغلب فشرده نباشد. خود پروژه ها بسیار ساده هستند، اگرچه برای اولین آشنایی با تراشه جدید مناسب هستند.

امیدوارم مقاله من به کسی در مرحله مشابهی از غوطه ور شدن در سرگرمی کمک کند.

STM32

16 LED با DM634 و SPI

یک پروژه کوچک با استفاده از قرص آبی (STM32F103C8T6) و درایور LED DM634. با استفاده از دیتاشیت ها، درایور، پورت های STM IO را مشخص کرده و SPI را پیکربندی می کنیم.

DM634

تراشه تایوانی با 16 خروجی 16 بیتی PWM، می تواند به صورت زنجیره ای متصل شود. مدل پایین 12 بیتی از یک پروژه داخلی شناخته شده است لایت پک. در یک زمان، انتخاب بین DM63x و TLC5940 معروف، DM را به چند دلیل انتخاب کردم: 1) TLC در Aliexpress قطعا جعلی است، اما این یکی نیست. 2) DM یک PWM مستقل با ژنراتور فرکانس خود دارد. 3) به جای اینکه منتظر بسته ای از علی باشید، می توان آن را ارزان در مسکو خرید. و البته جالب بود که یاد بگیرید چگونه خودتان تراشه را کنترل کنید، نه اینکه از یک کتابخانه آماده استفاده کنید. تراشه‌ها در حال حاضر عمدتاً در بسته‌بندی SSOP24 ارائه می‌شوند و به راحتی به آداپتور لحیم می‌شوند.

از آنجایی که سازنده تایوانی است، برگه داده تراشه به زبان انگلیسی چینی نوشته شده است، به این معنی که سرگرم کننده خواهد بود. ابتدا به پینوت نگاه می کنیم (اتصال پین) برای درک اینکه کدام پایه باید به چه چیزی وصل شود، و شرح پین ها (شرح پین). 16 پین:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
منابع سینک DC (تخلیه باز)

فرو رفتن / خروجی تخلیه باز - زه کشی؛ منبع جریان ورودی؛ خروجی در حالت فعال به زمین متصل می شود - LED ها توسط کاتد به درایور متصل می شوند. از نظر الکتریکی، البته، این یک "زهکشی باز" نیست (زهکشی باز، اما در دیتاشیت ها این نام گذاری برای پین ها در حالت تخلیه اغلب یافت می شود.

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
مقاومت های خارجی بین REXT و GND برای تنظیم مقدار جریان خروجی

یک مقاومت مرجع بین پایه REXT و زمین نصب شده است که مقاومت داخلی خروجی ها را کنترل می کند، نمودار صفحه 9 دیتاشیت را ببینید. در DM634، این مقاومت را می توان با نرم افزار کنترل کرد و روشنایی کلی را تنظیم کرد (روشنایی جهانی) من در این مقاله وارد جزئیات نمی شوم، فقط یک مقاومت 2.2 - 3 کیلو اهم را در اینجا قرار می دهم.

برای درک نحوه کنترل تراشه، بیایید به توضیح رابط دستگاه نگاه کنیم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

بله، اینجاست، انگلیسی چینی با تمام شکوهش. ترجمه این مشکل ساز است، در صورت تمایل می توانید آن را درک کنید، اما راه دیگری وجود دارد - ببینید چگونه اتصال به TLC5940 مشابه عملکردی در برگه داده توضیح داده شده است:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
... برای وارد کردن اطلاعات به دستگاه فقط سه پین ​​لازم است. لبه افزایشی سیگنال SCLK داده ها را از پین SIN به ثبت داخلی منتقل می کند. پس از بارگیری همه داده ها، یک سیگنال کوتاه XLAT بالا، داده های متوالی را به رجیسترهای داخلی می چسباند. ثبات های داخلی دروازه هایی هستند که توسط سطح سیگنال XLAT راه اندازی می شوند. تمام داده ها ابتدا مهم ترین بیت منتقل می شوند.

چفت – چفت/چفت/قفل.
لبه در حال افزایش - لبه جلویی نبض
اول MSB – مهم ترین (سمت چپ) بیت رو به جلو.
به داده های ساعت - انتقال داده ها به صورت متوالی (بیت به بیت).

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

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

بنابراین، انتقال داده به DM634 به این صورت انجام می شود: ورودی DAI را روی مقدار مهم ترین بیت LED دور تنظیم کنید، DCK را بالا و پایین بکشید. ورودی DAI را روی مقدار بیت بعدی تنظیم کنید، DCK را بکشید. و به همین ترتیب تا زمانی که همه بیت ها منتقل شوند (ساعت در) پس از آن LAT را می کشیم. این کار را می توان به صورت دستی انجام داد (بیت بنگ، اما بهتر است از یک رابط SPI که مخصوص این کار طراحی شده است استفاده کنید، زیرا در STM32 ما در دو نسخه ارائه شده است.

قرص آبی STM32F103

مقدمه: کنترلرهای STM32 بسیار پیچیده تر از Atmega328 هستند که ممکن است ترسناک به نظر برسند. علاوه بر این، به دلایل صرفه جویی در انرژی، تقریباً تمام تجهیزات جانبی در شروع خاموش می شوند و فرکانس ساعت 8 مگاهرتز از منبع داخلی است. خوشبختانه، برنامه نویسان STM کدی نوشتند که تراشه را به 72 مگاهرتز "محاسبه شده" می رساند و نویسندگان همه IDE هایی که من می شناسم آن را در روند اولیه سازی گنجانده اند، بنابراین نیازی به کلاک نداریم (اما اگر واقعاً بخواهید می توانید). اما باید لوازم جانبی را روشن کنید.

مستندات: قرص آبی به تراشه محبوب STM32F103C8T6 مجهز شده است، دو سند مفید برای آن وجود دارد:

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

  • پین‌آت‌ها – پین‌آت‌های تراشه‌ای – در صورتی که تصمیم بگیریم خود تابلوها را بسازیم.
  • نقشه حافظه - نقشه حافظه برای یک تراشه خاص. کتابچه راهنمای مرجع یک نقشه برای کل خط دارد و به ثبت‌هایی اشاره می‌کند که ما آن‌ها را ندارند.
  • جدول تعاریف پین – لیستی از توابع اصلی و جایگزین پین ها. برای "قرص آبی" می توانید تصاویر راحت تری را با لیستی از پین ها و عملکرد آنها در اینترنت پیدا کنید. بنابراین، ما بلافاصله پین‌آوت قرص آبی را در گوگل جستجو می‌کنیم و این تصویر را در دسترس داریم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
نکته: یک خطایی در تصویر از اینترنت وجود داشت که در نظرات ذکر شده بود، از شما متشکرم. تصویر جایگزین شده است، اما این یک درس است - بهتر است اطلاعات را نه از برگه های داده بررسی کنید.

دیتاشیت را حذف می کنیم، Reference Manual را باز می کنیم و از این به بعد فقط از آن استفاده می کنیم.
روش: ما با ورودی/خروجی استاندارد سروکار داریم، SPI را پیکربندی می کنیم، لوازم جانبی لازم را روشن می کنیم.

ورودی خروجی

در Atmega328، I/O بسیار ساده پیاده سازی می شود، به همین دلیل است که فراوانی گزینه های STM32 می تواند گیج کننده باشد. اکنون ما فقط به نتیجه گیری نیاز داریم، اما حتی اینها نیز چهار گزینه دارند:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
زهکش باز، فشار کش، فشار کش جایگزین، زهکش باز جایگزین

"بکشید فشار دهید" (فشار کشش) خروجی معمولی از آردوینو است، پین می تواند مقدار HIGH یا LOW را بگیرد. اما با "زهکشی باز" وجود دارد مشکلات، اگرچه در واقع همه چیز در اینجا ساده است:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
پیکربندی خروجی / هنگامی که پورت به خروجی اختصاص داده می شود: / بافر خروجی فعال است: / – حالت تخلیه باز: "0" در ثبات خروجی N-MOS را فعال می کند، "1" در رجیستر خروجی پورت را در حالت Hi-Z ترک می کند ( P-MOS فعال نیست ) / – حالت فشار کش: "0" در ثبات خروجی N-MOS را فعال می کند، "1" در رجیستر خروجی P-MOS را فعال می کند.

تمام تفاوت بین تخلیه باز (زهکشی باز) از «فشار کش» (فشار کشش) این است که در پین اول نمی تواند حالت HIGH را بپذیرد: هنگام نوشتن یک در رجیستر خروجی، به حالت مقاومت بالا می رود (امپدانس بالا, سلام-Z). هنگام نوشتن صفر، پین در هر دو حالت، چه از نظر منطقی و چه الکتریکی، یکسان عمل می کند.

در حالت خروجی معمولی، پین به سادگی محتویات رجیستر خروجی را پخش می کند. در "جایگزین" توسط تجهیزات جانبی مربوطه کنترل می شود (به 9.1.4 مراجعه کنید):

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
اگر یک بیت پورت به عنوان یک پایه تابع جایگزین پیکربندی شده باشد، ثبت پین غیرفعال می شود و پین به پین ​​محیطی متصل می شود.

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

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
اگر چندین دستگاه جانبی از یک پین استفاده می‌کنند، برای جلوگیری از تضاد بین توابع جایگزین، فقط یک دستگاه جانبی باید در هر بار استفاده شود که با استفاده از بیت فعال ساعت محیطی (در رجیستر RCC مناسب) جابجا شود.

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

بنابراین: ما از SPI استفاده می کنیم، به این معنی که دو پین (با داده و با سیگنال ساعت) باید "عملکرد فشار کش جایگزین" و یکی دیگر (LAT) باید "فشار کش معمولی" باشد. اما قبل از تخصیص آنها، بیایید به SPI بپردازیم.

SPI

یک برنامه آموزشی کوچک دیگر

SPI یا Serial Peripheral Interface (اینترفیس جانبی سریال) یک رابط ساده و بسیار موثر برای اتصال MK با سایر MK ها و به طور کلی دنیای خارج است. اصل عملکرد آن قبلاً در بالا توضیح داده شده است ، جایی که در مورد درایور LED چینی (در کتابچه راهنمای مرجع ، به بخش 25 مراجعه کنید). SPI می تواند در حالت Master ("master") و Slave ("slave") کار کند. SPI دارای چهار کانال اصلی است که ممکن است از همه آنها استفاده نشود:

  • MOSI، خروجی اصلی / ورودی Slave: این پین داده ها را در حالت اصلی منتقل می کند و در حالت برده داده ها را دریافت می کند.
  • MISO، Master Input / Slave Output: برعکس، در master دریافت می کند و در Slave ارسال می کند.
  • SCK, Serial Clock: فرکانس انتقال داده را در Master تنظیم می کند یا سیگنال ساعت را در Slave دریافت می کند. اساسا ضربه زدن.
  • SS، Slave Select: با کمک این کانال، برده می داند که چیزی از او می خواهد. در STM32 NSS نامیده می شود، که در آن N = منفی، یعنی. اگر زمین در این کانال وجود داشته باشد، کنترلر به یک برده تبدیل می شود. به خوبی با حالت Open Drain Output ترکیب می شود، اما این داستان دیگری است.

مانند هر چیز دیگری، SPI در STM32 از نظر عملکرد غنی است که درک آن را تا حدودی دشوار می کند. به عنوان مثال، می تواند نه تنها با SPI، بلکه با یک رابط I2S نیز کار کند، و در مستندات توضیحات آنها مخلوط شده است، لازم است که مازاد بر آن به موقع قطع شود. وظیفه ما بسیار ساده است: فقط باید داده ها را با استفاده از MOSI و SCK ارسال کنیم. ما به بخش 25.3.4 (ارتباط نیمه دوبلکس، ارتباط نیمه دوبلکس) می رویم، جایی که می یابیم 1 ساعت و 1 سیم داده یک طرفه (1 سیگنال ساعت و 1 جریان داده یک طرفه):

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
در این حالت، برنامه از SPI در حالت فقط ارسال یا دریافت فقط استفاده می کند. / حالت فقط انتقال مشابه حالت دوطرفه است: داده ها روی پین انتقال (MOSI در حالت اصلی یا MISO در حالت برده) منتقل می شود و پایه دریافت (به ترتیب MISO یا MOSI) می تواند به عنوان یک پایه ورودی/خروجی معمولی استفاده شود. . در این مورد، برنامه فقط باید بافر Rx را نادیده بگیرد (اگر خوانده شود، هیچ داده ای منتقل نمی شود).

عالی است، پین MISO رایگان است، بیایید سیگنال LAT را به آن وصل کنیم. بیایید Slave Select را بررسی کنیم، که در STM32 می تواند به صورت برنامه ریزی شده کنترل شود، که بسیار راحت است. ما پاراگراف با همین نام را در بخش 25.3.1 SPI توضیحات کلی می خوانیم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
کنترل نرم افزار NSS (SSM = 1) / اطلاعات انتخاب برده در بیت SSI ثبت SPI_CR1 موجود است. پین خارجی NSS برای سایر نیازهای برنامه رایگان باقی می ماند.

وقت آن است که به ثبت نام ها بنویسید. تصمیم گرفتم از SPI2 استفاده کنم، آدرس پایه آن را در دیتاشیت جستجو کنم - در بخش 3.3 Memory Map:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

خوب، بیایید شروع کنیم:

#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))

بخش 25.3.3 را با عنوان خود توضیحی "پیکربندی SPI در حالت Master" باز کنید:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

1. فرکانس ساعت سریال را با بیت های BR[2:0] در ثبات SPI_CR1 تنظیم کنید.

رجیسترها در بخش راهنمای مرجع به همین نام جمع آوری می شوند. تغییر آدرس (آفست آدرس) برای CR1 – 0x00، به طور پیش فرض همه بیت ها پاک می شوند (بازنشانی مقدار 0x0000):

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

بیت های BR تقسیم کننده ساعت کنترل کننده را تنظیم می کنند، بنابراین فرکانس عملکرد SPI را تعیین می کنند. فرکانس STM32 ما 72 مگاهرتز خواهد بود، درایور LED، طبق دیتاشیت آن، با فرکانس حداکثر 25 مگاهرتز کار می کند، بنابراین باید بر چهار تقسیم کنیم (BR[2:0] = 001).

#define _SPI_CR1 0x00

#define BR_0        0x0008
#define BR_1        0x0010
#define BR_2        0x0020

_SPI2_ (_SPI_CR1) |= BR_0;// pclk/4

2. بیت های CPOL و CPHA را برای تعریف رابطه بین انتقال داده و زمان بندی ساعت سریال تنظیم کنید (نمودار صفحه 240 را ببینید).

از آنجایی که ما در اینجا یک دیتاشیت را می خوانیم و به شماتیک ها نگاه نمی کنیم، بیایید نگاهی دقیق تر به توضیحات متن بیت های CPOL و CPHA در صفحه 704 بیندازیم (توضیحات عمومی SPI):

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
فاز ساعت و قطبیت
با استفاده از بیت های CPOL و CPHA رجیستر SPI_CR1، می توانید چهار رابطه زمان بندی را به صورت برنامه نویسی انتخاب کنید. بیت CPOL (قطب ساعت) وضعیت سیگنال ساعت را هنگامی که هیچ داده ای ارسال نمی شود کنترل می کند. این بیت حالت های master و slave را کنترل می کند. اگر CPOL ریست شود، پین SCK در حالت استراحت پایین است. اگر بیت CPOL تنظیم شده باشد، پین SCK در حالت استراحت بالاست.
هنگامی که بیت CPHA (فاز ساعت) تنظیم می شود، بارق تله بیت بالا لبه دوم سیگنال SCK است (اگر CPOL واضح باشد سقوط می کند، اگر CPOL تنظیم شود افزایش می یابد). داده ها با تغییر دوم در سیگنال ساعت گرفته می شود. اگر بیت CPHA واضح باشد، بارق تله بیت بالا لبه افزایشی سیگنال SCK است (لبه در حال سقوط اگر CPOL تنظیم شده باشد، لبه صعودی اگر CPOL پاک شود). داده ها در اولین تغییر در سیگنال ساعت گرفته می شود.

با جذب این دانش، به این نتیجه می رسیم که هر دو بیت باید صفر باقی بمانند، زیرا ما می‌خواهیم سیگنال SCK زمانی که استفاده نمی‌شود پایین بماند و داده‌ها در لبه بالارونده پالس ارسال شوند (شکل XNUMX را ببینید). لبه در حال افزایش در دیتاشیت DM634).

به هر حال، در اینجا برای اولین بار با ویژگی واژگان در دیتاشیت های ST مواجه شدیم: در آنها عبارت "تنظیم مجدد بیت به صفر" نوشته شده است. برای تنظیم مجدد کمیو نه تا کمی پاک شود، مانند، به عنوان مثال، Atmega.

3. بیت DFF را تنظیم کنید تا مشخص شود بلوک داده 8 بیتی است یا 16 بیتی

من به طور خاص یک DM16 634 بیتی گرفتم تا مانند DM12 با انتقال داده های PWM 633 بیتی خود را خسته نکنم. منطقی است که DFF را روی یک تنظیم کنید:

#define DFF         0x0800

_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode

4. بیت LSBFIRST را در ثبات SPI_CR1 پیکربندی کنید تا قالب بلوک را تعیین کنید.

LSBFIRST، همانطور که از نامش پیداست، ابتدا انتقال را با کمترین بیت مهم پیکربندی می کند. اما DM634 می خواهد داده ها را از مهم ترین بیت دریافت کند. بنابراین، آن را تنظیم مجدد می کنیم.

5. در حالت سخت افزاری، اگر ورودی از پایه NSS مورد نیاز است، در طول کل توالی انتقال بایت، یک سیگنال بالا به پایه NSS اعمال کنید. در حالت نرم افزار NSS، بیت های SSM و SSI را در ثبات SPI_CR1 تنظیم کنید. اگر قرار است از پین NSS به عنوان خروجی استفاده شود، فقط بیت SSOE باید تنظیم شود.

SSM و SSI را نصب کنید تا حالت سخت افزاری NSS را فراموش کنید:

#define SSI         0x0100
#define SSM         0x0200

_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high

6. بیت های MSTR و SPE باید تنظیم شوند (تنها در صورتی تنظیم می شوند که سیگنال NSS بالا باشد)

در واقع، با این بیت ها ما SPI خود را به عنوان یک Master تعیین می کنیم و آن را روشن می کنیم:

#define MSTR        0x0004
#define SPE         0x0040

_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;

SPI پیکربندی شده است، بیایید فورا توابعی بنویسیم که بایت ها را به درایور ارسال می کنند. به خواندن 25.3.3 "پیکربندی SPI در حالت اصلی" ادامه دهید:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
سفارش انتقال داده
انتقال زمانی شروع می شود که یک بایت در بافر Tx نوشته شود.
بایت داده در رجیستر شیفت بارگذاری می شود موازی حالت (از گذرگاه داخلی) در حین انتقال بیت اول، پس از آن به آن منتقل می شود متوالی حالت پین MOSI، اولین یا آخرین بیت به جلو بسته به تنظیم بیت LSBFIRST در ثبات CPI_CR1. پرچم TXE پس از انتقال داده تنظیم می شود از بافر Tx به رجیستر شیفتو همچنین اگر بیت TXEIE در ثبات CPI_CR1 تنظیم شده باشد، یک وقفه ایجاد می کند.

من چند کلمه را در ترجمه برجسته کردم تا توجه را به یکی از ویژگی های پیاده سازی SPI در کنترل کننده های STM جلب کنم. در Atmega پرچم TXE (Tx Empty، Tx خالی است و آماده دریافت داده است) تنها پس از ارسال کل بایت تنظیم می شود بیرون. و در اینجا این پرچم پس از درج بایت در ثبات داخلی شیفت تنظیم می شود. از آنجایی که با تمام بیت ها به طور همزمان (موازی) به آنجا فشار داده می شود و سپس داده ها به صورت متوالی منتقل می شوند، TXE قبل از ارسال کامل بایت تنظیم می شود. این مهم است زیرا در مورد درایور LED ما، پس از ارسال باید پین LAT را بکشیم از همه داده ها، یعنی پرچم TXE به تنهایی برای ما کافی نخواهد بود.

این بدان معناست که ما به پرچم دیگری نیاز داریم. بیایید به 25.3.7 نگاه کنیم - "پرچم های وضعیت":

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
<…>
برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
پرچم مشغول
پرچم BSY توسط سخت افزار تنظیم و پاک می شود (نوشتن روی آن تاثیری ندارد). پرچم BSY وضعیت لایه ارتباطی SPI را نشان می دهد.
بازنشانی می کند:
وقتی انتقال کامل شد (به جز در حالت اصلی اگر انتقال پیوسته باشد)
وقتی SPI غیرفعال است
هنگامی که یک خطای حالت اصلی رخ می دهد (MODF=1)
اگر انتقال پیوسته نباشد، پرچم BSY بین هر انتقال داده پاک می شود

بسیار خوب، این به کار خواهد آمد. بیایید دریابیم که بافر Tx در کجا قرار دارد. برای انجام این کار، "SPI Data Register" را بخوانید:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
بیت 15:0 DR [15:0] ثبت داده
داده های دریافتی یا داده هایی که قرار است منتقل شوند.
ثبت داده به دو بافر تقسیم می شود - یکی برای نوشتن (بافر انتقال) و دیگری برای خواندن (بافر دریافت). نوشتن در رجیستر داده در بافر Tx می نویسد و خواندن از ثبات داده مقدار موجود در بافر Rx را برمی گرداند.

خوب، و ثبت وضعیت، که در آن پرچم های TXE و BSY یافت می شوند:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

ما نوشتیم:

#define _SPI_DR  0x0C
#define _SPI_SR  0x08

#define BSY         0x0080
#define TXE         0x0002

void dm_shift16(uint16_t value)
{
    _SPI2_(_SPI_DR) = value; //send 2 bytes
    while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}

خوب، از آنجایی که با توجه به تعداد خروجی های درایور LED باید 16 برابر دو بایت ارسال کنیم، چیزی شبیه به این:

void sendLEDdata()
{
    LAT_low();
    uint8_t k = 16;
    do
    {   k--;
        dm_shift16(leds[k]);
    } while (k);

    while (_SPI2_(_SPI_SR) & BSY); // finish transmission

    LAT_pulse();
}

اما ما هنوز نمی دانیم که چگونه پین ​​LAT را بکشیم، بنابراین به I/O برمی گردیم.

اختصاص پین

در STM32F1، رجیسترهای مسئول وضعیت پین‌ها کاملاً غیرعادی هستند. واضح است که تعداد آنها از Atmega بیشتر است، اما با سایر تراشه های STM نیز متفاوت هستند. بخش 9.1 شرح کلی GPIO:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
هر یک از پورت های ورودی/خروجی عمومی (GPIO) دارای دو رجیستر پیکربندی 32 بیتی (GPIOx_CRL و GPIOx_CRH)، دو رجیستر داده 32 بیتی (GPIOx_IDR و GPIOx_ODR)، یک رجیستر تنظیم/تنظیم مجدد 32 بیتی (GPIOx_BSRR)، یک رجیستر تنظیم مجدد 16 بیتی (GPIOx_32-) و یک رجیستر مسدودکننده بیت (GPIOx_LCKR).

دو رجیستر اول غیرمعمول و همچنین کاملاً ناخوشایند هستند، زیرا 16 پین پورت در قالب "چهار بیت برای هر برادر" در سراسر آنها پراکنده شده اند. آن ها پین های صفر تا هفت در CRL و بقیه در CRH هستند. در همان زمان، رجیسترهای باقی‌مانده با موفقیت حاوی بیت‌های تمام پین‌های پورت هستند - اغلب نیمه «رزرو» باقی می‌مانند.

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

ما نیازی به ثبت مسدود کننده نداریم.

رجیسترهای تنظیم و تنظیم مجدد کاملاً خنده دار هستند زیرا تا حدی یکدیگر را تکرار می کنند: می توانید همه چیز را فقط در BSRR بنویسید ، جایی که 16 بیت بالاتر پین را صفر می کند و بیت های پایین تر روی 1 تنظیم می شود ، یا همچنین می توانید از BRR استفاده کنید که 16 بیت پایین آن فقط پین را بازنشانی می کند. من گزینه دوم را دوست دارم. این رجیسترها مهم هستند زیرا دسترسی اتمی به پین ​​ها را فراهم می کنند:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
تنظیم یا تنظیم مجدد اتمی
هنگام برنامه نویسی GPIOx_ODR در سطح بیت، نیازی به غیرفعال کردن وقفه ها نیست: یک یا چند بیت را می توان با یک عملیات نوشتن اتمی APB2 تغییر داد. این با نوشتن یک "1" در ثبت تنظیم/تنظیم مجدد (GPIOx_BSRR یا فقط برای بازنشانی، GPIOx_BRR) بیتی که باید تغییر کند به دست می آید. سایر بیت ها بدون تغییر باقی می مانند.

رجیسترهای داده اسامی کاملاً واضح دارند - IDR = ورودی ثبت جهت، ثبت ورودی؛ ODR = تولید ثبت جهت، ثبت خروجی. ما در پروژه فعلی به آنها نیاز نخواهیم داشت.

و در نهایت رجیسترهای کنترلی. از آنجایی که ما به دومین پین SPI، یعنی PB13، PB14 و PB15 علاقه مند هستیم، بلافاصله به CRH نگاه می کنیم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

و می بینیم که باید چیزی را در بیت های 20 تا 31 بنویسیم.

ما قبلاً متوجه شده‌ایم که از پین‌ها چه چیزی می‌خواهیم، ​​بنابراین در اینجا بدون اسکرین شات این کار را انجام می‌دهم، فقط می‌گویم که MODE جهت (ورودی اگر هر دو بیت روی 0 تنظیم شده باشند) و سرعت پین را مشخص می‌کند (به 50 مگاهرتز نیاز داریم، به عنوان مثال. هر دو پین روی "1")، و CNF حالت را تنظیم می کند: "فشار کش" معمولی - 00، "جایگزین" - 10. به طور پیش فرض، همانطور که در بالا می بینیم، همه پین ​​ها بیت سوم را از پایین دارند (CNF0). آنها را روی حالت قرار می دهد ورودی شناور.

از آنجایی که قصد دارم کار دیگری با این تراشه انجام دهم، برای سادگی همه مقادیر ممکن MODE و CNF را برای هر دو رجیستر کنترل پایین و بالا تعریف کرده ام.

یه جورایی اینجوری

#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000

#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000

پین های ما در پورت B قرار دارند (آدرس پایه - 0x40010C00)، کد:

#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))

#define _BRR  0x14
#define _BSRR 0x10
#define _CRL  0x00
#define _CRH  0x04

//используем стандартный SPI2: MOSI на B15, CLK на B13
//LAT пусть будет на неиспользуемом MISO – B14

//очищаем дефолтный бит, он нам точно не нужен
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);

//альтернативные функции для MOSI и SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;

//50 МГц, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;

و بر این اساس، می توانید تعاریفی را برای LAT بنویسید که توسط رجیسترهای BRR و BSRR منقبض می شوند:

/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)

#define LAT_low() _PORTB_(_BRR) = (1<<14)

(LAT_low فقط با اینرسی، همیشه همینطور بوده، بگذارید بماند)

اکنون همه چیز عالی است، اما کار نمی کند. از آنجایی که این STM32 است، آنها در مصرف برق صرفه جویی می کنند، به این معنی که باید کلاک تجهیزات جانبی مورد نیاز را فعال کنید.

ساعت را روشن کنید

ساعت که با نام ساعت نیز شناخته می شود، وظیفه ساعت را بر عهده دارد. و ما قبلاً می توانستیم به علامت اختصاری RCC توجه کنیم. ما آن را در مستندات جستجو می کنیم: این تنظیم مجدد و کنترل ساعت است.

همانطور که در بالا گفته شد، خوشبختانه سخت ترین قسمت موضوع کلاک توسط افرادی از STM برای ما انجام شد که از آنها بسیار تشکر می کنیم (یک بار دیگر لینک می دهم وب سایت دی هالت، تا مشخص شود که چقدر گیج کننده است). ما فقط به رجیسترهایی نیاز داریم که مسئول فعال کردن ساعت محیطی هستند (Peripheral Clock Enable Registers). ابتدا، بیایید آدرس پایه RCC را پیدا کنیم، در همان ابتدای "نقشه حافظه" است:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))

و سپس روی پیوندی که در آن سعی می‌کنید چیزی در صفحه پیدا کنید کلیک کنید، یا، خیلی بهتر، توضیحات ثبت‌های فعال را از بخش‌های مربوط به فعال کردن رجیسترها. جایی که RCC_APB1ENR و RCC_APB2ENR را پیدا خواهیم کرد:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

و بر این اساس، آنها حاوی بیت هایی هستند که شامل کلاک SPI2، IOPB (I/O Port B) و توابع جایگزین (AFIO) می شود.

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001

//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;

//включаем  тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;

کد نهایی را می توان یافت اینجا.

اگر فرصت و تمایل به آزمایش دارید، DM634 را به این صورت وصل کنید: DAI به PB15، DCK به PB13، LAT به PB14. ما راننده را از 5 ولت تغذیه می کنیم، فراموش نکنید که پایه ها را وصل کنید.

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

STM8 PWM

PWM در STM8

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

تراشه نیز دارد برگه داده и راهنمای مرجع RM0016، در اولی آدرس های پین اوت و ثبت نام وجود دارد، در دومی - هر چیز دیگری. STM8 در C در یک IDE وحشتناک برنامه ریزی شده است ST Visual Develop.

ساعت و I/O

به طور پیش فرض، STM8 در فرکانس 2 مگاهرتز کار می کند، این باید بلافاصله اصلاح شود.

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
ساعت HSI (سرعت بالا) داخلی
سیگنال ساعت HSI از یک نوسان ساز داخلی RC 16 مگاهرتز با یک تقسیم کننده قابل برنامه ریزی (1 تا 8) مشتق شده است. در ثبت تقسیم کننده ساعت (CLK_CKDIVR) تنظیم شده است.
توجه: در شروع، یک نوسان ساز HSI RC با تقسیم کننده 8 به عنوان منبع اصلی سیگنال ساعت انتخاب می شود.

آدرس رجیستر را در دیتاشیت، توضیحات در refman پیدا می کنیم و می بینیم که رجیستر باید پاک شود:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

از آنجایی که می‌خواهیم PWM را اجرا کنیم و LED‌ها را وصل کنیم، بیایید به پین‌آوت نگاه کنیم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

تراشه کوچک است، بسیاری از عملکردها روی همان پین ها معلق هستند. آنچه در پرانتز است "عملکرد جایگزین" است، با "بایت های گزینه" تغییر می کند (بایت های گزینه) – چیزی شبیه فیوزهای Atmega. شما می توانید مقادیر آنها را به صورت برنامه ای تغییر دهید، اما لازم نیست، زیرا عملکرد جدید فقط پس از راه اندازی مجدد فعال می شود. استفاده از ST Visual Programmer (دانلود شده با Visual Develop) که می تواند این بایت ها را تغییر دهد، ساده تر است. پین‌آوت نشان می‌دهد که پین‌های CH1 و CH2 اولین تایمر در براکت‌های مربع پنهان شده‌اند. لازم است بیت های AFR1 و AFR0 را در STVP تنظیم کنید و دومی نیز خروجی CH1 تایمر دوم را از PD4 به PC5 منتقل می کند.

بنابراین، 6 پین LED ها را کنترل می کنند: PC6، PC7 و PC3 برای تایمر اول، PC5، PD3 و PA3 برای تایمر دوم.

تنظیم پین های I/O خود در STM8 ساده تر و منطقی تر از STM32 است:

  • آشنا از ثبت جهت داده Atmega DDR (ثبت جهت داده ها): 1 = خروجی;
  • اولین رجیستر کنترلی CR1، هنگام خروجی، حالت فشار کش (1) یا تخلیه باز (0) را تنظیم می کند. از آنجایی که من LED ها را با کاتد به تراشه وصل می کنم، صفرها را در اینجا می گذارم.
  • رجیستر کنترل دوم CR2، هنگام خروجی، سرعت ساعت را تنظیم می کند: 1 = 10 مگاهرتز

#define PA_DDR     *(volatile uint8_t *)0x005002
#define PA_CR2     *(volatile uint8_t *)0x005004
#define PD_DDR     *(volatile uint8_t *)0x005011
#define PD_CR2     *(volatile uint8_t *)0x005013
#define PC_DDR     *(volatile uint8_t *)0x00500C
#define PC_CR2     *(volatile uint8_t *)0x00500E

PA_DDR = (1<<3); //output
PA_CR2 |= (1<<3); //fast
PD_DDR = (1<<3); //output
PD_CR2 |= (1<<3); //fast
PC_DDR = ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //output
PC_CR2 |= ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //fast

تنظیم PWM

ابتدا اجازه دهید اصطلاحات را تعریف کنیم:

  • فرکانس PWM - فرکانس تیک تایمر؛
  • بارگذاری مجدد خودکار، AR - مقدار قابل بارگیری خودکار که تایمر تا آن مقدار محاسبه می شود (دوره پالس).
  • به روز رسانی رویداد، UEV - رویدادی که زمانی رخ می دهد که تایمر تا AR شمارش کرده باشد.
  • چرخه وظیفه PWM - چرخه وظیفه PWM که اغلب به آن "ضریب وظیفه" می گویند.
  • گرفتن/مقایسه ارزش - مقدار برای ضبط/مقایسه، که تایمر به آن حساب کرده است کاری خواهد کرد (در مورد PWM، سیگنال خروجی را معکوس می کند).
  • مقدار پیش بارگذاری - مقدار از پیش بارگذاری شده ارزش را مقایسه کنید نمی تواند در حالی که تایمر تیک می زند تغییر کند، در غیر این صورت چرخه PWM شکسته می شود. بنابراین، مقادیر ارسالی جدید در یک بافر قرار می گیرند و زمانی که تایمر به پایان شمارش معکوس خود رسید و تنظیم مجدد شد، خارج می شوند.
  • تراز لبه и حالت های تراز مرکزی - تراز در امتداد مرز و در مرکز، مانند Atmel PWM سریع и PWM با فاز صحیح.
  • OCiREF، سیگنال مرجع مقایسه خروجی - سیگنال خروجی مرجع، در واقع همان چیزی است که روی پین مربوطه در حالت PWM ظاهر می شود.

همانطور که قبلاً از پین اوت مشخص است، دو تایمر دارای قابلیت PWM هستند - اولی و دومی. هر دو 16 بیتی هستند، اولی دارای بسیاری از ویژگی های اضافی است (به ویژه، می تواند هم بالا و هم پایین شمارش کند). ما نیاز داریم که هر دو به یک اندازه کار کنند، بنابراین تصمیم گرفتم با دومی بدیهی فقیرتر شروع کنم تا تصادفاً از چیزی که وجود ندارد استفاده نکنم. برخی از مشکلات این است که شرح عملکرد PWM همه تایمرها در کتابچه راهنمای مرجع در فصل مربوط به تایمر اول (حالت PWM 17.5.7) آمده است، بنابراین شما باید همیشه در طول سند به جلو و عقب بپرید.

PWM در STM8 یک مزیت مهم نسبت به PWM در Atmega دارد:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
PWM تراز شده مرزی
پیکربندی حساب از پایین به بالا
اگر بیت DIR در رجیستر TIM_CR1 پاک شود، شمارش از پایین به بالا فعال است
مثال
مثال از اولین حالت PWM استفاده می کند. سیگنال مرجع PWM OCiREF تا زمانی که TIM1_CNT < TIM1_CCRi بالا نگه داشته می شود. در غیر این صورت سطح پایینی می گیرد. اگر مقدار مقایسه در ثبات TIM1_CCRi از مقدار بارگذاری خودکار (رجیستر TIM1_ARR) بیشتر باشد، سیگنال OCiREF روی 1 نگه داشته می شود. اگر مقدار مقایسه 0 باشد، OCiREF روی صفر نگه داشته می شود....

تایمر STM8 در طول رویداد به روز رسانی ابتدا چک می کند ارزش را مقایسه کنید، و تنها پس از آن یک سیگنال مرجع تولید می کند. تایمر Atmega ابتدا خراب می شود و سپس مقایسه می کند و در نتیجه compare value == 0 خروجی یک سوزن است که باید به نحوی با آن برخورد کرد (مثلاً با معکوس کردن منطق برنامه‌ای).

بنابراین کاری که می خواهیم انجام دهیم: PWM 8 بیتی (AR == 255)، شمارش از پایین به بالا، تراز در امتداد مرز. از آنجایی که لامپ ها توسط کاتد به تراشه متصل می شوند، PWM باید 0 (LED روشن) را تا زمانی که ارزش را مقایسه کنید و 1 بعد از

قبلاً در مورد برخی خوانده ایم حالت PWM، بنابراین با جستجو در دفترچه راهنمای این عبارت (18.6.8 - TIMx_CCMR1) رجیستر مورد نیاز تایمر دوم را پیدا می کنیم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
110: حالت اول PWM - هنگام شمارش از پایین به بالا، اولین کانال فعال است در حالی که TIMx_CNT < TIMx_CCR1. در غیر این صورت کانال اول غیر فعال است. [در ادامه در سند یک کپی پیست اشتباه از تایمر 1 وجود دارد] 111: حالت دوم PWM - هنگام شمارش از پایین به بالا، اولین کانال غیرفعال است در حالی که TIMx_CNT < TIMx_CCR1. در غیر این صورت کانال اول فعال است.

از آنجایی که LED ها توسط کاتد به MK متصل می شوند، حالت دوم برای ما مناسب است (اولین نیز، اما ما هنوز آن را نمی دانیم).

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
بیت 3 OC1PE: بارگذاری اولیه پین ​​1 را فعال کنید
0: ثبت پیش بارگذاری در TIMx_CCR1 غیرفعال است. می‌توانید در هر زمانی به TIMx_CCR1 بنویسید. مقدار جدید بلافاصله کار می کند.
1: ثبت پیش بارگذاری در TIMx_CCR1 فعال است. عملیات خواندن/نوشتن به ثبت پیش بارگذاری دسترسی دارند. مقدار از پیش بارگذاری شده TIMx_CCR1 در طی هر رویداد به روز رسانی در ثبات سایه بارگذاری می شود.
*توجه: برای اینکه حالت PWM به درستی کار کند، رجیسترهای پیش بارگذاری باید فعال باشند. این در حالت سیگنال تک ضروری نیست (بیت OPM در ثبات TIMx_CR1 تنظیم شده است).

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

#define TIM2_CCMR1 *(volatile uint8_t *)0x005307
#define TIM2_CCMR2 *(volatile uint8_t *)0x005308
#define TIM2_CCMR3 *(volatile uint8_t *)0x005309

#define PWM_MODE2   0x70 //PWM mode 2, 0b01110000
#define OCxPE       0x08 //preload enable

TIM2_CCMR1 = (PWM_MODE2 | OCxPE);
TIM2_CCMR2 = (PWM_MODE2 | OCxPE);
TIM2_CCMR3 = (PWM_MODE2 | OCxPE);

AR از دو ثبات هشت بیتی تشکیل شده است، همه چیز ساده است:

#define TIM2_ARRH  *(volatile uint8_t *)0x00530F
#define TIM2_ARRL  *(volatile uint8_t *)0x005310

TIM2_ARRH = 0;
TIM2_ARRL = 255;

تایمر دوم فقط می تواند از پایین به بالا شمارش کند، تراز در امتداد مرز، چیزی نیاز به تغییر ندارد. بیایید تقسیم کننده فرکانس را مثلاً روی 256 قرار دهیم. برای تایمر دوم، تقسیم کننده در ثبات TIM2_PSCR تنظیم شده است و توان دو است:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

تنها چیزی که باقی می ماند این است که نتیجه گیری و خود تایمر دوم را روشن کنیم. اولین مشکل با رجیسترها حل می شود گرفتن/مقایسه کردن فعال: دو، سه کانال به طور نامتقارن در سراسر آنها پراکنده شده است. در اینجا ما همچنین می توانیم یاد بگیریم که می توان قطبیت سیگنال را تغییر داد، یعنی. در اصل، امکان استفاده از حالت PWM 1 وجود داشت. ما می نویسیم:

#define TIM2_CCER1 *(volatile uint8_t *)0x00530A
#define TIM2_CCER2 *(volatile uint8_t *)0x00530B

#define CC1E  (1<<0) // CCER1
#define CC2E  (1<<4) // CCER1
#define CC3E  (1<<0) // CCER2

TIM2_CCER1 = (CC1E | CC2E);
TIM2_CCER2 = CC3E;

و در نهایت، تایمر را در ثبات TIMx_CR1 شروع می کنیم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

بیایید یک آنالوگ ساده از AnalogWrite بنویسیم، که مقادیر واقعی را برای مقایسه به تایمر منتقل می کند. رجیسترها به صورت قابل پیش بینی نامگذاری می شوند ثبت ها را ضبط/مقایسه کنید، دو عدد از آنها برای هر کانال وجود دارد: 8 بیت مرتبه پایین در TIM2_CCRxL و بیت های مرتبه بالا در TIM2_CCRxH. از آنجایی که ما یک PWM 8 بیتی ایجاد کرده ایم، کافی است فقط بیت های کم اهمیت را بنویسیم:

#define TIM2_CCR1L *(volatile uint8_t *)0x005312
#define TIM2_CCR2L *(volatile uint8_t *)0x005314
#define TIM2_CCR3L *(volatile uint8_t *)0x005316

void setRGBled(uint8_t r, uint8_t g, uint8_t b)
{
    TIM2_CCR1L = r;
    TIM2_CCR2L = g;
    TIM2_CCR3L = b;
}

خواننده توجه متوجه خواهد شد که ما یک PWM کمی معیوب داریم که قادر به تولید 100% پر شدن نیست (در حداکثر مقدار 255، سیگنال برای یک چرخه تایمر معکوس می شود). برای LED ها این مهم نیست و خواننده با دقت می تواند حدس بزند که چگونه آن را تعمیر کند.

PWM روی تایمر دوم کار می کند، اجازه دهید به سراغ اولی برویم.

تایمر اول دقیقاً همان بیت‌ها را در رجیسترهای یکسان دارد (فقط آن بیت‌هایی که در تایمر دوم "رزرو" باقی مانده‌اند به طور فعال در اولی برای انواع چیزهای پیشرفته استفاده می‌شوند). بنابراین کافی است آدرس همان رجیسترها را در دیتاشیت پیدا کنید و کد را کپی کنید. خوب، مقدار تقسیم کننده فرکانس را تغییر دهید، زیرا ... تایمر اول می خواهد نه توان دو، بلکه یک مقدار دقیق 16 بیتی را در دو رجیستر دریافت کند. Prescaler High и کم. ما همه کارها را انجام می دهیم و ... تایمر اول کار نمی کند. موضوع چیه؟

مشکل فقط با نگاه کردن به کل بخش مربوط به رجیسترهای کنترلی تایمر 1 قابل حل است، جایی که ما به دنبال تایمر دوم می گردیم که ندارد. خواهد بود 17.7.30 ثبت شکست (TIM1_BKR)، جایی که این بیت وجود دارد:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
خروجی اصلی را فعال کنید

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

این همه مطمئنا در حال حاضر، کد آنجا.

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

STM8 Multiplex

Multiplexing در STM8

سومین مینی پروژه، اتصال هشت LED RGB به تایمر دوم در حالت PWM و نشان دادن رنگ های مختلف است. این مبتنی بر مفهوم مالتی پلکس LED است، یعنی اگر LED ها را خیلی خیلی سریع روشن و خاموش کنید، به نظر می رسد که آنها دائما روشن هستند (تداوم دید، اینرسی ادراک بصری). من یک بار انجام دادم چیزی شبیه به این در آردوینو.

الگوریتم کار به شکل زیر است:

  • آند اولین LED RGB را وصل کرد.
  • آن را روشن کرد و سیگنال های لازم را به کاتدها ارسال کرد.
  • تا پایان چرخه PWM منتظر ماند.
  • آند LED RGB دوم را وصل کرد.
  • روشنش کرد...

خوب و غیره البته، برای عملکرد زیبا، لازم است که آند متصل شود و LED در همان زمان "اشتعال" شود. خوب، یا تقریبا. در هر صورت، باید کدی بنویسیم که مقادیر را در سه کانال از تایمر دوم خروجی بگیرد، با رسیدن به UEV آنها را تغییر دهد و در همان زمان LED RGB فعال فعلی را تغییر دهد.

از آنجایی که سوئیچینگ LED خودکار است، باید یک "حافظه ویدئویی" ایجاد کنیم که کنترل کننده وقفه داده ها را از آن دریافت کند. این یک آرایه ساده است:

uint8_t colors[8][3];

برای تغییر رنگ یک LED خاص، کافی است مقادیر مورد نیاز را در این آرایه بنویسید. و متغیر مسئول تعداد LED فعال خواهد بود

uint8_t cnt;

دیموکس

برای مالتی پلکس مناسب، به طرز عجیبی به یک دممولتی پلکسر CD74HC238 نیاز داریم. Demultiplexer - تراشه ای که اپراتور را در سخت افزار پیاده سازی می کند <<. از طریق سه پایه ورودی (بیت های 0، 1 و 2) یک عدد سه بیتی X را به آن تغذیه می کنیم و در پاسخ عدد خروجی را فعال می کند (1<<X). ورودی های باقی مانده تراشه برای مقیاس بندی کل طراحی استفاده می شود. ما به این تراشه نه تنها برای کاهش تعداد پین های میکروکنترلر اشغال شده، بلکه برای ایمنی نیز نیاز داریم - تا به طور تصادفی بیش از حد ممکن LED روشن نشود و MK نسوزد. تراشه یک پنی قیمت دارد و باید همیشه در کابینت داروهای خانگی شما نگهداری شود.

CD74HC238 ما وظیفه تامین ولتاژ آند LED مورد نظر را بر عهده خواهد داشت. در یک مالتی پلکس تمام عیار، ولتاژ را از طریق P-MOSFET به ستون تامین می کند، اما در این نسخه آزمایشی این امکان به طور مستقیم وجود دارد، زیرا 20 میلی آمپر می کشد، با توجه به اعتبار حداکثر مطلق در دیتاشیت از جانب برگه داده CD74HC238 ما به پین ​​اوت ها و این برگه تقلب نیاز داریم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
H = سطح ولتاژ بالا، L = سطح ولتاژ پایین، X - مهم نیست

E2 و E1 را به زمین، E3، A0، A1 و A3 را به پین ​​های PD5، PC3، PC4 و PC5 STM8 متصل می کنیم. از آنجایی که جدول بالا شامل سطوح پایین و بالا است، ما این پین ها را به عنوان پین های فشاری-کششی پیکربندی می کنیم.

PWM

PWM در تایمر دوم مانند داستان قبلی با دو تفاوت پیکربندی شده است:

ابتدا باید وقفه را فعال کنیم به روز رسانی رویداد (UEV) که تابعی را فراخوانی می کند که LED فعال را تغییر می دهد. این کار با تغییر بیت انجام می شود Update Interrupt Enable در یک ثبت نام با نامی گویا

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
وقفه فعال کردن ثبت نام

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

تفاوت دوم مربوط به پدیده مالتی پلکس شدن است، مانند شبح - درخشش انگلی دیودها. در مورد ما، ممکن است به این دلیل به نظر برسد که تایمر، که باعث وقفه در UEV شده است، همچنان به تیک زدن ادامه می دهد، و کنترل کننده وقفه قبل از اینکه تایمر شروع به نوشتن چیزی روی پین ها کند، زمان لازم برای تغییر LED را ندارد. برای مبارزه با این، شما باید منطق را معکوس کنید (0 = حداکثر روشنایی، 255 = هیچ چیز روشن نیست) و از مقادیر شدید چرخه وظیفه اجتناب کنید. آن ها اطمینان حاصل کنید که پس از UEV LED ها برای یک دوره PWM کاملا خاموش می شوند.

تغییر قطبیت:

//set polarity 
    TIM2_CCER1 |= (CC1P | CC2P);
    TIM2_CCER2 |= CC3P;

از تنظیم r، g و b روی 255 خودداری کنید و به یاد داشته باشید که هنگام استفاده آنها را معکوس کنید.

قطع می کند

ماهیت وقفه این است که تحت شرایط خاصی تراشه اجرای برنامه اصلی را متوقف می کند و برخی از عملکردهای خارجی را فراخوانی می کند. وقفه ها به دلیل تأثیرات خارجی یا داخلی از جمله تایمر رخ می دهد.

هنگامی که ما برای اولین بار پروژه ای را در ST Visual Develop ایجاد کردیم، علاوه بر این main.c ما یک پنجره با یک فایل مرموز دریافت کردیم stm8_interrupt_vector.c، به طور خودکار در پروژه گنجانده شده است. در این فایل به هر وقفه یک تابع اختصاص داده شده است NonHandledInterrupt. باید تابع خود را به وقفه مورد نظر متصل کنیم.

دیتاشیت دارای جدولی از بردارهای وقفه است که در آن موارد مورد نیاز خود را پیدا می کنیم:

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8
13 به روز رسانی/سرریز TIM2
14 TIM2 گرفتن/مقایسه

ما باید LED را در UEV تغییر دهیم، بنابراین به وقفه شماره 13 نیاز داریم.

بر این اساس اولاً در پرونده stm8_interrupt_vector.c نام پیش فرض تابع مسئول وقفه شماره 13 (IRQ13) را به نام خود تغییر دهید:

{0x82, TIM2_Overflow}, /* irq13 */

در مرحله دوم، ما باید یک فایل ایجاد کنیم main.h با محتوای زیر:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void TIM2_Overflow (void);
#endif

و در نهایت این تابع را در خود بنویسید main.c:

@far @interrupt void TIM2_Overflow (void)
{
    PD_ODR &= ~(1<<5); // вырубаем демультиплексор
    PC_ODR = (cnt<<3); // записываем в демультиплексор новое значение
    PD_ODR |= (1<<5); // включаем демультиплексор

    TIM2_SR1 = 0; // сбрасываем флаг Update Interrupt Pending

    cnt++; 
    cnt &= 7; // двигаем счетчик LED

    TIM2_CCR1L = ~colors[cnt][0]; // передаем в буфер инвертированные значения
    TIM2_CCR2L = ~colors[cnt][1]; // для следующего цикла ШИМ
    TIM2_CCR3L = ~colors[cnt][2]; // 

    return;
}

تنها چیزی که باقی می ماند فعال کردن وقفه ها است. این کار با استفاده از دستور اسمبلر انجام می شود rim - باید به دنبال آن بگردید کتابچه راهنمای برنامه نویسی:

//enable interrupts
_asm("rim");

یکی دیگر از دستورات اسمبلر این است sim - وقفه ها را خاموش می کند. آنها باید در حین نوشتن مقادیر جدید در "حافظه ویدیویی" خاموش شوند تا وقفه ایجاد شده در لحظه اشتباه آرایه را خراب نکند.

همه کدها - در GitHub.

برگه های داده 2 را بخوانید: SPI در STM32. PWM، تایمرها و وقفه ها در STM8

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

منبع: www.habr.com

اضافه کردن نظر