static void red_stack_spi_handle_reset(void) { int slave; stack_announce_disconnect(&_red_stack.base); _red_stack.base.recipients.count = 0; log_info("Starting reinitialization of SPI slaves"); // Someone pressed reset we have to wait until he stops pressing while (gpio_input(_red_stack_reset_stack_pin) == 0) { // Wait 100us and check again. We wait as long as the user presses the button SLEEP_NS(0, 1000*100); } SLEEP_NS(1, 1000*1000*500); // Wait 1.5s so slaves can start properly // Reinitialize slaves _red_stack.slave_num = 0; for (slave = 0; slave < RED_STACK_SPI_MAX_SLAVES; slave++) { _red_stack.slaves[slave].stack_address = slave; _red_stack.slaves[slave].status = RED_STACK_SLAVE_STATUS_ABSENT; _red_stack.slaves[slave].sequence_number_master = 1; _red_stack.slaves[slave].sequence_number_slave = 0; _red_stack.slaves[slave].next_packet_empty = false; // Unfortunately we have to discard all of the queued packets. // we can't be sure that the packets are for the correct slave after a reset. while (queue_peek(&_red_stack.slaves[slave].packet_to_spi_queue) != NULL) { queue_pop(&_red_stack.slaves[slave].packet_to_spi_queue, NULL); } } }
// Resets stack static void red_stack_reset(void) { // Change mux of reset pin to output gpio_mux_configure(_red_stack_reset_stack_pin, GPIO_MUX_OUTPUT); gpio_output_clear(_red_stack_reset_stack_pin); SLEEP_NS(0, 1000*1000*100); // Clear reset pin for 100ms to force reset gpio_output_set(_red_stack_reset_stack_pin); SLEEP_NS(1, 1000*1000*500); // Wait 1.5s so slaves can start properly // Change mux back to interrupt, so we can see if a human presses reset gpio_mux_configure(_red_stack_reset_stack_pin, GPIO_MUX_6); }
void calibrate(void) { uint32_t value = 0; for(uint8_t i = 0; i < 200; i++) { value += BA->adc_channel_get_data(BS->adc_channel); SLEEP_NS(100); } BC->offset = ADC_MAX_VALUE/2 - value/200; BA->bricklet_select(BS->port - 'a'); BA->i2c_eeprom_master_write(BA->twid->pTwi, EEPROM_POSITION, (char *)&BC->offset, 2); BA->bricklet_deselect(BS->port - 'a'); }
void i2c_sleep_halfclock(void) { SLEEP_NS(I2C_HALF_CLOCK_100KHZ); }
// Main SPI loop. This runs independently from the brickd event thread. // Data between RED Brick and SPI slave is exchanged every 500us. // If there is no data to be send, we cycle through the slaves and request // data. If there is data to be send the slave that ought to receive // the data gets priority. This can greatly reduce latency in a big stack. static void red_stack_spi_thread(void *opaque) { REDStackPacket *packet_to_spi = NULL; uint8_t stack_address_cycle; int ret; (void)opaque; do { stack_address_cycle = 0; _red_stack_reset_detected = 0; _red_stack.slave_num = 0; red_stack_spi_create_routing_table(); _red_stack_spi_thread_running = false; if (_red_stack.slave_num > 0) { _red_stack_spi_thread_running = true; } // Ignore resets that we received in the meantime to prevent race conditions. _red_stack_reset_detected = 0; while (_red_stack_spi_thread_running) { REDStackSlave *slave = &_red_stack.slaves[stack_address_cycle]; REDStackPacket *request = NULL; memset(&_red_stack.packet_from_spi, 0, sizeof(Packet)); // Get packet from queue. The queue contains that are to be // send over SPI. It is filled through from the main brickd // event thread, so we have to make sure that there is not race // condition. if(slave->next_packet_empty) { slave->next_packet_empty = false; packet_to_spi = NULL; } else { mutex_lock(&(slave->packet_queue_mutex)); packet_to_spi = queue_peek(&slave->packet_to_spi_queue); mutex_unlock(&(slave->packet_queue_mutex)); } stack_address_cycle++; if (stack_address_cycle >= _red_stack.slave_num) { stack_address_cycle = 0; } // Set request if we have a packet to send if (packet_to_spi != NULL) { log_packet_debug("Packet will now be send over SPI (%s)", packet_get_request_signature(packet_signature, &packet_to_spi->packet)); request = packet_to_spi; } ret = red_stack_spi_transceive_message(request, &_red_stack.packet_from_spi, slave); if ((ret & RED_STACK_TRANSCEIVE_RESULT_MASK_SEND) == RED_STACK_TRANSCEIVE_RESULT_SEND_OK) { if ((!((ret & RED_STACK_TRANSCEIVE_RESULT_MASK_READ) == RED_STACK_TRANSCEIVE_RESULT_READ_ERROR))) { // If we send a packet it must have come from the queue, so we can // pop it from the queue now. // If the sending didn't work (for whatever reason), we don't pop it // and therefore we will automatically try to send it again in the next cycle. mutex_lock(&(slave->packet_queue_mutex)); queue_pop(&slave->packet_to_spi_queue, NULL); mutex_unlock(&(slave->packet_queue_mutex)); } } // If we received a packet, we will dispatch it immediately. // We have some time until we try the next SPI communication anyway. if ((ret & RED_STACK_TRANSCEIVE_RESULT_MASK_READ) == RED_STACK_TRANSCEIVE_RESULT_READ_OK) { // TODO: Check again if packet is valid? // We did already check the hash. // Before the dispatching we insert the stack position into an enumerate message red_stack_spi_insert_position(slave); red_stack_spi_request_dispatch_response_event(); // Wait until message is dispatched, so we don't overwrite it // accidentally. semaphore_acquire(&_red_stack_dispatch_packet_from_spi_semaphore); } SLEEP_NS(0, 1000*_red_stack_spi_poll_delay); } if (_red_stack.slave_num == 0) { pthread_mutex_lock(&_red_stack_wait_for_reset_mutex); // Use helper to be save against spurious wakeups _red_stack_wait_for_reset_helper = 0; while (_red_stack_wait_for_reset_helper == 0) { pthread_cond_wait(&_red_stack_wait_for_reset_cond, &_red_stack_wait_for_reset_mutex); } pthread_mutex_unlock(&_red_stack_wait_for_reset_mutex); } if (_red_stack_reset_detected > 0) { red_stack_spi_handle_reset(); } } while (_red_stack_reset_detected > 0); }
// If data should just be polled, set packet_send to NULL. // // If no packet is received from slave the length in packet_recv will be set to 0, // the exact reason for that is encoded in the return value. // // For the return value see RED_STACK_TRANSCEIVE_RESULT_* at the top of this file. static int red_stack_spi_transceive_message(REDStackPacket *packet_send, Packet *packet_recv, REDStackSlave *slave) { int retval = 0; uint8_t length, length_send; uint8_t checksum; int rc; uint8_t sequence_number_master = 0xFF; uint8_t sequence_number_slave = 0xFF; uint8_t tx[RED_STACK_SPI_PACKET_SIZE] = {0}; uint8_t rx[RED_STACK_SPI_PACKET_SIZE] = {0}; // We assume that we don't receive anything. If we receive a packet the // length will be overwritten again packet_recv->header.length = 0; // Preamble is always the same tx[RED_STACK_SPI_PREAMBLE] = RED_STACK_SPI_PREAMBLE_VALUE; if (packet_send == NULL) { // If packet_send is NULL // we send a message with empty payload (4 byte) tx[RED_STACK_SPI_LENGTH] = RED_STACK_SPI_PACKET_EMPTY_SIZE; retval = RED_STACK_TRANSCEIVE_RESULT_SEND_NONE; } else if (slave->status == RED_STACK_SLAVE_STATUS_AVAILABLE) { length = packet_send->packet.header.length; if (length > sizeof(Packet)) { retval |= RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR; log_error("Send length is greater then allowed (actual: %d > maximum: %d)", length, (int)sizeof(Packet)); goto ret; } retval = RED_STACK_TRANSCEIVE_DATA_SEND; tx[RED_STACK_SPI_LENGTH] = length + RED_STACK_SPI_PACKET_EMPTY_SIZE; memcpy(tx+2, &packet_send->packet, length); } else { retval = RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR; log_error("Slave with stack address %d is not present in stack", slave->stack_address); goto ret; } length = tx[RED_STACK_SPI_LENGTH]; // Set master and slave sequence number tx[RED_STACK_SPI_INFO(length)] = slave->sequence_number_master | slave->sequence_number_slave; // Calculate checksum tx[RED_STACK_SPI_CHECKSUM(length)] = red_stack_spi_calculate_pearson_hash(tx, length-1); struct spi_ioc_transfer spi_transfer = { .tx_buf = (unsigned long)&tx, .rx_buf = (unsigned long)&rx, .len = RED_STACK_SPI_PACKET_SIZE, }; red_stack_spi_select(slave); rc = ioctl(_red_stack_spi_fd, SPI_IOC_MESSAGE(1), &spi_transfer); red_stack_spi_deselect(slave); if (rc < 0) { // Overwrite current return status with error, // it seems ioctl itself didn't work. retval = RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } log_error("ioctl failed: %s (%d)", get_errno_name(errno), errno); goto ret; } length_send = rc; if (length_send != RED_STACK_SPI_PACKET_SIZE) { // Overwrite current return status with error, // it seems ioctl itself didn't work. retval = RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } log_error("ioctl has unexpected result (actual: %d != expected: %d)", length_send, RED_STACK_SPI_PACKET_SIZE); goto ret; } if (rx[RED_STACK_SPI_PREAMBLE] != RED_STACK_SPI_PREAMBLE_VALUE) { // Do not log by default, an "unproper preamble" is part of the protocol // if the slave is too busy to fill the DMA buffers fast enough // log_error("Received packet without proper preamble (actual: %d != expected: %d)", // rx[RED_STACK_SPI_PREAMBLE], RED_STACK_SPI_PREAMBLE_VALUE); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } goto ret; } // Check length length = rx[RED_STACK_SPI_LENGTH]; if ((length != RED_STACK_SPI_PACKET_EMPTY_SIZE) && ((length < (RED_STACK_SPI_PACKET_EMPTY_SIZE + sizeof(PacketHeader))) || (length > RED_STACK_SPI_PACKET_SIZE))) { log_error("Received packet with malformed length: %d", length); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } goto ret; } // Calculate and check checksum checksum = red_stack_spi_calculate_pearson_hash(rx, length-1); if (checksum != rx[RED_STACK_SPI_CHECKSUM(length)]) { log_error("Received packet with wrong checksum (actual: %x != expected: %x)", checksum, rx[RED_STACK_SPI_CHECKSUM(length)]); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } goto ret; } // If we send data and the master sequence number matches to the one // set in the packet we know that the slave received the packet! if ((packet_send != NULL) /*&& (packet_send->status == RED_STACK_PACKET_STATUS_SEQUENCE_NUMBER_SET)*/) { sequence_number_master = rx[RED_STACK_SPI_INFO(length)] & RED_STACK_SPI_INFO_SEQUENCE_MASTER_MASK; if (sequence_number_master == slave->sequence_number_master) { retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_SEND)) | RED_STACK_TRANSCEIVE_RESULT_SEND_OK; // Increase sequence number for next packet red_stack_increase_master_sequence_number(slave); } } else { // If we didn't send anything we can always increase the sequence number, // it doesn't matter if the slave actually received it. red_stack_increase_master_sequence_number(slave); } // If the slave sequence number matches we already processed this packet sequence_number_slave = rx[RED_STACK_SPI_INFO(length)] & RED_STACK_SPI_INFO_SEQUENCE_SLAVE_MASK; if (sequence_number_slave == slave->sequence_number_slave) { retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_NONE; } else { // Otherwise we save the new sequence number slave->sequence_number_slave = sequence_number_slave; if (length == RED_STACK_SPI_PACKET_EMPTY_SIZE) { // Do not log by default, will produce 2000 log entries per second // log_packet_debug("Received empty packet over SPI (w/ header)"); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_NONE; } else { // Everything seems OK, we can copy to buffer memcpy(packet_recv, rx+2, length - RED_STACK_SPI_PACKET_EMPTY_SIZE); log_packet_debug("Received packet over SPI (%s)", packet_get_response_signature(packet_signature, packet_recv)); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_OK; retval |= RED_STACK_TRANSCEIVE_DATA_RECEIVED; } } ret: return retval; } // Creates the "routing table", which is just the // array of REDStackSlave structures. static void red_stack_spi_create_routing_table(void) { char base58[BASE58_MAX_LENGTH]; int tries, ret, i; uint8_t stack_address = 0; uint8_t uid_counter = 0; log_debug("Starting to discover SPI stack slaves"); while (stack_address < RED_STACK_SPI_MAX_SLAVES) { REDStackSlave *slave = &_red_stack.slaves[stack_address]; Packet packet; StackEnumerateResponse *response; REDStackPacket red_stack_packet = { slave, {{ 0, // UID 0 sizeof(StackEnumerateRequest), FUNCTION_STACK_ENUMERATE, 0x08, // Return expected 0 }, {0}, {0}}, RED_STACK_PACKET_STATUS_ADDED, }; // We have to assume that the slave is available slave->status = RED_STACK_SLAVE_STATUS_AVAILABLE; // Send stack enumerate request for (tries = 0; tries < RED_STACK_SPI_ROUTING_TRIES; tries++) { ret = red_stack_spi_transceive_message(&red_stack_packet, &packet, slave); if ((ret & RED_STACK_TRANSCEIVE_RESULT_MASK_SEND) == RED_STACK_TRANSCEIVE_RESULT_SEND_OK) { break; } SLEEP_NS(0, RED_STACK_SPI_ROUTING_WAIT); // Give slave some more time } if (tries == RED_STACK_SPI_ROUTING_TRIES) { // Slave does not seem to be available, slave->status = RED_STACK_SLAVE_STATUS_ABSENT; // This means that there can't be any more slaves above // and we are actually done here already! break; } // Receive stack enumerate response for (tries = 0; tries < RED_STACK_SPI_ROUTING_TRIES; tries++) { // We first check if we already received an answer before we try again if ((ret & RED_STACK_TRANSCEIVE_RESULT_MASK_READ) == RED_STACK_TRANSCEIVE_RESULT_READ_OK) { break; } // Here we sleep before transceive so that there is some time // between the sending of stack enumerate and the receiving // of the answer SLEEP_NS(0, RED_STACK_SPI_ROUTING_WAIT); // Give slave some more time ret = red_stack_spi_transceive_message(NULL, &packet, slave); } if (tries == RED_STACK_SPI_ROUTING_TRIES) { // Slave does not seem to be available, slave->status = RED_STACK_SLAVE_STATUS_ABSENT; // This means that there can't be any more slaves above // and we are actually done here already! break; } response = (StackEnumerateResponse *)&packet; for (i = 0; i < PACKET_MAX_STACK_ENUMERATE_UIDS; i++) { if (response->uids[i] != 0) { uid_counter++; stack_add_recipient(&_red_stack.base, response->uids[i], stack_address); log_debug("Found UID number %d of slave %d with UID %s", i, stack_address, base58_encode(base58, uint32_from_le(response->uids[i]))); } else { break; } } stack_address++; } _red_stack.slave_num = stack_address; log_info("SPI stack slave discovery done. Found %d slave(s) with %d UID(s) in total", stack_address, uid_counter); }