Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

В birinci hissəsində Arduino şalvarından böyüyən hobbi elektronika mühəndislərinə mikrokontrollerlər üçün məlumat cədvəllərini və digər sənədləri necə və niyə oxumalı olduqlarını söyləməyə çalışdım. Mətn böyük oldu, buna görə də ayrı bir məqalədə praktiki nümunələri göstərməyə söz verdim. Yaxşı, o, özünü yükləyici adlandırdı ...

Bu gün sizə STM32 (Mavi Həb) və STM8 kontrollerlərindəki bir çox layihələr üçün kifayət qədər sadə, lakin zəruri tapşırıqları həll etmək üçün məlumat cədvəllərindən necə istifadə edəcəyinizi göstərəcəyəm. Bütün demo layihələri mənim sevimli LED-lərə həsr olunub, biz onları böyük miqdarda işıqlandıracağıq, bunun üçün hər cür maraqlı periferiyalardan istifadə etməli olacağıq.

Mətn yenə böyük oldu, ona görə də rahatlıq üçün məzmunu hazırlayıram:

STM32 Blue Pill: DM16 sürücüsü ilə 634 LED
STM8: Altı PWM pininin qurulması
STM8: Üç sancaqda 8 RGB LED, kəsir

İmtina: Mən mühəndis deyiləm, elektronikada dərin biliyə malik olduğumu iddia etmirəm, məqalə mənim kimi həvəskarlar üçün nəzərdə tutulub. Əslində iki il əvvəl özümü hədəf auditoriya hesab edirdim. Əgər kimsə mənə tanış olmayan çip üçün məlumat vərəqlərini oxumağın qorxulu olmadığını desəydi, mən İnternetdə bəzi kod parçaları axtarmağa və qayçı və yapışqan gips ilə qoltuqağaqları icad etməyə çox vaxt sərf etməzdim.

Bu məqalənin diqqəti layihələr deyil, məlumat cədvəlləridir, buna görə kod çox cilalanmış və tez-tez qoltuqağacı olmaya bilər. Layihələrin özləri çox sadədir, baxmayaraq ki, onlar yeni bir çiplə ilk tanışlıq üçün uyğundur.

Ümid edirəm ki, məqaləm hobbi ilə məşğul olmağın oxşar mərhələsində kiməsə kömək edəcəkdir.

STM32

DM16 və SPI ilə 634 LED

Blue Pill (STM32F103C8T6) və DM634 LED sürücüsündən istifadə edən kiçik layihə. Məlumat cədvəllərinin köməyi ilə biz sürücü, STM IO portları ilə məşğul olacağıq və SPI-ni konfiqurasiya edəcəyik.

DM634

16 x 16-bit PWM çıxışı olan Tayvan çipi zəncirlə bağlana bilər. Daha gənc 12 bitlik model yerli layihədən tanınır yüngül paket. Bir vaxtlar, DM63x və məşhur TLC5940 arasında seçim edərək, bir neçə səbəbə görə DM-də qərar verdim: 1) Aliexpress-də TLC mütləq saxtadır, amma bu deyil; 2) DM öz tezlik generatoru olan avtonom PWM-ə malikdir; 3) onu Moskvada ucuz qiymətə almaq olar və Əlidən bağlama gözləmək olmaz. Və əlbəttə ki, hazır kitabxanadan istifadə etmədən çipi özünüz idarə etməyi öyrənmək maraqlı idi. Çiplər indi əsasən SSOP24 paketində təqdim olunur, onları adapterdə lehimləmək asandır.

İstehsalçı Tayvanlı olduğundan, məlumat vərəqi çip üçün Çin ingiliscə yazılmışdır, yəni əyləncəli olacaq. Əvvəlcə pinouta baxınPin bağlantısı) hansı ayağın nəyi birləşdirəcəyini və sancaqların təsvirini anlamaq üçün (Pin təsviri). 16 sancaqlar:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Sink DC mənbələri (açıq drenaj)

Sink / Açıq drenaj çıxışı - ehtiyat; daxil olan cərəyanın mənbəyi; aktiv vəziyyətdə yerə qoşulmuş bir çıxış - LED-lər katodlar vasitəsilə sürücüyə qoşulur. Elektrik baxımından bu, əlbəttə ki, "açıq drenaj" deyil (açıq drenaj), lakin məlumat cədvəllərində drenaj rejimində çıxışlar üçün belə bir təyinat tez-tez olur.

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Çıxış cərəyanı dəyərini təyin etmək üçün REXT və GND arasındakı xarici rezistorlar

Çıxışların daxili müqavimətini idarə edən REXT pin və torpaq arasında istinad rezistoru quraşdırılmışdır, məlumat cədvəlinin 9-cu səhifəsindəki qrafikə baxın. DM634-də bu müqavimət ümumi parlaqlığı təyin etməklə proqram təminatı ilə də idarə oluna bilər (qlobal parlaqlıq); Bu yazıda təfərrüatlara girməyəcəyəm, sadəcə olaraq 2.2 - 3 kOhm rezistor qoyacağam.

Çipin necə idarə olunacağını başa düşmək üçün cihazın interfeysinin təsvirinə baxaq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Bəli, budur, bütün şöhrəti ilə Çin İngilis dili. Bunu tərcümə etmək problemlidir, istəsəniz başa düşə bilərsiniz, amma başqa bir yol var - məlumat cədvəlində funksional olaraq yaxın TLC5940 ilə əlaqənin necə təsvir olunduğuna baxmaq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
… Cihaza məlumat daxil etmək üçün yalnız üç sancaq tələb olunur. SCLK siqnalının yüksələn kənarı məlumatları SIN pinindən daxili registrə köçürür. Bütün məlumatlar yükləndikdən sonra qısa bir yüksək XLAT siqnalı daxili registrlərdə ardıcıl olaraq ötürülən məlumatları bağlayır. Daxili registrlər XLAT siqnal səviyyəsi ilə işə salınan qapılardır. Bütün məlumatlar əvvəlcə MSB-yə ötürülür.

Latch - mandalı / mandalı / mandalı.
Yüksələn kənar nəbzin aparıcı kənarıdır
Əvvəlcə MSB – ən əhəmiyyətli (ən solda) bir az irəli.
saat məlumatı üçün – verilənlərin ardıcıl ötürülməsi (bit-bit).

Söz latch tez-tez çiplər üçün sənədlərdə tapılır və müxtəlif yollarla tərcümə olunur, buna görə də başa düşmək üçün özümə icazə verəcəyəm

