TCD1304 with teensy 3.2

Hi Qar,
I assume you are talking about the TCD1304neg code for the 3.2 and not the ntyrell code for the 4.0. I have worked with the ntyrell code and have found that it works well (although not perfectly for my application). I dont have a Teensy 3.2, so I can't test this stuff out properly. If you are willing to switch to the Teensy 4.0 (or 4.1) I might be of more use to you.
I thought I might be able to modify the 3.2 code a little, put it on my teensy 4.0 and see what the scope says is happening. Unfortunately, I have not been able to get the code loaded.
Anyway, in my experience the TCD works fine on 3V3 power and no transistor. Seems to me that I have seen schematics were people put a logic inverter between the microcontroller and the tcd1304. I am not sure whether the code you are using is set up for that or not. I think one of the posts above had at least one of the pins going the wrong direction. If the logic is backwards (high when it is supposed to be low) then you are not going to get anything meaningful out.
The other thing I would point out is that someone above said that the sensor is really sensitive to light. I think that is the most likely source of your problem. If you have this thing working in a normal room level light, I think you need to run with something like 5 ms integration time or else all of the pixels will be saturated (relatively low voltage output. I think it is somewhere between 0.6 and 1.2 V). If you have the TCD1304 in total darkness, you should get a higher voltage reading on the output pin (like about 2.8 V). The integration time is the period of the SH pin. Looking at the TCD1304neg code, it looks like t_int is the integration time and it is set at 20000 us as a default. While you can cut the input value down, it looks like the user can't go below the ICG minimum, which is ~14 ms / fm where fm is the clock speed in MHz. Not sure what the clock speed is here, but if it is 2MHz then yoiur integration time is at least 7 ms. That is not a limitation of the TCD1304, it is just what the code says.
Good luck on this.
I know it's an old topic, but I think it fits here for everyone who is searching and has not much experience with electronics like me.

I wrote a code using interrupts to fit the timings with a teensy 4 and couldn't get get the sensor to work at 1Mhz. I found this thread and tried the code of jremington, but also only constant output on all 3694 pixels.

I am using a breadboard connecting everything directly to a teensy 4.0 (and a teensy 3.6 at 96Mhz for the code here). I have no oscilloscope, so i can't check the graphs. I read low Mhz range is working fine on breadboards (1Mhz I2C was working fine for me). I checked the datasheet and the 3.3V power supply (to prevent damaging the ADC) is fine for 1Mhz clock. So can the sensor run without a transistor at the output, capacitor on the input (the power supply line has a capacitor on the teensy board) on a breadboard? Is aliexpress 10€ sensor the reason for the problems?

Sorry for the late reply. The code posted in reply #23 assumes that there are inverting buffers on the TCD1304 SH and ICG inputs. To make it work with a direct connection, the HIGH and LOW states must all be swapped.

This is essentially that same code, but with those modifications, has been tested and works perfectly.

#include "ADC.h"
// last updated 12/3/2023 by SJR, new ADC library calls
// Works for TCD1304DG with DIRECT connections to control inputs
// Notebook 9/13/20 p9
// Teensy 4.0 stuff at
// TCD1304 timing notes from
    The data-rate is 1/4 of fM  (4 us/sample for fM = 1MHz)
    Pixels are only moved to the shift registers when ICG and SH coincide. If SH runs with a shorter period than ICG, the CCD runs in electronic shutter mode, and SH serves to control the integration time.
    The shortest integration time is 10 µs
unsigned long fM  = 1000;       // Mclk in !!!! kHz !!!!!!!!!!
unsigned int t_int = 20000;      // us (min 10 us)
unsigned int n = 1;             // nr SH pulses for whole T_ICG
unsigned int nrSHs = 0;         // nr of executed SH pulses
unsigned long T_ICGmin = 3694 * 4 * 1000 / fM ;  // us  >= n * t_int;

unsigned long T_ICG;

int LED_control = 2; //low to activate
int fMPin   = 3;      // Mclk out 3  FTM1
int SHPin   = 14;     //
int ADCtrc  = 6;      // ADCtriggerClock out 6 FTM0, only internal use
int ICGPin  = 15;     //
int analogPin = A3;   // Pin 17
ADC *adc = new ADC(); // adc object
uint16_t buffer[4000];
elapsedMicros elMi;
char cmdBuffer[16];
int cmdIndex;
char LED_state=1;  //off

