Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

В bagian pertama Saya mencoba memberi tahu para insinyur hobi elektronik yang tumbuh dari Arduino bagaimana dan mengapa mereka harus membaca lembar data dan dokumentasi lain untuk mikrokontroler. Teksnya ternyata besar, jadi saya berjanji untuk menunjukkan contoh praktisnya di artikel terpisah. Yah, dia menyebut dirinya jamur susu...

Hari ini saya akan menunjukkan kepada Anda cara menggunakan lembar data untuk menyelesaikan tugas yang cukup sederhana, tetapi diperlukan untuk banyak proyek, pada pengontrol STM32 (Blue Pill) dan STM8. Semua proyek demo didedikasikan untuk LED favorit saya, kami akan menyalakannya dalam jumlah besar, dan untuk itu kami harus menggunakan segala macam periferal yang menarik.

Teksnya lagi-lagi ternyata besar, jadi untuk kenyamanan saya membuat kontennya:

Pil Biru STM32: 16 LED dengan driver DM634
STM8: Menyiapkan enam pin PWM
STM8: 8 LED RGB pada tiga pin, interupsi

Penafian: Saya bukan seorang insinyur, saya tidak berpura-pura memiliki pengetahuan mendalam di bidang elektronik, artikel ini ditujukan untuk amatir seperti saya. Faktanya, saya menganggap diri saya dua tahun lalu sebagai target audiens. Jika seseorang memberi tahu saya bahwa lembar data pada chip asing tidak menakutkan untuk dibaca, saya tidak akan menghabiskan banyak waktu mencari beberapa potongan kode di Internet dan membuat kruk dengan gunting dan pita perekat.

Fokus artikel ini adalah pada lembar data, bukan proyek, sehingga kodenya mungkin tidak terlalu rapi dan sering kali sempit. Proyeknya sendiri sangat sederhana, meskipun cocok untuk pengenalan pertama dengan chip baru.

Saya berharap artikel saya akan membantu seseorang yang berada pada tahap pendalaman hobi yang sama.

STM32

16 LED dengan DM634 dan SPI

Sebuah proyek kecil menggunakan driver LED Blue Pill (STM32F103C8T6) dan DM634. Dengan menggunakan lembar data, kami akan mengetahui driver, port STM IO, dan mengkonfigurasi SPI.

DM634

Chip Taiwan dengan 16 output PWM 16-bit, dapat dihubungkan secara berantai. Model 12-bit low-end diketahui dari proyek dalam negeri paket ringan. Pada suatu waktu, ketika memilih antara DM63x dan TLC5940 yang terkenal, saya memilih DM karena beberapa alasan: 1) TLC di Aliexpress jelas palsu, tetapi yang ini tidak; 2) DM memiliki PWM otonom dengan pembangkit frekuensinya sendiri; 3) bisa dibeli dengan harga murah di Moskow, daripada menunggu parsel dari Ali. Dan, tentu saja, menarik untuk mempelajari cara mengontrol chip sendiri, dan tidak menggunakan perpustakaan yang sudah jadi. Chip sekarang sebagian besar disajikan dalam paket SSOP24, mudah disolder ke adaptor.

Karena pabrikannya adalah orang Taiwan, lembaran data chipnya ditulis dalam bahasa Inggris Cina, yang artinya akan menyenangkan. Pertama kita melihat pinout (Sambungan Pin) untuk memahami kaki mana yang harus disambungkan, dan deskripsi pinnya (Deskripsi Pin). 16 pin:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Sumber Wastafel DC (Saluran Terbuka)

Wastafel / Keluaran saluran terbuka - mengeringkan; sumber arus masuk; output terhubung ke ground dalam keadaan aktif - LED terhubung ke driver melalui katoda. Secara elektrik, tentu saja ini bukan “saluran terbuka” (saluran pembuangan terbuka), tetapi dalam lembar data sebutan untuk pin dalam mode pengurasan ini sering ditemukan.

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Resistor eksternal antara REXT dan GND untuk mengatur nilai arus keluaran

Resistor referensi dipasang antara pin REXT dan ground, yang mengontrol resistansi internal output, lihat grafik di halaman 9 lembar data. Di DM634, resistansi ini juga dapat dikontrol melalui perangkat lunak, mengatur kecerahan keseluruhan (kecerahan global); Saya tidak akan menjelaskan secara detail di artikel ini, saya hanya akan memasang resistor 2.2 - 3 kOhm di sini.

Untuk memahami cara mengontrol chip, mari kita lihat deskripsi antarmuka perangkat:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Ya, ini dia, Bahasa Inggris Mandarin dengan segala kemegahannya. Menerjemahkan ini bermasalah, Anda dapat memahaminya jika Anda mau, tetapi ada cara lain - lihat bagaimana koneksi ke TLC5940 yang secara fungsional serupa dijelaskan dalam lembar data:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
... Hanya diperlukan tiga pin untuk memasukkan data ke dalam perangkat. Meningkatnya tepi sinyal SCLK menggeser data dari pin SIN ke register internal. Setelah semua data dimuat, sinyal XLAT pendek dan tinggi mengunci data yang ditransfer secara berurutan ke dalam register internal. Register internal adalah gerbang yang dipicu oleh level sinyal XLAT. Semua data dikirimkan bit paling signifikan terlebih dahulu.

Memalangi – kait/gerendel/kunci.
Meningkat tajam – ujung depan pulsa
MSB dulu – bit maju yang paling signifikan (paling kiri).
untuk mencatat data – mengirimkan data secara berurutan (sedikit demi sedikit).

Kata memalangi sering ditemukan dalam dokumentasi chip dan diterjemahkan dengan berbagai cara, jadi demi pemahaman saya akan mengizinkannya sendiri

