Now that we have RPM value let's display it nicely. Instead of inventing a bicycle, we can use TunerStudio - it has a free Lite version, it's multi-platform and it's just the right tool for the job!
Phil from EFI Analytics has helper me with a minimal configuration file:
[TunerStudio]
queryCommand = "H"
signature = "MShift v0.01" ; signature is expected to be 7 or more characters.
[Constants]
endianness = little
nPages = 1
pageSize = 128
pageReadCommand = "C"
[OutputChannels]
ochGetCommand = "O"
ochBlockSize = 4
; name = class, type, offset, shape, units, scale, translate, lo, hi, digits
rpm = scalar, U32, 0, "RPM", 1.00000, 0.00000, 0.00, 3000.0, 0 ; * ( 4 bytes)
[TableEditor]
[FrontPage]
; Gauges are numbered left to right, top to bottom.
; 1 2 3 4
; 5 6 7 8
; currently a minimum of 6 gauges must be on the dash, this appears to be an old not needed limitation. next release 1 will work.
gauge1 = tachometer
gauge2 = tachometer
gauge3 = tachometer
gauge4 = tachometer
gauge5 = tachometer
gauge6 = tachometer
[GaugeConfigurations]
;Name Var Title Units Lo Hi LoD LoW HiW HiD vd ld
tachometer = rpm, "Engine Speed", "RPM", 0, 8000, 200, 500, 6000, 6000, 0, 0
[Menu]
|
Since we have already used serial-over-usb port for debug messages, we would need a $3 uart USB module to support the second communication channel.
Now with just a page of code we implemet the protocol
static SerialConfig tsSerialConfig = { TS_SERIAL_SPEED, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };
typedef struct {
int rpm;
} TunerStudioOutputChannels;
TunerStudioOutputChannels tsOutputChannels;
// values within this array are not used yet
static char constants[128];
static void handleHelloCommand() {
chprintf(CONSOLE_DEVICE, "got H (Hello)\r\n");
chSequentialStreamWrite(TS_SERIAL_DEVICE, SIGNATURE, strlen(SIGNATURE) + 1);
}
static void handleOutputChannelsCommand() {
chSequentialStreamWrite(TS_SERIAL_DEVICE, &tsOutputChannels, sizeof(tsOutputChannels));
}
static void handleConstantsCommand() {
chprintf(CONSOLE_DEVICE, "got C (Constants)\r\n");
chSequentialStreamWrite(TS_SERIAL_DEVICE, constants, 128);
}
static void handleTSCommand(short code) {
if (code == 'H') {
handleHelloCommand();
} else if (code == 'O') {
handleOutputChannelsCommand();
} else if (code == 'C') {
handleConstantsCommand();
} else {
chprintf(CONSOLE_DEVICE, "got unexpected command %c:%d\r\n", code, code);
}
}
static msg_t tsThreadEntryPoint(void *arg) {
(void) arg;
chRegSetThreadName("tunerstudio thread");
while (true) {
short code = (short) chSequentialStreamGet(TS_SERIAL_DEVICE);
handleTSCommand(code);
}
return 0;
}
void startTunerStudioConnectivity(void) {
palSetPadMode(TS_SERIAL_PORT, TS_SERIAL_RX_PIN, PAL_MODE_ALTERNATE(7));
palSetPadMode(TS_SERIAL_PORT, TS_SERIAL_TX_PIN, PAL_MODE_ALTERNATE(7));
sdStart(TS_SERIAL_DEVICE, &tsSerialConfig);
chThdCreateStatic(TS_WORKING_AREA, sizeof(TS_WORKING_AREA), NORMALPRIO, tsThreadEntryPoint, NULL);
}
int main(void) {
halInit();
chSysInit();
// serial-over-usb initialization
usb_serial_start();
// configure input signal pin
palSetPadMode(CRANK_INPUT_PORT, CRANK_INPUT_PIN, PAL_MODE_ALTERNATE(GPIO_AF_TIM2));
// start input capture - we will handle input events and calculate RPM based on the timestamps
icuStart(&CRANK_DRIVER, &wave_icucfg);
icuEnable(&CRANK_DRIVER);
startTunerStudioConnectivity();
while (TRUE) {
// RPM value is updated by the input event handler
chprintf(&SDU1, "rpm: %d\r\n", rpm);
tsOutputChannels.rpm = rpm;
chThdSleep(100);
}
return 0;
}
|
Voila, instant gratification
It really isn't that hard, right?
Here is the whole project source: tachometer_ts.zip
|