
Pada bagian pertama, saya mencoba memberi tahu penghobi elektronik yang tumbuh dari celana Arduino bagaimana dan mengapa mereka harus membaca lembar data dan dokumentasi lain untuk mikrokontroler. Teksnya ternyata besar, jadi saya berjanji untuk menunjukkan contoh-contoh praktis dalam artikel terpisah. Yah, dia menyebut dirinya kargo ...
Hari ini saya akan menunjukkan cara menggunakan lembar data untuk menyelesaikan tugas yang agak 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 semua jenis periferal yang menarik.
Teks berubah menjadi besar lagi, jadi untuk kenyamanan saya melakukan konten:
STM32 Blue Pill: 16 LED dengan Driver DM634
STM8: Mengkonfigurasi Enam Pin PWM
STM8: 8 RGB LED pada tiga pin, menyela
Penafian: Saya bukan seorang insinyur, saya tidak berpura-pura memiliki pengetahuan yang mendalam di bidang elektronik, artikel ini ditujukan untuk pecinta seperti saya. Bahkan, sebagai target audiens, saya menganggap diri saya dua tahun lalu. Jika seseorang mengatakan kepada saya maka itu tidak menakutkan untuk membaca lembar data pada chip yang tidak dikenal, saya tidak akan menghabiskan banyak waktu mencari beberapa potongan kode di Internet dan menemukan kruk dengan gunting dan bantuan band.
Di tengah-tengah artikel ini adalah lembar data, bukan proyek, sehingga kode mungkin tidak terlalu disisir dan sering dikruk. Proyek-proyek itu sendiri sangat sederhana, meskipun cocok untuk kenalan pertama dengan chip baru.
Saya berharap artikel saya akan membantu seseorang pada tahap yang sama dalam hobi menyelam.
STM32
16 LED dengan DM634 dan SPI
Proyek kecil menggunakan Blue Pill (STM32F103C8T6) dan driver LED DM634. Dengan bantuan lembar data kita akan berurusan dengan driver, IO-ports STM dan mengkonfigurasi SPI.
DM634
Chip Taiwan dengan 16 output PWM 16-bit, dapat dihubungkan secara berantai. Model 12-bit termuda dikenal untuk proyek domestik Lightpack . Pada suatu waktu, memilih antara DM63x dan TLC5940 yang terkenal, saya berhenti di DM karena beberapa alasan: 1) TLC pada Aliexpress benar-benar palsu, tetapi yang ini tidak; 2) DM memiliki PWM otonom dengan generator frekuensinya sendiri; 3) itu bisa dibeli dengan murah di Moskow, dan tidak menunggu paket dengan Ali. Yah, tentu saja, itu menarik untuk belajar bagaimana mengelola chip sendiri, dan tidak menggunakan perpustakaan yang sudah jadi. Chip sekarang terutama disajikan dalam paket SSOP24, mereka mudah disolder ke adaptor.
Karena pabrikannya adalah orang Taiwan, lembar data untuk chip ditulis dalam Bahasa Inggris China, yang artinya akan menyenangkan. Pertama, lihat koneksi pin ( Koneksi Pin ) untuk memahami kaki mana yang terhubung dan deskripsi pin ( Deskripsi Pin ). 16 kesimpulan:

Sumber arus searah inflowing (drain terbuka)
Sink / Output saluran terbuka - tiriskan; sumber arus masuk; output aktif terhubung ke ground - LED terhubung ke driver oleh katoda. Secara elektrik, ini, tentu saja, bukan "saluran terbuka", tetapi dalam lembar data penunjukan ini untuk keluaran dalam mode saluran adalah umum.

Resistor eksternal antara REXT dan GND untuk mengatur nilai arus keluaran
Sebuah resistor referensi dipasang di antara pin REXT dan ground, yang memantau resistansi internal output, lihat grafik di halaman 9 dari lembar data. Dalam DM634, hambatan ini juga dapat dikontrol secara terprogram dengan mengatur kecerahan global ; Saya tidak akan masuk ke rincian dalam artikel ini, saya hanya meletakkan 2.2 - 3 kOhm resistor di sini.
Untuk memahami cara mengontrol chip, lihat deskripsi antarmuka perangkat:

Ya, ini dia, Bahasa Inggris Tiongkok dengan segala kemuliaan. Terjadi masalah saat menerjemahkan, Anda bisa memahaminya jika mau, tetapi ada cara lain - untuk melihat bagaimana koneksi dijelaskan dalam lembar data ke TLC5940 yang secara fungsional ditutup:

