Pada artikel terakhir , saya berbicara tentang interupsi DebugMon dan register yang terkait dengannya.Pada artikel ini kita akan menulis implementasi debugger untuk UART.Bagian tingkat rendah
Di sini dan di sini ada deskripsi struktur permintaan dan tanggapan dari server GDB. Meskipun tampaknya sederhana, kami tidak akan mengimplementasikannya di mikrokontroler karena alasan berikut:- Redundansi data besar. Alamat, nilai register, variabel dikodekan sebagai hex-string, yang meningkatkan volume pesan sebanyak 2 kali
- Memilah dan mengumpulkan pesan akan membutuhkan sumber daya tambahan
- Pelacakan akhir paket diperlukan baik oleh waktu habis (timer akan sibuk), atau oleh mesin otomatis yang kompleks, yang akan meningkatkan waktu yang dihabiskan dalam interupsi UART
Untuk mendapatkan modul debugging termudah dan tercepat, kami akan menggunakan protokol biner dengan urutan kontrol:- 0xAA 0xFF - Mulai dari bingkai
- 0xAA 0x00 - Akhir bingkai
- 0xAA 0xA5 - Mengganggu
- 0xAA 0xAA - Digantikan oleh 0xAA
Untuk memproses urutan ini selama penerimaan, diperlukan otomat dengan 4 status:- Menunggu karakter ESC
- Menunggu karakter kedua dari Mulai urutan frame
- Penerimaan data
- Terakhir kali karakter Esc diterima
Tetapi untuk mengirim status, Anda harus sudah 7:- Mengirim byte awal dari frame
- Mengirim byte awal dari frame
- Mengirim data
- Mengirimkan Akhir bingkai
- Mengirim penggantian karakter Esc
- Mengirim byte Interrupt pertama
- Mengirim Byte Interupsi Kedua
Mari kita menulis definisi struktur di mana semua variabel modul akan ditempatkan:typedef struct
{
unsigned tx:1;
unsigned StopProgramm:1;
union {
enum rx_state_e
{
rxWaitS = 0,
rxWaitC = 1,
rxReceive = 2,
rxEsc = 3,
} rx_state;
enum tx_state_e
{
txSendS = 0,
txSendC = 1,
txSendN = 2,
txEsc = 3,
txEnd = 4,
txSendS2 = 5,
txBrk = 6,
} tx_state;
};
uint8_t pos;
uint8_t buf[128];
uint8_t txCnt;
} dbg_t;
#define dbgG ((dbg_t*)DBG_ADDR)
Keadaan mesin penerima dan transmisi digabungkan menjadi satu variabel karena pekerjaan akan dilakukan dalam mode setengah dupleks. Sekarang Anda dapat menulis automata sendiri dengan penangan interrupt.Penangan UARTvoid USART6_IRQHandler(void)
{
if (((USART6->ISR & USART_ISR_RXNE) != 0U)
&& ((USART6->CR1 & USART_CR1_RXNEIE) != 0U))
{
rxCb(USART6->RDR);
return;
}
if (((USART6->ISR & USART_ISR_TXE) != 0U)
&& ((USART6->CR1 & USART_CR1_TXEIE) != 0U))
{
txCb();
return;
}
}
void rxCb(uint8_t byte)
{
dbg_t* dbg = dbgG;
if (dbg->tx)
return;
switch(dbg->rx_state)
{
default:
case rxWaitS:
if (byte==0xAA)
dbg->rx_state = rxWaitC;
break;
case rxWaitC:
if (byte == 0xFF)
dbg->rx_state = rxReceive;
else
dbg->rx_state = rxWaitS;
dbg->pos = 0;
break;
case rxReceive:
if (byte == 0xAA)
dbg->rx_state = rxEsc;
else
dbg->buf[dbg->pos++] = byte;
break;
case rxEsc:
if (byte == 0xAA)
{
dbg->buf[dbg->pos++] = byte;
dbg->rx_state = rxReceive;
}
else if (byte == 0x00)
{
parseAnswer();
}
else
dbg->rx_state = rxWaitS;
}
}
void txCb()
{
dbg_t* dbg = dbgG;
switch (dbg->tx_state)
{
case txSendS:
USART6->TDR = 0xAA;
dbg->tx_state = txSendC;
break;
case txSendC:
USART6->TDR = 0xFF;
dbg->tx_state = txSendN;
break;
case txSendN:
if (dbg->txCnt>=dbg->pos)
{
USART6->TDR = 0xAA;
dbg->tx_state = txEnd;
break;
}
if (dbg->buf[dbg->txCnt]==0xAA)
{
USART6->TDR = 0xAA;
dbg->tx_state = txEsc;
break;
}
USART6->TDR = dbg->buf[dbg->txCnt++];
break;
case txEsc:
USART6->TDR = 0xAA;
dbg->txCnt++;
dbg->tx_state = txSendN;
break;
case txEnd:
USART6->TDR = 0x00;
dbg->rx_state = rxWaitS;
dbg->tx = 0;
CLEAR_BIT(USART6->CR1, USART_CR1_TXEIE);
break;
case txSendS2:
USART6->TDR = 0xAA;
dbg->tx_state = txBrk;
break;
case txBrk:
USART6->TDR = 0xA5;
dbg->rx_state = rxWaitS;
dbg->tx = 0;
CLEAR_BIT(USART6->CR1, USART_CR1_TXEIE);
break;
}
}
Semuanya sangat sederhana di sini. Bergantung pada peristiwa yang terjadi, penangan interupsi memanggil mesin penerima atau mesin transmisi. Untuk memverifikasi bahwa semuanya berfungsi, kami menulis penangan paket yang merespons dengan satu byte:void parseAnswer()
{
dbg_t* dbg = dbgG;
dbg->pos = 1;
dbg->buf[0] = 0x33;
dbg->txCnt = 0;
dbg->tx = 1;
dbg->tx_state = txSendS;
SET_BIT(USART6->CR1, USART_CR1_TXEIE);
}
Kompilasi, jahit, jalankan. Hasilnya terlihat di layar, itu berhasil.Selanjutnya, Anda perlu menerapkan analog perintah dari protokol server GDB:- membaca memori
- catatan memori
- program berhenti
- eksekusi lanjutan
- register kernel baca
- entri register kernel
- pengaturan breakpoint
- hapus breakpoint
Perintah akan dikodekan dengan byte data pertama. Kode tim memiliki angka sesuai urutan pelaksanaannya:- 2 - baca memori
- 3 - catatan memori
- 4 - berhenti
- 5 - lanjutan
- 6 - baca case
- 7 - pasang breakpoint
- 8 - membersihkan breakpoint
- 9 - langkah (gagal diterapkan)
- 10 - daftar entri (tidak diterapkan)
Parameter akan dikirim dalam byte data berikut.Jawabannya tidak akan berisi nomor perintah, seperti kita sudah tahu tim mana yang dikirim.Untuk mencegah modul menyebabkan pengecualian BusFault selama operasi baca / tulis, Anda harus menutupinya saat digunakan pada M3 atau lebih tinggi, atau menulis penangan HardFault untuk M0.Memcpy amanint memcpySafe(uint8_t* to,uint8_t* from, int len)
{
static const uint32_t BFARVALID_MASK = (0x80 << SCB_CFSR_BUSFAULTSR_Pos);
int cnt = 0;
SCB->CFSR |= BFARVALID_MASK;
uint32_t mask = __get_FAULTMASK();
__disable_fault_irq();
SCB->CCR |= SCB_CCR_BFHFNMIGN_Msk;
while ((cnt<len))
{
*(to++) = *(from++);
cnt++;
}
SCB->CCR &= ~SCB_CCR_BFHFNMIGN_Msk;
__set_FAULTMASK(mask);
return cnt;
}
Pengaturan breakpoint diimplementasikan dengan mencari FP_COMP register tidak aktif pertama.Memasang Kode Breakpoints
dbg->pos = 0;
addr = ((*(uint32_t*)(&dbg->buf[1])))|1;
for (tmp = 0;tmp<8;tmp++)
if (FP->FP_COMP[tmp] == addr)
break;
if (tmp!=8)
break;
for (tmp=0;tmp<NUMOFBKPTS;tmp++)
if (FP->FP_COMP[tmp]==0)
{
FP->FP_COMP[tmp] = addr;
break;
}
break;
Pembersihan dilakukan dengan mencari breakpoint yang ditetapkan. Menghentikan eksekusi akan menetapkan breakpoint pada PC saat ini. Ketika keluar dari interupsi UART, kernel segera memasuki DebugMon_Handler.Handler DebugMon sendiri sangat sederhana:- 1. Bendera untuk menghentikan eksekusi diatur.
- 2. Semua breakpoint yang ditetapkan dihapus.
- 3. Menunggu penyelesaian pengiriman respons terhadap perintah di uart (jika dia tidak berhasil pergi)
- 4. Pengiriman urutan Interrupt dimulai
- 5. Dalam loop, handler dari mesin pengirim dan penerima dipanggil sampai bendera berhenti diturunkan.
Kode Penangan DebugMonvoid DebugMon_Handler(void)
{
dbgG->StopProgramm = 1;
for (int i=0;i<NUMOFBKPTS;i++)
FP->FP_COMP[i] = 0;
while (USART6->CR1 & USART_CR1_TXEIE)
if ((USART6->ISR & USART_ISR_TXE) != 0U)
txCb();
dbgG->tx_state = txSendS2;
dbgG->tx = 1;
SET_BIT(USART6->CR1, USART_CR1_TXEIE);
while (dbgG->StopProgramm)
{
if (((USART6->ISR & USART_ISR_RXNE) != 0U)
&& ((USART6->CR1 & USART_CR1_RXNEIE) != 0U))
rxCb(USART6->RDR);
if (((USART6->ISR & USART_ISR_TXE) != 0U)
&& ((USART6->CR1 & USART_CR1_TXEIE) != 0U))
txCb();
}
}
Baca register kernel dari C-Syshny ketika tugas bermasalah, jadi saya menulis ulang beberapa kode pada ASM. Hasilnya adalah DebugMon_Handler, atau interrupt handler UART, atau mesin tidak menggunakan stack. Ini menyederhanakan definisi nilai register kernel.Server Gdb
Bagian mikrokontroler dari debugger berfungsi, sekarang mari kita menulis tautan antara IDE dan modul kami.Dari awal, menulis server debug tidak masuk akal, jadi mari kita ambil yang sudah selesai sebagai dasar. Karena saya memiliki pengalaman paling banyak dalam mengembangkan program di .net, saya mengambil proyek ini sebagai dasar dan menulis ulang untuk persyaratan lain. Akan lebih benar untuk menambahkan dukungan untuk antarmuka baru di OpenOCD, tetapi itu akan membutuhkan lebih banyak waktu.Saat startup, program ini menanyakan port COM mana untuk bekerja, kemudian mulai mendengarkan pada port TCP 3333 dan menunggu klien GDB untuk terhubung.Semua perintah protokol GDB diterjemahkan ke dalam protokol biner.Akibatnya, implementasi debugging UART yang bisa diterapkan dirilis.Kesimpulan
Ternyata debugging controller itu sendiri bukanlah sesuatu yang super rumit.Secara teoritis, dengan menempatkan modul ini di bagian memori yang terpisah, modul ini juga dapat digunakan untuk mem-flash controller.File sumber diposting di GitHub untuk studi umumbagian mikrokontroler dariserver GDB