Browse Source

Fix line endings

tags/zoom-1.0
Jim Paris 9 years ago
parent
commit
431f387b55
16 changed files with 1133 additions and 1133 deletions
  1. +65
    -65
      firmware/adc.c
  2. +16
    -16
      firmware/adc.h
  3. +136
    -136
      firmware/adcext.c
  4. +23
    -23
      firmware/adcext.h
  5. +180
    -180
      firmware/calibrate.c
  6. +42
    -42
      firmware/calibrate.h
  7. +77
    -77
      firmware/dac.c
  8. +60
    -60
      firmware/dac.h
  9. +40
    -40
      firmware/led.c
  10. +27
    -27
      firmware/led.h
  11. +9
    -9
      firmware/mode.h
  12. +167
    -167
      firmware/mode_debug.c
  13. +139
    -139
      firmware/mode_normal.c
  14. +100
    -100
      firmware/timer.c
  15. +46
    -46
      firmware/timer.h
  16. +6
    -6
      firmware/zoom.h

+ 65
- 65
firmware/adc.c View File

@@ -1,65 +1,65 @@
#include "config.h"
#include "adc.h"
#include "timer.h"
/* Currently using software SPI because it's easy.
Consider using hardware SPI with DMA on a timer, though. */
#define TRIS_SDATA TRISFbits.TRISF7
#define R_SDATA PORTFbits.RF7
#define TRIS_SCLK TRISFbits.TRISF6
#define LAT_SCLK LATFbits.LATF6
#define TRIS_CS TRISBbits.TRISB2
#define LAT_CS LATBbits.LATB2
void adc_init(void)
{
TRIS_SDATA = 1;
LAT_CS = IO_HIGH;
TRIS_CS = 0;
LAT_SCLK = IO_HIGH;
TRIS_SCLK = 0;
}
int16_t adc_get(void)
{
uint16_t v = 0;
int i;
LAT_CS = IO_LOW;
for (i = 0; i < 16; i++) {
v <<= 1;
if (R_SDATA == IO_HIGH)
v |= 1;
LAT_SCLK = IO_LOW;
nop(); nop(); nop();
LAT_SCLK = IO_HIGH;
}
LAT_CS = IO_HIGH;
/* Sign-extend the 12-bit value */
if (v & 0x0800)
v |= 0xF000;
else
v &= ~0xF000;
return (int16_t) v;
}
uint16_t adc_get_raw(void)
{
uint16_t v = 0;
int i;
LAT_CS = IO_LOW;
for (i = 0; i < 16; i++) {
v <<= 1;
if (R_SDATA == IO_HIGH)
v |= 1;
LAT_SCLK = IO_LOW;
nop(); nop(); nop();
LAT_SCLK = IO_HIGH;
}
LAT_CS = IO_HIGH;
return (uint16_t) v;
}
#include "config.h"
#include "adc.h"
#include "timer.h"
/* Currently using software SPI because it's easy.
Consider using hardware SPI with DMA on a timer, though. */
#define TRIS_SDATA TRISFbits.TRISF7
#define R_SDATA PORTFbits.RF7
#define TRIS_SCLK TRISFbits.TRISF6
#define LAT_SCLK LATFbits.LATF6
#define TRIS_CS TRISBbits.TRISB2
#define LAT_CS LATBbits.LATB2
void adc_init(void)
{
TRIS_SDATA = 1;
LAT_CS = IO_HIGH;
TRIS_CS = 0;
LAT_SCLK = IO_HIGH;
TRIS_SCLK = 0;
}
int16_t adc_get(void)
{
uint16_t v = 0;
int i;
LAT_CS = IO_LOW;
for (i = 0; i < 16; i++) {
v <<= 1;
if (R_SDATA == IO_HIGH)
v |= 1;
LAT_SCLK = IO_LOW;
nop(); nop(); nop();
LAT_SCLK = IO_HIGH;
}
LAT_CS = IO_HIGH;
/* Sign-extend the 12-bit value */
if (v & 0x0800)
v |= 0xF000;
else
v &= ~0xF000;
return (int16_t) v;
}
uint16_t adc_get_raw(void)
{
uint16_t v = 0;
int i;
LAT_CS = IO_LOW;
for (i = 0; i < 16; i++) {
v <<= 1;
if (R_SDATA == IO_HIGH)
v |= 1;
LAT_SCLK = IO_LOW;
nop(); nop(); nop();
LAT_SCLK = IO_HIGH;
}
LAT_CS = IO_HIGH;
return (uint16_t) v;
}

+ 16
- 16
firmware/adc.h View File

@@ -1,16 +1,16 @@
#ifndef ADC_H
#define ADC_H
#include "config.h"
/* Initialize external 12-bit ADC (AD7450) */
void adc_init(void);
/* Trigger conversion and return it.
Result is a signed value (-2048 to +2047) */
int16_t adc_get(void);
/* Trigger conversion and get raw 16-bit value from ADC */
uint16_t adc_get_raw(void);
#endif
#ifndef ADC_H
#define ADC_H
#include "config.h"
/* Initialize external 12-bit ADC (AD7450) */
void adc_init(void);
/* Trigger conversion and return it.
Result is a signed value (-2048 to +2047) */
int16_t adc_get(void);
/* Trigger conversion and get raw 16-bit value from ADC */
uint16_t adc_get_raw(void);
#endif

+ 136
- 136
firmware/adcext.c View File

@@ -1,136 +1,136 @@
#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;
}
#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;
}

+ 23
- 23
firmware/adcext.h View File

@@ -1,23 +1,23 @@
#ifndef ADCEXT_H
#define ADCEXT_H
/* Initialize ADC */
void adcext_init(void);
/* 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);
/* Convert a raw 32-bit value into a signed result.
The return value range is -(2^31) to (2^31)-1 */
int32_t adcext_convert(uint32_t raw);
/* Start a new conversion. If a conversion was already started
but the result was not read, this does nothing. */
void adcext_start_conversion(void);
/* Return 1 if a conversion is in progress, 0 otherwise */
int adcext_is_conversion_ready(void);
#endif
#ifndef ADCEXT_H
#define ADCEXT_H
/* Initialize ADC */
void adcext_init(void);
/* 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);
/* Convert a raw 32-bit value into a signed result.
The return value range is -(2^31) to (2^31)-1 */
int32_t adcext_convert(uint32_t raw);
/* Start a new conversion. If a conversion was already started
but the result was not read, this does nothing. */
void adcext_start_conversion(void);
/* Return 1 if a conversion is in progress, 0 otherwise */
int adcext_is_conversion_ready(void);
#endif

