Weird Teensy SPI problem

Status
Not open for further replies.

Gripporillat

Well-known member
Hi there!

I got arduino code from a manufacturer to test his development board and tried using it with a Teensy 3.5. The manufacturer wrote it for Arduino Due. At first things seemed fine. I could transmit and receive valid data over SPI. But when I do a certain operation (generate_DMX) the module stops working after a few seconds. After a long conversation with the manufacturer I decided to buy and try it with a Arduino Due just to make sure that it's not a Teensy problem.

Bad news is: it works perfectly fine with the Arduino so it must be a Teensy-related problem. But I just don't get what's wrong. Any help would be highly appreciated! I am aware that you don't own the module so you can't test it yourself but maybe you have a helpful idea.

Thank you in advance!
Robert


Code:
#include <SPI.h>
#include "timo_spi.h"

#define TIMO_IDENTIFY_IRQ (1 << 5)

#define TIMO_SPI_DEVICE_BUSY_IRQ_MASK (1 << 7)

static timo_t timos[2] = {
  { .csn_pin = 10, .irq_pin = 9 },
  { .csn_pin =  7, .irq_pin = 6 }
};

static timo_t *timo = &timos[1];
static uint8_t tx_buffer[1024];
static uint8_t rx_buffer[1024];
static bool poll_pending_irqs = true;
static timo_register_info_t timo_registers[] = TIMO_REGISTERS;

void irq_pin_handler_0() {
  timos[0].irq_pending = 1;
}

void irq_pin_handler_1() {
  timos[1].irq_pending = 1;
}

bool irq_is_pending() {
  noInterrupts();
  bool pending = timo->irq_pending;
  timo->irq_pending = false;
  interrupts();
  return pending;
}

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

  SPI.begin();

  for (int i = 0; i < (sizeof(timos) / sizeof(timos[0])); i++) {
    pinMode(timos[i].irq_pin, INPUT);
    pinMode(timos[i].csn_pin, OUTPUT);
    digitalWrite(timos[i].csn_pin, HIGH);
  }
  attachInterrupt(digitalPinToInterrupt(timos[0].irq_pin), irq_pin_handler_0, FALLING);
  attachInterrupt(digitalPinToInterrupt(timos[1].irq_pin), irq_pin_handler_1, FALLING);

  SPI.beginTransaction(SPISettings(250000, MSBFIRST, SPI_MODE0));
  
  delay(1000);

  while(Serial.available() > 0) {
    Serial.read();
  }

  Serial.println("Running");
  Serial.flush();
}

void loop() {
  int16_t irq_flags;
  static String string = "";

  if (poll_pending_irqs) {
    if (irq_is_pending()) {
      irq_flags = timo_transfer(TIMO_NOP_COMMAND, rx_buffer, tx_buffer, 0);
      Serial.print("/ Got IRQ. IRQ flags: ");
      print_irq_flags(irq_flags);
      if (irq_flags & TIMO_IDENTIFY_IRQ) {
        Serial.print("Clearing flags by reading the STATUS register: ");
        irq_flags = timo_transfer(TIMO_READ_REG_COMMAND(TIMO_STATUS_REG), rx_buffer, tx_buffer, 2);
        print_response(irq_flags, rx_buffer, 1);
      }
      Serial.println();
      Serial.flush();
    }
  }
  
  while (Serial.available() > 0) {
    char letter = Serial.read();
    if (letter == '\n') {
      process_user_input(string);
      Serial.flush();
      string = "";
    }else {
      string += letter;
    }
  }
}



/**
 * @param len   Length in bytes. IRQ flag is counted. Example: Use length 9 when reading the version register.
 */
