Metaboard/2010 activities/6-pin auto-detecting programming

aus Metalab, dem offenen Zentrum für meta-disziplinäre Magier und technisch-kreative Enthusiasten.
Wechseln zu: Navigation, Suche

For programming the sensor boards, we want to be able to program them from the master board, with these goals in decreasing priority:

  • simple sensor design (no components at all)
  • safe operation (especially against the sensors being inserted in the wrong direction) without prior notification
  • minimal pin usage
  • simple master board design

For programming, 6 pins are needed:

  • power (3.3V)
  • ground
  • MISO (response from the sensor board)
  • MOSI (commands to the sensor board)
  • SCK (clock of the MISO/MOSI lines, driven by the master)
  • slave select (could be pulled to ground on the sensor board, but then it has to be made sure that sensors are only connected if no other slave on the SPI is active)

To prevent external pins from being pulled (too) high when inactive, MISO, MOSI and SCK get 1kΩ resistors on the master board. Slave select and power will both be driven from general purpose IO pins. (Don't remember where I read that powering an AVR from another AVR's GPIO pin works, but it does.)

The pinout chosen for the plug is

left (PIN6) right (PIN1)
base RESET POWER MOSI MISO SCK GND
regular RESET POWER MOSI MISO SCK GND
flipped GND SCK MISO MOSI POWER RESET

When idling, RESET is actively pulled low, and POWER is configured as input with the builtin pullup resistor. Thus, connecting a sensor regularly or flipped pulls POWER down and a device is recognized.

After the presence of a plug is detected, it can be determined whether the direction is ok by setting RESET as input with pullup. If POWER goes up again, the connector is plugged in the wrong way, otherwise the system can be powered.

Here is an example of how to poll for the connector:

#define RESET 9
#define POWER 10
#define MISO 12
#define MOSI 11
#define SCK 13


void reset() {
	digitalWrite(RESET, LOW);
	pinMode(RESET, OUTPUT);
	pinMode(POWER, INPUT);
	digitalWrite(POWER, HIGH);
}

void setup() {
	reset();

	pinMode(MISO, INPUT);
	pinMode(MOSI, INPUT);
	pinMode(SCK, INPUT);
	digitalWrite(MISO, HIGH);
	digitalWrite(MOSI, HIGH);
	digitalWrite(SCK, HIGH);

	Serial.begin(19200);
}

void loop() {
	if(digitalRead(POWER) != LOW) {
		Serial.write("Nothing detected on power line, allowing other SPI communication\r\n");
		while(digitalRead(POWER) != LOW); // blocking in the poll is nothing we normally want; using it here to supress duplicate messages
	}
	Serial.write("Detected something\r\n");

	pinMode(RESET, INPUT);
	digitalWrite(RESET, HIGH);

	if(digitalRead(POWER) == HIGH) {
		Serial.write("Match was bad\r\n");
	} else {
		Serial.write("Match was GOOD!\r\n");
		Serial.write("Waiting for confirmation.\r\n");
		int bad = 0;
		for(int i=0; i < 20; ++i) {
			delay(1);
			if(digitalRead(POWER) == HIGH)
			{
				bad = 1;
				break;
			}
		}
		if(bad == 0) {
			Serial.write("Confirmed, taking up the chip.\r\n");
			pinMode(RESET, OUTPUT);
			digitalWrite(RESET, HIGH);
			pinMode(POWER, OUTPUT);
			digitalWrite(POWER, HIGH);
			delay(10000);
			Serial.write("enough\r\n");
		} else
			Serial.write("Unconfirmed, returning\r\n");
	}

	delay(200);
	reset();
}

Note that for polling to work in practice, the digitalWrite({MOSI, SCK}, HIGH) might have to be set before polling.