image

В радиолюбительской практике иногда возникает потребность сделать что-нибудь на микроконтроллере. Если не занимаешься такого рода поделками постоянно, то приходится долго гуглить нужное схемное решение и подходящие библиотеки для МК, позволяющие быстро решить задачу. Недавно захотелось мне сделать автоматический антенный переключатель. В процессе работы пришлось использовать многие возможности МК Atmega в одном компактном проекте. Тем, кто начинает изучать AVR, переходит с ардуино или эпизодически программирует МК могут быть полезны куски кода, использованные мной в проекте.

Антенный переключатель задумывался мной как устройство, автоматически подключающее к трансиверу антенну, которая наилучшим образом подходит для рабочего диапазона коротких волн. У меня есть две антенны: Inverted V и Ground Plane, подключены они к антенному тюнеру MFJ, в котором их можно дистанционно переключать. Есть фирменный ручной переключатель MFJ, который хотелось заменить.

image

Для оперативного переключения антенн к МК подключена одна кнопка. Её я же приспособил для запоминания предпочтительной антенны для каждого диапазона: при нажатии кнопки более 3 секунд выбранная антенна запоминается и выбирается правильно автоматически после очередного включения питания устройства. Информация о текущем диапазоне, выбранной антенне и состоянии её настройки выводится на однострочный LCD дисплей.

О том, на каком сейчас диапазоне работает трансивер, можно узнать разными способами: можно измерять частоту сигнала, можно получать данные по интерфейсу CAT, но самое простое для меня – использовать интерфейс трансивера YAESU для подключения внешнего усилителя. В нём есть 4 сигнальных линии, в двоичном коде указывающие на текущий диапазон. Они выдают логический сигнал от 0 до 5 вольт и их можно через пару согласующих резисторов соединить с ногами МК.

image

Это еще не всё. В режиме передачи через тот же интерфейс передаются сигналы PTT и ALC. Это логический сигнал о включении передатчика (подтягивается к земле) и аналоговый сигнал от 0 до -4В о работе системы автоматического управления мощностью передатчика. Его я тоже решил измерять и выводить на LCD в режиме передачи.

Кроме того, тюнер MFJ умеет передавать на пульт дистанционного управления сигналы о том, что он ведет настройку и о том, что антенна настроена. Для этого на фирменном пульте MFJ предусмотрено два контрольных светодиода. Я вместо светодиодов подключил оптроны и подал с них сигнал на МК, так чтоб всю информацию видеть на одном дисплее. Выглядит готовый девайс так.

image

Коротко о самоделке вроде всё. Теперь о программной части. Код написан в Atmel Studio (Свободно скачивается с сайта Atmel). В проекте для начинающих демонстрируются следующие возможности использования популярного МК Atmega8:

  1. Подключение кнопки
  2. Подключение линии вход для цифрового сигнала от трансивера и тюнера
  3. Подключение выхода управления реле переключения антенн
  4. Подключение однострочного LCD дисплея
  5. Подключение зуммера и вывод звука
  6. Подключение линии аналогового входа ADC и измерение напряжения
  7. Использование прерываний
  8. Использование таймера для отсчёта времени нажатия кнопки
  9. Использование сторожевого таймера
  10. Использование энергонезависимой памяти для хранения выбранных антенн
  11. Использование UART для отладочной печати
  12. Экономия энергии в простое МК

Итак, начнём. По ходу в тексте будут встречаться всякие названия регистров и константы, свойственные для применяемого МК. Это не ардуино, здесь к сожалению, придётся почитать даташит на МК. Иначе вам не понять, что значат все эти регистры и как можно поменять их значения. Но структура программы в целом останется той же.

Первым делом подключим к МК кнопку


Это самое простое. Один контакт подключаем к ноге МК, второй контакт кнопки – на землю. Чтобы кнопка работала, понадобится включить подтягивающий резистор в МК. Он соединит кнопку через сопротивление с шиной +5В. Сделать это совсем просто:

PORTB |= (1 << PB2); // pullup resistor для кнопки

Аналогично к шине +5В подтягиваются все цифровые входы, которые управляются замыканием на землю (оптроны, сигнальные линии от трансивера, сигнал PTT). Иногда лучше физически припаять такой резистор меньшего наминала (например 10к) между входом МК и шиной +5В, но обсуждение этого вопроса за рамками статьи. Поскольку все входные сигналы в проекте редко изменяют значения, то они для защиты от помех зашунтированы на землю конденсаторами в 10 нанофарад.