... Hanya tiga pin yang diperlukan untuk memasukkan data ke perangkat. Tepi depan sinyal SCLK menggeser data dari pin SIN ke register internal. Setelah semua data diunduh, sinyal XLAT pendek tinggi menangkap data serial dalam register internal. Register internal - katup gerbang yang dipicu XLAT. Semua data ditransmisikan dalam bit ke depan yang paling signifikan.
Kait - kait / kait / penjepit.
Rising edge - edge terdepan dari impuls
MSB pertama - bit paling signifikan (paling kiri) ke depan.
to clock data - mengirimkan data secara berurutan (bitwise).
Kata kait sering ditemukan dalam dokumentasi untuk chip dan diterjemahkan dengan berbagai cara, jadi saya akan membiarkan diri saya mengerti
program pendidikan kecilDriver LED pada dasarnya adalah register geser. "Shift" dalam namanya adalah gerakan bitwise data di dalam perangkat: setiap bit baru yang didorong ke dalam mendorong seluruh rantai di depannya. Karena tidak ada yang ingin mengamati flashing kacau LED selama shift, proses berlangsung di register buffer dipisahkan dari yang bekerja dengan kait - ini adalah semacam ruang tunggu di mana bit diatur dalam urutan yang diinginkan. Ketika semuanya sudah siap, rana terbuka, dan bit dikirim untuk bekerja, menggantikan batch sebelumnya. Kata latch dalam dokumentasi untuk sirkuit mikro hampir selalu menyiratkan peredam seperti itu, tidak peduli di mana kombinasi itu digunakan.
Jadi, transfer data ke DM634 adalah sebagai berikut: atur input DAI ke bit orde tinggi dari LED jauh, tarik DCK ke atas dan ke bawah; atur input DAI ke bit berikutnya, tarik DCK; dan seterusnya, sampai semua bit ditransmisikan ( clocked in ), setelah itu kita tarik LAT. Ini dapat dilakukan secara manual ( bit-bang ), tetapi lebih baik menggunakan antarmuka SPI yang dipertajam khusus untuk ini, karena disajikan pada STM32 kami dalam dua salinan.
Tablet Biru STM32F103
Pendahuluan: Pengontrol STM32 jauh lebih rumit daripada Atmega328 daripada yang bisa mereka takuti. Pada saat yang sama, untuk alasan penghematan energi, hampir semua periferal dinonaktifkan pada awalnya, 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 kenal memasukkannya ke dalam prosedur inisialisasi, jadi kami tidak perlu clock (tapi Anda bisa, jika Anda benar-benar mau ). Tetapi Anda harus menghidupkan periferal.
Dokumentasi: Chip STM32F103C8T6 yang populer dipasang pada Blue Pill, ada dua dokumen yang berguna untuknya:
Dalam lembar data kita mungkin tertarik pada:
- Pinout - pinouts of chips - jika kita memutuskan untuk membuat papan sendiri;
- Memory Map - kartu memori untuk chip tertentu. Dalam Manual Referensi ada kartu untuk seluruh baris, itu daftar register yang tidak ada pada kita.
- Table Pin Definition - daftar fungsi utama dan alternatif pin; untuk "pil biru" di Internet Anda dapat menemukan gambar yang lebih nyaman dengan daftar pin dan fungsinya. Karena itu, segera google pinout Pil Biru dan simpan gambar ini di tangan:

NB: dalam gambar dari Internet ada kesalahan yang diperhatikan dalam komentar, terima kasih. Gambar telah diganti, tapi ini pelajaran - lebih baik memeriksa informasi dari lembar data.
Kami menghapus lembar data, buka Manual Referensi, sekarang kami hanya menggunakannya.
Prosedur: kita berurusan dengan input / output standar, konfigurasi SPI, nyalakan periferal yang diinginkan.
Input-output
Atmega328 I / O sangat sederhana, itulah sebabnya banyaknya pilihan STM32 bisa membingungkan. Sekarang kita hanya perlu kesimpulan, tetapi bahkan ada empat opsi:

open drain output, output push-pull, output push-pull alternatif, alternatif open drain
" Push-pull " ( push-pull ) - kesimpulan biasa dari Arduina, pin bisa TINGGI atau RENDAH. Tetapi dengan "saluran terbuka" ada kesulitan , meskipun sebenarnya semuanya sederhana di sini:


Konfigurasi output / ketika port ditugaskan ke output: / buffer output aktif: / - mode drain terbuka: "0" mengaktifkan N-MOS di register output, "1" meninggalkan port dalam mode Hi-Z di register output (P-MOS tidak diaktifkan ) / - Mode "tarik-dorong": "0" pada register output mengaktifkan N-MOS, "1" pada register output mengaktifkan P-MOS.
Seluruh perbedaan antara drain terbuka dan push-pull adalah bahwa pada pin pertama tidak dapat menerima status TINGGI: ketika menulis unit ke register output, ia beralih ke impedansi tinggi , Hai -Z ). Saat merekam nol, pin di kedua mode berperilaku sama, baik secara logis maupun elektrik.
Dalam mode output normal, pin hanya menerjemahkan isi register output. Dalam "alternatif" itu dikendalikan oleh pinggiran yang sesuai (lihat 9.1.4):