program pendidikan kecilDriver LED pada dasarnya adalah register geser. "Menggeser" (bergeser) dalam namanya - pergerakan data bitwise di dalam perangkat: setiap bit baru yang dimasukkan ke dalam mendorong seluruh rantai ke depan di depannya. Karena tidak ada seorang pun yang ingin mengamati kedipan LED yang kacau selama shift, proses berlangsung di register buffer yang dipisahkan dari register kerja oleh peredam (memalangi) adalah semacam ruang tunggu tempat bit-bit disusun dalam urutan yang diinginkan. Ketika semuanya sudah siap, rana terbuka dan bit mulai bekerja, menggantikan batch sebelumnya. Kata memalangi dalam dokumentasi untuk sirkuit mikro hampir selalu menyiratkan peredam seperti itu, tidak peduli kombinasi apa yang digunakan.

Jadi transfer data ke DM634 dilakukan seperti ini: atur input DAI ke nilai bit paling signifikan dari LED jauh, tarik DCK ke atas dan ke bawah; atur input DAI ke nilai bit berikutnya, tarik DCK; dan seterusnya sampai semua bit telah dikirim (masuk), setelah itu kita tarik LAT. Ini dapat dilakukan secara manual (sedikit-bang), tetapi lebih baik menggunakan antarmuka SPI yang dirancang khusus untuk ini, karena antarmuka ini disajikan di STM32 kami dalam dua salinan.

Pil Biru STM32F103

Pengantar: Pengontrol STM32 jauh lebih kompleks daripada Atmega328 daripada yang terlihat menakutkan. Selain itu, karena alasan penghematan energi, hampir semua periferal dimatikan saat start, dan frekuensi clock adalah 8 MHz dari sumber internal. Untungnya, pemrogram STM menulis kode yang membawa chip ke 72 MHz yang "dihitung", dan penulis semua IDE yang saya tahu memasukkannya ke dalam prosedur inisialisasi, jadi kita tidak perlu melakukan clock (tetapi kamu bisa jika kamu benar-benar menginginkannya). Tapi Anda harus menyalakan periferalnya.

Dokumentasi: Blue Pill dilengkapi dengan chip STM32F103C8T6 yang populer, ada dua dokumen berguna untuk itu:

Dalam lembar data kami mungkin tertarik pada:

  • Pinout – pinout chip – jika kita memutuskan untuk membuat papannya sendiri;
  • Memory Map – peta memori untuk chip tertentu. Manual Referensi memiliki peta untuk seluruh jalur, dan menyebutkan register yang tidak dimiliki oleh kami.
  • Tabel Definisi Pin – mencantumkan fungsi utama dan alternatif pin; untuk "pil biru" Anda dapat menemukan gambar yang lebih nyaman di Internet dengan daftar pin dan fungsinya. Oleh karena itu, kami segera mencari pinout Blue Pill di Google dan menyimpan gambar ini:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
NB: ada kesalahan pada gambar dari Internet, yang dicatat di komentar, terima kasih untuk itu. Gambarnya sudah diganti, tapi ini pelajaran - lebih baik mengecek informasi bukan dari lembar data.

Kami menghapus lembar data, membuka Manual Referensi, dan mulai sekarang kami hanya menggunakannya.
Prosedur: kita menangani input/output standar, mengkonfigurasi SPI, menyalakan periferal yang diperlukan.

Input output

Pada Atmega328, I/O diimplementasikan dengan sangat sederhana, itulah sebabnya banyaknya pilihan STM32 dapat membingungkan. Sekarang kita hanya memerlukan kesimpulan, tetapi kesimpulan ini pun mempunyai empat pilihan:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
saluran terbuka, tarik-dorong, tarik-dorong alternatif, saluran terbuka alternatif

"Tarik dorong" (Dorong tarik) merupakan keluaran biasa dari Arduino, pin dapat mengambil nilai HIGH atau LOW. Tapi dengan “saluran terbuka” ada kesulitan, meskipun sebenarnya semuanya sederhana di sini:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Konfigurasi keluaran / ketika port ditetapkan ke keluaran: / buffer keluaran diaktifkan: / – mode pengurasan terbuka: “0” di register keluaran mengaktifkan N-MOS, “1” di register keluaran meninggalkan port dalam mode Hi-Z ( P-MOS tidak diaktifkan ) / – mode push-pull: “0” pada register keluaran mengaktifkan N-MOS, “1” pada register keluaran mengaktifkan P-MOS.

Semua perbedaan antara saluran terbuka (saluran pembuangan terbuka) dari “dorong-tarik” (Dorong tarik) adalah bahwa pada pin pertama tidak dapat menerima status HIGH: ketika menulis satu ke register keluaran, ia masuk ke mode resistansi tinggi (impedansi tinggi, Hai-Z). Saat menulis nol, pin berperilaku sama di kedua mode, baik secara logis maupun elektrik.

Dalam mode keluaran normal, pin hanya menyiarkan isi register keluaran. Dalam "alternatif" itu dikendalikan oleh periferal yang sesuai (lihat 9.1.4):

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Jika bit port dikonfigurasi sebagai pin fungsi alternatif, register pin dinonaktifkan dan pin dihubungkan ke pin periferal.

Fungsi alternatif setiap pin dijelaskan dalam Definisi Pin Lembar data ada pada gambar yang diunduh. Ketika ditanya apa yang harus dilakukan jika pin memiliki beberapa alternatif fungsi, jawabannya diberikan melalui catatan kaki di datasheet:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Jika beberapa periferal menggunakan pin yang sama, untuk menghindari konflik antara fungsi-fungsi alternatif, hanya satu periferal yang harus digunakan pada satu waktu, dialihkan menggunakan bit pengaktif jam periferal (dalam register RCC yang sesuai).

Terakhir, pin dalam mode keluaran juga memiliki kecepatan clock. Ini adalah fitur hemat energi lainnya; dalam kasus kami, kami hanya mengaturnya ke maksimum dan melupakannya.

Jadi: kita menggunakan SPI, yang berarti dua pin (dengan data dan sinyal clock) harus menjadi "fungsi push-pull alternatif", dan satu lagi (LAT) harus menjadi "push-pull biasa". Tapi sebelum menugaskannya, mari kita bahas SPI.

SPI

Program pendidikan kecil lainnya

SPI atau Serial Peripheral Interface (antarmuka periferal serial) adalah antarmuka yang sederhana dan sangat efektif untuk menghubungkan suatu MK dengan MK lain dan dunia luar pada umumnya. Prinsip pengoperasiannya telah dijelaskan di atas, tentang driver LED Cina (dalam manual referensi, lihat bagian 25). SPI dapat beroperasi dalam mode master (“master”) dan slave (“slave”). SPI memiliki empat saluran dasar, yang tidak semuanya dapat digunakan:

  • MOSI, Output Master / Input Budak: pin ini mengirimkan data dalam mode master, dan menerima data dalam mode budak;
  • MISO, Input Master / Output Budak: sebaliknya, diterima di master, dan ditransmisikan di budak;
  • SCK, Serial Clock: mengatur frekuensi transmisi data di master atau menerima sinyal clock di slave. Pada dasarnya memukul ketukan;
  • SS, Slave Select: dengan bantuan saluran ini, budak mengetahui bahwa ada sesuatu yang diinginkan darinya. Pada STM32 disebut NSS, dimana N = negatif, yaitu pengontrol menjadi budak jika ada ground di saluran ini. Ini dikombinasikan dengan baik dengan mode Open Drain Output, tapi itu cerita lain.

Seperti yang lainnya, SPI pada STM32 kaya akan fungsionalitas, sehingga agak sulit untuk dipahami. Misalnya, ia dapat bekerja tidak hanya dengan SPI, tetapi juga dengan antarmuka I2S, dan dalam dokumentasi deskripsinya tercampur, kelebihannya harus dipotong tepat waktu. Tugas kita sangat sederhana: kita hanya perlu mengirim data hanya menggunakan MOSI dan SCK. Kita pergi ke bagian 25.3.4 (komunikasi setengah dupleks, komunikasi setengah dupleks), di mana kita menemukan 1 jam dan 1 kabel data searah (1 sinyal jam dan 1 aliran data searah):

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Dalam mode ini, aplikasi menggunakan SPI dalam mode transmisi saja atau mode terima saja. / Mode transmisi-saja mirip dengan mode dupleks: data ditransmisikan pada pin transmisi (MOSI dalam mode master atau MISO dalam mode slave), dan pin penerima (masing-masing MISO atau MOSI) dapat digunakan sebagai pin I/O biasa . Dalam hal ini, aplikasi hanya perlu mengabaikan buffer Rx (jika dibaca, tidak akan ada data yang ditransfer ke sana).

Hebat, pin MISO gratis, mari kita sambungkan sinyal LAT ke sana. Mari kita lihat Slave Select, yang pada STM32 dapat dikontrol secara terprogram, yang sangat nyaman. Kita membaca paragraf dengan nama yang sama di bagian 25.3.1 Gambaran Umum SPI:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Kontrol perangkat lunak NSS (SSM = 1) / Informasi pemilihan budak terdapat dalam bit SSI dari register SPI_CR1. Pin NSS eksternal tetap gratis untuk kebutuhan aplikasi lainnya.

Saatnya untuk menulis ke register. Saya memutuskan untuk menggunakan SPI2, cari alamat dasarnya di lembar data - di bagian 3.3 Peta Memori:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Baiklah, mari kita mulai:

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

Buka bagian 25.3.3 dengan judul yang cukup jelas “Mengonfigurasi SPI dalam Mode Master”:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

1. Atur frekuensi clock serial dengan bit BR[2:0] pada register SPI_CR1.

