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; }
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; }