Teensy LC Code Not Working on External Power

Status
Not open for further replies.

ryan1

Member
Hello!

My project involves sending commands over UART from my computer to my Teensy LC, triggering the Teensy to send commands over I2C (using the Wire.h library) to an external device that can measure impedance. When I have my Teensy LC connected via USB, the code works just fine--I can measure impedance, and all the data is sent back to my main computer without issue. However, when I connect my Teensy to an external power supply, my code does not work (though it is running--I can tell because I have LEDs blinking). I've been scratching my head over this for hours now and thought I would come here for help.

My setup involves 12 V battery --> RECOM 12V-to-5V DC power converter (500 mA) --> Teensy LC. I'm pretty sure the Teensy isn't current-limited; my multimeter showed it only drawing around 26 mA or so.

My code is below. I apologize if it is messy or confusing in any way--I'm fairly new to coding, and I'm definitely more of a mechanical guy than a computer guy.

Code:
#include <Wire.h>

// define macros
#define IA_ADDRESS              0x0D  //IA 
#define BLOCK_WRITE_CMD         0xA0  //block write command
#define BLOCK_READ_CMD          0xA1  //block read command
#define ADR_PTR_CMD             0xB0  //address pointer command

// define registers
#define CONTROL_REG             0x80
#define START_FREQUENCY_REG     0x82
#define FREQUENCY_INCREMENT_REG 0x85
#define NUM_INCREMENTS_REG      0x88
#define NUM_SETTLING_TIME_REG   0x8A
#define STATUS_REG              0x8F
#define TEMP_DATA_REG           0x92
#define REAL_DATA_REG           0x94
#define IMAG_DATA_REG           0x96

// define control register commands
#define INIT_START_FREQ         0x10
#define START_FREQ_SWEEP        0x20
#define INC_FREQ                0x30
#define REPEAT_FREQ             0x40
#define MEASURE_TEMP            0x90
#define PWR_DOWN                0xA0
#define STAND_BY                0xB0

// define frequency multiplier for start frequency calculation based on default 16 MHz clock speed
#define FREQUENCY_MULTIPLIER 33.5544

// define serial UART port
#define HWSERIAL Serial1

// initialize sweep parameters
struct SWEEP_PARAM
{
  bool calibration_Flag;
  int start_Freq;
  int freq_Step;
  int sweep_Samples;
  bool repeat_Flag;
}SweepParam;

// initialize pin variables
int ledPin = 13;
int sdaPin = 18;
int sclPin = 19;
int txPin = 1;
int rxPin = 0;
int selPin = 3;

// initialize blockWrite arrays
byte StartFreqData[3];
byte FrequencyIncData[3];
byte NumIncData[2];
byte SettlingTimeData[2];
byte ControlData[2];
byte realData[2];
byte imagData[2];

// initialize variables used for impedance analyzer data collection
int pga_gain = 1;
int voltage_range = 2000;
int16_t rVAL = 0;
int16_t iVAL = 0;

// SETUP AND LOOP FUNCTIONS

void setup() {
  delay(5000);
  Wire.setSDA(sdaPin);
  Wire.setSCL(sclPin);
  Wire.begin();

  HWSERIAL.setTX(txPin);
  HWSERIAL.setRX(rxPin);
  HWSERIAL.begin(115200);
  
  pinMode(ledPin, OUTPUT);
  pinMode(selPin, OUTPUT);
  
  // Serial.begin(9600);
  
  SweepParam.calibration_Flag = false;
  SweepParam.start_Freq = 0;
  SweepParam.freq_Step = 0;
  SweepParam.sweep_Samples = 0;
  SweepParam.repeat_Flag = false;

  // while (!Serial);
  while (!HWSERIAL);
}

void loop() {
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
  digitalWrite(ledPin, HIGH);

  String reception = "";
  // Serial.println("Booted up and waiting for command from UART.");
  HWSERIAL.clear();
  while(reception.length() < 22){
    if(HWSERIAL.available() > 0){
      char inChar = HWSERIAL.read();
      reception += inChar; 
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
      }
  }
  evaluateCommand(reception);
//  digitalWrite(ledPin, LOW);
//  delay(300);
//  digitalWrite(ledPin, HIGH);

  int16_t* data_ptr = PerformFrequencySweep();

  for(int k = 0; k < (2*SweepParam.sweep_Samples); k++){
    //Serial.print((String)data_ptr[k] + ", ");
    //Serial.println(data_ptr[k], HEX);
    if(data_ptr[k] < 0){
      HWSERIAL.write(0x01);
    }
    else{
      HWSERIAL.write(0x00);
    }
    
    HWSERIAL.write(data_ptr[k] >> 8);
    HWSERIAL.write(data_ptr[k]);
  }

  // send some bits to indicate end of data
  HWSERIAL.write(0x00);
  HWSERIAL.write(0x01);
  HWSERIAL.write(0x02);
  HWSERIAL.write(0x03);

  delete[] data_ptr;
  delay(100);
}