Jika bit port dikonfigurasikan sebagai output fungsi alternatif, register output dinonaktifkan dan pin terhubung ke sinyal output periferal
Fungsi alternatif untuk setiap pin dijelaskan dalam lembar data Pin Definition dan ada pada gambar yang diunduh. Ketika ditanya apa yang harus dilakukan jika pin memiliki beberapa fungsi alternatif, jawabannya memberikan catatan kaki di datasheet:

Jika beberapa blok periferal menggunakan pin yang sama, untuk menghindari konflik antara fungsi-fungsi alternatif, hanya satu blok periferal yang harus digunakan pada saat yang sama, beralih menggunakan bit aktivasi jam periferal (dalam register RCC yang sesuai).
Akhirnya, pin dalam mode keluaran juga memiliki kecepatan clock. Ini adalah fitur hemat energi lain, dalam kasus kami, kami cukup mengaturnya secara maksimal dan melupakannya.
Jadi: kami menggunakan SPI, jadi dua pin (dengan data dan sinyal clock) harus menjadi "fungsi push-push alternatif", dan lainnya (LAT) harus "normal-push-push". Tetapi sebelum kita menugaskan mereka, kita akan berurusan dengan SPI.
SPI
Program pendidikan kecil lainnyaSPI atau Serial Peripherial Interface (antarmuka periferal serial) - antarmuka sederhana dan sangat efektif untuk komunikasi MK dengan MK lain dan dunia luar pada umumnya. Prinsip operasinya telah dijelaskan di atas, di mana 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 mungkin terlibat:
- MOSI, Master Output / Slave Input: pin ini dalam mode master mengirim, tetapi dalam mode slave menerima data;
- MISO, Input Master / Output Slave: sebaliknya, di master menerima, di slave - beri;
- SCK, Serial Clock: mengatur frekuensi transfer data dalam master atau menerima sinyal clock dalam slave. Pada dasarnya mengalahkan bit;
- SS, Slave Select: melalui saluran ini, budak mengetahui bahwa mereka menginginkan sesuatu darinya. Pada STM32, ini disebut NSS, di mana N = negatif, mis. controller menjadi budak jika ada ground di saluran ini. Dikombinasikan dengan baik dengan mode Open Drain Output, tapi itu cerita yang berbeda.
Seperti yang lainnya, SPI pada STM32 kaya akan fungsionalitas, yang membuatnya agak sulit untuk dipahami. Sebagai contoh, ini dapat bekerja tidak hanya dengan SPI, tetapi juga dengan antarmuka I2S, dan dalam dokumentasi deskripsi mereka tercampur, Anda perlu memotong kelebihannya secara tepat waktu. Tugas kami sangat sederhana: Anda hanya perlu mengirim data hanya menggunakan MOSI dan SCK. Kita pergi ke bagian 25.3.4 (komunikasi setengah dupleks), di mana kita menemukan 1 jam dan 1 kawat data searah (1 sinyal jam dan 1 aliran data searah):

Dalam mode ini, aplikasi menggunakan SPI dalam mode hanya-kirim atau hanya-terima. / Mode hanya transmisi 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 input-output reguler. Dalam hal ini, cukup bagi aplikasi untuk mengabaikan buffer Rx (jika Anda membacanya, tidak akan ada data yang dikirimkan).
Nah, pin MISO bebas dari kita, mari sambungkan sinyal LAT ke sana. Kami akan berurusan dengan Slave Select, yang dapat dikontrol secara terprogram pada STM32, yang sangat nyaman. Kita membaca paragraf dengan nama yang sama di bagian 25.3.1 dari Uraian Umum SPI:

Kontrol Program NSS (SSM = 1) / Informasi tentang pemilihan budak terkandung dalam bit SSI dari register SPI_CR1. Pin NSS eksternal tetap gratis untuk kebutuhan aplikasi lain.
Saatnya menulis di register. Saya memutuskan untuk menggunakan SPI2, kami mencari alamat dasar di datasheet - di bagian 3.3 Memory Map:

Baiklah, kita mulai:
#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))
Kami membuka bagian 25.3.3 dengan mengatakan "Mengonfigurasi SPI dalam mode master":

1. Atur kecepatan jam seri dengan bit BR [2: 0] dalam register SPI_CR1.
Register dikumpulkan di bagian manual referensi dengan nama yang sama. CR1 memiliki offset alamat ( Alamat offset ) 0x00, secara default semua bit diatur ulang ( Nilai reset 0x0000):

Bit-bit BR mengatur pembagi frekuensi clock pengontrol, sehingga menentukan frekuensi di mana SPI akan beroperasi. Kami akan memiliki frekuensi STM32 dari 72 MHz, driver LED, menurut datasheet-nya, bekerja dengan frekuensi hingga 25 MHz, oleh karena itu, perlu untuk membagi dengan 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;
2. Atur bit CPOL dan CPHA untuk menentukan hubungan antara transfer data dan clocking dari antarmuka serial (lihat diagram di halaman 240)
Karena kita membaca lembar data di sini, dan tidak mempertimbangkan sirkuit, mari kita mempelajari deskripsi tekstual bit CPOL dan CPHA di halaman 704 (Deskripsi Umum SPI):

Fase Jam dan Polaritas
Menggunakan bit CPOL dan CPHA dari register SPI_CR1, empat opsi untuk hubungan waktu dapat dipilih secara terprogram. Bit CPOL (polaritas jam) mengontrol keadaan jam ketika tidak ada data yang dikirim. Bit ini mengontrol mode master dan slave. Jika CPOL diatur ulang, pin SCK rendah dalam mode siaga. Jika bit CPOL diatur, pin SCK berada pada level tinggi dalam mode siaga.
Jika bit CPHA (fase jam) diatur, tepi kedua dari sinyal SCK bertindak sebagai gerbang perangkap dari bit tinggi (turun jika CPOL dihapus, atau naik jika CPOL diatur). Data ditangkap oleh perubahan kedua pada sinyal clock. Jika bit CPHA dikosongkan, tepi depan sinyal SCK bertindak sebagai gerbang perangkap dari bit tinggi (ke bawah jika CPOL diatur, atau ke atas jika CPOL dihapus). Data ditangkap oleh perubahan pertama pada sinyal clock.
Setelah merokok pengetahuan ini, kami menyimpulkan bahwa kedua bit harus tetap nol, karena kita membutuhkan sinyal SCK untuk tetap rendah ketika tidak digunakan, dan data yang akan ditransmisikan di sepanjang tepi utama pulsa (lihat Tepi Meningkat dalam lembar data DM634).
Ngomong-ngomong, di sini kita pertama kali menemukan fitur kosa kata dalam lembar data ST: di dalamnya frasa "reset bit to zero" ditulis untuk mengatur ulang sedikit , dan tidak menghapus sedikit , seperti, misalnya, di Atmega.
3. Atur bit DFF untuk menentukan format blok data 8-bit atau 16-bit.
Saya secara khusus mengambil DM634 16-bit, agar tidak repot dengan transmisi data PWM 12-bit, seperti DM633. DFF masuk akal untuk dimasukkan ke dalam unit:
#define DFF 0x0800 _SPI2_ (_SPI_CR1) |= DFF;
4. Konfigurasikan bit LSBFIRST dalam register SPI_CR1 untuk menentukan format blok
LSBFIRST, seperti namanya, menetapkan bit orde rendah ke depan. Tetapi DM634 ingin menerima data yang dimulai dengan bit tinggi. Karenanya, kami membiarkannya dibuang.
5. Dalam mode perangkat keras, jika input dari pin NSS diperlukan, kirim sinyal tinggi ke pin NSS selama seluruh urutan transfer byte. Dalam mode program NSS, atur bit SSM dan SSI dalam register SPI_CR1. Jika pin NSS harus bekerja pada output, hanya bit SSOE yang harus ditetapkan.
Instal SSM dan SSI untuk melupakan mode perangkat keras NSS:
#define SSI 0x0100 #define SSM 0x0200 _SPI2_ (_SPI_CR1) |= SSM | SSI;
6. Bit MSTR dan SPE harus diatur (mereka tetap hanya jika sinyal tinggi diterapkan ke NSS)
Sebenarnya, dengan bit-bit ini kami menetapkan SPI kami sebagai master dan menyalakannya:
#define MSTR 0x0004 #define SPE 0x0040 _SPI2_ (_SPI_CR1) |= MSTR;
SPI dikonfigurasi, mari kita menulis fungsi yang mengirim byte ke driver segera. Kami terus membaca 25.3.3 "Mengkonfigurasi SPI dalam mode master":

Prosedur Transfer Data
Transmisi dimulai ketika byte ditulis ke buffer Tx.
Byte data dimuat ke register geser dalam mode paralel (dari bus internal) selama transmisi bit pertama, setelah itu ditransmisikan dalam mode serial ke pin MOSI, bit pertama atau terakhir ke depan, tergantung pada pengaturan bit LSBFIRST dalam register CPI_CR1. Bendera TXE diatur setelah data ditransfer dari buffer Tx ke register geser , dan interupsi juga dibuat jika bit TXEIE diatur dalam register CPI_CR1.
Saya menyoroti beberapa kata dalam terjemahan untuk menarik perhatian pada satu fitur implementasi SPI pada pengontrol STM. Pada Atmega, flag TXE ( Tx Empty , Tx kosong dan siap menerima data) ditetapkan hanya setelah seluruh byte keluar . Dan di sini flag ini diatur setelah byte dimasukkan ke register geser internal. Karena didorong di sana oleh semua bit secara bersamaan (paralel), dan kemudian data ditransmisikan secara berurutan, TXE diatur sebelum byte sepenuhnya dikirim. Ini penting karena dalam hal driver LED kami, kami perlu menarik pin LAT setelah mengirim semua data, mis. Hanya bendera TXE tidak akan cukup bagi kami.
Dan ini berarti kita perlu bendera lainnya. Mari kita lihat di 25.3.7 - "Bendera status":