+ 180
- 180
firmware/calibrate.c View File

@@ -1,180 +1,180 @@
#include "config.h"
#include "calibrate.h"
#include "util.h"
#include "adc.h"
#include "dac.h"
#include "led.h"
#include "timer.h"
#include "uart.h"
//#define DEBUG_CALIBRATION
float g_scale; /* delta(DAC) / delta(ADC) */
/* Initialize. Assume some relatively-safe scaling if no calibration is run */
void calibrate_init(void)
{
g_scale = (DAC_RANGE / 65536.0); /* 1 count at 16-bit DAC means 1 counts at ADC */
}
/* Given the current DAC and ADC values d1 and a1,
compute a new DAC value d2 to give the desired ADC value a2 */
uint16_t adc_to_dac(uint16_t d1, int16_t a1, int16_t a2, float scale)
{
int32_t delta;
int32_t d2;
delta = (int32_t)((a2 - a1) * scale + 0.5);
d2 = d1 + delta;
return clamp(DAC_MIN, d2, DAC_MAX);
}
/* Calculate a new scale factor given two DAC and ADC points */
float calculate_scale(uint16_t d1, float a1, uint16_t d2, float a2)
{
float scale;
float a = a2 - a1;
/* Correct for known errors */
d1 = dac_get_actual_float(d1);
d2 = dac_get_actual_float(d2);
if (a < 0.1 && a > -0.1)
scale = 1.0;
else {
scale = ((float)d2 - d1) / a;
if (scale < 0.01)
scale = 0.01;
if (scale > 20)
scale = 20;
}
return scale;
}
/* Seek with the DAC to reach a specific ADC value. Uses g_scale as
an initial guess for scaling factor, but adjusts it dynamically. */
uint16_t seek(uint16_t starting_dac, int16_t desired_adc)
{
uint16_t old_dac, dac;
int16_t old_adc, adc;
float scale = g_scale;
int steps = 0;
dac = starting_dac;
/* goto current location */
dac_write(dac);
msleep(1);
adc = adc_get();
while (1)
{
/* give up if we're not making progress */
if (steps++ > SEEK_MAX_STEPS) {
// 2 flashes, delay, repeat
led_pattern(0b00101000);
break;
}
old_dac = dac;
old_adc = adc;
/* jump to the desired value */
dac = adc_to_dac(old_dac, old_adc, desired_adc, scale);
/* write it out */
dac_write(dac);
msleep(1);
adc = adc_get();
#ifdef DEBUG_CALIBRATION
uart1_put_hex16(dac);
uart1_put(' ');
uart1_put_hex16(adc);
uart1_put(' ');
uart1_put_hex16(desired_adc);
uart1_put(' ');
uart1_put_hex32(*(uint32_t *)&scale);
uart1_crlf();
#endif
/* if we're close, accept it */
if (abs(adc - desired_adc) <= SEEK_FUZZ_ADC) {
led_pattern(0b11111110);
break;
}
/* otherwise, if we were within ADC clamp limits, and
the DAC changed a non-trivial amount, readjust
scale factor */
if (adc > ADC_CLAMP_MIN && old_adc > ADC_CLAMP_MIN &&
adc < ADC_CLAMP_MAX && old_adc < ADC_CLAMP_MAX &&
abs((int32_t)dac - old_dac) >= SEEK_FUZZ_DAC) {
scale = calculate_scale(old_dac, old_adc, dac, adc);
}
/* if we totally overshot the window, cut the scale in half */
if ((adc < ADC_CLAMP_MIN && old_adc > ADC_CLAMP_MAX) ||
(adc > ADC_CLAMP_MAX && old_adc < ADC_CLAMP_MIN))
{
scale *= 0.5;
}
}
return dac;
}
/* Perform calibration */
uint16_t do_calibrate(void)
{
uint16_t daczero, x1, x2;
float y1, y2;
/* Zero ADC */
daczero = seek((DAC_MIN + DAC_MAX) / 2, CALIBRATE_ADC_ZERO);
/* Go down take an accurate sample */
x1 = seek(daczero, CALIBRATE_ADC_LOW);
y1 = oversample(x1);
/* Go up and take an accurate sample */
x2 = seek(daczero, CALIBRATE_ADC_HIGH);
y2 = oversample(x2);
/* Calculate scale */
g_scale = calculate_scale(x1, y1, x2, y2);
#ifdef DEBUG_CALIBRATION
uart1_put_string("calibrate x1=");
uart1_put_dec(x1);
uart1_put_string(" y1=");
uart1_put_float(y1);
uart1_put_string(" x2=");
uart1_put_dec(x2);
uart1_put_string(" y2=");
uart1_put_float(y2);
uart1_put_string(" scale=");
uart1_put_float(g_scale);
uart1_crlf();
#endif
/* Return to zero position */
dac_write(daczero);
return daczero;
}
/* Oversample to get a nice ADC value */
float oversample(uint16_t dac)
{
int i;
int32_t sum = 0;
for (i = 0; i < OVERSAMPLE_COUNT; i++) {
dac_write(dac);
msleep(1);
sum += adc_get();
}
return (float)sum / (float)OVERSAMPLE_COUNT;
}
#include "config.h"
#include "calibrate.h"
#include "util.h"
#include "adc.h"
#include "dac.h"
#include "led.h"
#include "timer.h"
#include "uart.h"
//#define DEBUG_CALIBRATION
float g_scale; /* delta(DAC) / delta(ADC) */
/* Initialize. Assume some relatively-safe scaling if no calibration is run */
void calibrate_init(void)
{
g_scale = (DAC_RANGE / 65536.0); /* 1 count at 16-bit DAC means 1 counts at ADC */
}
/* Given the current DAC and ADC values d1 and a1,
compute a new DAC value d2 to give the desired ADC value a2 */
uint16_t adc_to_dac(uint16_t d1, int16_t a1, int16_t a2, float scale)
{
int32_t delta;
int32_t d2;
delta = (int32_t)((a2 - a1) * scale + 0.5);
d2 = d1 + delta;
return clamp(DAC_MIN, d2, DAC_MAX);
}
/* Calculate a new scale factor given two DAC and ADC points */
float calculate_scale(uint16_t d1, float a1, uint16_t d2, float a2)
{
float scale;
float a = a2 - a1;
/* Correct for known errors */
d1 = dac_get_actual_float(d1);
d2 = dac_get_actual_float(d2);
if (a < 0.1 && a > -0.1)
scale = 1.0;
else {
scale = ((float)d2 - d1) / a;
if (scale < 0.01)
scale = 0.01;
if (scale > 20)
scale = 20;
}
return scale;
}
/* Seek with the DAC to reach a specific ADC value. Uses g_scale as
an initial guess for scaling factor, but adjusts it dynamically. */
uint16_t seek(uint16_t starting_dac, int16_t desired_adc)
{
uint16_t old_dac, dac;
int16_t old_adc, adc;
float scale = g_scale;
int steps = 0;
dac = starting_dac;
/* goto current location */
dac_write(dac);
msleep(1);
adc = adc_get();
while (1)
{
/* give up if we're not making progress */
if (steps++ > SEEK_MAX_STEPS) {
// 2 flashes, delay, repeat
led_pattern(0b00101000);
break;
}
old_dac = dac;
old_adc = adc;
/* jump to the desired value */
dac = adc_to_dac(old_dac, old_adc, desired_adc, scale);
/* write it out */
dac_write(dac);
msleep(1);
adc = adc_get();
#ifdef DEBUG_CALIBRATION
uart1_put_hex16(dac);
uart1_put(' ');
uart1_put_hex16(adc);
uart1_put(' ');
uart1_put_hex16(desired_adc);
uart1_put(' ');
uart1_put_hex32(*(uint32_t *)&scale);
uart1_crlf();
#endif
/* if we're close, accept it */
if (abs(adc - desired_adc) <= SEEK_FUZZ_ADC) {
led_pattern(0b11111110);
break;
}
/* otherwise, if we were within ADC clamp limits, and
the DAC changed a non-trivial amount, readjust
scale factor */
if (adc > ADC_CLAMP_MIN && old_adc > ADC_CLAMP_MIN &&
adc < ADC_CLAMP_MAX && old_adc < ADC_CLAMP_MAX &&
abs((int32_t)dac - old_dac) >= SEEK_FUZZ_DAC) {
scale = calculate_scale(old_dac, old_adc, dac, adc);
}
/* if we totally overshot the window, cut the scale in half */
if ((adc < ADC_CLAMP_MIN && old_adc > ADC_CLAMP_MAX) ||
(adc > ADC_CLAMP_MAX && old_adc < ADC_CLAMP_MIN))
{
scale *= 0.5;
}
}
return dac;
}
/* Perform calibration */
uint16_t do_calibrate(void)
{
uint16_t daczero, x1, x2;
float y1, y2;
/* Zero ADC */
daczero = seek((DAC_MIN + DAC_MAX) / 2, CALIBRATE_ADC_ZERO);
/* Go down take an accurate sample */
x1 = seek(daczero, CALIBRATE_ADC_LOW);
y1 = oversample(x1);
/* Go up and take an accurate sample */
x2 = seek(daczero, CALIBRATE_ADC_HIGH);
y2 = oversample(x2);
/* Calculate scale */
g_scale = calculate_scale(x1, y1, x2, y2);
#ifdef DEBUG_CALIBRATION
uart1_put_string("calibrate x1=");
uart1_put_dec(x1);
uart1_put_string(" y1=");
uart1_put_float(y1);
uart1_put_string(" x2=");
uart1_put_dec(x2);
uart1_put_string(" y2=");
uart1_put_float(y2);
uart1_put_string(" scale=");
uart1_put_float(g_scale);
uart1_crlf();
#endif
/* Return to zero position */
dac_write(daczero);
return daczero;
}
/* Oversample to get a nice ADC value */
float oversample(uint16_t dac)
{
int i;
int32_t sum = 0;
for (i = 0; i < OVERSAMPLE_COUNT; i++) {
dac_write(dac);
msleep(1);
sum += adc_get();
}
return (float)sum / (float)OVERSAMPLE_COUNT;
}

