Адаптер датчика оборотов для Graupner HoTT
Девайс представляет из себя небольшую плату (которая могла бы быть еще меньше, если применить двусторонний монтаж). С одной стороны адаптер подключается к приемнику HoTT, с другой стороны подключается к датчику оборотов.
Распиновки со стороны приемника такая:
- GND - земля,
- V_IN - питание адаптера (5-12В),
- Rx,
- Tx.
Как и в случае с Heli модулем, в рабочем состянии выводы Rx и Tx должны быть замкнуты, то есть замкнули выводы, подали питание на адаптер, адаптер в рабочем положении. В противном случае адаптер входит в режим установки коэффициента редукции трансмиссии (об этом чуть ниже). Я предлагаю соединять эти выводы проводком типа как в Heli модуле, только там не использовался центральный провод питания, а тут он используется. То есть с одной стороны у нас полноценный сервоудлиннитель (земля питание сигнал), который подключается к приемнику в телеметрийный разъем, с другой у нас колодка на 4 пина и два крайних в ней замкнуты.
Со стороны датчика оборотов контакты такие:
- S1 - первый датчик (пока не используется),
- S2 - второй датчик,
- +5V - используется для питания датчика, если это необходимо,
- GND.
Датчик оборотов должен выдавать импулься, кратные числу этих самых оборотов. Импульсы могут иметь амплитуду до 40В, но не менее 4В. Простой пример датчика - это маленький магнитик на роторе и датчик Холла рядом с ним. Этот датчик как раз имеет три ноги: земля, питание, выход. Можно подключить простым сервоудлиннителем. Я использую специальный выход моего регулятора.
Сам адаптер разведен для работы с двумя датчиками оборотов. В принципе и логика обсчета тоже готова (не проверена, правда, пока). Но так как я не нашел удобного способа вывода показаний с двух датчиков в стандартное окно Електрик аир модуля, пока прошивка позволяет работать с одним датчиком (вход S2). Чуть позже покапаюсь с текстовым режимом и сделаю полноценную поддержку двух датчиков. Может быть и настройки после этого будут осуществляться с пульта. В принципе, можно и 4 датчика задействовать, но тут я бы использовал мегу 64, потому как у нее для этого хватит входов внешних прерываний.
Установка коэффициента редукции трансмиссии осущетсвляется аналогично Heli модулю.
Устройство позволяет задавать коэффициент передачи от 0,01 до 99,99. Для магнитного датчика на валу ОР коэффициент будет 1, для случаев с сигналом из ESC, датчиков оборотов, которые вешаются на провода между мотором и ESС, необходимо рассчитывать коэффициент передачи, как шестерней, так и в моторе. В моторе коэффициент передачи равен половине количества магнитов ротора. У меня передатка между валом мотора и ОР составляет 9,18, в моем моторе 8 магнитов, следовательно, моя итоговая передатка получается 9,18 х (8 / 2) = 36,72. Эту цифру мы и запишем в устройство во время настройки.
Для входа в режим программирования необходимо освободить контакты Rx и Tx, а затем подать питание на адаптер. Сделать это можно как со стороны датчика (земля и +5В там есть), так и со стороны приемника (земля и 5-12В). Условием входа в режим программирования являются отсутствие какого либо потенциала на ножках Rx и Tx. Затем необходимо подключить модуль к USB конвертеру тремя проводками (GND – GND, RxD – TxD, TxD – RxD) и подключить конвертер к компьютеру.
Также нужна терминальная программа. В настройках терминалки мы выставляем скорость 19 200 бод, а остальное все как всегда (без четности, 8 бит, 1 стоп бит).
Для установки коэффициента передачи трансмиссии применяется команда, состоящая из одной буква “r” и четырех цифр. Цифры выравниваются по центру, потому как есть дробная часть. Например:
r0918 – устанавливает коэффициент передачи, равный 9,18,
r3672 – устанавливает коэффициент передачи, равный 36,72,
r0100 – устанавливает коэффициент передачи, равный 1,00.
На выполенение команды адаптер должен ответить:
Команда: r3672 Ответ: 36.72 ОК
Только в этом случае можно считать, что коэффициент установлен.
Для любителей покопаться в коде, ниже исходник:
#define u08 unsigned char
#define u16 unsigned int
#define F_CPU 8000000L
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
#include <avr/eeprom.h>
#include <stdint.h>
#define baudrate 19200L
#define bauddivider (F_CPU/(16*baudrate)-1)
#define HI(x) ((x)>>8)
#define LO(x) ((x)& 0xFF)
#define r_gear_ratio_0 0x0066
#define r_gear_ratio_1 0x0067
#define r_gear_ratio_2 0x0068
#define r_gear_ratio_3 0x0069
volatile u08 USART_msg_number = 0,
UDR_data[2],
UDR_set_data[5],
UDR_tmp,
sys_status = 0,
settings = 0,
tmr_ovf = 0,
cnt = 0,
data_update = 0;
volatile u16 tcnt1, rpm;
volatile float gear_ratio;
struct HOTT_EAM_MSG {
u08 start_byte; //#01 start int8_t
u08 eam_sensor_id; //#02 EAM sensort id. constat value 0x8e
u08 warning_beeps; //#03 1=A 2=B ... or 'A' - 0x40 = 1
// Q Min cell voltage sensor 1
// R Min Battery 1 voltage sensor 1
// J Max Battery 1 voltage sensor 1
// F Mim temperature sensor 1
// H Max temperature sensor 1
// S Min cell voltage sensor 2
// K Max cell voltage sensor 2
// G Min temperature sensor 2
// I Max temperature sensor 2
// W Max current
// V Max capacity mAh
// P Min main power voltage
// X Max main power voltage
// O Min altitude
// Z Max altitude
// C (negative) sink rate m/sec to high
// B (negativeADIF 4 6) sRXC 7 11ink rate m/3sec to high
// N climb rate m/sec to high
// M climb rate m/3sec to high
u08 sensor_id; //#04 constant value 0xe0
u08 alarm_invers1; //#05 alarm bitmask. Value is displayed inverted
//Bit# Alarm field
// 0 mAh
// 1 Battery 1
// 2 Battery 2
// 3 Temperature 1
// 4 Temperature 2
// 5 Altitude
// 6 Current
// 7 Main power voltage
u08 alarm_invers2; //#06 alarm bitmask. Value is displayed inverted
//Bit# Alarm Field
// 0 m/s
// 1 m/3s
// 2 Altitude (duplicate?)
// 3 m/s (duplicate?)
// 4 m/3s (duplicate?)
// 5 unknown/unused
// 6 unknown/unused
// 7 "ON" sign/text msg active
u08 cell1_L; //#07 cell 1 voltage lower value. 0.02V steps, 124=2.48V
u08 cell2_L; //#08
u08 cell3_L; //#09
u08 cell4_L; //#10
u08 cell5_L; //#11
u08 cell6_L; //#12
u08 cell7_L; //#13
u08 cell1_H; //#14 cell 1 voltage high value. 0.02V steps, 124=2.48V
u08 cell2_H; //#15
u08 cell3_H; //#16
u08 cell4_H; //#17
u08 cell5_H; //#18
u08 cell6_H; //#19
u08 cell7_H; //#20
u08 batt1_voltage_L; //#21 battery 1 voltage lower value. opt. cell8_L 0.02V steps
u08 batt1_voltage_H; //#22
u08 batt2_voltage_L; //#23 battery 2 voltage lower value. opt cell8_H value-. 0.02V steps
u08 batt2_voltage_H; //#24
u08 temp1; //#25 Temperature sensor 1. 0°=20, 26°=46
u08 temp2; //#26 temperature sensor 2
u08 altitude_L; //#27 Attitude lower value. unit: meters. Value of 500 = 0m
u08 altitude_H; //#28
u08 current_L; //#29 Current in 0.1 steps
u08 current_H; //#30
u08 main_voltage_L; //#30 Main power voltage (drive) in 0.1V steps
u08 main_voltage_H; //#31
u08 batt_cap_L; //#32 used battery capacity in 10mAh steps
u08 batt_cap_H; //#33
u08 climbrate_L; //#34 climb rate in 0.01m/s. Value of 30000 = 0.00 m/s
u08 climbrate_H; //#35
u08 climbrate3s; //#36 climbrate in m/3sec. Value of 120 = 0m/3sec
u08 rpm_L; //#37 RPM. Steps: 10 U/min
u08 rpm_H; //#38
u08 electric_min; //#39 Electric minutes. Time does start, when motor current is > 3 A
u08 electric_sec; //#40
u08 speed_L; //#41 (air?) speed in km/h. Steps 1km/h
u08 speed_H; //#42
u08 stop_byte; //#43 stop int8_t
//u08 parity; //#44 CRC/Parity
} hott_eam_msg;
ISR (USART_RXC_vect)
{
switch (settings)
{
case 0:
UDR_data[1] = UDR;
if ((USART_msg_number == 0) && (UDR_data[1] == 0x80))
{
UDR_data[0] = UDR_data[1];
USART_msg_number = 1;
}
else
{
if ((UDR_data[1] == 0x8e) || (UDR_data[1] == 0x80)) sys_status = 1;
USART_msg_number = 0;
}
break;
case 1:
UDR_tmp = UDR;
if ((UDR_tmp == 'r') || (UDR_tmp == 'R'))
{
USART_msg_number = 0;
}
UDR_set_data[USART_msg_number] = UDR_tmp;
USART_msg_number++;
break;
}
}
ISR (INT1_vect) //?????????? ?? ?????????? ??????? ????????
{
if (cnt < 36) cnt++;
else
{
tcnt1 = TCNT1;
TCNT1 = 0x0000;
cnt = 0;
if (!data_update)
{
data_update = 1;
}
}
}
ISR (TIMER1_OVF_vect)
{
tmr_ovf = 1;
}
void UART_INIT (void);
void INT1_INIT (void);
void TIMER1_INIT (void);
void UART_RX_EN (void);
void UART_TX_EN (void);
void SEND_EAM_MSG (void);
void DATA_UPDATE (void);
void main (void)
{
DDRD &= ~(1<<1);
PORTD |= 1<<1;
DDRD |= 1<<0;
PORTD &= ~(1<<0);
sei();
if (PIND & (1<<1))
{
UART_INIT();
settings = 1;
UCSRB |= 1<<TXEN | 1<<RXEN;
_delay_ms(20);
while (1)
{
while (USART_msg_number != 5);
USART_msg_number = 0;
for (u08 i = 1; i < 5; i++)
{
UDR_set_data[i] &= 0x0f;
}
if (UDR_set_data[0] == 'r')
{
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_0;
EEDR = UDR_set_data[1];
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_1;
EEDR = UDR_set_data[2];
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_2;
EEDR = UDR_set_data[3];
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_3;
EEDR = UDR_set_data[4];
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
///////////////////////////
_delay_ms(3);
UDR = ' ';
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_0;
EECR |= (1<<EERE);
UDR = EEDR | 0x30;
_delay_ms(3);
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_1;
EECR |= (1<<EERE);
UDR = EEDR | 0x30;
_delay_ms(3);
UDR = '.';
_delay_ms(3);
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_2;
EECR |= (1<<EERE);
UDR = EEDR | 0x30;
_delay_ms(3);
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_3;
EECR |= (1<<EERE);
UDR = EEDR | 0x30;
_delay_ms(3);
UDR = ' ';
_delay_ms(3);
UDR = 'O';
_delay_ms(3);
UDR = 'K';
}//if...
} // while
}// if(...
////////////считываем значения из ПЗУ и формируем коэффициент передачи трансмиссии.
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_0;
EECR |= (1<<EERE);
gear_ratio = (float)EEDR*10;
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_1;
EECR |= (1<<EERE);
gear_ratio += (float)EEDR;
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_2;
EECR |= (1<<EERE);
gear_ratio += ((float)EEDR)/10;
while (EECR & (1<<EEWE));
EEAR = r_gear_ratio_3;
EECR |= (1<<EERE);
gear_ratio += ((float)EEDR)/100;
UART_INIT();
INT1_INIT ();
TIMER1_INIT ();
UART_RX_EN ();
memset(&hott_eam_msg, 0, sizeof(struct HOTT_EAM_MSG));
hott_eam_msg.start_byte = 0x7c;
hott_eam_msg.eam_sensor_id = 0x8e; //HOTT_TELEMETRY_EAM_SENSOR_ID
hott_eam_msg.sensor_id = 0xe0;
hott_eam_msg.stop_byte = 0x7d;
while(1)
{
if (sys_status) SEND_EAM_MSG ();
if (data_update) DATA_UPDATE ();
}
}
void UART_INIT (void)
{
UBRRL = LO(bauddivider);
UBRRH = HI(bauddivider);
UCSRA = 0;
UCSRC |= 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
UCSRB |= 1<<RXCIE;
}
void TIMER1_INIT (void)
{
TIMSK &= ~((1<<TICIE1)|(1<<OCIE1A)|(1<<OCIE1B)|(1<<TOIE1));
TCCR1B |= 5<<CS10; // prescale /1024
TIMSK |= 1<<TOIE1;
}
void UART_RX_EN (void)
{
UCSRB &= ~(1<<TXEN);
UCSRB |= 1<<RXEN;
}
void UART_TX_EN (void)
{
UCSRB &= ~(1<<RXEN);
UCSRB |= 1<<TXEN;
}
void SEND_EAM_MSG (void)
{
_delay_ms(5);
UART_TX_EN ();
u08 msg_len = sizeof(struct HOTT_EAM_MSG);
u08 *msg_pntr;
u08 msg_crc=0;
msg_pntr = &hott_eam_msg;
while (msg_len)
{
UDR=*msg_pntr;
msg_crc+=*msg_pntr;
msg_pntr++;
msg_len--;
_delay_ms(3);
}
msg_crc &= 0xFF;
UDR=msg_crc;
UART_RX_EN ();
sys_status = 0;
}
void INT1_INIT (void)
{
DDRD &= ~(1<<3);
PORTD |= 1<<3;
MCUCR |= 3<<ISC10;
GICR |= 1<<INT1;
}
void DATA_UPDATE (void)
{
if (!tmr_ovf)
{
rpm = (u16)((2160 / (tcnt1 * 0.00128)) / gear_ratio);
}
else
{
rpm = 0;
}
tmr_ovf = 0;
hott_eam_msg.rpm_L = rpm;
hott_eam_msg.rpm_H = rpm>>8;
data_update = 0;
}
Ну вот собственно и все. Если есть вопросы, задавайте, буду рад ответить. Файлы к статье (схема, печатная плата, прошивка) тут.
UPD: Не так давно выяснилось, что адаптер с текущей прошивкой не корректно работает на передаточным отношении 1:1. Я делал его в основном для вертолета с большим передаточным числом, и думал, что будет достаточно просто “недоразделить” число оборотов и отправить это дело приемнику. Оказалось что нет. Валерий возможно именно об этом пытался мне рассказать в комментариях, но я его не понял, а он был не достаточно настойчив.
Тем не менее, косяк этот я поправил. Не стал делать универсальную прошивку, сделал “экспресс” прошивку для систем с передаточным числом 1:1 и диапазоном до 15 000 об/мин. Адаптер считает и дальше, но там цена деления одного тика таймера уже получает очень высокой, но грубо оценить обороты можно. Ничего настраивать в этой прошивки не нужно. Залил, подключил и полетел. Прошивка доступная по ссылке. Не знаю, буду ли делать универсальную - очень туго со временем. Пока так.