// EVALUATING INCOMING MESSAGES OVER UART

void evaluateCommand(String cmd){
  // Serial.println(command);

  int checker = SweepParam.start_Freq;

  voltage_range = cmd.substring(1,5).toInt();
  SweepParam.calibration_Flag = cmd.substring(0,1).toInt();
  SweepParam.start_Freq = cmd.substring(5,11).toInt();
  SweepParam.freq_Step = cmd.substring(11,17).toInt();
  SweepParam.sweep_Samples = cmd.substring(17,20).toInt();
  SweepParam.repeat_Flag = cmd.substring(20,21).toInt();
  pga_gain = cmd.substring(21).toInt();

  // Serial.println((String)"Calibration flag: " + SweepParam.calibration_Flag);
  // Serial.println((String)"Start frequency: " + SweepParam.start_Freq);
  // Serial.println((String)"Frequency increment: " + SweepParam.freq_Step);
  // Serial.println((String)"Number of samples: " + SweepParam.sweep_Samples);
  // Serial.println((String)"Repeat flag: " + SweepParam.repeat_Flag);
  // Serial.println((String)"PGA Gain: " + pga_gain);
  // Serial.println((String)"Voltage range (mV): " + voltage_range);

  if(pga_gain == 5){
    // Serial.println("SEL connected to 3.3V");
    digitalWrite(selPin, HIGH);
  }
  else {
    // Serial.println("SEL connected to ground");
    digitalWrite(selPin, LOW);
  }
}

// BLOCK READING AND WRITING

void setRegisterPointer(uint8_t RegAddress)
{
  Wire.beginTransmission(IA_ADDRESS);
  Wire.send(ADR_PTR_CMD);  
  Wire.send(RegAddress);
  Wire.endTransmission();
}

void blockWrite(int numBytes, uint8_t *data) {
  // Serial.println("New command");
  
  Wire.beginTransmission(IA_ADDRESS);
  Wire.send(BLOCK_WRITE_CMD);
  Wire.send(numBytes);
  for(int i = 0; i < numBytes; i++)
  {
    // Serial.println(data[i], HEX); // display data being transmitted to slave device
    Wire.send(data[i]);
  }
  
  Wire.endTransmission();
}

void blockRead (int numBytes, uint8_t *buffer, uint8_t RegAddress)
{
  
  for(int i = 0; i<numBytes; i++)
  {
    setRegisterPointer(RegAddress);
    Wire.requestFrom(IA_ADDRESS, 1);
    while(Wire.available())
    {
      buffer[i] = Wire.receive();
    }
    Wire.endTransmission();
    RegAddress++;
  }
}


// ESTABLISHING SETTINGS AND COLLECTING DATA

void setSweepParam(bool cal_Flag, int begin_Freq, int f_Step, int nSamples, bool r_Flag){
  SweepParam.calibration_Flag = cal_Flag;
  SweepParam.start_Freq = begin_Freq;
  SweepParam.freq_Step = f_Step;
  SweepParam.sweep_Samples = nSamples;
  SweepParam.repeat_Flag = r_Flag;
}

void setStartFrequency(unsigned int freq){
  unsigned long result = freq * FREQUENCY_MULTIPLIER;

  // Serial.println(result, HEX);

  StartFreqData[2] = (uint8_t) (result & 0xFF);   
  StartFreqData[1] = (uint8_t) ((result >> 8) & 0xFF); 
  StartFreqData[0] = (uint8_t) ((result >> 16) & 0xFF);
  
  setRegisterPointer(START_FREQUENCY_REG);
  blockWrite(3, StartFreqData);
}

void setFrequencyIncrement(unsigned int deltaFreq)
{
  unsigned long result = deltaFreq * FREQUENCY_MULTIPLIER;
  
  FrequencyIncData[2] = (uint8_t) (result & 0xFF);
  FrequencyIncData[1] = (uint8_t) ((result>>8) & 0xFF);
  FrequencyIncData[0] = (uint8_t) ((result>>16) & 0xFF);
  
  setRegisterPointer(FREQUENCY_INCREMENT_REG);
  blockWrite(3, FrequencyIncData);
}

