Adapter sterowania w kierownicy Ford do radia Kenwood (jak zrobić)

Aktualizacje:
[2018-04-27] - Drobne poprawki

Jakiś czas temu postanowiłem wymienić standardowe radio Ford 6000CD na radio 1dinowe Kenwood. Zrobiłem tak, przez słabą jakoś dźwięku radia oryginalnego i brak portu USB (nie jest zbyt wygodne szukanie i wymienianie płyt CD podczas jazdy).

Po tym zabiegu po jakimś czasie zaczęło mi brakować obsługi radia poprzez sterowanie przy kierownicy (nie jest ono kompatybilne z radiami Kenwood).

Zdecydowałem się zrobić adapter, który byłby podłączony pomiędzy sterowaniem z kierownicy a moim radiem. Można powiedzieć, nie ma problemu, wystarczy kupić taki adapter w necie. Tylko jak się okazuje, takie adaptery kosztują trochę (ok 100-150zł) a jak zobaczycie, można zrobić go niższym kosztem (ok 15zł).

Ten poradnik odnosi się do sterowania Ford'owskiego i radia Kenwood (moje radio to model KMM-122Y z 2017 roku) jak na zdjęciach poniżej. Może być przydatny dla innego sterowania na rezystorach ale wtedy należy zmienić część odpowiedzialną za odczytywanie napięcia (ADC).

Rysunek 1. Sterowanie z kierownicy (Ford) Rysunek 2. Przewód zdalnego sterowania radia Kenwood

Co będzie potrzebne do zbudowania adaptera:

Część pierwsza - sygnał analogowy z sterowania w kierownicy (Ford)

Sterowanie w kierownicy bazuje na przełączanych rezystancjach (przyciski odłączają różne rezystancje) jak widać na Rysunku 3.

Rysunek 3. Schemat sterowania w kierownicy

Należy zrobić dzielnik napięcia ze sterowaniem z kierownicy wpiętym jako jeden z rezystorów, podłączyć wykonany dzielnik do źródła napięcia i odczytać wartość na przetworniku AC mikrokontrolera.

Rysunek 4. Schemat dzielnika napięcia

W dzielniku napięcia przedstawionym na Rysunku 3 użyłem rezystora o wartości 1.47 kΩ ponieważ dawał niezły zakres mierzonego napięcia przy napięciu wejściowym 5V.

W miejsce rezystora R5 należy wpiąć sterowanie z kierownicy.

W Tabeli 1 umieściłem zmierzone rezystancje sterowania z kierownicy i odpowiadające im wartości napięcia na dzielniku napięcia.

Tabela 1. Rezystancje i napięcia sterowania w kierownicy
Funkcja Rezystancja [Ω] Napięcie na dzielniku [V]
NC 5040 3.87
SEL 1050 2.08
SEEK- 562 1.38
SEEK+ 301 0.85
VOL+ 147.8 0.46
VOL- 54.4 0.18

Schemat całego układu przedstawiony jest na Rysunku 5. Jak widać jest to dosyć prosty układ.

Rysunek 5. Schemat adaptera

W miejscu złącza J3 można dać przycisk reset (w tym układzie nie jest to za bardzo potrzebne).

Piny sterowania w kierownicy na złączu FAKRA są widoczne na Rysunku 6 (piny 6 i 8 w części B).

Rysunek 6. Złącze Ford FAKRA

W gnieździe (czarna kostka w dole z prawej strony) należy wpiąć w miejsca jak na Rysunku 7. To połączenie było zrobione tylko do testów, w końcowym urządzeniu należy to zrobić bardziej solidnie, żeby nie wypadło ze złączki podczas jazdy.

Rysunek 7. Złącze sterowania z kierownicy

Część druga to sygnał cyfrowy do radia Kenwood

Okazuje się, że sygnał do sterowania radiem Kenwood to protokół NEC, taki sam jak w pilotach zdalnego sterowania tegoż producenta, tylko odwrócony i bez nośnej. Dzięki temu, że nie jest to sygnał na bazie rezystancji, nie trzeba używać na przykład cyfrowego potencjometru.

Informacje o protokóle NEC:

Jak działa transmisja sygnału NEC:

  1. Zaczyna się 9ms st. wys i 4.5ms przerwy
  2. 8 bitów danych adresu
  3. 8 bitów odwróconych danych adresu
  4. 8 bitów polecenia
  5. 8 odwróconych bitów polecenia
  6. Transmisja kończy się pojedynczym 562.5µs st. wys.

Rysunek 8. Protokół ramki wiadomości NEC

Należy wygenerować w mikrokontrolerze cyfrowy sygnał w formacie NEC, odpowiadający przyciśniętemu przyciskowi. Sygnał ten, wysłany przez cyfrowe wyjście mikrokontrolera (PB1) będzie włączał i wyłączał tranzystor Q1. Do kolektora tranzystora podłączony jest przewód remote radia.

Kod adresu radia Kenwood: 0xb9

