0

I want to read the TCD1304 linear CCD sensor with an ESP32 and I'm facing some problems. As a first "warm-up" and to get familiar with this sensor I tried to generate the sensors input signals using a timer interrupt and bitbanging, but soon I found out that the resulting signals were way to slow and I didn't get any useful readings.

The signals involved are:

  • SH (shift gate): determines the integration time
  • ICG (integration clear gate): used to transfer pixel values to the shift register
  • ΦM: (master clock): self-explanatory
  • OS: output signal

Timing chart of  the TCD1304

The timing of those signals seems to be quite critical (see diagram below) and need to be quite fast - supported master clock frequency ist 0.8-4MHz and an analog measurement has to be done after every 4th clock pulse (0.2-1MHz).

enter image description here

Right now I'm pretty lost and I believe that this task requires some progamming techniques that I'm not familiar with so far.

Basically, my question is:

What is the best way to generate those signals and how do I read the ADC at that high sampling rate/accurate in timing?

I have some ideas, but - as I said - I probably lack the required skills, so it's hard for me to decide which approach I should take:

  1. Using a single timer and direct port manipulation: This would be easy to implement, but my assumption is that the interrupts will fire much too frequently to handle the ISR (which would be more or less complex, even if I refactorize my current test code). Especially reading the ADC will take too much time, I guess.

  2. using the ESP's I2S-ADC: I have no experience with I2S. From what I understand it is good for reading the ADC with a high frequency, but it is typically used for periodic sampling. In my case I would need the readings to be aligned with the control signals and I don't know if that is possible/convenient with I2S-ADC.

  3. using ADC-DMA: @esben Rossel has a done great work providing a framwork for STM32s and the TCD1304 in which he uses DMA for readout. I have never used direct memory access, but I feel this is a promising approach. Unfortunately, the documentation of the ADC only covers the ADC-RTC mode while there is no information about how to use the ADC-DMA mode. If DMA is the way to go I appreciate any hints on how I can get started with DMA programming on the ESP32.

In the latter cases 2) and 3) I would still need to generate the driving pulses in a "CPU-friendly" manner, probably hardware PWM (?), while the analog reading has to be triggered at every fourth clock pulse.

Would I use one timer for each signal and if yes, how would I ensure that the signals have the correct phase offset? Also, how can I trigger the analog measurement after every 4th clock pulse?

5
  • I am not familiar with the internals of the ESP32, but if I were doing it on an equivalent PIC32 I'd probably use SPI to generate the clock signal, which I then divide by 2. SPI then generates an interrupt every 8 clocks (or 4 after dividing by 2) which triggers DMA to read from the ADC. Another DMA channel sends out dummy bytes through SPI to keep the clock running and performing enough transactions. I2S relies on an external CODEC chip to do the ADC (and DAC).
    – Majenko
    Commented Aug 13, 2020 at 11:42
  • @Majenko using SPI for clock generation is a nice idea, I like it! The ESP32's SPI supports 4-bit transmission mode, which seems to be perfect for this. But I still have no idea how I could synchronize the SPI dummy signal with the SH/ICG signals and how I would generate those. Do you think, bitbanging using a timer followed by a call to my dummy SPI transmission (which triggers ADC-DMA) could work?
    – Sim Son
    Commented Aug 13, 2020 at 12:31
  • The SPI interrupt could be used to manage those pulses I would think. You could also use it to start / stop the ADC reading DMA channel.
    – Majenko
    Commented Aug 13, 2020 at 12:32
  • @Majenko concerning I2S: do you mean I2S actually requires external hardware? From what I understand, the ESP32's I2S interface is capable of reading the internal ADC to achieve sampling rates of up to ~300kHz, or am I wrong?
    – Sim Son
    Commented Aug 13, 2020 at 12:33
  • As I mentioned earlier, I am not familiar with the internals. They may have added some loopback to an internal CODEC I guess, but I haven't a clue. I've only ever worked with external CODEC chips. You'd have to consult the TRM datasheet.
    – Majenko
    Commented Aug 13, 2020 at 12:35

1 Answer 1

0

The chip has been driven from the digital I/O pins of a few different Arduino class boards and even read with the analog inputs. But if you want reproducible readings, that is to say, that you are not just making a toy, you want to be quantitative, then you need to take it a bit more seriously.

You can find a good answer to this, with circuits and code and even a spice model, here:

https://github.com/drmcnelson/Linear-CCD-with-LTSpice-KiCAD-Firmware-and-Python-Library

Here is a summary of some of the issues and at a high level how you get around them.

The electrical problems challenges are (a) that the SH and ICG pins have very large capacitances and (b) that the output source impedance and offset vary over a wide range from chip to chips.

The first of the challenges is dealt with by using logic buffers or drivers to provide sufficient current. With a 4mA dio pin by itself, the rise time is order of 4V x 600pf/4mA ~ 600nsec. Historically it seems to work, perhaps despite expectations, but it is a bit slow. (The above repo focuses on the analog signal and omits this.)

The second challenge is addressed using a voltage follower. Look around to see how to do this.

If you choose to use the internal ADC in the Arduino or other MCU board, then you have to deal with the large internal resistance that they insert in series before the switched sampling capacitor. And, the 600mV output swing of the TCD1304 is only 1/4 of the range of the internal ADC at 3.3V.

The solution to both of those is a second opamp to shift and amplify, or invert shift and amplify, so that the signal fills the ADC's range and enough current is provided so that the sampling capacitor reaches the correct voltage within the sampling window in time.

What is special about reading a CCD in this regard is that you can have steps from 0 to full scale that mean something, whereas in an audio signal those would normally be noise. So the response of the analog input is very important for reading the CCD and not loosing spatial resolution.

If you choose to use an external ADC, you still need to design the analog section carefully, usually the datasheet for the ADC will give you some help. But then you have to deal with timing in the MCU and in particular the SPI interface.

In many MCU boards there is an inconvenient setup time for each SPI tansfer, and the standard Arduino SPI library implements the 16 bit transfer as two 78 bit transfers. So, generally, if you want even 0.5MHz data rate, you will need to do some careful timing and a correct implementation of the 16 bit transfer. I posted a corrected implementation to my github, for the SPI library for one arduino board and there are some discussions how to do this in the Teensy forum. It depends on your processor, but most nowadays have a way to set the transfer size.

So that is how you do it, in a nutshell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.