Sakelar antena otomatis dengan kontrol MK

gambar

Dalam latihan radio amatir, terkadang ada kebutuhan untuk melakukan sesuatu pada mikrokontroler. Jika Anda tidak melakukan kerajinan seperti ini sepanjang waktu, maka Anda harus mencari solusi sirkuit yang sesuai dan perpustakaan yang cocok untuk MK untuk waktu yang lama, sehingga Anda dapat dengan cepat menyelesaikan masalahnya. Baru-baru ini saya ingin membuat saklar antena otomatis. Dalam prosesnya, saya harus menggunakan banyak fitur Atmega MK dalam satu proyek ringkas. Mereka yang mulai mempelajari AVR, beralih dari Arduino atau kadang-kadang program MK dapat menjadi potongan kode yang berguna yang digunakan oleh saya dalam proyek.

Saya memikirkan saklar antena sebagai perangkat yang secara otomatis menghubungkan antena ke transceiver, yang paling cocok untuk rentang kerja gelombang pendek. Saya memiliki dua antena: V terbalik dan Ground Plane, mereka terhubung ke tuner antena MFJ, di mana mereka dapat diaktifkan dari jarak jauh. Ada saklar manual bermerek MFJ, yang ingin saya ganti.

gambar

Untuk pengalihan operasional antena, satu tombol terhubung ke MK. Saya mengadaptasinya untuk mengingat antena yang disukai untuk setiap rentang: ketika Anda menekan tombol selama lebih dari 3 detik, antena yang dipilih diingat dan dipilih dengan benar secara otomatis setelah power-up perangkat berikutnya. Informasi tentang kisaran saat ini, antena yang dipilih, dan keadaan penyetelannya ditampilkan pada layar LCD satu baris.

Anda dapat mengetahui kisaran transceiver yang saat ini bekerja dengan cara yang berbeda: Anda dapat mengukur frekuensi sinyal, Anda dapat menerima data melalui antarmuka CAT, tetapi hal paling sederhana bagi saya adalah menggunakan antarmuka transceiver YAESU untuk menghubungkan amplifier eksternal. Ini memiliki 4 garis sinyal, dalam kode biner, yang menunjukkan kisaran saat ini. Mereka memberikan sinyal logis dari 0 hingga 5 volt dan mereka dapat dihubungkan ke kaki-kaki MK melalui sepasang resistor terminating.

gambar

Bukan itu saja. Dalam mode transmisi, sinyal PTT dan ALC ditransmisikan melalui antarmuka yang sama. Ini adalah sinyal logis tentang menyalakan pemancar (ditarik ke tanah) dan sinyal analog dari 0 hingga -4V tentang pengoperasian sistem kontrol daya pemancar otomatis. Saya juga memutuskan untuk mengukurnya dan menampilkannya pada LCD dalam mode transmisi.

Selain itu, tuner MFJ dapat mengirimkan sinyal ke remote control yang sedang disetel dan antena disetel. Untuk melakukan ini, panel kontrol perusahaan MFJ memiliki dua LED kontrol. Alih-alih LED, saya menghubungkan optocoupler dan mengirim sinyal dari mereka ke MK, sehingga saya bisa melihat semua informasi di satu layar. Perangkat yang sudah selesai terlihat seperti ini.

gambar

Secara singkat tentang buatan sendiri seperti segalanya. Sekarang tentang bagian perangkat lunak. Kode ini ditulis dalam Atmel Studio (Unduhan gratis dari situs web Atmel). Proyek untuk pemula menunjukkan fitur-fitur berikut menggunakan Atmega8 MK yang populer:

  1. Hubungkan tombol
  2. Hubungkan input saluran untuk sinyal digital dari transceiver dan tuner
  3. Menghubungkan output kontrol dari saklar antena
  4. Menghubungkan satu layar LCD baris
  5. Koneksi buzzer dan output suara
  6. Koneksi jalur input analog ADC dan pengukuran tegangan
  7. Menggunakan interupsi
  8. Menggunakan timer untuk menghitung waktu tombol ditekan
  9. Menggunakan Watchdog
  10. Menggunakan memori non-volatile untuk menyimpan antena yang dipilih
  11. Menggunakan UART untuk Pencetakan Debug
  12. Menghemat energi dalam idle MK

