commit 6230d6dc18c74fbfaac41cf1c390ab610a56ef2f Author: Daniil A. Smirnov Date: Wed Mar 8 13:15:50 2023 +0300 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..88f034f --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +Модуль для управления поворотными устройствами Yaesu серий DXA/DXC через usb +Эмулирует протокол Yaesu GS232B для повороток с углом 450 градусов +Параметры порта: 9600 8N1 +Поддерживаемые команды: +BOOT - переход в bootloader +0x18 (hex) - переход в bootloader +CAL0 - калибровка в позиции 0 градусов(левый концевик) +CAL90 - калибровка в позиции 90 градусов +CAL180 - калибровка в позиции 180 градусов +CAL270 - калибровка в позиции 270 градусов +CAL360 - калибровка в позиции 360 градусов +CAL450 - калибровка в позиции 450 градусов(overlap, правый концевик) +C - чтения текущего азимута +S - остановка вращения +L - вращение в лево(против часовой стрелки) +R - вращение в право(по часовой стрелке) +Mxxx - вращение на заданный азимут, xxx - азимут +X1 - установка скорости вращения, минимум +X2 - установка скорости вращения, средняя 1 +X3 - установка скорости вращения, средняя 2 +X4 - установка скорости вращения, максимальная + +Каждая команда должна заканчиваться символом возврата каретки(0x0D, \r) + diff --git a/fw/Makefile b/fw/Makefile new file mode 100644 index 0000000..b5275d3 --- /dev/null +++ b/fw/Makefile @@ -0,0 +1,53 @@ +# Название проекта +PRG=dxa_rotor_ctl +# МК +MCU=atmega88 +# Частота МК в герцах +FREQ=8000000UL +# GCC +CC=avr-gcc +# OBJCOPY +OBJCOPY= avr-objcopy +# SIZE +SIZE=avr-size +# Флаги GCC +CFLAGS= -Wall -Os -std=gnu99 -lm +# Флаги LD +LDFLAGS= -Wall -lm +# Папка с промежуточными файлами +OBJDIR=obj +# +OUTDIR=out + +# Cписок .c файлов +C_SRC= \ +main.c + +# Список объектных файлов (формируется автоматически на основе списка .с файлов) +OBJS=$(C_SRC:.c=.o) +OBJ=$(addprefix $(OBJDIR)/, $(OBJS)) + +.PHONY: all LD DIR clean + +# Выполняем цели в указанном порядке +all: DIR $(C_SRC) $(OBJS) LD + +# Линковка +LD: $(OBJ) + $(CC) $^ -o $(OBJDIR)/$(PRG).elf $(LDFLAGS) -mmcu=$(MCU) + $(OBJCOPY) -O ihex -R .eeprom -R .fuse -R .lock $(OBJDIR)/$(PRG).elf $(OUTDIR)/$(PRG).hex + $(OBJCOPY) -O ihex -j .eeprom --change-section-lma .eeprom=0 $(OBJDIR)/$(PRG).elf $(OUTDIR)/$(PRG).eep + $(SIZE) -C --format=avr --mcu=$(MCU) $(OBJDIR)/$(PRG).elf + +# +%.o: %.c + $(CC) $< -o $(OBJDIR)/$@ $(CFLAGS) -mmcu=$(MCU) -DF_CPU=$(FREQ) -c + +# Создание папок +DIR: + mkdir -p $(OBJDIR) + mkdir -p $(OUTDIR) + +# +clean: + rm -rf $(OBJDIR) $(OUTDIR) *.hex *.eep diff --git a/fw/main.c b/fw/main.c new file mode 100755 index 0000000..c9229ac --- /dev/null +++ b/fw/main.c @@ -0,0 +1,511 @@ +/* + * USB rotor card + * + * Created: 30.05.2022 + * Update: 11.06.2022 00:34:00 + * Author : R5CA + * Support only GS232B 450deg + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BAUD 9600 +#define MYUBRR F_CPU/16/BAUD-1 + +volatile uint8_t uartRXBuf[16]; // Буфер для данных принятых по UART +volatile uint8_t uartRXBufNum = 0; // Кол-во принятых байт по UART +volatile uint8_t uartRXC = 0; // Флаг окончания приема данных по UART + +#define ROTOR_DDR DDRD +#define ROTOR_PORT PORTD +#define ROTOR_CW PD5 +#define ROTOR_CCW PD6 +#define ROTOR_AZ_IN 0 // Канал АЦП измерения азимута +#define ROTOR_SPEED_DDR DDRB // DDR регулировки скорости +#define ROTOR_SPEED_PORT PORTB // Порт регулировки скорости +#define ROTOR_SPEED_PINNUM PB3 // Номер пина регулировки скорости(обязательно OC2A) + +#define STOP 0 +#define SLOW_START 1 +#define ROTARY 2 +#define SLOW_DOWN 3 + +#define SLOW_DOWN_AZ 20 // Расстояние начала плавной остановки + +uint16_t EEMEM EEPcal0 = 0; // EEProm значение калибровки 0 градусов +uint16_t EEMEM EEPcal90 = 0; // EEProm значение калибровки 90 градусов +uint16_t EEMEM EEPcal180 = 0; // EEProm значение калибровки 180 градусов +uint16_t EEMEM EEPcal270 = 0; // EEProm значение калибровки 270 градусов +uint16_t EEMEM EEPcal360 = 0; // EEProm значение калибровки 360 градусов +uint16_t EEMEM EEPcal450 = 0; // EEProm значение калибровки 450 градусов +uint8_t EEMEM EEPspeed = 128; // EEProm скорость вращения + +uint16_t cal0 = 0; // Значение калибровки 0 градусов +uint16_t cal90 = 0; // Значение калибровки 90 градусов +uint16_t cal180 = 0; // Значение калибровки 180 градусов +uint16_t cal270 = 0; // Значение калибровки 270 градусов +uint16_t cal360 = 0; // Значение калибровки 360 градусов +uint16_t cal450 = 0; // Значение калибровки 450 градусов + +uint8_t rot_cw = 0; // Вращение по часовой +uint8_t rot_ccw = 0; // Вращение против часовой + +volatile uint8_t timer = 0; // Переменная таймера задержки плавного старта/остановки +volatile uint16_t timer2 = 0; // Переменная таймера + +void uartbuf_zero(void) // Обнуление буфера UART +{ + for(uint8_t i=0; i<16; i++) // Обнуление буфера UART + { + uartRXBuf[i] = 0x00; // Обнуление буфера UART + } +} + +void usart_init(unsigned int ubrr) // Инициализация USART +{ + UBRR0H = (unsigned char) (ubrr >> 8); // Установка скорости + UBRR0L = (unsigned char) ubrr; // Установка скорости + UCSR0B = (1<= cal450) // Если считанное значение АЦП больше 450-ой калибровки + { + return 450; // То возвращаем 450 + } + else if(az_raw_data > 0 && az_raw_data < 90) // Если приблизительный азимут от 0 до 90 градусов + { + az_raw_data = lround((adc_val - cal0)*90.0/(cal90-cal0)); // То делаем уточненный расчет по части шкалы в 90 градусов(0-90) с использованием дополнительных калибровок + return az_raw_data; // Возвращаем значчение азимута + } + else if(az_raw_data >= 90 && az_raw_data < 180) // Если приблизительный азимут от 90 до 180 градусов + { + az_raw_data = lround(90.0+((adc_val - cal90)*90.0/(cal180-cal90))); // То делаем уточненный расчет по части шкалы в 90 градусов(90-180) с использованием дополнительных калибровок + return az_raw_data; // Возвращаем значчение азимута + } + else if(az_raw_data >= 180 && az_raw_data < 270) // Если приблизительный азимут от 180 до 270 градусов + { + az_raw_data = lround(180.0+((adc_val - cal180)*90.0/(cal270-cal180))); // То делаем уточненный расчет по части шкалы в 90 градусов(180-270) с использованием дополнительных калибровок + return az_raw_data; // Возвращаем значчение азимута + } + else if(az_raw_data >= 270 && az_raw_data < 360) // Если приблизительный азимут от 270 до 360 градусов + { + az_raw_data = lround(270.0+((adc_val - cal270)*90.0/(cal360-cal270))); // То делаем уточненный расчет по части шкалы в 90 градусов(270-360) с использованием дополнительных калибровок + return az_raw_data; // Возвращаем значчение азимута + } + else if(az_raw_data >= 360 && az_raw_data <= 450) // Если приблизительный азимут от 360 до 450 градусов + { + az_raw_data = lround(360.0+((adc_val - cal360)*90.0/(cal450-cal360))); // То делаем уточненный расчет по части шкалы в 90 градусов(360-450) с использованием дополнительных калибровок + return az_raw_data; // Возвращаем значчение азимута + } + else + { + return az_raw_data; + } +} + +void set_az(uint16_t az_r, uint16_t az_n) // Расчет кратчайшего пути для поворота антенны +{ + int16_t cw; // Переменная для длины пути по часовой стрелке + int16_t ccw; // Переменная для длины пути против часовой стрелки + ccw = az_r - az_n; // Расчет пути против часовой стрелки + if(ccw < 0) // Расчет пути против часовой стрелки + { + ccw += 360; // Расчет пути против часовой стрелки + } + cw = az_n - az_r; // Расчет пути по часовой стрелке + if(cw < 0) // Расчет пути по часовой стрелке + { + cw += 360; // Расчет пути по часовой стрелке + } + if(cw < ccw) // Если по часовой короче, чем против + { + if(cw + az_r <= 450) // и получившийся азимут не выходит за возможности редуктора + { + rot_cw = 1; // То вращаем по часовой стрелке + } + else // Иначе + { + rot_ccw = 1; // против часовой стрелки + } + } + else // Если против часовой короче, чем по + { + if((int16_t)az_r - (int16_t)ccw > 0) // и получившийся азимут не выходит за возможности редуктора + { + rot_ccw = 1; // То вращаем против часовой + } + else // Иначе + { + rot_cw = 1; // По часовой + } + } +} + +void pwm_init(void) // Инициализация ШИМ +{ + TCCR2A |= (1< 0) + { + timer2--; + } +} + +void timer0_stop(void) // Остановка таймера 0 +{ + TCCR0B &= ~(1< 20) // Текущая скорость меньше максимальной и таймер дотикал до переключения + { + step_val += 16; // чуть добавляем скорость + OCR2A = step_val; // чуть добавляем скорость + timer = 0; // обнуляем таймер + } + if(step_val == speed) // Если достигли максимальной скорости + { + rot_stat = ROTARY; // Меняем статус + } + } + if((rot_stat == SLOW_START || rot_stat == ROTARY) && ((rot_ccw == 1 && ((raw_az - new_az) < SLOW_DOWN_AZ)) || (rot_cw == 1 && ((new_az - raw_az) < SLOW_DOWN_AZ))) && manual == 0) // Разница между текущим и заданным азимутом меньше, чем задана для плавной остановки + { + rot_stat = SLOW_DOWN; // Меняем статус + } + if(rot_stat == SLOW_DOWN) // Плавная остановка + { + if(step_val > 31 && timer > 15) // Если значение скорости больше минимальной и таймер дотикал до переключения + { + OCR2A = step_val; // Чуть уменьшаем скорость + step_val -= 16; // Чуть уменьшаем скорость + timer = 0; // обнуляем таймер + } + if(step_val <= 31 && need_stop == 1) + { + ROTOR_PORT &= ~((1<