kiçik təhsil proqramıLED sürücüsü mahiyyətcə bir növbə qeydidir. "Shift" (keçmək) adında - cihazın daxilində məlumatların bit-bit hərəkəti: içəriyə atılan hər yeni bit bütün zənciri onun qarşısında irəli itələyir. Növbə zamanı LED-lərin xaotik yanıb-sönməsini heç kim müşahidə etmək istəmədiyi üçün proses işçilərdən deklanşörlə ayrılmış bufer registrlərində baş verir (latch) bitlərin istənilən ardıcıllıqla düzüldüyü bir növ soyunma otağıdır. Hər şey hazır olduqda, deklanşör açılır və bitlər əvvəlki partiyanı əvəz edərək işə gedir. Söz latch mikrosxemlər üçün sənədlərdə demək olar ki, həmişə belə bir damper nəzərdə tutulur, hansı birləşmələrdə istifadə olunur.

Beləliklə, məlumatların DM634-ə ötürülməsi aşağıdakı kimi həyata keçirilir: DAI girişini uzaq LED-in yüksək bitinin dəyərinə təyin edin, DCK-ni yuxarı və aşağı çəkin; DAI girişini növbəti bitin dəyərinə təyin edin, DCK-nı çəkin; və bütün bitlər ötürülənədək (saat daxil oldu), bundan sonra LAT-ı çəkirik. Bu əl ilə edilə bilərbit bang), lakin bunun üçün xüsusi itilənmiş SPI interfeysindən istifadə etmək daha yaxşıdır, çünki STM32-də iki nüsxədə təqdim olunur.

Mavi Tablet STM32F103

Giriş: STM32 nəzarətçiləri Atmega328-dən qorxulu ola biləcəklərindən daha mürəkkəbdir. Eyni zamanda, enerjiyə qənaət səbəblərinə görə, demək olar ki, bütün periferik qurğular başlanğıcda söndürülür və saat tezliyi daxili mənbədən 8 MHz-dir. Xoşbəxtlikdən, STM proqramçıları çipi "hesablanmış" 72 MHz-ə çatdıran bir kod yazdılar və mənim bildiyim bütün IDE-lərin müəllifləri onu başlanğıc proseduruna daxil etdilər, buna görə də saatlamağa ehtiyacımız yoxdur (lakin həqiqətən istəsən edə bilərsən). Ancaq periferiyaları işə salmalısınız.

Sənədlər: Məşhur STM32F103C8T6 çipi Blue Pill-də quraşdırılıb, bunun üçün iki faydalı sənəd var:

Məlumat cədvəlində bizi maraqlandıra bilər:

  • Pinouts - çip pinouts - lövhələri özümüz hazırlamağa qərar verdiyimiz halda;
  • Yaddaş xəritəsi - müəyyən bir çip üçün yaddaş xəritəsi. İstinad Təlimatında bütün xətt üçün xəritə var, bizdə olmayan registrlərdən bəhs edir.
  • Pin tərifləri cədvəli - əsas və alternativ pin funksiyalarının siyahısı; İnternetdə "mavi həb" üçün sancaqlar və onların funksiyaları siyahısı ilə daha rahat şəkillər tapa bilərsiniz. Buna görə də, biz dərhal google-da Blue Pill pinout-a baxırıq və bu şəkli əlimizdə saxlayırıq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Qeyd: İnternetdən alınan şəkildə bir səhv var idi, şərhlərdə qeyd edildi, buna görə təşəkkür edirəm. Şəkil dəyişdirildi, amma bu bir dərsdir - məlumatları məlumat cədvəllərindən yox, yoxlamaq daha yaxşıdır.

Məlumat vərəqini çıxarırıq, İstinad Təlimatını açırıq, bundan sonra yalnız ondan istifadə edirik.
Prosedur: standart giriş / çıxışla məşğul olun, SPI-ni konfiqurasiya edin, lazımi periferiyaları yandırın.

Giriş Çıxışı

Atmega328-də I/O olduqca sadədir, buna görə də STM32 seçimlərinin bolluğu çaşdırıcı ola bilər. İndi yalnız nəticələrə ehtiyacımız var, amma hətta dörd variant var:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Açıq Drenaj Çıxışı, Push-Pull Çıxışı, Push-Pull Alternativi, Açıq Drenaj Alternativi

"Çək-tək" (itələmək) - Arduino-dan adi çıxış, pin ya YÜKSƏK, ya da LOW ola bilər. Ancaq "açıq drenaj" yaranır çətinliklər, əslində burada hər şey sadə olsa da:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Çıxış konfiqurasiyası / port çıxışa təyin edildikdə: / çıxış buferi aktivləşdirildi: / – açıq drenaj rejimi: çıxış registrində "0" N-MOS-u aktivləşdirir, çıxış registrindəki "1" portu Hi-Z rejimində tərk edir (P) -MOS aktivləşdirilməyib ) / - push-pull rejimi: çıxış registrində "0" N-MOS-u, çıxış registrində "1" P-MOS-u aktivləşdirir.

Bütün açıq drenaj fərqi (açıq drenaj) "push-pull" dən (itələmək) odur ki, birinci pində YÜKSƏK vəziyyəti qəbul edə bilməz: vahid çıxış registrinə yazıldıqda o, yüksək müqavimət rejiminə keçir (yüksək impedance, Salam). Sıfır yazarkən, hər iki rejimdə pin həm məntiqi, həm də elektrik olaraq eyni davranır.

Normal çıxış rejimində pin sadəcə çıxış registrinin məzmununu tərcümə edir. "Alternativ"də o, müvafiq periferiya tərəfindən idarə olunur (bax 9.1.4):

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Port biti alternativ funksiya çıxışı kimi konfiqurasiya edilibsə, çıxış registrini söndürür və pin periferiyanın çıxış siqnalına qoşulur.

Hər bir sancağın alternativ funksionallığı aşağıda təsvir edilmişdir Pin Tərifləri Məlumat vərəqi endirilmiş şəkildədir. Pəncərənin bir neçə alternativ funksiyası varsa nə etməli olduğunuzu soruşduqda, cavab məlumat vərəqindəki qeyd ilə verilir:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Əgər birdən çox periferiya eyni pindən istifadə edərsə, alternativ funksiyalar arasında ziddiyyətin qarşısını almaq üçün Periferik Saatı Aktivləşdirən bitdən (müvafiq RCC registrində) istifadə edərək keçid etməklə eyni vaxtda yalnız bir periferiyadan istifadə edilməlidir.