+ 42
- 42
firmware/calibrate.h View File

@@ -1,42 +1,42 @@
#ifndef CALIBRATE_H
#define CALIBRATE_H
#define ADC_CLAMP_MIN 256
#define ADC_CLAMP_MAX 1792
#define OVERSAMPLE_COUNT 256
#define DAC_MIN DAC_LOW
#define DAC_MAX DAC_HIGH
#define SEEK_MAX_STEPS 1000
#define SEEK_FUZZ_ADC 100
#define SEEK_FUZZ_DAC (1+((DAC_RANGE * 5) / 65536))
#define CALIBRATE_ADC_ZERO 1024
#define CALIBRATE_ADC_LOW 512
#define CALIBRATE_ADC_HIGH 1536
/* Initialize. Assume some relatively-safe scaling if no calibration is run */
void calibrate_init(void);
/* Given the current DAC and ADC values d1 and a1,
compute a new DAC value d2 to give the desired ADC value a2 */
uint16_t adc_to_dac(uint16_t d1, int16_t a1, int16_t a2, float scale);
/* Calculate a new scale factor given two DAC and ADC points */
float calculate_scale(uint16_t d1, float a1, uint16_t d2, float a2);
/* Seek with the DAC to reach a specific ADC value,
using current scaling as a starting point. */
uint16_t seek(uint16_t starting_dac, int16_t desired_adc);
/* Perform calibration */
uint16_t do_calibrate(void);
/* Oversample to get a nice ADC value */
float oversample(uint16_t dac);
extern float g_scale; /* delta(DAC) / delta(ADC) */
#endif
#ifndef CALIBRATE_H
#define CALIBRATE_H
#define ADC_CLAMP_MIN 256
#define ADC_CLAMP_MAX 1792
#define OVERSAMPLE_COUNT 256
#define DAC_MIN DAC_LOW
#define DAC_MAX DAC_HIGH
#define SEEK_MAX_STEPS 1000
#define SEEK_FUZZ_ADC 100
#define SEEK_FUZZ_DAC (1+((DAC_RANGE * 5) / 65536))
#define CALIBRATE_ADC_ZERO 1024
#define CALIBRATE_ADC_LOW 512
#define CALIBRATE_ADC_HIGH 1536
/* Initialize. Assume some relatively-safe scaling if no calibration is run */
void calibrate_init(void);
/* Given the current DAC and ADC values d1 and a1,
compute a new DAC value d2 to give the desired ADC value a2 */
uint16_t adc_to_dac(uint16_t d1, int16_t a1, int16_t a2, float scale);
/* Calculate a new scale factor given two DAC and ADC points */
float calculate_scale(uint16_t d1, float a1, uint16_t d2, float a2);
/* Seek with the DAC to reach a specific ADC value,
using current scaling as a starting point. */
uint16_t seek(uint16_t starting_dac, int16_t desired_adc);
/* Perform calibration */
uint16_t do_calibrate(void);
/* Oversample to get a nice ADC value */
float oversample(uint16_t dac);
extern float g_scale; /* delta(DAC) / delta(ADC) */
#endif