<...>

Bendera sibuk
Bendera BSY diatur dan diatur ulang oleh perangkat keras (menulis padanya tidak mempengaruhi apa pun). Bendera BSY menunjukkan status lapisan komunikasi SPI.
Sudah diatur ulang:
ketika transfer selesai (kecuali untuk mode master, jika transfer berlanjut)
ketika SPI dinonaktifkan
ketika kesalahan mode wizard terjadi (MODF = 1)
Jika transmisi tidak kontinu, bendera BSY dibersihkan antara setiap pengiriman data.
Oke, berguna. Kami mencari tahu di mana buffer Tx berada. Untuk melakukan ini, baca "Daftar Data SPI":

Bit 15: 0 DR [15: 0] Data Register
Menerima data atau data untuk transmisi.
Register data dibagi menjadi dua buffer - satu untuk menulis (mengirimkan buffer) dan yang kedua untuk membaca (menerima buffer). Menulis ke register data menulis ke buffer Tx, dan membaca dari register data akan mengembalikan nilai yang terkandung dalam buffer Rx.
Daftar status, di mana ada bendera TXE dan BSY:

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;
Nah, karena kita perlu mengirimkan 16 byte dua kali, 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);
Tapi kami masih belum tahu cara menarik pin LAT, jadi kami akan kembali ke I / O.
Tetapkan pin
Dalam STM32F1, register yang bertanggung jawab untuk keadaan pin agak tidak biasa. Jelas bahwa ada lebih banyak dari mereka daripada Atmega, tetapi mereka juga berbeda dari chip STM lainnya. Bagian 9.1 Deskripsi Umum GPIO:

Setiap port input / output 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), 16-bit register reset (GPIOx_BRR) dan register blok 32-bit (GPIOx_LCKR).
Tidak biasa, juga agak merepotkan, dua register pertama ada di sini, karena 16 pin port tersebar di atasnya dalam format "empat bit per saudara". Yaitu pin nol hingga ketujuh berada di CRL, dan sisanya di CRH. Pada saat yang sama, sisa register berhasil masuk dalam bit semua pin port - sering kali tersisa setengah "dicadangkan".
Untuk kesederhanaan, mulailah dari akhir daftar.
Kami tidak perlu register blokir.
Setup dan reset register cukup lucu karena mereka saling menduplikasi: Anda dapat menulis semuanya hanya dalam BSRR, di mana 16 bit tertinggi akan mengatur ulang pin ke nol, dan yang lebih rendah - set ke 1, atau gunakan juga BRR, 16 bit yang lebih rendah di mana hanya reset pin . Saya suka opsi kedua. Register ini penting karena memberikan akses atom ke pin:


Instalasi atom atau reset
Anda tidak perlu menonaktifkan interupsi ketika memprogram GPIOx_ODR pada tingkat bit: Anda dapat mengubah satu atau lebih bit dengan satu operasi penulisan atom APB2. Ini dicapai dengan menulis "1" ke register set / reset (GPIOx_BSRR atau, hanya untuk reset, di GPIOx_BRR) bit yang ingin Anda ubah. Bit lain akan tetap tidak berubah.
Register data memiliki nama yang cukup berbicara - IDR = Input Direction Register, input register; ODR = Output Direction Register, register keluaran. Dalam proyek saat ini kami tidak akan membutuhkannya.
Dan akhirnya, kontrol register. Karena kami tertarik pada pin SPI kedua, yaitu PB13, PB14 dan PB15, kami segera melihat CRH:

Dan kita melihat bahwa perlu untuk menulis sesuatu dalam bit dari tanggal 20 hingga tanggal 31.
Kami telah menemukan apa yang kami inginkan dari pin, jadi di sini saya dapat melakukannya tanpa tangkapan layar, cukup katakan bahwa MODE menentukan arah (input, jika kedua bit diatur ke 0) dan kecepatan pin (kita perlu 50MHz, mis. Keduanya pin di "1"), dan CNF menetapkan mode: normal "push-push" - 00, "alternative" - โโ10. Secara default, seperti yang kita lihat di atas, semua pin memiliki bit ketiga dari bawah (CNF0), itu menetapkan mereka untuk mode input mengambang .
Karena saya berencana untuk melakukan hal lain dengan chip ini, untuk kesederhanaan, saya telah mendefinisikan semua nilai MODE dan CNF yang mungkin untuk register kontrol bawah dan atas.
Nah, kira-kira 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 adalah 0x40010C00), kode:
#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset))) #define _BRR 0x14 #define _BSRR 0x10 #define _CRL 0x00 #define _CRH 0x04
Dan, oleh karena itu, Anda dapat menulis definisi untuk LAT, yang akan menggerakkan register BRR dan BSRR:
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14) #define LAT_low() _PORTB_(_BRR) = (1<<14)
(LAT_low hanya dengan inersia, entah bagaimana itu selalu, biarkan diri Anda tetap)
Sekarang semuanya sudah bagus, tidak berfungsi. Karena STM32, ini menghemat listrik, yang berarti Anda harus mengaktifkan jam untuk periferal yang diperlukan.
Hidupkan timing
Jam bertanggung jawab atas pencatatan jam kerja, mereka juga Jam. Dan kita sudah bisa melihat singkatan RCC. Kami mencarinya di dokumentasi: ini adalah Reset dan Kontrol Jam.
Seperti yang dikatakan di atas, untungnya, orang-orang dari STM melakukan bagian yang paling sulit dari topik pengaturan waktu untuk kami, dan mereka sangat berterima kasih (sekali lagi saya akan memberikan tautan ke situs web Di Halt untuk menjelaskan betapa bingungnya hal ini). Kami hanya perlu register yang bertanggung jawab untuk mengaktifkan jam periferal (Peripheral Clock Enable Register). Pertama, temukan alamat dasar RCC, itu di bagian paling awal "Kartu Memori":

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))
Dan kemudian klik pada tautan tempat mencoba menemukan sesuatu di piring, atau, jauh lebih baik, bacalah deskripsi register inklusi dari bagian mengaktifkan register . Di mana kami menemukan RCC_APB1ENR dan RCC_APB2ENR:


Dan di dalamnya, masing-masing, bit, termasuk clocking 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
Kode terakhir dapat ditemukan di sini .
Jika ada peluang dan keinginan untuk menguji, maka kami menghubungkan DM634 seperti ini: DAI ke PB15, DCK ke PB13, LAT ke PB14. Kami memberi makan pengemudi dari 5 volt, jangan lupa untuk menggabungkan bumi.

STM8 PWM
PWM pada STM8
Ketika saya merencanakan artikel ini, misalnya, saya memutuskan untuk mencoba mempelajari beberapa fungsionalitas dari chip yang tidak dikenal hanya dengan lembar data, sehingga saya tidak akan mendapatkan pembuat sepatu tanpa sepatu bot. STM8 secara ideal cocok dengan peran ini: pertama, saya memiliki beberapa motherboard Cina dengan STM8S103, dan kedua, itu tidak terlalu populer, dan oleh karena itu godaan untuk membaca dan menemukan solusi di Internet terletak pada ketiadaan solusi yang sangat ini.
Chip ini juga memiliki lembar data dan referensi manual RM0016 , di pinout pertama dan mendaftarkan alamat, di detik - semuanya. STM8 diprogram dalam C di IDE ST Visual Develop yang jelek.
Clocking dan I / O
Secara default, STM8 beroperasi pada frekuensi 2 MHz, ini harus segera diperbaiki.

Jam HSI (Kecepatan Internal)
Jam HSI diperoleh dari osilator RC 16-MHz internal dengan pembagi yang dapat diprogram (1 hingga 8). Sudah diatur dalam register pembagi jam (CLK_CKDIVR).
Catatan: pada awalnya, osilator HSI RC dengan pembagi 8 dipilih sebagai sumber clock terkemuka.
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 memulai PWM dan menghubungkan LED, kita melihat pinout:

Chipnya kecil, banyak fungsi yang ditangguhkan pada pin yang sama. Fakta bahwa dalam tanda kurung siku adalah "fungsionalitas alternatif", ia dialihkan oleh " opsi byte " - sesuatu seperti sekering Atmega. Anda dapat mengubah nilai-nilai mereka secara terprogram, tetapi tidak perlu, karena fungsi baru diaktifkan hanya setelah reboot. Lebih mudah menggunakan ST Visual Programmer (unduhan bersama dengan Visual Develop) yang dapat mengubah byte ini. Pinout menunjukkan bahwa kesimpulan CH1 dan CH2 dari timer pertama disembunyikan dalam tanda kurung; perlu untuk menempatkan bit AFR1 dan AFR0 di STVP, dan yang kedua juga akan mentransfer output CH1 dari timer kedua dari PD4 ke PC5.
Dengan demikian, 6 pin akan mengontrol LED: PC6, PC7 dan PC3 untuk timer pertama, PC5, PD3 dan PA3 untuk yang kedua.
Mengkonfigurasi pin I / O sendiri pada STM8 lebih sederhana dan lebih logis daripada pada STM32:
- akrab dengan Atmega Data Direction Register : 1 = output;
- register kontrol pertama CR1 pada output menetapkan mode push-pull (1) atau drain terbuka (0); karena saya menghubungkan LED ke chip dengan katoda, saya meninggalkan nol di sini;
- register kontrol kedua CR2 pada output menetapkan 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 tentukan persyaratannya:
- Frekuensi PWM - frekuensi yang digunakan oleh timer;
- Reload otomatis, AR - nilai pengisian otomatis yang akan dihitung penghitung waktu (periode pulsa);
- Perbarui Acara, UEV - suatu peristiwa yang terjadi ketika penghitung waktu telah dihitung untuk AR;
- PWM Duty Cycle - siklus tugas PWM, sering disebut sebagai "duty cycle";
- Capture / Compare Value - nilai untuk capture / perbandingan, setelah dihitung sampai kapan timer akan melakukan sesuatu (dalam kasus PWM, itu membalikkan sinyal output);
- Nilai Preload - nilai preload. Nilai perbandingan tidak dapat berubah saat timer terus berdetak, jika tidak siklus PWM akan terputus. Oleh karena itu, nilai-nilai yang dikirimkan baru ditempatkan di buffer dan ditarik keluar dari sana ketika timer mencapai akhir hitungan dan direset;
- Mode Edge-aligned dan Center-aligned - penjajaran di perbatasan dan di tengah, sama seperti PWM Cepat Atmelovskie dan PWM yang benar-fase .
- OCiREF, Output Reference Reference Signal - sinyal keluaran referensi, pada kenyataannya, dalam mode PWM ada pada pin yang sesuai.
Seperti yang sudah jelas dari pinout, dua timer memiliki kemampuan PWM - pertama dan kedua. Keduanya 16-bit, yang pertama memiliki banyak fitur tambahan (khususnya, dapat menghitung naik dan turun). Kita perlu keduanya bekerja dengan cara yang sama, jadi saya memutuskan untuk memulai dengan yang kedua jelas lebih miskin, agar tidak sengaja menggunakan sesuatu yang tidak ada di dalamnya. Beberapa masalah adalah bahwa deskripsi fungsi PWM dari semua timer dalam manual referensi ada di bab pada timer pertama (17.5.7 Mode PWM), jadi Anda harus melompat-lompat di seluruh dokumen.
PWM pada STM8 memiliki keunggulan penting dibandingkan PWM Atmega:

PWM dengan perataan tepi
Konfigurasi akun dari bawah ke atas
Hitungan bottom-up aktif jika bit DIR dalam register TIM_CR1 dihapus
Contoh
Contohnya menggunakan mode PWM pertama. Sinyal referensi PWM OCiREF bertahan tinggi sementara TIM1_CNT <TIM1_CCRi. Kalau tidak, dibutuhkan level rendah. Jika nilai perbandingan di TIM1_CCRi lebih besar dari nilai awal (register TIM1_ARR), sinyal OCiREF ditahan di 1. Jika nilai perbandingannya adalah 0, OCiREF ditahan di nol. ...
Timer STM8 selama acara pembaruan pertama memeriksa nilai perbandingan , dan hanya kemudian memberikan sinyal referensi. Timer Atmega pertama kali mengocok, dan kemudian membandingkan, sebagai hasilnya, ketika compare value == 0
output menghasilkan jarum yang perlu diperangi entah bagaimana (misalnya, dengan logika pembalik terprogram).
Jadi, apa yang ingin kita lakukan: 8-bit PWM ( AR == 255
), kami pertimbangkan dari bawah ke atas, penjajaran di sepanjang perbatasan. Karena bola lampu terhubung ke chip dengan katoda, PWM harus menampilkan 0 (LED menyala) sebelum membandingkan nilai dan 1 setelah.
Kami sudah membaca tentang beberapa mode PWM , jadi kami menemukan register yang diinginkan dari timer kedua dengan mencari di manual referensi untuk frasa ini (18.6.8 - TIMx_CCMR1):

110: Mode PWM pertama - saat menghitung dari bawah ke atas, saluran pertama aktif saat TIMx_CNT <TIMx_CCR1. Jika tidak, saluran pertama tidak aktif. [lebih lanjut dalam dokumen ini salah-tempel dari pengatur waktu 1]
111: Mode PWM kedua - saat menghitung dari bawah ke atas, saluran pertama tidak aktif sementara TIMx_CNT <TIMx_CCR1. Jika tidak, saluran pertama aktif.
Karena LED terhubung ke katoda MK, mode kedua cocok untuk kami (yang pertama juga, tapi kami belum tahu).