Nəhayət, çıxış rejimində olan sancaqlar da saat sürətinə malikdir. Bu, başqa bir enerji qənaət xüsusiyyətidir, bizim vəziyyətimizdə sadəcə onu maksimuma qoyuruq və unuduruq.

Beləliklə: biz SPI istifadə edirik, yəni iki sancaq (məlumat və saat siqnalı ilə) "alternativ təkan-çəkmə funksiyası" və daha biri (LAT) "normal təkan çəkmə" olmalıdır. Ancaq onları təyin etməzdən əvvəl SPI ilə məşğul olaq.

SPI

Başqa bir kiçik hiylə

SPI və ya Serial Periferik İnterfeys (seriyalı periferik interfeys) MK-ni digər MK-lər və ümumiyyətlə xarici dünya ilə əlaqələndirmək üçün sadə və çox effektiv interfeysdir. Onun işləmə prinsipi artıq yuxarıda təsvir edilmişdir, burada Çin LED sürücüsü haqqında (istinad təlimatında 25-ci bölməyə baxın). SPI master ("master") və slave ("qul") rejimlərində işləyə bilər. SPI dörd əsas kanala malikdir, bunların hamısı iştirak edə bilməz:

  • MOSI, Master Output / Slave Input: bu pin məlumatı master rejimində göndərir və qul rejimində məlumatları qəbul edir;
  • MISO, Master Input / Slave Output: əksinə, ustada alır, qulda verir;
  • SCK, Serial Clock: masterdə məlumat ötürmə tezliyini təyin edir və ya qulda saat siqnalını alır. Əsasən, vuruşları döyür;
  • SS, Slave Select: bu kanalla qul ondan nəsə istədiklərini bilir. STM32-də NSS adlanır, burada N = mənfi, yəni. bu kanalda torpaq varsa, kontroller qul olur. Açıq Drenaj Çıxış rejimi ilə yaxşı birləşir, lakin bu başqa bir hekayədir.

Hər şey kimi, STM32-də SPI funksionallıqla zəngindir, bu da onu başa düşməyi bir qədər çətinləşdirir. Məsələn, yalnız SPI ilə deyil, həm də I2S interfeysi ilə işləyə bilər və onların təsvirləri sənədlərdə qarışıqdır, artıqlığı vaxtında kəsmək lazımdır. Tapşırığımız olduqca sadədir: yalnız MOSI və SCK istifadə edərək məlumat vermək lazımdır. Biz 25.3.4 bölməsinə gedirik (yarım dupleks rabitə, yarım dupleks rabitə), burada tapırıq 1 saat və 1 bir istiqamətli məlumat teli (1 saat və 1 bir istiqamətli məlumat axını):

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Bu rejimdə proqram SPI-dən yalnız ötürmə və ya yalnız qəbul rejimində istifadə edir. / Yalnız ötürmə rejimi dupleks rejiminə bənzəyir: məlumat ötürmə pinində (ana rejimdə MOSI və ya qul rejimində MISO) ötürülür, qəbul pinindən isə (müvafiq olaraq MISO və ya MOSI) normal I/O kimi istifadə edilə bilər. pin. Bu halda, tətbiqin Rx buferinə məhəl qoymaması kifayətdir (əgər oxunubsa, ötürülən məlumat olmayacaq).

Əla, MISO pin pulsuzdur, gəlin LAT siqnalını ona birləşdirək. Gəlin STM32-də proqramlı şəkildə idarə oluna bilən Slave Select ilə məşğul olaq ki, bu da son dərəcə rahatdır. Eyni adlı paraqrafı 25.3.1 SPI bölməsində oxuyuruq Ümumi təsvir:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
NSS proqram nəzarəti (SSM = 1) / Slave seçim məlumatı SPI_CR1 registrinin SSI bitində var. Xarici NSS pin digər proqram ehtiyacları üçün pulsuz olaraq qalır.

Reyestrlərə yazmaq vaxtıdır. SPI2-dən istifadə etmək qərarına gəldim, biz onun əsas ünvanını məlumat cədvəlində axtarırıq - 3.3 Yaddaş Xəritəsi (Yaddaş Xəritəsi) bölməsində:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Yaxşı, başlayaq:

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

25.3.3 bölməsini "SPI-nin master rejimində konfiqurasiyası" başlığı ilə açırıq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

1. SPI_CR2 registrində BR[0:1] bitləri ilə serial interfeys saatını təyin edin.

Reyestrlər eyni adlı istinad kitabçası bölməsində toplanır. Ünvan dəyişikliyi (ünvan ofset) CR1-də 0x00 var, standart olaraq bütün bitlər təmizlənir (Dəyəri sıfırlayın 0x0000):

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

