#include #include #include #include #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; }