Hello everyone!
I'm using a SAMD51 based board and I'm trying to understand how it really works. So far, I've done several tasks with it using the Arduino IDE and many of the libraries made by the community.
I come from a background in high-level programming, so I took my time learning a bit more of embedded programming using cpp and c. However, I'm trying to do some stuff that needs to understand and manipulate the board registers.
I'm trying to face a simple problem before increasing the difficulty and for this, I choose to understand this code that reads the SAMD51 temperature as it has built-in capability to do so.
I've tested this and it did work. However, as mentioned before, I want to understand it.
Let's take this part, as an example:
SUPC->VREF.reg |= SUPC_VREF_TSEN | SUPC_VREF_ONDEMAND;
ADC0->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val;
I can't find SUPC_VREF_TSEN
on the datasheet. But I do find the separated, like SUPC
, VREF
, TSEN
. In toppic 19. SUPC – Supply Controller
there is a table 19.7 Register Summary
that shows VREF and TSEN:
From my understanding, looks like that in the SUPC register, in the 32bit section related to the VREF, it changes the TSEN (2nd bit) and ONDEMAND (7th bit) to 1 with the SUPC_VREF_TSEN | SUPC_VREF_ONDEMAND
while keeping the others unchanged due to the |=
.
Is this correct?
Does anyone have good recommendations to keep reading/learning about this? Like a book, blog, post, youtube, ...
Full code
// m4 SAMD51 chip temperature sensor on ADC
// Decimal to fraction conversion. (adapted from ASF sample).
//#define NVMCTRL_TEMP_LOG (0x00800100) // ref pg 59
#define NVMCTRL_TEMP_LOG NVMCTRL_TEMP_LOG_W0
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 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);
}
float get_tempc() {
// enable and read 2 ADC temp sensors, 12-bit res
volatile uint16_t ptat;
volatile uint16_t ctat;
SUPC->VREF.reg |= SUPC_VREF_TSEN | SUPC_VREF_ONDEMAND;
ADC0->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val;
while (ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_CTRLB); //wait for sync
while ( ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_INPUTCTRL ); //wait for sync
ADC0->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_PTAT;
while ( ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync
ADC0->CTRLA.bit.ENABLE = 0x01; // Enable ADC
// Start conversion
while ( ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync
ADC0->SWTRIG.bit.START = 1;
// Clear the Data Ready flag
ADC0->INTFLAG.reg = ADC_INTFLAG_RESRDY;
// Start conversion again, since The first conversion after the reference is changed must not be used.
ADC0->SWTRIG.bit.START = 1;
while (ADC0->INTFLAG.bit.RESRDY == 0); // Waiting for conversion to complete
ptat = ADC0->RESULT.reg;
while ( ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_INPUTCTRL ); //wait for sync
ADC0->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_CTAT;
// Start conversion
while ( ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync
ADC0->SWTRIG.bit.START = 1;
// Clear the Data Ready flag
ADC0->INTFLAG.reg = ADC_INTFLAG_RESRDY;
// Start conversion again, since The first conversion after the reference is changed must not be used.
ADC0->SWTRIG.bit.START = 1;
while (ADC0->INTFLAG.bit.RESRDY == 0); // Waiting for conversion to complete
ctat = ADC0->RESULT.reg;
while ( ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync
ADC0->CTRLA.bit.ENABLE = 0x00; // Disable ADC
while ( ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync
return calculate_temperature(ptat, ctat);
}
void setup() {
Serial.begin(9600);
while (!Serial);
delay(1000);
}
void loop() {
Serial.println(get_tempc());
delay(2000);
}
Reference
SAM D5x/E5x Family Data Sheet (microchip.com)
samd51/m4temp.ino at master · manitou48/samd51 · GitHub