Dasi yang rumit
Latar Belakang ...
Sebagai bagian dari pekerjaan saya tentang rekayasa balik label harga eInk elektronik, saya mengalami masalah yang menarik. Perusahaan tertentu (Samsung Electro Mechanics / SoluM) beralih dari menggunakan chip pihak ketiga, yang asalnya dapat saya identifikasi (Marvell 88MZ100) ke chip baru, yang mulai digunakan dengan label harga generasi berikutnya.
Tampaknya ini adalah chip mereka sendiri, yang dikembangkan oleh perusahaan untuk tujuan ini. Mengambil rekayasa terbalik dari hal semacam itu adalah masalah yang mati. Seorang teman memberi saya beberapa label harga dengan chip seperti itu - untuk bermain-main. Ternyata ada dua jenis: satu dengan tampilan tersegmentasi pada e-ink, dan yang lainnya dengan tampilan grafik konvensional pada e-tinta. Chip utama di kedua model sama, jadi hal pertama yang saya lakukan adalah dengan perangkat tampilan tersegmentasi, karena lebih sederhana dan lebih mudah menangani sistem yang tidak dikenal yang menggunakannya. Tidak sepenuhnya jelas harus mulai dari mana, tetapi, tentu saja, ini adalah tugas yang selalu paling menarik!
Belajar
Bodoh untuk mencoba memecahkan teka-teki silang tanpa membaca pertanyaannya. Sama bodohnya dengan merekayasa balik perangkat tanpa terlebih dahulu mengumpulkan semua informasi yang sudah tersedia tentangnya. Jadi apa yang awalnya kita ketahui? Protokol transfer data nirkabel mungkin sama seperti biasanya, karena tidak ada perusahaan yang ingin bermigrasi ke yang baru atau mendukung dua protokol untuk pelanggannya sekaligus, perlahan-lahan melakukan migrasi. Protokol lama adalah 2,4 GHz seperti ZigBee, jadi yang baru mungkin sama. Ini adalah foto papan dari kedua sisi.
Jadi apa yang kita lihat? Pertama, contoh keren tentang pengoptimalan biaya. Mereka melaminasi layar e-ink langsung ke PCB! Siapa yang membutuhkan panel belakang kaca konduktif bila ada PCB? Panel depan terbuat dari plastik konduktif. Tapi itu tidak penting.
Dua antena terlihat, keduanya, dilihat dari ukurannya - pada 2,4 GHz. Seperti yang diharapkan, perangkat generasi sebelumnya juga memiliki dua antena 2,4 GHz. Kami melihat dua chip. Besar dan kecil. Yang besar (disebut "SEM9010") tampaknya memiliki banyak kontak ke layar dan tidak ada ke antena. Jelas ini adalah pengontrol tampilan.
Otak kecil (disebut "SEM9110") tampaknya adalah otak yang bertanggung jawab atas semua operasi. Ini terhubung ke antena, kristal waktu, dan poin-poin penting yang jelas di sini untuk pemrograman pabrik.
Ada 12 bantalan di sini: satu terhubung ke terminal positif baterai, satu ke ground, tujuan 10 lainnya adalah sebuah misteri. Mencari nama chip secara online, saya tidak menemukan sesuatu yang berguna - pasti pengembangannya sendiri. Tetapi siapa yang mendesain chip mereka sendiri untuk aplikasi sederhana seperti itu? Mungkin hanya rebranding? Dimanfaatkan, kami sedang bekerja!
Anehnya, Pencarian Gambar Google membantu di sini. Kebetulan alat ini berguna untuk rekayasa balik. Dalam hal ini, dia membawa kita ke nugget ini. (salinan arsip di sini untuk anak cucu). Ini adalah pertanyaan dari StackExchange - bertanya-tanya bagaimana label rak elektronik ini bekerja. Pertanyaannya menarik karena dalam foto yang diposting di sini, papan sirkuit tercetak terlihat hampir sama dengan milik kita. Keripiknya juga persis sama, tetapi labelnya berbeda! Papan tersebut mungkin dibuat sebelum SoluM mulai mengubah merek chip ini.
Chip yang saya anggap sebagai pengontrol layar diberi label
SSD1623L2
. Memang, ini adalah pengontrol tampilan tersegmentasi e-ink yang mendukung hingga 96 segmen. Mencari online, saya menemukan lembar data versi 0.1 pra-rilis (salinan arsip di sini untuk anak cucu). Ini baik! Jika mereka tahu cara menyelesaikannya, mereka dapat mengambil kode yang dimengerti, dan segera setelah kita melihat kode ini, itu saja!
Ternyata mikrokontroler utamanya adalah
ZBS242
. Baik. Saya kurang paham dengan mikrokontroler ini. Mari cari di Internet lebih banyak - dan pencarian membawa kita ke tautan (salinan arsip di sini untuk anak cucu), yang juga menyebutkan jawaban yang sama dari StackExchange. Halamannya berbahasa Korea, tetapi itu menunjukkan bahwa chip ini memiliki inti 8.051, serta peralatan periferal yang cukup dapat diprediksi: UART, SPI, I2C, ADC, DAC, komparator, sensor suhu, PWM 5-channel, pengontrol triac 3-ch , Pemancar IR, fungsi pemindaian tombol, fungsi RF-Wake, jarak antena, radio dan MAC yang kompatibel dengan ZigBiee. Gambar tersebut menunjukkan bahwa ada juga osilator RC 32 kHz internal, yang, seperti yang dinyatakan, dapat mengonsumsi paling sedikit 1 uA dalam mode tidur. Saya pikir perusahaan inilah yang membuat chip kami untuk Samsung. Menarik ...
Mari kita lihat gambar-gambarnya dan temukan bahwa kristal SEM9110 yang membingungkan kami juga ditembak langsung (salinan arsip di sini untuk anak cucu). Dinyatakan sebagai ZBS243. Saya rasa itu berarti ada seluruh keluarga chip di sini: ZBS24x. Sangat menarik.
Kami memiliki utas!
Setelah membuka tag segmen lainnya, kami terus bersukacita dalam berita: kepala pemrograman ditandatangani dengan huruf emas yang jelas dan dapat dibaca! Kepala tampaknya memiliki SPI, UART, pin reset, catu daya, ground, dan pin yang disebut "test", mungkin digunakan untuk masuk ke mode uji pabrik. Semuanya lebih penasaran dan penasaran.
Adalah logis bahwa perwakilan tertua dari keluarga hipotetis ZBS24x akan diberi nama "ZBS240". Mungkin pencarian untuk pertanyaan seperti itu akan memberi kita sesuatu yang menarik? Mencari "ZBS240" dan menyaring slag, kami menemukan halaman menarik lainnya dalam bahasa Korea (salinan arsip di sini untuk anak cucu). Sepertinya perusahaan ini membuat pemrogram grup sesuai permintaan khusus. Setelah melihat-lihat di situs web mereka, kami menemukan manual (salinan arsip di sini untuk anak cucu) pada perangkat pemograman mereka, dan kami bahkan dapat mengunduh utilitas untuk PC agar dapat bekerja dengan perangkat semacam itu. Utilitas ini bahkan memiliki alat untuk memperbarui firmware pada perangkat. Saya melihat apakah mungkin untuk menebak dari informasi ini bagaimana memprogram perangkat, tetapi firmware ternyata dienkripsi. Rupanya utilitas sisi PC hanya mengirim data melalui port serial USB, jadi tidak ada informasi berguna di sini juga. Sedih ...
Setelah mencari lebih banyak, kami menemukan halaman yang lebih menarik (salinan arsip di sini untuk anak cucu). Apa itu? Apakah itu diobral?!? Sudah pasti tidak lagi, bukan? Saya hanya menulis surat ke perusahaan ini untuk sabun, untuk berjaga-jaga. Diam ... Sebagai tanda putus asa, saya bertanya kepada seorang teman dari Hong Kong apakah dia mengenal seseorang di Korea yang dapat menghubungi orang-orang ini, karena situs web mereka menunjukkan bahwa mereka hanya menerima transfer dari bank Korea sebagai pembayaran. Saya hanya kagum ketika dia membalas dan berkata, memang, dia bisa mendapatkan saya perangkat ini melalui perantara yang ditemukan di Korea! Beberapa hari kemudian, perangkat tersebut dikirim melalui DHL!
Anda bisa menghubunginya!
Bagaimana cara menghubunginya
Bekerja! Saya bisa membaca chip dan menulis padanya. Saya butuh beberapa saat untuk meneliti alat pemrograman. Rupanya, chip tersebut memiliki memori flash 64KB dan "blok informasi" 1KB, yang menurut saya digunakan untuk menyimpan nilai kalibrasi, alamat MAC, dan sejenisnya. Saya bisa mencegat beberapa jejak, dipersenjatai dengan penganalisis logika Saleae Logic yang luar biasa , menyaksikan programmer melakukan tugasnya. Anda dapat mengunduh temuan saya di sini . Dalam arsip ini Anda akan menemukan jejak membaca, menghapus dan menulis ke ruang INFOBLOCK dan CODE. Faktanya, protokolnya SANGAT sederhana! Frekuensi clock bisa apa saja dari 100 kHz hingga 8 MHz.
Protokol ISP: potong ke tulang
Semuanya dimulai dengan mengatur garis ke keadaan yang diinginkan: SCLK bottom, MOSI top, RESET top, SS top. Kondisi ini dipertahankan selama 20 ms. Kemudian RESET diturunkan sebesar 32 ms. Kemudian setidaknya 4 clock prosesor dikirim ke jalur SCK pada 500 kHz. Kemudian ada penundaan 10 ms lagi hingga RESET didorong ke atas. Sekarang Anda dapat menyetel penundaan 100 md sebelum memulai komunikasi. Setelah itu, berapa pun jumlah transaksi bisa dilakukan. Beberapa aturan dasar: harus ada setidaknya 5us antara SS turun dan mengirim byte, setidaknya 2us antara akhir byte dan SS naik, dan periode terpendek yang dapat digunakan SS adalah 2,5us. Oleh karena itu, setiap byte dikirim dalam status: SS turun, satu byte dikirim dalam mode SPI 0, SS naik. Ya tentu saja,SS membalik untuk setiap byte.
Semua transaksi memiliki panjang tiga hingga empat byte. Byte pertama menunjukkan jenis transaksi, bit terendah menentukan arah transaksi: nol berarti menulis ke perangkat, satu berarti membaca dari perangkat.
0x02
/ Perintah
0x03
digunakan untuk memulai sesi komunikasi. Programmer mengirimkan tulisan tiga-byte:
02 BA A5
dan kemudian membaca, pertama-tama mengirimkan perintah baca dan "alamat" :,
03 BA
master mengirimkan
FF
saat menerima
A5
. Jika ini berhasil, maka komunikasi terjalin.
Perintah
0x12
/
0x13
digunakan untuk membaca / menulis register tujuan khusus (SFR) di CPU (menurut saya ini lebih sulit, tetapi dalam hal ini urutannya tidak begitu penting). Untuk memilih INFOBLOCK, SFR
0xD8
harus diatur ke
0x80
, untuk memilih area flash utama, harus diatur ke
0x00
. Untuk menuliskan nilai vv ke register rr diperlukan data SPI
12 rr vv
. Untuk memastikan bahwa nilai telah dibaca, nilai dapat dibaca kembali dengan terlebih dahulu mengirimkan perintah baca dan "alamat":
13 rr
setelah itu master mengirim
FF
saat menerima
vv
.
Mudah untuk membaca memori flash. Untuk melakukan ini, terapkan
0x09
, perintah empat byte. Setelah byte perintah, alamat dikirim, pertama byte tinggi, kemudian rendah. Master kemudian mengirimkan
FF
, sementara itu menerima byte yang telah dibaca. Baiklah. Perintah terpisah diperlukan untuk membaca setiap byte. Menulis juga mudah. Untuk ini, perintah digunakan
0x08
. Ini adalah perintah empat byte. Setelah byte perintah, alamat dikirim, pertama byte tinggi, kemudian byte rendah, dan kemudian byte yang akan ditulis. Perintah terpisah juga diperlukan untuk menulis setiap byte. Pastikan untuk menghapus sebelum merekam. Untuk INFOBLOCK menghapus, itu hanya membutuhkan satu urutan 4-byte:
48 00 00 00
. Menghapus di memori flash utama dilakukan dengan menggunakan perintah
88 00 00 00
.
Jadi sekarang Anda cukup tahu untuk memprogram ZBS24x Anda dengan mudah!
Mulai bekerja!
Primer untuk 8051
Jika Anda sudah terbiasa dengan 8051, Anda dapat melewati bagian ini dengan aman .
8051 adalah mikrokontroler tua yang dirancang oleh intel di zaman kuno . Ini sangat merepotkan untuk dikerjakan, tetapi masih cukup sering digunakan karena murah untuk dilisensikan (sebenarnya, gratis). Apa masalahnya? 8051 memiliki beberapa ruang memori terpisah.
CODE
Apakah area memori disediakan untuk kode. Ukuran maksimumnya adalah 64KB (alamat 16-bit). Dalam desain paling modern, ini adalah memori flash. Kode dapat membaca byte dari sini menggunakan instruksi khusus
movc
("MOVe from Code").
XRAM
Apakah memori "eksternal". Artinya, eksternal ke inti. Anda dapat menyimpan berbagai hal di dalamnya, tetapi hampir tidak berguna untuk hal lain. Seperti ini: satu-satunya operasi yang dapat dilakukan dalam memori ini adalah menulis dan membaca. Ukuran maksimumnya adalah 64KB (alamat 16-bit). Bagaimana cara kerja memori alamat dari alamat 8-bit dengan alamat lebar 16-bit? Ternyata sangat lambat. Perintah
movx
("MOVe ke / dari eXternal") mengakses memori jenis ini, tetapi bagaimana Anda menentukan alamat 16-bit? Untuk ini, register khusus yang disebut
DPTR
("Data PoinTeR") digunakan, serta untuk bekerja dengan instruksi
movc
.
DPTR
terdiri dari register atas
DPH
dan register bawah
DPL
... Akibatnya, dengan menulis setengah alamat untuk masing-masing alamat, Anda dapat mengalamatkan memori eksternal dan memori kode. Seperti yang Anda duga, proses ini dengan cepat mulai tergelincir, karena, misalnya, untuk menyalin bagian dari memori eksternal ke memori eksternal, Anda perlu berulang kali mengocok nilai antara
DPL
dan
DPH
. Karena alasan ini, beberapa versi yang lebih maju dari 8051 memiliki banyak register
DPTR
, tetapi tidak semua, dan tidak semuanya diimplementasikan dengan cara yang sama.
Intel telah menambahkan cara yang lebih cepat untuk mengakses subset memori eksternal. Dalam hal ini, idenya adalah menggunakan register
R0
dan
R1
sebagai register penunjuk. Tetapi ukurannya 8 bit, dari mana 8 bit lainnya dalam alamat tersebut berasal? Mereka berasal dari register
P2
(yang juga mengontrol port 2 untuk pin GPIO). Jelas, praktik ini menghalangi penggunaan port 2 untuk ... Anda tahu ... GPIO. Ada cara untuk memuluskan situasi ini, tetapi saya tidak sedang membicarakannya sekarang. Jadi, jumlah memori yang tersedia untuk kami dibatasi hingga 256 byte (kecuali jika Anda mengubah port 2 secara dinamis, yang mungkin tidak ingin Anda lakukan). Biasanya memori ini disebut
PDATA
. Akses memori serupa juga dilakukan dengan menggunakan instruksi
movx
. Baris berikutnya yang kita miliki
SFR
- berbagai register konfigurasi yang digunakan untuk konfigurasi periferal. Area memori ini hanya dapat diakses secara langsung. Ini adalah situasinya: alamat harus dikodekan langsung dalam instruksi, tidak akan ada akses melalui register penunjuk. Ada 128 byte
SFR
. Tabel berikut menunjukkan daftar yang
SFR
tersedia sesuai dengan standar 8051. Kotak abu-abu berisi
SFR
bit mana yang dapat diakses secara individual menggunakan perintah bit-bijaksana. Ini berguna saat menetapkan pin port atomic, atau saat mengaktifkan / menonaktifkan sumber interupsi, atau saat memeriksa beberapa status.
Memori internal pada 8051 agak rumit. Pada semua 8051 modern, ukurannya 256 byte. 128 byte terakhir hanya
0x80-0xff
tersedia secara tidak langsung melalui register dan , tetapi, tidak seperti situasi dengan memori eksternal, sekarang tidak hanya membaca dan menulis yang tersedia bagi kita. Kita dapat melakukan peningkatan satu ( rement), lebih rendah satu ( rement), penambahan ( ), dan sebagian besar operasi yang diharapkan lainnya. Faktanya, SEMUA RAM internal diakses secara tidak langsung melalui register penunjuk ini. 128 byte terendah
R0
R1
inc
dec
add
0x00-0x7f
juga tersedia secara langsung (alamat langsung dikodekan dalam instruksi itu sendiri, seperti saat bekerja dengan
SFR
. 16 byte memori dalam kisaran
0x20-0x2f
juga dapat dialamatkan bit menggunakan instruksi pemrosesan bit. Lebih mudah untuk menyimpan variabel untuk nilai boolean di bagian ini. 32 byte terendah
0x00-0x1f
membentuk 4 register bank
R0
...
R7
Dalam register status
PSW
ada bit yang memungkinkan Anda untuk memilih bank mana yang sedang digunakan, tetapi kenyataannya, karena biasanya ada kekurangan di area memori internal, kode ini kebanyakan hanya menggunakan satu bank memori.
8051 adalah mesin yang dirancang khusus untuk bekerja dengan satu operan. Yaitu: dalam kebanyakan operasi, baterai digunakan sebagai salah satu sumber dan, mungkin, sebagai tujuan. Register juga dapat digunakan untuk banyak (tetapi tidak semua) operasi, dan beberapa operasi memungkinkan akses tidak langsung ke RAM internal, seperti dijelaskan di atas. Tumpukan adalah upstream kosong, dapat dialamatkan
SFR
, dipanggil
sp
dan terletak hanya di RAM internal, ukuran maksimumnya dibatasi hingga 256 byte, tetapi pada kenyataannya jauh lebih kecil.
Gambar ROM 8051 dimulai dengan tabel vektor yang berisi lompatan ke kode awal yang ingin Anda jalankan serta penangan interupsi. Pada tahun 8051, secara historis, vektor reset terletak di
0x0000
, dan penangan interupsi mulai dari alamat
0x0003
dan kemudian setiap 8 byte. Karena instruksi
reti
hanya digunakan untuk kembali dari interupsi, itu dapat digunakan untuk dengan mudah mendeteksi apakah fungsi tertentu adalah penangan interupsi.
Isi saluran kompilator C Anda dengan semua ini dan terima kasih banyak!
Kompiler C yang cocok untuk arsitektur ini ada: Keil's C51. Tapi itu tidak murah. Ada juga kompiler open source: SDCC . Biasa saja, tapi gratis. Saat melakukan proyek ini, saya hanya menemukan dua bug hebat di dalamnya, yang hanya dapat diatasi dengan melewati; tidak buruk sama sekali untuk proyek open source.
Mari kita mulai analisisnya
void prvTxBitbang(u8 val)
__naked {
__asm__(
" setb PSW.5 \n"
" jbc _EA, 00004$ \n"
" clr PSW.5 \n"
"00004$: \n"
" clr C \n"
" mov A, DPL \n"
" rlc A \n"
" mov DPL, A \n"
" mov A, #0xff \n"
" rlc A \n"
" mov DPH, A \n"
" mov B, #11 \n"
"00001$: \n"
" mov A, DPH \n"
" rrc A \n"
" mov DPH, A \n"
" mov A, DPL \n"
" rrc A \n"
" mov DPL, A \n"
" jnc 00002$ \n"
" setb _P1_0 \n"
" sjmp 00003$ \n"
"00002$: \n"
" clr _P1_0 \n"
" nop \n"
" nop \n"
"00003$: \n"
" nop \n"
" nop \n"
" nop \n"
" djnz B, 00001$ \n"
" mov C, PSW.5 \n"
" mov _EA, C \n"
" ret \n"
); }
Mudah untuk memulai dengan konfigurasi GPIO. Sebagai aturan, Anda akan menemukan beberapa bit yang cocok, yang akan disetel atau dihapus dalam beberapa register berturut-turut. Ini logis, karena ketika mengaktifkan atau menonaktifkan, Anda biasanya harus menggunakan pin sebagai fungsi (dari GPIO), mengaturnya sebagai input atau output, dan mengatur atau membaca nilainya. Anda harus menemukan kode semacam ini di awal pekerjaan. Mari kita lihat apa yang ada ... kami menemukan bahwa register standar
P0
,
P1
dan
P2
benar - benar digunakan seperti itu, bagaimana menangani register GPIO. Dengan melihat register mana yang tertulis di sekitarnya dan apa yang kemudian terjadi pada bit-bit di dalamnya (apakah mereka dibaca (input) atau tulis (output)), kita dapat berasumsi bahwa register
AD
,
AE
,
AF
Apakah yang dirancang untuk "fungsi" - dan tampaknya GPIO, yang mengatur bit yang sesuai tidak digunakan sebagai GPIO, dan semua GPIO, benar-benar digunakan sebagai GPIO, mulai bekerja sehingga hanya setelah sedikit yang sesuai di salah satu register ini akan dihapus. Saya menamainya di
PxFUNC
mana x adalah nomor portnya. Maka dapat disimpulkan bahwa
B9
,
BA
,
BB
mengontrol arah. Setiap kali bit disetel di salah satunya, GPIO terkait hanya dibaca, dan saat bit dihapus, GPIO terkait hanya tulis. Oleh karena itu, kami memahami bahwa register ini mengontrol arah GPIO. Saya menamai mereka
PxDIR
dimana x adalah nomor port. Jadi sekarang, secara teori, saya bisa mengontrol GPIO. Andai saja saya tahu siapa di antara mereka yang melakukan apa ...
Saya memutuskan untuk mencoba semuanya berturut-turut sampai saya menemukan salah satu yang mengontrol "pad TEST" di kepala pemrograman, atau mungkin pad URX dan UTX. Bagaimanapun, sebenarnya ... Saya menemukan bahwa port 1 pin 0 (
P1.0
) adalah "TEST",
P0.6
ini adalah "UTX", dan
P0.7
ini adalah "URX". Memiliki GPIO yang terkontrol, Anda dapat menyederhanakan hidup Anda, tetapi hanya selama Anda dapat menangani debugging dengan mengganti GPIO yang berbeda, dan sampai Anda bosan. Saya punya waktu untuk mempraktikkan ini!
Kami memiliki printf!
Saya menggunakan fungsi ini untuk mengubah pad "TEST" menjadi port serial 8n1 biasa menggunakan metode bit-bang, dan mengumpulkan output menggunakan penganalisis logika saya. Saya mengotak-atiknya sampai memberikan baud rate yang dapat ditangani oleh kabel adaptor USB ke serial saya. Saya sudah memiliki implementasi printf 8.051 di assembler. Selama satu jam, saya berlatih mengeluarkan baris debug kompleks dari port serial dadakan ini. Bukan awal yang buruk, tentu saja, ini adalah satu-satunya cara yang Anda perlukan untuk bergerak maju secara efektif!
Pada titik ini, saya telah menampilkan di jendela nilai semua
SFR
, untuk setidaknya menavigasi nilai-nilai ini. Masih ada beberapa masalah dengan penelitian lebih lanjut. Untuk memulainya, pengatur waktu pengawas (WDT) sepertinya hanya disetel secara default dan mengatur ulang chip setelah satu detik eksekusi, jadi semua eksperimen saya harus sesuai dalam satu detik atau kurang. Saya belum tahu cara mengoperasikan WDT, jadi saya tahan dengan batasan ini untuk sementara waktu. Bagaimanapun, satu detik adalah banyak siklus!
Memperluas akses
Sekarang saya dapat menjalankan kode dengan andal dan menampilkan hasilnya, saya memutuskan untuk mencari tahu di mana kontrol centang berada. Hampir semua register memiliki setidaknya satu register yang mengontrol kecepatan berbeda (setidaknya kecepatan CPU) dan register lain yang mengontrol clock rate (atau reset) berbagai modul. Mereka biasanya ditemukan seperti ini: yang pertama biasanya direkam SANGAT awal pada pemuatan awal, dan setelah itu hampir tidak disentuh (jika sama sekali). Yang kedua biasanya memiliki sedikit set (siklus jam) atau sedikit dihapus sebelum kita mulai mengkonfigurasi perangkat. Kami tidak tahu di mana berbagai periferal dikonfigurasi, tetapi biasanya set
SFR
dengan nomor yang sama sesuai dengan perangkat periferal. Jadi mari kita lihat. Pasti ada kasus, sesuai dengan deskripsi ini:
B7
. Kita melihat bahwa ini disetel satu bit pada satu waktu, sebelum beberapa
SFR
dengan nomor yang sama ditulis , dan bit di dalamnya akan dihapus setelah panggilan ke beberapa
SFR
nomor yang sama berhenti. Kami juga melihat bahwa itu awalnya dicatat sebagai
0x2F
, jadi di sini kita berurusan dengan periferal yang disertakan sebelumnya. Karena bit tampaknya disetel sebelum apa yang kami anggap sebagai periferal inisialisasi, saya akan memanggil register ini
CLKEN
... Saya bermain-main dengan mengubah bit dalam register ini, dan sepertinya tidak ada yang terjadi ketika dihapus. Pada prinsipnya, ini logis, karena saya tidak menggunakan periferal apa pun.
Register lain yang tertulis di dekatnya (kode literasi biasanya menginisialisasi semua operasi jam bersama-sama), yang kemudian tidak ditulis ulang, adalah ini
8E
. Dia menulis kepada
0x21
. Saya menyarankan bahwa ini mungkin terkait dengan kecepatan. Saya bereksperimen. Rupanya, 4 bit paling tidak signifikan tidak tercermin dengan cara apa pun di tempat kerja, jadi saya tidak tahu mengapa mereka dipasang
0b0001
, tetapi tiga bit berikutnya, mungkin, mengubah kecepatan CPU cukup signifikan (sejauh yang saya bisa menilai dari kecepatan UART saya, mengalami penyimpangan). Bit yang paling signifikan tampaknya mengubah frekuensi sedikit, saya berasumsi bahwa itu bertanggung jawab untuk beralih antara rangkaian RC internal dan kristal eksternal. Tiga bit, yang saya asumsikan berfungsi sebagai pembagi frekuensi, mengatur kecepatan clock agar tampak sama
16M / (1 + )
. Saya menamai register ini
CLKSPEED
. Akibatnya, kecepatan tertinggi dicapai pada nilai tersebut
0x01
, dan yang terendah pada
0xf1
Membuat Pengatur Waktu Bekerja
Banyak pabrikan membangun segala macam hal di 8051, jadi hanya ada sedikit standarisasi di sini. Namun, sebagian besar tidak menyentuh peralatan normal 8051, seperti timer 0 dan timer 1. Harap diperhatikan: ini bukan aturan praktis. Misalnya, TI secara signifikan mengubah pengatur waktu di chip seri CC-nya. Saya perhatikan bahwa dalam chip ini, register yang biasanya seharusnya mengkonfigurasi timer standar 8051 tampaknya terjadi dekat, dan penangan interupsi # 1 tampaknya juga mempengaruhi mereka. Apakah mungkin? Pengatur waktu standar? Saya mencobanya dan ... berhasil. Sepenuhnya standar, terlihat persis sama dengan spesifikasi aslinya. Saya memeriksa register
CLKEN
dan menemukan bahwa bit 0 (mask
0x01
) agar pengatur waktu berfungsi. Dikonfirmasi bahwa register standar
IEN0
juga berfungsi seperti yang diharapkan, dan bahwa angka 1 dan 3 benar-benar mendorong interupsi untuk Timer 0 dan Timer 1! Pengatur waktu tampaknya bekerja tepat pada 1/12 dari 16MHz, persis seperti yang diharapkan dalam 8051 standar yang beroperasi pada 16MHz. Sejauh ini, saya belum menemukan cara mengubah frekuensi ini. Apa yang kita tahu sekarang mengungkapkan register
TL0
,
TH0
,
TL1
,
TH1
,
TMOD
,
TCON
! Kami sekarang memiliki pengatur waktu presisi yang berfungsi!
Saya tidak terlalu malas untuk memeriksa apakah timer 2 benar-benar diterapkan dalam standar 8.052 (lanjutan ke 8.051). Tidak, tidak.
Atau mungkin UART?
void uartInit(void) {
//
CLKEN |= 0x20;
//
P0FUNC |= (1 << 6) | (1 << 7);
P0DIR &=~ (1 << 6);
P0DIR |= (1 << 7);
//
UARTBRGH = 0x00;
UARTBRGL = 0x89;
UARTSTA = 0x12;
}
void uartTx(u8 ch) {
while (UARTSTA_1));
UARTSTA_1 = 0;
UARTBUF = ch;
}
Ada beberapa baris dalam modul OTA. Masuk akal jika mereka harus berhubungan dengan sesuatu, bukan? Mungkin port serial debug? Ini akan cocok dengan papan yang memiliki titik kunci "UTX" dan "URX". Kode ini sedikit berbelit-belit, tetapi sepertinya ia menyimpan byte dalam semacam buffer. Kode tersebut jelas terlihat seperti buffer ring standar. Saya melihat di mana buffer ini sedang dibaca. Ternyata berada di pawang untuk interupsi # 0. Oooh, menarik. Mungkinkah penangan interupsi UART? Kode tersebut tampaknya memeriksa bit # 1 di area yang menyerupai register status (register
98
), dan jika diset, ia membaca byte dari buffer ring kami dan menulisnya ke register
99
... Jika bit lain (# 0) disetel dalam register status yang disebutkan di atas, maka bit tersebut membaca register
99
dan memasukkan hasilnya ke ... buffer melingkar lain. Nah, ini sangat sejalan dengan apa yang saya harapkan dari penangan interupsi UART! Apa yang kita lakukan selanjutnya?
Setiap buffer melingkar memiliki dua pointer, satu untuk membaca dan satu lagi untuk menulis. Masuk akal bahwa mereka harus diinisialisasi sebelum buffer digunakan untuk apa pun. Jadi jika kami menemukan di mana indeks ini diinisialisasi, maka kami mungkin akan menemukan di mana UART dipasang, bukan? Jelas terlihat seperti ini. Dalam fungsi itu, yang menginisialisasi UART, kita melihat GPIO itu
P0.6
dan
P0.7
diatur dalam mode fungsi,
P0.7
diletakkan pada masukan, dan
P0.6
- pada keluaran. Dua register lagi:
9A
dan
9B
masing-masing ditulis dengan
0x00
dan
0x89
. Register yang, menurut versi saya, berfungsi dengan status (register
98
) ditulis sebagai
0x10
, dan kemudian bit 0 dan 1 di dalamnya dihapus. Kemudian
CLKEN
bit 5 diatur masuk , dan
IEN0
bit 0 diatur masuk Itu, pada prinsipnya, semua yang kita butuhkan!
Jadi kami beri nama register dan register menjadi . Kami tahu itu
99
UARTBUF
98
UARTSTA
UARTSTA
perlu disetel ke 0x10 agar blok ini berfungsi, dan kita tahu bahwa bit 0 berarti UART memiliki byte bebas di antrean TX FIFO, dan bit 1 berarti UART memiliki byte dalam antrean RX FIFO untuk kita. Kita tahu bahwa
CLKEN
bit 5 mengaktifkan jam untuk UART dan nomor interupsi 0 sesuai dengan penangan interupsi UART. Itu hanya harta karun informasi. Mengetahui hal ini, saya dapat membuat driver UART yang berfungsi dalam kode saya dan mengirim pesan keluar ke pin "UTX" yang diinginkan, yang, seperti yang kita ketahui sekarang, terletak di port 0 pin 6 (
P0.6
). Kami juga mempelajari bahwa titik kunci "URX" terhubung ke
P0.7
, dan ini adalah garis RX di UART. UART mengirimkan data pada 115.200 bps, 8n1, dan sama sekali tidak terpengaruh oleh register
CLKSPEED
... Jadi apa dua register misterius lainnya yang memberikan makna magis ini?
Saya mencoba mengotak-atik dua register yang tersisa,
9A
dan
9B
. Dengan cepat menjadi jelas untuk apa mereka. Ini adalah pembagi frekuensi. Saya telah memasukkan beberapa nilai untuk melihat bagaimana pengaruhnya terhadap baud rate. Ternyata itu sederhana.
9A
(selanjutnya disebut
UARTBRGL
) adalah byte rendah, dan
9B
(selanjutnya disebut
UARTBRGH
) adalah byte tinggi (4 bit atas tampaknya diabaikan). Tingkat baud dihitung dengan sederhana
16M / (UARTBRGH:UARTBRGL + 1)
. Ini dengan sempurna menjelaskan nilai-nilai yang tampak ajaib - mereka sesuai dengan 115.200 baud.
Rupanya, bug kecil terkait dengan fakta bahwa bit status dapat dihapus secara terprogram tanpa memengaruhi FIFO, jadi jika Anda secara tidak sengaja menghapus bit yang berarti "ada ruang kosong di TX FIFO" (
UARTSTA
.1), maka interupsi tidak akan pernah terjadi, dan bit akan tetap rendah.
Anehnya, lokasi ini cocok dengan alamat 8.051 yang benar
SCON
dan
SBUF
, yang merupakan register port serial 8.051. Bit 0, 1, dan 2
UARTSTA
benar-benar sesuai dengan deskripsi
SCON
dari 8051, tetapi di sanalah kesamaannya berakhir. UART dari 8051 membutuhkan bit 7 dan 6 untuk disetel
SCON
di 0 dan 1, hanya dengan cara ini akan menjadi UART normal. Chip ini dalam hal ini membutuhkan 0 dan 0. Selain itu, UART 8051 biasanya tidak memiliki pembagi baud, sebagai gantinya timer 1 digunakan.
Timer pengawas dan "lihat!"
Pada titik ini, batas eksekusi 1 detik yang dijamin oleh konfigurasi pengawas default mulai mengganggu saya. Saya memutuskan untuk mencari tahu di mana dan bagaimana pengawas dikonfigurasi. Biasanya, pengatur waktu pengawas dikonfigurasi sebagai bagian dari fungsinya sendiri, dan ukurannya kecil. Tentu saja, saya tidak akan mengatakan bahwa ini selalu terjadi, tetapi paling sering terlihat seperti ini. Saya memiliki beberapa kandidat, dan saya mencoba menyalin dari masing-masing secara bergiliran penulisan register ke dalam program pengujian saya, tetapi pengawas tidak memberi jalan. Saya perlu mengatur ulang chip dengan benar setiap detik.
Saat melakukan hal itu, saya melihat ada fungsi yang sangat aneh. Rupanya, dia membaca register di bawah nomor tersebut
FF
, menulis sesuatu di sana, lalu mengatur ulang
P1DIR
, menulis ke register lain, dan kemudian mengembalikan nilai asli di register
FF
. Keanehannya adalah ia mengatur SEMUA pin dari port 1 ke pin. Ini tidak masuk akal. Pada model lain, port 1 memiliki beberapa pin yang dikonfigurasi sebagai input. Selain itu, register seperti itu biasanya dioperasikan sedikit demi sedikit, menggunakan instruksi
anl
(logika AND) dan
orl
(logika OR). Tulisan yang kasar bagi seluruh register sekaligus tampak menjijikkan. Ada apa dengan register
FF
yang perlu dicadangkan dan dipulihkan? Itu terlihat sangat aneh!
Saya memutuskan untuk menyelidiki. Saat membuang nilai register ke konsol
FF
, ternyata nol, yang, tentu saja, tidak cocok untukku. Saya mencari seluruh firmware dan melihat bahwa hampir di mana-mana di dalamnya ada rekaman, kemudian cadangan, dan kemudian nilai aslinya dipulihkan. Saya juga memperhatikan bahwa menulis hampir selalu terjadi dengan nilai
0x04
dan jarang dengan
0x00
... Register ini hanya dapat dibaca selama pencadangan untuk pemulihan lebih lanjut; tidak ada tindakan lain yang dilakukan pada nilai ini. Fungsionalitas apa yang ditunjukkan ini? Pada dasarnya, begitulah biasanya kontrol perbankan memori bekerja! Jika Anda memiliki lebih banyak informasi daripada yang dapat Anda muat di ruang alamat Anda, Anda harus beralih. Pola akses ini (membuat cadangan sebelum mengubah dan kemudian memulihkan) adalah tipikal untuk situasi praktis seperti itu. Tapi apa yang bisa mereka simpan? Mungkinkah ini? Apakah orang-orang gila ini membebani ruang memori itu sendiri
SFR
?!
Saya menulis sebuah program yang dapat menampilkan semua nilai
SFR
, semua 128. Kemudian saya mengubah bitnya
0x04
menjadi
FF
SFR
dan sekali lagi mengambil semua ruang
SFR
. Kemudian program membungkus bit ini kembali dan kembali menampilkan semua nilai. Tuhan yang maha kuasa! Dan ada! Bit 2 di register
FF
benar-benar menghemat ruang
SFR
. Saya tidak ragu melihat bahwa ketika bit ini disetel, nilai yang muncul berubah. Rupanya, ini tidak memengaruhi SEMUA alamat
SFR
, tetapi banyak. Saya menamai register ini
CFGPAGE
.
Sekarang
CFGPAGE
saya pikir saya sudah beres, saya kembali ke fungsi misterius saya, yang memusatkan perhatian
P1DIR
. Sudah tahu bahwa itu TIDAK diatur ulang ke nol dalam kasus ini
P1DIR
, tetapi sepupunya yang aneh di halaman lain
SFR
, Saya mencoba menyalin kode ini ke dalam program saya. Percaya atau tidak, saya tidak sengaja menemukan kode yang menonaktifkan WDT !!!
Menyelidiki kode yang mengelilingi fungsi ini, karena biasanya fungsi terkait dalam biner terletak bersebelahan. Memang ada beberapa fungsi di dekatnya yang juga mengakses
CFGPAGE
dan mengakses alamat yang berdekatan
P1DIR
. Setelah beberapa jam coba-coba, saya sepenuhnya memahami detail cara kerja pengawas. Pada halaman ke-4 konfigurasi, alamat tersebut
BF
muncul untuk mengontrol pengaktifan dan penyetelan ulang pengatur waktu pengawas; bit paling signifikan dari register ini mengaktifkan atau menonaktifkan fungsi reset chip di pengatur waktu pengawas. Saya menamakannya
WDTCONF
. Alamat
BA
(yang ada
P1DIR
di halaman konfigurasi 0) adalah register pengawas aktif. Bit 0 di sini mengaktifkan atau menonaktifkan pengatur waktu pengawas itu sendiri. Saya menamakannya
WDTENA
.
Sampai di sini, saya masih memikirkan cara menjinakkan pengatur waktu pengawas. Butuh beberapa saat, tetapi pada akhirnya saya menemukan jawabannya. Sebuah register
BB
(sekarang dinamai
WDTPET
) dapat ditulis menjadi nol untuk menjinakkan timer pengawas. Butuh beberapa menit lagi untuk mencari tahu cara mengonfigurasi penundaan dalam pengatur waktu pengawas, karena jelas ada lubang di ruang alamat antara
BB
dan
BF
... Penghitung panjangnya 24 bit dan kelebihan beban saat dijinakkan. Itu tidak bisa dibaca. Nilai Reload disimpan di
WDTRSTVALH
:
WDTRSTVALM
:
WDTRSTVALL
, terletak di
BE
,
BD
,
BC
masing-masing, pada halaman konfigurasi 4. kontra jumlah UP pada frekuensi sekitar 62 kHz, dan meluap dipicu. Jadi, untuk memberikan penundaan yang meningkat, nilai yang lebih kecil harus ditulis ke register reset ini.
Kemungkinan yang lebih halus
Pemrograman memori flash
// irqs
voif flashDo(void) {
TRIGGER |= 8;
while (!(TCON2 & 0x08));
TCON2 &=~ 0x48;
SETTINGS &=~ 0x10;
}
void flashWrite(u8 pgNo, u16 ofst,
void *src, u16 len) {
u8 cfgPg, speed;
speed = CLKSPEED;
CLKSPEED = 0x21;
cfgPg = CFGPAGE;
CFGPAGE = 4;
SETTINGS = 0x18;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = ofst;
FWRDSTH = ofst >> 8;
FWRLENL = len - 1;
FWRLENH = (len - 1) >> 8;
FWRSRCL = (u8)src;
FWRSRCH = ((u16)src) >> 8;
flashDo();
CFGPAGE = cfgPg;
CLKSPEED = speed;
}
void flashRead(u8 pgNo, u16 ofst,
void __xdata *dst, u16 len) {
u8 pgNo, cfgPg, speed;
speed = CLKSPEED;
CLKSPEED = 0x21;
cfgPg = CFGPAGE;
CFGPAGE = 4;
SETTINGS = 0x8;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = (u8)dst;
FWRDSTH = ((u16)dst) >> 8;
FWRSRCL = ofst;
FWRSRCH = ofst >> 8;
FWRLENL = len - 1;
FWRLENH = (len - 1) >> 8;
flashDo();
CFGPAGE = cfgPg;
CLKSPEED = speed;
}
void flashErase(u8 pgNo) {
u8 __xdata dummy = 0xff;
u8 cfgPg, speed;
speed = CLKSPEED;
CLKSPEED = 0x21;
cfgPg = CFGPAGE;
CFGPAGE = 4;
SETTINGS |= 0x38;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = 0;
FWRDSTH = 0;
FWRLENL = 0;
FWRLENH = 0;
FWRSRCL = (u8)&dummy;
FWRSRCH = ((u16)&dummy) >> 8;
flashDo();
CFGPAGE = cfgPg;
CLKSPEED = speed;
}
Saya fokus pada gambar OTA karena lebih kecil dari firmware utama. Salah satu detail yang pasti dibutuhkan dalam gambar OTA adalah kemampuan menulis ke memori flash. Seperti apa bentuknya? Diasumsikan bahwa kita membutuhkan semacam fungsi yang akan menghapus flash, karena flashdisk dihapus dalam beberapa blok. Anda juga membutuhkan fungsi tulis yang dapat menulis satu halaman data atau lebih sedikit. Kami membutuhkan semacam verifikasi data yang direkam. Satu-satunya detail yang berbeda dalam penerapannya adalah bagaimana kami akan memasukkan data yang dimaksudkan untuk menulis ke pengontrol flash. Aku tidak tahu apa itu harus seperti, tapi sisanya adalah cukup mudah untuk menemukan. Verifikasi mungkin akan bermuara pada hanya menelepon
memcmp
atau siklus. Operasi penghapusan flash menghabiskan memori flash, jadi halaman harus diperiksa sebelum menghapus dan kemudian operasi dilakukan.
Mencari pemeriksaan pra-hapus, saya dengan cepat menemukan fungsi yang membuat area
0x400
byte-to
XRAM
-full dari byte
0xFF
. Kemudian area memori
CODE
dibandingkan dengan buffer ini, dan jika tidak sama, maka interupsi dinonaktifkan, dan beberapa disentuh
SFR
pada halaman konfigurasi 4. Ukuran halaman dalam memori flash jelas 1024 byte. Memeriksa tempat lain apa yang terpengaruh oleh hal yang sama
SFR
, kami menemukan kode flash yang tersisa. Jelas dari konteks apa register ini lakukan dan bagaimana caranya. Dalam hal ini, menarik bagaimana data diumpankan ke unit kontrol memori flash. Blok kontrol ini jelas berisi blok DMA. Sebuah alamat disuplai ke unit kontrol memori flash
XDATA
dan data diserap langsung dari sana. Keren banget!
Saat itu, saya belum yakin bagaimana cara membaca INFOBLOCK. Rupanya, kode OTA itu bukan urusannya, tetapi dari suatu tempat HARUS dibaca - lagipula, ada data di dalamnya. Saya memeriksa gambar utama dan melihat potongan kode yang mempengaruhi hal yang sama
SFR
dari memori flash, tetapi dengan cara yang berbeda. Dengan beberapa analisis lagi, saya dapat mereproduksi pembacaan INFOBLOCK yang benar. Sangat mengherankan bahwa metode yang sama dapat digunakan untuk membaca blok memori flash lainnya, tetapi tidak perlu melakukan ini, karena yang perlu Anda lakukan untuk membaca memori flash adalah membaca area memori
CODE
. INFOBLOCK hanya dapat diakses melalui unit kontrol memori flash. Untuk menulis dan membaca dari memori flash, blok kontrol menggunakan akses memori langsung (DMA) dan menulis ke
XDATA
.
Satu register
DF
(
FWRTHREE
) menentang upaya apa pun untuk menjelaskannya. Itu selalu memiliki rekor dengan nilainya
0x03
, Saya tidak tahu kenapa. Kode akses flash saya melakukan hal yang sama. Register
D8
(
FPGNO
) ditulis dengan nomor halaman flash. Halaman utama memori flash diberi nomor dari 0 hingga 63, INFOBLOCK memiliki nomor 128
DA
.:
D9
(
FWRSRCH
:)
FWRSRCL
adalah sumber blok DMA di blok kontrol memori flash. Untuk tulisan ke flashdisk berisi alamat
XDATA
dimana kita mencari data untuk menulis. Untuk membaca flash, offset byte pada halaman asli dicari, dan pembacaan dimulai pada offset tersebut.
DC
:
DB
(
FWRDSTH
:
FWRDSTL
) Apakah penetapan untuk DMA di blok manajemen memori flash. Untuk penulisan ke flash, ini akan berisi byte offset pada halaman tujuan, dan penulisan akan dimulai dari titik itu. Untuk membaca flash, alamat digunakan
XDATA
di mana data yang diterima selama membaca ditulis.
DE
:
DD
(
FWRLENH
:)
FWRLENL
Adalah panjang data yang harus ditransfer oleh blok DMA, minus satu.
Menulis ke memori flash seperti itu dipicu oleh pengaturan sedikit di satu lagi
SFR
. Berbagai bit di dalamnya juga disetel untuk mengontrol kode lain, yang tampaknya tidak terkait dengan memori flash, jadi saya menyimpulkan bahwa register ini mungkin akan memulai berbagai tindakan. Saya menamai register ini
D7
pada halaman konfigurasi 4
TRIGGER
. Status penyelesaian juga diperiksa di register yang tampaknya juga dibagikan oleh kode lain.
CF
Saya menamai register ini dari halaman konfigurasi 4
TCON2
, mengapa tidak? Ada juga register pada
C7
, juga digunakan sehubungan dengan kode lain, yang tampaknya mengkonfigurasi operasi mana yang akan dilakukan. Saya menamakannya
SETTINGS
.
0x30
ditulis padanya dengan logika ATAU untuk menghapus + menulis,
0x18
menulis flash,
0x08
membaca flash. Saya menduga bahwa bit
0x08
berarti "transfer data tertunda"
0x10
berarti "dalam sekejap", dan
0x20
"Menghapus". Ini masuk akal mengingat nilai apa yang kita lihat dan operasi apa yang dilakukan di sini.
Membaca dan menulis ke flash bekerja dengan sangat baik, tetapi menghapus tampaknya tidak berhasil. Alih-alih menghapus halaman dengan kode yang diberikan, karena alasan tertentu, halaman tempat kode yang meminta penghapusan berada akan terhapus sepanjang waktu. Jelas, masalah ini bukan pada kode yang terdapat di perangkat ini, saya melakukan sesuatu yang salah. Dicek, dicek, dan dicek lagi untuk memastikan kode saya cocok dengan kode pabrik. Cocok. Apa yang salah? Saya bekerja selama beberapa hari sampai saya menyadari bahwa kode pabrik bekerja pada 4MHz, dan kode pabrik saya pada 16MHz. Mungkinkah ini intinya? Ternyata begitu! Saya mengubah kode penghapusan flash saya untuk mempertahankan pembagi frekuensi saat ini dan memperlambat jam ke 4MHz selama durasi penghapusan flash. Tidak masalah karena kode ini sudah berjalan dengan interupsi dinonaktifkan.
Kehalusan lain dari unit kontrol memori flash ini adalah tampaknya unit ini tidak menyediakan operasi "hapus" sederhana. Saya berpikir untuk menetapkan if-bits yang sesuai di register
SETTINGS
, dan kemudian tampak logis bagi saya bahwa ketika diatur ke
0x20
atau
0x30
, penghapusan sederhana harus terjadi. Satu-satunya cara untuk menghapus ini adalah dengan melakukan operasi hapus + tulis, yang menulis setidaknya satu byte (karena tidak ada cara untuk merepresentasikan panjang nol dalam
FWRLENH
:.
FWRLENL
Untuk melakukan penghapusan sederhana, saya hanya meminta untuk menulis satu byte
0xFF
. Berhasil
SPI
Pada dasarnya semua driver SPI itu sama. Sebuah byte diterima di input, satu byte dikembalikan pada output. Tentu saja, beberapa memiliki DMA dan beberapa digerakkan oleh interupsi, tetapi 99% dari mereka dalam sistem kecil dikendalikan oleh perangkat lunak, dan di suatu tempat terdapat fungsi sederhana
u8 spiByte(u8 byte);
.
Masuk akal untuk melihat lebih jauh ke dalam SPI. Karena kami tahu bahwa ia
SSD1623L2
berkomunikasi dengan SPI, dan kami juga mengetahui detail pengaturan komunikasi tersebut, kami hanya perlu melihat kodenya dan mencari tahu bagian mana yang harus melakukan operasi ini. Sama seperti Sudoku, mengingat banyak hal yang sudah kita ketahui, pencarian ini tidak akan sulit. Melihat lembar data
SSD1623L2
kita melihat bahwa nomor register dari byte pertama yang dikirim ditulis dalam bit 1..6, dan bit "tulis" berada pada posisi # 7. Semua register panjangnya 24 bit. Adalah logis bahwa pemrogram akan menulis kode yang akan mengambil nomor register sebagai parameter, menggesernya ke kiri satu, mungkin logis-atau-masuk
0x80
, jika menulis diminta, dan kemudian mentransfer tiga byte. Tidak semua programmer bertindak secara logis, tetapi asumsi ini sangat membantu dalam rekayasa balik. Melihat kodenya, mudah untuk melihat fungsi yang terlihat seperti itu. Beberapa menambahkan
0x80
, beberapa tidak. Mereka semua menyebut fungsi misterius yang sama untuk setiap byte. Jadi, kami berasumsi bahwa beberapa teks tampilan di layar, beberapa dibaca. Mari kita tangani fungsi misterius itu sendiri.
Faktanya, semuanya semudah mengupas buah pir di sini. Ini beralih
CFGPAGE
ke 4, kemudian menulis
ED
nilai ke register
0x81
, menulis byte untuk dikirim ke
EE
, menulis
0xA0
ke
EC
, membuat penundaan 12 mikrodetik, menetapkan bit 3 ke
EB
, membaca byte yang diterima dari
EF
, menyimpan
0x80
ke
ED
. Itu saja. Bagaimana memahami semua ini? Seperti sebelumnya, mengandalkan apa yang sudah diketahui.
0x80
dan
0x81
berbeda hanya dalam satu bit, dan kami menyetelnya sebelum memulai operasi SPI, dan setelah pekerjaan selesai kami menyetel ulang, jadi ini, tampaknya, semacam bit "pengaktifan". Di sisi lain, artinya
0xA0
secara harfiah terdengar seperti konfigurasi tertentu. Daftarnya
EB
masih menjadi misteri. Tetapi, jika saya mereproduksi kode ini tanpa menuliskannya, semuanya akan berfungsi, jadi saya menyimpulkan bahwa tidak banyak yang bergantung pada register ini. Pasti
EE
ini
SPITX
dan
EF
ini
SPIRX
. Saya menelepon
ED
-
SPIENA
dan
EC
-
SPICFG
.
Itu tetap menjadi ciri khas apa yang dilakukan ketukan
SPICFG
... Saya melakukan sedikit coba-coba, dipersenjatai dengan penganalisis logika. Bit 7 harus disetel, bit 6 harus dihapus. Bit 5 memulai transmisi byte SPI dan membersihkan dirinya sendiri setelah selesai. Bits 3 dan 4 mengatur frekuensi clock, Anda dapat memilih dari nilai: 500KHz, 1MHz, 2MHz, 4MHz. 2 adalah bit konfigurasi standar
CPHA
untuk SPI, bit 1 adalah
CPOL
. Bit 0 tampaknya melanggar RX. Saya berasumsi dia dapat mengkonfigurasi blok untuk setengah dupleks (sejalan
MOSI
). Secara umum, ini tidak terlalu sulit.
Pin demi pin, segera temukan konfigurasi GPIO dan lihat apa
P0.0
ini
SCLK
,
P0.1
ini
MOSI
dan
P0.2
ini
MISO
... Dengan mencari di mana GPIO ini dikonfigurasi, kami juga melihat bagaimana bit
CLKEN
SPI dibutuhkan : itu bit 3. Hebat - kami sekarang memiliki SPI yang berfungsi!
Tentukan suhunya
volatile u8 __xdata mTempRet[2];
void TEMP_ISR(void) __interrupt (10)
{
uint8_t i;
i = CFGPAGE;
CFGPAGE = 4;
mTempRet[0] = TEMPRETH;
mTempRet[1] = TEMPRETL;
CFGPAGE = i;
IEN1 &=~ 0x10;
}
int16_t tempGet(void)
{
u16 temp, sum = 0;
u8 i;
CLKEN |= 0x80;
i = CFGPAGE;
CFGPAGE = 4;
TEMPCFG = 0x81;
TEMPCAL2 = 0x22;
TEMPCAL1 = 0x55;
TEMPCAL4 = 0;
TEMPCAL3 = 0;
TEMPCAL6 = 3;
TEMPCAL5 = 0xff;
TEMPCFG &=~ 0x08;
CFGPAGE = i;
IEN1 &=~ 0x10;
for (i = 0; i < 9; i++) {
//
IEN1 |= 0x10;
//
while (IEN1 & 0x10);
if (i) { //
sum += u8Bitswap(mTempRet[0]) << 2;
if (mTempRet[1] & 1)
sum += 2;
if (mTempRet[1] & 2)
sum += 1;
}
timerDelay(TICKS_PER_S / 1000);
}
//
CLKEN &=~ 0x80;
return sum / 8;
}
E-Ink menampilkan pembaruan secara berbeda berdasarkan suhu saat ini, jadi mengetahui suhu sekitar sangat penting untuk memperbaruinya dengan benar. Bentuk gelombang yang benar dipilih tergantung pada suhu. Di sini pengetahuan dari luar akan berguna. Jadi jika kita dapat menemukan di mana bentuk gelombang dimuat ke pengontrol tampilan, kita dapat menemukan di mana pilihan dibuat. Dari tempat ini Anda bisa berjalan langsung ke titik pengukuran suhu, bukan? Setelah melakukan ini, kita pergi ke satu fungsi, outputnya menentukan bentuk gelombang mana yang akan digunakan. Ini pasti dia! Ngomong-ngomong: biasanya sensor suhu dipasang ke ADC - hampir tidak ada yang membuatnya dalam versi terpisah. Tapi itu tidak masalah [belum].
Semuanya dimulai dengan mengatur bit 7 ke
CLKEN
dan diakhiri dengan pengaturan ulang, sehingga setidaknya kita tahu bahwa ini adalah cara kita menghidupkan dan mematikan sensor suhu (atau ADC). Fungsi ini beralih
CFGPAGE
ke 4, lalu menulis serangkaian nilai ke serangkaian register. Semua nilai konstan.
0x81
-> reg.
F7
,
0x22
-> reg.
E7
,
0x55
-> reg.
E6
,
0x00
-> reg.
FC
,
0x00
-> reg.
FB
,
0x03
-> reg.
FE
,
0xFF
-> reg.
FD
, kemudian bit-bit tersebut
0x81
dialirkan ke
F7
. Kemudian
CFGPAGE
pulih dan kemudian menghapus bit 4 di register
A1
. Ini sepertinya pengaturan awal. Setelah prosedur tertentu terjadi lima kali, hasil dari semua operasi kecuali yang pertama dirata-ratakan. Setelah itu, banyak perhitungan rata-rata dilakukan dengan cara ini, khususnya, menggunakan nilai dari INFOBLOCK - mungkin ini adalah nilai kalibrasi. Hasilnya kemudian dikembalikan. Mari kita lihat lebih dekat detailnya.
Dalam prosesnya, bit 4 di register hanya diatur
A1
, bit global ditetapkan dan kemudian dalam mode siaga aktif kami menghabiskan waktu sampai bit dihapus. Nilai rata-rata spesifik, tampaknya, diambil dari beberapa nilai global. Ini aneh ... Saya mencari di mana itu tertulis dan menemukannya di penangan interrupt # 10. Rupanya, begitulah bit 4 di register dihapus
A1
, kemudian beralih ke halaman konfigurasi 4 terjadi, nilai-nilai dibaca dari register
F8
dan
F9
, dan beberapa hal aneh dilakukan dengannya, dan kemudian nilai global ini ditulis . Tapi apa yang dilakukan dengan nilai-nilai ini?
Aku hanya di mata ditusuk konstanta
0x55
,
0xAA
,
0xCC
dan
0x33
... Apakah ini mungkin? Mungkinkah seseorang begitu blak-blakan sehingga ... ya, ya. Ini adalah konstanta untuk cara cerdas membalik urutan bit dalam satu byte. Rumit, tetapi hanya pada prosesor yang lebih canggih. Pada 8051, pendekatan ini sangat tidak efektif. Tapi kenapa? Tampaknya apa pun IP (penunjuk perintah) yang mereka lisensikan untuk mengukur suhu, itu menghasilkan hasil di mana bit berada dalam urutan terbalik. Mengapa masalah ini harus diselesaikan pada tingkat perangkat lunak dari chip berpemilik adalah pertanyaan besar. Lagi pula, membalik urutan bit dalam perangkat keras tidak lebih sulit daripada menyusun ulang beberapa kabel ... Apa fungsinya? Saya tidak tahu. Nyatanya, saya tidak pernah mendapatkannya.
Hampir tidak ada yang merancang penghitung perintah khusus untuk sensor suhu, benda ini hanya dicolokkan ke ADC. Setelah saya dapat menerapkan kembali kode ini dan memastikannya bekerja dengan sangat baik, saya mencoba mengubah semua register ini. Sebagian besar mempengaruhi penguatan sensor suhu, sebagian tidak berpengaruh. Jika ini ADC normal, kami mengharapkan beberapa bit untuk mengubahnya ke jenis input yang berbeda dan memberikan nilai yang sama sekali berbeda. Sayangnya, ini tidak terjadi. Itu benar-benar terlihat seperti sensor suhu normal. Ini juga dikonfirmasi karena register ini tidak disentuh di tempat lain. Aneh sekali, tapi oke ...
Karena hampir semua register ini ditulis hanya sekali, dan ini adalah nilai-nilai ini, dan mengubahnya mempengaruhi nilai yang diukur, saya memutuskan untuk memanggil mereka semua nilai kalibrasi suhu. Oleh karena itu, kami berkenalan dengan
TEMPCAL1
(reg.
E6
),
TEMPCAL2
(Reg.
E7
),
TEMPCAL3
(Reg.
FB
),
TEMPCAL4
(Reg.
FC
),
TEMPCAL5
(Reg.
FD
) And
TEMPCAL6
(reg.
FE
). Saya menamakannya karena digunakan beberapa kali dan tampaknya benar-benar mengelola pemuatan nilai kalibrasi. Hasilnya diterbitkan dalam (reg.
F7
TEMPCFG
TEMPRETH
F8
) dan
TEMPRETL
(reg.
F9
). Hasilnya adalah 10 bit panjangnya, disejajarkan dengan ujung atas register hasil 16-bit, dengan urutan bit terbalik.
Saya juga memperhatikan bahwa bit 3 in
TEMPCFG
diatur ketika sampel selesai dibuat. Anehnya, kode pabrik tidak memeriksanya, melainkan mengandalkan interupsi. Namun, pada kenyataannya, itu berguna dalam mendekode tujuan register
A1
. Seperti yang Anda lihat, klasik 8051 terbatas pada 7 sumber interupsi, karena kami memiliki 8 bit di register
IEN
dan bit 7 dicadangkan untuk mengaktifkan interupsi global. Jadi, bagaimana Anda mengelola interupsi bernomor 7 ke atas? Faktanya, ini seperti wild west, yang Anda inginkan adalah apa yang Anda lakukan. Tetapi di sini kami memiliki perangkat keras yang memicu interupsi nomor 10, dan menggunakan sedikit, kami dapat mengetahui kapan itu selesai. Ini bagus untuk bereksperimen. di mana kami ingin tahu bagaimana interupsi di atas 7 diaktifkan dan dinonaktifkan. Itu hanya perlu untuk mengutak-atik kode ini sampai Anda menyingkirkan interupsi, tetapi sampel dibuat . Pencarian tidak memakan waktu lama. Pasti itu
A1
! Saya menamainya
IEN1
... Saya tidak yakin apa fungsi bit 0 di sini, tetapi bit 1 dan di atasnya mengontrol aktivasi interupsi nomor 7 ke atas. Saya bisa mengkonfirmasi ini nanti. Oke, selesai - kami telah mendokumentasikan perangkat lain, sehingga menemukan lebih banyak keanehan ...
I2C
Pada tahap ini, saya membuka label harga e-Ink yang lebih besar yang dilengkapi dengan chip yang sama. Itu adalah model 2,9 inci dengan tampilan grafis e-ink dan NFC !!! Sekali lagi, pengetahuan pihak ketiga berguna di sini. Sebagian besar perangkat NFC akan memberi tahu Anda dengan tepat jika Anda memintanya dengan sopan. Ini adalah hal yang baik, karena chip NFC di papan terlalu kecil untuk diberi label dengan benar. Setelah memindai menggunakan NFC dan memeriksa ID perangkat, kami menemukan bahwa itu adalah NXP NT3H1101 (salinan diarsipkan di sini untuk anak cucu). Dari halaman yang sangat nyaman ini Anda dapat mengunduh lembar data - dan segera menjadi jelas bagaimana komunikasi dengan chip ini harus dilanjutkan. Informasi yang berguna! (Semua informasi berguna di sini). Satu-satunya hal yang mengganggu adalah bahwa alamat I2C perangkat ini tidak tetap, tetapi dapat diatur ke nilai apa pun; namun, nilai default disediakan. Alfabet rekayasa terbalik: dalam 99,9% kasus, nilai default tidak berubah. Saya yakin alamat I2C default juga tidak berubah!
Menemukan analog biner untuk
0x55
cukup mudah - nilai ini tidak begitu umum. Rupanya, semuanya dilakukan sebelum panggilan ke salah satu dari dua fungsi tersebut. Masuk akal bahwa mereka harus dihubungkan ke I2C. Selain itu, dalam semua kasus, sebelum panggilan ini, bit 4 sudah disetel
CLKEN
yang kemudian dibuang. Kami sekarang tahu bahwa I2C diaktifkan melalui bit ini. Mari kita lihat apa fungsi fungsi-fungsi ini. Beberapa menyalin data dari parameter yang disediakan di awal, beberapa melakukannya di akhir. Di tengah mereka semua menulis beberapa hal global, mengatur bit global, menghapus bit 4, dan mengatur bit 5 di register
95
dan menunggu sampai dihapus. Hmm, berfungsi seperti sensor suhu. Rupanya bit 2 di
IEN1
aktifkan interupsi.
Mari kita lihat di mana pengendali interupsi yang mempengaruhi nilai-nilai global ini berada. Memang, nomor interupsinya adalah 8, seperti yang diharapkan. Ini set
CFGPAGE
ke 0 dan kemudian membaca register
91
... 3 bit yang paling tidak signifikan diabaikan, dan bit yang tersisa digunakan dalam kasus sakelar untuk memutuskan apa yang harus dilakukan. Kode ini ternyata sedikit membingungkan, jadi saya memutuskan untuk bereksperimen. Memasang penganalisis logika ke garis menuju ke chip NFC dan dengan cepat menemukan di mana
SDA
dan di mana
SCL
. Caranya mudah karena sudah ada datasheet untuk chip ini.
Tampaknya menghapus bit 4 di register
95
tidak akan mempengaruhi apa pun, tetapi pengaturan bit 5 menyebabkan kondisi START di bus menjadi benar. Interupsi dipicu. Jika Anda melakukan hal yang sama menggunakan penangan bawaan dan membaca 5 bit paling signifikan di register
91
, kita melihat bahwa mereka memiliki nilai
0x08
... Byte alamat kemudian disimpan dengan
94
bit R / W (baca / tulis) di register , dan bit 3 di register dihapus
95
. Perlu juga dicatat bahwa SEMUA jalur melalui penangan interupsi ini menghasilkan bit 3 yang dihapus di register
95
. Saya rasa ini adalah "bagian yang perlu dihentikan". Saya belum menemukan jawabannya, tapi kita sudah bisa menamai beberapa register. Tampaknya semua register I2C ada di halaman config 0.
Saya akan menelepon karena I2C yang ada di dalamnya dan tidak pernah dibaca karena alasan lain. Saya belum pernah melihat perubahan tiga bit paling tidak signifikan atau digunakan dengan cara apa pun. - jadi saya akan menelepon
91
I2CSTATE
I2CBUF
94
, karena data dipompa melaluinya di sepanjang conveyor, dan
95
di masa depan akan dinamai
I2CCTL
, karena untuk melakukan sesuatu, perlu dituliskan sesuatu ke dalamnya.
Kami menggali lebih jauh dan menemukan bahwa ketika byte alamat dikirim, salah satu dari empat nilai status dapat diperoleh. Jika byte alamat yang kami kirimkan membutuhkan akses tulis, maka statusnya adalah
0x18
jika itu diakui (ACK), dan
0x20
jika tidak. Jika byte alamat yang kami kirimkan membutuhkan akses baca, maka statusnya adalah
0x40
jika itu diakui (ACK), dan
0x48
jika tidak. Penanganan NAK (byte tidak diakui) cukup mudah. Saat bit 5 disetel ke
I2CCTL
kondisi STOP di bus benar.
Mengirim data dalam mode tulis itu mudah. Byte hanya ditulis ke
I2CBUF
. Jika byte terkirim diakui (ACK), maka status akan menjadi,
0x28
dan jika tidak, maka
0x30
. Untuk memprovokasi restart, atur bit 4 ke
I2CCTL
- itu berhasil. Saat eksekusi perintah RESTART di bus selesai, status menjadi
0x10
.
Jika kita ingin membaca informasinya, maka, setelah mengirim bit restart dan byte alamat dalam mode baca, segera setelah kita melihat statusnya
0x40
, kita dapat memutuskan bagaimana menanggapi byte berikutnya yang kita terima - ACK atau NAK. Untuk mengakuinya (ACK), setel bit 2 ke
I2CCTL
, dan agar tidak mengkonfirmasi (NAK) - kami menghapus sedikit ini. Dengan kembalinya handler, byte akan diterima. Ketika ini selesai, kita akan melihat status
0x50
jika byte telah dikonfirmasi, dan
0x58
jika belum dikonfirmasi. Dengan satu atau lain cara,
I2CBUF
byte yang diterima akan dimuat di.
Setelah meninjau kode inisialisasi dan mengutak-atik salinan kami, kami menemukan bahwa bit 7 dalam
I2CCTL
kontrol apakah perangkat periferal akan memicu interupsi. Jika tidak, maka register ini diinisialisasi ke
0x43
... Saya berasumsi ini adalah bagaimana blok dikonfigurasi untuk beroperasi dalam mode master. Karena saya tidak memiliki kode sampel untuk mode budak, saya tidak menyelidiki pertanyaan ini lebih lanjut, tetapi saya yakin bahwa mode budak didukung. Bisa dilakukan, tapi saya malas :).
Register
96
juga mencatat informasi dalam waktu inisialisasi, dan kemudian tidak lagi berubah. Ini berkorelasi dengan baik dengan satu bit informasi yang masih kami kurang - menunjukkan bagaimana kecepatan clock diatur. Setelah bereksperimen dengan register ini (yang sekarang disebut
I2CSPEED
), kami melihat bahwa ia memiliki interdependensi yang kompleks dengan frekuensi clock, tetapi setelah beberapa lusin upaya saya sampai pada yang berikut: di
rate = 16MHz / ((dividerB ? 10 * (1 + dividerB) : 12) << dividerA)
mana pembagi A adalah tiga bit paling signifikan
I2CSPEED
dan pembagiB adalah 4. Bit yang paling signifikan tampaknya tidak digunakan.
Fakta bahwa penyetelan GPIO awal terjadi di dekat titik inisialisasi periferal tampaknya menyiratkan bahwa pin
P1.4
dan penting dalam kasus ini
P1.5
.
Semuanya bekerja, tetapi ada satu rahasia. Ketika interupsi untuk blok ini diaktifkan (c
IEN1
), bit 2 juga diset di register
A2
. Karena
IEN1
terletak di alamat
A1
, saya curiga ada hubungannya dengan interupsi. Saya masih belum tahu persis apa fungsinya, dan tidak ada kode selain kode pengaturan awal I2C yang menggunakannya. Saya sebelumnya menamakannya
I2CUNKNOWN
meskipun lebih mungkin terkait interupsi daripada terkait I2C. Bagaimanapun, kode saya sekarang dapat melakukan transaksi I2C sebagai master!
Deteksi perubahan pin
Firmware label harga dibangunkan saat dipindai oleh perangkat berkemampuan NFC. Chip NFC onboard memiliki pin "deteksi medan" yang terhubung ke mikrokontroler utama. Kebetulan? Tidakberpikir! Harus ada cara untuk mendeteksi perubahan pada pin. Ia bahkan membangunkan chip dari mode tidur (hemat daya). Selain itu, perlu beberapa waktu untuk menggambar dengan tinta elektronik, dan selama penantian ini, chip mungkin akan terus tidur. Layar akan memberi tanda akhir gambar dengan mengubah sinyal "BUSY". Jadi ... kami memiliki dua kasus di mana CPU harus mendeteksi perubahan pada pin dan, kemungkinan besar, kami tidak berbicara tentang siklus tunggu yang aktif. Sulit untuk menemukan kasus pertama yang dijelaskan - saya masih tidak tahu persis di mana kode hibernasi ini. Kasus kedua, sebaliknya, sangat mudah ditemukan - maksud saya, kode untuk menggambar di layar mudah ditemukan. Sekali lagi, membangun pengetahuan yang ada sangat membantu di sini. Saya tahu,tim mana yang bertanggung jawab untuk "menyegarkan layar" pada hampir semua chip tampilan e-ink yang ada. Saya baru saja memasukinya dan melihat apa yang akan terjadi. Ada banyak kode, banyak yang tersentuh
SFR
... Saya mulai bereksperimen dengan beberapa yang saya lihat. Membuat beberapa tebakan: Semua pin harus dapat memicu deteksi perubahan. Ini tidak selalu terjadi, tetapi tebakan yang cerdas biasanya diambil. Saya berasumsi bahwa register konfigurasi apa pun yang kita bicarakan, mereka akan berurutan dan bekerja dengan tiga port. Saya juga berasumsi bahwa mengubah pin harus memberikan interupsi, dan tidak hanya membangunkan perangkat. Masuk akal bahwa jumlah register konfigurasi cukup dapat diprediksi. Untuk setiap pin, kita membutuhkan ENABLE, STATUS dan, kemungkinan besar, DIRECTION. Selain itu, register yang terkait dengan deteksi perubahan GPIO kemungkinan besar berada di sekitar register konfigurasi GPIO lainnya.
Berdasarkan ini, saya melakukan beberapa eksperimen, karena saya dapat dengan mudah mengganti setidaknya beberapa pin (misalnya, TEST). Juga luangkan waktu untuk melihat bagaimana peta saya saat ini berkembang
SFR
. Saya tidak lupa untuk melihat register
BC
,
BD
dan
BE
pada halaman konfigurasi 0. Beberapa percobaan telah menunjukkan bahwa mereka mengontrol pullup setiap pin. Benar, saya belum pernah melihat konfigurasi yang memungkinkan "menarik pin ke bawah". Saya menamai mereka
PxPULL
.
Setelah beberapa percobaan, menjadi jelas bahwa ada tiga register per port, dan mereka mengontrol interupsi saat pin berubah.
PxLVLSEL
(
A3
,
A4
,
A4
) memilih level yang diinginkan (0 = tinggi, 1 = rendah).
PxINTEN
(
A6
,
A7
,
A9
) Menyediakan perubahan pin pelacakan di tingkat hardware.
PxCHSTA
(
AA
,
AB
,
AC
) Toko status deteksi (bit set = sesuatu telah berubah). Eksperimen lain menunjukkan bahwa nomor interupsi saat mengganti pin adalah 11. Bekerja dengan baik, dan saya bahkan berhasil membangunkan chip dari mode hemat daya (lebih lanjut tentang ini di bawah).
DPTR Kedua
Mendaftarkan
84
dan
85
menyimpan secara misterius di tengah semua transaksi swap
CFGPAGE
dan menyimpan semua 8 bit yang tersimpan di dalamnya. Dalam banyak varian 8051, di sinilah seharusnya register kedua
DPTR
. Namun, jika demikian, bagaimana Anda beralih ke sana? Setiap orang melakukannya secara berbeda. Saya memutuskan untuk mencobanya. Menulis program di assembler untuk membalikkan setiap bit di setiap register secara bergantian dan memeriksa apakah penulisan integer
DPTR
(instruksi khusus) cocok dengan pembacaan berikutnya
DPL
dan
DPH
(akses normal ke
SFR
). Dapat diprediksi bahwa banyak dari hal-hal ini tidak dapat dialihkan dengan mudah tanpa merusak program. Tapi, setelah berlatih dengan hati-hati melompati satu atau yang lain, saya mengisolasi bit 0 masuk
92
. Ya, ya ... Itulah yang dia lakukan. Seperti pada banyak 8051-an, saya menamai register ini
DPS
, yang berarti "pemilihan penunjuk data". Register
84
dan
85
saya beri nama, secara alami,
DPL1
dan
DPH1
.
Eksperimen lainnya.
Beberapa eksperimen telah menunjukkan bahwa dua bit paling tidak signifikan dalam
PCON
(siaga dan tidur) bekerja seperti yang diharapkan dalam mode tidur untuk 8051 (meskipun mode tidur dalam mode hemat energi dapat dikonfigurasi juga). Saya juga mencatat bahwa pengaturan bit 4 dinonaktifkan
XRAM
. Ini menghemat lebih banyak energi dalam mode tidur!
Register dalam kisaran
B2
.. menarik
B6
. Mereka tampak berbeda-beda tergantung pada instruksi yang diikuti di lokasi mereka. Setelah mempertimbangkan semuanya dengan cermat, saya menyadari bahwa
B4
:
B5
selalu up-to-date
PC
!!! Mengapa seseorang mungkin membutuhkannya - saya tidak tahu. Memberi nama mereka
PCH
dan
PCL
... Mereka hanya bisa dibaca. Tetapi bagaimana dengan register lain dalam kisaran ini?
B2
dan
B3
tampaknya terkait dengan lompatan bersyarat. Pada lompat jauh (misalnya, saat berlari
ljmp
,
lcall
atau
ret
), mereka sepertinya menyimpan tujuan lompatan. Dengan transisi pendek (seperti
sjmp
),
B2
tampaknya perpindahan. Hal-hal aneh, tapi tidak berguna, jadi saya tidak membahasnya lebih jauh. Saya menamai sisa register
PERFMONx
.
Tidur dalam mode hemat energi
Manusia adalah manusia, dan tidak ada manusia yang asing bagi mereka. Orang menyukai angka bulat. Saya suka akurasi, bahkan jika saya tidak membutuhkannya. Ini sangat membantu dengan rekayasa balik. Misalnya, bagaimana Anda menanggapi sebuah konstanta
0x380000
? Tidak ada? Mungkin. Bagaimana dengan
0x36EE80
? Mata sudah menempel padanya. Apa maksudnya itu? Terjemahkan ke dalam sistem desimal dan Anda akan melihat: 3.600.000. Nah , ini adalah satu jam, dinyatakan dalam milidetik. Nilai ini mungkin berguna, mungkin, hanya dalam kasus tidur lama dalam mode hemat energi. Saya lelah menghitung berapa banyak hal yang saya "rekayasa balik" dengan mengandalkan konstanta semacam ini yang menjelaskan di mana impian itu terwujud!
Berikut adalah konstanta pada perangkat ini yang diteruskan ke fungsi yang menarik bagi saya: 1 5000 2 000 5 000 10000 3 600 000 1 800 000 0xffffffff. Dapat dimengerti bahwa ini adalah indikasi durasi dalam milidetik. Yang terakhir ini mungkin merupakan rintisan untuk "selamanya atau hampir selamanya".
Hampir tidak ada kesempatan untuk memahami apa yang dilakukan sebagian besar register di sini, karena register digunakan oleh kode hampir secara eksklusif dalam mode tidur. Beberapa di dalam
SFR
dan beberapa di luar angkasa
MMIO
... Saya bisa menyalin kode dan mereproduksinya. Secara khusus, saya tertarik bahwa pengatur waktu tidur dapat bekerja pada dua kecepatan: dengan frekuensi 32KHz dan 1Hz. Ini adalah pengatur waktu 24-bit, dengan waktu tidur sesingkat mungkin berlangsung sekitar 30 ms, dan yang terpanjang dapat bertahan sekitar 194 hari! Baca lebih lanjut di SDK.
Radio
Radio biasanya membutuhkan konfigurasi yang ekstensif, sehingga
SFR
terlalu ramai di tempat yang padat . Kebanyakan 8.051 yang dilengkapi dengan radio digunakan untuk mengatasi masalah ini
MMIO
. I / O yang dipetakan memori di 8051 biasanya hanya dipetakan ke ruang alamat
XRAM
. Melihat kode secara diagonal, saya menyadari bahwa radio di chip ini masuk
MMIO:df00 β MMIO:dfff
.
Jalur RX
Sekali lagi, saya memutuskan untuk memulai dengan gambar OTA. Ini cukup kecil untuk menyederhanakan analisis. Segera menjadi jelas bahwa image OTA tidak mengirim paket radio apa pun, tetapi hanya menerimanya (pemberitahuan secara otomatis dikirim ke tingkat perangkat keras, yang khas untuk sebagian besar chip ZigBee). Tapi itu bagus! Berkat ini, cukup bagi kami untuk menganalisis hanya setengah dari pengemudi, yang berarti tugasnya dua kali sesederhana mungkin!
Ketika saya mulai mencari dari mana kode OTA mendapatkan datanya, sepertinya ada antrean buffer. Apa itu: Ini adalah antrian yang berisi byte individu, yang masing-masing merupakan penunjuk ke daftar buffer. Kode yang tampaknya menerima paket dan memproses paket yang diterima mengambil buffer dari antrian, memprosesnya, dan kemudian meletakkannya di antrian lain. Skema yang sangat sederhana. Satu antrian menyimpan buffer penuh dengan data yang diterima, antrian lain menyimpan buffer kosong yang siap menerima data baru yang diterima. Cukup jelas.
Melihat-lihat sedikit, kami dengan cepat menemukan di mana antrian diakses dengan cara yang berbeda: menghapus buffer dari antrian "kosong" dan mengantrekan yang penuh. Ini adalah pawang untuk interupsi # 5! Penangan interupsi itu sendiri cukup sederhana, asalkan bitnya disetel
TCON2.2
, disimpan
0xC6
di
MMIO:df48
, dequeue buffer, menyalin byte ke dalamnya dan meletakkannya di antrian lain. Tapi dari mana dia menyalin byte itu? Dari mana Anda mendapatkan panjang salinannya? Keduanya diambil dari buffer
XRAM
di mana dia tidak menulis! Saya tidak pernah bisa mengungkap misteri ini.
Pencarian tidak berakhir di situ. Interrupt 4 memainkan peran kunci, Handlernya ternyata lebih sederhana. Dia menguji bit 5 in
MMIO:dfad
(saya akan menyebutnya
RADIO_IRQ4_pending
dan, jika disetel, ia memanggil prosedur yang tidak dipanggil di tempat lain. Prosedur ini membaca , memeriksa bahwa nilai di dalamnya kurang dari atau sama dengan 128, membaca , memeriksa bahwa dengan peningkatan satu, itu akan menjadi sama dengan nilai sebelumnya. Jika salah satu di atas tidak terpenuhi, maka disimpan di , jika halaman konfigurasi 4 terpilih, nilai membaca pertama disimpan dalam sebuah variabel global, yang selanjutnya dilambangkan panjang. Salah satu nilai minus ini bertahan , dan pointer ke buffer dari mana data selanjutnya disalin disimpan dalam : . Kemudian bit 2 dipasang .
SFR
FA
MMIO:df98
0xC6
MMIO:df48
D5
D4
D3
TRIGGER
Di sini, sekali lagi, mengetahui konteks membantu. 127 adalah nilai maksimum yang dapat dimiliki paket 802.15.4 yang valid, dan panjang ini mencakup pemeriksaan redundansi siklik 2-byte (CRC), tetapi tidak termasuk panjang byte itu sendiri. Oleh karena itu, tebakan saya adalah bahwa
FA
ini adalah panjang yang dihasilkan (dengan mempertimbangkan panjang byte dan CRC). Saya menamakannya
RADIO_GOTLEN
. Dalam kasus seperti itu, masuk akal bahwa
MMIO:df48
(sekarang dinamai
RADIO_rxFirstByte
) bisa menjadi byte pertama yang diterima (byte panjang). Dengan semua register yang tersisa jelas:
D5
itu adalah panjang DMA untuk RX DMA (sekarang disebut
RADIO_RXLEN
)
D4
:
D3
itu dibongkar menjadi bagian-bagian penunjuk ke RX DMA tujuan (
RADIO_RXPTRH
dan
RADIO_RXPTRL
masing-masing).
Kemudian semuanya berhasil. Interupsi nomor 4 dipicu segera setelah radio menerima paket ke buffer internal. Bit 5 yang disetel ke
RADIO_IRQ4_pending
(sekarang disebut
RADIO_IRQ4_pending
) memberi tahu kita bahwa ini telah terjadi. Kami melanjutkan dengan pemeriksaan awal paket (memastikan panjangnya dalam batas yang wajar), dan kemudian kami menjalankan DMA dari buffer internal ke
XRAM
, jika semuanya baik-baik saja. Jika tidak, maka kita menulis
0xC6
di
MMIO:df48
. Logikanya, ini dapat dibandingkan dengan "mengosongkan RX FIFO", maka register ini sekarang disebut
RADIO_command
. Jika semuanya baik-baik saja dengan paket dan operasi DMA selesai, maka bit 2 diatur masuk
TCON2
, dan interupsi 5. Di sini, sekali lagi, kita menulis "mengosongkan RX FIFO" ke
RADIO_command
. Ini berguna karena kami telah menarik data menggunakan metode DMA. Kemudian data disalin dan pekerjaan selesai!
Di sebagian besar radio, kode redundansi siklik yang diterima tidak tersedia di lapisan yang lebih tinggi - kode ini hanya diperiksa dan dikembalikan dengan bit status tunggal dengan nilai ya atau tidak. Seperti biasa, disarankan untuk berasumsi bahwa semuanya bekerja "normal". Anda periksa - ini benar-benar biasa. Kebanyakan radio ZigBee melaporkan dalam dua byte ini LQI (Indikator Kualitas Radio Link) dan RSSI (Indikator Kekuatan Sinyal yang Diterima) daripada CRC. Dalam model ini, radio bekerja dengan cara yang hampir sama. Hampir. Ternyata byte pertama selalu
0xD0
tetapi yang kedua tampaknya benar-benar berisi LQI (dalam 7 bit paling tidak signifikan) dan status CRC (dalam bit 7). Faktanya, ini secara fungsional sangat mirip dengan cara kerja radio Chipcon. Perintah ini
0xC6
juga berarti "RX FIFO kosong" untuk radio chipcon (sekarang TI)! Banyak hal lain yang tidak sama, tetapi perintahnya BERBUKA , dan ini membantu saya menavigasi elemen lain dari tumpukan radio ini!
Lebih lanjut tentang radio
Jika Anda mempertimbangkan bagaimana kode OTA memulai radio, Anda dapat melihat bahwa LOTS register hanya terpengaruh sekali, beberapa nilai tertulis di dalamnya, yang tampaknya benar-benar acak. Kemungkinan besar, banyak dari mereka yang terukur. Setiap register yang ditulis satu kali (atau berulang kali, tetapi nilai yang sama dimasukkan) adalah register kalibrasi. Saya akan melewatkan detail membosankan dari register yang terlibat, tetapi saya akan berbicara tentang kode inisiasi kerja yang ada di SDK.
Di sini, sekali lagi, kami mengamati berapa banyak nilai yang ditulis ke register
RADIO_command
... Nilai yang terekam cocok dengan nilai yang kami harapkan jika kami bekerja dengan nilai perintah chipcon, meskipun kami dapat melihat beberapa nilai yang tidak ada di modul radio chipcon. Jadi, apakah radio ini adalah keparat chipcon langka, atau keduanya merupakan keturunan dari nenek moyang yang sama. Bagaimanapun, situasi ini membantu untuk memahami beberapa perintah lagi yang dikeluarkan oleh mereka.
Mereproduksi kode inisiasi dan menulis penangan interupsi, seperti yang ada di dalam chip, memberi kita biner yang berfungsi yang dapat berfungsi untuk penerimaan dan kondusif untuk eksperimen. Memperhatikan beberapa register lagi yang ditulis oleh firmware utama, saya segera menentukan bahwa
MMIO:df88 β MMIO:df8f
ini adalah "alamat MAC saya yang panjang", yang akan digunakan di tingkat perangkat keras untuk memfilter paket yang masuk. Demikian pula,
MMIO:df90 β MMIO:df91
mengatur "ID PAN sendiri" untuk filter RX. Sebuah
MMIO:df92 β MMIO:df93
set "alamat pendek sendiri". Peralatan ini akan menerima dan mengakui (ACK) setiap paket yang dikirim ke alamat broadcast kami.
MMIO:dfc0
mengatur saluran radio dalam penomoran 802.15.4 (11..26) standar.
Karena radio akan menerima paket, saya juga dapat menemukan bahwa
MMIO:dfc9
kekuatan pancar disesuaikan saat menyesuaikan . Saya pikir ini tentang register yang mengatur kekuatan TX. Saya juga memperhatikan bahwa ketika saluran diatur di firmware pabrik utama, dua register lagi ditulis dengan nilai per saluran. Hanya ada satu register seperti itu di firmware OTA. Yang terkait dengan RX disebut
MMIO:dfcb
, dan yang terkait dengan TX disebut
MMIO:dffd
... Cukup mudah untuk ditiru dan dipahami. Maka inilah saatnya mencari tahu TX!
Ayo kirim beberapa byte!
Setelah mendekripsi jalur data, saya mentransfer fungsi dan mendaftarkan nama ke gambar master saya yang telah dibongkar. Melihat apa yang masih tidak ditandai, kita dapat melihat di mana letak jalur TX. Memang, ada dua antrean buffer lagi di sini: satu penuh dengan buffer TX kosong yang siap digunakan, dan yang lainnya penuh dengan buffer TX "habis" yang siap dikirim. Saya menemukan fungsi transfer dengan sangat cepat.
Pada 802.15.4, adalah kebiasaan untuk mendengarkan saluran radio sebelum melakukan transmisi. Operasi ini disebut CCA (Channel Idle Assessment). Sebelum kita melakukan apa pun dengan data yang akan kita kirim, pertimbangkan loop yang berbunyi
MMIO:df98
dan memeriksa bit 0. Jika disetel, maka fungsinya gagal, dan pengatur waktu disetel untuk mencoba lagi. Saya pikir ini adalah jalur CCA. Jika kami melihat nol di bit ini 128 kali, maka kami menganggap saluran itu gratis.
Fungsi transfer itu sendiri ternyata sangat sederhana: Anda memilih halaman konfigurasi 4, panjang yang diinginkan (tidak termasuk panjang byte atau CRC), dan semuanya ditulis
CD
. Sebuah pointer ke buffer di
XRAM
ditulis dalam
CA
:
C9
. Buffer dimulai dengan byte panjang.
RADIO_command
sarat dengan nilai
0xCB
. Tidak ada perintah seperti itu di radio chipcon, tapi saya rasa itu berarti "muat TX FIFO". Kemudian bit 1 diatur
TRIGGER
... Saya kira ini adalah bagaimana akses DMA ke antrian radio TX FIFO internal dimulai. Kemudian
MMIO:dfc8
setel ke
0xFF
, 255 percobaan dilakukan untuk menunggu TX berakhir, memeriksa bahwa bit 7 in
MMIO:df9b
(sekarang dipanggil
RADIO_curRfState
) telah disetel. Kemudian, setelah penundaan singkat, ini
MMIO:dfc8
disetel ke
0x7F
. Anehnya, saya tidak tahu mengapa itu direkam
MMIO:dfc8
. Dalam kode saya, saya mencoba melakukannya tanpa itu dan semuanya bekerja dengan baik.
Ekor
Setelah sedikit bereksperimen, saya menemukan beberapa trik yang tidak dapat dilakukan oleh firmware pabrik. Bit 6 di
RADIO_IRQ4_pending
set setelah kita "TX" paket dan penundaan pengakuan (ACK) kedaluwarsa. Jika kita benar-benar menerima ACK, maka bit 4 juga akan diset, oleh karena itu mudah untuk menentukan (1) kapan kita benar-benar mengirim paket dan (2) apakah kita menerima ACK. Keren!
Juga, jika bit 4 in
RADIO_IRQ4_pending
disetel dan bit 5 in
RADIO_curRfState
tidak ditempati, ini berarti kami sedang dalam proses menerima paket. Kami perlu memilih RSSI secara manual, yang kami baca
MMIO:df84
(sekarang
RADIO_currentRSSI
). Ini memiliki offset sekitar 56 dBm.
Saya juga memperhatikan bahwa bit 1 in
TCON2
ditetapkan setelah menyelesaikan TX DMA (tetapi belum tentu proses TX itu sendiri). Bit 0 in
TCON2
disetel saat inisialisasi radio berakhir.
Misteri yang belum terpecahkan
ADC / Pengukuran Baterai dan Mesin Enkripsi AES
Masuk akal bahwa harus ada cara untuk mengukur voltase baterai, tetapi saya belum menemukan jejak kode serupa. Tanpa kode yang menggunakan ADC dengan cara ini, kemungkinan menemukan metode menghilang ini sangat kecil. Pada prinsipnya, blok AES sama dengan ADC. Saya tahu ada blok akselerasi AES di dalam chip (diperlukan untuk ZigBee). Tetapi karena kode sebenarnya tidak menggunakannya, saya tidak melihat cara untuk menemukannya.
bermacam-macam
Hal-hal yang tidak dapat kami temukan, tetapi tidak terlalu kami pedulikan, karena kami tidak dapat membeli chip ini: Kontroler LED IR, unit PWM, DAC. Saya akan menyerahkan hal-hal ini kepada pembaca untuk dilakukan sendiri.
ZBS242 / 3 Pinout, Fitur, SFR, Unduhan
Unduh ZBS24x SDK .
- Sel berbayang menunjukkan register beralamat bitwise
- Register berbayang secara diagonal yang tidak disimpan di bank
CFGPAGE
- Register berbayang vertikal, yang, tampaknya, tidak muncul di halaman mana pun sama sekali.
- Sel kosong adalah register yang tidak diketahui
- Nama register RADIO dimulai dengan huruf "r"
Pelajaran untuk reverse engineer pemula
- Bacalah materi setidaknya selama beberapa jam atau hari sebelum mulai bekerja.
- - . -, .
- . SPI , I2C , . .
- , β ( ).
- : , . , , .
- , , .
- . - , - . , .
- - . , , .
- , , . , , .
- - . .
- - , , - .
Server cloud dari Macleod sangat bagus untuk hosting situs web.
Daftar menggunakan tautan di atas atau dengan mengklik spanduk dan dapatkan diskon 10% untuk bulan pertama menyewa server dengan konfigurasi apa pun!