|
|
@@ -1,17 +1,103 @@ |
|
|
|
#include "config.h"
|
|
|
|
#include "adc.h"
|
|
|
|
|
|
|
|
/* The ADC (AD7846) is tricky, as new conversions start
|
|
|
|
automatically when the previous data read finishes.
|
|
|
|
To allow more control over start/read, we stretch the
|
|
|
|
read indefinitely by delaying the final SCK edge.
|
|
|
|
This means we can't have the hardware do SPI for us. */
|
|
|
|
|
|
|
|
/* External serial clock, 2-wire I/O -- tie /EXT low,
|
|
|
|
tie SDI for rate selection, tie /CS low, use BUSY for
|
|
|
|
interrupt notification if desired */
|
|
|
|
|
|
|
|
#define TRIS_SDO TRISEbits.TRISE7
|
|
|
|
#define R_SDO PORTEbits.RE7
|
|
|
|
#define TRIS_SCK TRISEbits.TRISE6
|
|
|
|
#define LAT_SCK LATEbits.LATE6
|
|
|
|
|
|
|
|
/* Short delays */
|
|
|
|
#define wait_200ns() do { \
|
|
|
|
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); \
|
|
|
|
} while(0)
|
|
|
|
#define wait_25ns() nop()
|
|
|
|
|
|
|
|
/* Initialize ADC */
|
|
|
|
void adc_init(void)
|
|
|
|
{
|
|
|
|
// TODO (SPI)
|
|
|
|
int i;
|
|
|
|
|
|
|
|
TRIS_SDO = 1;
|
|
|
|
LAT_SCK = 0;
|
|
|
|
TRIS_SCK = 0;
|
|
|
|
|
|
|
|
/* Startup delay CS down to SCK high t4 = 5000ns */
|
|
|
|
for (i = 0; i < 5000 / 25; i++)
|
|
|
|
wait_25ns();
|
|
|
|
|
|
|
|
/* Trigger a dummy read so we're prepared for the
|
|
|
|
next conversion */
|
|
|
|
(void) adc_read();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the result of the previous
|
|
|
|
conversion and start a new one */
|
|
|
|
/* Start a conversion if it hasn't already been started.
|
|
|
|
Wait for conversion to finish.
|
|
|
|
Read the result. */
|
|
|
|
uint32_t adc_read(void)
|
|
|
|
{
|
|
|
|
// TODO.
|
|
|
|
// can we separate read/start conversion?
|
|
|
|
// unclear
|
|
|
|
} |
|
|
|
uint32_t val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Start conversion by completing previous read */
|
|
|
|
LAT_SCK = 0;
|
|
|
|
|
|
|
|
/* Wait tKQMAX for SCK down to SDO valid */
|
|
|
|
wait_200ns();
|
|
|
|
|
|
|
|
/* Wait for conversion to finish */
|
|
|
|
while (R_SDO == 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Read it out */
|
|
|
|
val = 0;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
/* SCK low tLESCK = 25ns */
|
|
|
|
wait_25ns();
|
|
|
|
LAT_SCK = 1;
|
|
|
|
|
|
|
|
/* SCK high tHESCK = 25ns, but
|
|
|
|
we also have SCK down to SDO valid tKQMAX = 200ns?
|
|
|
|
Probably misspecified but wait tKQMAX anyway. */
|
|
|
|
wait_200ns();
|
|
|
|
|
|
|
|
val <<= 1;
|
|
|
|
if (R_SDO)
|
|
|
|
val |= 1;
|
|
|
|
/* Leave SCK high on final bit to delay new conversion */
|
|
|
|
if (i < 31)
|
|
|
|
LAT_SCK = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Done */
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start a new conversion. If a conversion was already started
|
|
|
|
but the result was not read, this does nothing. */
|
|
|
|
void adc_start_conversion(void)
|
|
|
|
{
|
|
|
|
/* Start conversion by completing previous read */
|
|
|
|
LAT_SCK = 0;
|
|
|
|
|
|
|
|
/* Wait tKQMAX for SCK down to SDO valid in case we
|
|
|
|
call adc_is_conversion_finished right away. */
|
|
|
|
wait_200ns();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return 1 if a conversion is in progress, 0 otherwise */
|
|
|
|
int adc_is_conversion_finished(void)
|
|
|
|
{
|
|
|
|
if (LAT_SCK == 0 && R_SDO == 1)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|