Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

В ang unang bahagi Sinubukan kong sabihin sa mga hobby electronics engineer na lumaki mula sa Arduino pants kung paano at bakit dapat nilang basahin ang mga datasheet at iba pang dokumentasyon para sa mga microcontroller. Ang teksto ay naging malaki, kaya nangako akong magpapakita ng mga praktikal na halimbawa sa isang hiwalay na artikulo. Well, tinawag niya ang kanyang sarili na isang kabute ng gatas ...

Ngayon ay ipapakita ko sa iyo kung paano gumamit ng mga datasheet upang malutas ang medyo simple, ngunit kinakailangan para sa maraming mga proyekto, mga gawain sa STM32 (Blue Pill) at STM8 controllers. Ang lahat ng mga proyekto ng demo ay nakatuon sa aking mga paboritong LED, sisindihin namin ang mga ito sa maraming dami, kung saan kakailanganin naming gumamit ng lahat ng uri ng mga kagiliw-giliw na peripheral.

Ang teksto muli ay naging napakalaki, kaya para sa kaginhawahan ay ginagawa ko ang nilalaman:

STM32 Blue Pill: 16 LED na may driver ng DM634
STM8: Pagse-set up ng anim na PWM pin
STM8: 8 RGB LED sa tatlong pin, naaabala

Disclaimer: Hindi ako engineer, hindi ako nagpapanggap na may malalim na kaalaman sa electronics, ang artikulo ay inilaan para sa mga baguhan na tulad ko. Sa katunayan, isinasaalang-alang ko ang aking sarili dalawang taon na ang nakakaraan bilang target na madla. Kung may nagsabi sa akin noon na ang mga datasheet para sa isang hindi pamilyar na chip ay hindi nakakatakot basahin, hindi ako gumugol ng maraming oras sa paghahanap ng ilang piraso ng code sa Internet at pag-imbento ng mga saklay na may gunting at adhesive tape.

Ang pokus ng artikulong ito ay sa mga datasheet, hindi mga proyekto, kaya maaaring hindi masyadong maayos at madalas na masikip ang code. Ang mga proyekto mismo ay napaka-simple, bagaman angkop para sa isang unang kakilala sa bagong chip.

Umaasa ako na ang aking artikulo ay makakatulong sa isang tao sa isang katulad na yugto ng paglulubog sa libangan.

STM32

16 LED na may DM634 at SPI

Isang maliit na proyekto gamit ang Blue Pill (STM32F103C8T6) at DM634 LED driver. Gamit ang mga datasheet, malalaman natin ang driver, mga STM IO port at i-configure ang SPI.

DM634

Ang Taiwanese chip na may 16 16-bit na PWM output, ay maaaring konektado sa mga chain. Ang low-end na 12-bit na modelo ay kilala mula sa isang domestic na proyekto Lightpack. Sa isang pagkakataon, ang pagpili sa pagitan ng DM63x at ang kilalang TLC5940, pinili ko ang DM sa ilang kadahilanan: 1) TLC sa Aliexpress ay tiyak na peke, ngunit ito ay hindi; 2) Ang DM ay may autonomous na PWM na may sariling frequency generator; 3) maaari itong mabili nang mura sa Moscow, sa halip na maghintay ng parsela mula kay Ali. At, siyempre, ito ay kagiliw-giliw na malaman kung paano kontrolin ang chip sa iyong sarili, sa halip na gumamit ng isang handa na library. Pangunahing ipinakita ngayon ang mga chip sa SSOP24 package; madali silang maghinang sa isang adaptor.

Dahil ang tagagawa ay Taiwanese, sheet ng datos ang chip ay nakasulat sa Chinese English, na nangangahulugang magiging masaya ito. Una naming tinitingnan ang pinout (Koneksyon ng Pin) upang maunawaan kung aling binti ang ikokonekta kung ano ang, at isang paglalarawan ng mga pin (Paglalarawan ng Pin). 16 na pin:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Mga Pinagmumulan ng DC Sink (Open Drain)

lababo / Open-drain na output – alisan ng tubig; pinagmumulan ng umaagos na kasalukuyang; ang output ay konektado sa lupa sa aktibong estado - ang mga LED ay konektado sa driver sa pamamagitan ng mga cathodes. Sa elektrisidad, ito ay, siyempre, hindi isang "bukas na kanal" (bukas na alisan ng tubig), ngunit sa mga datasheet ang pagtatalaga na ito para sa mga pin sa drain mode ay madalas na matatagpuan.

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Panlabas na resistors sa pagitan ng REXT at GND upang itakda ang kasalukuyang halaga ng output

Ang isang reference resistor ay naka-install sa pagitan ng REXT pin at ground, na kumokontrol sa panloob na resistensya ng mga output, tingnan ang graph sa pahina 9 ng datasheet. Sa DM634, ang paglaban na ito ay maaari ding kontrolin ng software, na nagtatakda ng pangkalahatang liwanag (pandaigdigang liwanag); Hindi na ako magdedetalye sa artikulong ito, maglalagay lang ako ng 2.2 - 3 kOhm risistor dito.

Upang maunawaan kung paano kontrolin ang chip, tingnan natin ang paglalarawan ng interface ng device:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

Oo, narito, Chinese English sa lahat ng kaluwalhatian nito. Ang pagsasalin nito ay may problema, maaari mong maunawaan ito kung nais mo, ngunit may isa pang paraan - tingnan kung paano inilarawan ang koneksyon sa functionally na katulad na TLC5940 sa datasheet:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
... Tatlong pin lang ang kailangan para magpasok ng data sa device. Inilipat ng tumataas na gilid ng signal ng SCLK ang data mula sa SIN pin patungo sa panloob na rehistro. Matapos ma-load ang lahat ng data, ang isang maikling mataas na XLAT signal ay nakakabit sa sunud-sunod na inilipat na data sa mga panloob na rehistro. Ang mga panloob na rehistro ay mga gate na na-trigger ng antas ng signal ng XLAT. Ang lahat ng data ay ipinadala ang pinaka makabuluhang bit muna.

Latch – trangka / trangka / lock.
Tumataas na gilid – nangungunang gilid ng pulso
MSB muna – pinaka makabuluhang (pinakakaliwa) bit forward.
para sa data ng orasan – magpadala ng data nang sunud-sunod (bit by bit).

Salita aldaba ay madalas na matatagpuan sa dokumentasyon para sa mga chips at isinalin sa iba't ibang paraan, kaya para sa pag-unawa ay hahayaan ko ang aking sarili