void setNumSamples(unsigned int samples)
{
  // NOTE: samples must be lower than 512
  NumIncData[1] = (uint8_t)(samples & 0xFF);
  NumIncData[0] = (uint8_t)((samples>>8) & 0xFF);
  
  setRegisterPointer(NUM_INCREMENTS_REG);
  blockWrite(2, NumIncData);
}

void setSettlingTime(unsigned int settlingTime, unsigned int settlingFactor)
{ 
  SettlingTimeData[1] = (uint8_t)(settlingTime & 0xFF);
  SettlingTimeData[0] = (uint8_t)((settlingTime >> 8)& 0xFF);    

  switch(settlingFactor)  {
    case 0:
      SettlingTimeData[0] |= 0x00; 
      break;
    case 1:
      SettlingTimeData[0] |= 0x2; //D10, D9 --> 01 
      break;
    case 3: 
      SettlingTimeData[0] |= 0x6; //D10, D9 --> 11
      break; 
  }
   
  setRegisterPointer(NUM_SETTLING_TIME_REG);
  blockWrite(2, SettlingTimeData);
}

void setControlRegister(unsigned int function, unsigned int gain)
{
  // Starts with the bits representing the desired PmodIA function
  ControlData[0] = 0x00;
  ControlData[0] |= function; 
  // Serial.println(ControlData[0], HEX);
  
  // Adds the voltage range bits (note values are in mV)
  switch(voltage_range){
    case 200:
      ControlData[0] |= 0x2; break;
    case 400:
      ControlData[0] |= 0x4; break;
    case 1000:
      ControlData[0] |= 0x6; break;
    case 2000:
      ControlData[0] |= 0x0; break;
    default:
      break;
  }

  // Adds the gain bits
  switch(gain)
  {
    case 1: // x1
      ControlData[0] |= 0x1; break;
    case 5: // x5
      ControlData[0] |= 0x0; break;
    default:
      break;
  }

  // Serial.println("Final Control Data");
  // Serial.println(ControlData[0], HEX);
  
  setRegisterPointer(CONTROL_REG);
  Wire.beginTransmission(IA_ADDRESS);
  Wire.send(CONTROL_REG);
  Wire.send(ControlData[0]);
  Wire.endTransmission();
}

int16_t* PerformFrequencySweep(){
  uint8_t BIT_1, BIT_2;
  int16_t* data_array = new int16_t[2 * SweepParam.sweep_Samples]; // 2x the number of samples because this has real and imaginary parts
  int index = 0;
  
  setStartFrequency(SweepParam.start_Freq);
  setFrequencyIncrement(SweepParam.freq_Step);
  setNumSamples(SweepParam.sweep_Samples); 
  setSettlingTime(10, 3);

  // Start data collection
  // 1. Place AD5933 into standby mode;
  setControlRegister(STAND_BY, pga_gain);
  delay(50);

  // 2. Initialize the Start Frequency Command to the Control Register
  setControlRegister(INIT_START_FREQ, pga_gain);
  delay(50);

  // 3. Program Start Frequency Sweep Command in the Control Register
  setControlRegister(START_FREQ_SWEEP, pga_gain);
  delay(50);

  for(int num = 0; num < SweepParam.sweep_Samples; num++){
    // 4. Poll Status register to check if the DFT conversion is complete 
    setRegisterPointer(STATUS_REG);

    do{
      Wire.requestFrom(IA_ADDRESS,1);
      BIT_1 = Wire.receive();
      BIT_1 = BIT_1 & B00000010;
    }while(BIT_1 != 0x02);

    // 5. Read values from real and imaginary registers and add to array
    blockRead(2, realData, REAL_DATA_REG);
    rVAL = realData[0];
    rVAL = rVAL << 8;
    rVAL |= realData[1]; 
    data_array[index] = rVAL; 
    index++;
  
    blockRead(2, imagData, IMAG_DATA_REG);
    iVAL = imagData[0];
    iVAL = iVAL << 8;
    iVAL |= imagData[1];
    data_array[index] = iVAL; 
    index++;
    
    // 6. Poll Status Register to check if frequency sweep is done
    do{
      Wire.requestFrom(IA_ADDRESS,1);
      BIT_2 = Wire.receive();
      BIT_2 = BIT_2 & B00000100;
    }while(BIT_2 != 0x04);

    if(SweepParam.sweep_Samples == 0 ){       
      break;
    }
    else if(SweepParam.repeat_Flag){ // Repeat Frequency Command if flagged 
      setControlRegister(REPEAT_FREQ, pga_gain);
      delay(10);
    }
    else{
      setControlRegister(INC_FREQ, pga_gain);
      delay(10);
    }
    
  }

  // 7. Program the AD5933 into Power Down Mode
  setControlRegister(PWR_DOWN, pga_gain);

  // Note that this data array will have every even index (i.e. 0, 2, 4, etc) as the real data
  // and every odd index (i.e. 1, 3, 5, etc) as the imaginary data
  return data_array;
}