+ 77
- 77
firmware/dac.c View File

@@ -1,77 +1,77 @@
#include "config.h"
#include "dac.h"
/* Initialize DAC (AD5542) */
void dac_init(void)
{
/* SPI2 */
IEC2bits.SPI2IE = 0;
SPI2CON1bits.DISSCK = 0;
SPI2CON1bits.DISSDO = 0;
SPI2CON1bits.MODE16 = 1;
SPI2CON1bits.SMP = 0;
SPI2CON1bits.CKE = 0;
SPI2CON1bits.SSEN = 0;
SPI2CON1bits.CKP = IO_HIGH;
SPI2CON1bits.MSTEN = 1;
SPI2CON1bits.SPRE = 4;
SPI2CON1bits.PPRE = 3;
/* There's no framed mode that does the normal
"chip select" behavior, so control /SS2 manually */
SPI2CON2bits.FRMEN = 0;
LATGbits.LATG9 = IO_HIGH;
TRISGbits.TRISG9 = 0;
SPI2STATbits.SPISIDL = 0;
SPI2STATbits.SPIEN = 1;
dac_write(0x0000);
}
static const uint16_t dac_lookup[1024] = {
#include "lookup.inc"
};
/* Write raw 16-bit desired value to DAC:
DAC_HIGH 4.9998v
DAC_MID 0v
DAC_LOW -5v
*/
void dac_write(uint16_t val)
{
LATGbits.LATG9 = IO_LOW;
if (IO_HIGH == 1)
SPI2BUF = __dac_to_spi_cmd(val);
else
SPI2BUF = ~__dac_to_spi_cmd(val);
while (!SPI2STATbits.SPIRBF)
continue;
(void) SPI2BUF;
LATGbits.LATG9 = IO_HIGH;
}
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a 16-bit value, where:
0xffff = 4.9998v
0x8000 = 0v
0x0000 = -5v
*/
uint16_t dac_get_actual_16bit(uint16_t val)
{
return __dac_to_16bit_equiv(val);
}
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a FLOAT, where:
DAC_HIGH = 4.9998v
DAC_MID = 0v
DAC_LOW = -5v
Example: for 10-bit DAC, convert integer command 123 into
the more accurate 123.45 using known lower bits.
*/
float dac_get_actual_float(uint16_t val)
{
return __dac_to_16bit_equiv(val) * (DAC_RANGE / 65536.0);
}
#include "config.h"
#include "dac.h"
/* Initialize DAC (AD5542) */
void dac_init(void)
{
/* SPI2 */
IEC2bits.SPI2IE = 0;
SPI2CON1bits.DISSCK = 0;
SPI2CON1bits.DISSDO = 0;
SPI2CON1bits.MODE16 = 1;
SPI2CON1bits.SMP = 0;
SPI2CON1bits.CKE = 0;
SPI2CON1bits.SSEN = 0;
SPI2CON1bits.CKP = IO_HIGH;
SPI2CON1bits.MSTEN = 1;
SPI2CON1bits.SPRE = 4;
SPI2CON1bits.PPRE = 3;
/* There's no framed mode that does the normal
"chip select" behavior, so control /SS2 manually */
SPI2CON2bits.FRMEN = 0;
LATGbits.LATG9 = IO_HIGH;
TRISGbits.TRISG9 = 0;
SPI2STATbits.SPISIDL = 0;
SPI2STATbits.SPIEN = 1;
dac_write(0x0000);
}
static const uint16_t dac_lookup[1024] = {
#include "lookup.inc"
};
/* Write raw 16-bit desired value to DAC:
DAC_HIGH 4.9998v
DAC_MID 0v
DAC_LOW -5v
*/
void dac_write(uint16_t val)
{
LATGbits.LATG9 = IO_LOW;
if (IO_HIGH == 1)
SPI2BUF = __dac_to_spi_cmd(val);
else
SPI2BUF = ~__dac_to_spi_cmd(val);
while (!SPI2STATbits.SPIRBF)
continue;
(void) SPI2BUF;
LATGbits.LATG9 = IO_HIGH;
}
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a 16-bit value, where:
0xffff = 4.9998v
0x8000 = 0v
0x0000 = -5v
*/
uint16_t dac_get_actual_16bit(uint16_t val)
{
return __dac_to_16bit_equiv(val);
}
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a FLOAT, where:
DAC_HIGH = 4.9998v
DAC_MID = 0v
DAC_LOW = -5v
Example: for 10-bit DAC, convert integer command 123 into
the more accurate 123.45 using known lower bits.
*/
float dac_get_actual_float(uint16_t val)
{
return __dac_to_16bit_equiv(val) * (DAC_RANGE / 65536.0);
}

+ 60
- 60
firmware/dac.h View File

