Использование внутреннего датчик температуры STM32 (и библиотека)
В очерке про блютуз-датчик влажности почвы, которому полагает находится на улице помимо термистора, расположенного на конце и погружаемого в грунт упоминался внутренний датчик датчик температуры, с которого не проблема (как оказалось позже, для STM32F030 проблема, его нет) снять показания, не применяя дополнительных компонентов и использовать для оценки состояния окружающей среды.
Алгоритм работы
Встроенный первичный преобразователь подключен внутри к каналу аналогово-цифрового преобразователя, то есть сначала нужно настроить АЦП микроконтроллера и получить значение (там напряжение будет около 0.76V в нормальных условиях).
А далее по выражению из документа вычисляется текущее значение температуры в градусах Цельсия, учитывая коэффициент Avg_Slope, указанный в листе данных (может отличатся для разных серий).
Проект в STM32CubeIDE
1) Новый проект:
2) Любое название:
3) Выбираем МК, у меня это черная пилюля:
4) Включаем отладчик: SYS->Debug: Serial Wire
5) Включаем внешнее тактирования RCC -> HSE Crystal/Ceramic Resonator
6) Частоту тактирования ( HCLK) ставим 84МГц:
7) Выбираем канал АЦП: Temperature Sensor Channel и Vrefint Channel
8) Во вкладке DMA настраиваем поток от периферии в память, циклически (Circular):
В настройках АЦП разрешаем постоянные запросы от АЦП, наибольшее время преобразования
Обновление от 06.10.2023 НАЧАЛО:
Скорее всего режим Continuous Conversion Mode должен быть выключен, чтобы именно таймер инициировал преобразования, а не они происходили один за одним.
Также на картинке была ошибка с ранками (по сути это порядковый номер преобразования), теперь порядок Vrefint и Temperature Sensor правильный (это можно было бы поправить в коде).
Adc.Raw[1] <- Channel Temperature Sensor (Rank 2)
Adc.Raw[0] <- Channel Vrefint (Rank 1)
Обратите внимание на то, что на картинке:
Rank 1 - Channel Temperature Sensor
Rank 2 - Channel Vrefint
А в коде все наоборот:
Adc.Raw[1] <- Channel Temperature Sensor (Rank 2)
Adc.Raw[0] <- Channel Vrefint (Rank 1)
Обновление от 06.10.2023 КОНЕЦ.
Также нужно прерывание по окончанию преобразования:
9) Настраиваем таймер на частоту 10 Гц (не принципиально):
f = HCLK_TIM3/(PSC-1)·(ARR-1) = 84000000/(840-1)·(10000-1) = 10 Hz
Библиотека
⏩ https://github.com/Egoruch/Internal-Temperature-Se...
Код
Алгоритм
Получение значениям с внутреннего канала
температурного датчика от АЦП |
-> |
Расчёт значение температуры
по формуле |
Программирование
Чтобы каждый раз не делать одно и то же, набросал простую библиотеку с одной функцией, в которую нужно скормить значение с АЦП от датчика температуры и канала питающего (опорного) напряжения АЦП (не обязательно) и в ответ получить температуру в градусах Цельсия (функция возвращает), поэтому сразу переходим к её использованию:
1) Добавляем библиотеку tmpsensor. Файл tmpsensor.c закидываем в Src, файл tmpsensor.h в Inc (папки Src и Inc находятся в папке Core)
2) Добавляем заголовочный файл библиотеки:
/* USER CODE BEGIN Includes */
#include "tmpsensor.h"
/* USER CODE END Includes */
3) Создаем структуру (для удобства) и в ней определяем массив Raw из двух элементов uint16_t для значений с АЦП (первый для значения с датчика, второй для значения питающего напряжения) и переменную с плавающей точкой IntSensTmp (типа doulbe) для значения температуры.
typedef struct AdcValues{
uint16_t Raw[2]; /* Raw values from ADC */
double IntSensTmp; /* Temperature */
}adcval_t;
adcval_t Adc;
4) Таким же образом через структуру определяем переменную-флаг, которая будет выставляться в положительное значения для определения окончания преобразования (чтобы не считать постоянно)
Обновление от 06.10.2023 НАЧАЛО:
typedef struct Flags
{
uint8_t ADCCMPLT;
}flag_t;
typedef struct AdcValues{
uint16_t Raw[2]; /* Raw values from ADC */
double IntSensTmp; /* Temperature */
}adcval_t;
adcval_t Adc;
Обновление от 06.10.2023 КОНЕЦ.
5) Запускаем преобразования с АЦП и таймер, который будет их инициировать:
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)Adc.Raw, 2);
HAL_TIM_Base_Start(&htim3); /* This timer starts ADC conversion */
/* USER CODE END 2 */
6) Запишем функцию обратного вызова прямо в файле main.c, которая вызывается после окончания преобразования АЦП (названия этой функции и других можно посмотреть в файле stm32f4xx_hal_adc.h).
Здесь значение переменной Flg.ADCCMPLT (ADC Complete) выставляется в положительное значение всякий раз, конца преобразование от АЦП1 оконечно:
/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1) /* Check if the interrupt comes from ACD1 */
{
/* Set flag to true */
Flg.ADCCMPLT = 255;
}
}
/* USER CODE END 4 */
7) В главном циклей проверяем флаг-переменную Flg.ADCCMPLT, если она положительная, то выполняем вычисление температуры, загоняя в качестве первого аргумента значение с канала АЦП, в а качестве второго с внутреннего источника опорного напряжения (последовательность преобразцовая настраивалась в Кубе).
/* USER CODE BEGIN WHILE */
while (1)
{
if (Flg.ADCCMPLT) /* Conversion completed, do calculations */
{
/* Temperature Sensor ADC-value, Reference Voltage ADC-value (if use) */
Adc.IntSensTmp = TMPSENSOR_getTemperature(Adc.Raw[1], Adc.Raw[0]);
Flg.ADCCMPLT = 0; /* Nullify flag */
}
/* USER CODE END WHILE */
8) Вот эта самая функция:
/**
* @brief Calculate temperature (tested on STM32F401, other MCU may have different constants!)
* @note If IntRef not use, set it [ex.: #define TMPSENSOR_USE_INTREF 0]
* @param Temperature sensor's ADC 16-bit value, Internal Reference ADC 16-bit value (if use)
* @retval Internal sensor temperature
*/
double TMPSENSOR_getTemperature(uint16_t adc_sensor, uint16_t adc_intref){
#if(TMPSENSOR_USE_INTREF)
double intref_vol = (TMPSENSOR_ADCMAX*TMPSENSOR_ADCVREFINT)/adc_intref;
#else
double intref_vol = TMPSENSOR_ADCREFVOL;
#endif
double sensor_vol = adc_sensor * intref_vol/TMPSENSOR_ADCMAX;
double sensor_tmp = (sensor_vol - TMPSENSOR_V25) *1000.0/TMPSENSOR_AVGSLOPE + 25.0;
return sensor_tmp;
}
Все постоянные величины (константы) занесены в tmpsensor.h файл и взяты из тех. документации (Datasheet) или справочного руководства (Reference Manual).
Проверка
1) Подключаем STLINK к микроконтроллеру и ПК.
2) Заходим в режим отладки:
3) Добавляем структуры (ранее созданные) Adc и Flg, раскрываем их чтобы смотреть значения. (Debug -> Run) Запускаем программу и смотрим какая температура у микросхемы.
Если что-то не работает, то смотрим на значения с АЦП, если их нет, что АЦП (Adc.Raw[0] и Adc.Raw[1]) не работает, перепроверяем.
Отображение переменной в STM32CubeMonitor
Ранее уже описано как пользоваться данным ПО STM32CubeMonitor, программулина красивая и самое главное – строит графики невероятно шустро (в отличии от SWO), считывая по сути через STLINK значение глобальной переменной по адрес из ОЗУ:
Стресс-испытание термофеном
Чтобы прогреть микросхему подключил китайский паяльный фен и регулятор мощности на SMD компонентах.
Выводы
Теперь несложно прикрутить к своему проекту грубую измерялку температуры, не, ну а что, может быть полезна для оценки температуры платы внутри корпуса. ? Жаль, конечно, что на серии 030 такого нет.
В STM32F030F есть: Импульсный паяльник «ШМЕЛЬ.F03» 12В/36Вт . Имеет цветной дисплей на котором отображаются: режимы работы, входное напряжение, температура компонентов печатной платы, выходная мощность во время пайки. t.me/SHMEL_03F
Спасибо, хорошая статья, только ляпы поправить бы.
структура флага описана не верно, копипаст от предыдущей видимо.
в настройках ADC, Rank1 неправильный канал стоит.
Спасибо!
Кусок кода с флагом исправлен.
Картинка с настройками АЦП исправлена.