BR bitləri nəzarətçi saat bölücüsünü təyin edir, beləliklə, SPI-nin işləyəcəyi tezliyi müəyyən edir. STM32 tezliyi 72 MHz olacaq, LED sürücüsü, məlumat cədvəlinə görə, 25 MHz-ə qədər tezlikdə işləyir, buna görə də dördə bölmək lazımdır (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. Məlumat ötürülməsi və serial interfeys saatı arasındakı əlaqəni müəyyən etmək üçün CPOL və CPHA bitlərini təyin edin (240-cı səhifədəki diaqrama baxın)

Burada məlumat vərəqini oxuduğumuz və sxemlərə baxmadığımız üçün gəlin 704-cü səhifədəki CPOL və CPHA bitlərinin mətn təsvirinə daha yaxından nəzər salaq (SPI Ümumi Təsviri):

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Saat fazası və polarite
SPI_CR1 registrinin CPOL və CPHA bitlərindən istifadə edərək, vaxt nisbətləri üçün proqramlı olaraq dörd variant seçə bilərsiniz. CPOL (Clock Polarite) biti heç bir məlumat ötürülmədikdə saat siqnalının vəziyyətinə nəzarət edir. Bu bit master və slave rejimlərini idarə edir. CPOL sıfırlanırsa, SCK sancağı istirahətdə aşağıdır. CPOL biti quraşdırılıbsa, boş vəziyyətdə SCK pin yüksəkdir.
CPHA (Saat Fazası) biti təyin edilərsə, MSB tələsinin strobu SCK siqnalının ikinci kənarıdır (CPOL təmizləndikdə aşağı düşür və ya CPOL təyin edildikdə yüksələn kənar). Məlumat ikinci saat dəyişikliyində kilidlənir. CPHA biti təmizlənərsə, SCK siqnalının yüksələn kənarı (CPOL təyin olunarsa, enən kənar və ya CPOL aydındırsa, yüksələn kənar) yüksək bit tələsinin strobe kimi xidmət edir. Məlumat ilk saat dəyişikliyində bağlanır.

Bu biliyin dadına baxaraq belə nəticəyə gəlirik ki, hər iki bit sıfır qalmalıdır, çünki biz SCK siqnalının istifadə edilmədikdə aşağı qalmasını və məlumatların nəbzin yüksələn kənarında ötürülməsini istəyirik (şək. yüksələn kənar DM634 məlumat cədvəlində).

Yeri gəlmişkən, burada ilk olaraq ST məlumat vərəqlərində lüğətin bir xüsusiyyəti ilə qarşılaşdıq: onlarda "biti sıfırla" ifadəsi yazılmışdır bir az sıfırlamaq üçünbir az təmizləmək üçünməsələn, Atmega kimi.

3. 8-bit və ya 16-bit məlumat blokunun formatını müəyyən etmək üçün DFF bitini təyin edin

DM16 kimi 634 bitlik PWM məlumatlarının ötürülməsi ilə narahat olmamaq üçün xüsusi olaraq 12 bitlik DM633 aldım. DFF birlik yaratmağın mənası var:

#define DFF         0x0800

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

4. Blok formatını müəyyən etmək üçün SPI_CR1 registrində LSBFIRST bitini konfiqurasiya edin

LSBFIRST, adından da göründüyü kimi, ötürməni əvvəlcə ən az əhəmiyyətli bitlə qurur. Lakin DM634 əvvəlcə MSB məlumatlarını almaq istəyir. Buna görə də onu sıfırlamağa buraxırıq.

5. Aparat rejimində, əgər NSS pinindən daxiletmə tələb olunursa, bütün bayt ötürmə ardıcıllığı ərzində NSS pinini yüksək idarə edin. NSS proqram rejimində SPI_CR1 registrində SSM və SSI bitlərini təyin edin. NSS pininin çıxarılması üçün yalnız SSOE bitini təyin etmək lazımdır.

NSS aparat rejimini unutmaq üçün SSM və SSI quraşdırın:

#define SSI         0x0100
#define SSM         0x0200

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

6. MSTR və SPE bitləri təyin edilməlidir (onlar yalnız NSS yüksək olduqda təyin edilmiş olaraq qalır)

Əslində, bu bitlərlə biz SPI-mizi master olaraq təyin edirik və onu işə salırıq:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI konfiqurasiya olunub, dərhal sürücüyə bayt göndərən funksiyaları yazaq. 25.3.3 "Master rejimində SPI konfiqurasiyası"nı oxumağa davam edin:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Məlumat ötürmə qaydası
Transfer Tx buferinə bayt yazıldıqda başlayır.
Məlumat baytı keçid registrinə yüklənir paralel rejimi (daxili avtobusdan) ilk bitin ötürülməsi zamanı, sonra isə ötürülür ardıcıl MOSI pin rejimi, CPI_CR1 registrindəki LSBFIRST bitinin parametrindən asılı olaraq ilk və ya son bit irəli. TXE bayrağı məlumat ötürüldükdən sonra təyin olunur Tx buferindən keçid registrinə, və CPI_CR1 registrində TXEIE biti təyin edildikdə kəsmə yaranır.

STM kontrollerlərində SPI tətbiqinin bir xüsusiyyətinə diqqət çəkmək üçün tərcümədə bir neçə sözü vurğuladım. Atmega-da TXE bayrağı (Tx Boş, Tx boşdur və məlumatları qəbul etməyə hazırdır) yalnız bütün bayt göndərildikdən sonra təyin edilir zahiri. Və burada bayt daxili sürüşmə registrinə daxil edildikdən sonra bu bayraq qoyulur. Orada bütün bitlərlə eyni vaxtda (paralel olaraq) itələndiyindən və sonra verilənlər ardıcıl olaraq ötürüldüyündən, bayt tamamilə göndərilməmişdən əvvəl TXE təyin olunur. Bu vacibdir, çünki LED sürücümüzün vəziyyətində, göndərdikdən sonra LAT pinini çəkməliyik Bütün məlumatlar, yəni. yalnız TXE bayrağı bizə kifayət etməyəcək.

Bu o deməkdir ki, bizə başqa bir bayraq lazımdır. 25.3.7 - "Status bayraqları"na baxaq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
<...>
Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
BUSY bayrağı
BSY bayrağı hardware tərəfindən təyin edilir və təmizlənir (ona yazının heç bir təsiri yoxdur). BSY bayrağı SPI rabitə qatının vəziyyətini göstərir.
Sıfırlayır:
köçürmə tamamlandıqda (köçürmə davamlı olduqda master rejim istisna olmaqla)
SPI söndürüldükdə
master rejimi xətası baş verdikdə (MODF=1)
Əgər ötürmə davamlı deyilsə, hər bir məlumat ötürülməsi arasında BSY bayrağı silinir.

Yaxşı, işinə yarayacaq. Tx buferinin harada yerləşdiyini öyrənin. Bunu etmək üçün "SPI Məlumat Reyestrini" oxuyun:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Bit 15:0 DR[15:0] Məlumat reyestri
Alınan məlumatlar və ya ötürüləcək məlumatlar.
Məlumat reyestri iki buferə bölünür, biri yazmaq (ötürmə buferi) və digəri oxumaq üçün (qəbul buferi). Məlumat registrinə yazı Tx buferinə yazır və məlumat registrindən oxunuş Rx buferində olan dəyəri qaytaracaq.

Yaxşı, TXE və BSY bayraqlarının olduğu status reyestri:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Biz yazırıq:

#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
}

Yaxşı, LED sürücüsünün çıxışlarının sayına görə 16 dəfə iki bayt ötürməli olduğumuz üçün belə bir şey:

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();
}

Amma biz hələ LAT pinini necə çəkəcəyimizi bilmirik, ona görə də I/O-ya qayıdaq.

Sancaqlar təyin edin

STM32F1-də pinlərin vəziyyətinə cavabdeh olan registrlər olduqca qeyri-adidir. Onların Atmega-dan daha çox olduğu aydındır, lakin onlar digər STM çiplərindən də fərqlənirlər. Bölmə 9.1 GPIO Ümumi Təsvir:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Ümumi təyinatlı I/O portlarının hər biri (GPIO) iki 32-bit konfiqurasiya registrinə (GPIOx_CRL və GPIOx_CRH), iki 32-bit məlumat registrinə (GPIOx_IDR və GPIOx_ODR), 32-bit quraşdırma/sıfırlama registrinə (GPIOx_BSRR), 16-bit sıfırlama registrinə (GPIOx_BRR) və 32- malikdir. bit bloklama reyestri (GPIOx_LCKR).