@@ -1,60 +1,60 @@
#ifndef DAC_H
#define DAC_H
#include "config.h"
#define DAC_TYPE 2
#if DAC_TYPE == 0
/* AD5542, normal 16-bit range */
#define DAC_BITS 16
#define __dac_to_spi_cmd(x) (x)
#define __dac_to_16bit_equiv(x) (x)
#elif DAC_TYPE == 1
/* AD5542, fake 10-bit range using random lower bits */
#define DAC_BITS 10
#define __dac_to_spi_cmd(x) (dac_lookup[(x)&1023])
#define __dac_to_16bit_equiv(x) (dac_lookup[(x)&1023])
#elif DAC_TYPE == 2
/* MAX504, true 10-bit DAC */
#define DAC_BITS 10
#define __dac_to_spi_cmd(x) ((x) << 2)
#define __dac_to_16bit_equiv(x) ((x) << 6)
#else
#error Unknown DAC type
#endif
#define DAC_LOW 0
#define DAC_HIGH ((uint16_t)(((uint32_t)1 << DAC_BITS) - 1))
#define DAC_MID ((uint16_t)((DAC_LOW + DAC_HIGH + (uint32_t)1) / 2))
#define DAC_RANGE (DAC_HIGH - DAC_LOW + 1)
/* Initialize DAC (AD5542) */
void dac_init(void);
/* Write raw value to DAC:
DAC_HIGH 4.9998v
DAC_MID 0v
DAC_LOW -5v
*/
void dac_write(uint16_t val);
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a 16-bit value, where:
0xffff = 4.9998v
0x8000 = 0v
0x0000 = -5v
*/
uint16_t dac_get_actual_16bit(uint16_t val);
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a FLOAT, where:
DAC_HIGH = 4.9998v
DAC_MID = 0v
DAC_LOW = -5v
Example: for 10-bit DAC, convert integer command 123 into
the more accurate 123.45 using known lower bits.
*/
float dac_get_actual_float(uint16_t val);
#endif
#ifndef DAC_H
#define DAC_H
#include "config.h"
#define DAC_TYPE 2
#if DAC_TYPE == 0
/* AD5542, normal 16-bit range */
#define DAC_BITS 16
#define __dac_to_spi_cmd(x) (x)
#define __dac_to_16bit_equiv(x) (x)
#elif DAC_TYPE == 1
/* AD5542, fake 10-bit range using random lower bits */
#define DAC_BITS 10
#define __dac_to_spi_cmd(x) (dac_lookup[(x)&1023])
#define __dac_to_16bit_equiv(x) (dac_lookup[(x)&1023])
#elif DAC_TYPE == 2
/* MAX504, true 10-bit DAC */
#define DAC_BITS 10
#define __dac_to_spi_cmd(x) ((x) << 2)
#define __dac_to_16bit_equiv(x) ((x) << 6)
#else
#error Unknown DAC type
#endif
#define DAC_LOW 0
#define DAC_HIGH ((uint16_t)(((uint32_t)1 << DAC_BITS) - 1))
#define DAC_MID ((uint16_t)((DAC_LOW + DAC_HIGH + (uint32_t)1) / 2))
#define DAC_RANGE (DAC_HIGH - DAC_LOW + 1)
/* Initialize DAC (AD5542) */
void dac_init(void);
/* Write raw value to DAC:
DAC_HIGH 4.9998v
DAC_MID 0v
DAC_LOW -5v
*/
void dac_write(uint16_t val);
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a 16-bit value, where:
0xffff = 4.9998v
0x8000 = 0v
0x0000 = -5v
*/
uint16_t dac_get_actual_16bit(uint16_t val);
/* Given a DAC command between DAC_LOW and DAC_HIGH,
get the actual expected output voltage as a FLOAT, where:
DAC_HIGH = 4.9998v
DAC_MID = 0v
DAC_LOW = -5v
Example: for 10-bit DAC, convert integer command 123 into
the more accurate 123.45 using known lower bits.
*/
float dac_get_actual_float(uint16_t val);
#endif

+ 40
- 40
firmware/led.c View File

@@ -1,40 +1,40 @@
#include "config.h"
#include "led.h"
#include "timer.h"
int16_t __led_pattern;
/* Debug LED */
void TISR_HANDLER(6)
{
timer_clear_txif(6);
if (__led_pattern == -1)
return;
__led_pattern <<= 1;
if (__led_pattern & 0x100) {
PORTBbits.RB13 = 0; /* on */
__led_pattern |= 1;
} else {
PORTBbits.RB13 = 1; /* off */
}
__led_pattern &= 0xff;
}
void led_init(void)
{
TRISBbits.TRISB13 = 0;
PORTBbits.RB13 = 1;
__led_pattern = -1;
timer_setup_16bit(6, LED_BLINK_RATE, 1);
timer_set_priority(6, 1); /* low priority */
}
/* Set a pattern (8-bit binary pattern). */
void led_pattern(uint8_t pattern)
{
__led_pattern = pattern;
}
#include "config.h"
#include "led.h"
#include "timer.h"
int16_t __led_pattern;
/* Debug LED */
void TISR_HANDLER(6)
{
timer_clear_txif(6);
if (__led_pattern == -1)
return;
__led_pattern <<= 1;
if (__led_pattern & 0x100) {
PORTBbits.RB13 = 0; /* on */
__led_pattern |= 1;
} else {
PORTBbits.RB13 = 1; /* off */
}
__led_pattern &= 0xff;
}
void led_init(void)
{
TRISBbits.TRISB13 = 0;
PORTBbits.RB13 = 1;
__led_pattern = -1;
timer_setup_16bit(6, LED_BLINK_RATE, 1);
timer_set_priority(6, 1); /* low priority */
}
/* Set a pattern (8-bit binary pattern). */
void led_pattern(uint8_t pattern)
{
__led_pattern = pattern;
}

+ 27
- 27
firmware/led.h View File

@@ -1,27 +1,27 @@
#ifndef LED_H
#define LED_H
#include "config.h"
#define LED_BLINK_RATE 8
extern int16_t __led_pattern;
/* Initialize LED */
void led_init(void);
/* Set a pattern (8-bit binary pattern). */
void led_pattern(uint8_t pattern);
static inline void led_on(void)
{
__led_pattern = -1;
PORTBbits.RB13 = 0;
}
static inline void led_off(void)
{
__led_pattern = -1;
PORTBbits.RB13 = 1;
}
#endif
#ifndef LED_H
#define LED_H
#include "config.h"
#define LED_BLINK_RATE 8
extern int16_t __led_pattern;
/* Initialize LED */
void led_init(void);
/* Set a pattern (8-bit binary pattern). */
void led_pattern(uint8_t pattern);
static inline void led_on(void)
{
__led_pattern = -1;
PORTBbits.RB13 = 0;
}
static inline void led_off(void)
{
__led_pattern = -1;
PORTBbits.RB13 = 1;
}
#endif

