5.15 OPAMP Guide
This chapter describes the file composition and the 14 public functions of the OPAMP driver in the N32G41x standard peripheral library: the reset and initialization of the four operational amplifiers, work-mode and gain selection, positive and negative input selection, write protection and offset calibration, together with the two examples PGA and PGA_SwitchByTim. Read the register descriptions of the work modes, the input selection and the calibration together with the “Operational amplifier (OPAMP)” chapter of the user manual, and take the errata sheet as the authority on known chip issues.
5.15.1 Related files
| Relative path | Purpose |
|---|---|
firmware/n32g41x_std_periph_driver/inc/n32g41x_opamp.h |
Defines the OPAMPX enumeration, the OPAMP_InitType structure and the gain, mode, input-selection, reference-voltage, calibration and lock macros, and declares 14 public functions in the five sections reset and initialization, enable and disable, configuration, lock and calibration |
firmware/n32g41x_std_periph_driver/src/n32g41x_opamp.c |
Implements those 14 functions: read-modify-write of the control registers, default structure values, mode and input selection for the four operational amplifiers, and enable, lock and calibration bit operations |
firmware/CMSIS/device/n32g41x.h |
Defines the OPAMP_Module register structure with the OPAMP pointer, the OPAMPx_CS_* bit masks and the OPAMP_LOCK_* lock bits |
projects/n32g41x_EVAL/examples/OPAMP/ |
OPAMP example root directory, holding the 2 examples PGA and PGA_SwitchByTim |
projects/n32g41x_EVAL/examples/OPAMP/PGA/ |
Composition of that example project directory: MDK-ARM/, EWARM/, inc/, src/, readme.txt |
5.15.2 API reference
The following 14 public functions are listed in the declaration order of n32g41x_opamp.h in 5 groups; each entry gives a summary and the original comment above the function in n32g41x_opamp.c.
Reset and initialization
OPAMP_DeInit, OPAMP_StructInit, OPAMP_Init
Enable and disable
OPAMP_Enable
Configuration
OPAMP_SetWorkMode, OPAMP_SetPgaGain, OPAMP_SetVpSel, OPAMP_SetVmSel, OPAMP_SetVmSecondSel, OPAMP_SetVpSecondSel, OPAMP_VREFSel
Lock
OPAMP_SetLock
Calibration
OPAMP_CalEn, OPAMP_IsCalOutHigh
void OPAMP_DeInit(void)Reset the OPAMP registers.
name OPAMP_DeInit.
fun Reset the OPAMP registers.
return none
void OPAMP_StructInit(OPAMP_InitType *OPAMP_InitStruct)Fills each OPAMP_InitStruct member with its default value.
name OPAMP_StructInit.
fun Fills each OPAMP_InitStruct member with its default value.
param OPAMP_InitStruct :
- Gain
- Mode
- OPAMP_Vpsel
- OPAMP_Vmsel
- OPAMP_Vpssel
- OPAMP_Vmssel
- VREFSEL
- CALEN
return none
Use note: The implementation sets Gain to OPAMP_CS_PGA_GAIN_2, Mode to OPAMP_CS_EXT_OPAMP and VREFSEL to OPAMP_VREF_SEL_0_1VDD, and clears the remaining selection fields and CALEN.
void OPAMP_Init(OPAMPX OPAMPx, const OPAMP_InitType *OPAMP_InitStruct)Initializes the OPAMP according to OPAMP_InitStruct.
name OPAMP_Init.
fun Initializes the OPAMP according to OPAMP_InitStruct.
param OPAMPX :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param OPAMP_InitStruct :
- Mode :
- OPAMP_CS_EXT_OPAMP
- OPAMP_CS_PGA_EN
- OPAMP_CS_FOLLOW
- OPAMP_Vpsel :
- OPAMP1_CS_VPSEL_PA1
- OPAMP1_CS_VPSEL_PA5
- OPAMP1_CS_VPSEL_PA4
- OPAMP1_CS_VPSEL_PA7
- OPAMP2_CS_VPSEL_PA7
- OPAMP2_CS_VPSEL_PA4
- OPAMP2_CS_VPSEL_PB0
- OPAMP2_CS_VPSEL_PB14
- OPAMP3_CS_VPSEL_PB2
- OPAMP3_CS_VPSEL_PA1
- OPAMP3_CS_VPSEL_PA5
- OPAMP3_CS_VPSEL_PC3
- OPAMP4_CS_VPSEL_PA4
- OPAMP4_CS_VPSEL_PC5
- OPAMP4_CS_VPSEL_PB13
- OPAMP4_CS_VPSEL_PA7
- OPAMP_Vmsel :
- OPAMP1_CS_VMSEL_PA3
- OPAMP1_CS_VMSEL_PC5
- OPAMP1_CS_VMSEL_PA0
- OPAMP1_CS_VMSEL_FLOAT
- OPAMP2_CS_VMSEL_PA5
- OPAMP2_CS_VMSEL_PC5
- OPAMP2_CS_VMSEL_PB0
- OPAMP2_CS_VMSEL_FLOAT
- OPAMP3_CS_VMSEL_PC4
- OPAMP3_CS_VMSEL_PB10
- OPAMP3_CS_VMSEL_NC
- OPAMP3_CS_VMSEL_FLOAT
- OPAMP4_CS_VMSEL_PB10
- OPAMP4_CS_VMSEL_PB1
- OPAMP4_CS_VMSEL_PA5
- OPAMP4_CS_VMSEL_FLOAT
- OPAMP_Vpssel :
- OPAMP1_CS_VPSSEL_PA1
- OPAMP1_CS_VPSSEL_PA5
- OPAMP1_CS_VPSSEL_PA4
- OPAMP1_CS_VPSSEL_PA7
- OPAMP2_CS_VPSSEL_PA7
- OPAMP2_CS_VPSSEL_PA4
- OPAMP2_CS_VPSSEL_PB0
- OPAMP2_CS_VPSSEL_PB14
- OPAMP3_CS_VPSSEL_PB2
- OPAMP3_CS_VPSSEL_PA1
- OPAMP3_CS_VPSSEL_PA5
- OPAMP3_CS_VPSSEL_PC3
- OPAMP4_CS_VPSSEL_PA4
- OPAMP4_CS_VPSSEL_PC5
- OPAMP4_CS_VPSSEL_PB13
- OPAMP4_CS_VPSSEL_PA7
- OPAMP_Vmssel :
- OPAMP1_CS_VMSSEL_PA3
- OPAMP1_CS_VMSSEL_PC5
- OPAMP1_CS_VMSSEL_PA0
- OPAMP1_CS_VMSSEL_FLOAT
- OPAMP2_CS_VMSSEL_PA5
- OPAMP2_CS_VMSSEL_PC5
- OPAMP2_CS_VMSSEL_PB0
- OPAMP2_CS_VMSSEL_FLOAT
- OPAMP3_CS_VMSSEL_PC4
- OPAMP3_CS_VMSSEL_PB10
- OPAMP3_CS_VMSSEL_NC
- OPAMP3_CS_VMSSEL_FLOAT
- OPAMP4_CS_VMSSEL_PB10
- OPAMP4_CS_VMSSEL_PB1
- OPAMP4_CS_VMSSEL_PA5
- OPAMP4_CS_VMSSEL_FLOAT
- Gain :
- OPAMP_CS_PGA_GAIN_2
- OPAMP_CS_PGA_GAIN_4
- OPAMP_CS_PGA_GAIN_8
- OPAMP_CS_PGA_GAIN_16
- OPAMP_CS_PGA_GAIN_32
- VREFSEL :
- OPAMP_VREF_SEL_0_1VDD
- OPAMP_VREF_SEL_0_9VDD
- CALEN :
- OPAMP_CS_CALEN_ENABLE
- OPAMP_CS_CALEN_DISABLE
return none
Use note: The implementation reads back OPAMPx_CS, clears the enable, mode, gain, input-selection, reference-voltage and calibration bits and then writes the corresponding structure values; the structure has no enable member, so OPAMP_Init clears the enable bit of OPAMPx and OPAMP_Enable must be called afterwards.
void OPAMP_Enable(OPAMPX OPAMPx, FunctionalState Cmd)Enable or disable OPAMPx .
name OPAMP_Enable.
fun Enable or disable OPAMPx .
param OPAMPx :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param Cmd :
- ENABLE
- DISABLE
return none
void OPAMP_SetWorkMode(OPAMPX OPAMPx, OPAMP_CS_MOD Mode)OPA work mode select.
name OPAMP_SetWorkMode.
fun OPA work mode select.
param OPAMPx :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param Mode :
- OPAMP_CS_EXT_OPAMP
- OPAMP_CS_PGA_EN
- OPAMP_CS_FOLLOW
return none
void OPAMP_SetPgaGain(OPAMPX OPAMPx, OPAMP_CS_PGA_GAIN Gain)Set opampx gain value selection
name OPAMP_SetPgaGain.
fun Set opampx gain value selection
param OPAMPx :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param Gain :
- OPAMP_CS_PGA_GAIN_2
- OPAMP_CS_PGA_GAIN_4
- OPAMP_CS_PGA_GAIN_8
- OPAMP_CS_PGA_GAIN_16
- OPAMP_CS_PGA_GAIN_32
return none
void OPAMP_SetVpSel(OPAMPX OPAMPx, OPAMP_CS_VPSEL VpSel)Set opamp VP selection
name OPAMP_SetVpSel.
fun Set opamp VP selection
param OPAMPX :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param VpSel :
- OPAMP1_CS_VPSEL_PA1
- OPAMP1_CS_VPSEL_PA5
- OPAMP1_CS_VPSEL_PA4
- OPAMP1_CS_VPSEL_PA7
- OPAMP2_CS_VPSEL_PA7
- OPAMP2_CS_VPSEL_PA4
- OPAMP2_CS_VPSEL_PB0
- OPAMP2_CS_VPSEL_PB14
- OPAMP3_CS_VPSEL_PB2
- OPAMP3_CS_VPSEL_PA1
- OPAMP3_CS_VPSEL_PA5
- OPAMP3_CS_VPSEL_PC3
- OPAMP4_CS_VPSEL_PA4
- OPAMP4_CS_VPSEL_PC5
- OPAMP4_CS_VPSEL_PB13
- OPAMP4_CS_VPSEL_PA7
return none
void OPAMP_SetVmSel(OPAMPX OPAMPx, OPAMP_CS_VMSEL VmSel)Set opamp VM selection
name OPAMP_SetVmSel.
fun Set opamp VM selection
param OPAMPX :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param VmSel
- OPAMP1_CS_VMSEL_PA3
- OPAMP1_CS_VMSEL_PC5
- OPAMP1_CS_VMSEL_PA0
- OPAMP1_CS_VMSEL_FLOAT
- OPAMP2_CS_VMSEL_PA5
- OPAMP2_CS_VMSEL_PC5
- OPAMP2_CS_VMSEL_PB0
- OPAMP2_CS_VMSEL_FLOAT
- OPAMP3_CS_VMSEL_PC4
- OPAMP3_CS_VMSEL_PB10
- OPAMP3_CS_VMSEL_NC
- OPAMP3_CS_VMSEL_FLOAT
- OPAMP4_CS_VMSEL_PB10
- OPAMP4_CS_VMSEL_PB1
- OPAMP4_CS_VMSEL_PA5
- OPAMP4_CS_VMSEL_FLOAT
return none
void OPAMP_SetVmSecondSel(OPAMPX OPAMPx, OPAMP_CS_VMSSEL VmsSel)Set opamp VM secondary selection
name OPAMP_SetVmSecondSel.
fun Set opamp VM secondary selection
param OPAMPx :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param VmsSel :
- OPAMP1_CS_VMSSEL_PA3
- OPAMP1_CS_VMSSEL_PC5
- OPAMP1_CS_VMSSEL_PA0
- OPAMP1_CS_VMSSEL_FLOAT
- OPAMP2_CS_VMSSEL_PA5
- OPAMP2_CS_VMSSEL_PC5
- OPAMP2_CS_VMSSEL_PB0
- OPAMP2_CS_VMSSEL_FLOAT
- OPAMP3_CS_VMSSEL_PC4
- OPAMP3_CS_VMSSEL_PB10
- OPAMP3_CS_VMSSEL_NC
- OPAMP3_CS_VMSSEL_FLOAT
- OPAMP4_CS_VMSSEL_PB10
- OPAMP4_CS_VMSSEL_PB1
- OPAMP4_CS_VMSSEL_PA5
- OPAMP4_CS_VMSSEL_FLOAT
return none
void OPAMP_SetVpSecondSel(OPAMPX OPAMPx, OPAMP_CS_VPSSEL VpsSel)Set opamp VP secondary selection
name OPAMP_SetVpSecondSel.
fun Set opamp VP secondary selection
param OPAMPX :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param VpsSel
- OPAMP1_CS_VPSSEL_PA1
- OPAMP1_CS_VPSSEL_PA5
- OPAMP1_CS_VPSSEL_PA4
- OPAMP1_CS_VPSSEL_PA7
- OPAMP2_CS_VPSSEL_PA7
- OPAMP2_CS_VPSSEL_PA4
- OPAMP2_CS_VPSSEL_PB0
- OPAMP2_CS_VPSSEL_PB14
- OPAMP3_CS_VPSSEL_PB2
- OPAMP3_CS_VPSSEL_PA1
- OPAMP3_CS_VPSSEL_PA5
- OPAMP3_CS_VPSSEL_PC3
- OPAMP4_CS_VPSSEL_PA4
- OPAMP4_CS_VPSSEL_PC5
- OPAMP4_CS_VPSSEL_PB13
- OPAMP4_CS_VPSSEL_PA7
return none
void OPAMP_VREFSel(OPAMPX OPAMPx, uint32_t VrefSel)Set opampx vref_opt selection
name OPAMP_VREFSel.
fun Set opampx vref_opt selection
param OPAMPx :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param VrefSel :
- OPAMP_VREF_SEL_0_1VDD
- OPAMP_VREF_SEL_0_9VDD
return none
void OPAMP_SetLock(uint32_t Lock)Set opamp lock
name OPAMP_SetLock.
fun Set opamp lock
param Lock :
- OPAMP1_LOCK
- OPAMP2_LOCK
- OPAMP3_LOCK
- OPAMP4_LOCK
return none
Use note: The implementation ORs Lock into OPAMP_LOCK, so several macros can be passed as one bitwise OR.
void OPAMP_CalEn(OPAMPX OPAMPx, FunctionalState Cmd)Set OPAMPx offset calibration EN
name OPAMP_CalEn.
fun Set OPAMPx offset calibration EN
param OPAMPx :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
param Cmd :
- ENABLE
- DISABLE
return none
bool OPAMP_IsCalOutHigh(OPAMPX OPAMPx)Check whether the specified OPAMP calibration output is high.
name OPAMP_IsCalOutHigh.
fun Check whether the specified OPAMP calibration output is high.
param OPAMPx :
- OPAMP1
- OPAMP2
- OPAMP3
- OPAMP4
return true or false
5.15.3 Demo descriptions
This section describes the two OPAMP examples: PGA demonstrates dual-channel PGA amplification with OPAMP1 and OPAMP2, and PGA_SwitchByTim demonstrates a PGA whose input is switched by ATIM1_CC6 and whose output is captured by ADC and DMA.
5.15.3.1 PGA Dual-channel PGA gain amplification
Example directory: projects/n32g41x_EVAL/examples/OPAMP/PGA/
Enable GPIO and OPAMP clocks→Configure PA1 and PA7 as analog inputs→Configure PA2 and PA6 as analog outputs→Set OPAMP1 and OPAMP2 to PGA mode with gain 2→Spin in the main loop and watch the outputs with an oscilloscope
| Item | Source analysis |
|---|---|
| Function | Configures OPAMP1 and OPAMP2 in PGA mode with gain 2, with outputs on PA2 and PA6; observe the inputs and outputs with an oscilloscope, where the output equals twice the input. |
| Source files | src/main.c: main with RCC_Configuration, GPIO_Configuration and OPAMP_Configuration;inc/main.h: includes n32g41x_opamp.h and defines the VP and VOUT pin macros of OPAMP1 and OPAMP2;src/n32g41x_it.c, inc/n32g41x_it.h: interrupt service routines;readme.txt: function, environment and instructions;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | PA1 and PA7 are analog inputs (the positive inputs of OPAMP1 and OPAMP2), PA2 and PA6 are analog outputs (the two VOUT pins); the GPIOs are set to GPIO_MODE_ANALOG, GPIO_NO_PULL, GPIO_DS_HIGH and GPIO_AF0. The OPAMP mode is OPAMP_CS_PGA_EN with gain OPAMP_CS_PGA_GAIN_2;the positive input of OPAMP1 selects OPAMP1_CS_VPSEL_PA1 and that of OPAMP2 selects OPAMP2_CS_VPSEL_PA7. The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415. |
| Program and data path | main first calls RCC_Configuration to enable the GPIO (RCC_AHB_PERIPH_GPIO) and OPAMP (RCC_APB1_PERIPH_OPAMP) clocks, then GPIO_Configuration sets PA1, PA7, PA2 and PA6 to analog function, and OPAMP_Configuration takes the default values with OPAMP_StructInit, sets the gain and mode, and calls OPAMP_Init, OPAMP_SetVpSel and OPAMP_Enable for OPAMP1 and OPAMP2 in turn, before entering an empty while(1). |
| Main APIs | OPAMP_StructInit, OPAMP_Init, OPAMP_SetVpSel, OPAMP_Enable, GPIO_InitStruct, GPIO_InitPeripheral |
| Verification | After compiling, enter debug mode, apply an input to PA1 and PA7, and observe the outputs on PA2 and PA6 with an oscilloscope; the output should be twice the input. |
| Use note | The G41x embeds 4 independent OPAMPs and this example uses only OPAMP1 and OPAMP2; in PGA mode the VM pin is internally connected to the feedback network, so unused VM pins can serve as ordinary GPIO; the OPAMP output range is 0.4 V to VDDA-0.4 V. The development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example first locates the inputs and outputs of the two OPAMPs through pin macros, then sets the positive inputs and outputs to analog function, and finally writes the PGA mode and gain and enables the amplifiers.
Data path: main -> RCC_Configuration enables the OPAMP clock -> GPIO_Configuration sets PA1 and PA7 as analog inputs and PA2 and PA6 as analog outputs -> OPAMP_Configuration writes OPAMP_CS_PGA_EN and OPAMP_CS_PGA_GAIN_2 -> OPAMP_Enable -> the outputs on PA2 and PA6 are twice the inputs
1. The board macros give the pins of the two OPAMPs. inc/main.h defines the positive input of OPAMP1 as PA1 and its output as PA2, and the positive input of OPAMP2 as PA7 and its output as PA6; the N32G412 and N32G415 branches share the same pin mapping.
/* OPAMP pin definitions
* OPAMP1: VP0 = PA1, VOUT = PA2
* OPAMP2: VP0 = PA7, VOUT = PA6
* Both N32G412 and N32G415 share the same OPAMP pin mapping
*/
#ifdef N32G412
#define OPAMP1_VP_PORT GPIOA
#define OPAMP1_VP_PIN GPIO_PIN_1
#define OPAMP1_OUT_PORT GPIOA
#define OPAMP1_OUT_PIN GPIO_PIN_2
#define OPAMP2_VP_PORT GPIOA
#define OPAMP2_VP_PIN GPIO_PIN_7
#define OPAMP2_OUT_PORT GPIOA
#define OPAMP2_OUT_PIN GPIO_PIN_6
#endif
When switching to another OPAMP channel or board, check this group of OPAMPx_VP_PIN and OPAMPx_OUT_PIN in step.
2. Enable the OPAMP and GPIO clocks. The OPAMP hangs off APB1 and the GPIO off AHB; RCC_Configuration enables both clocks, and either one missing keeps the later register writes from taking effect.
void RCC_Configuration(void)
{
/* Enable GPIO clocks */
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_GPIO, ENABLE);
/* Enable OPAMP clocks */
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_OPAMP, ENABLE);
}
When porting to other peripherals, check that RCC_AHB_PERIPH_GPIO and RCC_APB1_PERIPH_OPAMP match the ports and amplifiers used by your board.
3. Set the positive inputs and outputs to analog function. GPIO_Configuration uses one structure to set PA1 and PA7 as analog inputs, then changes Pin to set PA2 and PA6 as analog outputs; in analog mode the pins connect directly to the input and output network of the amplifier.
void GPIO_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;
GPIO_InitStruct(&GPIO_InitStructure);
/* Configure OPAMP1_VP (PA1) and OPAMP2_VP (PA7) as analog inputs */
GPIO_InitStructure.Pin = OPAMP1_VP_PIN | OPAMP2_VP_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_ANALOG;
GPIO_InitStructure.GPIO_Slew_Rate = GPIO_SLEW_RATE_FAST;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF0;
GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
GPIO_InitStructure.GPIO_Current = GPIO_DS_HIGH;
GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
/* Configure OPAMP1_OUT (PA2) and OPAMP2_OUT (PA6) as analog outputs */
GPIO_InitStructure.Pin = OPAMP1_OUT_PIN | OPAMP2_OUT_PIN;
GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
}
GPIO_SLEW_RATE_FAST applies only to PA0, PA1, PA2, PB5, PB6 and PB7, so it does not affect PA6 or PA7; when changing pins, check GPIOA and the chosen pins in step.
4. Write the PGA mode and gain and enable the amplifiers. OPAMP_Configuration first takes the default values with OPAMP_StructInit, then sets Gain to OPAMP_CS_PGA_GAIN_2 and Mode to OPAMP_CS_PGA_EN, and then initializes OPAMP1 and OPAMP2 in turn and selects their positive inputs.
void OPAMP_Configuration(void)
{
OPAMP_InitType OPAMP_InitStructure;
OPAMP_StructInit(&OPAMP_InitStructure);
OPAMP_InitStructure.Gain = OPAMP_CS_PGA_GAIN_2;
OPAMP_InitStructure.Mode = OPAMP_CS_PGA_EN;
/* Configure OPAMP1: PGA mode, gain x2, VP = PA1 */
OPAMP_Init(OPAMP1, &OPAMP_InitStructure);
OPAMP_SetVpSel(OPAMP1, OPAMP1_CS_VPSEL_PA1);
OPAMP_Enable(OPAMP1, ENABLE);
/* Configure OPAMP2: PGA mode, gain x2, VP = PA7 */
OPAMP_Init(OPAMP2, &OPAMP_InitStructure);
OPAMP_SetVpSel(OPAMP2, OPAMP2_CS_VPSEL_PA7);
OPAMP_Enable(OPAMP2, ENABLE);
/* OPAMP output pin is automatically enabled when OPAMPx is enabled */
}
OPAMP_Init clears the enable bit, so always call OPAMP_Init first and OPAMP_Enable afterwards; to change the gain only replace the Gain value, and to change channels check OPAMP1 and OPAMP2 and their positive-input macros in step.
Corresponding source files: src/main.c, inc/main.h
5.15.3.2 PGA_SwitchByTim Timer-switched PGA sampling
Example directory: projects/n32g41x_EVAL/examples/OPAMP/PGA_SwitchByTim/
Enable GPIO, OPAMP, DMA, ADC1 and ATIM1 clocks→Configure PA1 and PA7 as analog inputs and PA2 as an analog output→Set OPAMP1 to PGA mode with gain 2→Configure ADC1 to be triggered by ATIM1 and moved into memory by DMA→Configure the ATIM1 counter and the CC6 output and start them→Spin in the main loop and read ADCConvertedValue
| Item | Source analysis |
|---|---|
| Function | Configures OPAMP1 in PGA mode with gain 2, its positive input switched automatically by ATIM1_CC6 between PA1 and PA7; the output goes to PA2 into ADC1 channel 2, which is triggered by ATIM1 and stored in ADCConvertedValue in a circular DMA transfer. |
| Source files | src/main.c: main with RCC_Configuration, GPIO_Configuration, DMA_Config, OPAMP_Configuration, ADC_Initial and TIM_Configuration;inc/main.h: includes n32g41x_opamp.h, n32g41x_adc.h, n32g41x_dma.h and n32g41x_tim.h and defines the OPAMP1 pin macros;src/n32g41x_it.c, inc/n32g41x_it.h: interrupt service routines;readme.txt: function, environment and instructions;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | Pins: PA1 and PA7 are the primary and secondary positive inputs of OPAMP1, PA2 is an analog output; OPAMP: mode OPAMP_CS_PGA_EN at gain OPAMP_CS_PGA_GAIN_2, primary input OPAMP1_CS_VPSEL_PA1, secondary input OPAMP1_CS_VPSSEL_PA7;ADC: triggered by ADC_EXT_TRIG_REG_CONV_ATIM1_TRGO on channel ADC1_Channel_02_PA2 with sample time ADC_SAMP_TIME_CYCLES_6;DMA: DMA1 channel 1 moves 4 half-words in circular mode; Timer: ATIM1 center-aligned mode 3, Period 1000, prescaler SystemCoreClock/12000 - 1, CC6 pulse 500, triggered by TIM_TRGO_SRC_UPDATE;Clock: HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415. |
| Program and data path | main calls RCC_Configuration to enable the GPIO, OPAMP, DMA, ADC1 and ATIM1 clocks, GPIO_Configuration to set the analog pins, DMA_Config to set up DMA1 channel 1, OPAMP_Configuration to configure OPAMP1, ADC_Initial to configure ADC1 and TIM_Configuration to configure ATIM1, then starts the counter with TIM_EnableCtrlPwmOutputs and TIM_Enable;the ATIM1 update event triggers an ADC1 conversion and DMA1 channel 1 moves ADC1->DAT into ADCConvertedValue[4] circularly while the main loop spins. |
| Main APIs | OPAMP_StructInit, OPAMP_Init, OPAMP_Enable, GPIO_InitStruct, GPIO_InitPeripheral, DMA_Init, ADC_ConfigRegularChannel, ADC_Enable, ADC_EnableDMA, TIM_InitTimeBase, TIM_SelectOutputTrig |
| Verification | After compiling, enter debug mode, apply a voltage to PA1 and PA7 and watch the data in ADCConvertedValue;the values should be the samples of PA1 and PA7 amplified by 2. The output on PA2 can be watched with an oscilloscope. |
| Use note | The G41x embeds 4 independent OPAMPs and this example uses only OPAMP1; in PGA mode the VM pin is internally connected to the feedback network, so unused VM pins can serve as ordinary GPIO; the OPAMP output range is 0.4 V to VDDA-0.4 V; the voltage amplified by the PGA must not exceed VDDA-0.3 V nor fall below 0.3 V; make sure the PGA switching rate (ATIM1_CC6) is greater than the ADC conversion rate. The development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example first locates the three pins of OPAMP1, then sets up the primary and secondary positive inputs of the PGA, next uses ATIM1 to supply the switching and trigger signals, and finally has ADC1 and DMA bring the amplified result back into memory.
Data path: ATIM1_CC6 -> OPAMP1 positive input switched between PA1 and PA7 -> PGA gain 2 -> output on PA2 -> ADC1 channel 2 sampling (triggered by ATIM1 TRGO) -> DMA1 channel 1 -> ADCConvertedValue
1. The board macros give the three pins of OPAMP1. inc/main.h defines the primary positive input of OPAMP1 as PA1, its secondary positive input as PA7 and its output as PA2; both chip branches share the same mapping.
/* OPAMP pin definitions
* OPAMP1: VP0 = PA1,VPS = PA7, VOUT = PA2
* Both N32G412 and N32G415 share the same OPAMP pin mapping
*/
#ifdef N32G412
#define OPAMP1_VP_PORT GPIOA
#define OPAMP1_VP_PIN GPIO_PIN_1
#define OPAMP1_VPS_PORT GPIOA
#define OPAMP1_VPS_PIN GPIO_PIN_7
#define OPAMP1_OUT_PORT GPIOA
#define OPAMP1_OUT_PIN GPIO_PIN_2
When switching to another OPAMP channel, check OPAMP1_VP_PIN, OPAMP1_VPS_PIN and OPAMP1_OUT_PIN and the amplifier channel number in step.
2. Configure the primary and secondary positive inputs of OPAMP1 and enable it. OPAMP_Configuration takes the default structure, sets PGA mode with gain 2, and sets the primary positive input to PA1 and the secondary one to PA7 for ATIM1_CC6 to switch between them.
void OPAMP_Configuration(void)
{
OPAMP_InitType OPAMP_InitStructure;
OPAMP_StructInit(&OPAMP_InitStructure);
OPAMP_InitStructure.Gain = OPAMP_CS_PGA_GAIN_2;
OPAMP_InitStructure.Mode = OPAMP_CS_PGA_EN;
OPAMP_InitStructure.OPAMP_Vpsel = OPAMP1_CS_VPSEL_PA1;
OPAMP_InitStructure.OPAMP_Vpssel = OPAMP1_CS_VPSSEL_PA7;
/* Configure OPAMP1: PGA mode, gain x2*/
OPAMP_Init(OPAMP1, &OPAMP_InitStructure);
OPAMP_Enable(OPAMP1, ENABLE);
/* OPAMP output pin is automatically enabled when OPAMPx is enabled */
}
The secondary positive input OPAMP_Vpssel only matters when OPAMPx_CS.VPSSEL takes part in the ATIM1_CC6 automatic switching; when changing the switched object, check the channel macros of OPAMP1_CS_VPSSEL_* in step.
3. Use ATIM1 for the switching and trigger. TIM_Configuration sets the prescaler and the period of 1000 from a SystemCoreClock/12000 base, configures the pulses of channels 1 and 6, and selects the update event as the trigger output.
void TIM_Configuration(void)
{
TIM_DeInit(ATIM1);
/* Compute the prescaler value */
PrescalerValue = (uint32_t)((SystemCoreClock) / 12000) - 1;
/* Time base configuration */
TIM_InitTimBaseStruct(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.Period = 1000;
TIM_TimeBaseStructure.Prescaler = PrescalerValue;
TIM_TimeBaseStructure.ClkDiv = TIM_CLK_DIV1;
TIM_TimeBaseStructure.CounterMode = TIM_CNT_MODE_CENTER_ALIGN3;
TIM_InitTimeBase(ATIM1, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel6 */
TIM_OCInitStructure.OutputState = TIM_OUTPUT_STATE_ENABLE;
TIM_OCInitStructure.Pulse = CCR6_Val;
TIM_InitOc6(ATIM1, &TIM_OCInitStructure);
TIM_ConfigOc6Preload(ATIM1, TIM_OC_PRE_LOAD_ENABLE);
TIM_SelectOutputTrig(ATIM1,TIM_TRGO_SRC_UPDATE);
TIM_ConfigArPreload(ATIM1, ENABLE);
}
Both the switching and the trigger come from ATIM1; when changing the switching rate, check the prescaler, the Period and the CCR6_Val of CC6 in step and keep the switching rate greater than the ADC conversion rate.
4. ADC1 samples PA2 triggered by TRGO. ADC_Initial selects the ATIM1 TRGO as the trigger source, connects the single regular channel to PA2 with a 6-cycle sampling time, and enables DMA after the ADC is ready.
void ADC_Initial(void)
{
/* ADCx configuration ------------------------------------------------------*/
ADC_InitStructure.WorkMode = ADC_WORKMODE_INDEPENDENT;
ADC_InitStructure.MultiChEn = DISABLE;
ADC_InitStructure.ContinueConvEn = DISABLE;
ADC_InitStructure.ExtTrigSelect = ADC_EXT_TRIG_REG_CONV_ATIM1_TRGO;
ADC_InitStructure.DatAlign = ADC_DAT_ALIGN_R;
ADC_InitStructure.ChsNumber = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_ConfigRegularChannel(ADC1, ADC1_Channel_02_PA2, 1, ADC_SAMP_TIME_CYCLES_6);
/* Enable ADC1 */
ADC_Enable(ADC1, ENABLE);
/*Check ADC1 Ready*/
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_RDY) == RESET)
;
/* Enable ADC1 DMA */
ADC_EnableDMA(ADC1, ENABLE);
}
The sampling channel is fixed at ADC1_Channel_02_PA2, so when changing the output pin check the ADC channel number and the sampling time in step.
5. Bring the samples back with circular DMA. DMA_Config points the peripheral address at ADC1->DAT and the memory address at ADCConvertedValue, with 4 half-words, memory increment and circular mode, and maps channel 1 to ADC1.
void DMA_Config(void)
{
DMA_DeInit(DMA_CH1);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (uint32_t)&ADC1->DAT;
DMA_InitStructure.MemAddr = (uint32_t)&ADCConvertedValue;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = 4;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.CircularMode = DMA_MODE_CIRCULAR;
DMA_InitStructure.Priority = DMA_PRIORITY_HIGH;
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
DMA_Init(DMA_CH1, &DMA_InitStructure);
DMA_RequestRemap(DMA_REMAP_ADC1, DMA, DMA_CH1, ENABLE);
/* Enable DMA channel1 */
DMA_EnableChannel(DMA_CH1, ENABLE);
}
BufSize is 4 and matches ADCConvertedValue[4]; when changing the buffer length, change BufSize and the array size together and check the mapping of DMA_RequestRemap in step.
Corresponding source files: src/main.c, inc/main.h
5.15.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| Negative input in PGA mode | The “OPAMP single-ended gain (PGA) mode” section of the user manual states that PGA is OPAMPx_CS.MOD = “10” with 2/4/8/16/32 gain, that OPAMPx_CS.VMSSEL or OPAMPx_CS.VMSEL must be set to floating, and that the positive input is selected by OPAMPx_CS.VPSSEL or OPAMPx_CS.VPSEL. |
User manual |
| Negative input wiring in follower mode | The “OPAMP follower mode” section of the user manual states that follower mode is OPAMPx_CS.MOD = “11”, that the negative input is connected internally to the output port, and that unused VM pins can serve as other GPIO. |
User manual |
| Output range limitation | The opening of the “Operational amplifier (OPAMP)” chapter of the user manual states that the OPAMP input range is 0V to VDDA and the output range is 0.4V to VDDA-0.4V. | User manual |
| Automatic input pin switching | The features and the register description of the user manual state that OPAMPx_CS.VPSSEL and OPAMPx_CS.VMSSEL support the automatic switching of the OPAMP1 and OPAMP2 input pins by ATIM1_CC6, and that OPAMP3 and OPAMP4 are switched automatically by ATIM2_CC6. |
User manual |
| Write protection | The “OPAMP write protection” section and the OPAMP_LOCK register description of the user manual state that after OPAMP1LK to OPAMP4LK are set to 1 the corresponding OPAMP registers become read-only, and that each bit is write-once and cleared only by a power-on reset. |
User manual |
The N32G412 and N32G415 series errata sheet V1.0.0 (2026.6.30) lists a single row for revision A chips with an empty errata link column, and gives no OPAMP-related entries.