Kody poleceń:

Tabela 2. Kody poleceń Kenwood
Remote Polecenie Remote Polecenie Remote Polecenie
0 0x00 8 0x08 volume + 0x14
1 0x01 9 0x09 volume - 0x15
2 0x02 track - 0x0a mute 0x16
3 0x03 track + 0x0b tuner 0x1c
4 0x04 rev 0x0c tape 0x1d
5 0x05 ff 0x0d cd 0x1e
6 0x06 play/pause 0x0e cd-md-ch 0x1f
7 0x07 source 0x13 dnpp 0x5e

Przykładowy format danych dla sygnału volume+ :

Tabela 3. Przykładowy format danych
Dane do przesłania
Addr ~Addr Comm ~Comm
0xB9 0x46 0x14 0xEB
10111001 01000110 00010100 11101011
LSB
0x9D 0x62 0x28 0xD7
10011101 01100010 00101000 11010111

Ostateczny wygląd polecenia:

[START] 10011101 01100010 00101000 11010111 [END]

Ten kod należy wysłać (używając protokołu NEC z sygnałami początki i końca) na tranzystor Q1.

I to by było na tyle. Jak widać, można zrobić taki adapter ok 10 razy mniejszym kosztem niż kupno takiego w sklepie (bez ceny gniazd, kabli i obudowy).

Możliwe, że jeszcze uaktualnię ten poradnik, w programie mikrokontrolera można dodać jeszcze obsługę powtórzeń przycisków.

Poniżej jest program w C do mikrokontrolera (Atmega8).

pilot1.c


/* pilot1.c
 * Ford SWC to Kenwood radio (NEC protocol) adapter
 * Author: Michal Babik <michalb1981@o2.pl>
 */

// Steering wheel remote resistances with a half values to use
// in adc as a compare values
//
// REMOTE KEY   RESISTANCE  ADC VAL     KEY VAL
// NC           5040.0      198         0
//                          152
// SEL          1050.0      106         1
//                          88
// SEEK -       562.0       70          2
//                          56.5
// SEEK +       301.0       43          3
//                          33
// VOL +        147.8       23          4
//                          16
// VOL -        54.4        8           5
//                          4
#define F_CPU 1000000UL

#include <avr/io.h> 
#include <util/delay.h> 
#include <inttypes.h> 

#define sbi(x,y) x |= _BV(y) //set bit - using bitwise OR operator
#define cbi(x,y) x &= ~(_BV(y)) //clear bit - using bitwise AND operator
#define tbi(x,y) x ^= _BV(y) //toggle bit - using bitwise XOR operator
#define is_high(x,y) (x & _BV(y) == _BV(y)) //check if the y'th bit of
//register 'x' is high ... test if its AND with 1 is 1
/* _BV(a) is a macro which returns the value corresponding to 2 to the 
 * power 'a'. Thus _BV(PX3) would be 0x08 or 0b00001000 */

#define NEC_TIME 562.5f // base time in us
#define NEC_LINE_HI 1   // pin value to set line high
#define NEC_LINE_LO 0   // pin value to set line low
#define NEC_PORT PORTB  // kenwood radio remote pin port
#define NEC_PIN PB1     // kenwood radio remote pin
#define NEC_DDR DDRB    // kenwood radio remote direction

