Preamplifier Offset Drift Correcttion
SUMMARY:
Trimmer usage with preamplifier
The preamplifier has a quad trimmer AD5263. Two of the 4 trimmers are exploited to adjust the output offset voltage, coarse ancd fine. The third trimmer is for drift compensation while the last trimmer is for CMRR mitigation. The 4 trimmers are defined this way:
-
trimmer_CMRR_compensation
-
trimmer_fine_offset
-
trimmer_thermal_compensation
-
trimmer_coarse_offset
To write or read any trimmer we use only one function: preamplifier_scrittura_lettura_trimmer(uint8_t scheda_su_scheda_giu, uint8_t canale, uint8_t trimmer,uint8_t valore, uint8_t scrivi_1_leggi_0 ) where:
- scheda_su_scheda_giu is the board where the trimmer is;
- canale is the preamplifier channel (0 to 5);
- trimmer is one of the 4 trimmers to write or to read (0 to 3 of the list above);
- valore is the value to write, a unsigned int8 (0 to 255);
- scrivi_1_leggi_0 is boolean: when true a write of valore is done, if 0 a reading of the content is done. For both cases the values of the trimmers are in the global matrix contenuto_trimmer_preamplifier[6][4] .
An example of writing of the value 158 in the trimmer for coarse offset compensation of the second channel is:
#define preamplifier_scrivi_il_trimmer
void preamplifier_scrittura_lettura_trimmer(uint8_t scheda_su_scheda_giu, uint8_t canale, uint8_t trimmer, uint8_t valore, uint8_t scrivi_1_leggi_0)
Setting the value of any trimmer for preamplifier trimmer, the contents of the trimmers are stored in...
#define trimmer_coarse_offset
In the example above:
Effects of trimmers on preamplifier
The effects of the trimmers on the output or input of the preamplifier are summarized in Table below:
Table 1: Effects of the trimmers on preamplifier
Preamplifier/PGA offset setting
Amplifer offset can be set with an accuracy of sligtly less than 16 bits via the 2 trimmers trimmer_coarse_offset and trimmer_fine_offset. Offset is implemented with 2 different modalities with the aim of speed up the process of correction and details are given below.
There are some common initial settings. We use 2 trimmers that are initialized. A few steps are adopted in order to minimize disturbances. First the PGA gains of all the channles of the board are set to 1 V/V. Then the offsets, for those channles that must be adjusted, being set at PGA gain of 1 V/V, are scaled by the gain factor. The same is done for the expected final error. This is done channel by channel. As an example if we need an output offset of 0.5 V at a PGA gain of 10, then, the PGA gain is set to 1 V/V and the offset adjusted at 0.05 V. Since some error can be introduced when the gain is returned at its value, the gain of every channel is not recovered at the end of the process, but a few bits before, 2 or 3 depending on the difference between the target value and the acyual value. This way possible errors are compensated.
The offset is adjusted in pseudo-paralalel way. Pseudo-parallel is intended for the fact the the ADC measurement is the only action that is made channel after channel, since only one ADC is present.
The ADC to perform the measurement can be that on the post-board or can be external. In the latter case every time the measurement is needed the µ-controller asks from the CAN bus the measurement. This is done in a way similar to the instr_output_offset_to_be_set.
The modalities for doing an adjustment can be set with the instr_preamplifier_scrittura_lettura_trimmer_offset
The main function to adjust the offset is instr_Vbias_to_be_set_function():
278uint8_t iii,canale, indice;
279uint8_t
volatile tx_data_buffer[8],fai_fai=1;
284 for(iii=0;iii < 8;iii++){
286 tx_data_buffer[iii]=
tx_data[iii];
296 if ( preamplifier_attesa_tra_le_misure <100) preamplifier_attesa_tra_le_misure=100;
297 if (preamplifier_attesa_tra_le_misure > 10000) preamplifier_attesa_tra_le_misure=10000;
302 preamplifier_canali_da_regolare_old=preamplifier_canali_da_regolare;
327 uint8_t dati_scambio[4] , scheda=scheda_su_scheda_giu;
328 for( canale=0;canale<6;canale++){
331 if(dati_scambio[0] ==0){
333 preamplifier_coarse_step_trimmer[canale+scheda]=*(int32_t *)dati_scambio;
335 preamplifier_fine_step_trimmer[canale+scheda]= *(int32_t *)dati_scambio;
337 preamplifier_error_voltage_at_the_moment[canale+scheda]= preamplifier_coarse_step_trimmer[canale+scheda] >>1;
343 for( canale=0;canale<12;canale++){
344 preamplifier_SAR_ini[canale].starting_value =128;
345 preamplifier_SAR_ini[canale].starting_exp_value[0]=7;
346 preamplifier_SAR_ini[canale].starting_exp_value[1]=7;
349 for( canale=0;canale<6;canale++){
350 if((preamplifier_canali_da_regolare >> canale) & 1){
361 for( canale=0;canale<6;canale++){
362 if((preamplifier_canali_da_regolare >> canale) & 1){
364 termalizzazione_fatta[canale + scheda_su_scheda_giu]=0;
373 uint8_t ri_misuriamo=0;preamplifier_canali_da_regolare_old=preamplifier_canali_da_regolare;
374 for( canale=0;canale<6;canale++){
375 if((preamplifier_canali_da_regolare_old >> canale) & 1){
376 if (termalizzazione_fatta[canale + scheda_su_scheda_giu]==0){
377 preamplifier_canali_da_regolare=1 <<canale;
399 for(iii=0;iii < 8 ;iii++){
400 tx_data[iii]= tx_data_buffer[iii];
402 tx_data[6] = preamplifier_canali_da_regolare;
404 for( iii=0;iii<6;iii++){
405 somma += preamplifier_error_voltage_at_PGA_gain[iii+scheda_su_scheda_giu];
407 somma = somma / 6000;
408 if( somma >255) somma=255;
volatile uint16_t ADC_medie_per_misura
the number of ADC readings to average, the maximum is 400
uint8_t tx_data[8]
Transmission data vector.
#define detector_bit_per_storing_startup
#define detector_bit_per_storing_user_state
#define detector_bit_recall_user_defined
#define detector_bit_recall_startup
#define detector_bit_store_state_in_user_as_it_is
#define detector_offset_from_half_scale
void Error_imposta_la_istruzione(void)
Function to be located at the end of every instruction to mark the error, if any.
void ERROR_codifica_errore(uint8_t scheda_su_scheda_giu, unsigned char error_addres, unsigned char code_to_shift, uint8_t reset_count_se_0)
If an error is found its flag is codified here.
volatile uint16_t LED_rosso_fondo_scala
void EPROM_lettura_M24C32_64(uint8_t scheda_su_scheda_giu_, uint8_t mainboard_postmainboard, uint8_t canale, short indirizzo_memoria, uint8_t *dati_letti)
Read from preamplifier and on-board flashes.
void EPROM_store_recover_state_M24C32_64(uint8_t scheda_su_scheda_giu, uint8_t canali_da_regolare, uint8_t set_1_store_0, uint8_t startup_1_user_0)
Store the system state or recover and apply the system state.
@ Memory_preamplifier_slope_calibration_ON_0_OFF_ff
slope trimmer active if zero
@ Memory_preamplifier_address_fine_trimmer_slope_offset
fine slope for trimmer offset
@ Memory_preamplifier_address_coarse_trimmer_slope_offset
coarse slope for trimmer offset
void preamplifier_trova_il_guess(uint8_t trimmer_da_usare)
static void preamplifier_normalize_gain_offset_error(uint8_t scheda_su_scheda_giu_)
The ouput offset will be set at the value that was given with the instr_ouput_offset_we_want_to_set i...
void instr_output_offset_to_be_set_function(void)
The detector bias will be set at the value set with the function instr_detector_Vbias_we_want_to_set_...
void preamplifier_determina_trimmer_therma_comp(uint8_t scheda_su_scheda_giu, uint8_t canale, uint8_t valore_previsto_trimmer_offset, uint8_t solo_recupero_default_comp_se_1)
This function determines and set the thermal compensation trimmer.
void preamplifier_aggiusta_offset_SAR(void)
This function provide the output offset with the standard SAR method.
#define trimmer_fine_offset
volatile int8_t vettore_istruzioni[8]
This is a copy of the received 8 bytes from the CAN.
From it, after setting some variables, we go to preamplifier_normalize_gain_offset_error(), where the gain of all the channels are set to 1 V/V, the target offsets and the final target errors are normalized to 1 V/V, too:
529 uint8_t canale=0, indice;
530 if(scheda_su_scheda_giu_>=1) scheda_su_scheda_giu_=6;
534 preamplifier_canali_da_regolare_old = preamplifier_canali_da_regolare;
536 for(canale=0; canale< 6; canale++){
537 indice= canale + scheda_su_scheda_giu_;
540 if ( preamplifier_error_voltage < ((
PGA_settled_gain[indice] * preamplifier_fine_step_trimmer[indice] *3 )/2) ){
542 preamplifier_error_voltage_at_PGA_gain[indice] =(3 *
PGA_settled_gain[indice] * preamplifier_fine_step_trimmer[indice] )/2;
544 preamplifier_error_voltage_at_PGA_gain[indice] = preamplifier_error_voltage;
546 if( preamplifier_error_voltage_at_PGA_gain[indice] > max_error) max_error = preamplifier_error_voltage_at_PGA_gain[indice];
void PGA_GAIN_CROSS(uint8_t scheda_su_scheda_giu, uint8_t canale, uint8_t PGA_gain_to_set)
Here the 2 digital signals from the trimmer are exploited to set the gain of the input stage of the P...
uint8_t guadagno_minimo_PGA
uint8_t PGA_settled_gain[12]
PGA set gain.
int32_t preamplifier_Output_offset_target[12]
Offset target values.
At this point if the standard SAR procedure is chosen we go to SAR_procedure, while if the Fast SAR procedure is adopted, a first measurement of the outputs is done from which a guess for the coarse trimmers is determined. Then, we go to FAST_SAR_procedure.
- Note
- Both SAR and Fast SAR procedures work rigtly if the calibration of the steps effects determined by the fine and coarse trimmers are known and stored in the preamplifier EPROM. This must be done during preampleifers initial test and the 2 instructions XXXXXXX and YYYYYY are dedicated to this.
SAR_procedure
SAR is implemented in standard way and both fine and coarse trimmers are initialized to 0. The function called by instr_Vbias_to_be_set_function() is preamplifier_aggiusta_offset_SAR():
568 uint8_t target_raggiunto_1_non_raggiunto_0[]={0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0};
569 int32_t ADC_misura_fatta_prov,ADC_misura_fatta_prov_vet[200];
570 uint16_t iii, media_prov;
572 if( scheda_su_scheda_giu>=1) scheda_su_scheda_giu=6;
585 preamplifier_canali_da_regolare_old= preamplifier_canali_da_regolare;
586 for(indice_trimmer=0; indice_trimmer<2;indice_trimmer++){
588 if( indice_trimmer) {
589 preamplifier_canali_da_regolare = preamplifier_canali_da_regolare_old;
590 for(canale=0; canale<6;canale++){
591 indice= canale + scheda_su_scheda_giu;
592 preamplifier_error_voltage_at_the_moment[indice]= preamplifier_error_voltage_at_PGA_gain[indice] / PGA_settled_gain_old[indice];
593 target_raggiunto_1_non_raggiunto_0[indice]=0;
598 for(scalata=0; scalata<8; scalata++){
602 confronto= 1 << (7-scalata);
603 for(canale=0; canale<6;canale++){
604 indice= canale + scheda_su_scheda_giu;
605 if( (preamplifier_SAR_ini[indice].starting_exp_value[indice_trimmer] >= (7-scalata))){
606 if( (preamplifier_canali_da_regolare >> canale) & 1){
615 for(canale=0; canale<6;canale++){
616 indice= canale + scheda_su_scheda_giu;
617 if( (preamplifier_SAR_ini[indice].starting_exp_value[indice_trimmer] >= (7-scalata)) ){
618 if( (preamplifier_canali_da_regolare >> canale) & 1){
619 if( target_raggiunto_1_non_raggiunto_0[indice]==0){
622 if( ( ADC_misura_fatta - preamplifier_Output_offset_target_at_the_moment[indice] ) < \
623 ( - preamplifier_error_voltage_at_the_moment[indice]) ){
625 target_raggiunto_1_non_raggiunto_0[indice]=0;
626 ADC_misura_fatta_old[indice]= ADC_misura_fatta;
627 }
else if ( ( ADC_misura_fatta - preamplifier_Output_offset_target_at_the_moment[indice] ) < \
630 target_raggiunto_1_non_raggiunto_0[indice]=1;
631 preamplifier_canali_da_regolare -= 1 << canale ;
632 ADC_misura_fatta_old[indice]= ADC_misura_fatta;
633 }
else if ( ( ADC_misura_fatta - preamplifier_Output_offset_target_at_the_moment[indice] ) >\
634 ( preamplifier_error_voltage_at_the_moment[indice])){
636 target_raggiunto_1_non_raggiunto_0[indice]=0;
641 }
else if ( ( ADC_misura_fatta - preamplifier_Output_offset_target_at_the_moment[indice] ) >\
644 target_raggiunto_1_non_raggiunto_0[indice]=1;
645 ADC_misura_fatta_old[indice]= ADC_misura_fatta;
646 preamplifier_canali_da_regolare -= 1 << canale ;
int32_t ADC_misura_differenziale_con_media_generico(uint8_t scheda_su_scheda_giu_, uint8_t preamplifier_externalADC_1_onboardADC_0, uint8_t nodo_da_misurare, uint8_t differenziale_1_single_0)
This function sets the original gain when a few LSb have to be determined. The umber of bits is bit_t...
void preamplifier_ritorno_al_gain_originale(uint8_t scalata, uint8_t indice_trimmer, uint8_t *target_raggiunto_1_non_raggiunto_0)
This function sets the original gain when a few LSb have to be determined. The umber of bits is bit_t...
void Aspetta_tanti_ms(int millisecondi)
The timing function.
uint8_t volatile contenuto_trimmer_preamplifier[12][4]
Note that the function preamplifier_ritorno_al_gain_originale() is called to verify if it is the time to come back to the original gain. Normally this is done when 2 or 3 bits of the fine trimmer are those remaining to test and set, for compensanting any possible small error introducd by the gain change.
FAST_SAR_procedure
The Fats SAR procedure tries to reduce the numeber of SAR steps from 16 to between 4 and 6.
When the gains are set to 1 V/V the PGA output voltages are read and a tentative calculation of the 8 MSB for target offset is done (in instr_Vbias_to_be_set_function()). Then, depending on how far is the new measured offset from the target, the 2 or 3 LSb of the MSB, or coarse trimmer, are set to 0 and the SAR is applied from here to the end of the MSB. At this point, the final adjustment part is left to the LSB, or fine trimmer, that was set to 0.
The first step with the setting of the LSB is to recover to the original value the gains. Then, the target values for the LSB are calculated from the measured offset and, depending on how far the new offset is from the final value, the 2 or 3 LSb are set to 0 and the SAR is applied to them.
The Fast SAR function preamplifier_aggiusta_offset_FastSAR() is this:
- Note
- If, when the gain is set to 1 V/V, the output voltage of any channel is found outside the saturation levels (high and low), the procedure is set to standard SAR, for those channels.
Node measurements with internal or external ADC
Node reading can be made with the internal or external ADC. In case an external ADC is chosen, the function called is the preamplifier_ADC_external_measured_node_function():
718 uint8_t buffer_istruzione[8],iii;
719 for(iii=0;iii<8;iii++){
720 buffer_istruzione[iii]=
tx_data[iii];
735 while( (ii <50) && (
evento_CAN != ARM_CAN_EVENT_RECEIVE ) ){
746 valore_mis= ( ( ( *(int32_t *)
rx_data ) & 0xFFFFFFFF ) ) ;
750 for(iii=0;iii<8;iii++){
751 tx_data[iii]=buffer_istruzione[iii];
const struct ADC_coefficiente_type ADC_coefficiente[]
Node normalizing coeficinets used in ADC_lettura_24bit()
#define ADC_molteplicita_node_to_read
@ instr_readback_node_voltages
Nodes voltage read from external ADC.
uint32_t volatile evento_CAN
This is the variable which resembles the flags from the communication.
uint32_t tx_obj_idx
This is the variable which resembles the flags from the communication.
unsigned int indirizzo_CAN_della_scheda
Per ora lo assegnamo cos\i l'indirizzo della scheda.
volatile uint8_t rx_data[8]
Received data vector.
@ CAN_error_comunichiamo_qualche_dato_timeout_on_transmission
Transmission CAN error.
@ error_address_CAN
Error register for CAN bus
void Analog_mux_line_to_select_deselect(uint8_t scheda_su_scheda_giu, uint8_t line_to_select, uint8_t select_1_deselect_0)
Selection of the line to mesure with the analog MUX of the mainboard, driven by the I2C->parallel mux...
@ node_voltage_Analog_Mux_0_offset
Starting Offset for this set of nodes, 32.
int32_t preamplifier_ADC_external_measured_node_function(uint8_t scheda_su_scheda_giu, uint8_t indice)
This function provide node reading from an external ADC.
The function invokes the reading from an external ADC from the CAN.
In case the internal ADC is used, the function to call is the XXXXXXXX():
Sw details
Let's see the function preamplifier_scrittura_lettura_trimmer() for writing, reading the trimmer:
284 uint8_t valore, uint8_t scrivi_1_leggi_0 ){
286 if( scheda_su_scheda_giu >=1) scheda_su_scheda_giu=6;
292 if (scrivi_1_leggi_0){
301 while( I2C_mux->GetStatus().busy){};
305 while( I2C_mux->GetStatus().busy){};
volatile uint32_t Error_bad_operation
exploited to mark the errors
@ I2C_error_termometro_0
termometro ch0
@ error_address_I2C1_B
Error register B for I2C1
void I2C_mux_select_ch(uint8_t scheda_su_scheda_giu, uint8_t mainboard_postmainboard, uint8_t canale_da_abilitare)
The I2C mux.
const struct address_detector_bias_trimmer_type address_preamplifier_trimmer[6]
Preamplifier trimmer addresses
#define maschera_solo_bit_di_scrittura_trimmer
Mask for command of the trimmer.
The identification of the trimmers is done with the structure address_preamplifier_trimmer[6]:
The I2C addresses are 2 less than those of the detector trimmer (remember that to communicate with the 6 trimmers we use 3 different I2C):
The codes for mamnaging the trimmer are: