parent
764005c6c5
commit
6440543da1
@ -0,0 +1,217 @@ |
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include <libopencm3/cm3/common.h> |
||||
#include <libopencm3/stm32/memorymap.h> |
||||
|
||||
#include "opamp.h" |
||||
#include "clock.h" |
||||
|
||||
|
||||
/** Lock the opamp config. No way to unlock until restart. */ |
||||
void opamp_lock(uint32_t opamp) |
||||
{ |
||||
OPAMP_CSR(opamp) |= OPAMP_CSR_LOCK; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Enable OPAMP |
||||
* Must also enable RCC_SYSCFG_COMP in RCC |
||||
*/ |
||||
void opamp_enable(uint32_t opamp) |
||||
{ |
||||
OPAMP_CSR(opamp) |= OPAMP_CSR_OPAMPEN; |
||||
} |
||||
|
||||
|
||||
/** Disable OPAMP */ |
||||
void opamp_disable(uint32_t opamp) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_OPAMPEN; |
||||
} |
||||
|
||||
|
||||
/** Send reference to ADC input channel */ |
||||
void opamp_ref_to_adc(uint32_t opamp, bool yes) |
||||
{ |
||||
if (yes) { |
||||
OPAMP_CSR(opamp) |= OPAMP_CSR_TSTREF; |
||||
} else { |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_TSTREF; |
||||
} |
||||
} |
||||
|
||||
|
||||
/** Send reference to non-inverting input */ |
||||
void opamp_ref_to_vp(uint32_t opamp, bool yes) |
||||
{ |
||||
if (yes) { |
||||
OPAMP_CSR(opamp) |= OPAMP_CSR_FORCE_VP; |
||||
} else { |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_FORCE_VP; |
||||
} |
||||
} |
||||
|
||||
|
||||
/** Set PGA gain. To use PGA, set VM to PGA mode. */ |
||||
void opamp_pga_set_gain(uint32_t opamp, enum OPAMP_PGA_GAIN gain) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_PGA_GAIN_MSK; |
||||
OPAMP_CSR(opamp) |= gain; |
||||
} |
||||
|
||||
|
||||
/** Set PGA midpoint routing */ |
||||
void opamp_pga_set_midpoint(uint32_t opamp, enum OPAMP_PGA_MIDPOINT midpoint) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_PGA_MIDPOINT_MSK; |
||||
OPAMP_CSR(opamp) |= midpoint; |
||||
} |
||||
|
||||
|
||||
/** Connect VM pin (can be used to select follower and PGA mode) */ |
||||
void opamp_vm_select(uint32_t opamp, enum OPAMP_VM_SEL vm) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_VM_SEL_MSK; |
||||
OPAMP_CSR(opamp) |= vm; |
||||
} |
||||
|
||||
|
||||
/** Connect the VP pin */ |
||||
void opamp_vp_select(uint32_t opamp, enum OPAMP_VP_SEL vp) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_VP_SEL_MSK; |
||||
OPAMP_CSR(opamp) |= vp; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Set opamp to PGA mode (shortcut function) |
||||
* Midpoint can be connected to VM pins with opamp_pga_set_midpoint() |
||||
*/ |
||||
void opamp_mode_pga(uint32_t opamp) |
||||
{ |
||||
opamp_vm_select(opamp, OPAMP_VM_SEL_PGA); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Set opamp to follower mode (shortcut function). |
||||
* VM is not connected to GPIO in this mode. |
||||
*/ |
||||
void opamp_mode_follower(uint32_t opamp) |
||||
{ |
||||
opamp_vm_select(opamp, OPAMP1_VM_SEL_FLW); |
||||
} |
||||
|
||||
|
||||
/** Select reference value to use */ |
||||
void opamp_ref_select(uint32_t opamp, enum OPAMP_CALSEL ref) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_CALSEL_MSK; |
||||
OPAMP_CSR(opamp) |= ref; |
||||
} |
||||
|
||||
|
||||
/** Set opamp trim offset N */ |
||||
static void set_trimoffset_n(uint32_t opamp, uint8_t offset) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_TRIMOFFSETN_MSK; |
||||
OPAMP_CSR(opamp) |= ((offset & 0x1F) << OPAMP_CSR_TRIMOFFSETN_SHIFT); |
||||
} |
||||
|
||||
|
||||
/** Set opamp trim offset P */ |
||||
static void set_trimoffset_p(uint32_t opamp, uint8_t offset) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_TRIMOFFSETP_MSK; |
||||
OPAMP_CSR(opamp) |= ((offset & 0x1F) << OPAMP_CSR_TRIMOFFSETP_SHIFT); |
||||
} |
||||
|
||||
|
||||
/** Enter calibration mode */ |
||||
static void calib_start(uint32_t opamp) |
||||
{ |
||||
OPAMP_CSR(opamp) |= OPAMP_CSR_USER_TRIM; // enable user trimming
|
||||
OPAMP_CSR(opamp) |= OPAMP_CSR_CALON; // enable calibration mode (connect VP,VM to ref)
|
||||
} |
||||
|
||||
|
||||
/** Leave calibration mode, disable user calibration constants */ |
||||
static void calib_abort(uint32_t opamp) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_USER_TRIM; // disable user trim
|
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_CALON; // leave calibration mode
|
||||
} |
||||
|
||||
|
||||
/** Leave calibration mode */ |
||||
static void calib_end(uint32_t opamp) |
||||
{ |
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_CALON; // leave calibration mode
|
||||
} |
||||
|
||||
|
||||
/** Calibrate N-MOS block. Returns false failure */ |
||||
static bool calib_do_nmos(uint32_t opamp) |
||||
{ |
||||
uint8_t trimoffset; |
||||
opamp_ref_select(opamp, OPAMP_CALSEL_90); |
||||
|
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_TRIMOFFSETN_MSK; |
||||
|
||||
for (trimoffset = 0; trimoffset <= 0x1F; trimoffset++) { |
||||
set_trimoffset_n(opamp, trimoffset); |
||||
delay_ms(2); |
||||
|
||||
if ((OPAMP_CSR(opamp) & OPAMP_CSR_OUTCAL) == 0) { |
||||
return true; // Calibration complete.
|
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
|
||||
|
||||
/** Calibrate P-MOS block. Returns false failure */ |
||||
static bool calib_do_pmos(uint32_t opamp) |
||||
{ |
||||
uint8_t trimoffset; |
||||
opamp_ref_select(opamp, OPAMP_CALSEL_10); |
||||
|
||||
OPAMP_CSR(opamp) &= ~OPAMP_CSR_TRIMOFFSETP_MSK; |
||||
|
||||
for (trimoffset = 0; trimoffset <= 0x1F; trimoffset++) { |
||||
set_trimoffset_p(opamp, trimoffset); |
||||
delay_ms(2); |
||||
|
||||
if ((OPAMP_CSR(opamp) & OPAMP_CSR_OUTCAL) == 0) { |
||||
return true; // Calibration complete.
|
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
|
||||
/** Calibrate an OpAmp */ |
||||
bool opamp_calibrate(uint32_t opamp) |
||||
{ |
||||
opamp_enable(opamp); // make sure it's enabled
|
||||
calib_start(opamp); |
||||
|
||||
if (!calib_do_nmos(opamp)) { |
||||
calib_abort(opamp); |
||||
return false; |
||||
} |
||||
|
||||
if (!calib_do_pmos(opamp)) { |
||||
calib_abort(opamp); |
||||
return false; |
||||
} |
||||
|
||||
calib_end(opamp); |
||||
return true; |
||||
} |
Loading…
Reference in new issue