Qeyri-adi, həm də olduqca əlverişsiz olanlar burada ilk iki registrdir, çünki portun 16 pinləri onların arasında “qardaş üçün dörd bit” formatında səpələnmişdir. Bunlar. XNUMX-dan XNUMX-yə qədər olan sancaqlar CRL-də, qalanları isə CRH-dədir. Eyni zamanda, qalan registrlər bütün port pinlərinin bitlərinə uğurla uyğun gəlir - çox vaxt qalan yarı "ehtiyat" olur.

Sadəlik üçün siyahının sonundan başlayaq.

Bloklama reyestrinə ehtiyacımız yoxdur.

Quraşdırma və sıfırlama registrləri olduqca gülməlidir ki, onlar bir-birini qismən təkrarlayır: hər şeyi yalnız BSRR-də yaza bilərsiniz, burada yuxarı 16 bit pin sıfıra endiriləcək, aşağı olanlar isə 1-ə təyin ediləcək və ya siz də edə bilərsiniz. BRR istifadə edin, aşağı 16 biti yalnız pin sıfırlanır. İkinci variantı bəyənirəm. Bu registrlər vacibdir, çünki onlar sancaqlara atom girişini təmin edir:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Atom quraşdırma və ya sıfırlama
GPIOx_ODR-i bit səviyyəsində proqramlaşdırarkən kəsilmələri söndürməyə ehtiyac yoxdur: bir və ya bir neçə biti tək APB2 atom yazma əməliyyatı ilə dəyişə bilərsiniz. Bu, dəyişdiriləcək bitin təyin/sıfırlama registrinə (GPIOx_BSRR və ya yalnız sıfırlama üçün GPIOx_BRR) "1" yazmaqla əldə edilir. Digər bitlər dəyişməz qalacaq.

Məlumat registrlərinin kifayət qədər danışan adları var - IDR = Input İstiqamət reyestri, giriş reyestri; ODR = Buraxılış İstiqamət reyestri, çıxış registri. Hazırkı layihədə onlara ehtiyacımız yoxdur.

Və nəhayət, nəzarət qeydləri. İkinci SPI-nin sancaqları, yəni PB13, PB14 və PB15 ilə maraqlandığımız üçün dərhal CRH-ə baxırıq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Və görürük ki, 20-dən 31-ə kimi bitlərlə nəsə yazmaq lazım gələcək.

Yuxarıdakı sancaqlardan nə istədiyimizi artıq anladıq, buna görə də burada ekran görüntüsü olmadan edəcəm, sadəcə deyin ki, MODE istiqaməti (hər iki bit 0-a qoyulubsa giriş) və pin sürətini təyin edir (bizə 50MHz lazımdır, yəni. hər iki pin "1") və CNF rejimi təyin edir: normal "push-push" - 00, "alternativ" - 10. Varsayılan olaraq, yuxarıda gördüyümüz kimi, bütün sancaqlar aşağıdan üçüncü bitə malikdir ( CNF0), onları rejimə qoyur üzən giriş.

Bu çiplə başqa bir şey etməyi planlaşdırdığımdan, sadəlik üçün ümumiyyətlə həm aşağı, həm də yuxarı nəzarət registrləri üçün bütün mümkün MODE və CNF dəyərlərini təyin etdim.

Nədənsə bu kimi

#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

Sancaqlarımız B portundadır (əsas ünvan - 0x40010C00), kod:

#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;

Və buna uyğun olaraq, siz BRR və BSRR registrlərini bükəcək LAT üçün təyinlər yaza bilərsiniz:

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

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

(LAT_aşağı yalnız ətalətlə, birtəhər həmişə belə idi, qoy özünüz üçün qalsın)

İndi hər şey əladır, sadəcə işləmir. Bu STM32 olduğundan, onlar burada elektrik enerjisinə qənaət edirlər, yəni lazımi periferik cihazların saat rejimini işə salmaq lazımdır.

Saat rejimini yandırın

Saat saatdan məsuldur, onlar da Saatdır. Artıq RCC abbreviaturasını görə bildik. Biz bunu sənədlərdə axtarırıq: bu Sıfırlama və Saata Nəzarətdir (Sıfırlama və Saatın İdarə Edilməsi).

Yuxarıda qeyd edildiyi kimi, xoşbəxtlikdən, STM-dən olan insanlar saat mövzusunun ən çətin hissəsini bizim üçün etdilər, bunun üçün onlara çox sağ olun (yenə də bir keçid verəcəyəm Di Haltın saytınə qədər qarışıq olduğunu aydınlaşdırmaq üçün). Bizə yalnız periferik saatların aktivləşdirilməsi üçün cavabdeh olan registrlərə ehtiyacımız var (Periferik Saat Aktiv Reyestrlər). Əvvəlcə RCC-nin əsas ünvanını tapaq, o, "Yaddaş Kartı" nın ən başındadır:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

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

Və sonra ya cədvəldə bir şey tapmağa çalışmaq üçün linki vurun, ya da daha yaxşısı, haqqında bölmələrdən registrlərin təsvirinə keçin. registrləri aktivləşdirin. RCC_APB1ENR və RCC_APB2ENR-ni harada tapa bilərik:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Onlarda, müvafiq olaraq, SPI2, IOPB (I / O Port B) və alternativ funksiyaların (AFIO) taktını ehtiva edən bitlər.

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

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

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

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

Son kodu tapa bilərsiniz burada.

Sınaq imkanı və istəyi varsa, DM634-ü belə bağlayırıq: DAI-ni PB15-ə, DCK-ni PB13-ə, LAT-ı PB14-ə. Sürücüyü 5 voltdan qidalandırırıq, zəminləri birləşdirməyi unutmayın.

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

STM8 PWM

STM8-də PWM

Mən bu məqaləni yenicə planlaşdırarkən, məsələn, bir ayaqqabıçı çəkməsiz qalmaması üçün yalnız məlumat vərəqinin köməyi ilə tanımadığı bir çipin bəzi funksionallığını mənimsəməyə qərar verdim. STM8 bu rol üçün mükəmməl idi: birincisi, məndə STM8S103 ilə bir neçə Çin lövhəsi var idi, ikincisi, o qədər də populyar deyil və buna görə də İnternetdə oxumaq və həll tapmaq istəyi bu həllərin olmamasına əsaslanır.