I've thrown a bunch of LED blink commands all over my code to see if there were any infinite loops my code was getting stuck on, but there were none. Due to quick LED blinking, it appears that I am receiving the data over UART. However, I don't think that the "SweepParam" structure variables are being overwritten in the evaluateCommand() function because the script seems to speed through the PerformFrequencySweep() function without doing anything, ending back to the beginning of the void loop() function (I can tell because of the 1-second long LED blink happens again after command is received over UART HWSERIAL). The PerformFrequencySweep() function usually takes around 2 seconds or so. Another reason I think this is because I did write an if-statement (not in the code right now) comparing SweepParam.start_Freq before and after the evaluateCommand() function, and it didn't seem to change.

Hence, I suspect my error is somewhere in the evaluateCommand() function, or perhaps something is wrong with HWSERIAL reading data commands.

Does anyone have any idea why this might be the case? I'm truly stumped. Any tips would be greatly appreciated, and please let me know if any more information is needed. Thank you!!
 
As a first quick check, program your Teensy LC with the LED blink example (File > Examples > 01.Basics > Blink) and then power up with only the external power to see whether the LED blinks. That can at least point to whether to focus your effort on the power supply or the software side.
 
Thanks for the quick reply! I didn't think I needed to run the blink example because I have the LED blink in my void loop() function. When I apply external power, the LED turns off for 1 second and then turns back on. Then, I send a command from my computer over UART to the Teensy, and the Teensy blinks a bunch of times according to this code in void loop():

Code:
  while(reception.length() < 22){
    if(HWSERIAL.available() > 0){
      char inChar = HWSERIAL.read();
      reception += inChar; 
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
      }

However, almost immediately after that, the LED blinks slowly once again (indicating that the script has returned to the beginning of void loop() and is awaiting another command). The Teensy is supposed to send commands over I2C to another measurement device, receive data from that measurement device, and then send that data over UART. I'm pretty sure my problem is on the software side, seeing that the LEDs blink according to how I programmed it. With a lot more debugging, I think I managed to narrow down the problem to somewhere in the evaluateCommands() function or maybe my HWSerial UART function calls in void loop(), but I'm not 100% sure.
 
Using some if-statements and LED blinks, the problem seems to be with the UART reading garbage when powered by 5V. It's not getting the right characters that are being sent from my computer. I checked by first plugging in the Teensy via the USB port and having my computer send characters to it, prompting the Teensy to blink the LED every time a certain character comes through. However, when I power the Teensy externally, the LED does not blink as I told it to when a certain character comes through HWSERIAL, though it appears that characters are coming through (otherwise the code would never exit the loop I wrote in my previous reply above).

Does anyone know why this is the case and what the solution might be?
 
Update: I cut the trace between USB power and external power (as one might do to run Teensy off external power while having the USB plugged in). Still doesn't work. Seems that the Teensy has to be connected to my computer via the micro-USB to USB cord in order for the UART to not collect garbage data. No idea why this is the case.

Also tried setting the rxPin to INPUT_PULLUP. Still no luck.
 
The first thing I look for when someone says that their program does not work on external power is lines like:
Code:
  // while (!Serial);
  while (!HWSERIAL);
This says wait forever until the USB connected. Which are the last few lines of your setup function.

What I often do is something like:
while(!HWSerial && millis() < 5000) ;

Which will wait for up to 5 seconds after the teensy starts... If I wanted a more precise wait I would either use something like elapsedMillis or do it myself, by grabbing the millis() before my wait as a start time and then compare millis() - start_time to be less than 5000, but since this is close enough to start of program, I usually do it the lazy way above.
 
The first thing I look for when someone says that their program does not work on external power is lines like:
Code:
  // while (!Serial);
  while (!HWSERIAL);
This says wait forever until the USB connected. Which are the last few lines of your setup function.

What I often do is something like:
while(!HWSerial && millis() < 5000) ;

Which will wait for up to 5 seconds after the teensy starts... If I wanted a more precise wait I would either use something like elapsedMillis or do it myself, by grabbing the millis() before my wait as a start time and then compare millis() - start_time to be less than 5000, but since this is close enough to start of program, I usually do it the lazy way above.

Thanks for the input! Unfortunately, I don't think this did anything.

I now have void loop() like this:

Code:
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
  digitalWrite(ledPin, HIGH);

  String reception = "";
  // Serial.println("Booted up and waiting for command from UART.");
  Serial1.clear();
  while(reception.length() < 22){
    if(Serial1.available() > 0){
      char inChar = Serial1.read();
      reception += inChar; 
      delay(50);
      digitalWrite(ledPin, LOW);
      delay(50);
      digitalWrite(ledPin, HIGH);
      }
  }
  digitalWrite(ledPin, LOW);
  delay(5000);
  digitalWrite(ledPin, HIGH);
  evaluateCommand(reception);

When I have the USB attached to the Teensy, the code runs fine. When I don't, the LED doesn't turn off for 5 seconds after the loop, meaning that the loop is never exited. Interesting.

I tried changing the baud rate from 115200 to 9600 on both devices. Now it appears that the Teensy doesn't receive anything when the USB is detached (the LED doesn't blink rapidly).
 
Last edited:
A bunch of questions I have but might not be relevant:
Could something else be blocking reception via UART? Do I need to disable the other RX/TX pins for some reason?
Do all the ground pins on the Teensy need to be connected to ground?
What differences are there in connecting the USB vs not connecting it? Right now, the micro-USB is not supplying power--I have both the micro-USB and the external power supply connected with the appropriate trace cut (double checked with multimeter). I swapped out my converter and 12V battery and powered the Teensy with just 4.5 V (3 AAA batteries)--still the same problems.
 
Last edited:
I'm sorry...I'm just going to keep rambling on this post about my debugging process in the hope that it might trigger an idea from someone.

I luckily had a Teensy 3.2 on hand and tested the same code on it making sure the pin numbers are right. Same problem, and now it's on a breadboard, not my PCB. When I have the USB plugged in, it works fine. When I don't, the code doesn't work (but still runs), and I don't think that the UART is receiving properly. Unlike the Teensy LC, interesting enough, is that the code exits the loop (shown below) in void loop() just fine (like the Teensy LC used to do at the beginning of this thread).

Code:
  while(reception.length() < 22){
    if(Serial1.available() > 0){
      char inChar = Serial1.read();
      reception += inChar; 
      delay(50);
      digitalWrite(ledPin, LOW);
      delay(50);
      digitalWrite(ledPin, HIGH);
      }
  }

Hence, I think it is safe to conclude that my Teensy LC board isn't broken.
 
Glancing at posted #1 code - it is using Serial1 as HWSERIAL - that is cool. Those are the default pins to the SET_x() commands are not needed.

To talk to UART the other device at same baud also needs to have GND as well as Rx and Tx properly 'crossed'.

For a TEST with USB connected change "#define HWSERIAL Serial1" to " #define HWSERIAL Serial " and the SerMon should work to show the expected output.

Also was the suggested 'BLINK' sketch tried? That should work with or without USB connected to upload with USB then BLINK if the T_LC is running right.

Going back to "#define HWSERIAL Serial1" then should work if there is a usable 3.3V UART device connected at the same baud with GND/Rx/Tx connected.
 
Ok, this is a long-shot guess, but it probably can't hurt (unless I've misunderstood your code - admittedly I did only spend a few minutes....)

Try replacing the dynamic memory allocation in PerformFrequencySweep() with just a static maximum size buffer.

So replace this:

Code:
  int16_t* data_array = new int16_t[2 * SweepParam.sweep_Samples];

with this (assuming 500 is big enough for the worse case)

Code:
  static int16_t data_array[500];

and then in loop(), of course get rid of this:

Code:
  delete[] data_ptr;

In theory I can't see why this would be really different with versus without the USB connected. I did say it was a long-shot guess. But everything else in your program seems pretty straightforward, except for this dynamic memory use. Your code is using the pointer without checking for NULL, which would cause a memory fault (when you try to write using that pointer) and make your program stop if for some reason it couldn't allocate whatever size you need. Since the function isn't recursive and your use appears to immediately free the memory before allocating again, seems like a simpler fixed size buffer would work just as well. Maybe also add some code to check for the condition where SweepParam.sweep_Samples is too large and avoid writing or reading beyond the end of the fixed size buffer.
 
Thanks for the responses!

Turns out that I need to attach a wire between the ground pins of the Teensy and the device connected to my computer. Didn't know you needed to do that...I've used UART in the past without attaching the ground pins of the two devices.

Oh well. I'm glad it was a quick solution. Thank you everyone who helped me out!
 
Status
Not open for further replies.
Back
Top