int16_t timo_transfer(uint8_t command, uint8_t *dst, uint8_t *src, uint32_t len) {
  uint8_t irq_flags;

  uint32_t start_time = millis();

  digitalWrite(timo->csn_pin, LOW);
  irq_flags = SPI.transfer(command);
  digitalWrite(timo->csn_pin, HIGH);

  if (len == 0) {
    return irq_flags;
  }
  
  while(!irq_is_pending()) {
    if (millis() - start_time > 10000) {
      return -1;
    }
  }
  
  digitalWrite(timo->csn_pin, LOW);
  irq_flags = SPI.transfer(0xff);
  
  if (irq_flags & TIMO_SPI_DEVICE_BUSY_IRQ_MASK) {
    digitalWrite(timo->csn_pin, HIGH);
    return irq_flags;
  }

  for (uint32_t i = 0; i < len - 1; i++) {
    *dst++ = SPI.transfer(*src++);
  }

  digitalWrite(timo->csn_pin, HIGH);
  return irq_flags;
}

String parsed_command[4] = { "", "", "", "" };

void split(String string, String delimiter, String dst[], uint32_t dst_len) {
  int prev_index = 0;

  for (uint32_t i = 0; i < dst_len; i++) {
    dst[i] = "";
  }
  for (uint32_t i = 0; i < dst_len; i++) {
    int next_index = string.indexOf(delimiter, prev_index + 1);
    if (next_index == -1) {
      dst[i] = string.substring(prev_index, string.length());
      dst[i].trim();
      break;
    } else {
      dst[i] = string.substring(prev_index, next_index);
      dst[i].trim();
    }
    prev_index = next_index;
  }
}

uint8_t hex_nibble_to_val(char ascii) {
  if (ascii >= '0' && ascii <= '9') {
    return ascii - '0';
  } else if (ascii >= 'A' && ascii <= 'F') {
    return ascii - 'A' + 10;
  } else if (ascii >= 'a' && ascii <= 'f') {
    return ascii - 'a' + 10;
  }
  Serial.print("! ");
  Serial.print("0x");
  Serial.print(ascii, HEX);
  Serial.println(" is not a valid hex symbol");
  return 0;
}

void hex_to_bytes(String string, uint8_t *dst, uint32_t dst_len) {
  if (string.length() % 2 != 0) {
    Serial.println("! Expecting hex string length to be a multiple of 2. Example: Send '04', not '4'.");
    return;
  }
  for (uint32_t i = 0; i < string.length() / 2; i++) {
    if (i >= dst_len) {
      return;
    }
    *dst = hex_nibble_to_val(string.charAt(i * 2)) * 16;
    *dst += hex_nibble_to_val(string.charAt(i * 2 + 1));
    dst++;
  }
}

int hex_to_int(String string) {
  int result = 0;
  for (int i = 0; i < string.length(); i++) {
    result <<= 4;
    result |= hex_nibble_to_val(string.charAt(i));
  }
  return result;
}

timo_register_info_t* find_register(String name) {
  for (int i = 0; i < (sizeof(timo_registers) / sizeof(timo_registers[0])); i++) {
    timo_register_info_t *reg = &timo_registers[i];
    if (String(reg->name).equals(name)) {
      return reg;
    }
  }
  return NULL;
}