Çip də var məlumat vərəqi и İstinad təlimatı RM0016, birinci pinout və qeydiyyat ünvanlarında, ikincisində - hər şey. STM8-in C-də çirkin IDE-də proqramlaşdırılması ST Vizual İnkişaf.

Saatlama və I/O

Varsayılan olaraq, STM8 2 MHz tezliyində işləyir, bu dərhal düzəldilməlidir.

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
HSI Saatı (Yüksək Daxili)
HSI saatı proqramlaşdırıla bilən bölücü (16-dən 1-ə qədər) olan daxili 8 MHz RC osilatorundan əldə edilmişdir. O, saat bölücü registrində (CLK_CKDIVR) təyin edilir.
Qeyd: 8 bölücü ilə HSI RC osilatoru işə salındıqda master saat mənbəyi kimi seçilir.

Məlumat vərəqində reyestrin ünvanını, refman-da təsviri tapırıq və reyestrin təmizlənməsinin lazım olduğunu görürük:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

PWM-i işə salacağımız və LED-ləri birləşdirəcəyimiz üçün pinouta baxaq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Çip kiçikdir, bir çox funksiya eyni sancaqlarda dayandırılıb. Kvadrat mötərizədə olan "alternativ funksionallıq"dır, "seçim baytları" ilə dəyişdirilir (seçim baytları) - Atmega qoruyucuları kimi bir şey. Onların dəyərlərini proqramlı şəkildə dəyişə bilərsiniz, lakin bu lazım deyil, çünki. Yeni funksiya yalnız yenidən başladıqdan sonra aktivləşdirilir. Bu baytları dəyişə bilən ST Visual Programmer (Visual Develop ilə yüklənmiş) istifadə etmək daha asandır. Pinout göstərir ki, ilk taymerin CH1 və CH2 çıxışları kvadrat mötərizədə gizlənir; STVP-də AFR1 və AFR0 bitlərini təyin etmək lazımdır, ikincisi də ikinci taymerin CH1 çıxışını PD4-dən PC5-ə köçürəcəkdir.

Beləliklə, 6 pin LED-ləri idarə edəcək: birinci taymer üçün PC6, PC7 və PC3, ikincisi üçün PC5, PD3 və PA3.

STM8-də I/O pinlərinin quraşdırılması STM32-dən daha sadə və daha məntiqlidir:

  • Atmega ilə tanış olan məlumat istiqaməti qeydiyyatı DDR (Məlumat İstiqaməti Qeydiyyatı): 1 = çıxış;
  • birinci nəzarət registr CR1, çıxış zamanı, təkan çəkmə rejimini (1) və ya açıq drenajı (0) təyin edir; LEDləri katodlarla çipə bağladığım üçün burada sıfırları buraxıram;
  • ikinci nəzarət registri CR2 çıxış zamanı takt sürətini təyin edir: 1 = 10 MHz

#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 qəbulu

Əvvəlcə terminləri müəyyənləşdirək:

  • PWM Tezliyi – taymerin işarələmə tezliyi;
  • Avtomatik yenidən yükləmə, AR – taymerin sayacağı avtomatik yüklənmiş dəyər (nəbz dövrü);
  • Yeniləmə hadisəsi, UEV – taymer AR-a hesablandıqda baş verən hadisə;
  • PWM vəzifə dövrü - PWM vəzifə dövrü, tez-tez "vəzifə dövrü" adlanır;
  • Dəyəri Tut/Müqayisə et – tutmaq/müqayisə etmək üçün dəyər, taymerin sayına qədər bir şey edəcək (PWM vəziyyətində, çıxış siqnalını çevirir);
  • əvvəlcədən yükləmə dəyəri - əvvəlcədən yüklənmiş dəyər. dəyəri müqayisə edin Taymeri işarələyən zaman dəyişə bilməz, əks halda PWM dövrü pozulacaq. Buna görə də, yeni ötürülən dəyərlər buferə yerləşdirilir və taymer geri sayımın sonuna çatdıqda və sıfırlandıqda çıxarılır;
  • Kənar düzülmüş и Mərkəzə düzülmüş rejimlər – sərhəddə və mərkəzdə hizalanma, atmelovskie ilə eynidir Sürətli PWM и Faza-düzgün PWM.
  • OCiREF, Çıxış Müqayisə Referans Siqnalı - istinad çıxış siqnalı, əslində, PWM rejimində müvafiq pində görünən şey.

Pinoutdan artıq aydın olduğu kimi, iki taymer PWM imkanlarına malikdir - birinci və ikinci. Hər ikisi 16 bitdir, birincisi bir çox əlavə xüsusiyyətlərə malikdir (xüsusilə, həm yuxarı, həm də aşağı saya bilər). Hər ikimiz eyni şəkildə işləməliyik, ona görə də təsadüfən içində olmayan bir şeyi istifadə etməmək üçün açıq-aydın daha kasıb ikinci ilə başlamaq qərarına gəldim. Bəzi problem ondan ibarətdir ki, istinad kitabçasındakı bütün taymerlərin PWM funksionallığının təsviri birinci taymer (17.5.7 PWM rejimi) haqqında fəsildədir, ona görə də siz hər zaman sənəddə irəli-geri tullanmaq məcburiyyətindəsiniz.

STM8-də PWM Atmega PWM ilə müqayisədə mühüm üstünlüyə malikdir:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Kənar hizalama ilə PWM
Hesabın konfiqurasiyası aşağıdan yuxarı
TIM_CR1 registrindəki DIR biti aydın olarsa, yuxarı sayma aktivdir
Misal
Nümunədə ilk PWM rejimi istifadə olunur. PWM istinad siqnalı OCiREF TIM1_CNT < TIM1_CCRi olduğu müddətdə yüksək saxlanılır. Əks təqdirdə, aşağı səviyyə tələb olunur. Əgər TIM1_CCRi registrində müqayisə ediləcək dəyər avtomatik yükləmə dəyərindən (TIM1_ARR registri) böyükdürsə, OCiREF siqnalı 1-də saxlanılır. Əgər müqayisə dəyəri 0 olarsa, OCiREF sıfırda saxlanılır....

zamanı STM8 taymer yeniləmə hadisəsi əvvəlcə yoxlayır dəyəri müqayisə edin, və yalnız bundan sonra istinad siqnalı yaradır. Atmega-da taymer əvvəlcə titrəyir, sonra isə müqayisə edir, nəticədə nə vaxt compare value == 0 çıxış bir iynədir ki, onunla hansısa bir şəkildə məşğul olmaq lazımdır (məsələn, məntiqi proqrama çevirməklə).

Beləliklə, nə etmək istəyirik: 8 bit PWM (AR == 255), aşağıdan yuxarıya doğru sayma, sərhəd boyunca düzülmə. Lampalar katodlar vasitəsilə çipə qoşulduğundan, PWM 0 (LED yandırılır) çıxmalıdır. dəyəri müqayisə edin və 1 sonra.

Bəziləri haqqında artıq oxumuşuq PWM rejimi, buna görə də bu ifadə üçün istinad kitabçasında axtarış edərək ikinci taymerin istədiyiniz reyestrini tapırıq (18.6.8 - TIMx_CCMR1):

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
110: Birinci PWM rejimi - aşağıdan yuxarıya doğru sayarkən birinci kanal TIMx_CNT < TIMx_CCR1 olduğu müddətdə aktivdir. Əks halda, birinci kanal qeyri-aktivdir. [bundan sonra sənəddə, taymer 1-dən səhv kopyala-yapışdırın] 111: İkinci PWM rejimi - aşağıdan yuxarıya doğru sayarkən birinci kanal TIMx_CNT < TIMx_CCR1-ə qədər qeyri-aktivdir. Əks halda birinci kanal aktivdir.

LED-lər katodlarla MK-ya qoşulduğundan, ikinci rejim bizə uyğun gəlir (birincisi də, lakin biz bunu hələ bilmirik).

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Bit 3 OC1PE: Əvvəlcədən yükləmə Çıxışı 1-i aktivləşdirin
0: TIMx_CCR1-də əvvəlcədən yükləmə reyestri deaktiv edilib. İstənilən vaxt TIMx_CCR1-ə yaza bilərsiniz. Yeni dəyər dərhal işləyir.
1: TIMx_CCR1-də reyestri əvvəlcədən yükləmə aktivləşdirildi. Oxu/yazma əməliyyatları əvvəlcədən yükləmə registrinə daxil olur. TIMx_CCR1-in əvvəlcədən yüklənmiş dəyəri hər yeniləmə hadisəsi zamanı kölgə registrinə yüklənir.
*Qeyd: PWM rejiminin düzgün işləməsi üçün əvvəlcədən yükləmə registrləri aktivləşdirilməlidir. Bu, tək siqnal rejimində isteğe bağlıdır (OPM biti TIMx_CR1 registrində təyin olunur).

Tamam, ikinci taymerin üç kanalı üçün lazım olan hər şeyi yandırın:

#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 iki səkkiz bitlik registrdən ibarətdir, burada hər şey sadədir:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

İkinci taymer yalnız aşağıdan yuxarıya doğru saya bilər, sərhəddə hizalama, heç bir şeyi dəyişdirmək lazım deyil. Tezlik bölgüsünü, məsələn, 256-a təyin edin. İkinci taymer üçün bölücü TIM2_PSCR registrində quraşdırılıb və ikinin gücüdür:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Nəticələri və ikinci taymerin özünü işə salmaq qalır. Birinci vəzifə registrlər tərəfindən həll edilir Çək/müqayisə et Imkan: bunlardan ikisi var, üç kanal onların üzərində asimmetrik olaraq səpələnmişdir. Burada biz də öyrənə bilərik ki, siqnalın polaritesini dəyişdirmək mümkündür, yəni. Prinsipcə, PWM Mode 1 də istifadə edilə bilər.Biz yazırıq:

#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;

Və nəhayət, TIMx_CR1 registrində taymeri işə salırıq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Müqayisə üçün faktiki dəyərləri taymerə ötürəcək AnalogWrite () sadə analoqunu yazaq. Reyestrlərin adları proqnozlaşdırılır Reyestrləri tut/müqayisə et, hər kanal üçün onlardan ikisi var: TIM8_CCRxL-də aşağı 2 bit və TIM2_CCRxH-də yüksək bit. 8 bitlik PWM-ə başladığımız üçün yalnız aşağı bitləri yazmaq kifayətdir:

#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;
}

Diqqətli oxucu, 100% doldurmağı təmin edə bilməyən bir az qüsurlu PWM-nin olduğunu görəcək (maksimum 255 dəyərində siqnal bir taymer dövrü ilə çevrilir). LED-lər üçün bu rol oynamır və diqqətli oxucu artıq onu necə düzəltməyi təxmin edir.

İkinci taymerdə PWM işləyir, birinciyə keçin.

Birinci taymer eyni registrlərdə tam olaraq eyni bitlərə malikdir (sadəcə ikinci taymerdə "ehtiyatda" qalan bitlər birincidə bütün növ qabaqcıl şeylər üçün fəal şəkildə istifadə olunur). Buna görə də verilənlər cədvəlində eyni registrlərin ünvanlarını tapmaq və kodu kopyalamaq kifayətdir. Yaxşı, tezlik bölücüsünün dəyərini dəyişdirin, çünki. ilk taymer ikinin gücünü deyil, iki registrdə dəqiq 16 bitlik dəyər əldə etmək istəyir Prescaler High и Aşağı. Biz hər şeyi edirik və ... ilk taymer işləmir. Nə məsələdir?

Problemi həll etməyin yeganə yolu taymer 1-in nəzarət registrləri haqqında bütün bölməyə baxmaqdır, burada ikinci taymerdə olmayan birini axtarırıq. olacaq 17.7.30 Fasilə reyestri (TIM1_BKR), bu kimi bir şey olduğu yerdə:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Əsas çıxışı aktivləşdirin

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Hələlik kod budur eyni yerdə.

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

STM8 Multipleks

STM8-də çoxalma

Üçüncü mini-layihə səkkiz RGB LED-i PWM rejimində ikinci taymerə qoşmaq və onların müxtəlif rənglər göstərməsini təmin etməkdir. Bu, LED-ləri çox, çox tez yandırıb-söndürsəniz, bizə elə gəlir ki, onlar daim yanır (görmə əzmliliyi, vizual qavrayışın ətaləti). Mən bir dəfə etdim arduino-da buna bənzər bir şey.

İş alqoritmi belə görünür:

  • ilk RGB LED-nin anodunu birləşdirdi;
  • katodlara lazımi siqnalları verərək onu yandırdı;
  • PWM dövrünün sonunu gözlədi;
  • ikinci RGB LED-nin anodunu birləşdirdi;
  • onu yandırın...

Yaxşı və s. Əlbəttə ki, gözəl iş üçün anod bağlantısı və LED-in "alovlanması" eyni vaxtda baş verməlidir. Yaxşı, demək olar ki. Hər halda, ikinci taymerin üç kanalında dəyərlər çıxaracaq bir kod yazmalıyıq, UEV-ə çatdıqda onları dəyişdirməli və eyni zamanda hazırda aktiv olan RGB LED-i dəyişdirməliyik.

