4.2 Initialization
First, initialize the peripherals to be used: TCD, CCL, AC and OPAMP.
Start with setting up the OPAMP as two cascaded noninverting Programmable Gain Amplifiers (PGA), with a total gain of 60. This gain is chosen based on the input signal produced by the current draw of the fan motor used during development. Depending on the current draw, a higher or lower gain can be chosen to fit your application. To lower noise, the first op amp in the cascade is set to maximum, that is 16x, while the second op amp can be adjusted to application needs, preferably to a gain that results in a 1-2V range output for ease of detection. If a different setup is used, please refer to the AVR128DB48 Data Sheet for gain calculation. The following table shows possible gain settings for OPAMP.OP2RESMUX.
Group Configuration | Resulting Total Gain |
---|---|
OPAMP_OP2RESMUX_MUXWIP_WIP0_gc | ~17 |
OPAMP_OP2RESMUX_MUXWIP_WIP1_gc | ~18 |
OPAMP_OP2RESMUX_MUXWIP_WIP2_gc | ~21 |
OPAMP_OP2RESMUX_MUXWIP_WIP3_gc | ~32 |
OPAMP_OP2RESMUX_MUXWIP_WIP4_gc | ~43 |
OPAMP_OP2RESMUX_MUXWIP_WIP5_gc | ~64 |
OPAMP_OP2RESMUX_MUXWIP_WIP6_gc | ~128 |
OPAMP_OP2RESMUX_MUXWIP_WIP7_gc | ~256 |
void opamp_init(void) { /*Disable input on op amp output pin*/ PORTD.PIN5CTRL = PORT_ISC_INPUT_DISABLE_gc; PORTE.PIN1CTRL = PORT_ISC_INPUT_DISABLE_gc; /*Set up op amp*/ OPAMP.CTRLA = OPAMP_ENABLE_bm; OPAMP.TIMEBASE = (uint8_t) ceil(CLK_PER*0.000001)-1; /*Number of peripheral clock cycles that amounts to 1us*/ //OP2 setup OPAMP.OP2CTRLA = OPAMP_RUNSTBY_bm | OPAMP_ALWAYSON_bm | OPAMP_OP2CTRLA_OUTMODE_NORMAL_gc; OPAMP.OP2SETTLE = OPAMP_MAX_SETTLE_TIME; //As the settle time is unknown, the maximum should be set OPAMP.OP2INMUX = OPAMP_OP2INMUX_MUXNEG_WIP_gc | OPAMP_OP2INMUX_MUXPOS_LINKOUT_gc; OPAMP.OP2RESMUX = OPAMP_OP2RESMUX_MUXWIP_WIP5_gc | OPAMP_OP2RESMUX_MUXBOT_GND_gc|OPAMP_OP2RESMUX_MUXTOP_OUT_gc; //OP1 setup OPAMP.OP1CTRLA = OPAMP_RUNSTBY_bm | OPAMP_ALWAYSON_bm | OPAMP_OP1CTRLA_OUTMODE_NORMAL_gc; OPAMP.OP1SETTLE = OPAMP_MAX_SETTLE_TIME; //As the settle time is unknown, the maximums should be set OPAMP.OP1INMUX = OPAMP_OP1INMUX_MUXNEG_WIP_gc | OPAMP_OP1INMUX_MUXPOS_INP_gc; OPAMP.OP1RESMUX = OPAMP_OP1RESMUX_MUXWIP_WIP7_gc | OPAMP_OP1RESMUX_MUXBOT_INN_gc | OPAMP_OP1RESMUX_MUXTOP_OUT_gc; }
Then, set up the TCD to drive the PF0 pin, and to shut down on a failure event. Currently, the TCD is not enabled. However, it will be started later on.
void tcd_init(void) { PORTMUX.TCDROUTEA = PORTMUX_TCD0_ALT2_gc; PORTF.DIRSET=PIN0_bm; TCD0.CTRLB = TCD_WGMODE_TWORAMP_gc; TCD0.CMPASET = 0; /* Compare A Set: 0 */ TCD0.CMPACLR = 1001; /* Compare A Clear: 1001 */ TCD0.CMPBSET = 0; /* Compare B Set: 0 */ TCD0.CMPBCLR = 1000; /* Compare B Clear: 1000 */ ccp_write_io((void*)&(TCD0.FAULTCTRL),1 << TCD_CMPAEN_bp /* Compare A enable: enabled */ | 0 << TCD_CMPA_bp /* Compare A value: disabled */ | 0 << TCD_CMPB_bp /* Compare B value: disabled */ | 0 << TCD_CMPBEN_bp /* Compare B enable: disabled */ | 0 << TCD_CMPC_bp /* Compare C value: disabled */ | 0 << TCD_CMPCEN_bp /* Compare C enable: disabled */ | 0 << TCD_CMPD_bp /* Compare D vaule: disabled */ | 0 << TCD_CMPDEN_bp /* Compare D enable: disabled */); TCD0.EVCTRLA = TCD_CFG_ASYNC_gc /* Neither Filter nor Asynchronous Event is enabled */ | TCD_ACTION_FAULT_gc /* Event trigger a fault */ | TCD_EDGE_RISE_HIGH_gc /* The falling edge or low level of event generates retrigger or fault action */ | 1 << TCD_TRIGEI_bp; /* Trigger event enable: enabled */ TCD0.INPUTCTRLA=TCD_INPUTMODE_WAITSW_gc; //Wait for a reset command TCD0.INPUTCTRLB=TCD_INPUTMODE_WAITSW_gc; //Wait for a reset command while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); // Wait for Enable Ready to be high. TCD0.CTRLA = 0 << TCD_ENABLE_bp /* Enable: disabled */ | TCD_CLKSEL_OSCHF_gc /* */ | TCD_CNTPRES_DIV1_gc /* Sync clock divided by 1 */ | TCD_SYNCPRES_DIV1_gc; }
The AC needs to be set up to trigger at a suitable level. First, set it to trigger at some low level, here 1.1V. This level will later be automatically calibrated to suit the motor at hand.
void ac_init(void) { PORTA.DIRSET=PIN7_bm; VREF.ACREF = VREF_REFSEL_4V096_gc; AC0.MUXCTRL = 0 << AC_INVERT_bp /* Invert AC Output: disabled */ | AC_MUXNEG_DACREF_gc /* DAC Reference */ | AC_MUXPOS_AINP2_gc; /* Positive Pin 0 */ AC0.DACREF = ac_calculate_trigger_voltage(AC_TRIGGER_VOLTAGE_MV_INIT); /* DAC Voltage Reference: 0x64 */ AC0.CTRLA = 1 << AC_ENABLE_bp /* Enable: enabled */ | AC_HYSMODE_NONE_gc /* No hysteresis */ | AC_POWER_PROFILE0_gc /* Power profile 0, lowest consumption and highest response time. */ | 1 << AC_OUTEN_bp /* Output Buffer Enable: enabled */ | 0 << AC_RUNSTDBY_bp; /* Run in Standby Mode: disabled */ }
The helper function ac_calculate_trigger_voltage(uint16_t mV)
is used to
set the AC trigger. It calculates the DACREF value corresponding to a trigger level in
millivolt.
uint8_t ac_calculate_trigger_voltage(uint16_t mV) { uint8_t triggerVoltage = (((uint32_t)mV*256)/VREF_AC_MV); return triggerVoltage; }
To keep the overcurrent signal active until reset, the CCL LUT0 and LUT1 form an RS-latch with the AC0 output as S input and Software Event A as R input as shown in Figure 3-1.
void ccl_init(void) { CCL.SEQCTRL0 = CCL_SEQSEL_RS_gc; // Create a RS latch /*Set up LUT0*/ CCL.LUT0CTRLB = CCL_INSEL0_AC0_gc; // AC0 and mask all other bits CCL.TRUTH0 = 0x02; // When AC is hight hight output, otherwise low output CCL.LUT0CTRLA = CCL_ENABLE_bm | CCL_OUTEN_bm; PORTA.DIRSET = PIN3_bm; /*Set up LUT1*/ EVSYS.USERCCLLUT1A = EVSYS_USER_CHANNEL1_gc; CCL.LUT1CTRLB = CCL_INSEL0_EVENTA_gc; // Event A as input and mask all other bits CCL.TRUTH1 = 0x02; // When Event A is high, we have a high output, otherwise low output CCL.LUT1CTRLA = CCL_ENABLE_bm; CCL.CTRLA = CCL_ENABLE_bm; }
Lastly, the button and debug signals showing the AC event level are set up.
void button_init(void)
{
PORTB.DIRCLR=PIN2_bm;
PORTB.PIN2CTRL=PORT_ISC_FALLING_gc | PORT_PULLUPEN_bm;
sei();
}
button_init(); PORTC.DIRSET = PIN4_bm | PIN5_bm; PORTC.OUTCLR = PIN4_bm | PIN5_bm; PORTD.DIRSET = PIN2_bm;