#define ADC_SAMPLES 50   // number of samples to read pressed key
// ----------------------------------------------------------------------------
uint8_t adc_read (void);
uint8_t check_pressed_adc_value (uint8_t adc_v);
void couple_more_samples (uint8_t *k_pressed);
void nec_set_pin (uint8_t hilo);
void nec_base (uint8_t hm, uint8_t lm);
void nec_start (void);
void nec_finish (void);
void nec_one (void);
void nec_zero (void);
void nec_8bit (uint8_t *bits);
void nec_data (uint8_t addr, uint8_t dta);
// ----------------------------------------------------------------------------
int
main (void)
{
    /*
    uint8_t k_codes[] = {0x00,    // 0
                         0x01,    // 1
                         0x02,    // 2
                         0x03,    // 3
                         0x04,    // 4
                         0x05,    // 5
                         0x06,    // 6
                         0x07,    // 7
                         0x08,    // 8
                         0x09,    // 9
                         0x0a,    // track -
                         0x0b,    // track +
                         0x0c,    // rev
                         0x0d,    // ff
                         0x0e,    // play / pause
                         0x13,    // source
                         0x14,    // volume +
                         0x15,    // volume -
                         0x16,    // mute
                         0x1c,    // tuner
                         0x1d,    // tape
                         0x1e,    // cd
                         0x1f,    // cd-md-ch
                         0x5e     // dnpp
                         };
    */

    uint8_t ken_code     = 0xb9;
    uint8_t key_codes[]  = {0x00, 0x13, 0x0a, 0x0b, 0x14, 0x15};
    uint8_t key_pressed  = 0;
    uint8_t prev_pressed = 0;

    NEC_DDR = _BV (NEC_PIN); // Communication pin as output
    cbi (NEC_PORT, NEC_PIN); // set nec pin low

    ADMUX= (1 << REFS0) | (1 << ADLAR);  //AVCC, left align
    // only 8 highest adc bits will be used, there is no need for a 10 bit precision

    ADCSRA= (1 << ADEN) | (1 << ADPS2);  //ad enable, prescaler 16 (62.5kHz)

    while (1) {
        key_pressed = check_pressed_adc_value (adc_read ());
        if (key_pressed != prev_pressed) {
            if ((key_pressed > 0) && (prev_pressed == 0)) {
                couple_more_samples (&key_pressed);
                nec_start ();
                nec_data (ken_code, key_codes[key_pressed]);
                nec_finish ();
            }
            prev_pressed = key_pressed;
        }
    }
	return 0;
}
// ----------------------------------------------------------------------------
uint8_t
adc_read (void)
{
    //Start Single conversion
    ADCSRA |= (1 << ADSC);
    //Wait for conversion to complete
    while (!(ADCSRA & (1 << ADIF)));
    //Clear ADIF by writing one to it
    ADCSRA |= (1 << ADIF);
    return ADCH; // only 8 highest bits
}
// ----------------------------------------------------------------------------
uint8_t
check_pressed_adc_value (uint8_t adc_v)
{
    uint8_t key_val = 0; // nothing (0)
    if (adc_v < 152) {
        key_val++; // SEL (1)
        if (adc_v < 88) {
            key_val++; // SEEK - (2)
            if (adc_v < 57) {
                key_val++; // SEEK + (3)
                if (adc_v < 33) {
                    key_val++; // VOL + (4)
                    if (adc_v < 16 && adc_v > 4) {
                        key_val++; // VOL - (5)
                    }
                }
            }
        }
    }
    return key_val;
}
// ----------------------------------------------------------------------------
void
couple_more_samples (uint8_t *k_pressed)
{
    // take couple more samples and create a small histogram of
    // of values, find which key has most counts
    uint8_t key_rep[] = {0, 0, 0, 0, 0, 0};
    key_rep[*k_pressed]++;
    for (uint8_t i=0; i<ADC_SAMPLES; ++i) {
        key_rep[check_pressed_adc_value (adc_read ())]++;
    }
    uint8_t max_v = 0;
    for (uint8_t j=1; j<6; ++j) {
        if (key_rep[j] > max_v) {
            max_v = key_rep[j];
            *k_pressed = j;
        }
    }
}
// ----------------------------------------------------------------------------
void
nec_set_pin (uint8_t hilo)
{
    if (hilo) sbi (NEC_PORT, NEC_PIN); // set high
    else      cbi (NEC_PORT, NEC_PIN); // set low
}
// ----------------------------------------------------------------------------
void
nec_base (uint8_t hm, uint8_t lm)
{
    nec_set_pin (NEC_LINE_HI);
    for (uint8_t i=0; i<hm; ++i) 
        _delay_us (NEC_TIME);
    nec_set_pin (NEC_LINE_LO);
    for (uint8_t i=0; i<lm; ++i) 
        _delay_us (NEC_TIME);
}
// ----------------------------------------------------------------------------
void
nec_start (void)
{
    nec_base (16, 8); // 9ms - 1, 4.5ms - 0
}
// ----------------------------------------------------------------------------
void
nec_finish (void)
{
    nec_base (1, 0); // 562.5us - 1, 0 - 0
}
// ----------------------------------------------------------------------------
void
nec_one (void)
{
    nec_base (1, 3); // 562.5us - 1, 1.6875us - 0
}
// ----------------------------------------------------------------------------
void
nec_zero (void)
{
    nec_base (1, 1); // 562.5us - 1, 562.5us - 0
}
// ----------------------------------------------------------------------------
void
nec_8bit (uint8_t *bits)
{
    for (uint8_t i=0; i<8; ++i) {
        if (*bits & 0x01)
            nec_one ();
        else
            nec_zero ();
        *bits = *bits >> 1;
    }
}
// ----------------------------------------------------------------------------
void
nec_data (uint8_t addr, uint8_t dta)
{
    uint8_t naddr = ~addr;
    uint8_t ndta  = ~dta;
    nec_8bit (&addr);
    nec_8bit (&naddr);
    nec_8bit (&dta);
    nec_8bit (&ndta);
}
// ----------------------------------------------------------------------------

Referencje:

  1. https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol
  2. https://exploreembedded.com/wiki/NEC_IR_Remote_Control_Interface_with_8051
  3. https://forum.arduino.cc/index.php?topic=358121.0
  4. http://www.panuworld.net/minidisc/fordsony/index.htm
  5. http://www.angelfire.com/nd/maza/kenwood.html
  6. http://www.kinotechnik.edis.at/pages/atmel/kw-kangoo-e.html


Powrót na stronę główną