+ 9
- 9
firmware/mode.h View File

@@ -1,9 +1,9 @@
#ifndef MODE_H
#define MODE_H
#include "config.h"
void run_normal(void);
void run_debug(void);
#endif
#ifndef MODE_H
#define MODE_H
#include "config.h"
void run_normal(void);
void run_debug(void);
#endif

+ 167
- 167
firmware/mode_debug.c View File

@@ -1,167 +1,167 @@
#include "config.h"
#include "adc.h"
#include "dac.h"
#include "uart.h"
#include "timer.h"
#include <stdio.h>
#include <math.h>
#include "calibrate.h"
#include "util.h"
#include "led.h"
#include "mode.h"
#include "zoom.h"
static uint16_t dac = DAC_MID;
void sweep(void)
{
int32_t d;
int16_t a;
#define SWEEP ((DAC_HIGH - DAC_LOW) * (int32_t)2000 / 65535)
/* sweep range */
for (d = (int32_t)dac - SWEEP; d < (int32_t)dac + SWEEP; d++) {
if (d < DAC_LOW) {
uart1_put_dec(DAC_LOW);
uart1_put_string(" 0\r\n");
continue;
}
if (d > DAC_HIGH) {
uart1_put_dec(DAC_HIGH);
uart1_put_string(" 0\r\n");
continue;
}
dac_write(d);
msleep(1);
a = adc_get();
uart1_put_dec(d);
uart1_put(' ');
uart1_put_dec(a);
uart1_crlf();
}
}
void run_debug(void)
{
int16_t adc;
int32_t v;
char buf[4];
uart1_init(115200);
led_pattern(0b10101010);
uart1_put_string("Zoom NILM Debug\r\n");
while (1) {
dac = dac & DAC_HIGH; // mask off invalid bits
dac_write(dac);
uart1_put_hex16(dac);
uart1_put(' ');
uart1_put_dec(dac_get_actual_16bit(dac));
uart1_put(' ');
uart1_put(' ');
adc = adc_get();
uart1_put_hex16(adc);
uart1_put(' ');
uart1_put_dec(adc);
uart1_crlf();
switch (uart1_get()) {
// small step
case '[':
dac--;
break;
case ']':
dac++;
break;
// medium step
case '-':
dac -= 16;
break;
case '+':
case '=':
dac += 16;
break;
// big step
case ',':
case '<':
dac -= 1024;
break;
case '.':
case '>':
dac += 1024;
break;
// set DAC to midpoint
case '0':
dac = DAC_MID;
break;
// set DAC to specified hex value
case 'v':
case 'V':
buf[0] = uart1_get();
buf[1] = uart1_get();
buf[2] = uart1_get();
buf[3] = uart1_get();
v = hex_to_u16(buf);
if (v < 0)
uart1_put_string("bad value\r\n");
else
dac = v;
break;
// maintain ADC input at zero
case 'z':
case 'Z':
uart1_put_string("zeroing input...\r\n");
while (!uart1_can_get())
dac = seek(dac, 1024);
uart1_get();
break;
// test seeking
case '1':
uart1_put_string("seek 512\r\n");
dac = seek(dac, 512);
break;
case '2':
uart1_put_string("seek 1536\r\n");
dac = seek(dac, 1536);
break;
// run calibration
case 'c':
case 'C':
uart1_put_string("calibrating...\r\n");
dac = do_calibrate();
uart1_put_string("new g_scale ");
uart1_put_float(g_scale);
uart1_crlf();
break;
// sweep DAC
case 's':
case 'S':
uart1_put_string("sweep around ");
uart1_put_dec(dac);
uart1_crlf();
sweep();
break;
// dump raw ADC value
case 'r':
case 'R':
while(!uart1_can_get()) {
uart1_put_hex16(adc_get_raw());
uart1_crlf();
}
uart1_get();
break;
}
}
}
#include "config.h"
#include "adc.h"
#include "dac.h"
#include "uart.h"
#include "timer.h"
#include <stdio.h>
#include <math.h>
#include "calibrate.h"
#include "util.h"
#include "led.h"
#include "mode.h"
#include "zoom.h"
static uint16_t dac = DAC_MID;
void sweep(void)
{
int32_t d;
int16_t a;
#define SWEEP ((DAC_HIGH - DAC_LOW) * (int32_t)2000 / 65535)
/* sweep range */
for (d = (int32_t)dac - SWEEP; d < (int32_t)dac + SWEEP; d++) {
if (d < DAC_LOW) {
uart1_put_dec(DAC_LOW);
uart1_put_string(" 0\r\n");
continue;
}
if (d > DAC_HIGH) {
uart1_put_dec(DAC_HIGH);
uart1_put_string(" 0\r\n");
continue;
}
dac_write(d);
msleep(1);
a = adc_get();
uart1_put_dec(d);
uart1_put(' ');
uart1_put_dec(a);
uart1_crlf();
}
}
void run_debug(void)
{
int16_t adc;
int32_t v;
char buf[4];
uart1_init(115200);
led_pattern(0b10101010);
uart1_put_string("Zoom NILM Debug\r\n");
while (1) {
dac = dac & DAC_HIGH; // mask off invalid bits
dac_write(dac);
uart1_put_hex16(dac);
uart1_put(' ');
uart1_put_dec(dac_get_actual_16bit(dac));
uart1_put(' ');
uart1_put(' ');
adc = adc_get();
uart1_put_hex16(adc);
uart1_put(' ');
uart1_put_dec(adc);
uart1_crlf();
switch (uart1_get()) {
// small step
case '[':
dac--;
break;
case ']':
dac++;
break;
// medium step
case '-':
dac -= 16;
break;
case '+':
case '=':
dac += 16;
break;
// big step
case ',':
case '<':
dac -= 1024;
break;
case '.':
case '>':
dac += 1024;
break;
// set DAC to midpoint
case '0':
dac = DAC_MID;
break;
// set DAC to specified hex value
case 'v':
case 'V':
buf[0] = uart1_get();
buf[1] = uart1_get();
buf[2] = uart1_get();
buf[3] = uart1_get();
v = hex_to_u16(buf);
if (v < 0)
uart1_put_string("bad value\r\n");
else
dac = v;
break;
// maintain ADC input at zero
case 'z':
case 'Z':
uart1_put_string("zeroing input...\r\n");
while (!uart1_can_get())
dac = seek(dac, 1024);
uart1_get();
break;
// test seeking
case '1':
uart1_put_string("seek 512\r\n");
dac = seek(dac, 512);
break;
case '2':
uart1_put_string("seek 1536\r\n");
dac = seek(dac, 1536);
break;
// run calibration
case 'c':
case 'C':
uart1_put_string("calibrating...\r\n");
dac = do_calibrate();
uart1_put_string("new g_scale ");
uart1_put_float(g_scale);
uart1_crlf();
break;
// sweep DAC
case 's':
case 'S':
uart1_put_string("sweep around ");
uart1_put_dec(dac);
uart1_crlf();
sweep();
break;
// dump raw ADC value
case 'r':
case 'R':
while(!uart1_can_get()) {
uart1_put_hex16(adc_get_raw());
uart1_crlf();
}
uart1_get();
break;
}
}
}