void setup()
  pinMode(analogPin, INPUT);
  pinMode(SHPin,     OUTPUT);
  pinMode(ICGPin,    OUTPUT);
  pinMode(fMPin,     OUTPUT);
  pinMode(ADCtrc,    OUTPUT);
  pinMode(LED_control, OUTPUT);

  digitalWriteFast(SHPin, HIGH);
  digitalWriteFast(ICGPin, LOW);
  digitalWriteFast(LED_control, LED_state);

  /*  The pulses for Mclk and ADCtrigger are made with PWM outputs.
      On teensy 3.2 there are 3 timers for PWM:
      FTM0  can drive pins 5, 6, 9, 10, 20, 21, 22, 23
      FTM1  3, 4
      FTM2  25, 32
      all pins of one timer have the same frequency,
      so three different frequencies are possible.
      We can set the
        resolution with analogWriteResolution(bits),
        frequency with analogWriteFrequency(pin, frequency) and
        dutycycle with analogWrite(pin, value)
      Depending on the resolution we can choose different ranges of
      frequency, the lower the resolution the higher the possible frequency.
      e.g. 5 bits up to 1,5 MHz,  4 bits up to 3 MHz. (f_CPU = 96 MHz)
      The dutycycle must be half of 2^resolution (16 for 5 bits, 8 for 4 bits)
      to get a square wave.
      for details:
  analogWriteResolution(4);             // f <= 3 MHz
  analogWriteFrequency(fMPin, fM * 1000);
  analogWrite(fMPin, 8);             // dutycycle 50% von 2^4
  analogWriteFrequency(ADCtrc, fM * 1000 / 4);
  analogWrite(ADCtrc, 8);              // dutycycle 50% von 2^4

     Settings for analog Read
     for details:
  adc->adc0->setAveraging(1);                 // set number of averages
  adc->adc0->setResolution(12);               // set bits of resolution
  adc->adc0->singleMode();              // is needed for analogReadFastADC0()

  //  Serial.println(T_ICGmin);

/* the function uint16_t analogReadFastADC0(uint8_t pin)
   is from ADC.cpp and ADC_Module.cpp, here specialised for ADC0
   for more speed; this one takes 2,2 us with f_CPU = 96 MHz
uint16_t analogReadFastADC0(uint8_t pin) {
  uint16_t result;

  adc->adc0->startReadFast(pin);      // start single read (with adc->adc0->singleMode(); in setup() )
  while (adc->adc0->isConverting()) {} // wait for the ADC to finish
  //  __disable_irq();
  result = (uint16_t)ADC0_RA;
  //   __enable_irq();
  return result;
// expect decreasing output for increasing illumination.

void readTCD() {
  int i;

  n = 1;                                // calculate n and T_ICG from t_int
  while (n * t_int < T_ICGmin) {
    n++;  // t_int is given by user input
  T_ICG = n * t_int;
  while (!digitalReadFast(ADCtrc)) {}
  digitalWriteFast(ICGPin, LOW);
  digitalWriteFast(SHPin, HIGH);
  elMi = 0;
  digitalWriteFast(SHPin, LOW);
  nrSHs = 1;
  digitalWriteFast(ICGPin, HIGH);
  for (i = 0; i < 3694; i++) {
    while (!digitalReadFast(ADCtrc)) {}
    buffer[i] = analogReadFastADC0(analogPin);  // takes 2,2 us
    if (elMi >= nrSHs * t_int) {                // new SH-puls
      digitalWriteFast(SHPin, HIGH);
      digitalWriteFast(SHPin, LOW);
  while (elMi < T_ICG) {}
} //exits with SH LOW

void printBuffer() {
  int i, j, jmax, Imax = 0, jmin, Imin=10000;
  //0 to 3693, 3694 elements
  //skip first 16 and last 14
  for (i = 16; i < 3680; i++) {
    j = i - 16; //index for 3648 active elements + 16 light shield
    Serial.print (j);
    Serial.print (',');
    if (buffer[i] > Imax) {
      Imax = buffer[i];
      jmax = j;
     if (buffer[i] < Imin) {
      Imin = buffer[i];
      jmin = j;
  Serial.print(Imin); Serial.print("@"); Serial.print(jmin); Serial.print(" "); Serial.print(Imax);
  Serial.print("@"); Serial.println(jmax);
  Serial.print("T_ICG "); Serial.print(T_ICG); Serial.print(" min: "); Serial.print(T_ICGmin);
  Serial.print(" T_int "); Serial.print (t_int); Serial.print(" nSH "); Serial.println(n);

void loop() {
  if (Serial.available())  {
    cmdBuffer[cmdIndex++] =;
  if (cmdBuffer[0] == 'r')  {
  else if (cmdBuffer[0] == 't')  {
    LED_state = !LED_state;
    digitalWriteFast(LED_control, LED_state);

  cmdBuffer[0] = '\0';
  cmdIndex = 0;
Last edited:
I am preparing a post for github with circuits and firmware for the TCD1304 with T4, and a python class library and control program. It illustrates an optimal electrical design for signal and digitization performance, and a full set of features including clocked, triggered and gated frames and kinetic series. Hopefully it will be up in the next week or two. I have used this in my research for a few years.

Stay tuned.
Hi all,
I am trying to replicate Reims project. I am reusing the setup from my supervisors, who were successfull in replicating this in past. I already checked the connections, the tcd1304 itself and even swapped out detectos and microcontroller to an unused one (teensy 3.2). Upload of Reims script is successfull. The setReference function had to be changed to one input, like jremington did. All I get as an output on the serial monitor/plotter of Aduino IDE is "14776" , once. No matter if the sensor is covered or uncovered. So I dont really have a serial output. The hardware should be fine. The adc libary on the latest version. Does anybody have an idea what could cause this? Are there any important steps or settings that I should look at/take care of to guarantee an ouput? I dont have much experience within using Aduino IDE.
@Huyd It sounds like you are asking for help debugging. That is okay, I think. But you need to post your code and circuit. With apologies to the moderator, I think you could start a new thread, you are not really asking about the original post from this thread but rather how to get your own gizmo working.

And finally, as noted above, I would very much suggest you take a quick read through the following. You will find there most of what you need to know about using a Linear CCD.

Also, if you can, take a look in Hobb's book "Building Electro-Optic Systems", just a page or two, but important to know.
@Qar sorry for the delayed response.

If you want something that is reproducible you need a transistor follower, or better still an opamp follower, and you need an RC (resistor in series with capacitor to ground) - because (A) you are driving an SAR type ADC in the microcontroller, and (B) there is a large variation in impendance in the sensor. There are a lot of application notes and text book entries on driving an SAR ADC, Analog devices has some good ones as I recalll.

See my note above for links for more on the topic.