IPDM56

From openinverter.org wiki
Jump to navigation Jump to search

Getting started with the iPDM56 VCU

Note: all documentation is under https://github.com/celeron55/ipdm56

Connect the board to USB and power it up

This is a sanity check before you start soldering and chase your tail troubleshooting.

Use a good quality micro USB cable and power the board up through it via a charger or computer.

both LEDs are lit after the initial Arduino sketch has started

Solder the connector onto the board

Before you start soldering, test fit the PCB to see if the remaining holes line up with the threaded mounts of the enclosure.

Apart from soldering equipment you will need two small screws and nuts to secure the 56 pin header connector to the PCB so it is secured during soldering.

If you're in a rush, you should alternate between the two sections to avoid overheating the board and reduce stresses on the joints.

Connector PCB screws (borrowed from ATX mainboard mounts)
Connector secured for soldering
Alternating between both sides
all 56 pins soldered

Wiring in the pins

(starting with the USB connection)


Plastic grid removed, note different numbering
Numbering scheme of connector can be different!
Perforate the seal with multimeter probe, put the 4 wires from the USB cable through the grid.
Front view, note different numbering
Square and kerb a chopstick for pushing the crimped pins
Push the pins through


For powering the board from 12V DC it makes sense to use either Pin 3 or 4 for ground but only Pin 2 for +12V. Pin 1 was +Vbat on V1.0, it's M11 on V1.1. An automotive fuse is recommended.

12V connected

If you want to use high side outputs, you have wire the supply into Pins 55, 56 and 33 for HOUT 1-9.


Q: Can this be a different voltage? HOUT10 is supplied by Pin 2?

First program - blink LEDs off board

Set up the Arduino IDE. The ATMega 328 is also used on Arduino UNO, Arduino Pro Mini and Arduino Nano, so programming should be familiar. See https://docs.arduino.cc/hardware/uno-rev3 for some tips.

As the iPDM56 uses a CH340G USB-to-serial converter, the choice of bootloader will be ignored, just choose the correct serial port & Arduino Uno.

Arduino programming setup
/*
ipdmsw - iPDM56 firmware template
Copyright (c) 2023 Perttu "celeron55" Ahola

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include "src/ipdm_library.h"
#include "src/ipdm_util.h"
#include "src/ipdm_can.h"
#include "src/params.h"

// Matrix inputs/outputs
constexpr int UNUSED_M1          = A0; // A0 = M1
constexpr int UNUSED_M2          = A1; // A1 = M2
constexpr int UNUSED_M3          = A2; // A2 = M3
constexpr int UNUSED_M4          = A3; // A3 = M4
constexpr int UNUSED_M5          = A6; // A6 = M5
constexpr int UNUSED_M6          =  2; // D2 = M6
constexpr int UNUSED_M7          =  3; // D3 = M7
constexpr int UNUSED_M8          = ipdm::ED4; // ED4 = M8
constexpr int UNUSED_M9          = ipdm::ED5; // ED5 = M9
constexpr int UNUSED_M10         = ipdm::ED6; // ED6 = M10

// Outputs
constexpr int LOUT1                       = ipdm::LOUT1; // Pin 45 LEFT Indicator
constexpr int LOUT2                       = ipdm::LOUT2; // Pin 46 RIGHT Indicator
constexpr int UNUSED_LOUT3                = ipdm::LOUT3;
constexpr int UNUSED_LOUT4                = ipdm::LOUT4;
constexpr int UNUSED_LOUT5                = ipdm::LOUT5;
constexpr int UNUSED_LOUT6                = ipdm::LOUT6;
constexpr int UNUSED_HOUT1                = ipdm::HOUT1;
constexpr int UNUSED_HOUT2                = ipdm::HOUT2;
constexpr int UNUSED_HOUT3                = ipdm::HOUT3;
constexpr int UNUSED_HOUT4                = ipdm::HOUT4;
constexpr int UNUSED_HOUT5                = ipdm::HOUT5;
constexpr int UNUSED_HOUT6                = ipdm::HOUT6;
constexpr int UNUSED_AOUT1                = ipdm::AOUT1;
constexpr int UNUSED_AOUT2                = ipdm::AOUT2;

// custom variables

constexpr int indicator_delay = 250; // in ms


void setup()
{
	Serial.begin(115200);

	ipdm::setup();

	
}

void loop()
{
	ipdm::loop();

	EVERY_N_MILLISECONDS(30000){
		ipdm::util_print_timestamp(Serial);
		Serial.println("-!- iPDM56 running");
	}

	// Print out output changes using a helper macro
	EVERY_N_MILLISECONDS(1000){
		REPORT_BOOL(ipdm::digitalRead(LOUT1));
		REPORT_BOOL(ipdm::digitalRead(LOUT2));
	}

	// Example: blink external LEDs via low side outputs - intention: flash both 1x,  right 4x, left 2x, repeat
	
	ipdm::digitalWrite(ipdm::LOUT1, 1);
	ipdm::digitalWrite(ipdm::LOUT2, 1);
    ipdm::delay(indicator_delay*2);
    ipdm::digitalWrite(ipdm::LOUT1, 0);  
	ipdm::digitalWrite(ipdm::LOUT2, 0);  

    ipdm::delay(indicator_delay*5);

        ipdm::loop(); // to reset watchdog timer - needs to be kept under 3000ms 

    ipdm::digitalWrite(ipdm::LOUT2, 1);    
    ipdm::delay(indicator_delay);
	ipdm::digitalWrite(ipdm::LOUT2, 0);    
    ipdm::delay(indicator_delay);    
	ipdm::digitalWrite(ipdm::LOUT2, 1);    
    ipdm::delay(indicator_delay);
    ipdm::digitalWrite(ipdm::LOUT2, 0);  
    ipdm::delay(indicator_delay);
    ipdm::loop(); // to reset watchdog timer - needs to be kept under 3000ms 
    ipdm::digitalWrite(ipdm::LOUT2, 1);    
    ipdm::delay(indicator_delay);
	ipdm::digitalWrite(ipdm::LOUT2, 0);    
    ipdm::delay(indicator_delay);    
	ipdm::digitalWrite(ipdm::LOUT2, 1);    
    ipdm::delay(indicator_delay);
    ipdm::digitalWrite(ipdm::LOUT2, 0);  
    
        ipdm::loop(); // to reset watchdog timer - needs to be kept under 3000ms 

    ipdm::delay(indicator_delay);
    ipdm::digitalWrite(ipdm::LOUT1, 1);  
	ipdm::delay(indicator_delay);
    ipdm::digitalWrite(ipdm::LOUT1, 0);  
    ipdm::delay(indicator_delay);
    ipdm::digitalWrite(ipdm::LOUT1, 1);
    ipdm::delay(indicator_delay);
    ipdm::digitalWrite(ipdm::LOUT1, 0);  
    
    ipdm::delay(indicator_delay*2);      

}

As you can see in the definitions, low side outputs on pins 45 & 46 on the connector are used to drive the 12V LED clusters.

Short video of the indicators

Getting started with CAN bus

A CAN bus needs to be terminated on each end and the iPDM56 gives you the flexibility to terminate either CAN1 and/or CAN2 with jumpers. We need 4 pins and 2 jumpers (e.g. from a PC mainboard) each and something to hold them in place while soldering - I used a third hand, but tape should work, too.

Pre-assemble the jumpers and pins to ensure proper fitting, then solder the pins in.

If you have v1.1 of the iPDM56, as per https://github.com/celeron55/ipdm56/tree/master/ipdmhw1.1, R97 needs to be replaced with a 1k resistor or CAN won't work. The existing SMD one can easily be "wiped off" with the soldering iron. I bent a through-hole version to fit as we're already soldering. Leaving the leads on until last allows you to check if the connection is solid.

pins and jumpers
third hand holding jumpers and pins up
jumpers soldered
1k resistor bent for SMD
legs attached for pull test
mods to enable CAN bus done

And here is the code to scan the CAN bus and print to serial:

/*
ipdmsw - iPDM56 firmware template
Copyright (c) 2023 Perttu "celeron55" Ahola

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include "src/ipdm_library.h"
#include "src/ipdm_util.h"
#include "src/ipdm_can.h"
#include "src/params.h"

// Matrix inputs/outputs
constexpr int UNUSED_M1          = A0; // A0 = M1
constexpr int UNUSED_M2          = A1; // A1 = M2
constexpr int UNUSED_M3          = A2; // A2 = M3
constexpr int UNUSED_M4          = A3; // A3 = M4
constexpr int UNUSED_M5          = A6; // A6 = M5
constexpr int UNUSED_M6          =  2; // D2 = M6
constexpr int UNUSED_M7          =  3; // D3 = M7
constexpr int UNUSED_M8          = ipdm::ED4; // ED4 = M8
constexpr int UNUSED_M9          = ipdm::ED5; // ED5 = M9
constexpr int UNUSED_M10         = ipdm::ED6; // ED6 = M10

// Outputs
constexpr int UNUSED_LOUT1                = ipdm::LOUT1;
constexpr int UNUSED_LOUT2                = ipdm::LOUT2;
constexpr int UNUSED_LOUT3                = ipdm::LOUT3;
constexpr int UNUSED_LOUT4                = ipdm::LOUT4;
constexpr int UNUSED_LOUT5                = ipdm::LOUT5;
constexpr int UNUSED_LOUT6                = ipdm::LOUT6;
constexpr int UNUSED_HOUT1                = ipdm::HOUT1;
constexpr int UNUSED_HOUT2                = ipdm::HOUT2;
constexpr int UNUSED_HOUT3                = ipdm::HOUT3;
constexpr int UNUSED_HOUT4                = ipdm::HOUT4;
constexpr int UNUSED_HOUT5                = ipdm::HOUT5;
constexpr int UNUSED_HOUT6                = ipdm::HOUT6;
constexpr int UNUSED_AOUT1                = ipdm::AOUT1;
constexpr int UNUSED_AOUT2                = ipdm::AOUT2;

void setup()
{
	Serial.begin(115200);

	ipdm::setup();

	// The MCP2515 CAN controllers will be initialized with these speeds and
	// filters when the 5Vsw rail is powered up using ipdm::enable_switched_5v()
	ipdm::can1_params.speed = CAN_500KBPS;
	ipdm::can2_params.speed = CAN_500KBPS;
	// You can set the CAN filters like this. By default all messages pass.
	/*ipdm::can1_params.filter1_mask = 0xfff;
	ipdm::can1_params.filter1_ids[0] = 0x123;
	ipdm::can1_params.filter1_ids[1] = 0x456;
	ipdm::can1_params.filter2_mask = 0xff0;
	ipdm::can1_params.filter2_ids[0] = 0x120;
	ipdm::can1_params.filter2_ids[1] = 0x340;
	ipdm::can1_params.filter2_ids[2] = 0x560;
	ipdm::can1_params.filter2_ids[3] = 0x780;*/
}

void loop()
{
	ipdm::loop();

	EVERY_N_MILLISECONDS(30000){
		ipdm::util_print_timestamp(Serial);
		Serial.println("-!- iPDM56 running");
    REPORT_UINT16_HYS(analogRead_mV_factor16(ipdm::VBAT_PIN, ipdm::ADC_FACTOR16_VBAT), 100);
	}

	// Consider D7 (IN10) to be the ignition pin and switch 5Vsw according to it
	//if(ipdm::digitalRead(IGNITION_PIN)){
	// Always enable switched 5V for testing CANbus
	if(true){
		ipdm::enable_switched_5v();
	} else {
		ipdm::disable_switched_5v();
	}

	// Read incoming CAN frames
    ipdm::can_receive(ipdm::can1, handle_can1_frame);
	// ipdm::can_receive(ipdm::can2, handle_can2_frame);

	
	// Print out (12V) battery voltage
	EVERY_N_MILLISECONDS(1000){
		
		REPORT_UINT16_HYS(analogRead_mV_factor16(ipdm::VBAT_PIN, ipdm::ADC_FACTOR16_VBAT), 100);
	}

	
}

static void print_frame(const CAN_FRAME &frame)
{
	Serial.print("id=0x");
	Serial.print(frame.id, HEX);
	for(uint8_t i=0; i<8; i++){
		Serial.print(" 0x");
		Serial.print(frame.data.bytes[i], HEX);
	}
}

void handle_can1_frame(const CAN_FRAME &frame)
{
#if 1
	Serial.print("CAN1 received frame ");
	print_frame(frame);
	Serial.println();
#endif


}