Jadi mari kita mulai. Dalam perjalanan teks, akan ada segala macam nama register dan karakteristik konstanta dari MK yang diterapkan. Ini bukan Arduino, sayangnya, Anda harus membaca datasheet di MK. Jika tidak, Anda tidak mengerti apa arti semua register ini dan bagaimana Anda dapat mengubah nilainya. Tetapi struktur program secara keseluruhan akan tetap sama.

Pertama, hubungkan tombol ke MK


Ini yang paling sederhana. Kami menghubungkan satu kontak ke kaki MK, kontak tombol kedua ke tanah. Agar tombol berfungsi, Anda harus mengaktifkan resistor pull-up di MK. Dia akan menghubungkan tombol melalui perlawanan ke +5V bus. Untuk melakukan ini cukup sederhana:

PORTB |= (1 << PB2); // pullup resistor   

Demikian pula, semua input digital yang dikendalikan oleh gangguan tanah (optocoupler, jalur sinyal dari transceiver, sinyal PTT) ditarik ke bus + 5V. Terkadang lebih baik menyolder resistor yang lebih kecil secara fisik (misalnya 10k) antara input MK dan bus + 5V, tetapi pembahasan masalah ini berada di luar cakupan artikel. Karena semua sinyal input dalam proyek jarang mengubah nilai, mereka didorong ke tanah oleh 10 kapasitor nanofarad untuk melindungi terhadap gangguan.

