Close

Addendum To Log# 62 - Updated C/C++ code for TCD1304 CCD driver circuit

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/27/2017 at 00:130 Comments

#include <util/delay_basic.h>

#define RD (1<<2)

#define CNVST (1<<3)

#define BYTESWAP (1<<4)

#define ICG (1<<5)

#define SH (1<<6)

#define MCLK (1<<7)

// Full frame, including dark pixels

// and dead pixels.

#define PIXEL_COUNT 3694

#define CLOCKS PORTD

#define CLOCKP PIND

#define CLOCKS_DDR DDRD

#define DATA_PINS PINC

#define DATA_PORT PORTC

#define DATA_DDR DDRC

// 1mS exposure time.

#define EXPOSURE_TIME 10

// Initial clock state.

uint8_t clocks0 = (RD + CNVST + ICG);

// 16-bit pixel buffer

uint16_t pixBuf[PIXEL_COUNT];

char cmdBuffer[16];

int cmdIndex;

int exposureTime = EXPOSURE_TIME;

int cmdRecvd = 0;

/*

* 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 = 0;

uint8_t scratch = 0;

// Disable interrupts or the timer will get us.

cli();

// Synchronize with MCLK and

// set ICG low and SH high.

scratch = CLOCKS;

scratch &= ~ICG;

scratch |= SH;

while(!(CLOCKP & MCLK));

while((CLOCKP & MCLK));

TCNT2 = 0;

_delay_loop_1(1);

__asm__("nop");

__asm__("nop");

__asm__("nop");

__asm__("nop");

__asm__("nop");

CLOCKS = scratch;

// Wait the remainder of 4uS @ 20MHz.

_delay_loop_1(22);

__asm__("nop");

__asm__("nop");

// Set SH low.

CLOCKS ^= SH;

// Wait the reaminder of 4uS.

_delay_loop_1(23);

// Start the readout loop at the first pixel.

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

__asm__("nop");

do {

// Wait a minimum of 250nS for acquisition.

_delay_loop_1(2);

// Start the conversion.

CLOCKS &= ~CNVST;

CLOCKS |= CNVST;

// Wait a minimum of 1uS for conversion.

_delay_loop_1(6);

// Read the low byte of the result.

CLOCKS &= ~RD;

_delay_loop_1(3);

__asm__("nop");

__asm__("nop");

*buf++ = DATA_PINS;

// Setup and read the high byte.

CLOCKS &= ~(BYTESWAP);

_delay_loop_1(3);

__asm__("nop");

*buf++ = DATA_PINS;

// Set the clocks back to idle state

CLOCKS |= (RD + BYTESWAP);

// Toggle SH for the next pixel.

CLOCKS ^= SH;

} while (++x < PIXEL_COUNT);

sei();

}

/*

* clearLine() Clears the CCD.

*/

void clearLine() {

int x = 0;

// Set ICG low.

CLOCKS &= ~ICG;

CLOCKS |= SH;

_delay_loop_1(14);

// Set SH low.

CLOCKS ^= SH;

_delay_loop_1(10);

// Reset the timer so the edges line up.

TCNT2 = 0;

CLOCKS |= (RD + CNVST + ICG + BYTESWAP + MCLK);

do {

CLOCKS ^= SH;

_delay_loop_1(10);

} while (++x < PIXEL_COUNT);

}

/*

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

*/

void sendLine() {

uint16_t x;

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

Serial.print(x);

Serial.print(",");

Serial.print(pixBuf[x]);

Serial.print("\n");

}

}

/*

* setup()

* Set the data port to input.

* Set the clock port to output.

* Start timer2 generating the Mclk signal

*/

void setup() {

CLOCKS_DDR = 0xff;

CLOCKS = clocks0;

DATA_DDR = 0x0;

Serial.begin(115200);

// Setup timer2 to generate a 667kHz frequency on D10

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

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

OCR2A = 8;

TCNT2 = 0;

}

/*

* loop()

* Read the CCD continuously.

* Upload to user on switch press.

*/

void loop() {

int x;

char ch;

// If we got a command last time, execute it now.

if (cmdRecvd) {

if (cmdBuffer[0] == 'r') {

// Send the readout to the host.

sendLine();

} else if (cmdBuffer[0] == 'e') {

// Set the exposure time.

sscanf(cmdBuffer + 1, "%d", &exposureTime);

if (exposureTime > 1000) exposureTime = 1000;

if (exposureTime < 0) exposureTime = 0;

}

// Get ready for the next command.

memset(cmdBuffer, 0, sizeof(cmdBuffer));

cmdIndex = 0;

cmdRecvd = 0;

}

// Clear the CCD.

clearLine();

// Integrate.

delay(exposureTime);

// Read it for real.

readLine();

// See if the host is talking to us.

if (Serial.available()) {

ch = Serial.read();

// If char is linefeed, it is end of command.

if (ch == 0x0a) {

cmdBuffer[cmdIndex++] = '\0';

cmdRecvd = 1;

// Otherwise it is a command character.

} else {

cmdBuffer[cmdIndex++] = ch;

cmdRecvd = 0;

}

}

}

Discussions