LED kommutasiyası avtomatik olduğundan, kəsmə işləyicisinin məlumatları qəbul edəcəyi yerdən "video yaddaş" yaratmalısınız. Bu sadə massivdir:

uint8_t colors[8][3];

Müəyyən bir LED-in rəngini dəyişdirmək üçün bu massivdə lazımi dəyərləri yazmaq kifayətdir. Və dəyişən aktiv LED-in sayına cavabdeh olacaq

uint8_t cnt;

Demux

Düzgün multipleksləşdirmə üçün bizə qəribə də olsa, CD74HC238 demultipleksatoru lazımdır. Demultiplexer - operatoru aparatda həyata keçirən çip <<. Üç giriş sancağı (bit 0, 1 və 2) vasitəsilə ona üç bitlik X nömrəsini veririk və cavab olaraq çıxış nömrəsini aktivləşdirir (1<<X). Çipin qalan girişləri bütün dizaynı ölçmək üçün istifadə olunur. Bu çip yalnız mikrokontrolörün işğal edilmiş sancaqlarının sayını azaltmaq üçün deyil, həm də təhlükəsizlik üçün lazımdır - təsadüfən mümkün olandan daha çox LED yandırmamaq və MK-ni yandırmamaq üçün. Çipin qiyməti bir qəpikdir, onu həmişə evdə ilk yardım dəstində saxlamaq lazımdır.

CD74HC238 istənilən LED-in anoduna gərginlik verməkdən məsul olacaq. Tam hüquqlu multipleksdə, P-MOSFET vasitəsilə sütuna gərginlik verəcəkdir, lakin bu demoda bunu birbaşa edə bilərsiniz, çünki. uyğun olaraq 20mA çəkir mütləq maksimum reytinqlər məlumat cədvəlində. From CD74HC238 məlumat cədvəli bizə pinout və bu fırıldaqçı vərəq lazımdır:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
H = yüksək gərginlik səviyyəsi, L = aşağı gərginlik səviyyəsi, X - əhəmiyyət verməyin

E2 və E1-i yerə, E3, A0, A1 və A3-ü STM5-in PD3, PC4, PC5 və PC8 pinlərinə bağlayırıq. Yuxarıdakı cədvəl həm aşağı, həm də yüksək səviyyələri ehtiva etdiyinə görə, biz bu sancaqları itələyici sancaqlar kimi quraşdırırıq.

PWM

İkinci taymerdəki PWM əvvəlki hekayədə olduğu kimi iki fərqlə konfiqurasiya edilmişdir:

Əvvəlcə kəsilməni aktivləşdirməliyik Hadisəni yeniləyin (UEV) aktiv LED-i dəyişdirmək üçün bir funksiya çağıracaq. Bu, bitin dəyişdirilməsi ilə edilir Yeniləmə kəsilməsini aktivləşdirin danışan adı olan reyestrdə

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
Aktiv reyestrini kəsin

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

İkinci fərq, multipleksləşmənin belə bir fenomeni ilə bağlıdır xəyal qırıqlığı - diodların parazit parıltısı. Bizim vəziyyətimizdə, UEV-də fasiləyə səbəb olan taymerin işarələməyə davam etməsi və taymer çıxışlara bir şey yazmağa başlamazdan əvvəl kəsmə idarəedicisinin LED-i dəyişdirməyə vaxtı olmaması səbəbindən görünə bilər. Bununla mübarizə aparmaq üçün məntiqi tərsinə çevirməli olacaqsınız (0 = maksimum parlaqlıq, 255 = heç bir şey aktiv deyil) və həddindən artıq iş dövrü dəyərlərinə icazə verməməlisiniz. Bunlar. UEV-dən sonra LED-lərin bir PWM dövrü üçün tamamilə söndüyünə əmin olun.

Polariteyi dəyişdirin:

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

r, g və b-ni 255-ə təyin etməkdən çəkinin və istifadə edərkən onları tərsinə çevirməyi unutmayın.

Ara verir

Kəsilin mahiyyəti ondan ibarətdir ki, müəyyən şəraitdə çip əsas proqramı icra etməyi dayandırır və bəzi xarici funksiyaları çağırır. Kesintilər xarici və ya daxili təsirlərə görə baş verir, o cümlədən taymerdən.

ST Visual Develop-da ilk dəfə bir layihə yaratdığımızda, bundan başqa main.c sirli bir faylı olan bir pəncərə aldıq stm8_interrupt_vector.cavtomatik olaraq layihəyə daxil edilir. Bu faylda hər kəsilməyə funksiya əlavə olunur NonHandledInterrupt. Biz öz funksiyamızı istədiyimiz kəsməyə bağlamalıyıq.

Məlumat vərəqində kəsmə vektorlarının cədvəli var, burada bizə lazım olanları tapırıq:

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr
13 TIM2 yeniləməsi/daşması
14 TIM2 Çək/Müqayisə et

UEV-də LED-i dəyişdirməliyik, buna görə də #13 kəsilməsi lazımdır.

Buna görə, ilk növbədə, faylda stm8_interrupt_vector.c 13 nömrəli kəsmə (IRQ13) üçün cavabdeh olan funksiyanın adını standart olaraq özümüzə dəyişdirin:

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

İkincisi, bir fayl yaratmalı olacağıq main.h bu kimi məzmun:

#ifndef __MAIN_H
#define __MAIN_H

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

Və nəhayət, bu funksiyanı özünüzə yazın 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;
}

Kesintiləri aktivləşdirmək üçün qalır. Bu assembler əmri ilə edilir. rim - onu içəridə axtarmalı olacaqsınız Proqramlaşdırma təlimatı:

//enable interrupts
_asm("rim");

Digər assembler təlimatı - sim - Kesintiləri söndürür. "Video yaddaşa" yeni dəyərlər yazılarkən onlar söndürülməlidir ki, uğursuz bir anda yaranan fasilə serialı korlamasın.

Bütün kodlar - Github-da.

Məlumat vərəqlərini oxuyun 2: STM32-də SPI; STM8-də PWM, taymerlər və fasilələr

Əgər heç olmasa kiməsə bu məqalə faydalıdırsa, deməli boş yerə yazmamışam. Şərhlərə və qeydlərə şad olaram, hamısına cavab verməyə çalışacağam.

Mənbə: www.habr.com

Добавить комментарий