uint16_t spi_stack_master_send(const void *data, const uint16_t length, uint32_t *options) {
	if(spi_stack_buffer_size_send > 0 || transceive_state != TRANSCEIVE_STATE_MESSAGE_EMPTY) {
		return 0;
	}

	// If the stack address is in the options, we use it
	if(options && *options >= SPI_ADDRESS_MIN && *options <= com_info.last_stack_address) {
		if(spi_stack_master_start_transceive(data, length, *options) != TRANSCEIVE_INFO_SEND_OK) {
			return 0;
		}
	} else {
		// We should not need to do our own routing here, the (commented out) code below implements
		// a theoretical routing that we should not need to use.
		// Keep an eye out for this error messages!
		if(options == NULL) {
			logspise("spi_stack_master_send called with invalid option: NULL\n\r");
		} else {
			logspise("spi_stack_master_send called with invalid option: %d\n\r", *options);
		}

/*		// Otherwise we try to find it in the routing table
		RouteTo route_to = routing_route_stack_to(spi_stack_buffer_send[0] |
												  (spi_stack_buffer_send[1] << 8) |
												  (spi_stack_buffer_send[2] << 16) |
												  (spi_stack_buffer_send[3] << 24));

		// If it is not in there, we broadcast the message
		if(route_to.option < SPI_ADDRESS_MIN || route_to.option > com_info.last_stack_address) {
			stack_address_broadcast = 1;
			if(spi_stack_master_start_transceive(data, length, 1) != TRANSCEIVE_INFO_SEND_OK) {
				stack_address_broadcast = 0;
				return 0;
			}
		} else {
			// Otherwise we can send it the routing table address
			if(spi_stack_master_start_transceive(data, length, route_to.option) != TRANSCEIVE_INFO_SEND_OK) {
				return 0;
			}
		}*/
	}

	led_rxtx++;

	return length;
}
Beispiel #2
0
void routing_table_create_stack(void) {
	uint8_t stack_address = 0;
	uint8_t tries = 0;
	while(stack_address < SPI_ADDRESS_MAX) {
		StackEnumerate se;
		com_make_default_header(&se, 0, sizeof(StackEnumerate), FID_STACK_ENUMERATE);

		uint32_t options = stack_address + 1;
		if(spi_stack_send(&se, sizeof(StackEnumerate), &options) != 0) {
			spi_stack_select(stack_address + 1);

			tries = 0;
			while(!spi_stack_master_transceive() && tries < 10) {
				SLEEP_MS(50);
				tries++;
			}
			if(tries == 10) {
				break;
			}

			spi_stack_deselect();

			spi_stack_buffer_size_recv = 0;
		}

		StackEnumerateReturn ser;
		tries = 0;
		while(tries < 10) {
			SLEEP_MS(50);
			spi_stack_select(stack_address + 1);
			spi_stack_master_transceive();
			spi_stack_deselect();
			if(spi_stack_recv(&ser, sizeof(StackEnumerateReturn), NULL)) {
				break;
			}
			tries++;
		}

		if(tries == 10) {
			logspise("Did not receive answer for Stack Enumerate\n\r");
			break;
		}

		stack_address++;
		com_info.last_stack_address = stack_address;

		for(uint8_t i = 0; i < STACK_ENUMERATE_MAX_UIDS; i++) {
			if(ser.uids[i] != 0) {
				if(routing_table_size >= ROUTING_TABLE_MAX_SIZE) {
					break;
				}
				routing_table[routing_table_size].uid = ser.uids[i];
				routing_table[routing_table_size].route_to.to = ROUTING_STACK;
				routing_table[routing_table_size].route_to.option = stack_address;
				routing_table_last_stack = routing_table_size;
				routing_table_size++;
				logspisi("New Stack participant (sa, uid): %d, %lu\n\r", stack_address, ser.uids[i]);
			}
		}
	}

	spi_stack_buffer_size_send = 0;
}
void spi_stack_master_irq(void) {
	volatile uint32_t status = SPI->SPI_SR;

	// We only do anything here if RX and TX are finished.
	if((status & (SPI_SR_ENDRX | SPI_SR_ENDTX)) == (SPI_SR_ENDRX | SPI_SR_ENDTX)) {
		spi_stack_master_disable_dma();

		if(spi_stack_buffer_recv[SPI_STACK_PREAMBLE] != SPI_STACK_PREAMBLE_VALUE) {
			// An "unproper preamble" is part of the protocol,
			// if the slave is too busy to fill the DMA buffers fast enough.
			// If we did try to send something we need to try again.

			transceive_state = TRANSCEIVE_STATE_MESSAGE_READY;
			return;
		}

		uint8_t length = spi_stack_buffer_recv[SPI_STACK_LENGTH];
		if((length != SPI_STACK_EMPTY_MESSAGE_LENGTH) &&
		   ((length < SPI_STACK_MESSAGE_LENGTH_MIN) ||
		    (length > SPI_STACK_MAX_MESSAGE_LENGTH))) {
			logspise("Received packet with malformed length: %d\n\r", length);

			transceive_state = TRANSCEIVE_STATE_MESSAGE_READY;
			return;
		}

		uint8_t checksum = spi_stack_calculate_pearson(spi_stack_buffer_recv, length-1);
		if(checksum != spi_stack_buffer_recv[SPI_STACK_CHECKSUM(length)]) {
			logspise("Received packet with wrong checksum (actual: %x != expected: %x)\n\r",
			         checksum, spi_stack_buffer_recv[SPI_STACK_CHECKSUM(length)]);

			// Commented out code below is for debugging wrong checksum problems.
			// This should only be needed during initial development, we leave it here just in case.
/*
			logwohe("spi packet (%d): preamble(%d), length(%d), info(%d), checksum(%d)\n\r",
					stack_address_current,
					spi_stack_buffer_recv[SPI_STACK_PREAMBLE],
					spi_stack_buffer_recv[SPI_STACK_LENGTH],
					spi_stack_buffer_recv[SPI_STACK_INFO(length)],
					spi_stack_buffer_recv[SPI_STACK_CHECKSUM(length)]);

			MessageHeader *header = ((MessageHeader*)(spi_stack_buffer_recv+2));

			logwohe("Message UID: %lu", header->uid);
			logwohe(", length: %d", header->length);
			logwohe(", fid: %d", header->fid);
			logwohe(", (seq#, r, a, oo): (%d, %d, %d, %d)", header->sequence_num, header->return_expected, header->authentication, header->other_options);
			logwohe(", (e, fu): (%d, %d)\n\r", header->error, header->future_use);

			logwohe("Message data: [");

			uint8_t *data = (uint8_t*)header;
			for(uint8_t i = 0; i < header->length - 8; i++) {
				logwohe("%d ", data[i+8]);
			}
			logwohe("]\n\r");*/

			transceive_state = TRANSCEIVE_STATE_MESSAGE_READY;
			return;
		}

		// If the sequence number is the same as the last time, we already
		// handled this response, we don't handle it again in this case
		if((spi_stack_buffer_recv[SPI_STACK_INFO(length)] & SPI_STACK_INFO_SEQUENCE_SLAVE_MASK) != spi_stack_master_slave_seq[stack_address_current-1]) {
			spi_stack_master_slave_seq[stack_address_current-1] = spi_stack_buffer_recv[SPI_STACK_INFO(length)] & SPI_STACK_INFO_SEQUENCE_SLAVE_MASK;

			if(length == SPI_STACK_EMPTY_MESSAGE_LENGTH) {
				// We didn't receive anything, there is nothing to copy anywhere
			} else {
				for(uint8_t i = 0; i < length-SPI_STACK_EMPTY_MESSAGE_LENGTH; i++) {
					spi_stack_buffer_recv[i] = spi_stack_buffer_recv[i+2];
				}

				spi_stack_buffer_size_recv = length-SPI_STACK_EMPTY_MESSAGE_LENGTH;

				// Insert stack position (in case of Enumerate or GetIdentity).
				// The SPI Slave can not know its position in the stack.
				spi_stack_master_insert_position(spi_stack_buffer_recv, stack_address_current);

				// Handle routing for Co MCU Bricklets
				spi_stack_master_update_routing_table(spi_stack_buffer_recv, stack_address_current);
			}
		}

		const uint8_t last_master_seq_seen_by_slave = spi_stack_buffer_recv[SPI_STACK_INFO(length)] & SPI_STACK_INFO_SEQUENCE_MASTER_MASK;
		uint8_t seq_inc = spi_stack_master_master_seq[stack_address_current-1] & SPI_STACK_INFO_SEQUENCE_MASTER_MASK;
		spi_stack_increase_master_seq(&seq_inc);

		// If the slave received our last message or we didn't send anything anyway, we can increase sequence number.
		// We do not increase the sequence number if the last ack we saw had the same sequence number as
		// the one we would get after increasing. Otherwise we may get a false positive ACK.
		if(((last_master_seq_seen_by_slave != spi_stack_master_master_seq[stack_address_current-1]) &&
		   ((spi_stack_buffer_size_send > 0) || (seq_inc == last_master_seq_seen_by_slave)))) {
			transceive_state = TRANSCEIVE_STATE_MESSAGE_READY;
			return;
		} else {
			spi_stack_increase_master_seq(&spi_stack_master_master_seq[stack_address_current-1]);
			spi_stack_buffer_size_send = 0;
		}

		transceive_state = TRANSCEIVE_STATE_MESSAGE_EMPTY;
	}
}
bool spi_stack_master_transceive(void) {
	uint8_t master_checksum = 0;
	uint8_t slave_checksum = 0;
	uint8_t send_length = spi_stack_buffer_size_send;

	volatile uint8_t dummy = 0;

	__disable_irq();
	SPI->SPI_SR;
	SPI->SPI_RDR;

	uint16_t timeout = SPI_STACK_MASTER_TIMEOUT;

	// Sync with slave
	while(dummy != 0xFF && timeout != 0) {
		while((SPI->SPI_SR & SPI_SR_TDRE) == 0);
		SPI->SPI_TDR = 0xFF;
		while((SPI->SPI_SR & SPI_SR_RDRF) == 0);
		dummy = SPI->SPI_RDR;
		timeout--;
	}

	if(timeout == 0) {
		logspise("Timeout, did not receive 0xFF from Slave\n\r");
		__enable_irq();
		return false;
	}

	// Write length
	while((SPI->SPI_SR & SPI_SR_TDRE) == 0);
	SPI->SPI_TDR = send_length;
	PEARSON(master_checksum, send_length);

	// Write first byte
	while((SPI->SPI_SR & SPI_SR_TDRE) == 0);
	SPI->SPI_TDR = spi_stack_buffer_send[0];
	PEARSON(master_checksum, spi_stack_buffer_send[0]);

	// Read length
    while((SPI->SPI_SR & SPI_SR_RDRF) == 0);
    uint8_t slave_length = SPI->SPI_RDR;
    PEARSON(slave_checksum, slave_length);

    // If master and slave length are 0, stop communication
    if(slave_length == 0 && send_length == 0) {
    	// Read dummy
        while((SPI->SPI_SR & SPI_SR_RDRF) == 0);
        dummy = SPI->SPI_RDR;

		__enable_irq();
        return true;
    }

    // Length to transceive is maximum of slave and master length
    uint8_t max_length = MIN(MAX(send_length, slave_length),
                             SPI_STACK_BUFFER_SIZE);

    // Exchange data
    for(uint8_t i = 1; i < max_length; i++) {
    	// Write
    	while((SPI->SPI_SR & SPI_SR_TDRE) == 0);
    	SPI->SPI_TDR = spi_stack_buffer_send[i];
    	PEARSON(master_checksum, spi_stack_buffer_send[i]);

    	// Read
        while((SPI->SPI_SR & SPI_SR_RDRF) == 0);
        spi_stack_buffer_recv[i-1] = SPI->SPI_RDR;
        PEARSON(slave_checksum, spi_stack_buffer_recv[i-1]);
    }

    // Write CRC
	while((SPI->SPI_SR & SPI_SR_TDRE) == 0);
	SPI->SPI_TDR = master_checksum;

	// Read last data byte
    while((SPI->SPI_SR & SPI_SR_RDRF) == 0);
    spi_stack_buffer_recv[max_length-1] = SPI->SPI_RDR;
    PEARSON(slave_checksum, spi_stack_buffer_recv[max_length-1]);

	// Read CRC
    while((SPI->SPI_SR & SPI_SR_RDRF) == 0);
    uint8_t crc = SPI->SPI_RDR;

    // Is CRC correct?
    uint8_t master_ack = crc == slave_checksum;

    __NOP();
    __NOP();
    __NOP();

	// Write ACK/NACK
	while((SPI->SPI_SR & SPI_SR_TDRE) == 0);
	SPI->SPI_TDR = master_ack;

	// Read ACK/NACK
    while((SPI->SPI_SR & SPI_SR_RDRF) == 0);
    uint8_t slave_ack = SPI->SPI_RDR;

    // If everything is OK, set sizes accordingly
    if(slave_ack == 1 && master_ack == 1) {
    	spi_stack_buffer_size_recv = slave_length;
    	if(send_length != 0) {
    		spi_stack_buffer_size_send = 0;
    	}
    } else {
    	if(!master_ack) {
    		logspisw("Checksum: Master(%d) != Slave(%d)\n\r", crc,
    		                                                  slave_checksum);
    	}
    	if(!slave_ack) {
    		logspisw("Checksum: Received NACK from Slave\n\r");
    	}
		__enable_irq();
    	return false;
    }

    __enable_irq();
    return true;
}