Использование внутреннего датчик температуры 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 такого нет. 

2340
RSS
PAP.lab
12:55

В STM32F030F есть: Импульсный паяльник «ШМЕЛЬ.F03» 12В/36Вт .  Имеет цветной дисплей на котором отображаются: режимы работы, входное напряжение, температура компонентов печатной платы, выходная мощность во время пайки. t.me/SHMEL_03F

Алексей
18:01

Спасибо, хорошая статья, только ляпы поправить бы.

структура флага описана не верно, копипаст от предыдущей видимо.

в настройках ADC, Rank1 неправильный канал стоит.

19:57

Спасибо!

Кусок кода с флагом исправлен.

Картинка с настройками АЦП исправлена.

Загрузка...