void process_user_input(String string) {
  int16_t irq_flags;

  Serial.print("> ");
  Serial.flush();
  Serial.println(string);
  Serial.flush();
  split(string, " ", parsed_command, sizeof(parsed_command)/sizeof(parsed_command[0]));

  if (parsed_command[0] == "read" || parsed_command[0] == "read_reg") {
    parsed_command[0] = "READ_REG";
  } else if (parsed_command[0] == "write" || parsed_command[0] == "write_reg") {
    parsed_command[0] = "WRITE_REG";
  }
  
  if (parsed_command[0] == "NOP") {
    irq_flags = timo_transfer(TIMO_NOP_COMMAND, rx_buffer, tx_buffer, 0);
    print_response(irq_flags, rx_buffer, 0);
  } else if (parsed_command[0] == "READ_REG") {
    parsed_command[1].toUpperCase();
    timo_register_info_t *reg = find_register(parsed_command[1]);
    if (reg == NULL) {
      Serial.println("! " + parsed_command[0] + " Unknown register");
    } else {
      if (reg->read_access) {
        irq_flags = timo_transfer(TIMO_READ_REG_COMMAND(reg->address), rx_buffer, tx_buffer, reg->length + 1);
        print_response(irq_flags, rx_buffer, reg->length);
      } else {
        Serial.println("! " + parsed_command[0] + " Not allowed to read");
      }
    }
  } else if (parsed_command[0] == "WRITE_REG") {
    parsed_command[1].toUpperCase();
    timo_register_info_t *reg = find_register(parsed_command[1]);
    if (reg == NULL) {
      Serial.println("! " + parsed_command[0] + " Unknown register");
    } else {
      if (reg->write_access) {
        hex_to_bytes(parsed_command[2], tx_buffer, sizeof(tx_buffer));
        irq_flags = timo_transfer(TIMO_WRITE_REG_COMMAND(reg->address), rx_buffer, tx_buffer, reg->length + 1);
        print_response(irq_flags, rx_buffer, 0);
      } else {
        Serial.println("! " + parsed_command[0] + " Not allowed to write");
      }
    }
  } else if (parsed_command[0] == "READ_DMX") {
    memset(tx_buffer, 0xff, sizeof(tx_buffer));
    int read_length = hex_to_int(parsed_command[1]);
    if (read_length > 128) {
      Serial.print("! " + parsed_command[0] + " Invalid length: ");
      Serial.print(read_length);
      Serial.println(" (max is 128)");
    } else {
      irq_flags = timo_transfer(TIMO_READ_DMX_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
      print_response(irq_flags, rx_buffer, read_length);
    }
  } else if (parsed_command[0] == "FW_BLOCK_CMD_1") {
    poll_pending_irqs = false;
    irq_is_pending();
    hex_to_bytes(parsed_command[1], tx_buffer, sizeof(tx_buffer));
    irq_flags = timo_transfer(TIMO_FW_BLOCK_CMD_1_COMMAND, rx_buffer, tx_buffer, 255);
    while(!irq_is_pending());
    print_response(irq_flags, rx_buffer, 0);
  } else if (parsed_command[0] == "FW_BLOCK_CMD_2") {
    poll_pending_irqs = false;
    irq_is_pending();
    hex_to_bytes(parsed_command[1], tx_buffer, sizeof(tx_buffer));
    irq_flags = timo_transfer(TIMO_FW_BLOCK_CMD_2_COMMAND, rx_buffer, tx_buffer, 19);
    while(!irq_is_pending());
    print_response(irq_flags, rx_buffer, 0);
  } else if (parsed_command[0] == "disable_irq_polling") {
    poll_pending_irqs = false;
  } else if (parsed_command[0] == "enable_irq_polling") {
    poll_pending_irqs = true;
  } else if (parsed_command[0] == "generate_DMX") {
    uint16_t n_channels = 512;
    uint16_t interslot_time = 0;
    uint32_t refresh_period = 25000;
    tx_buffer[0] = n_channels >> 8;
    tx_buffer[1] = n_channels;
    tx_buffer[2] = interslot_time >> 8;
    tx_buffer[3] = interslot_time;
    tx_buffer[4] = refresh_period >> 24;
    tx_buffer[5] = refresh_period >> 16;
    tx_buffer[6] = refresh_period >> 8;
    tx_buffer[7] = refresh_period;
    irq_flags = timo_transfer(TIMO_WRITE_REG_COMMAND(TIMO_DMX_SPEC_REG), rx_buffer, tx_buffer, 1 + 8);

    tx_buffer[0] = 1;
    irq_flags = timo_transfer(TIMO_WRITE_REG_COMMAND(TIMO_DMX_CONTROL_REG), rx_buffer, tx_buffer, 1 + 1);
    
    for(uint16_t i = 0; i < 256; i++) {
      tx_buffer[i] = i;
    }
    for(uint16_t i = 0; i < 256; i++) {
      tx_buffer[256+i] =  255 - i;
    }
    irq_flags = timo_transfer(TIMO_WRITE_DMX_COMMAND, rx_buffer, tx_buffer, 1 + 128);
    irq_flags = timo_transfer(TIMO_WRITE_DMX_COMMAND, rx_buffer, tx_buffer + (1 * 128), 1 + 128);
    irq_flags = timo_transfer(TIMO_WRITE_DMX_COMMAND, rx_buffer, tx_buffer + (2 * 128), 1 + 128);
    irq_flags = timo_transfer(TIMO_WRITE_DMX_COMMAND, rx_buffer, tx_buffer + (3 * 128), 1 + 128);
    
  } else if (parsed_command[0] == "read_full_DMX_frame") {
    uint16_t window_size = 512;
    uint16_t start_address = 0;
    tx_buffer[0] = window_size >> 8;
    tx_buffer[1] = window_size;
    tx_buffer[2] = start_address >> 8;
    tx_buffer[3] = start_address;
    irq_flags = timo_transfer(TIMO_WRITE_REG_COMMAND(TIMO_DMX_WINDOW_REG), rx_buffer, tx_buffer, 1 + 4);
    uint8_t read_length = 128;
    irq_flags = timo_transfer(TIMO_READ_DMX_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
    irq_flags = timo_transfer(TIMO_READ_DMX_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
    irq_flags = timo_transfer(TIMO_READ_DMX_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
    irq_flags = timo_transfer(TIMO_READ_DMX_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
  }
  else if (parsed_command[0] == "read_ASC_FRAME") {
    uint8_t start_code = 0;
    uint16_t asc_frame_length = 512;
    tx_buffer[0] = asc_frame_length >> 8;
    tx_buffer[1] = asc_frame_length;
    tx_buffer[2] = start_code;
    //tx_buffer[2] = asc_frame_length >> 8;
    irq_flags = timo_transfer(TIMO_WRITE_REG_COMMAND(TIMO_ASC_FRAME_REG), rx_buffer, tx_buffer, 1 + 3);
    uint8_t read_length = 128;
    irq_flags = timo_transfer(TIMO_READ_ASC_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
    irq_flags = timo_transfer(TIMO_READ_ASC_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
    irq_flags = timo_transfer(TIMO_READ_ASC_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
    irq_flags = timo_transfer(TIMO_READ_ASC_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
  }
  
  else if (parsed_command[0] == "LINK_QUALITY") {
    uint8_t pdr = 0;
    tx_buffer[0] = pdr;
    irq_flags = timo_transfer(TIMO_WRITE_REG_COMMAND(TIMO_LINK_QUALITY_REG), rx_buffer, tx_buffer, 1 + 1);
    uint8_t read_length = 128;
    irq_flags = timo_transfer(TIMO_READ_ASC_COMMAND, rx_buffer, tx_buffer, 1 + read_length);
    print_response(irq_flags, rx_buffer, read_length);
    
  }
  else {
    Serial.println("! Unknown command");
  }
  Serial.flush();
}

char nibble_to_hex(uint8_t nibble) {
  nibble &= 0xf;
  if (nibble >= 0 && nibble <= 9) {
    return '0' + nibble;
  } else {
    return 'A' + nibble - 10;
  }
}

void print_response(int16_t irq_flags, uint8_t *data, uint32_t len) {
  if (irq_flags < 0) {
    Serial.println("! Timeout");
    return;
  }
  Serial.print("< ");
  print_irq_flags(irq_flags);
  Serial.print(" ");
  Serial.flush();
  for (uint32_t i = 0; i < len; i++) {
    Serial.print(nibble_to_hex(0xf & (data[i] >> 4)));
    Serial.print(nibble_to_hex(0xf & data[i]));
    Serial.flush();
  }
  Serial.println();
}

void print_irq_flags(int16_t irq_flags) {
  for (int i = 7; i >= 0; i--) {
    if (irq_flags & (1 << i)) {
      Serial.print('1');
    } else {
      Serial.print('0');
    }
  }
  Serial.flush();
}
 

Attachments

  • spi_master_example.zip
    6.3 KB · Views: 56
Status
Not open for further replies.
Back
Top