Close

16 Bit AD7667-ADC/MAX232/AD8021 Driving The TCD1304AP CCD chip...

A project log for DAV5 V3.01 Raman Spectrometer

The only thing worth doing, is the thing worth doing right!

david-h-haffner-srDavid H Haffner Sr 02/11/2017 at 14:250 Comments

After listening to some very wise counsel here at Hackaday, (thanks @Ted Yapo and @K.C.Lee,) I have decided to go full blown and build the 16 bit CCD driver. This will drive the over all cost up a bit but in the long run its worth the investment.

I would also encourage everyone reading this post to please take a little time and read my new blog at http://dhaffnersr.blog/

The reason is, it will explain why I have to do things in a certain way and in a certain order (I know it seems odd, but after reading my blog you will understand.)

Here is the new schematic and code;

16 bit version

#include <util/delay_basic.h>

#define RD (1<<0)

#define CS (1<<1)

#define CNVST (1<<2)

#define BYTESWAP (1<<3)

#define ICG (1<<4)

#define SH (1<<5)

#define MCLK (1<<6)

// Full frame, including dark pixels

// and dead pixels.

#define PIXEL_COUNT 3800

#define CLOCKS PORTF

#define CLOCKS_DDR DDRF

#define DATA_PINS PINK

#define DATA_PORT PORTK

#define DATA_DDR DDRK

// 10mS exposure time.

#define EXPOSURE_TIME 1

// Initial clock state.

uint8_t clocks0 = (RD + CS + CNVST + ICG);

// 16-bit pixel buffer

uint16_t pixBuf[PIXEL_COUNT];

/*

* readLine() Reads all pixels into a buffer.

*/

void readLine()

{

// Get an 8-bit pointer to the 16-bit buffer.

uint8_t * buf = (uint8_t *)pixBuf;

int x;

cli();

CLOCKS |= (RD + CS + CNVST + ICG + BYTESWAP + SH);

OCR2A = 5;

TCNT2 = 0;

PORTB &= ~0x02;

for (x = 0; x < PIXEL_COUNT; ++x)

{

CLOCKS ^= SH;

CLOCKS &= ~CNVST;

CLOCKS |= CNVST;

_delay_loop_1(4);

__asm__("nop\n\t");

__asm__("nop\n\t");

__asm__("nop\n\t");

CLOCKS ^= SH;

CLOCKS &= ~(CS + RD);

*buf++ = DATA_PINS;

CLOCKS &= ~(BYTESWAP);

*buf++ = DATA_PINS;

CLOCKS |= (RD + CS + BYTESWAP);

}

sei();

}

/*

* startLine() Toggles the clocks to shift the line

* into the CCD shift register.

*/

void startLine()

{

// Set ICG low.

CLOCKS &= ~ICG;

_delay_loop_1(5);

// Set SH high.

CLOCKS |= SH;

_delay_loop_1(5); // 10uS.

// Set SH low.

CLOCKS &= ~SH;

_delay_loop_1(10);

// Set ICG high.

CLOCKS |= ICG;

}

/*

* sendLine() Send the line of pixels to the user.

*/

void sendLine()

{

int x;

for (x = 0; x < PIXEL_COUNT; ++x)

{

Serial.println(pixBuf[x]);

}

}

/*

* cleanLine() Performs voodoo on the data.

* Looks for shot noise and replaces it with

* the previous pixel data. You might want to

* skip this if doing serious science. The blip

* might be your data.

*/

void cleanLine()

{

int x, fixes = 0;

for (x = 1; x < PIXEL_COUNT; ++x)

{

if (pixBuf[x] > (int)(pixBuf[x - 1] * 1.1))

{

if (++fixes < 10)

pixBuf[x] = pixBuf[x - 1];

}

else if (pixBuf[x] < (int)(pixBuf[x - 1] * 0.9))

{

if (++fixes < 10)

pixBuf[x] = pixBuf[x - 1];

}

else

{

fixes = 0;

}

}

}

/*

* setup()

* Set the data port to input.

* Set the clock port to output.

* Start timer2 generating the Mclk signal

* Set the pullup on pin 2 for the start switch.

*/

void setup() {

CLOCKS_DDR = 0xff;

CLOCKS = clocks0;

DATA_DDR = 0x0;

Serial.begin(115200);

// Setup timer2 to generate a 1.333MHz frequency on D10

TCCR2A = (0 << COM2A1) | (1 << COM2A0) | (1 << WGM21) | (0 << WGM20);

TCCR2B = (0 << WGM22) | (1 << CS20);

// "5" causes 6 intervals - 0 through 5

OCR2A = 5;

TCNT2 = 0;

// Output Mclk on D2

DDRB |= 0x10;

// For the trigger switch.

pinMode(2, INPUT_PULLUP);

}

/*

* loop()

* Read the CCD continuously.

* Upload to user on switch press.

*/

void loop() {

startLine();

readLine();

delay(EXPOSURE_TIME);

if (!digitalRead(2))

{

cleanLine();

sendLine();

}

}

Discussions