Теперь у нас на входе PB2 постоянно присутствует логическая 1, а при нажатии на кнопку будет логический 0. При нажатии\отжатии нужно отслеживать дребезг контактов кнопки, проверяя, что уровень сигнала не изменился за время, скажем 50 миллисекунд. Делается это в программе так:

	if(!(PINB&(1<<PINB2)) && !timer_on) { // только нажали кнопку
		_delay_ms(50);
		if( !(PINB&(1<<PINB2)) ) { // проверили на дребезг и она всё нажата - запускаем таймер
			passed_secs = 0;
			timer_on = 1;
		}
	}

Теперь подключаем пищалку


Она будет давать звуковой сигнал подтверждения, что антенна записана в память МК. Пищалка это просто пьезоэлемент. Он подключается через небольшое сопротивление к ноге МК, а вторым контактом к +5В. Для работы этого зуммера нужно сначала настроить ногу МК на вывод данных.

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

Теперь ею можно пользоваться. Для этого написана небольшая функция, использующая временные задержки для переключения ноги МК из 0 в 1 и обратно. Переключение с необходимыми задержками позволяет формировать на выходе МК сигнал звуковой частоты 4 кГц длительностью около четверти секунды, который и озвучивает пьезоэлемент.

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

Для работы функций задержек не забудьте подключить заголовочный файл и настроить константу скорости работы процессора. Она равна частоте подключенного к МК кварцевого резонатора. В моём случае был кварц на 16МГц.

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

Подключаем к МК реле переключения антенн


Здесь нужно просто настроить ногу МК для работы на выход. К этой ноге через усиливающий транзистор по стандартной схеме подключено герконовое реле.
void init_tuner_relay(void) {
	PORTB &= ~(1 << PB1); // relay
	DDRB  |=  (1 << PB1); // output
	PORTB &= ~(1 << PB1);
}

Подключение дисплея


Я использовал однострочный 16 символьный LCD дисплей 1601, добытый из старой аппаратуры. Он использует широкоизвестный контроллер HD44780, для управления которым в сети доступна масса библиотек. Какой-то добрый человек написал легкую библиотеку управления дисплеем, которую я и использовал в проекте. Настройка библиотеки сводится к указанию в заголовочном файле HD44780_Config.h номеров ног МК, подключенных нужным выводам дисплея. Я применил подключение дисплея по 4 линиям данных.

#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

Особенностью моего экземпляра дисплея стало то, что одна строка на экране выводилась как две строки по 8 символов, поэтому в программе был сделан промежуточный экранный буфер для более удобной работы с экраном.

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

Функция update_display() позволяет выводить содержимое буфера на экран. Значения байтов в буфере это коды ASCII выводимых символов.

Вывод отладочной печати в COM порт


В МК есть UART и я его использовал для отладки программы. При подключении МК компьютеру надо только помнить, что уровни сигнала на выходе МК в стандарте TTL, а не RS232, так что понадобится простейший переходник. Я использовал адаптер USB-Serial, аналогичных полно на aliexpress. Для чтения данных подойдет любая терминальная программа, например от ардуино. Код настройки порта 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;	

После настройки потока вывода, можно пользоваться обычным printf для печати в порт:
printf( "Start flag after reset = %u\r\n", mcusr_mirror );

Программа использует печать вещественных чисел. Обычные библиотеки не поддерживают такой режим вывода, поэтому пришлось подключить полноценную библиотеку при линковке проекта. Она, правда, увеличивает серьёзно объем кода, но у меня был большой запас памяти, так что это было некритично. В опциях линкера нужно указать строку:

-Wl,-u,vfprintf -lprintf_flt

Работа с таймером и прерываниями


Для отсчёта интервалов времени в программе важно иметь счётчик времени. Он нужен для отслеживания, что кнопка нажата более 3 секунд и, следовательно, нужно запомнить в энергонезависимой памяти новые настройки. Чтоб измерить время в стиле AVR нужно настроить счётчик импульсов тактового генератора и прерывание, которое будет выполняться при достижении счётчиком заданного значения. Я настроил таймер так, чтоб он примерно раз в секунду выдавал прерывание. В самом обработчике прерывания подсчитывается количество прошедших секунд. Управляет включением\отключением таймера переменная timer_on. Важно не забывать объявлять все переменные, которые обновляются в обработчике прерывания, как volatile, иначе компилятор может их «оптимизировать» и программа работать не будет.

// настройка счетчика 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++;
}

Значение переменной passed_secs проверяется в главном цикле программы. При нажатии кнопки таймер запускается и далее в главном цикле программы проверяется значение таймера при нажатой кнопке. Если это значение превысит 3 секунды, то производится запись в EEPROM, а таймер останавливается.