isang maliit na programang pang-edukasyonAng LED driver ay mahalagang isang shift register. "Shift" (ilipat) sa pangalan - bitwise na paggalaw ng data sa loob ng device: bawat bagong bit na itinutulak sa loob ay itinutulak ang buong chain pasulong sa harap nito. Dahil walang gustong obserbahan ang magulong pagkurap ng mga LED sa panahon ng shift, ang proseso ay nagaganap sa mga buffer register na pinaghihiwalay mula sa gumaganang mga rehistro ng isang damper (aldaba) ay isang uri ng waiting room kung saan ang mga bit ay nakaayos sa nais na pagkakasunod-sunod. Kapag handa na ang lahat, bubukas ang shutter at gagana ang mga bits, na pinapalitan ang nakaraang batch. salita aldaba sa dokumentasyon para sa microcircuits halos palaging nagpapahiwatig ng gayong damper, kahit na sa anong mga kumbinasyon ang ginagamit nito.

Kaya, ang paglipat ng data sa DM634 ay isinasagawa tulad nito: itakda ang input ng DAI sa halaga ng pinaka makabuluhang bit ng malayong LED, hilahin ang DCK pataas at pababa; itakda ang input ng DAI sa halaga ng susunod na bit, hilahin ang DCK; at iba pa hanggang ang lahat ng mga bit ay naipadala (nag-clock in), pagkatapos nito ay hinihila namin ang LAT. Maaari itong gawin nang manu-mano (bit-bang), ngunit mas mainam na gumamit ng interface ng SPI na espesyal na iniakma para dito, dahil ipinakita ito sa aming STM32 sa dalawang kopya.

Blue Pill STM32F103

Panimula: Ang mga controller ng STM32 ay mas kumplikado kaysa sa Atmega328 kaysa sa tila nakakatakot. Bukod dito, para sa mga kadahilanan ng pag-save ng enerhiya, halos lahat ng mga peripheral ay naka-off sa simula, at ang dalas ng orasan ay 8 MHz mula sa panloob na pinagmulan. Sa kabutihang palad, ang mga programmer ng STM ay nagsulat ng code na nagdadala ng chip hanggang sa "kinakalkula" na 72 MHz, at ang mga may-akda ng lahat ng mga IDE na kilala ko ay isinama ito sa pamamaraan ng pagsisimula, kaya hindi namin kailangang mag-orasan (ngunit kaya mo kung gusto mo talaga). Ngunit kakailanganin mong i-on ang mga peripheral.

Dokumentasyon: Ang Blue Pill ay nilagyan ng sikat na STM32F103C8T6 chip, mayroong dalawang kapaki-pakinabang na dokumento para dito:

Sa datasheet na maaaring interesado kami sa:

  • Pinouts - chip pinouts - kung sakaling magpasya kaming gumawa ng mga board sa aming sarili;
  • Memory Map – memory map para sa isang partikular na chip. Ang Reference Manual ay may mapa para sa buong linya, at binanggit nito ang mga rehistro na wala sa atin.
  • Talahanayan ng Mga Depinisyon ng Pin – naglilista ng mga pangunahing at alternatibong function ng mga pin; para sa "asul na tableta" maaari kang makahanap ng mas maginhawang mga larawan sa Internet na may isang listahan ng mga pin at ang kanilang mga pag-andar. Samakatuwid, agad naming i-google ang Blue Pill pinout at panatilihin ang larawang ito sa kamay:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
NB: mayroong isang error sa larawan mula sa Internet, na nabanggit sa mga komento, salamat para doon. Ang larawan ay pinalitan, ngunit ito ay isang aralin - mas mahusay na suriin ang impormasyon hindi mula sa mga datasheet.

Inalis namin ang datasheet, buksan ang Reference Manual, at mula ngayon ginagamit na lang namin ito.
Pamamaraan: nakikitungo kami sa karaniwang input/output, i-configure ang SPI, i-on ang mga kinakailangang peripheral.

Input Output

Sa Atmega328, ang I/O ay ipinatupad nang napakasimple, kaya naman ang kasaganaan ng mga opsyon sa STM32 ay maaaring nakakalito. Ngayon kailangan lang namin ng mga konklusyon, ngunit kahit na ang mga ito ay may apat na pagpipilian:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
open drain, push-pull, alternatibong push-pull, alternatibong open drain

"Hila tulak" (tulak hila) ay ang karaniwang output mula sa Arduino, maaaring kunin ng pin ang halaga alinman sa HIGH o LOW. Pero may "open drain" meron mga paghihirap, kahit na sa katunayan ang lahat ay simple dito:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Output configuration / kapag ang port ay nakatalaga sa output: / output buffer enabled: / – open drain mode: “0” sa output register ay nagbibigay-daan sa N-MOS, “1” sa output register ay umaalis sa port sa Hi-Z mode ( Ang P-MOS ay hindi isinaaktibo ) / – push-pull mode: “0” sa output register ay nag-activate ng N-MOS, “1” sa output register ay nag-activate ng P-MOS.

Lahat ng pagkakaiba sa pagitan ng open drain (bukas na alisan ng tubig) mula sa "push-pull" (tulak hila) ay na sa unang pin ay hindi maaaring tanggapin ang HIGH na estado: kapag nagsusulat ng isa sa output register, ito ay napupunta sa high resistance mode (mataas na impedance, Hi-Z). Kapag nagsusulat ng zero, ang pin ay kumikilos nang pareho sa parehong mga mode, parehong lohikal at elektrikal.

Sa normal na output mode, ang pin ay nagbo-broadcast lamang ng mga nilalaman ng output register. Sa "alternatibo" ito ay kinokontrol ng kaukulang mga peripheral (tingnan ang 9.1.4):

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Kung ang isang port bit ay na-configure bilang isang kahaliling pin ng function, ang rehistro ng pin ay hindi pinagana at ang pin ay konektado sa peripheral pin.

Ang alternatibong pag-andar ng bawat pin ay inilarawan sa Mga Kahulugan ng Pin Ang datasheet ay nasa na-download na larawan. Sa tanong kung ano ang gagawin kung ang isang pin ay may ilang mga alternatibong function, ang sagot ay ibinibigay ng isang footnote sa datasheet:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Kung maraming peripheral ang gumagamit ng parehong pin, upang maiwasan ang salungatan sa pagitan ng mga alternatibong function, isang peripheral lang ang dapat gamitin sa isang pagkakataon, na i-toggle gamit ang peripheral clock enable bit (sa naaangkop na RCC register).