+ 139
- 139
firmware/mode_normal.c View File

@@ -1,139 +1,139 @@
#include "config.h"
#include "adc.h"
#include "adcext.h"
#include "dac.h"
#include "uart.h"
#include "timer.h"
#include <stdio.h>
#include <math.h>
#include "calibrate.h"
#include "util.h"
#include "mode.h"
#include "led.h"
#include "zoom.h"
#include "packet.h"
int send_data = 0;
uint16_t send_adc;
uint16_t send_dac;
int possibly_clamped = 0;
uint16_t dac_cmd;
#define TIMER_RATE 8000 /* how often to read the ADC and update DAC */
#define PC_RATE 8000 /* how often to send data to the PC */
#define DEBUG_ISR_TIME
#define ADC_WINDOW_MIN 512
#define ADC_WINDOW_MIN_STEPTO 1280
#define ADC_WINDOW_MAX 1536
#define ADC_WINDOW_MAX_STEPTO 768
/* Run mode */
void TISR_HANDLER(5)
{
static int count = 0;
int16_t v;
#ifdef DEBUG_ISR_TIME
LATAbits.LATA9 = 1;
#endif
timer_clear_txif(5);
/* Get most recent sample from 12-bit ADC. */
v = adc_get();
if (v < ADC_CLAMP_MIN || v >= ADC_CLAMP_MAX)
possibly_clamped = 1;
/* Send data to PC */
if (++count >= (TIMER_RATE / PC_RATE)) {
count = 0;
/* Send most recent sample and old DAC value */
send_adc = v;
send_dac = dac_cmd;
send_data = 1;
}
#define WINDOW
#ifdef WINDOW
/* If ADC value is outside the window, step DAC */
if (v < ADC_WINDOW_MIN)
dac_cmd = adc_to_dac(dac_cmd, v, ADC_WINDOW_MIN_STEPTO, g_scale);
else if (v > ADC_WINDOW_MAX)
dac_cmd = adc_to_dac(dac_cmd, v, ADC_WINDOW_MAX_STEPTO, g_scale);
#else
dac_cmd = adc_to_dac(dac_cmd, v, 1024, g_scale);
#endif
/* Send it out */
dac_write(dac_cmd);
#ifdef DEBUG_ISR_TIME
LATAbits.LATA9 = 0;
#endif
}
void run_normal(void)
{
int i;
uart1_init(500000);
led_pattern(0b00110011);
/* Keep writing zero to the DAC for about 30 seconds after startup,
or until we receive a character on the UART */
while(uart1_can_get())
uart1_get();
for (i = 0; i < 1500; i++) {
dac_write(DAC_MID);
if (uart1_can_get()) {
uart1_get();
break;
}
msleep(10);
}
led_on();
/* Assume startup current is 0 */
msleep(100);
dac_cmd = do_calibrate();
timer_setup_16bit(5, TIMER_RATE, 1);
timer_set_priority(5, 6);
while(1) {
if (send_data) {
/* There's data to send. Disable the ISR briefly
while we grab it */
uint16_t a, d, o;
disable_int({
if (possibly_clamped) {
/* Mark a possible overflow in the output */
o = 1;
possibly_clamped = 0;
} else o = 0;
a = send_adc;
d = send_dac;
send_data = 0;
});
packet_send_adc_dac(a, d, o);
}
if (uart1_can_get()) {
switch (uart1_get()) {
case 'c':
disable_int({
send_data = 0;
dac_cmd = do_calibrate();
});
packet_send_calibration(g_scale);
default:
break;
}
}
}
}
#include "config.h"
#include "adc.h"
#include "adcext.h"
#include "dac.h"
#include "uart.h"
#include "timer.h"
#include <stdio.h>
#include <math.h>
#include "calibrate.h"
#include "util.h"
#include "mode.h"
#include "led.h"
#include "zoom.h"
#include "packet.h"
int send_data = 0;
uint16_t send_adc;
uint16_t send_dac;
int possibly_clamped = 0;
uint16_t dac_cmd;
#define TIMER_RATE 8000 /* how often to read the ADC and update DAC */
#define PC_RATE 8000 /* how often to send data to the PC */
#define DEBUG_ISR_TIME
#define ADC_WINDOW_MIN 512
#define ADC_WINDOW_MIN_STEPTO 1280
#define ADC_WINDOW_MAX 1536
#define ADC_WINDOW_MAX_STEPTO 768
/* Run mode */
void TISR_HANDLER(5)
{
static int count = 0;
int16_t v;
#ifdef DEBUG_ISR_TIME
LATAbits.LATA9 = 1;
#endif
timer_clear_txif(5);
/* Get most recent sample from 12-bit ADC. */
v = adc_get();
if (v < ADC_CLAMP_MIN || v >= ADC_CLAMP_MAX)
possibly_clamped = 1;
/* Send data to PC */
if (++count >= (TIMER_RATE / PC_RATE)) {
count = 0;
/* Send most recent sample and old DAC value */
send_adc = v;
send_dac = dac_cmd;
send_data = 1;
}
#define WINDOW
#ifdef WINDOW
/* If ADC value is outside the window, step DAC */
if (v < ADC_WINDOW_MIN)
dac_cmd = adc_to_dac(dac_cmd, v, ADC_WINDOW_MIN_STEPTO, g_scale);
else if (v > ADC_WINDOW_MAX)
dac_cmd = adc_to_dac(dac_cmd, v, ADC_WINDOW_MAX_STEPTO, g_scale);
#else
dac_cmd = adc_to_dac(dac_cmd, v, 1024, g_scale);
#endif
/* Send it out */
dac_write(dac_cmd);
#ifdef DEBUG_ISR_TIME
LATAbits.LATA9 = 0;
#endif
}
void run_normal(void)
{
int i;
uart1_init(500000);
led_pattern(0b00110011);
/* Keep writing zero to the DAC for about 30 seconds after startup,
or until we receive a character on the UART */
while(uart1_can_get())
uart1_get();
for (i = 0; i < 1500; i++) {
dac_write(DAC_MID);
if (uart1_can_get()) {
uart1_get();
break;
}
msleep(10);
}
led_on();
/* Assume startup current is 0 */
msleep(100);
dac_cmd = do_calibrate();
timer_setup_16bit(5, TIMER_RATE, 1);
timer_set_priority(5, 6);
while(1) {
if (send_data) {
/* There's data to send. Disable the ISR briefly
while we grab it */
uint16_t a, d, o;
disable_int({
if (possibly_clamped) {
/* Mark a possible overflow in the output */
o = 1;
possibly_clamped = 0;
} else o = 0;
a = send_adc;
d = send_dac;
send_data = 0;
});
packet_send_adc_dac(a, d, o);
}
if (uart1_can_get()) {
switch (uart1_get()) {
case 'c':
disable_int({
send_data = 0;
dac_cmd = do_calibrate();
});
packet_send_calibration(g_scale);
default:
break;
}
}
}
}