Register dikumpulkan di bagian manual referensi dengan nama yang sama. Pergeseran alamat (Alamat offset) untuk CR1 – 0x00, secara default semua bit dihapus (Setel ulang nilai 0x0000):

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Bit BR mengatur pembagi jam pengontrol, sehingga menentukan frekuensi pengoperasian SPI. Frekuensi STM32 kita akan menjadi 72 MHz, driver LED menurut datasheetnya beroperasi dengan frekuensi hingga 25 MHz, jadi kita perlu membaginya empat (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. Atur bit CPOL dan CPHA untuk menentukan hubungan antara transfer data dan pewaktuan jam serial (lihat diagram di halaman 240)

Karena kita membaca lembar data di sini dan tidak melihat skema, mari kita lihat lebih dekat deskripsi teks bit CPOL dan CPHA di halaman 704 (Deskripsi Umum SPI):

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Fase jam dan polaritas
Dengan menggunakan bit CPOL dan CPHA dari register SPI_CR1, Anda dapat memilih empat hubungan waktu secara terprogram. Bit CPOL (polaritas jam) mengontrol keadaan sinyal jam ketika tidak ada data yang dikirim. Bit ini mengontrol mode master dan slave. Jika CPOL direset, pin SCK rendah dalam mode istirahat. Jika bit CPOL diset, pin SCK bernilai tinggi selama mode istirahat.
Ketika bit CPHA (fase jam) diatur, strobo perangkap bit tinggi adalah tepi kedua dari sinyal SCK (turun jika CPOL bersih, naik jika CPOL diatur). Data ditangkap oleh perubahan kedua pada sinyal jam. Jika bit CPHA jelas, strobo perangkap bit tinggi adalah tepi naik dari sinyal SCK (tepi turun jika CPOL diatur, tepi naik jika CPOL dibersihkan). Data ditangkap pada perubahan pertama sinyal jam.

Setelah menyerap pengetahuan ini, kami sampai pada kesimpulan bahwa kedua bit harus tetap nol, karena Kami ingin sinyal SCK tetap rendah ketika tidak digunakan, dan data dikirimkan pada tepi pulsa yang naik (lihat Gambar. Meningkat tajam dalam lembar data DM634).

Ngomong-ngomong, di sini kita pertama kali menemukan fitur kosa kata dalam lembar data ST: di dalamnya tertulis frasa "reset bit ke nol" untuk mengatur ulang sedikitDan tidak untuk membersihkan sedikit, seperti misalnya Atmega.

3. Atur bit DFF untuk menentukan apakah blok data berformat 8-bit atau 16-bit

Saya khusus ambil DM16 634 bit agar tidak repot transmisi data PWM 12 bit seperti DM633. Masuk akal untuk menyetel DFF ke satu:

#define DFF         0x0800

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

4. Konfigurasikan bit LSBFIRST pada register SPI_CR1 untuk menentukan format blok

LSBFIRST, seperti namanya, mengkonfigurasi transmisi dengan bit paling tidak signifikan terlebih dahulu. Namun DM634 ingin menerima data mulai dari bit paling signifikan. Oleh karena itu, kami membiarkannya diatur ulang.

5. Dalam mode perangkat keras, jika input dari pin NSS diperlukan, berikan sinyal tinggi ke pin NSS selama seluruh urutan transfer byte. Dalam mode perangkat lunak NSS, atur bit SSM dan SSI di register SPI_CR1. Jika pin NSS ingin digunakan sebagai output, hanya bit SSOE yang perlu disetel.

Instal SSM dan SSI untuk melupakan mode perangkat keras NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. Bit MSTR dan SPE harus diset (tetap diset hanya jika sinyal NSS tinggi)

Sebenarnya dengan bit ini kita menetapkan SPI kita sebagai master dan menyalakannya:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI sudah dikonfigurasi, mari segera tulis fungsi yang mengirim byte ke driver. Lanjutkan membaca 25.3.3 “Mengonfigurasi SPI dalam mode master”:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Perintah transfer data
Transmisi dimulai ketika satu byte ditulis ke buffer Tx.
Byte data dimuat ke dalam register geser di paralel mode (dari bus internal) selama transmisi bit pertama, setelah itu ditransmisikan ke sekuensial Mode pin MOSI, bit pertama atau terakhir diteruskan tergantung pada pengaturan bit LSBFIRST pada register CPI_CR1. Bendera TXE disetel setelah transmisi data dari buffer Tx ke register geser, dan juga menghasilkan interupsi jika bit TXEIE di register CPI_CR1 disetel.

Saya menyoroti beberapa kata dalam terjemahan untuk menarik perhatian pada salah satu fitur implementasi SPI di pengontrol STM. Di Atmega, bendera TXE (Tx Kosong, Tx kosong dan siap menerima data) diatur hanya setelah seluruh byte dikirim keluar. Dan di sini tanda ini disetel setelah byte dimasukkan ke dalam register geser internal. Karena didorong ke sana dengan semua bit pada saat yang sama (secara paralel), dan kemudian data ditransfer secara berurutan, TXE diatur sebelum byte dikirim sepenuhnya. Ini penting karena dalam kasus driver LED kami, kami perlu menarik pin LAT setelah pengiriman Semua data, yaitu Bendera TXE saja tidak akan cukup bagi kami.

Ini berarti kita membutuhkan bendera lain. Mari kita lihat 25.3.7 - “Bendera Status”:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
<…>
Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Bendera SIBUK
Bendera BSY disetel dan dihapus oleh perangkat keras (menulisnya tidak berpengaruh). Bendera BSY menunjukkan keadaan lapisan komunikasi SPI.
Ini mengatur ulang:
ketika transfer selesai (kecuali dalam mode master jika transfer berkelanjutan)
ketika SPI dinonaktifkan
ketika terjadi kesalahan mode master (MODF=1)
Jika transfer tidak berkelanjutan, flag BSY akan dihapus di antara setiap transfer data

Oke, ini akan berguna. Mari kita cari tahu di mana letak buffer Tx. Untuk melakukannya, baca “Daftar Data SPI”:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Bit 15:0 DR[15:0] Daftar Data
Data yang diterima atau data yang akan dikirim.
Register data dibagi menjadi dua buffer - satu untuk menulis (transmit buffer) dan satu lagi untuk membaca (receive buffer). Menulis ke register data menulis ke buffer Tx, dan membaca dari register data akan mengembalikan nilai yang terdapat dalam buffer Rx.

Nah, dan register status tempat flag TXE dan BSY ditemukan:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Kami menulis:

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

Nah, karena kita perlu mengirimkan 16 kali dua byte, sesuai dengan jumlah output driver LED, kira-kira seperti ini:

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

Tapi kita belum tahu cara menarik pin LAT, jadi kita akan kembali ke I/O.

Menetapkan pin

Di STM32F1, register yang bertanggung jawab atas status pin sangatlah tidak biasa. Jelas jumlahnya lebih banyak daripada Atmega, tetapi mereka juga berbeda dari chip STM lainnya. Bagian 9.1 Gambaran Umum GPIO:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Masing-masing port I/O tujuan umum (GPIO) memiliki dua register konfigurasi 32-bit (GPIOx_CRL dan GPIOx_CRH), dua register data 32-bit (GPIOx_IDR dan GPIOx_ODR), register set/reset 32-bit (GPIOx_BSRR), register reset 16-bit (GPIOx_BRR) dan 32- register pemblokiran bit (GPIOx_LCKR).

Dua register pertama tidak biasa, dan juga cukup merepotkan, karena 16 pin port tersebar di seluruh register dalam format “empat bit per saudara”. Itu. pin nol hingga tujuh ada di CRL, dan sisanya di CRH. Pada saat yang sama, register yang tersisa berhasil berisi bit dari semua pin port - sering kali tersisa setengahnya "dicadangkan".

Untuk mempermudah, mari kita mulai dari akhir daftar.

Kami tidak memerlukan daftar pemblokiran.

Register set dan reset cukup lucu karena sebagian menduplikasi satu sama lain: Anda dapat menulis semuanya hanya di BSRR, di mana 16 bit yang lebih tinggi akan mengatur ulang pin ke nol, dan yang lebih rendah akan disetel ke 1, atau Anda juga bisa gunakan BRR, 16 bit terbawahnya hanya mereset pin. Saya suka opsi kedua. Register ini penting karena menyediakan akses atom ke pin:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Set atau Reset Atom
Tidak perlu menonaktifkan interupsi saat memprogram GPIOx_ODR pada level bit: satu atau lebih bit dapat diubah dengan operasi penulisan atom tunggal APB2. Hal ini dicapai dengan menulis "1" ke register set/reset (GPIOx_BSRR atau, untuk reset saja, GPIOx_BRR) dari bit yang perlu diubah. Bagian lainnya tidak akan berubah.

Register data mempunyai nama yang cukup jelas - IDR = Memasukkan Daftar Arah, register masukan; ODR = Keluaran Daftar Arah, register keluaran. Kami tidak membutuhkannya dalam proyek saat ini.

Dan terakhir, register kontrol. Karena kita tertarik dengan pin SPI yang kedua yaitu PB13, PB14 dan PB15, kita langsung lihat CRH :

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Dan kita melihat bahwa kita perlu menulis sesuatu dalam bit 20 hingga 31.

Kami telah mengetahui di atas apa yang kami inginkan dari pin, jadi di sini saya akan melakukannya tanpa tangkapan layar, saya hanya akan mengatakan bahwa MODE menentukan arah (input jika kedua bit disetel ke 0) dan kecepatan pin (kita memerlukan 50MHz, mis. keduanya pin ke “1”), dan CNF menyetel mode: reguler “push-pull” – 00, “alternative” – 10. Secara default, seperti yang kita lihat di atas, semua pin memiliki bit ketiga dari bawah (CNF0), itu mengaturnya ke mode masukan mengambang.

Karena saya berencana melakukan sesuatu yang lain dengan chip ini, untuk kesederhanaan saya telah mendefinisikan semua kemungkinan nilai MODE dan CNF untuk register kontrol bawah dan atas.

Entah bagaimana seperti ini

#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

Pin kami terletak di port B (alamat dasar – 0x40010C00), kode:

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

Dan karenanya, Anda dapat menulis definisi untuk LAT, yang akan digerakkan oleh register BRR dan 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 hanya karena inersia, selalu seperti itu, biarkan saja)