Sa wakas, ang mga pin sa output mode ay mayroon ding bilis ng orasan. Ito ay isa pang tampok sa pagtitipid ng enerhiya; sa aming kaso, itinakda lang namin ito sa maximum at kalimutan ito.

Kaya: gumagamit kami ng SPI, na nangangahulugan na ang dalawang pin (na may data at may signal ng orasan) ay dapat na "alternatibong push-pull function", at isa pa (LAT) ay dapat na "regular push-pull". Ngunit bago italaga ang mga ito, harapin natin ang SPI.

SPI

Isa pang maliit na programang pang-edukasyon

Ang SPI o Serial Peripheral Interface (serial peripheral interface) ay isang simple at napakaepektibong interface para sa pagkonekta ng MK sa iba pang MK at sa labas ng mundo sa pangkalahatan. Ang prinsipyo ng operasyon nito ay inilarawan na sa itaas, kung saan ang tungkol sa Chinese LED driver (sa reference manual, tingnan ang seksyon 25). Maaaring gumana ang SPI sa master (“master”) at slave (“slave”) mode. Ang SPI ay may apat na pangunahing channel, kung saan hindi lahat ay maaaring gamitin:

  • MOSI, Master Output / Slave Input: ang pin na ito ay nagpapadala ng data sa master mode, at tumatanggap ng data sa slave mode;
  • MISO, Master Input / Slave Output: sa kabaligtaran, ito ay tumatanggap sa master, at nagpapadala sa alipin;
  • SCK, Serial Clock: nagtatakda ng dalas ng paghahatid ng data sa master o tumatanggap ng signal ng orasan sa alipin. Mahalagang pagpindot sa mga beats;
  • SS, Slave Select: sa tulong ng channel na ito, alam ng alipin na may hinahanap sa kanya. Sa STM32 ito ay tinatawag na NSS, kung saan N = negatibo, i.e. nagiging alipin ang controller kung may ground sa channel na ito. Mahusay itong pinagsama sa Open Drain Output mode, ngunit isa pang kuwento iyon.

Tulad ng lahat, ang SPI sa STM32 ay mayaman sa pag-andar, na ginagawang medyo mahirap maunawaan. Halimbawa, maaari itong gumana hindi lamang sa SPI, kundi pati na rin sa isang interface ng I2S, at sa dokumentasyon ang kanilang mga paglalarawan ay halo-halong, kinakailangan upang putulin ang labis sa isang napapanahong paraan. Ang aming gawain ay napaka-simple: kailangan lang naming magpadala ng data gamit lamang ang MOSI at SCK. Pumunta kami sa seksyon 25.3.4 (half-duplex na komunikasyon, half-duplex na komunikasyon), kung saan makikita namin 1 orasan at 1 unidirectional data wire (1 signal ng orasan at 1 unidirectional na stream ng data):

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Sa mode na ito, ang application ay gumagamit ng SPI sa alinman sa transmit-only o receive-only na mode. / Transmit-only mode ay katulad ng duplex mode: ang data ay ipinapadala sa transmit pin (MOSI sa master mode o MISO sa slave mode), at ang receive pin (MISO o MOSI ayon sa pagkakabanggit) ay maaaring gamitin bilang isang regular na I/O pin . Sa kasong ito, ang application ay kailangan lamang na huwag pansinin ang Rx buffer (kung ito ay nabasa, walang maililipat na data doon).

Mahusay, libre ang MISO pin, ikonekta natin ang LAT signal dito. Tingnan natin ang Slave Select, na sa STM32 ay makokontrol sa programmatically, na lubhang maginhawa. Nabasa namin ang talata ng parehong pangalan sa seksyon 25.3.1 Pangkalahatang Paglalarawan ng SPI:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Software control NSS (SSM = 1) / Ang impormasyon sa pagpili ng Slave ay nakapaloob sa SSI bit ng SPI_CR1 register. Ang panlabas na NSS pin ay nananatiling libre para sa iba pang mga pangangailangan sa application.

Oras na para sumulat sa mga rehistro. Nagpasya akong gumamit ng SPI2, hanapin ang base address nito sa datasheet - sa seksyon 3.3 Memory Map:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

Well, magsimula tayo:

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

Buksan ang seksyon 25.3.3 na may sariling paliwanag na pamagat na "Pag-configure ng SPI sa Master Mode":

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

1. Itakda ang serial clock frequency na may bits BR[2:0] sa SPI_CR1 register.

Ang mga rehistro ay kinokolekta sa reference manual na seksyon ng parehong pangalan. Paglipat ng address (Offset ng address) para sa CR1 – 0x00, bilang default lahat ng mga bit ay na-clear (I-reset ang halaga 0x0000):

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

