Implement microcontroller.cpu.temperature on '21 and '51.
This commit is contained in:
parent
681399f8db
commit
3a2b4af830
@ -34,6 +34,7 @@
|
||||
#include "py/binary.h"
|
||||
#include "py/mphal.h"
|
||||
|
||||
#include "peripherals.h"
|
||||
#include "shared-bindings/analogio/AnalogIn.h"
|
||||
|
||||
#include "atmel_start_pins.h"
|
||||
@ -89,57 +90,20 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) {
|
||||
// Something else might have used the ADC in a different way,
|
||||
// so we completely re-initialize it.
|
||||
|
||||
// Turn the clocks on.
|
||||
#ifdef SAMD51
|
||||
if (self->instance == ADC0) {
|
||||
hri_mclk_set_APBDMASK_ADC0_bit(MCLK);
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, ADC0_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
||||
} else if (self->instance == ADC1) {
|
||||
hri_mclk_set_APBDMASK_ADC1_bit(MCLK);
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, ADC1_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SAMD21
|
||||
_pm_enable_bus_clock(PM_BUS_APBC, ADC);
|
||||
_gclk_enable_channel(ADC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val);
|
||||
#endif
|
||||
|
||||
struct adc_sync_descriptor adc;
|
||||
adc_sync_init(&adc, self->instance, (void *)NULL);
|
||||
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INTVCC1_Val);
|
||||
adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
|
||||
|
||||
samd_peripherals_adc_setup(&adc, self->instance);
|
||||
|
||||
// Full scale is 3.3V (VDDANA) = 65535.
|
||||
|
||||
// On SAMD21, INTVCC1 is 0.5*VDDANA. On SAMD51, INTVCC1 is 1*VDDANA.
|
||||
// So on SAMD21 only, divide the input by 2, so full scale will match 0.5*VDDANA.
|
||||
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INTVCC1_Val);
|
||||
#ifdef SAMD21
|
||||
adc_sync_set_channel_gain(&adc, self->channel, ADC_INPUTCTRL_GAIN_DIV2_Val);
|
||||
|
||||
// Load the factory calibration
|
||||
hri_adc_write_CALIB_BIAS_CAL_bf(ADC, (*((uint32_t*) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos);
|
||||
// Bits 7:5
|
||||
uint16_t linearity = ((*((uint32_t*) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
|
||||
// Bits 4:0
|
||||
linearity |= (*((uint32_t*) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
|
||||
hri_adc_write_CALIB_LINEARITY_CAL_bf(ADC, linearity);
|
||||
#endif
|
||||
|
||||
// SAMD51 has a CALIB register but doesn't have documented fuses for them.
|
||||
#ifdef SAMD51
|
||||
uint8_t biasrefbuf;
|
||||
uint8_t biasr2r;
|
||||
uint8_t biascomp;
|
||||
if (self->instance == ADC0) {
|
||||
biasrefbuf = ((*(uint32_t*) ADC0_FUSES_BIASREFBUF_ADDR) & ADC0_FUSES_BIASREFBUF_Msk) >> ADC0_FUSES_BIASREFBUF_Pos;
|
||||
biasr2r = ((*(uint32_t*) ADC0_FUSES_BIASR2R_ADDR) & ADC0_FUSES_BIASR2R_Msk) >> ADC0_FUSES_BIASR2R_Pos;
|
||||
biascomp = ((*(uint32_t*) ADC0_FUSES_BIASCOMP_ADDR) & ADC0_FUSES_BIASCOMP_Msk) >> ADC0_FUSES_BIASCOMP_Pos;
|
||||
} else {
|
||||
biasrefbuf = ((*(uint32_t*) ADC1_FUSES_BIASREFBUF_ADDR) & ADC1_FUSES_BIASREFBUF_Msk) >> ADC1_FUSES_BIASREFBUF_Pos;
|
||||
biasr2r = ((*(uint32_t*) ADC1_FUSES_BIASR2R_ADDR) & ADC1_FUSES_BIASR2R_Msk) >> ADC1_FUSES_BIASR2R_Pos;
|
||||
biascomp = ((*(uint32_t*) ADC1_FUSES_BIASCOMP_ADDR) & ADC1_FUSES_BIASCOMP_Msk) >> ADC1_FUSES_BIASCOMP_Pos;
|
||||
}
|
||||
hri_adc_write_CALIB_BIASREFBUF_bf(self->instance, biasrefbuf);
|
||||
hri_adc_write_CALIB_BIASR2R_bf(self->instance, biasr2r);
|
||||
hri_adc_write_CALIB_BIASCOMP_bf(self->instance, biascomp);
|
||||
#endif
|
||||
adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
|
||||
|
||||
adc_sync_enable_channel(&adc, self->channel);
|
||||
|
||||
|
@ -63,166 +63,219 @@
|
||||
|
||||
#include "common-hal/microcontroller/Processor.h"
|
||||
|
||||
#include "peripherals.h"
|
||||
|
||||
#include "peripheral_clk_config.h"
|
||||
|
||||
// #define ADC_TEMP_SAMPLE_LENGTH 4
|
||||
// #define INT1V_VALUE_FLOAT 1.0
|
||||
// #define INT1V_DIVIDER_1000 1000.0
|
||||
// #define ADC_12BIT_FULL_SCALE_VALUE_FLOAT 4095.0
|
||||
//
|
||||
// typedef struct nvm_calibration_data_t {
|
||||
// float tempR; // Production Room temperature
|
||||
// float tempH; // Production Hot temperature
|
||||
// float INT1VR; // Room temp 2's complement of the internal 1V reference value
|
||||
// float INT1VH; // Hot temp 2's complement of the internal 1V reference value
|
||||
// uint16_t ADCR; // Production Room temperature ADC value
|
||||
// uint16_t ADCH; // Production Hot temperature ADC value
|
||||
// float VADCR; // Room temperature ADC voltage
|
||||
// float VADCH; // Hot temperature ADC voltage
|
||||
// } nvm_calibration_data_t;
|
||||
#define ADC_TEMP_SAMPLE_LENGTH 4
|
||||
#define INT1V_VALUE_FLOAT 1.0
|
||||
#define INT1V_DIVIDER_1000 1000.0
|
||||
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT 4095.0
|
||||
|
||||
|
||||
// Decimal to fraction conversion. (adapted from ASF sample).
|
||||
// STATIC float convert_dec_to_frac(uint8_t val) {
|
||||
// float float_val = (float)val;
|
||||
// if (val < 10) {
|
||||
// return (float_val/10.0);
|
||||
// } else if (val < 100) {
|
||||
// return (float_val/100.0);
|
||||
// } else {
|
||||
// return (float_val/1000.0);
|
||||
// }
|
||||
// }
|
||||
STATIC float convert_dec_to_frac(uint8_t val) {
|
||||
float float_val = (float)val;
|
||||
if (val < 10) {
|
||||
return (float_val/10.0);
|
||||
} else if (val < 100) {
|
||||
return (float_val/100.0);
|
||||
} else {
|
||||
return (float_val/1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
// STATIC void configure_adc_temp(struct adc_module *adc_instance) {
|
||||
// struct adc_config config_adc;
|
||||
// adc_get_config_defaults(&config_adc);
|
||||
//
|
||||
// // The parameters chosen here are from the temperature example in:
|
||||
// // http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
|
||||
// // That note also recommends in general:
|
||||
// // "Discard the first conversion result whenever there is a change
|
||||
// // in ADC configuration like voltage reference / ADC channel change."
|
||||
//
|
||||
// config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV16;
|
||||
// config_adc.reference = ADC_REFERENCE_INT1V;
|
||||
// config_adc.positive_input = ADC_POSITIVE_INPUT_TEMP;
|
||||
// config_adc.negative_input = ADC_NEGATIVE_INPUT_GND;
|
||||
// config_adc.sample_length = ADC_TEMP_SAMPLE_LENGTH;
|
||||
//
|
||||
// adc_init(adc_instance, ADC, &config_adc);
|
||||
//
|
||||
// // Oversample and decimate. A higher samplenum produces a more stable result.
|
||||
// ADC->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(2) | ADC_AVGCTRL_SAMPLENUM_4;
|
||||
// //ADC->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(4) | ADC_AVGCTRL_SAMPLENUM_16;
|
||||
// }
|
||||
// Extract the production calibration data information from NVM (adapted from ASF sample),
|
||||
// then calculate the temperature
|
||||
#ifdef SAMD21
|
||||
STATIC float calculate_temperature(uint16_t raw_value) {
|
||||
volatile uint32_t val1; /* Temperature Log Row Content first 32 bits */
|
||||
volatile uint32_t val2; /* Temperature Log Row Content another 32 bits */
|
||||
uint8_t room_temp_val_int; /* Integer part of room temperature in °C */
|
||||
uint8_t room_temp_val_dec; /* Decimal part of room temperature in °C */
|
||||
uint8_t hot_temp_val_int; /* Integer part of hot temperature in °C */
|
||||
uint8_t hot_temp_val_dec; /* Decimal part of hot temperature in °C */
|
||||
int8_t room_int1v_val; /* internal 1V reference drift at room temperature */
|
||||
int8_t hot_int1v_val; /* internal 1V reference drift at hot temperature*/
|
||||
|
||||
// Extract the production calibration data information from NVM (adapted from ASF sample).
|
||||
//
|
||||
// STATIC void load_calibration_data(nvm_calibration_data_t *cal) {
|
||||
// volatile uint32_t val1; /* Temperature Log Row Content first 32 bits */
|
||||
// volatile uint32_t val2; /* Temperature Log Row Content another 32 bits */
|
||||
// uint8_t room_temp_val_int; /* Integer part of room temperature in °C */
|
||||
// uint8_t room_temp_val_dec; /* Decimal part of room temperature in °C */
|
||||
// uint8_t hot_temp_val_int; /* Integer part of hot temperature in °C */
|
||||
// uint8_t hot_temp_val_dec; /* Decimal part of hot temperature in °C */
|
||||
// int8_t room_int1v_val; /* internal 1V reference drift at room temperature */
|
||||
// int8_t hot_int1v_val; /* internal 1V reference drift at hot temperature*/
|
||||
//
|
||||
// uint32_t *temp_log_row_ptr = (uint32_t *)NVMCTRL_TEMP_LOG;
|
||||
//
|
||||
// val1 = *temp_log_row_ptr;
|
||||
// temp_log_row_ptr++;
|
||||
// val2 = *temp_log_row_ptr;
|
||||
//
|
||||
// room_temp_val_int = (uint8_t)((val1 & NVMCTRL_FUSES_ROOM_TEMP_VAL_INT_Msk) >> NVMCTRL_FUSES_ROOM_TEMP_VAL_INT_Pos);
|
||||
// room_temp_val_dec = (uint8_t)((val1 & NVMCTRL_FUSES_ROOM_TEMP_VAL_DEC_Msk) >> NVMCTRL_FUSES_ROOM_TEMP_VAL_DEC_Pos);
|
||||
//
|
||||
// hot_temp_val_int = (uint8_t)((val1 & NVMCTRL_FUSES_HOT_TEMP_VAL_INT_Msk) >> NVMCTRL_FUSES_HOT_TEMP_VAL_INT_Pos);
|
||||
// hot_temp_val_dec = (uint8_t)((val1 & NVMCTRL_FUSES_HOT_TEMP_VAL_DEC_Msk) >> NVMCTRL_FUSES_HOT_TEMP_VAL_DEC_Pos);
|
||||
//
|
||||
// room_int1v_val = (int8_t)((val1 & NVMCTRL_FUSES_ROOM_INT1V_VAL_Msk) >> NVMCTRL_FUSES_ROOM_INT1V_VAL_Pos);
|
||||
// hot_int1v_val = (int8_t)((val2 & NVMCTRL_FUSES_HOT_INT1V_VAL_Msk) >> NVMCTRL_FUSES_HOT_INT1V_VAL_Pos);
|
||||
//
|
||||
// cal->ADCR = (uint16_t)((val2 & NVMCTRL_FUSES_ROOM_ADC_VAL_Msk) >> NVMCTRL_FUSES_ROOM_ADC_VAL_Pos);
|
||||
//
|
||||
// cal->ADCH = (uint16_t)((val2 & NVMCTRL_FUSES_HOT_ADC_VAL_Msk) >> NVMCTRL_FUSES_HOT_ADC_VAL_Pos);
|
||||
//
|
||||
// cal->tempR = room_temp_val_int + convert_dec_to_frac(room_temp_val_dec);
|
||||
// cal->tempH = hot_temp_val_int + convert_dec_to_frac(hot_temp_val_dec);
|
||||
//
|
||||
// cal->INT1VR = 1 - ((float)room_int1v_val/INT1V_DIVIDER_1000);
|
||||
// cal->INT1VH = 1 - ((float)hot_int1v_val/INT1V_DIVIDER_1000);
|
||||
//
|
||||
// cal->VADCR = ((float)cal->ADCR * cal->INT1VR)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
// cal->VADCH = ((float)cal->ADCH * cal->INT1VH)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
// }
|
||||
float tempR; // Production Room temperature
|
||||
float tempH; // Production Hot temperature
|
||||
float INT1VR; // Room temp 2's complement of the internal 1V reference value
|
||||
float INT1VH; // Hot temp 2's complement of the internal 1V reference value
|
||||
uint16_t ADCR; // Production Room temperature ADC value
|
||||
uint16_t ADCH; // Production Hot temperature ADC value
|
||||
float VADCR; // Room temperature ADC voltage
|
||||
float VADCH; // Hot temperature ADC voltage
|
||||
|
||||
/*
|
||||
* Calculate fine temperature using Equation1 and Equation
|
||||
* 1b as mentioned in data sheet section "Temperature Sensor Characteristics"
|
||||
* of Electrical Characteristics. (adapted from ASF sample code).
|
||||
*/
|
||||
// STATIC float calculate_temperature(uint16_t raw_code, nvm_calibration_data_t *cal)
|
||||
// {
|
||||
// float VADC; /* Voltage calculation using ADC result for Coarse Temp calculation */
|
||||
// float VADCM; /* Voltage calculation using ADC result for Fine Temp calculation. */
|
||||
// float INT1VM; /* Voltage calculation for reality INT1V value during the ADC conversion */
|
||||
//
|
||||
// VADC = ((float)raw_code * INT1V_VALUE_FLOAT)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
//
|
||||
// // Hopefully compiler will remove common subepxressions here.
|
||||
//
|
||||
// /* Coarse Temp Calculation by assume INT1V=1V for this ADC conversion */
|
||||
// float coarse_temp = cal->tempR + (((cal->tempH - cal->tempR)/(cal->VADCH - cal->VADCR)) * (VADC - cal->VADCR));
|
||||
//
|
||||
// /* Calculation to find the real INT1V value during the ADC conversion */
|
||||
// INT1VM = cal->INT1VR + (((cal->INT1VH - cal->INT1VR) * (coarse_temp - cal->tempR))/(cal->tempH - cal->tempR));
|
||||
//
|
||||
// VADCM = ((float)raw_code * INT1VM)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
//
|
||||
// /* Fine Temp Calculation by replace INT1V=1V by INT1V = INT1Vm for ADC conversion */
|
||||
// float fine_temp = cal->tempR + (((cal->tempH - cal->tempR)/(cal->VADCH - cal->VADCR)) * (VADCM - cal->VADCR));
|
||||
//
|
||||
// return fine_temp;
|
||||
// }
|
||||
uint32_t *temp_log_row_ptr = (uint32_t *)NVMCTRL_TEMP_LOG;
|
||||
|
||||
val1 = *temp_log_row_ptr;
|
||||
temp_log_row_ptr++;
|
||||
val2 = *temp_log_row_ptr;
|
||||
|
||||
room_temp_val_int = (uint8_t)((val1 & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos);
|
||||
room_temp_val_dec = (uint8_t)((val1 & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos);
|
||||
|
||||
hot_temp_val_int = (uint8_t)((val1 & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos);
|
||||
hot_temp_val_dec = (uint8_t)((val1 & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos);
|
||||
|
||||
room_int1v_val = (int8_t)((val1 & FUSES_ROOM_INT1V_VAL_Msk) >> FUSES_ROOM_INT1V_VAL_Pos);
|
||||
hot_int1v_val = (int8_t)((val2 & FUSES_HOT_INT1V_VAL_Msk) >> FUSES_HOT_INT1V_VAL_Pos);
|
||||
|
||||
ADCR = (uint16_t)((val2 & FUSES_ROOM_ADC_VAL_Msk) >> FUSES_ROOM_ADC_VAL_Pos);
|
||||
ADCH = (uint16_t)((val2 & FUSES_HOT_ADC_VAL_Msk) >> FUSES_HOT_ADC_VAL_Pos);
|
||||
|
||||
tempR = room_temp_val_int + convert_dec_to_frac(room_temp_val_dec);
|
||||
tempH = hot_temp_val_int + convert_dec_to_frac(hot_temp_val_dec);
|
||||
|
||||
INT1VR = 1 - ((float)room_int1v_val/INT1V_DIVIDER_1000);
|
||||
INT1VH = 1 - ((float)hot_int1v_val/INT1V_DIVIDER_1000);
|
||||
|
||||
VADCR = ((float)ADCR * INT1VR)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
VADCH = ((float)ADCH * INT1VH)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
|
||||
float VADC; /* Voltage calculation using ADC result for Coarse Temp calculation */
|
||||
float VADCM; /* Voltage calculation using ADC result for Fine Temp calculation. */
|
||||
float INT1VM; /* Voltage calculation for reality INT1V value during the ADC conversion */
|
||||
|
||||
VADC = ((float)raw_value * INT1V_VALUE_FLOAT)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
|
||||
// Hopefully compiler will remove common subepxressions here.
|
||||
|
||||
// calculate fine temperature using Equation1 and Equation
|
||||
// 1b as mentioned in data sheet section "Temperature Sensor Characteristics"
|
||||
// of Electrical Characteristics. (adapted from ASF sample code).
|
||||
// Coarse Temp Calculation by assume INT1V=1V for this ADC conversion
|
||||
float coarse_temp = tempR + (((tempH - tempR)/(VADCH - VADCR)) * (VADC - VADCR));
|
||||
|
||||
// Calculation to find the real INT1V value during the ADC conversion
|
||||
INT1VM = INT1VR + (((INT1VH - INT1VR) * (coarse_temp - tempR))/(tempH - tempR));
|
||||
|
||||
VADCM = ((float)raw_value * INT1VM)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;
|
||||
|
||||
// Fine Temp Calculation by replace INT1V=1V by INT1V = INT1Vm for ADC conversion
|
||||
float fine_temp = tempR + (((tempH - tempR)/(VADCH - VADCR)) * (VADCM - VADCR));
|
||||
|
||||
return fine_temp;
|
||||
}
|
||||
#endif // SAMD21
|
||||
|
||||
#ifdef SAMD51
|
||||
STATIC float calculate_temperature(uint16_t TP, uint16_t TC) {
|
||||
uint32_t TLI = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_INT_ADDR & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos;
|
||||
uint32_t TLD = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_DEC_ADDR & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos;
|
||||
float TL = TLI + convert_dec_to_frac(TLD);
|
||||
|
||||
uint32_t THI = (*(uint32_t *)FUSES_HOT_TEMP_VAL_INT_ADDR & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos;
|
||||
uint32_t THD = (*(uint32_t *)FUSES_HOT_TEMP_VAL_DEC_ADDR & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos;
|
||||
float TH = THI + convert_dec_to_frac(THD);
|
||||
|
||||
uint16_t VPL = (*(uint32_t *)FUSES_ROOM_ADC_VAL_PTAT_ADDR & FUSES_ROOM_ADC_VAL_PTAT_Msk) >> FUSES_ROOM_ADC_VAL_PTAT_Pos;
|
||||
uint16_t VPH = (*(uint32_t *)FUSES_HOT_ADC_VAL_PTAT_ADDR & FUSES_HOT_ADC_VAL_PTAT_Msk) >> FUSES_HOT_ADC_VAL_PTAT_Pos;
|
||||
|
||||
uint16_t VCL = (*(uint32_t *)FUSES_ROOM_ADC_VAL_CTAT_ADDR & FUSES_ROOM_ADC_VAL_CTAT_Msk) >> FUSES_ROOM_ADC_VAL_CTAT_Pos;
|
||||
uint16_t VCH = (*(uint32_t *)FUSES_HOT_ADC_VAL_CTAT_ADDR & FUSES_HOT_ADC_VAL_CTAT_Msk) >> FUSES_HOT_ADC_VAL_CTAT_Pos;
|
||||
|
||||
// From SAMD51 datasheet: section 45.6.3.1 (page 1327).
|
||||
return (TL*VPH*TC - VPL*TH*TC - TL*VCH*TP + TH*VCL*TP) / (VCL*TP - VCH*TP - VPL*TC + VPH*TC);
|
||||
}
|
||||
#endif // SAMD51
|
||||
|
||||
// External interface.
|
||||
//
|
||||
float common_hal_mcu_processor_get_temperature(void) {
|
||||
// struct adc_module adc_instance_struct;
|
||||
//
|
||||
// system_voltage_reference_enable(SYSTEM_VOLTAGE_REFERENCE_TEMPSENSE);
|
||||
// configure_adc_temp(&adc_instance_struct);
|
||||
// nvm_calibration_data_t nvm_calibration_data;
|
||||
// load_calibration_data(&nvm_calibration_data);
|
||||
//
|
||||
// adc_enable(&adc_instance_struct);
|
||||
//
|
||||
// uint16_t data;
|
||||
// enum status_code status;
|
||||
//
|
||||
// // Read twice and discard first result, as recommended in section 14 of
|
||||
// // http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
|
||||
// // "Discard the first conversion result whenever there is a change in ADC configuration
|
||||
// // like voltage reference / ADC channel change"
|
||||
// // Empirical observation shows the first reading is quite different than subsequent ones.
|
||||
//
|
||||
// adc_start_conversion(&adc_instance_struct);
|
||||
// do {
|
||||
// status = adc_read(&adc_instance_struct, &data);
|
||||
// } while (status == STATUS_BUSY);
|
||||
//
|
||||
// adc_start_conversion(&adc_instance_struct);
|
||||
// do {
|
||||
// status = adc_read(&adc_instance_struct, &data);
|
||||
// } while (status == STATUS_BUSY);
|
||||
//
|
||||
// // Disable so that someone else can use the adc with different settings.
|
||||
// adc_disable(&adc_instance_struct);
|
||||
// return calculate_temperature(data, &nvm_calibration_data);
|
||||
return 0;
|
||||
struct adc_sync_descriptor adc;
|
||||
|
||||
static Adc* adc_insts[] = ADC_INSTS;
|
||||
samd_peripherals_adc_setup(&adc, adc_insts[0]);
|
||||
|
||||
#ifdef SAMD21
|
||||
// The parameters chosen here are from the temperature example in:
|
||||
// http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
|
||||
// That note also recommends in general:
|
||||
// "Discard the first conversion result whenever there is a change
|
||||
// in ADC configuration like voltage reference / ADC channel change."
|
||||
|
||||
adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
|
||||
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INT1V_Val);
|
||||
// Channel passed in adc_sync_enable_channel is actually ignored (!).
|
||||
adc_sync_enable_channel(&adc, ADC_INPUTCTRL_MUXPOS_TEMP_Val);
|
||||
adc_sync_set_inputs(&adc,
|
||||
ADC_INPUTCTRL_MUXPOS_TEMP_Val, // pos_input
|
||||
ADC_INPUTCTRL_MUXNEG_GND_Val, // neg_input
|
||||
ADC_INPUTCTRL_MUXPOS_TEMP_Val); // channel channel (this arg is ignored (!))
|
||||
|
||||
adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
|
||||
|
||||
hri_adc_write_CTRLB_PRESCALER_bf(adc.device.hw, ADC_CTRLB_PRESCALER_DIV32_Val);
|
||||
hri_adc_write_SAMPCTRL_SAMPLEN_bf(adc.device.hw, ADC_TEMP_SAMPLE_LENGTH);
|
||||
|
||||
hri_sysctrl_set_VREF_TSEN_bit(SYSCTRL);
|
||||
|
||||
// Oversample and decimate. A higher samplenum produces a more stable result.
|
||||
hri_adc_write_AVGCTRL_SAMPLENUM_bf(adc.device.hw, ADC_AVGCTRL_SAMPLENUM_4_Val);
|
||||
hri_adc_write_AVGCTRL_ADJRES_bf(adc.device.hw, 2);
|
||||
|
||||
volatile uint16_t value;
|
||||
|
||||
// Read twice and discard first result, as recommended in section 14 of
|
||||
// http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
|
||||
// "Discard the first conversion result whenever there is a change in ADC configuration
|
||||
// like voltage reference / ADC channel change"
|
||||
// Empirical observation shows the first reading is quite different than subsequent ones.
|
||||
|
||||
// The channel listed in adc_sync_read_channel is actually ignored(!).
|
||||
// Must be set as above with adc_sync_set_inputs.
|
||||
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_TEMP_Val, ((uint8_t*) &value), 2);
|
||||
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_TEMP_Val, ((uint8_t*) &value), 2);
|
||||
|
||||
adc_sync_deinit(&adc);
|
||||
return calculate_temperature(value);
|
||||
#endif // SAMD21
|
||||
|
||||
#ifdef SAMD51
|
||||
adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
|
||||
// Reference voltage choice is a guess. It's not specified in the datasheet that I can see.
|
||||
// INTVCC1 seems to read a little high.
|
||||
// INTREF doesn't work: ADC hangs BUSY.
|
||||
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INTVCC0_Val);
|
||||
|
||||
// If ONDEMAND=1, we don't need to use the VREF.TSSEL bit to choose PTAT and CTAT.
|
||||
hri_supc_set_VREF_ONDEMAND_bit(SUPC);
|
||||
hri_supc_set_VREF_TSEN_bit(SUPC);
|
||||
|
||||
// Channel passed in adc_sync_enable_channel is actually ignored (!).
|
||||
adc_sync_enable_channel(&adc, ADC_INPUTCTRL_MUXPOS_PTAT_Val);
|
||||
adc_sync_set_inputs(&adc,
|
||||
ADC_INPUTCTRL_MUXPOS_PTAT_Val, // pos_input
|
||||
ADC_INPUTCTRL_MUXNEG_GND_Val, // neg_input
|
||||
ADC_INPUTCTRL_MUXPOS_PTAT_Val); // channel (this arg is ignored (!))
|
||||
|
||||
// Read both temperature sensors.
|
||||
volatile uint16_t ptat;
|
||||
volatile uint16_t ctat;
|
||||
|
||||
// The channel listed in adc_sync_read_channel is actually ignored(!).
|
||||
// Must be set as above with adc_sync_set_inputs.
|
||||
// Read twice for stability (necessary?)
|
||||
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_PTAT_Val, ((uint8_t*) &ptat), 2);
|
||||
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_PTAT_Val, ((uint8_t*) &ptat), 2);
|
||||
|
||||
adc_sync_set_inputs(&adc,
|
||||
ADC_INPUTCTRL_MUXPOS_CTAT_Val, // pos_input
|
||||
ADC_INPUTCTRL_MUXNEG_GND_Val, // neg_input
|
||||
ADC_INPUTCTRL_MUXPOS_CTAT_Val); // channel (this arg is ignored (!))
|
||||
|
||||
// Channel passed in adc_sync_enable_channel is actually ignored (!).
|
||||
adc_sync_enable_channel(&adc, ADC_INPUTCTRL_MUXPOS_CTAT_Val);
|
||||
// The channel listed in adc_sync_read_channel is actually ignored(!).
|
||||
// Must be set as above with adc_sync_set_inputs.
|
||||
// Read twice for stability (necessary?)
|
||||
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_CTAT_Val, ((uint8_t*) &ctat), 2);
|
||||
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_CTAT_Val, ((uint8_t*) &ctat), 2);
|
||||
hri_supc_set_VREF_ONDEMAND_bit(SUPC);
|
||||
|
||||
adc_sync_deinit(&adc);
|
||||
return calculate_temperature(ptat, ctat);
|
||||
#endif // SAMD51
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "peripherals.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hpl_sercom_config.h"
|
||||
|
||||
@ -42,4 +42,3 @@ uint8_t samd_peripherals_spi_baudrate_to_baud_reg_value(const uint32_t baudrate)
|
||||
uint32_t samd_peripherals_spi_baud_reg_value_to_baudrate(const uint8_t baud_reg_value) {
|
||||
return PROTOTYPE_SERCOM_SPI_M_SYNC_CLOCK_FREQUENCY / (2 * (baud_reg_value + 1));
|
||||
}
|
||||
|
||||
|
@ -44,4 +44,4 @@ Sercom* sercom_insts[SERCOM_INST_NUM];
|
||||
#include "samd51_peripherals.h"
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_PINS_H
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_H
|
||||
|
@ -24,9 +24,11 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hal/include/hal_adc_sync.h"
|
||||
#include "hpl/gclk/hpl_gclk_base.h"
|
||||
#include "hpl/pm/hpl_pm_base.h"
|
||||
|
||||
|
||||
// The clock initializer values are rather random, so we need to put them in
|
||||
// tables for lookup. We can't compute them.
|
||||
|
||||
@ -91,3 +93,21 @@ uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
|
||||
bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad) {
|
||||
return clock_pad == 1 || clock_pad == 3;
|
||||
}
|
||||
|
||||
// Do initialization znd calibration setup needed for any use of the ADC.
|
||||
// The reference and resolution should be set by the caller.
|
||||
void samd_peripherals_adc_setup(struct adc_sync_descriptor *adc, Adc *instance) {
|
||||
// Turn the clocks on.
|
||||
_pm_enable_bus_clock(PM_BUS_APBC, ADC);
|
||||
_gclk_enable_channel(ADC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val);
|
||||
|
||||
adc_sync_init(adc, instance, (void *)NULL);
|
||||
|
||||
// Load the factory calibration
|
||||
hri_adc_write_CALIB_BIAS_CAL_bf(ADC, (*((uint32_t*) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos);
|
||||
// Bits 7:5
|
||||
uint16_t linearity = ((*((uint32_t*) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
|
||||
// Bits 4:0
|
||||
linearity |= (*((uint32_t*) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
|
||||
hri_adc_write_CALIB_LINEARITY_CAL_bf(ADC, linearity);
|
||||
}
|
||||
|
@ -28,9 +28,11 @@
|
||||
#define MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H
|
||||
|
||||
#include "include/sam.h"
|
||||
#include "hal/include/hal_adc_sync.h"
|
||||
|
||||
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
|
||||
uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad);
|
||||
bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad);
|
||||
void samd_peripherals_adc_setup(struct adc_sync_descriptor *adc, Adc *instance);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H
|
||||
|
@ -24,6 +24,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hal/include/hal_adc_sync.h"
|
||||
#include "hpl/gclk/hpl_gclk_base.h"
|
||||
#include "hri/hri_mclk_d51.h"
|
||||
|
||||
@ -130,3 +131,35 @@ uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
|
||||
bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad) {
|
||||
return clock_pad == 1;
|
||||
}
|
||||
|
||||
// Do initialization znd calibration setup needed for any use of the ADC.
|
||||
// The reference and resolution should be set by the caller.
|
||||
void samd_peripherals_adc_setup(struct adc_sync_descriptor *adc, Adc *instance) {
|
||||
// Turn the clocks on.
|
||||
if (instance == ADC0) {
|
||||
hri_mclk_set_APBDMASK_ADC0_bit(MCLK);
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, ADC0_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
||||
} else if (instance == ADC1) {
|
||||
hri_mclk_set_APBDMASK_ADC1_bit(MCLK);
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, ADC1_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
||||
}
|
||||
|
||||
adc_sync_init(adc, instance, (void *)NULL);
|
||||
|
||||
// SAMD51 has a CALIB register but doesn't have documented fuses for them.
|
||||
uint8_t biasrefbuf;
|
||||
uint8_t biasr2r;
|
||||
uint8_t biascomp;
|
||||
if (instance == ADC0) {
|
||||
biasrefbuf = ((*(uint32_t*) ADC0_FUSES_BIASREFBUF_ADDR) & ADC0_FUSES_BIASREFBUF_Msk) >> ADC0_FUSES_BIASREFBUF_Pos;
|
||||
biasr2r = ((*(uint32_t*) ADC0_FUSES_BIASR2R_ADDR) & ADC0_FUSES_BIASR2R_Msk) >> ADC0_FUSES_BIASR2R_Pos;
|
||||
biascomp = ((*(uint32_t*) ADC0_FUSES_BIASCOMP_ADDR) & ADC0_FUSES_BIASCOMP_Msk) >> ADC0_FUSES_BIASCOMP_Pos;
|
||||
} else {
|
||||
biasrefbuf = ((*(uint32_t*) ADC1_FUSES_BIASREFBUF_ADDR) & ADC1_FUSES_BIASREFBUF_Msk) >> ADC1_FUSES_BIASREFBUF_Pos;
|
||||
biasr2r = ((*(uint32_t*) ADC1_FUSES_BIASR2R_ADDR) & ADC1_FUSES_BIASR2R_Msk) >> ADC1_FUSES_BIASR2R_Pos;
|
||||
biascomp = ((*(uint32_t*) ADC1_FUSES_BIASCOMP_ADDR) & ADC1_FUSES_BIASCOMP_Msk) >> ADC1_FUSES_BIASCOMP_Pos;
|
||||
}
|
||||
hri_adc_write_CALIB_BIASREFBUF_bf(instance, biasrefbuf);
|
||||
hri_adc_write_CALIB_BIASR2R_bf(instance, biasr2r);
|
||||
hri_adc_write_CALIB_BIASCOMP_bf(instance, biascomp);
|
||||
}
|
||||
|
@ -28,10 +28,11 @@
|
||||
#define MICROPY_INCLUDED_ATMEL_SAMD_SAMD51_PERIPHERALS_H
|
||||
|
||||
#include "sam.h"
|
||||
#include "hal/include/hal_adc_sync.h"
|
||||
|
||||
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
|
||||
uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad);
|
||||
bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad);
|
||||
void samd_peripherals_adc_setup(struct adc_sync_descriptor *adc, Adc *instance);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD51_PERIPHERALS_H
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017 by Dan Halbert for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H
|
||||
#define MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H
|
||||
|
||||
#include "include/sam.h"
|
||||
|
||||
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
|
||||
uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad);
|
||||
bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H
|
Loading…
x
Reference in New Issue
Block a user