Bit 3 OC1PE: Mengaktifkan Output 1 Preload
0: Preload register pada TIMx_CCR1 off. Anda dapat menulis ke TIMx_CCR1 kapan saja. Nilai baru segera berfungsi.
1: Preload register di TIMx_CCR1 diaktifkan. Operasi baca / tulis mengakses register preload. Nilai yang dimuat sebelumnya TIMx_CCR1 dimuat ke dalam register bayangan selama setiap acara pembaruan.
* Catatan: agar mode PWM berfungsi dengan benar, register preload harus dihidupkan. ( TIMx_CR1 OPM).
, , , :
#define TIM2_CCMR1 *(volatile uint8_t *)0x005307 #define TIM2_CCMR2 *(volatile uint8_t *)0x005308 #define TIM2_CCMR3 *(volatile uint8_t *)0x005309 #define PWM_MODE2 0x70 //PWM mode 2, 0b01110000 #define OCxPE 0x08 //preload enable TIM2_CCMR1 = (PWM_MODE2 | OCxPE); TIM2_CCMR2 = (PWM_MODE2 | OCxPE); TIM2_CCMR3 = (PWM_MODE2 | OCxPE);
AR , :
#define TIM2_ARRH *(volatile uint8_t *)0x00530F #define TIM2_ARRL *(volatile uint8_t *)0x005310 TIM2_ARRH = 0; TIM2_ARRL = 255;
-, , . , , 256. TIM2_PSCR :
#define TIM2_PSCR *(volatile uint8_t *)0x00530E TIM2_PSCR = 8;
. Capture/Compare Enable : , . , , .. PWM Mode 1. :
#define TIM2_CCER1 *(volatile uint8_t *)0x00530A #define TIM2_CCER2 *(volatile uint8_t *)0x00530B #define CC1E (1<<0) // CCER1 #define CC2E (1<<4) // CCER1 #define CC3E (1<<0) // CCER2 TIM2_CCER1 = (CC1E | CC2E); TIM2_CCER2 = CC3E;
, , TIMx_CR1:

#define TIM2_CR1 *(volatile uint8_t *)0x005300 TIM2_CR1 |= 1;
AnalogWrite(), . Capture/Compare registers , : 8 TIM2_CCRxL TIM2_CCRxH. 8- , :
#define TIM2_CCR1L *(volatile uint8_t *)0x005312 #define TIM2_CCR2L *(volatile uint8_t *)0x005314 #define TIM2_CCR3L *(volatile uint8_t *)0x005316 void setRGBled(uint8_t r, uint8_t g, uint8_t b) { TIM2_CCR1L = r; TIM2_CCR2L = g; TIM2_CCR3L = b; }
, , 100% ( 255 ). , , .
, .
( , ยซยป , ). . , .. , 16- Prescaler High Low . โฆ . ?
1, , . 17.7.30 Break register (TIM1_BKR) , :

#define TIM1_BKR *(volatile uint8_t *)0x00526D TIM1_BKR = (1<<7);
, .

STM8 Multiplex
STM8
- , RGB- . โ LED-, , - , , ( persistence of vision , ). - - .
:
.. , , ยซยป . . , , , UEV RGB-.
LED , ยซยป, . :
uint8_t colors[8][3];
, , .
uint8_t cnt;
, , CD74HC238. โ , <<
. ( 0, 1 2) X, ( 1<<X
). . , โ , , . , .
CD74HC238 , . P-MOSFET, , .. 20 , absolute maximum ratings . CD74HC238 :

H = , L = , X โ
E2 E1 , E3, A0, A1 A3 PD5, PC3, PC4 PC5 STM8. , , push-pull .
, , :
-, Update Event (UEV), , LED. Update Interrupt Enable

#define TIM2_IER *(volatile uint8_t *)0x005303 //enable interrupt TIM2_IER = 1;
, ghosting โ . - , , UEV, , LED - . (0 = , 255 = ) . Yaitu , UEV .
:
//set polarity TIM2_CCER1 |= (CC1P | CC2P); TIM2_CCER2 |= CC3P;
r, g b 255 .
, - . - , .
ST Visual Develop, main.c
stm8_interrupt_vector.c
, . NonHandledInterrupt
. .
, :

13 TIM2 /
14 TIM2 /
LED UEV, โ13.
, -, stm8_interrupt_vector.c
, โ13 (IRQ13) :
{0x82, TIM2_Overflow}, /* irq13 */
-, main.h
:
#ifndef __MAIN_H #define __MAIN_H @far @interrupt void TIM2_Overflow (void); #endif
, , main.c
:
@far @interrupt void TIM2_Overflow (void) { PD_ODR &= ~(1<<5); // PC_ODR = (cnt<<3); // PD_ODR |= (1<<5); // TIM2_SR1 = 0; // Update Interrupt Pending cnt++; cnt &= 7; // LED TIM2_CCR1L = ~colors[cnt][0]; // TIM2_CCR2L = ~colors[cnt][1]; // TIM2_CCR3L = ~colors[cnt][2]; // return; }
. rim
โ Programming Manual :
//enable interrupts _asm("rim");
โ sim
โ . ยซยป, .
โ .

- , , . , .