Ang BR bits ay nagtatakda ng controller clock divider, kaya tinutukoy ang dalas kung saan gagana ang SPI. Ang aming STM32 frequency ay magiging 72 MHz, ang LED driver, ayon sa datasheet nito, ay gumagana nang may dalas na hanggang 25 MHz, kaya kailangan naming hatiin sa apat (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. Itakda ang CPOL at CPHA bits upang tukuyin ang kaugnayan sa pagitan ng paglilipat ng data at serial clock timing (tingnan ang diagram sa pahina 240)

Dahil nagbabasa tayo ng datasheet dito at hindi tumitingin sa schematics, tingnan natin ang paglalarawan ng teksto ng CPOL at CPHA bits sa pahina 704 (SPI General Description):

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Ang yugto ng orasan at polarity
Gamit ang CPOL at CPHA bits ng rehistro ng SPI_CR1, maaari kang pumili ng apat na relasyon sa timing sa pamamagitan ng program. Kinokontrol ng CPOL (clock polarity) bit ang estado ng signal ng orasan kapag walang data na ipinapadala. Kinokontrol ng bit na ito ang master at slave mode. Kung na-reset ang CPOL, mababa ang SCK pin sa rest mode. Kung ang CPOL bit ay nakatakda, ang SCK pin ay mataas sa panahon ng rest mode.
Kapag naka-set ang CPHA (clock phase), ang high bit trap strobe ay ang pangalawang gilid ng SCK signal (bumabagsak kung malinaw ang CPOL, tumataas kung nakatakda ang CPOL). Ang data ay nakunan ng pangalawang pagbabago sa signal ng orasan. Kung malinaw ang CPHA bit, ang high bit trap strobe ay ang tumataas na gilid ng signal ng SCK (falling edge kung nakatakda ang CPOL, rising edge kung na-clear ang CPOL). Kinukuha ang data sa unang pagbabago sa signal ng orasan.

Ang pagkakaroon ng hinihigop na kaalaman, dumating kami sa konklusyon na ang parehong mga bit ay dapat manatiling mga zero, dahil Nais naming manatiling mababa ang signal ng SCK kapag hindi ginagamit, at maipadala ang data sa tumataas na gilid ng pulso (tingnan ang Fig. Rising Edge sa datasheet ng DM634).

Sa pamamagitan ng paraan, dito kami unang nakatagpo ng isang tampok ng bokabularyo sa ST datasheets: sa kanila ang pariralang "i-reset ang bit sa zero" ay nakasulat upang i-reset ng kauntiAt hindi para medyo malinis, tulad ng, halimbawa, Atmega.

3. Itakda ang DFF bit upang matukoy kung ang data block ay 8-bit o 16-bit na format

Partikular akong kumuha ng 16-bit DM634 upang hindi mag-abala sa pagpapadala ng 12-bit na data ng PWM, tulad ng DM633. Makatuwirang itakda ang DFF sa isa:

#define DFF         0x0800

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

4. I-configure ang LSBFIRST bit sa SPI_CR1 register para matukoy ang block format

Ang LSBFIRST, gaya ng ipinahihiwatig ng pangalan nito, ay nagko-configure ng transmission na may hindi bababa sa makabuluhang bit muna. Ngunit gusto ng DM634 na makatanggap ng data simula sa pinaka makabuluhang bit. Samakatuwid, hinahayaan namin itong i-reset.

5. Sa hardware mode, kung kailangan ang input mula sa NSS pin, maglapat ng mataas na signal sa NSS pin sa buong byte transfer sequence. Sa NSS software mode, itakda ang SSM at SSI bits sa SPI_CR1 register. Kung ang NSS pin ay gagamitin bilang isang output, ang SSOE bit lamang ang kailangang itakda.

I-install ang SSM at SSI upang makalimutan ang tungkol sa NSS hardware mode:

#define SSI         0x0100
#define SSM         0x0200

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

6. Dapat itakda ang MSTR at SPE bits (nananatili lamang silang nakatakda kung mataas ang signal ng NSS)

Sa totoo lang, sa mga bit na ito ay itinalaga namin ang aming SPI bilang master at i-on ito:

#define MSTR        0x0004
#define SPE         0x0040

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

Naka-configure ang SPI, isulat agad natin ang mga function na nagpapadala ng mga byte sa driver. Magpatuloy sa pagbabasa 25.3.3 “Pag-configure ng SPI sa master mode”:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Pagkakasunud-sunod ng paglilipat ng data
Ang paghahatid ay nagsisimula kapag ang isang byte ay nakasulat sa Tx buffer.
Ang data byte ay na-load sa shift register sa parallel mode (mula sa panloob na bus) sa panahon ng paghahatid ng unang bit, pagkatapos nito ay ipinadala sa sunud-sunod MOSI pin mode, una o huling bit forward depende sa setting ng LSBFIRST bit sa CPI_CR1 register. Ang bandila ng TXE ay nakatakda pagkatapos ng paghahatid ng data mula sa Tx buffer hanggang sa shift register, at bubuo din ng interrupt kung nakatakda ang TXEIE bit sa CPI_CR1 register.

Nag-highlight ako ng ilang salita sa pagsasalin upang maakit ang pansin sa isang tampok ng pagpapatupad ng SPI sa mga controllers ng STM. Sa Atmega ang bandila ng TXE (Tx Walang laman, Tx ay walang laman at handang tumanggap ng data) ay nakatakda lamang pagkatapos maipadala ang buong byte labas. At dito nakatakda ang flag na ito pagkatapos maipasok ang byte sa internal shift register. Dahil ito ay itinulak doon kasama ang lahat ng mga bit sa parehong oras (kahanay), at pagkatapos ay ang data ay inilipat nang sunud-sunod, ang TXE ay nakatakda bago ang byte ay ganap na naipadala. Mahalaga ito dahil sa kaso ng aming LED driver, kailangan naming hilahin ang LAT pin pagkatapos ipadala lahat data, ibig sabihin. Ang bandila ng TXE lamang ay hindi sapat para sa atin.

Nangangahulugan ito na kailangan natin ng isa pang bandila. Tingnan natin ang 25.3.7 - "Mga Flag ng Katayuan":

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
<…>
Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Abala ang bandila
Ang bandila ng BSY ay nakatakda at na-clear ng hardware (walang epekto ang pagsulat dito). Ang bandila ng BSY ay nagpapahiwatig ng estado ng layer ng komunikasyon ng SPI.
Nire-reset nito:
kapag natapos na ang paglipat (maliban sa master mode kung tuloy-tuloy ang paglipat)
kapag ang SPI ay hindi pinagana
kapag nagkaroon ng master mode error (MODF=1)
Kung ang paglipat ay hindi tuloy-tuloy, ang BSY na flag ay iki-clear sa pagitan ng bawat paglilipat ng data

Okay, ito ay magiging kapaki-pakinabang. Alamin natin kung saan matatagpuan ang Tx buffer. Upang gawin ito, basahin ang “SPI Data Register”:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Bits 15:0 DR[15:0] Data Register
Data na natanggap o data na ipapadala.
Ang data register ay nahahati sa dalawang buffer - isa para sa pagsulat (transmit buffer) at isa para sa pagbabasa (receive buffer). Ang pagsusulat sa rehistro ng data ay nagsusulat sa Tx buffer, at ang pagbabasa mula sa rehistro ng data ay magbabalik ng halaga na nasa Rx buffer.

Well, at ang rehistro ng katayuan, kung saan matatagpuan ang mga flag ng TXE at BSY:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

Sumulat kami:

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

Buweno, dahil kailangan nating magpadala ng 16 na beses ng dalawang byte, ayon sa bilang ng mga output ng LED driver, tulad nito:

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

Ngunit hindi pa namin alam kung paano hilahin ang LAT pin, kaya babalik kami sa I/O.

Pagtatalaga ng mga pin

Sa STM32F1, ang mga rehistro na responsable para sa estado ng mga pin ay medyo hindi pangkaraniwan. Ito ay malinaw na mayroong higit pa sa kanila kaysa sa Atmega, ngunit sila ay naiiba din sa iba pang mga STM chips. Seksyon 9.1 Pangkalahatang Paglalarawan ng GPIO:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Ang bawat isa sa mga pangkalahatang layunin na I/O port (GPIO) ay may dalawang 32-bit configuration registers (GPIOx_CRL at GPIOx_CRH), dalawang 32-bit data registers (GPIOx_IDR at GPIOx_ODR), isang 32-bit set/reset register (GPIOx_BSRR), isang 16-bit reset register (GPIOx_BRR) at isang 32- bit blocking register (GPIOx_LCKR).

Ang unang dalawang rehistro ay hindi pangkaraniwan, at medyo hindi rin maginhawa, dahil ang 16 na port pin ay nakakalat sa mga ito sa isang "apat na bits bawat kapatid" na format. Yung. Ang mga pin na zero hanggang pito ay nasa CRL, at ang iba ay nasa CRH. Kasabay nito, ang natitirang mga rehistro ay matagumpay na naglalaman ng mga piraso ng lahat ng mga pin ng port - madalas na natitira sa kalahating "nakareserba".

Para sa pagiging simple, magsimula tayo sa dulo ng listahan.

Hindi namin kailangan ng blocking register.

Ang mga set at reset na mga rehistro ay medyo nakakatawa na bahagyang nadoble ang bawat isa: maaari mong isulat ang lahat lamang sa BSRR, kung saan ang mas mataas na 16 bits ay i-reset ang pin sa zero, at ang mga mas mababa ay itatakda sa 1, o maaari mo ring gumamit ng BRR, ang mas mababang 16 bits kung saan i-reset lamang ang pin . Gusto ko ang pangalawang opsyon. Ang mga rehistrong ito ay mahalaga dahil nagbibigay sila ng atomic na access sa mga pin:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Atomic Set o I-reset
Hindi na kailangang i-disable ang mga interrupts kapag nagprograma ng GPIOx_ODR sa bit level: ang isa o higit pang mga bit ay maaaring baguhin sa isang atomic write operation na APB2. Ito ay nakakamit sa pamamagitan ng pagsulat ng "1" sa set/reset register (GPIOx_BSRR o, para sa pag-reset lamang, GPIOx_BRR) ng bit na kailangang baguhin. Ang iba pang mga piraso ay mananatiling hindi nagbabago.

Ang mga rehistro ng data ay may medyo maliwanag na mga pangalan - IDR = input Direksyon Register, input register; ODR = Pagbubuhos Direksyon Register, output register. Hindi namin kakailanganin ang mga ito sa kasalukuyang proyekto.

At sa wakas, kontrolin ang mga rehistro. Dahil interesado kami sa pangalawang SPI pin, katulad ng PB13, PB14 at PB15, agad naming tinitingnan ang CRH:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

At nakita namin na kakailanganin naming magsulat ng isang bagay sa mga piraso mula 20 hanggang 31.

Naisip na namin sa itaas kung ano ang gusto namin mula sa mga pin, kaya dito gagawin ko nang walang screenshot, sasabihin ko lang na tinutukoy ng MODE ang direksyon (input kung ang parehong mga bit ay nakatakda sa 0) at bilis ng pin (kailangan namin ng 50MHz, i.e. parehong pin sa “1”), at itinatakda ng CNF ang mode: regular na “push-pull” – 00, “alternative” – 10. Bilang default, tulad ng nakikita natin sa itaas, ang lahat ng mga pin ay may ikatlong bit mula sa ibaba (CNF0), ito ay nagtatakda sa kanila sa mode lumulutang na input.

Dahil plano kong gumawa ng iba sa chip na ito, para sa pagiging simple ay tinukoy ko ang lahat ng posibleng mga halaga ng MODE at CNF para sa parehong lower at upper control registers.

Kahit papaano ganito

#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

Ang aming mga pin ay matatagpuan sa port B (base address – 0x40010C00), code:

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

At, nang naaayon, maaari kang sumulat ng mga kahulugan para sa LAT, na maa-twitch ng mga rehistro ng BRR at 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 by inertia lang, lagi na lang ganyan, let it stay)

