|
- #include "config.h"
- #include "adcext.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 ADC SDI for rate selection, tie /CS low, use BUSY for
- interrupt notification if desired */
-
- #define TRIS_SDO TRISBbits.TRISB5
- #define R_SDO PORTBbits.RB5
- #define TRIS_SCK TRISBbits.TRISB4
- #define LAT_SCK LATBbits.LATB4
- #define TRIS_SCS TRISBbits.TRISB3
- #define LAT_SCS LATBbits.LATB3
-
- /* Short delays */
- #define wait_200ns() do { \
- nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); \
- } while(0)
- #define wait_25ns() nop()
-
- /* Initialize ADC */
- void adcext_init(void)
- {
- int32_t i;
-
- TRIS_SDO = 1;
- LAT_SCK = IO_HIGH;
- TRIS_SCK = 0;
- LAT_SCS = IO_LOW;
- TRIS_SCS = 0;
-
- /* Startup delay CS down to SCK high t4 = 5000ns */
- for (i = 0; i < 5000 / 25; i++)
- wait_25ns();
-
- /* We need to monitor BUSY, I think. With the 2-wire
- interface, we never know if we start in the middle of
- a data output phase. For now, consider reading broken..
- just return early. XXXX */
- return;
-
- /* Trigger a dummy read so we're prepared for the
- next conversion */
- (void) adcext_read();
- }
-
- /* Start a conversion if it hasn't already been started.
- Wait for conversion to finish.
- Read the result and return the raw 32-bit value. */
- uint32_t adcext_read(void)
- {
- uint32_t val;
- int i;
-
- /* Start conversion by completing previous read */
- LAT_SCK = IO_LOW;
-
- /* Wait tKQMAX for SCK down to SDO valid */
- wait_200ns();
-
- /* Wait for conversion to finish */
- while (R_SDO == IO_HIGH)
- continue;
-
- /* Read it out */
- val = 0;
- for (i = 0; i < 32; i++) {
- /* SCK low tLESCK = 25ns */
- wait_25ns();
- LAT_SCK = IO_HIGH;
-
- /* 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 == IO_HIGH)
- val |= 1;
- /* Leave SCK high on final bit to delay new conversion */
- if (i < 31)
- LAT_SCK = IO_LOW;
- }
-
- /* Done */
- return val;
- }
-
- /* Convert a raw 32-bit value into a signed 32-bit result.
- The return value is full int32 range but only the high
- 24 should be significant: low 3 will always be 0,
- and the next 5 will be sub-resolution (see datasheet). */
- int32_t adcext_convert(uint32_t raw)
- {
- int sigmsb = (raw >> 28) & 3;
-
- /* If SIG & MSB, it is a positive overflow */
- if (sigmsb == 3)
- return (int32_t)0x7FFFFFFFL;
- /* If !SIG & !MSB, it is a negative overflow */
- if (sigmsb == 0)
- return (int32_t)0x80000000L;
- /* Shift over EOC,DMY,SIG and return */
- return ((int32_t)(raw << 3));
- }
-
- /* Start a new conversion. If a conversion was already started
- but the result was not read, this does nothing. */
- void adcext_start_conversion(void)
- {
- /* If we had a previous conversion ready to read,
- read it out so we can start a new conversion instead */
- if (adcext_is_conversion_ready())
- (void) adcext_read();
-
- /* Start conversion by completing previous read */
- LAT_SCK = 0;
-
- /* Wait tKQMAX for SCK down to SDO valid in case we
- call adcext_is_conversion_ready right away. */
- wait_200ns();
- }
-
- /* Return 1 if a conversion is finished and ready to be read, 0 otherwise */
- int adcext_is_conversion_ready(void)
- {
- if (LAT_SCK == 0 && R_SDO == 0)
- return 1;
- return 0;
- }
|