Последнее, но самое главное – после всех инициализаций нужно разрешить выполнение прерываний командой sei().

Измерение уровня ALC


Производится с помощью встроенного аналого-цифрового преобразователя (ADC). Я измерял напряжение на входе ADC7. Надо помнить, что можно измерить значение от 0 до 2.5В. а у меня входное напряжение было от -4В до 0В. Поэтому я подключил МК через простейший делитель напряжения на резисторах, так чтобы уровень напряжения на входе МК был на заданном уровне. Далее, мне не нужна была высокая точность, поэтому я применил 8 битное преобразование (достаточно читать данные только из регистра ADCH). В качестве опорного источника использовал внутренний ИОН на 2.56В, это чуть упрощает расчёты. Для работы ADC не забудьте подключить на землю конденсатор 0.1 мкФ к ноге REF.

ADC в моем случае работает непрерывно, сообщая об окончании преобразования вызовом прерывания ADC_vect. Хорошим тоном является усреднять значения нескольких циклов преобразования для уменьшения погрешности. В моём случае я вывожу среднее из 2500 преобразований. Весь код работы с ADC выглядит так:

// количество семплов для усреднения значения датчика напряжения 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); // вывод напряжения в последовательный порт

Использование EEPROM


Это энергонезависимая память в МК. Её удобно использовать для хранения всяких настроек, корректировочных значений и т.п. В нашем случае она используется только для хранения выбранной антенны для нужного диапазона. С этой целью в EEPROM выделен 16 байтный массив. Но обращаться к нему можно через специальные функции, определенные в заголовочном файле avr/eeprom.h. При запуске МК считывает информацию о сохранённых настройках в оперативную память и включает нужную антенну в зависимости от текущего диапазона. При длительном нажатии на кнопку в память записывается новое значение, сопровождаемое звуковым сигналом. Во время записи в EEPROM на всякий случай запрещаются прерывания. Код инициализации памяти:

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
	}

}

Фрагмент кода обработки нажатия кнопки 3 сек и записи в память:

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

Использование сторожевого таймера


Не секрет, что в условиях сильных электромагнитных помех МК может зависнуть. При работе радиостанции бывают такие помехи, что «утюги начинают разговаривать», так что нужно обеспечить аккуратную перезагрузку МК в случае зависания. Этой цели служит сторожевой таймер. Использовать его очень просто. Подключите сначала в проект заголовочный файл avr/wdt.h. В начале работы программы после выполнения всех настроек нужно запустить таймер вызовом функции wdt_enable(WDTO_2S), а потом не забывать периодически сбрасывать вызовом wdt_reset(), иначе он сам перезапустит МК. Для отладки чтоб узнать по какой причине был перезапущен МК, можно использовать значение специального регистра MCUSR, значение которого можно запомнить и затем выдать в отладочную печать.

// переменные для сохранения состояния контроллера после запуска
// используются только для отладки
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 );

Экономия энергии для любителей экологии


Пока МК ничем не занят, он может заснуть и ждать наступления очередного прерывания. В этом случае экономится немного электрической энергии. Пустяк, но почему бы его не использовать в проекте. Тем более, что это очень просто. Подключите заголовочный файл avr/sleep.h. Тело программы состоит из одного бесконечного цикла, в котором нужно вызывать функцию sleep_cpu(), после чего МК немного засыпает и основной цикл останавливается до возникновения следующего прерывания. Они возникают при работе таймера и ADC, так что долго спать МК не будет. Режим спячки определяется при инициализации МК вызовом двух функций:

    set_sleep_mode(SLEEP_MODE_IDLE);  // разрешаем сон в режиме IDLE
    sleep_enable();

На этом пока всё. Переключатель я сделал, он успешно трудится на моей любительской радиостанции без сбоев. Надеюсь, предоставленный материал будет полезен начинающим.

73 de R2AJP
Поделиться с друзьями
-->