Ngayon ang lahat ay mahusay, ngunit hindi ito gumagana. Dahil ito ay STM32, nakakatipid sila ng kuryente, na nangangahulugang kailangan mong paganahin ang clocking ng mga kinakailangang peripheral.

I-on ang pag-orasan

Ang relo, na kilala rin bilang Orasan, ay responsable para sa pag-orasan. At mapapansin na natin ang abbreviation na RCC. Hinahanap namin ito sa dokumentasyon: ito ay Reset at Clock Control.

Tulad ng sinabi sa itaas, sa kabutihang palad, ang pinakamahirap na bahagi ng clocking na paksa ay ginawa para sa amin ng mga tao mula sa STM, kung saan kami ay lubos na nagpapasalamat sa kanila (muli ay magbibigay ako ng isang link sa Ang website ng Di Halt, para maging malinaw kung gaano ito nakakalito). Kailangan lang namin ng mga register na responsable para sa pagpapagana ng peripheral clocking (Peripheral Clock Enable Registers). Una, hanapin natin ang base address ng RCC, ito ay sa pinakadulo simula ng "Memory Map":

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

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

At pagkatapos ay mag-click sa link kung saan sinusubukan mong maghanap ng isang bagay sa plato, o, mas mabuti, dumaan sa mga paglalarawan ng mga nagpapagana ng mga rehistro mula sa mga seksyon tungkol sa paganahin ang mga rehistro. Kung saan makikita natin ang RCC_APB1ENR at RCC_APB2ENR:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

