Hi guys, I don't really have much knowledge on arduino programming (yet) but use a code made by others quite frequently for my diy projects.
I have the following code to 8 bytes of data via SPI (it emulates arduino inputs as button press on Thrustmaster racing wheel) and run it on Arduino Nano:
/*
Ver. 1.01 - 2019-06-26
Changelog:
1.01 - 2019-06-25 - Wiring changed. Orange wire (SS) goes to pin D10 only. D2 now used for Button 5 (was at D11)
1.00 - 2019-06-17 - initial, based on tx_rw_ferrari_458_wheel_emu_16buttons.ino
This sketch emulates Thrustmaster TMX Pro stock wheel
allowing to connect arduino to TMX Pro wheelbase
and emulate button presses - up to 16 buttons can be connected to arduino (because stock TMX Pro wheel has 12 buttons + 4 for D_Pad)
Thrustmaster TMX Wheelbase cable pinout (2.54mm XH female connector)
1 - Green - not used
2 - Blue - GND (ground)
3 - White - MISO (master in, slave out - to read the data from the wheel)
4 - Orange - SS (slave select, or PL - parallel load, set it to 0 when you want to read the data)
5 - Red - CLK (clock impulses)
6 - Black - +VCC (+3.3 volts! if you have Arduino 5V version - use external +3.3V power or use the wheelbase power!!!)
Arduino UNO pins -> TMX wheelbase cable pins
Arduino GND -> TMX Blue wire (2)
Arduino pin 12 (MISO) -> TMX White wire (3) (data from wheel to base)
Arduino pin 10 (SS) -> TMX Orange wire pin (4) (INT0 moved to D10)
Arduino pin 13 (SCK) -> TMX Red wire (5)
Arduino +5V -> TMX Black wire (6) (it gives 3.3V, but must be connected into +5V socket on arduino uno side)
Button mappings (we send 8 bytes to wheelbase, only 3 first bytes matter, only 16 bits are used for buttons)
Byte 1 (constant)
1 - not used
1 - n/u
0 - n/u
1 - n/u
1 - n/u
0 - n/u
0 - n/u
1 – n/u
Byte 2
1 – Menu (button 8, bottom center) -> pin 7 (portD bit7)
1 – Menu (button 12, bottom right) -> pin 6 (portD bit6)
1 – Gear Up (RB paddle, button 2) -> pin 5 (portD bit5)
1 – B (button 9) -> pin 4 (portD bit4)
1 – RB (button 11, bottom right) -> pin 3 (portD bit3)
1 – A (button 5) -> pin 2 (portD bit2)
1 – Y (button 10) -> pin 1 (portD bit1) - if you don't use serial debug (UART TX)
1 – X (button 3) -> pin 0 (portD bit0) - if you don't use serial debug (UART RX)
Byte 3
1 – D-Pad Down -> pin 9 (portB bit1)
1 – View (button 7, bottom center) -> pin 8 (portB bit0)
1 – LB (button 6, bottom left) -> pin A5 (portC bit5)
1 – View (button 4, bottom left) -> pin A4 (portC bit4)
1 – Gear Down (LB paddle, button 1) -> pin A3 (portC bit3)
1 - D-Pad Left -> pin A2 (portC bit2)
1 - D-Pad Up -> pin A1 (portC bit1)
1 - D-Pad Right -> pin A0 (portC bit0)
Each push button has 2 pins - you connect one to Arduino, another to the Ground :)
Pressed button gives you "0" (grounded signal), released = "1" (pulled-up to +VCC)
Feel free to modify and use for your needs.
This sketch and the documentation above provided "AS IS" under the BSD New license.
http://opensource.org/licenses/BSD-3-Clause
(c) Taras Ivaniukovich (blog@rr-m.org)
http://rr-m.org/blog/
Credits:
Jake L. for reading his TMX Pro wheel and testing
Virág I. for Pin Change Interrupt idea (move external interrupt from Pin 2 to Pin 10)
*/
byte wheelState [8]; // local push-buttons state saved here
volatile byte pos;
void setup (void) {
DDRB |= B00000011; // digital pins 8,9 used as inputs with a pull-up to +VCC
PORTB |= B00000011;
DDRC |= B00111111; // pins 14-19 (A0 - A5) also used as digital inputs
PORTC |= B00111111; // pulled-up to +VCC via internal 100k resistors
DDRD |= B11111111; // digital pins 0-7 used as inputs
PORTD |= B11111111; // pulled-up to +VCC via internal 100k resistors
wheelState[0] = B11011001; // TMX Pro wheel first data byte - constant
wheelState[1] = B11111111; // second data byte - buttons
wheelState[2] = B11111111; // third data byte - buttons
wheelState[3] = B11111111; // this and below - not used, but wheelbase reads all 8 bytes...
wheelState[4] = B11111111;
wheelState[5] = B11111111;
wheelState[6] = B11111111;
wheelState[7] = B11111111;
/* Wheelbase acts as SPI Master device, Arduino as SPI Slave.
* SPCR SPI control Register
* SPIE // Enables the SPI interrupt when 1
* SPE // Enables the SPI when 1
* DORD // MSB first
* MSTR // Clear MSTR (MSTR = Master)
* CPOL // SPI mode 1 (CPOL|CPHA 0/1) - Clock Polarity - see Atmega8a datasheet 40001974A-page 179
* CPHA // Clock Phase
* SPR1 // SPI Clock Rate Select - Master only
* SPR0 // SPI Clock Rate Select - Master only
* 1 1 0 0 0 1?? x x
* SPIE SPE DORD MSTR CPOL CPHA SPR1 SPR0 */
SPCR = B11000000; // SPI Slave setup
pinMode(MISO, OUTPUT);
/* We use pin 10 (SS) to identify when SPI transfer starts (wheelbase sets it to LOW)
* and when it ends (wheelbase sets it to HIGH and we reset pos counter + prepare SPDR for next transfer)
* Here we enable external interrupt on pin 10 - via Pin Change Interrupt.
*
* https://www.teachmemicro.com/arduino-interrupt-tutorial/
* Pin Change Interrupt services
* PCICR = x x x x x PCIE2 PCIE1 PCIE0 - enable PCINT group. PCIE0 = PORTB pins group (PCINT0-PCINT7 = PB0-PB7 = pins 8-13 + XTAL), PCIE1 = PORTC group, PCIE2 = PORTD group.
*
* PCMSK0 = PCINT7 PCINT6 PCINT5 PCINT4 PCINT3 PCINT2 PCINT1 PCINT0 - those are PORTB PB0-PB7 (digital pins 8 - 13, XTAL1, XTAL2)
* PCMSK1 = x PCINT14 PCINT13 PCINT12 PCINT11 PCINT10 PCINT9 PCINT8 - those are PORTC PC0-PC6 (A0-A5, Reset)
* PCMSK2 = PCINT23 PCINT22 PCINT21 PCINT20 PCINT19 PCINT18 PCINT17 PCINT16 - those are PORTD PD0-PD7 (digital pins 0 - 7) */
PCMSK0 = B00000100; // Only react to PCINT2 = PB2 = D10 (SS pin) (when signal on this pin changes 0->1 OR 1->0 - interrupt happens)
PCIFR = B00000000; // clear all interrupt flags
PCICR = B00000001; // Enable PCIE0 = PORTB group (PCINT0 - PCINT7 = digital pins 8-13)
sei(); // enable global interrupts
}
// External interrupt handler for whole PCIE0 group. Prepare for next SPI communication
ISR (PCINT0_vect) {
if (PINB2) {
SPDR = wheelState[0]; // load first byte into SPI data register
pos = 1;
}
}
// SPI interrupt routine - this transfers data to wheelbase byte-by-byte
ISR (SPI_STC_vect) {
SPDR = wheelState[pos++]; // load the next byte to SPI output register and return.
}
void loop() {
// scan button presses and save that to wheelState array. Data transfer to wheelbase is interrupt-driven - see above.
// wheelState[0] - always constant for TMX Pro wheel
wheelState[1] = PIND; // read 8 buttons from PORTD
wheelState[2] = ((PINB & B00000011) << 6) | (PINC & B00111111); // take bits 0,1 from PORTB + bits 0-5 from PORTC
}
I connected a cheap rotary encoder to GND, D3 and D7. The problem is it's giving me a value in both directions when turning either CW or CCW. I thought it's a faulty encoder but tested few units and they all behave the same way.
It might be that the reason is poor quality of the encoder, but I used exactly same units on Arduino Leonardo with this code (it's a button box you connect to PC), and had no such issues (ran out of char for message length, will put the code in first reply).
The button box code (second one) is perfectly clear for me, unfortunately the emulator (first code) is just a black magic and I have no idea what makes my rotary encoder malfunction in one case and work perfectly fine in another.
I would be extremely grateful for your help!