Комментарии (23)


  1. Alexeyslav
    26.07.2017 15:54

    Усреднять конечно хорошо, но простое усреднение влоб — ниочем, цифры будут все так же прыгать. Лучше всего это усреднение по методу подвижного окна, когда усредняются не тысячи каких-то отсчетов, а N последних. В результате получаем достаточно быструю и плавную цифру. Выбрав значение N пропорционально степени двойки можно ещё отказаться от операции деления заменив на сдвиг.
    Если внедрять этот алгоритм в ваш готовый код, то проще всего это сделать с окном в 16 элементов на вход которого подаются ваши усреднённые значения. делал такое для измерения тока зарядки, шевеление на единицу было в 4-м знаке для 10 бит, а без него прыгали два последних знака.

    Подавление дребезга сделано не очень… безусловные задержки в основном потоке — великое зло, ибо контроллер останавливается на это время и не реагирует ни на что(кроме прерываний). Гораздо надёжнее и проще — фиксировать реальное состояние кнопок в таймере с периодом опроса 50мс это гарантирует отсутствие влияния дребезга пока он меньше 50мс(у нормальных кнопок он меньше 10мс, у герконовых — порядка 1мс). Главное делать это ОДИН раз за цикл опроса. Там же можно считать количество циклов и определять «короткое нажатие», «длинное нажатие» реализовать автоповтор и т.д. устанавливать признак соответствующего нажатия в глобальной переменной а сбрасывать его только в программе в основном цикле.


    1. lesha108
      26.07.2017 16:54

      Спасибо, буду иметь в виду. Тут проект крохотный, небольшие задержки и точность измерения в нем мне были не особо критичны.


    1. Alex_Sa
      26.07.2017 20:44
      +2

      Метод скользящего среднего имеет смысл когда частота измерений соизмерима с частотой изменения сигнала. Если же частота измерений намного превышает частоту изменения сигнала, то обычное усреднение прекрасно работает. Хотя в данном случае 2500 это перебор, особенно с учетом использования внутреннего источника опорного напряжения. Достаточно нескольких десятков.


      1. Alexeyslav
        27.07.2017 10:04
        +1

        Нет не работает. Цифры всё так же будут прыгать, усреднение сгладит только пиковые отклонения и то не до конца а основной шум на выход проходит без изменений. Скользящее среднее — это есть не что иное как самый настоящий ФНЧ, который сглаживает измеряемую величину и результат получается достаточно гладким.


        1. Alex_Sa
          28.07.2017 01:21

          Куда прыгать? Какой шум куда проходит?
          Если мы имеем постоянную измеряемую величину на которую накладывается случайный шум, то ошибка измерения при обычном усреднении обратно пропорциональна корню от числа измерений. Таким образом чем больше число измерений, тем меньше «будет прыгать».
          В случае если кол-во измерений больше нескольких десятков, то точность можно повысить путем отбрасывания измерений которые отличаются от среднего больше чем на 3 «сигмы», где «сигма» — среднеквадратическое отклонение.
          Все вышеописанное относится и к квазипостоянному сигналу, когда частота измерений на несколько порядков выше частоты измеряемого сигнала.


          1. Alexeyslav
            28.07.2017 09:33

            В реальности же идеального случайного шума не бывает, и при простом усреднении без истории получается прыгающая фигня. А скользящее среднее справляется с задачей лучше, особенно в задаче визуализации измеренной величины для человека.
            Вобщем-то, даже для скользящего и окна бывают разные, и фильтрация отклонений тоже актуальна.


  1. QDeathNick
    26.07.2017 18:32

    А в чем заключается настройка антенны? Вы же вроде только герконом переключаете две антенны.


    Подключение линии вход для цифрового сигнала от трансивера и тюнера

    Не нашёл в тексте этого момента более подробно. Вы все четыре ноги через ADC мониторите?


    1. lesha108
      26.07.2017 20:29

      Антенну настраивает тюнер MFJ-993 сам автоматически. У него есть разъем для пульта дистанционного управления, через который с ним общается моё устройство. Через этот разъём на пульт выдаются два сигнала: о том, что идёт автоматическая настройка и о том, что настройка прошла успешно. Эти сигналы в фирменном пульте просто подключаются к светодиодам красного и зеленого цвета. Я в своей конструкции вместо светодиодов подключаю оптроны и сигнал с них подаю на МК. Герконовое реле эмулирует кнопку переключения антенн на пульте дистанционного управления. Если вы хотите понять как этот пульт работает посмотрите документацию на пульт и тюнер MFJ.

      От трансивера идут сигналы по линиям BAND DATA, они постоянно мониторятся в МК. Они логические 0\5В, считываются напрямую, а не через ADC.


  1. NLO
    26.07.2017 20:29

    НЛО прилетело и опубликовало эту надпись здесь


    1. lesha108
      26.07.2017 20:36

      Видимо, по тексту не ясно, что герконовое реле не ВЧ сигнал коммутирует, а просто эмулирует кнопку на пульте дистанционного управления. Сама коммутация антенн идёт внутри тюнера MFJ. Таким образом, в пульте ВЧ никак не присутствует. По всем линиям с тюнером гальваническая развязка (кроме земли) через оптроны и реле. По цепям питания дроссели стоят и ферритовыми кольцами все шлейфы увешаны. Но это не тема данной статьи, я писал про программную часть.


      1. QDeathNick
        26.07.2017 20:42

        Да, именно это не ясно, уже написал комментарий, хорошо, что не отправил.
        А что с родным пультом случилось, почему вы его решили заменить?


        1. lesha108
          26.07.2017 20:47

          С родным все хорошо. Но там надо кнопку руками нажимать при смене диапазона. А тут всё автоматом делается. Это и стало поводом для разработки устройства.


      1. raefaraefa
        26.07.2017 20:53

        что герконовое реле не ВЧ сигнал коммутирует

        Ок, с этим понятно. Недочитал…
        Но это не тема данной статьи

        Код кодом, а описываете наверно устройство, его и обсуждаем. Хорошо бы экранировать все это дело в обязательном порядке. Сколько было случаев, когда сгорали электронные CW ключи, секвенсоры на МК и т.д. И тут вариантов всего два: либо не использовать МК в полях антенн, либо тщательно бороться с различными наводками не только ферритами.


  1. ub9obe
    27.07.2017 06:36
    +1

    Я вот не понимаю смысла публикации…
    «Чуваки, я знаю AVR» и куски кода. Ни схемы, ни кода ни прошивки. Где польза?
    Забирать показания ALC с входа «ALC IN», это что то новое. Всегда думал что «ALC IN» (исходя из названия) это вход для указания трансиверу от усилителя, что нагрузка не оптимальна и стоит снизить мощность.


  1. tronix286
    27.07.2017 10:40
    -1

    При всем уважении, для переключения антен обычно используют переключатель. Да, вот так тупо и просто — обычный едри его в корень механический переключатель. А AVR вообще читерский микроконтроллер. Он как плевок в душу всем кодерам на z80,i51,x86,pic. 32 регистра. Команда за такт. Да нихрена себе, если б мне такое в школе показали, я бы щаз тоже double'ми в прошивках сыпал.


    1. Alexeyslav
      27.07.2017 11:22

      Механическим переключателем можно спалить выход усилителя, который может оказаться не способен работать без антенны случайно задев переключатель во время передачи.


  1. t3hk0d3
    27.07.2017 12:31

    > UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );

    Чего люди только не делают, лишь бы не использовать битовые поля и нормальные называния.


    1. Punk_Joker
      27.07.2017 12:55
      +1

      А какое преимущество даст испоьзование битового поля?


      1. t3hk0d3
        27.07.2017 14:07

        Читаемость и поддерживаемость кода? Этого вам недостаточно?


        1. Punk_Joker
          27.07.2017 14:39

          Можете привести пример реализации этого с помощью битового поля?


          1. t3hk0d3
            27.07.2017 15:22

            Быстро в браузере по памяти и быстрым гуглением набросал:

            typedef struct {
                  uint8_t registerSelect : 1;
                  uint8_t modeSelect : 1;
                  uint8_t parityMode : 2;
                  uint8_t stopBitSelect : 1;
                  uint8_t characterSize : 3;
                  uint8_t clockPolarity : 1;
            } UCSRC_t;
            
            #define USART_CHAR_SIZE_8BIT 3;
            
            #define USART (*(volatile UCSRC_t*)_SFR_MEM_ADDR(UCSRC))
            
            # сам код
            USART.registerSelect = 1;
            USART.characterSize = USART_CHAR_SIZE_8BIT;
            


            1. Alex_Sa
              28.07.2017 01:33
              +1

              Интересно посмотреть во что это оттранслируется. Есть у меня подозрение, что из-за того что USART у нас volatile это приведет к тому, что в место одной записи в регистр, как было в оригинальном коде, будет будет 2 чтения, 2 NOT, 2 OR и 2 записи обратно. Что для мелких контроллеров достаточно рессурсоемко как с точки зрения размера бинарника (флеша там кот наплакал) так и по скорости выполнения.


              1. t3hk0d3
                28.07.2017 03:39

                Зависит от оптимизаций компилятора — некоторые умеют оптимизировать в одну инструкцию, некоторые нет.

                С другой стороны это код изначальной инициализации — он выполняется один раз, кол-во инструкций не так и важно. Плюс если действительно нужна оптимизация по тактам — это тоже несложно организовать.

                Но почему-то в большинстве кода который я встречал под микроконтроллеры — люди совсем не задумываются о качестве кода, при этом оправдывают это якобы большим кол-вом инструкций/тактов не смотря на то что это лимитирующим фактором не является — преждевременная оптимизация как она есть.