+ 100
- 100
firmware/timer.c View File

@@ -1,100 +1,100 @@
#include "config.h"
#include "timer.h"
/* Setup a 16-bit timer to overflow at the specified frequency
timer = 1-9
freq = Hz, or 0 to disable the timer.
interrupt = 1 to enable interrupt, 0 to disable
*/
int timer_setup_16bit(int timer, uint32_t freq, int ie)
{
uint32_t period;
uint16_t prescale;
if (timer < 1 || timer > 9)
return -1;
if (freq == 0) {
switch(timer) {
case 1: T1CONbits.TON = 0; return 0;
case 2: T2CONbits.TON = 0; return 0;
case 3: T3CONbits.TON = 0; return 0;
case 4: T4CONbits.TON = 0; return 0;
case 5: T5CONbits.TON = 0; return 0;
case 6: T6CONbits.TON = 0; return 0;
case 7: T7CONbits.TON = 0; return 0;
case 8: T8CONbits.TON = 0; return 0;
case 9: T9CONbits.TON = 0; return 0;
}
}
/* Figure out timer prescaler and period values. Max period
is 65535 (PRx = 65534) so we can still attain 100% duty
cycle when using a timer for PWM. */
if ((period = FCY / (freq * 1L)) <= 65535)
prescale = 0;
else if ((period = FCY / (freq * 8L)) <= 65535)
prescale = 1;
else if ((period = FCY / (freq * 64L)) <= 65535)
prescale = 2;
else if ((period = FCY / (freq * 256L)) <= 65535)
prescale = 3;
else
prescale = 3, period = 65535;
if (period > 0)
period -= 1;
switch (timer) {
#define __timer_setup_case(x) \
case x: \
T##x##CONbits.TON = 0; \
T##x##CONbits.TCKPS = prescale; \
PR##x = period; \
T##x##CONbits.TON = 1; \
break
__timer_setup_case(1);
__timer_setup_case(2);
__timer_setup_case(3);
__timer_setup_case(4);
__timer_setup_case(5);
__timer_setup_case(6);
__timer_setup_case(7);
__timer_setup_case(8);
__timer_setup_case(9);
#undef __timer_setup_case
}
/* Enable interrupt if requested */
timer_clear_txif(timer);
switch (timer) {
case 1: IEC0bits.T1IE = ie; break;
case 2: IEC0bits.T2IE = ie; break;
case 3: IEC0bits.T3IE = ie; break;
case 4: IEC1bits.T4IE = ie; break;
case 5: IEC1bits.T5IE = ie; break;
case 6: IEC2bits.T6IE = ie; break;
case 7: IEC3bits.T7IE = ie; break;
case 8: IEC3bits.T8IE = ie; break;
case 9: IEC3bits.T9IE = ie; break;
}
return 0;
}
/* sleep for between "ms" and "ms+1" milliseconds */
void msleep(int ms)
{
static int initialized = 0;
if (!initialized) {
timer_setup_16bit(1, 1000, 0);
initialized = 1;
}
/* Very basic, assumes timer1 is set up at 1 khz */
while (ms-- >= 0) {
IFS0bits.T1IF = 0;
while(IFS0bits.T1IF == 0)
continue;
}
}
#include "config.h"
#include "timer.h"
/* Setup a 16-bit timer to overflow at the specified frequency
timer = 1-9
freq = Hz, or 0 to disable the timer.
interrupt = 1 to enable interrupt, 0 to disable
*/
int timer_setup_16bit(int timer, uint32_t freq, int ie)
{
uint32_t period;
uint16_t prescale;
if (timer < 1 || timer > 9)
return -1;
if (freq == 0) {
switch(timer) {
case 1: T1CONbits.TON = 0; return 0;
case 2: T2CONbits.TON = 0; return 0;
case 3: T3CONbits.TON = 0; return 0;
case 4: T4CONbits.TON = 0; return 0;
case 5: T5CONbits.TON = 0; return 0;
case 6: T6CONbits.TON = 0; return 0;
case 7: T7CONbits.TON = 0; return 0;
case 8: T8CONbits.TON = 0; return 0;
case 9: T9CONbits.TON = 0; return 0;
}
}
/* Figure out timer prescaler and period values. Max period