At sila, nang naaayon, ay naglalaman ng mga bit na kasama ang clocking ng SPI2, IOPB (I/O Port B) at mga alternatibong function (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;

Ang huling code ay matatagpuan dito.

Kung mayroon kang pagkakataon at pagnanais na subukan, pagkatapos ay ikonekta ang DM634 tulad nito: DAI sa PB15, DCK sa PB13, LAT sa PB14. Pinapalakas namin ang driver mula sa 5 volts, huwag kalimutang ikonekta ang mga bakuran.

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

STM8 PWM

PWM sa STM8

Noong pinaplano ko lang ang artikulong ito, nagpasya ako, bilang isang halimbawa, na subukang makabisado ang ilang pag-andar ng isang hindi pamilyar na chip gamit lamang ang isang datasheet, upang hindi ako mapunta sa isang tagagawa ng sapatos na walang bota. Tamang-tama ang STM8 para sa tungkuling ito: una, mayroon akong ilang Chinese board na may STM8S103, at pangalawa, hindi ito masyadong sikat, at samakatuwid ang tuksong magbasa at maghanap ng solusyon sa Internet ay nakasalalay sa kakulangan ng mga mismong solusyong ito.

Ang chip ay mayroon din sheet ng datos и reference manual RM0016, sa una ay mayroong pinout at magrehistro ng mga address, sa pangalawa - lahat ng iba pa. Ang STM8 ay naka-program sa C sa isang kahila-hilakbot na IDE ST Visual Development.

Clocking at I/O

Bilang default, gumagana ang STM8 sa dalas na 2 MHz, dapat itong itama kaagad.

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
HSI (High Speed ​​​​Internal) Clock
Ang HSI clock signal ay nagmula sa isang panloob na 16 MHz RC oscillator na may programmable divider (1 hanggang 8). Ito ay nakatakda sa clock divider register (CLK_CKDIVR).
Tandaan: sa simula, ang isang HSI RC oscillator na may divider na 8 ay pinili bilang nangungunang pinagmumulan ng signal ng orasan.

Nakita namin ang address ng rehistro sa datasheet, ang paglalarawan sa refman at nakita namin na kailangang i-clear ang rehistro:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Dahil tatakbo tayo ng PWM at ikonekta ang mga LED, tingnan natin ang pinout:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

Ang chip ay maliit, maraming mga pag-andar ang nasuspinde sa parehong mga pin. Ang nasa square bracket ay "alternatibong pag-andar", ito ay inililipat ng "option bytes" (mga byte ng opsyon) – isang bagay tulad ng Atmega fuse. Maaari mong baguhin ang kanilang mga halaga sa programmatically, ngunit hindi ito kinakailangan, dahil Ang bagong pag-andar ay isinaaktibo lamang pagkatapos ng pag-reboot. Mas madaling gamitin ang ST Visual Programmer (na-download gamit ang Visual Develop), na maaaring baguhin ang mga byte na ito. Ipinapakita ng pinout na ang CH1 at CH2 pin ng unang timer ay nakatago sa mga square bracket; ito ay kinakailangan upang itakda ang AFR1 at AFR0 bits sa STVP, at ang pangalawa ay ililipat din ang CH1 output ng pangalawang timer mula PD4 sa PC5.

Kaya, 6 na pin ang magkokontrol sa mga LED: PC6, PC7 at PC3 para sa unang timer, PC5, PD3 at PA3 para sa pangalawa.

Ang pag-set up ng mga I/O pin mismo sa STM8 ay mas simple at mas lohikal kaysa sa STM32:

  • pamilyar mula sa rehistro ng direksyon ng data ng Atmega DDR (Rehistro ng Direksyon ng Data): 1 = output;
  • ang unang control register CR1, kapag output, ay nagtatakda ng push-pull mode (1) o open drain (0); dahil ikinonekta ko ang mga LED sa chip na may mga cathode, nag-iiwan ako ng mga zero dito;
  • ang pangalawang control register CR2, kapag output, ay nagtatakda ng bilis ng orasan: 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

Setting ng PWM

Una, tukuyin natin ang mga termino:

  • Dalas ng PWM – dalas ng pag-tick ng timer;
  • Auto-reload, AR – autoloadable na halaga hanggang sa kung saan ang timer ay mabibilang (pulse period);
  • I-update ang Kaganapan, UEV – isang kaganapan na nangyayari kapag ang timer ay binilang sa AR;
  • PWM Duty Cycle – PWM duty cycle, kadalasang tinatawag na “duty factor”;
  • Kunin/Ihambing ang Halaga – halaga para sa pagkuha/paghahambing, kung saan binilang ng timer may gagawin (sa kaso ng PWM, binabaligtad nito ang output signal);
  • Preload na Halaga – na-preload na halaga. Paghambingin ang halaga hindi maaaring baguhin habang ang timer ay ticking, kung hindi, ang PWM cycle ay masira. Samakatuwid, ang mga bagong nailipat na halaga ay inilalagay sa isang buffer at hinila kapag ang timer ay umabot sa dulo ng countdown nito at na-reset;
  • Naka-align sa gilid и Mga mode na nakahanay sa gitna – pagkakahanay sa kahabaan ng hangganan at sa gitna, katulad ng sa Atmel Mabilis na PWM и Phase-correct PWM.
  • OCiREF, Output Compare Reference Signal – ang reference na output signal, sa katunayan, kung ano ang lalabas sa kaukulang pin sa PWM mode.

Tulad ng malinaw na mula sa pinout, ang dalawang timer ay may mga kakayahan sa PWM - ang una at ang pangalawa. Parehong 16-bit, ang una ay may maraming karagdagang mga tampok (sa partikular, maaari itong mabilang pareho pataas at pababa). Kailangan naming pareho na magtrabaho nang pantay, kaya nagpasya akong magsimula sa malinaw na mas mahirap na pangalawa, upang hindi aksidenteng gumamit ng isang bagay na wala doon. Ang ilang problema ay ang paglalarawan ng PWM functionality ng lahat ng timer sa reference manual ay nasa kabanata tungkol sa unang timer (17.5.7 PWM Mode), kaya kailangan mong tumalon pabalik-balik sa buong dokumento sa lahat ng oras.

Ang PWM sa STM8 ay may mahalagang kalamangan sa PWM sa Atmega:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Boundary Aligned PWM
Configuration ng account mula sa ibaba hanggang sa itaas
Aktibo ang pagbilang sa ibaba kung ang DIR bit sa rehistro ng TIM_CR1 ay na-clear
Halimbawa
Ang halimbawa ay gumagamit ng unang PWM mode. Ang PWM reference signal OCiREF ay pinananatiling mataas hangga't TIM1_CNT < TIM1_CCRi. Kung hindi, ito ay tumatagal ng isang mababang antas. Kung ang paghahambing na halaga sa TIM1_CCRi register ay mas malaki kaysa sa autoload value (TIM1_ARR register), ang OCiREF signal ay gaganapin sa 1. Kung ang halaga ng paghahambing ay 0, ang OCiREF ay gaganapin sa zero....

STM8 timer habang i-update ang kaganapan suriin muna ihambing ang halaga, at pagkatapos lamang ay gumagawa ng isang reference signal. Ang timer ng Atmega ay unang umiikot at pagkatapos ay nagkukumpara, na nagreresulta compare value == 0 ang output ay isang karayom, na dapat harapin sa anumang paraan (halimbawa, sa pamamagitan ng programmatically inverting ang logic).

Kaya kung ano ang gusto naming gawin: 8-bit PWM (AR == 255), pagbibilang mula sa ibaba hanggang sa itaas, pagkakahanay sa kahabaan ng hangganan. Dahil ang mga bombilya ay konektado sa chip sa pamamagitan ng mga cathode, ang PWM ay dapat mag-output ng 0 (LED on) hanggang ihambing ang halaga at 1 pagkatapos.

Nabasa na natin ang tungkol sa ilan PWM mode, kaya nahanap namin ang kinakailangang rehistro ng pangalawang timer sa pamamagitan ng paghahanap sa reference manual para sa pariralang ito (18.6.8 - TIMx_CCMR1):

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
110: Unang PWM mode – kapag nagbibilang mula sa ibaba hanggang sa itaas, ang unang channel ay aktibo habang TIMx_CNT < TIMx_CCR1. Kung hindi, hindi aktibo ang unang channel. [Karagdagang sa dokumento ay mayroong maling copy-paste mula sa timer 1] 111: Pangalawang PWM mode – kapag binibilang mula ibaba hanggang itaas, ang unang channel ay hindi aktibo habang TIMx_CNT < TIMx_CCR1. Kung hindi, aktibo ang unang channel.

Dahil ang mga LED ay konektado sa MK sa pamamagitan ng mga cathodes, ang pangalawang mode ay nababagay sa amin (ang una rin, ngunit hindi pa namin alam iyon).

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Bit 3 OC1PE: I-enable ang pin 1 preload
0: Ang pagpaparehistro ng preload sa TIMx_CCR1 ay hindi pinagana. Maaari kang sumulat sa TIMx_CCR1 anumang oras. Gumagana kaagad ang bagong halaga.
1: Ang pagpaparehistro ng preload sa TIMx_CCR1 ay pinagana. I-access ang mga pagpapatakbo ng pagbabasa/pagsusulat sa preload na rehistro. Ang preloaded value na TIMx_CCR1 ay nilo-load sa shadow register sa bawat kaganapan sa pag-update.
*Tandaan: Para gumana nang maayos ang PWM mode, dapat na naka-enable ang mga preload register. Ito ay hindi kinakailangan sa single signal mode (ang OPM bit ay nakatakda sa TIMx_CR1 register).

Okay, i-on natin ang lahat ng kailangan natin para sa tatlong channel ng pangalawang timer:

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

Binubuo ang AR ng dalawang eight-bit registers, ang lahat ay simple:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Ang pangalawang timer ay maaari lamang mabilang mula sa ibaba hanggang sa itaas, pagkakahanay sa kahabaan ng hangganan, walang kailangang baguhin. Itakda natin ang frequency divider, halimbawa, sa 256. Para sa pangalawang timer, ang divider ay nakatakda sa TIM2_PSCR register at isang kapangyarihan ng dalawa:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Ang natitira na lang ay i-on ang mga konklusyon at ang pangalawang timer mismo. Ang unang problema ay nalutas sa pamamagitan ng mga rehistro Kunin/Ihambing Paganahin: mayroong dalawa, tatlong channel na nakakalat sa mga ito nang walang simetrya. Dito rin natin matututunan na posibleng baguhin ang polarity ng signal, i.e. sa prinsipyo, posible na gumamit ng PWM Mode 1. Sumulat kami:

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

At sa wakas, sinisimulan namin ang timer sa rehistro ng TIMx_CR1:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Sumulat tayo ng isang simpleng analogue ng AnalogWrite(), na maglilipat ng aktwal na mga halaga sa timer para sa paghahambing. Ang mga rehistro ay pinangalanang predictably Kunin/Ihambing ang mga rehistro, mayroong dalawa sa mga ito para sa bawat channel: ang low-order na 8 bits sa TIM2_CCRxL at ang mga high-order sa TIM2_CCRxH. Dahil nakagawa kami ng 8-bit na PWM, sapat na na isulat lamang ang hindi bababa sa makabuluhang mga piraso:

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

Mapapansin ng matulungin na mambabasa na mayroon kaming bahagyang may sira na PWM, hindi makagawa ng 100% na punan (sa maximum na halaga na 255, ang signal ay baligtad para sa isang ikot ng timer). Para sa mga LED hindi ito mahalaga, at ang matulungin na mambabasa ay maaari nang hulaan kung paano ayusin ito.

Gumagana ang PWM sa pangalawang timer, lumipat tayo sa una.

Ang unang timer ay may eksaktong parehong mga bit sa parehong mga rehistro (ito ay ang mga bit na nanatiling "nakareserba" sa pangalawang timer ay aktibong ginagamit sa una para sa lahat ng uri ng mga advanced na bagay). Samakatuwid, sapat na upang mahanap ang mga address ng parehong mga rehistro sa datasheet at kopyahin ang code. Well, baguhin ang halaga ng frequency divider, dahil... ang unang timer ay nais na makatanggap ng hindi isang kapangyarihan ng dalawa, ngunit isang eksaktong 16-bit na halaga sa dalawang rehistro Prescaler High и Mababa. Ginagawa namin ang lahat at... hindi gumagana ang first timer. Anong problema?

Ang problema ay malulutas lamang sa pamamagitan ng pagtingin sa buong seksyon tungkol sa mga control register ng timer 1, kung saan hinahanap namin ang isa na wala sa pangalawang timer. Magkakaroon 17.7.30 Rehistro ng break (TIM1_BKR), kung saan mayroong ganitong bit:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Paganahin ang pangunahing output

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Iyon lang ang sigurado ngayon, ang code doon.

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

STM8 Multiplex

Multiplexing sa STM8

Ang ikatlong mini-proyekto ay upang ikonekta ang walong RGB LED sa pangalawang timer sa PWM mode at ipakita ang mga ito ng iba't ibang kulay. Ito ay batay sa konsepto ng LED multiplexing, na kung i-on at i-off mo ang mga LED nang napakabilis, tila sa amin ay patuloy silang naka-on (pagtitiyaga ng pangitain, pagkawalang-kilos ng visual na perception). minsan ko na ginawa isang bagay na tulad nito sa Arduino.

Ang algorithm ng trabaho ay ganito ang hitsura:

  • ikinonekta ang anode ng unang RGB LED;
  • sinindihan ito, nagpapadala ng mga kinakailangang signal sa mga cathode;
  • naghintay hanggang sa katapusan ng PWM cycle;
  • ikinonekta ang anode ng pangalawang RGB LED;
  • sinindihan ito...

Well, atbp. Siyempre, para sa magandang operasyon kinakailangan na ang anode ay konektado at ang LED ay "nag-apoy" sa parehong oras. Well, o halos. Sa anumang kaso, kailangan naming magsulat ng isang code na maglalabas ng mga halaga sa tatlong mga channel ng pangalawang timer, baguhin ang mga ito kapag naabot ang UEV, at sabay na baguhin ang kasalukuyang aktibong RGB LED.

Dahil awtomatiko ang LED switching, kailangan nating lumikha ng "video memory" kung saan makakatanggap ng data ang interrupt handler. Ito ay isang simpleng array:

uint8_t colors[8][3];

Upang mabago ang kulay ng isang partikular na LED, sapat na upang isulat ang mga kinakailangang halaga sa array na ito. At ang variable ay magiging responsable para sa bilang ng aktibong LED

uint8_t cnt;

Demux

Para sa wastong multiplexing, kailangan namin, kakaiba, isang CD74HC238 demultiplexer. Demultiplexer - isang chip na nagpapatupad ng operator sa hardware <<. Sa pamamagitan ng tatlong input pin (bits 0, 1 at 2) pinapakain namin ito ng tatlong-bit na numero X, at bilang tugon ay pinapagana nito ang numero ng output (1<<X). Ang natitirang mga input ng chip ay ginagamit upang sukatin ang buong disenyo. Kailangan namin ang chip na ito hindi lamang upang bawasan ang bilang ng mga inookupahang pin ng microcontroller, kundi pati na rin para sa kaligtasan - upang hindi aksidenteng i-on ang higit pang mga LED kaysa sa posible at hindi masunog ang MK. Ang chip ay nagkakahalaga ng isang sentimos at dapat palaging itago sa iyong home medicine cabinet.

Ang aming CD74HC238 ay magiging responsable para sa pagbibigay ng boltahe sa anode ng nais na LED. Sa isang ganap na multiplex, ito ay magbibigay ng boltahe sa column sa pamamagitan ng isang P-MOSFET, ngunit sa demo na ito ito ay posible nang direkta, dahil ito ay kumukuha ng 20 mA, ayon sa ganap na pinakamataas na rating sa datasheet. Mula sa Datasheet CD74HC238 kailangan namin ng mga pinout at ang cheat sheet na ito:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
H = mataas na antas ng boltahe, L = mababang antas ng boltahe, X – walang pakialam

Ikinonekta namin ang E2 at E1 sa ground, E3, A0, A1 at A3 sa mga pin na PD5, PC3, PC4 at PC5 ng STM8. Dahil ang talahanayan sa itaas ay naglalaman ng parehong mababa at mataas na antas, kino-configure namin ang mga pin na ito bilang mga push-pull pin.

PWM

Ang PWM sa pangalawang timer ay na-configure sa parehong paraan tulad ng sa nakaraang kuwento, na may dalawang pagkakaiba:

Una, kailangan nating paganahin ang pagkagambala sa I-update ang Kaganapan (UEV) na tatawag sa isang function na magpapalipat-lipat sa aktibong LED. Ginagawa ito sa pamamagitan ng pagbabago ng bit I-update ang Interrupt Enable sa isang rehistro na may nagsasabi na pangalan

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
Makagambala paganahin ang pagpaparehistro

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Ang pangalawang pagkakaiba ay nauugnay sa hindi pangkaraniwang bagay ng multiplexing, tulad ng ghosting – parasitic glow ng diodes. Sa aming kaso, maaaring lumitaw ito dahil sa ang katunayan na ang timer, na nagdulot ng pagkagambala sa UEV, ay patuloy na tumitik, at ang interrupt handler ay walang oras upang ilipat ang LED bago magsimulang magsulat ng isang bagay ang timer sa mga pin. Upang labanan ito, kakailanganin mong baligtarin ang lohika (0 = maximum na liwanag, 255 = walang naiilawan) at iwasan ang matinding mga halaga ng duty cycle. Yung. siguraduhin na pagkatapos ng UEV ang mga LED ay ganap na lumabas para sa isang PWM cycle.

Pagbabago ng polarity:

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

Iwasang itakda ang r, g at b sa 255 at tandaan na baligtarin ang mga ito kapag ginagamit ang mga ito.

Nakakaabala

Ang kakanyahan ng isang interrupt ay na sa ilalim ng ilang mga pangyayari ang chip ay huminto sa pagpapatupad ng pangunahing programa at tumatawag ng ilang panlabas na function. Nangyayari ang mga pagkaantala dahil sa panlabas o panloob na mga impluwensya, kabilang ang timer.

Noong una kaming gumawa ng proyekto sa ST Visual Develop, bilang karagdagan sa main.c nakatanggap kami ng isang window na may misteryosong file stm8_interrupt_vector.c, awtomatikong kasama sa proyekto. Sa file na ito, may itinalagang function sa bawat interrupt NonHandledInterrupt. Kailangan nating itali ang ating function sa nais na interrupt.

Ang datasheet ay may talahanayan ng mga naka-interrupt na vector, kung saan makikita namin ang mga kailangan namin:

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8
13 TIM2 update/overflow
14 TIM2 capture/compare

Kailangan nating palitan ang LED sa UEV, kaya kailangan natin ng interrupt #13.

Alinsunod dito, una, sa file stm8_interrupt_vector.c baguhin ang default na pangalan ng function na responsable para sa interrupt No. 13 (IRQ13) sa iyong sarili:

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

Pangalawa, kailangan nating lumikha ng isang file main.h na may sumusunod na nilalaman:

#ifndef __MAIN_H
#define __MAIN_H

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

At sa wakas, isulat ang function na ito sa iyong 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;
}

Ang natitira na lang ay paganahin ang mga pagkagambala. Ginagawa ito gamit ang utos ng assembler rim - kailangan mong hanapin ito Manwal sa Programming:

//enable interrupts
_asm("rim");

Ang isa pang utos ng assembler ay sim – pinapatay ang mga interrupt. Dapat na i-off ang mga ito habang isinusulat ang mga bagong value sa "memorya ng video", upang ang isang interrupt na dulot sa maling sandali ay hindi masira ang array.

Lahat ng code - sa GitHub.

Basahin ang mga datasheet 2: SPI sa STM32; PWM, timers at interrupts sa STM8

Kung hindi bababa sa isang tao ang nakahanap ng kapaki-pakinabang na artikulong ito, kung gayon hindi ko ito isinulat nang walang kabuluhan. Magiging masaya akong makatanggap ng mga komento at komento, susubukan kong sagutin ang lahat.

Pinagmulan: www.habr.com

Magdagdag ng komento