内部に温度センサを内蔵しており、追加の部品なしにある程度の温度は知ることができる。あまり正確とはいえないが相対的に温度の動きを見るには十分

ADC1 の 16ch に繋がっている。データシート通りだが以下の手順を踏む

  • ADC1_IN16 を選択する
  • 17.1μs のサンプル時間にする
  • ADC_CR2 の TSVREFE ビットをセットする
    • 温度センサおよびV_{REFINT}がパワーオンする
  • ADON で変換する
  • V_{SENSE} を読みだす
  • 温度(℃) = (V_{25} - V_{SENSE}) / Avg_Slope + 25 を計算する

V_{25} と Avg_Slope は個別のデータシートに書いてある。STM32F103 の場合

  • V_{25} : 1.34〜1.52 (typ=1.43) V
  • Avg_Slope: 4.0〜4.6 (typ=4.3) mV/℃

17.1μs は、ADCクロックが12MHz なら 17.1e-6 / (1/12e6) = 205.2サイクル以上 、6MHz なら 102.6サイクル以上。どっちにしろサンプル時間レジスタは 0b111 (239.5サイクル) しかない。

static void setupADC(void) {
	rccEnableADC1(true);
	ADC1->CR1 = 0;
	ADC1->CR2 = ADC_CR2_ADON;

	ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_RSTCAL;
	while ((ADC1->CR2 & ADC_CR2_RSTCAL) != 0) ;

	ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CAL;
	while ((ADC1->CR2 & ADC_CR2_CAL) != 0) ;

	ADC1->SMPR1 |= (
		(0b111<<ADC_SMPR1_SMP17_Pos) |
		(0b111<<ADC_SMPR1_SMP16_Pos)
	);
}
static uint16_t startADC(uint8_t channel) {
	ADC1->CR2 |= ADC_CR2_TSVREFE;

	const uint8_t count = 1;
	ADC1->SQR1 = (count-1) << ADC_SQR1_L_Pos;
	ADC1->SQR2 = 0;
	ADC1->SQR3 = (channel & 0b11111) << ADC_SQR3_SQ1_Pos;

	ADC1->CR2 |= ADC_CR2_SWSTART;
	// write same bit to start conversion
	ADC1->CR2 |= ADC_CR2_ADON;

	while ((ADC1->SR & ADC_SR_EOC) == 0) ;

	return ADC1->DR & 0xffff;
}
static float getTemp(void) {
	float adc = 0;
	const uint8_t len = 10;
	for (int i = 0; i < len; i++) {
		adc += (float)startADC(16);
	}
	adc /= len;

	const float V_sense = adc / (float)(1<<12) * 3.3;
	const float V_25 = 1.43;
	const float Avg_Slope = 4.3e-3;
	const float temp = (V_25 - V_sense) / Avg_Slope + 25;
	return temp;
}
  1. トップ
  2. tech
  3. STM32F103 で内部温度センサを読み出す
▲ この日のエントリ