Sekarang kita memiliki 1 logis pada input PB2, dan ketika Anda menekan tombol, itu akan logis 0. Ketika Anda menekan \ tekan, Anda perlu melacak bouncing kontak tombol, memeriksa bahwa tingkat sinyal tidak berubah dari waktu ke waktu, katakanlah 50 milidetik. Ini dilakukan dalam program seperti ini:

  if(!(PINB&(1<<PINB2)) && !timer_on) { //    _delay_ms(50); if( !(PINB&(1<<PINB2)) ) { //        -   passed_secs = 0; timer_on = 1; } } 

Sekarang hubungkan squeaker


Ini akan memberikan sinyal konfirmasi audio bahwa antena direkam dalam memori MK. Sebuah tweeter hanyalah elemen piezoelektrik. Terhubung melalui resistansi kecil ke kaki MK, dan dengan kontak kedua ke + 5V. Agar buzzer ini berfungsi, Anda harus terlebih dahulu mengkonfigurasi kaki MK untuk menampilkan data.

 void init_buzzer(void) { PORTB &= ~(1 << PB0); // buzzer DDRB |= (1 << PB0); // output PORTB &= ~(1 << PB0); } 

Sekarang bisa digunakan. Untuk melakukan ini, fungsi kecil ditulis yang menggunakan waktu tunda untuk mengganti kaki MK dari 0 ke 1 dan sebaliknya. Beralih dengan penundaan yang diperlukan memungkinkan untuk menghasilkan sinyal audio 4 kHz dengan durasi sekitar seperempat detik pada output MK, yang merupakan suara elemen piezoelektrik.

 void buzz(void) { //    4 0,25  for(int i=0; i<1000; i++) { wdt_reset(); //    PORTB |= (1 << PB0); _delay_us(125); PORTB &= ~(1 << PB0); _delay_us(125); } } 

Agar fungsi penundaan berfungsi, jangan lupa sertakan file header dan tetapkan kecepatan prosesor konstan. Itu sama dengan frekuensi resonator kuarsa yang terhubung ke MK. Dalam kasus saya, ada kuarsa 16MHz.

 #ifndef F_CPU # define F_CPU 16000000UL #endif #include <util/delay.h> 

Kami terhubung ke antena switching relay MK


Di sini Anda hanya perlu mengkonfigurasi kaki MK untuk bekerja di jalan keluar. Relai buluh dihubungkan ke kaki ini melalui transistor yang menguatkan dengan cara standar.
 void init_tuner_relay(void) { PORTB &= ~(1 << PB1); // relay DDRB |= (1 << PB1); // output PORTB &= ~(1 << PB1); } 

Koneksi tampilan


Saya menggunakan layar LCD 160 karakter single-line 1601, diekstraksi dari perangkat keras lama. Ia menggunakan pengontrol HD44780 yang terkenal, untuk manajemen yang banyak perpustakaan tersedia di jaringan. Beberapa orang baik menulis perpustakaan kontrol tampilan ringan, yang saya gunakan dalam proyek ini. Menyiapkan pustaka dikurangi menjadi menunjukkan dalam file header HD44780_Config.h jumlah kaki MK yang terhubung ke pin tampilan yang diinginkan. Saya menerapkan koneksi tampilan melalui 4 jalur data.

 #define Data_Length 0 #define NumberOfLines 1 #define Font 1 #define PORT_Strob_Signal_E PORTC #define PIN_Strob_Signal_E 5 #define PORT_Strob_Signal_RS PORTC #define PIN_Strob_Signal_RS 4 #define PORT_bus_4 PORTC #define PIN_bus_4 0 #define PORT_bus_5 PORTC #define PIN_bus_5 1 #define PORT_bus_6 PORTC #define PIN_bus_6 2 #define PORT_bus_7 PORTC #define PIN_bus_7 3 

Fitur dari instance tampilan saya adalah bahwa satu baris pada layar ditampilkan sebagai dua baris 8 karakter, sehingga buffer layar perantara dibuat dalam program untuk pekerjaan yang lebih mudah dengan layar.

 void init_display(void) { PORTC &= ~(1 << PC0); // display DDRC |= (1 << PC0); // output PORTC &= ~(1 << PC0); PORTC &= ~(1 << PC1); // display DDRC |= (1 << PC1); // output PORTC &= ~(1 << PC1); PORTC &= ~(1 << PC2); // display DDRC |= (1 << PC2); // output PORTC &= ~(1 << PC2); PORTC &= ~(1 << PC3); // display DDRC |= (1 << PC3); // output PORTC &= ~(1 << PC3); PORTC &= ~(1 << PC4); // display DDRC |= (1 << PC4); // output PORTC &= ~(1 << PC4); PORTC &= ~(1 << PC5); // display DDRC |= (1 << PC5); // output PORTC &= ~(1 << PC5); LCD_Init(); LCD_DisplEnable_CursOnOffBlink(1,0,0); } /*   16  0-3   40M     4-8   A:GP  A:IV     9-15    : TUNING=, TUNED==, HI-SWR= */ uchar display_buffer[]={' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; // 16    void update_display() { LCD_Init(); LCD_DisplEnable_CursOnOffBlink(1,0,0); //   16      8         LCD for (uchar i=0; i<8; i++){ LCD_Show(display_buffer[i],1,i); LCD_Show(display_buffer[i+8],2,i); } } 

Fungsi update_display () memungkinkan Anda untuk menampilkan konten buffer di layar. Nilai byte dalam buffer adalah kode ASCII dari karakter output.

Debug mencetak output ke port COM


MK memiliki UART dan saya menggunakannya untuk men-debug program. Saat menghubungkan MK ke komputer, Anda hanya perlu mengingat bahwa level sinyal pada output MK berada dalam standar TTL, dan bukan RS232, sehingga Anda memerlukan adaptor sederhana. Saya menggunakan adaptor USB-Serial, mirip sepenuhnya pada aliexpress. Setiap program terminal, misalnya dari Arduino, cocok untuk membaca data. Kode pengaturan port UART:

 #define BAUD 9600 #include <stdio.h> #include <stdlib.h> #include <avr/io.h> // UART      RS232 void uart_init( void ) { /* //   UBRRH = 0; UBRRL = 103; //9600   16  */ #include <util/setbaud.h> UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1 << U2X); #else UCSRA &= ~(1 << U2X); #endif //8  , 1  ,    UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 ); //     // UCSRB = ( 1 << TXEN ) | ( 1 <<RXEN ); UCSRB = ( 1 << TXEN ); } int uart_putc( char c, FILE *file ) { //     while( ( UCSRA & ( 1 << UDRE ) ) == 0 ); UDR = c; wdt_reset(); return 0; } FILE uart_stream = FDEV_SETUP_STREAM( uart_putc, NULL, _FDEV_SETUP_WRITE ); stdout = &uart_stream; 

Setelah mengatur aliran output, Anda dapat menggunakan printf yang biasa untuk mencetak ke port:
 printf( "Start flag after reset = %u\r\n", mcusr_mirror ); 

Program ini menggunakan pencetakan bilangan real. Pustaka biasa tidak mendukung mode keluaran ini, jadi saya harus menghubungkan pustaka lengkap ketika menghubungkan proyek. Benar, itu benar-benar meningkatkan jumlah kode, tapi saya punya banyak memori, jadi itu tidak kritis. Dalam opsi tautan Anda perlu menentukan baris:

-Wl,-u,vfprintf -lprintf_flt

Bekerja dengan penghitung waktu dan interupsi


Untuk menghitung interval waktu dalam suatu program, penting untuk memiliki penghitung waktu. Diperlukan untuk melacak bahwa tombol ditekan selama lebih dari 3 detik dan, karena itu, Anda perlu mengingat pengaturan baru dalam memori non-volatile. Untuk mengukur waktu dalam gaya AVR, Anda perlu mengonfigurasi penghitung denyut dari penghasil clock dan interupsi yang akan dieksekusi ketika penghitung mencapai nilai yang ditetapkan. Saya mengatur timer sehingga menghasilkan interupsi sekitar sekali per detik. Interrupt handler sendiri menghitung jumlah detik yang berlalu. Variabel timer_on mengontrol on / off timer. Penting untuk tidak lupa mendeklarasikan semua variabel yang diperbarui di interrupt handler sebagai volatile, jika tidak kompiler dapat "mengoptimalkan" mereka dan program tidak akan berfungsi.

 //   1    -     void timer1_init( void ) { TCCR1A = 0; //    1 -   /* 16000000 / 1024 = 15625 ,     15625      1  */ //  CTC, ICP1 interrupt sense (falling)(not used) + prescale /1024 +    (not used) TCCR1B = (0 << WGM13) | (1 << WGM12) | (0 << ICES1) | ((1 << CS12) | (0 << CS11) | (1 << CS10)) | (0 << ICNC1); OCR1A = 15625; //  TIMSK |= (1 << OCIE1A); } uchar timer_on = 0; volatile uchar passed_secs = 0; //      e ISR(TIMER1_COMPA_vect) { if (timer_on) passed_secs++; } 

Nilai pass_secs diperiksa dalam loop utama program. Ketika tombol ditekan, timer mulai dan kemudian dalam siklus program utama nilai timer diperiksa saat tombol ditekan. Jika nilai ini melebihi 3 detik, maka EEPROM ditulis, dan timer berhenti.

Terakhir tetapi tidak kalah pentingnya, setelah semua inisialisasi, Anda perlu mengaktifkan interupsi dengan perintah sei ().

Pengukuran Level ALC


Itu dibuat menggunakan konverter analog-ke-digital (ADC) bawaan. Saya mengukur tegangan pada input dari ADC7. Harus diingat bahwa Anda dapat mengukur nilai dari 0 hingga 2.5V. dan tegangan input saya dari -4V ke 0V. Oleh karena itu, saya menghubungkan MK melalui pembagi tegangan paling sederhana pada resistor, sehingga level tegangan pada input MK berada pada level tertentu. Lebih lanjut, saya tidak membutuhkan akurasi tinggi, jadi saya menerapkan konversi 8-bit (cukup membaca data hanya dari register ADCH). Sebagai sumber referensi, saya menggunakan ion internal pada 2.56V, ini sedikit menyederhanakan perhitungan. Agar ADC berfungsi, pastikan untuk menghubungkan kapasitor 0,1 μF ke kaki REF di permukaan tanah.

ADC dalam kasus saya berfungsi terus-menerus, melaporkan akhir konversi dengan memanggil interupsi ADC_vect. Merupakan praktik yang baik untuk meratakan nilai beberapa siklus konversi untuk mengurangi kesalahan. Dalam kasus saya, saya menyimpulkan rata-rata 2500 transformasi. Semua kode ADC terlihat seperti ini:

 //        ALC #define SAMPLES 2500 //    #define REFERENCEV 2.56 //       #define DIVIDER 2.0 double realV = 0; //     ALC double current_realV = 0; volatile int sampleCount = 0; volatile unsigned long tempVoltage = 0; //     volatile unsigned long sumVoltage = 0; //         void ADC_init() // ADC7 { //   2,56, 8 bit  -   ADCH ADMUX = (1 << REFS0) | (1 << REFS1) | (1 << ADLAR) | (0 << MUX3) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0); // ADC7 // , free running,   ADCSRA = (1 << ADEN) | (1 << ADFR) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); //  128 ADCSRA |= (1 << ADSC); // Start ADC Conversion } ISR(ADC_vect) //     2500  { if (sampleCount++) //    tempVoltage += ADCH; if (sampleCount >= SAMPLES) { sampleCount = 0; sumVoltage = tempVoltage; tempVoltage = 0; } ADCSRA |=(1 << ADIF); // Acknowledge the ADC Interrupt Flag } realV = -1.0*(DIVIDER * ((sumVoltage * REFERENCEV) / 256) / SAMPLES - 5.0); //   ALC if (realV < 0.0) realV = 0.0; printf("ALC= -%4.2f\r\n", realV); //      

Menggunakan EEPROM


Ini adalah memori non-volatile di MK. Lebih mudah menggunakannya untuk menyimpan segala macam pengaturan, nilai koreksi, dll. Dalam kasus kami, ini hanya digunakan untuk menyimpan antena yang dipilih untuk rentang yang diinginkan. Untuk tujuan ini, array 16 byte dialokasikan di EEPROM. Tetapi Anda dapat mengaksesnya melalui fungsi-fungsi khusus yang ditentukan dalam file header avr / eeprom.h. Saat startup, MK membaca informasi tentang pengaturan yang disimpan ke dalam RAM dan menyalakan antena yang diinginkan, tergantung pada kisaran saat ini. Ketika tombol ditekan untuk waktu yang lama, nilai baru dicatat dalam memori, disertai dengan sinyal suara. Saat menulis ke EEPROM, interupsi dinonaktifkan untuk berjaga-jaga. Kode Inisialisasi Memori:

 EEMEM unsigned char ee_bands[16]; //         unsigned char avr_bands[16]; void EEPROM_init(void) { for(int i=0; i<16; i++) { avr_bands[i] = eeprom_read_byte(&ee_bands[i]); if (avr_bands[i] > 1) avr_bands[i] = ANT_IV; //    EEPROM   ,     FF } } 

Potongan kode pemrosesan untuk menekan tombol selama 3 detik dan menulis ke memori:

  if (!(PINB&(1<<PINB2)) && passed_secs >= 3) { //    3  timer_on = 0; //   read_ant = avr_bands[read_band]; //     cli(); EEPROM_init(); //          sei(); if (read_ant) { avr_bands[read_band] = ANT_GP; } else { avr_bands[read_band] = ANT_IV; } cli(); eeprom_write_byte(&ee_bands[read_band], avr_bands[read_band]); //    EEPROM sei(); buzz(); } 

Menggunakan Watchdog


Bukan rahasia lagi bahwa di bawah kondisi interferensi elektromagnetik yang kuat, MK dapat membeku. Ketika radio sedang beroperasi, ada gangguan sehingga "setrika mulai berbicara", jadi Anda harus memastikan restart MK dengan hati-hati jika terjadi hang. Pengawas waktu melayani tujuan ini. Menggunakannya sangat sederhana. Pertama, sertakan file header avr / wdt.h dalam proyek. Di awal program, setelah semua pengaturan selesai, Anda harus memulai timer dengan memanggil fungsi wdt_enable (WDTO_2S), dan kemudian ingat untuk mengatur ulang secara berkala dengan memanggil wdt_reset (), jika tidak maka akan me-restart MK. Untuk debugging untuk mengetahui mengapa MK itu restart, Anda dapat menggunakan nilai register MCUSR khusus, nilai yang dapat diingat dan kemudian output ke cetak debug.

 //        //     uint8_t mcusr_mirror __attribute__ ((section (".noinit"))); void get_mcusr(void) \ __attribute__((naked)) \ __attribute__((section(".init3"))); void get_mcusr(void) { mcusr_mirror = MCUSR; MCUSR = 0; wdt_disable(); } printf( "Start flag after reset = %u\r\n", mcusr_mirror ); 

Hemat energi untuk pecinta lingkungan


Sementara MK tidak sibuk dengan apa pun, ia bisa tertidur dan menunggu gangguan berikutnya. Dalam hal ini, sedikit energi listrik dihemat. Agak, tapi mengapa tidak menggunakannya dalam proyek. Apalagi itu sangat sederhana. Sertakan file header avr / sleep.h. Badan program terdiri dari satu loop tak terbatas di mana Anda perlu memanggil fungsi sleep_cpu (), setelah itu MC tertidur sedikit dan loop utama berhenti sampai gangguan berikutnya terjadi. Mereka terjadi selama pengoperasian timer dan ADC, sehingga MK tidak akan tidur untuk waktu yang lama. Mode hibernasi ditentukan ketika MK diinisialisasi dengan memanggil dua fungsi:

  set_sleep_mode(SLEEP_MODE_IDLE); //     IDLE sleep_enable(); 

Itu saja untuk saat ini. Saya beralih, itu berhasil bekerja di stasiun radio amatir saya tanpa kegagalan. Saya berharap materi yang diberikan akan bermanfaat bagi pemula.

73 de R2AJP

Source: https://habr.com/ru/post/id405581/


All Articles