Sekarang semuanya baik-baik saja, tetapi tidak berhasil. Karena ini adalah STM32, mereka menghemat listrik, yang berarti Anda harus mengaktifkan pencatatan jam kerja pada periferal yang diperlukan.

Aktifkan pencatatan jam kerja

Jam tangan, juga dikenal sebagai Jam, bertanggung jawab atas pencatatan jam kerja. Dan kita sudah bisa melihat singkatan RCC. Kami mencarinya di dokumentasi: ini Reset dan Kontrol Jam.

Seperti yang disebutkan di atas, untungnya, bagian tersulit dari topik pencatatan jam kerja dilakukan untuk kami oleh orang-orang dari STM, untuk itu kami sangat berterima kasih kepada mereka (sekali lagi saya akan memberikan tautan ke Situs web Di Halt, untuk memperjelas betapa membingungkannya hal itu). Kita hanya memerlukan register yang bertanggung jawab untuk mengaktifkan pencatatan jam kerja periferal (Peripheral Clock Enable Registers). Pertama, mari kita cari alamat dasar RCC, yang terletak di awal “Peta Memori”:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

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

Dan kemudian klik tautan tempat Anda mencoba menemukan sesuatu di piring, atau, lebih baik lagi, lihat deskripsi register pengaktifan dari bagian tentang mengaktifkan register. Dimana kita akan menemukan RCC_APB1ENR dan RCC_APB2ENR:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Dan karenanya, berisi bit-bit yang mencakup pencatatan jam kerja SPI2, IOPB (I/O Port B) dan fungsi alternatif (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;

Kode terakhir dapat ditemukan di sini.

Jika ada kesempatan dan keinginan untuk menguji, maka sambungkan DM634 seperti ini: DAI ke PB15, DCK ke PB13, LAT ke PB14. Kita nyalakan drivernya dari 5 volt, jangan lupa sambungkan groundnya.

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

STM8 PWM

PWM di STM8

Ketika saya pertama kali merencanakan artikel ini, saya memutuskan, misalnya, untuk mencoba menguasai beberapa fungsi chip asing hanya dengan menggunakan lembar data, sehingga saya tidak akan mendapatkan pembuat sepatu tanpa sepatu bot. STM8 ideal untuk peran ini: pertama, saya memiliki beberapa papan berbahasa Mandarin dengan STM8S103, dan kedua, ini tidak terlalu populer, dan oleh karena itu godaan untuk membaca dan menemukan solusi di Internet terletak pada kurangnya solusi ini.

Chipnya juga punya lembaran data и panduan referensi RM0016, yang pertama berisi pinout dan alamat register, yang kedua berisi yang lainnya. STM8 diprogram dalam C dalam IDE yang buruk ST Visual Berkembang.

Pencatatan jam kerja dan I/O

Secara default, STM8 beroperasi pada frekuensi 2 MHz, hal ini harus segera diperbaiki.

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Jam HSI (Internal Kecepatan Tinggi).
Sinyal jam HSI berasal dari osilator RC 16 MHz internal dengan pembagi yang dapat diprogram (1 hingga 8). Ini diatur dalam register pembagi jam (CLK_CKDIVR).
Catatan: pada awalnya, osilator HSI RC dengan pembagi 8 dipilih sebagai sumber utama sinyal clock.

Kami menemukan alamat register di lembar data, deskripsi di refman dan melihat bahwa register perlu dibersihkan:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Karena kita akan menjalankan PWM dan menghubungkan LED, mari kita lihat pinoutnya:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Chipnya kecil, banyak fungsi ditangguhkan pada pin yang sama. Apa yang ada dalam tanda kurung siku adalah "fungsi alternatif", yang dialihkan dengan "byte opsi" (byte opsi) – sesuatu seperti sekering Atmega. Anda dapat mengubah nilainya secara terprogram, tetapi ini tidak perlu, karena Fungsionalitas baru diaktifkan hanya setelah reboot. Lebih mudah menggunakan ST Visual Programmer (diunduh dengan Visual Develop), yang dapat mengubah byte ini. Pinout menunjukkan bahwa pin CH1 dan CH2 pada pengatur waktu pertama disembunyikan dalam tanda kurung siku; perlu untuk mengatur bit AFR1 dan AFR0 di STVP, dan yang kedua juga akan mentransfer output CH1 dari timer kedua dari PD4 ke PC5.

Jadi, 6 pin akan mengontrol LED: PC6, PC7 dan PC3 untuk pengatur waktu pertama, PC5, PD3 dan PA3 untuk pengatur waktu kedua.

Menyiapkan pin I/O sendiri di STM8 lebih sederhana dan lebih logis dibandingkan di STM32:

  • familiar dari register arah data Atmega DDR (Daftar Arah Data): 1 = keluaran;
  • register kontrol pertama CR1, ketika dikeluarkan, mengatur mode dorong-tarik (1) atau saluran terbuka (0); karena saya menghubungkan LED ke chip dengan katoda, saya meninggalkan angka nol di sini;
  • register kontrol kedua CR2, ketika dikeluarkan, mengatur kecepatan clock: 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

Pengaturan PWM

Pertama, mari kita definisikan istilahnya:

  • Frekuensi PWM – frekuensi detak pengatur waktu;
  • Muat ulang otomatis, AR – nilai yang dapat dimuat secara otomatis hingga pengatur waktu akan menghitung (periode pulsa);
  • Perbarui Acara, UEV – peristiwa yang terjadi ketika penghitung waktu telah menghitung hingga AR;
  • Siklus Tugas PWM – Siklus kerja PWM, sering disebut “faktor tugas”;
  • Tangkap/Bandingkan Nilai – nilai untuk pengambilan/perbandingan, yang telah dihitung oleh pengatur waktu akan melakukan sesuatu (dalam kasus PWM, ini membalikkan sinyal keluaran);
  • Nilai Pramuat – nilai yang dimuat sebelumnya. Bandingkan nilai tidak dapat berubah saat pengatur waktu terus berjalan, jika tidak, siklus PWM akan terputus. Oleh karena itu, nilai-nilai baru yang dikirimkan ditempatkan di buffer dan ditarik keluar ketika penghitung waktu mencapai akhir hitungan mundur dan disetel ulang;
  • Sejajar dengan tepi и Mode rata tengah – kesejajaran di sepanjang perbatasan dan di tengah, sama seperti yang dilakukan Atmel PWM cepat и PWM fase-benar.
  • OCiREF, Output Bandingkan Sinyal Referensi – sinyal keluaran referensi, sebenarnya, apa yang muncul pada pin yang sesuai dalam mode PWM.

Seperti yang sudah jelas dari pinout, dua pengatur waktu memiliki kemampuan PWM – yang pertama dan yang kedua. Keduanya 16-bit, yang pertama memiliki banyak fitur tambahan (khususnya, dapat menghitung naik dan turun). Kita membutuhkan keduanya untuk bekerja sama, jadi saya memutuskan untuk memulai dengan yang kedua, yang jelas-jelas lebih buruk, agar tidak menggunakan sesuatu yang tidak ada secara tidak sengaja. Beberapa masalahnya adalah deskripsi fungsionalitas PWM semua pengatur waktu dalam manual referensi ada di bab tentang pengatur waktu pertama (Mode 17.5.7 PWM), jadi Anda harus berpindah-pindah dokumen sepanjang waktu.

PWM di STM8 memiliki keunggulan penting dibandingkan PWM di Atmega:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
PWM Sejajar Batas
Konfigurasi akun dari bawah ke atas
Penghitungan bottom-up aktif jika bit DIR di register TIM_CR1 dihapus
Contoh
Contohnya menggunakan mode PWM yang pertama. Sinyal referensi PWM OCiREF tetap tinggi selama TIM1_CNT < TIM1_CCRi. Kalau tidak, dibutuhkan level rendah. Jika nilai perbandingan pada register TIM1_CCRi lebih besar dari nilai autoload (register TIM1_ARR), maka sinyal OCiREF ditahan pada 1. Jika nilai perbandingannya 0, OCiREF ditetapkan nol....

Pengatur waktu STM8 selama acara pembaruan memeriksa terlebih dahulu membandingkan nilai, dan baru kemudian menghasilkan sinyal referensi. Pengatur waktu Atmega pertama-tama disekrup dan kemudian dibandingkan, menghasilkan compare value == 0 keluarannya adalah sebuah jarum, yang entah bagaimana harus ditangani (misalnya, dengan membalikkan logika secara terprogram).

Jadi yang ingin kami lakukan: PWM 8-bit (AR == 255), menghitung dari bawah ke atas, menyelaraskan sepanjang perbatasan. Karena bola lampu dihubungkan ke chip melalui katoda, PWM akan menghasilkan output 0 (LED menyala) hingga membandingkan nilai dan 1 setelahnya.

Kami telah membaca tentang beberapa Mode PWM, jadi kami menemukan register pengatur waktu kedua yang diperlukan dengan mencari di manual referensi untuk frasa ini (18.6.8 - TIMx_CCMR1):

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
110: Mode PWM pertama – saat menghitung dari bawah ke atas, saluran pertama aktif saat TIMx_CNT < TIMx_CCR1. Jika tidak, saluran pertama tidak aktif. [selanjutnya di dokumen ada kesalahan copy-paste dari timer 1] 111: Mode PWM kedua – saat menghitung dari bawah ke atas, saluran pertama tidak aktif sementara TIMx_CNT < TIMx_CCR1. Jika tidak, saluran pertama akan aktif.

Karena LED terhubung ke MK melalui katoda, mode kedua cocok untuk kita (yang pertama juga, tapi kita belum mengetahuinya).

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Bit 3 OC1PE: Aktifkan pramuat pin 1
0: Daftar pramuat di TIMx_CCR1 dinonaktifkan. Anda dapat menulis ke TIMx_CCR1 kapan saja. Nilai baru langsung berfungsi.
1: Daftar pramuat di TIMx_CCR1 diaktifkan. Operasi baca/tulis mengakses register pramuat. Nilai yang dimuat sebelumnya TIMx_CCR1 dimuat ke dalam register bayangan selama setiap peristiwa pembaruan.
*Catatan: Agar mode PWM berfungsi dengan baik, register pramuat harus diaktifkan. Ini tidak diperlukan dalam mode sinyal tunggal (bit OPM diatur dalam register TIMx_CR1).

Oke, mari kita nyalakan semua yang kita perlukan untuk tiga saluran pengatur waktu kedua:

#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 terdiri dari dua register delapan bit, semuanya sederhana:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Pengatur waktu kedua hanya dapat menghitung dari bawah ke atas, sejajar sepanjang batas, tidak ada yang perlu diubah. Mari kita atur pembagi frekuensi, misalnya, menjadi 256. Untuk pengatur waktu kedua, pembagi diatur dalam register TIM2_PSCR dan merupakan pangkat dua:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Yang tersisa hanyalah menyalakan kesimpulan dan pengatur waktu kedua itu sendiri. Masalah pertama diselesaikan dengan register Tangkap/Bandingkan Aktifkan: ada dua, tiga saluran yang tersebar secara asimetris. Di sini kita juga dapat mempelajari bahwa polaritas sinyal dapat diubah, mis. pada prinsipnya dimungkinkan untuk menggunakan Mode PWM 1. Kami menulis:

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

Dan terakhir, kita memulai timer di register TIMx_CR1:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Mari kita menulis analog sederhana dari AnalogWrite(), yang akan mentransfer nilai sebenarnya ke pengatur waktu untuk perbandingan. Register-register tersebut diberi nama dengan mudah ditebak Tangkap/Bandingkan register, ada dua bit untuk setiap saluran: bit 8 orde rendah di TIM2_CCRxL dan bit orde tinggi di TIM2_CCRxH. Karena kita telah membuat PWM 8-bit, cukup menulis bit paling tidak signifikan saja:

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

Pembaca yang penuh perhatian akan melihat bahwa kita memiliki PWM yang sedikit rusak, tidak mampu menghasilkan pengisian 100% (pada nilai maksimum 255, sinyal dibalik untuk satu siklus pengatur waktu). Untuk LED, hal ini tidak menjadi masalah, dan pembaca yang penuh perhatian sudah dapat menebak cara memperbaikinya.

PWM pada timer kedua berfungsi, mari kita lanjutkan ke timer pertama.

Pengatur waktu pertama memiliki bit-bit yang persis sama dalam register yang sama (hanya saja bit-bit yang tetap "dicadangkan" pada pengatur waktu kedua secara aktif digunakan pada pengatur waktu pertama untuk segala macam hal tingkat lanjut). Oleh karena itu, cukup mencari alamat register yang sama di lembar data dan menyalin kodenya. Nah, ubahlah nilai pembagi frekuensinya, karena... pengatur waktu pertama ingin menerima bukan pangkat dua, tetapi nilai 16-bit yang tepat dalam dua register Prescaler Tinggi и Rendah. Kami melakukan segalanya dan... pengatur waktu pertama tidak berfungsi. Apa masalahnya?

Masalahnya hanya dapat diselesaikan dengan melihat keseluruhan bagian tentang register kendali pengatur waktu 1, dimana kita mencari yang tidak dimiliki oleh pengatur waktu kedua. Akan ada 17.7.30 Break register (TIM1_BKR), di mana ada bagian ini:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Aktifkan keluaran utama

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Itu saja yang pasti sekarang, kodenya di sana.

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Multipleks STM8

Multipleksing pada STM8

Proyek mini ketiga adalah menghubungkan delapan LED RGB ke pengatur waktu kedua dalam mode PWM dan membuatnya menampilkan warna berbeda. Hal ini didasarkan pada konsep multiplexing LED, yaitu jika Anda menghidupkan dan mematikan LED dengan sangat, sangat cepat, kita akan merasa bahwa LED tersebut terus menyala (kegigihan visi, inersia persepsi visual). Saya pernah melakukannya sesuatu seperti ini di Arduino.

Algoritma kerjanya terlihat seperti ini:

  • menghubungkan anoda LED RGB pertama;
  • menyalakannya, mengirimkan sinyal yang diperlukan ke katoda;
  • menunggu hingga akhir siklus PWM;
  • menghubungkan anoda LED RGB kedua;
  • menyalakannya...

Yah, dll. Tentu saja, untuk pengoperasian yang indah, anoda harus dihubungkan dan LED “dinyalakan” pada saat yang bersamaan. Ya, atau hampir. Bagaimanapun, kita perlu menulis kode yang akan menampilkan nilai dalam tiga saluran pengatur waktu kedua, mengubahnya ketika UEV tercapai, dan pada saat yang sama mengubah LED RGB yang sedang aktif.

Karena peralihan LED terjadi secara otomatis, kita perlu membuat "memori video" dari mana pengendali interupsi akan menerima data. Ini adalah array sederhana:

uint8_t colors[8][3];

Untuk mengubah warna LED tertentu, cukup dengan menulis nilai yang diperlukan ke dalam array ini. Dan variabel tersebut akan bertanggung jawab atas jumlah LED yang aktif

uint8_t cnt;

Demux

Untuk multiplexing yang tepat, anehnya kita memerlukan demultiplexer CD74HC238. Demultiplexer - sebuah chip yang mengimplementasikan operator di perangkat keras <<. Melalui tiga pin masukan (bit 0, 1 dan 2) kami memasukkan nomor tiga-bit X, dan sebagai responsnya mengaktifkan nomor keluaran (1<<X). Input chip yang tersisa digunakan untuk menskalakan keseluruhan desain. Kita membutuhkan chip ini tidak hanya untuk mengurangi jumlah pin yang ditempati mikrokontroler, tetapi juga untuk keamanan - agar tidak menyalakan lebih banyak LED secara tidak sengaja dan tidak membakar MK. Chip ini berharga satu sen dan harus selalu disimpan di lemari obat rumah Anda.

CD74HC238 kami akan bertanggung jawab untuk mensuplai tegangan ke anoda LED yang diinginkan. Dalam multipleks penuh, ini akan mensuplai tegangan ke kolom melalui P-MOSFET, tetapi dalam demo ini dimungkinkan secara langsung, karena itu menarik 20 mA, menurut peringkat maksimum absolut di lembar data. Dari Lembar Data CD74HC238 kita membutuhkan pinout dan lembar contekan ini:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
H = level tegangan tinggi, L = level tegangan rendah, X – tidak peduli

Kami menghubungkan E2 dan E1 ke ground, E3, A0, A1 dan A3 ke pin PD5, PC3, PC4 dan PC5 dari STM8. Karena tabel di atas berisi level rendah dan tinggi, kami mengonfigurasi pin ini sebagai pin dorong-tarik.

PWM

PWM pada timer kedua dikonfigurasi dengan cara yang sama seperti pada cerita sebelumnya, dengan dua perbedaan:

Pertama, kita perlu mengaktifkan interupsi Perbarui Acara (UEV) yang akan memanggil fungsi yang mengaktifkan LED aktif. Hal ini dilakukan dengan mengubah bit Perbarui Interupsi Aktif dalam register dengan nama yang jelas

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
Interupsi aktifkan register

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Perbedaan kedua terkait dengan fenomena multiplexing, seperti ghosting – cahaya parasit dioda. Dalam kasus kami, ini mungkin muncul karena pengatur waktu, yang menyebabkan interupsi pada UEV, terus berdetak, dan pengendali interupsi tidak punya waktu untuk mengganti LED sebelum pengatur waktu mulai menulis sesuatu ke pin. Untuk mengatasi hal ini, Anda harus membalikkan logika (0 = kecerahan maksimum, 255 = tidak ada yang menyala) dan menghindari nilai siklus kerja yang ekstrem. Itu. pastikan bahwa setelah UEV LED padam sepenuhnya selama satu siklus PWM.

Mengubah polaritas:

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

Hindari menyetel r, g, dan b ke 255 dan ingatlah untuk membaliknya saat menggunakannya.

Menyela

Inti dari interupsi adalah bahwa dalam keadaan tertentu chip berhenti menjalankan program utama dan memanggil beberapa fungsi eksternal. Interupsi terjadi karena pengaruh eksternal atau internal, termasuk pengatur waktu.

Saat pertama kali kami membuat proyek di ST Visual Develop, sebagai tambahan main.c kami menerima jendela dengan file misterius stm8_interrupt_vector.c, secara otomatis disertakan dalam proyek. Dalam file ini, suatu fungsi ditetapkan untuk setiap interupsi NonHandledInterrupt. Kita perlu mengikat fungsi kita ke interupsi yang diinginkan.

Lembar data memiliki tabel vektor interupsi, tempat kita menemukan vektor yang kita perlukan:

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8
13 pembaruan/meluap TIM2
14 menangkap/membandingkan TIM2

Kita perlu mengubah LED di UEV, jadi kita perlu interupsi #13.

Oleh karena itu, pertama, dalam file stm8_interrupt_vector.c ubah nama default fungsi yang bertanggung jawab atas interupsi No. 13 (IRQ13) menjadi nama Anda sendiri:

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

Kedua, kita harus membuat file main.h dengan konten berikut:

#ifndef __MAIN_H
#define __MAIN_H

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

Dan terakhir, tulis fungsi ini di file Anda 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;
}

Yang tersisa hanyalah mengaktifkan interupsi. Ini dilakukan dengan menggunakan perintah assembler rim - kamu harus mencarinya Manual Pemrograman:

//enable interrupts
_asm("rim");

Perintah assembler lainnya adalah sim – mematikan interupsi. Mereka harus dimatikan ketika nilai-nilai baru sedang ditulis ke "memori video", sehingga interupsi yang terjadi pada saat yang salah tidak merusak array.

Semua kode - di GitHub.

Baca lembar data 2: SPI di STM32; PWM, timer dan interupsi pada STM8

Jika setidaknya seseorang menganggap artikel ini bermanfaat, maka saya tidak menulisnya dengan sia-sia. Saya akan dengan senang hati menerima komentar dan komentar, saya akan mencoba menjawab semuanya.

Sumber: www.habr.com

Tambah komentar