Метеостанция на stm32
Комнатная метеостанция на STM32L-DISCOVERY
С детства мечтал о комнатном термометре, гигрометре и барометре (не прошли даром уроки природоведения и биологии).
Даже был куплен настенный вариант со стрелочными приборами советского образца типа такого:
Но по ошибке был повешен на створку двери и через некоторое время прийдя в негодность, от постоянных сотрясений, начал показывать одно и тоже значение. Психрометр пугал своим видом.
Да и записывать каждый день показания — глупая затея. Механические системы были похоронены на совсем с приходом контроллеров.
Долго присматривал различные отладочные платы. По совету знакомого купил я отладочную плату STM32L-Discovery от фирмы ST, подробное описание здесь.
Заманчиво звучало то, что это ARM на ядре Cortex-M3. Сердцем платы является STM32L152RBT6. Также на плате есть on-board программатор и отладчик ST-Link и шести сегментный LCD дисплей.
Посты Метеостанция своими руками, Термометр на SHT21 и наличие STM32L-Discovery вдохновили на реализацию проекта.
Немного освоившись в Keil загрузил вместо идущего на борту примера, другой пример Temperature project — и вуаля термометр уже готов. Микроконтроллер может измерять Vref. У него также есть свой собственный датчик температуры чипа.
Все бы хорошо но этот датчик показывает температуру кристалла, решил добавить датчик температуры DS18B20, да и one-wire интерфейс хорошо бы освоить.
Оказалась чтобы что либо добавить необходимо первым делом избавится от штатного LCD экрана, он со своими 6 символами занимает практически все свободные порты процессора.
В закромах родины завалялся старенький LCD экран собранный на контроллерах Hitachi (8 строк по 25 символов)В выше указанной статье упоминался цифровой датчик влажности воздуха HIH3610, но был приобретен емкостной датчик влажности HCH1000 и барометрический датчик HSF1000.
Итак организовались задачи по подключению оборудования: 1. Подключение и программирование LCD экрана; 2. Подключение и получение данных от RTL; 2. Подключение цифрового датчика температуры DS18B20 и чтение из него данных по шине one-wire; 3. Подключение емкостного датчика HCH1000 и получение данных; 4. Подключение пьезоэлектрического датчика HSF1000 и получение данных; Вот такое устройство вышло:
Подключение и программирование LCD экрана
Экземпляр попавший мне в руки оказался настолько старым что документации от него не нашлось. На нем были 4 чипа HD44102CH и 2 HD44102, и 4 дискретных микросхемы описание которых я не нашел.
Reference manual на HD44102 был найден, и было 8 ножек 4 микросхем соединенных между собой и выведенных на разъем — так нашлись D0-D7, питание было найдено по дискретным микросхемам.
Оставались сигналы RW,E,CS,R/S, В youtub был найден LCD модуль HLM9301 с виду очень похожий на мой LCD, итальянец на форуме www.lcdstudio.com дал распиновку которая совпадала с собранным мною априором:
1 GND; 2 VCC; 3 contraste ( generalmente terminal medio de potenciometro de 10k colocado entre vcc y gnd) 4 NC (no conectado); 5 NC; 6 CS1; 7 CS2; 8 CS3; 9 NC; 10 E; 11 R/W 12 R/S (DATA/INSTRUCTION) 13 D0; 14 D1; 15 D2; 16 D3 17 D4; 18 D5; 19 D6; 20 D7. Но при подаче команд матрица не проявляла признаков жизни. После долгого, не меньшего от предыдущего, поиска по интернетам выяснилось что старым графическим экранам необходимо было отрицательное напряжение для яркости. Был включен преобразователь DC-DC P6AU0505 и между выводом яркости и -5 установлен прецизионный переменный резистор 200кОм. Команды от HD44102 подошли. Была написана библиотека работы с HLM9301. На форумах ребята утверждали что с Arduino все работало сразу со стандартной библиотекой GLCD.Видео демонстрирует прочитанные данные из внутренних RTC и термометра.
Подключение и получение данных от RTL
Инициализация RTC происходит так:RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_SYSCFG, ENABLE);
PWR_RTCAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON); //do not touch LSE to prevent RTC calendar reset
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {}
RCC_RTCCLKCmd(ENABLE);
Проблема: при сбросе сбрасывался и RTC. Фрагмент кода взятый из демонстрационного примера что-то делал с LSE — этим и сбрасывался RTC. Чтение данных:RTC_DateTypeDef RTCDateStr;
RTC_TimeTypeDef RTCTimeStr;
RTC_GetTime(RTC_Format_BIN, &RTCTimeStr);
RTC_GetDate(RTC_Format_BIN, &RTCDateStr);
sprintf(strDisp, “%02d/%02d/%02d %02d:%02d:%02d”, RTCDateStr.RTC_Year, RTCDateStr.RTC_Month, RTCDateStr.RTC_Date, RTCTimeStr.RTC_Hours, RTCTimeStr.RTC_Minutes, RTCTimeStr.RTC_Seconds);
Тут то получились две новые задачи «или»: в цепь питания микросхемы включить ионистор и при падении напряжения питания переходить в «спящий режим», или прицепить внешний RTC. Думаю попробовать оба метода…
Подключение цифрового датчика температуры DS18B20
Благодаря статьям Stm32 + 1-wire + DMA (продолжение) и Stm32 + 1-wire + DMA добавлена библиотека onewire.c но для процессора STM32L152 инициализация портов выглядит немного по-другому:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.
GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
GPIO_InitStructure.
GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
Схема подключения взята из datasheet:
При помощи статьи «Поиск устройств» была добавлена возможность подключения нескольких устройств на шину one-wire.
Разрешение у DS18B20 при считывании всех 12 бит 0,0625 градуса по Цельсию.
Подключение емкостного датчика HCH1000
Измерять емкость можно по-разному, самый простой метод зарядить и следить за падением напряжения, посчитав время вычислить емкость, либо по сопротивлению переменному току оценивать емкость. Honeywell любезно предоставило datasheet в котором датчик был задающей величиной в генераторе на 555.
К последнему методу я и прибег собрав простой генератор:
Вычислить частоту оказалось не трудно STM32L152 имеет несколько таймеров которые могут работать в режиме захвата параметров PWM сигнала. Подробо здесь. Отличием оказалось, как и в случае с one-wire конфигурация портов:GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.
GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.
GPIO_Speed = GPIO_Speed_40MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
В остальном все по тексту в прерывании вычитываем значения счетчиков, получили длину периода, умножили на коэффициент есть емкость, от емкости согласно графиков датчика перешли к влажности.
Подключение пьезоэлектрического датчика HSF1000
Подключил датчик на Vref, GND и ко входу АЦП. Опыт показал, что точности 12 разрядного АЦП оказалось мало чтоб оценить полезный сигнал. Подключение инструментального усилителя AD8555 по стандартной схеме к датчику дало свои плоды. Усиления в 10 раз вполне хватило чтоб поднять уровень сигнала до 0,7В.
Вот главный экран устройствазначения по строкам: 1. дата время из внутреннего RTL; 2. скважность и период сигнала с генератора, также количество найденных устройств one-wire; 3. емкость датчика влажности и влажность; 4. идентификатор one-wire; 5. значение температуры; 6. значение напряжения; 7. значение температуры кристала; 8.
значение давления.
Исходники проекта здесь
Источник: https://habr.com/post/153395/
STM32L-DISCOVERY – метеостанция
С детства мечтал о комнатном термометре, гигрометре и барометре (не прошли даром уроки природоведения и биологии). Даже был куплен настенный вариант со стрелочными приборами советского образца типа такого:
Но по ошибке был повешен на створку двери и через некоторое время прийдя в негодность, от постоянных сотрясений, начал показывать одно и тоже значение.
Психрометр пугал своим видом. Да и записывать каждый день показания — глупая затея. Механические системы были похоронены на совсем с приходом контроллеров. Было несколько разработок на Atmel S51, внешнем АЦП и терморезисторе, дальше на Microchip PIC16F86, программирование этих устройств было без отладочных плат, чуда программирования было мало.
Были какие-то пытки и мучения: травил платы, делал программаторы, писал в текстовом, компилировал командной строкой, прошивал 3-м средством, постоянно переставляешь микросхему с программатора в устройство. Словом Ад. Быстро надоедает, при этом на работе все в рамках одного IDE, решаешь непростые задачи, никуда не отвлекаешься.
Долго присматривал разные отладочные платы, цена кусалась, как для хобби. По совету знакомого купил я отладочную плату STM32L-Discovery от фирмы ST, подробное описание . Она в виду своей слабой популярности не дорога, распиаренный Arduino выходит дороже (для Киева). Заманчиво звучало еще то, что это ARM на ядре Cortex-M3. Сердцем платы является STM32L152RBT6.
Также на плате есть on-board программатор и отладчик ST-Link.
Благодаря (спасибо автору) и наличии STM32L-Discovery приступил к реализации.
Немного освоившись в Keil загрузил вместо идущего на борту примера Temperature project — и вуаля термометр уже готов. Микроконтроллер может измерять Vref. У микроконтроллера также есть свой собственный датчик температуры чипа.
Все бы хорошо но он показывает температуру кристалла, решил добавить датчик температуры DS18B20, да и one-wire интерфейс хорошо бы освоить. Оказалась чтобы что либо добавить необходимо первым делом избавится от штатного LCD экрана, он со своими 6 символами занимает практически все свободные порты процессора.
В закромах родины завалялся старенький LCD экран собранный на контроллерах Hitachi (8 строк по 25 символов)В выше указанной статье упоминался датчик влажности воздуха HIH3610, хороший цифровой датчик, но весьма не бюджетный. Был приобретен на порядок дешевле датчик влажности HCH1000 и барометрический датчик HSF1000. HCH1000 это емкостной датчик влажности.
HSF1000 это мостовой резистивный пьезоэлектрический датчик абсолютного давления. Итак организовались задачи по подключению оборудования: 1. Подключение и программирование LCD экрана; 2. Подключение и получение данных от RTL; 2. Подключение цифрового датчика температуры DS18B20 и чтение из него данных по шине one-wire; 3.
Подключение емкостного датчика HCH1000 и получение данных; 4. Подключение пьезоэлектрического датчика HSF1000 и получение данных; Вот такое устройство вышло:
Читайте также: Usb мышка на базе акселерометра
Подключение и программирование LCD экрана:
Экземпляр попавший мне в руки оказался настолько старым что документации от него не нашлось. На нем были 4 чипа HD44102CH и 2 HD44102, и 4 дискретных микросхемы описание которых я не нашел.
Reference manual на HD44102 был найден, и было 8 ножек 4 микросхем соединенных между собой и выведенных на разъем — так нашлись D0-D7, питание было найдено по дискретным микросхемам.
Оставались сигналы RW,E,CS,R/S, В youtub был найден LCD модуль с виду очень похожий на мой HLM9301 LCD, итальянец на форуме дал распиновку которая совпадала с собранным мною априором: 1 GND 2 VCC 3 contraste ( generalmente terminal medio de potenciometro de 10k colocado entre vcc y gnd) 4 NC (no conectado) 5 NC 6 CS1 7 CS2 8 CS3 9 NC 10 E 11 R/W 12 R/S (DATA/INSTRUCTION) 13 D0 14 D1 15 D2 16 D3 17 D4 18 D5 19 D6 20 D7 Но при подаче команд матрица не проявляла признаков жизни. После долгого, не меньшего от предыдущего, поиска по интернетам выяснилось что старым графическим экранам необходимо было отрицательное напряжение для яркости. Был включен преобразователь DC-DC P6AU0505 и между выводом яркости и -5 установлен прецизионный переменный резистор 200кОм. Команды от HD44102 подошли. Была написана библиотека работы с HLM9301. На форумах ребята утверждали что с Arduino все работало сразу со стандартной библиотекой GLCD. Видео демонстрирует и вычитанные данные из внутренних RTC и термометра.
Подключение и получение данных от RTL
Инициализация RTC происходит так: RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_SYSCFG, ENABLE); PWR_RTCAccessCmd(ENABLE); RCC_LSEConfig(RCC_LSE_ON); //do not touch LSE to prevent RTC calendar reset while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {} RCC_RTCCLKCmd(ENABLE); Проблема: при сбросе сбрасывался и RTC.
Фрагмент кода взятый из демонстрационного примера что-то делал с LSE — этим и сбрасывался RTC. Чтение данных: RTC_DateTypeDef RTCDateStr; RTC_TimeTypeDef RTCTimeStr; RTC_GetTime(RTC_Format_BIN, &RTCTimeStr); RTC_GetDate(RTC_Format_BIN, &RTCDateStr); sprintf(strDisp, “%02d/%02d/%02d %02d:%02d:%02d”, RTCDateStr.RTC_Year, RTCDateStr.
RTC_Month, RTCDateStr.RTC_Date, RTCTimeStr.RTC_Hours, RTCTimeStr.RTC_Minutes, RTCTimeStr.RTC_Seconds); Тут то получились две новые задачи «или»: в цепь питания микросхемы включить ионистор и при падении напряжения питания переходить в «спящий режим», или прицепить внешний RTC.
Думаю попробовать оба метода…
Подключение цифрового датчика температуры DS18B20
Благодаря статьям и добавлена библиотека onewire.c но для процессора STM32L152 инициализация портов выглядит немного по-другому:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); Схема подключения взята из datasheet
Подключение емкостного датчика HCH1000
Измерять емкость можно по-разному, самый простой метод зарядить и следить за падением напряжения, посчитав время вычислить емкость, либо по сопротивлению переменному току оценивать емкость. Honeywell любезно предоставило datasheet в котором датчик был задающей величиной в генераторе на 555.
К последнему методу я и прибег собрав простой генератор:
Вычислить частоту оказалось не трудно STM32L152 имеет несколько таймеров которые могут работать в режиме захвата параметров PWM сигнала. Подробо . Отличием оказалось, как и в случае с one-wire конфигурация портов: GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.
GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.
GPIO_Speed = GPIO_Speed_40MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2); в остальном все по тексту в прерывании вычитываем значения счетчиков, получили длину периода, умножили на коэффициент есть емкость, от емкости согласно графиков датчика перешли к влажности.
Подключение пьезоэлектрического датчика HSF1000
Подключил датчик на Vref,GND и ко входу АЦП. опыт показал, что точности 12 разрядного АЦП оказалось мало чтоб оценить полезный сигнал. Подключение инструментального усилителя AD8555 по стандартной схеме к датчику дало свои плоды. Усиления в 10 раз вполне хватило чтоб поднять уровень сигнала до 0,7В.
Источник: http://robocraft.ru/blog/1033.html
STM32.Встроенный датчик температуры
Микроконтроллер STM32F303VCT6 имеет в своем составе встроенный термометр. Данный термометр подключен к АЦП микроконтроллера через 16 канал.
Работать с данным датчиком очень просто, особенно при использовании библиотеки SPL. Для получения текущей температуры необходимо:
- Включить термометр
- Настроить канал 16 АЦП
- Считать значение напряжения термометра при помощи АЦП
- Рассчитать текущую температуру
Термометр включается командой ADC_TempSensorCmd(ADC1,ENABLE).
О том как настроить АЦП, и считать напряжение можете узнать здесь.
Расчет температуры ведется по следующей формуле:
t = ((V25-V)/VSlope)+25
Где,
V — Текущие показания термометра в вольтах.
V25 — Показания термометра при 25 Градусах в вольтах. Данное значение берется из документации на соответствующий контроллер. Для STM32F303VCT6 равно 1.43 Вольта.
VSlope— Изменение напряжения термометра, при изменении температуры на градус Цельсия. Так же берется из документации. Для STM32F303VCT6 равно 4.3 мВ/Градус.
Пример работы с данным датчиком:
#include “stm32f30x_gpio.h”#include “stm32f30x_adc.h”RCC_HSEConfig(RCC_HSE_ON); //Enable HSEwhile(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET) ; //Waiting for HSEFLASH->ACR |= FLASH_ACR_PRFTBE;FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);FLASH->ACR |= (uint32_t)((uint8_t)0x02);RCC_PREDIV1Config(RCC_PREDIV1_Div1);//PREDIV 1 Divider = 1RCC_PLLConfig(RCC_PLLSource_PREDIV1,RCC_PLLMul_9);//Set PREDIV1 as source for PLL,And set PLLMUL=9RCC_PLLCmd(ENABLE);//Enable PLLwhile(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) ;//Waiting for PLLRCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//Set PLL as SYSCLK SoucreRCC_HSICmd(DISABLE);//Disable HSIRCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div2);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC,ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12,ENABLE);ADC_TempSensorCmd(ADC1,ENABLE);PORTC.GPIO_Mode = GPIO_Mode_AN;PORTC.GPIO_Pin = GPIO_Pin_1;PORTC.GPIO_Speed = GPIO_Speed_2MHz;ADC_VoltageRegulatorCmd(ADC1,ENABLE);ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single);ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) != RESET );ADC_GetCalibrationValue(ADC1);adc1.ADC_AutoInjMode = ADC_AutoInjec_Disable;adc1.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable;adc1.ADC_DataAlign = ADC_DataAlign_Right;adc1.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_0;adc1.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;adc1.ADC_NbrOfRegChannel = 1;adc1.ADC_OverrunMode = ADC_OverrunMode_Disable;adc1.ADC_Resolution = ADC_Resolution_12b;ADC_RegularChannelConfig(ADC1,ADC_Channel_16,1,ADC_SampleTime_601Cycles5);NVIC_EnableIRQ(ADC1_2_IRQn);ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));ADC_StartConversion(ADC1);void ADC1_2_IRQHandler(void)if(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))uint16_t result = ADC_GetConversionValue(ADC1);float v = result*0.000732421875;temp = (1.43-v)/0.0043+25.0; |
О функции InitRCC можете почитать здесь.
Читайте также GPS маячок. STM32
Запустив отладку видим:
Спасибо за внимание! На сегодня всё.
Источник: http://mkprog.ru/mikrokontrollery-stm32/stm32-vstroennyj-datchik-temperatury.html
Режимы пониженного энергопотребления в STM32L
Одной из распространенных разновидностей современных встроенных систем стали устройства с автономным питанием.
Главным требованием к таким изделиям выступает минимизация потребления электрического тока, с целью продления срока службы батарей и следовательно, непрерывной работы конструкции.
Реализация режимов пониженного энергопотребления давно стала одним из факторов, которым производители привлекают внимание к своей продукции. Не является исключением и STMicroelectronics. Наиболее интересными изделиями в этом направлении – микроконтроллеры серии STM32L.
В современных микроконтроллерах возможно два направления реализации пониженного энергопотребления. Первое из них заключается в уменьшении потребляемого тока в рабочем режиме.
Достигается это с помощью проектирования и производства специализированных устройств, способных снижать производительность или отключать все неиспользуемые модули в рабочем режиме. Суть второго направления в полном или частичном останове процессора на период простоя.
Оба эти способа нашли применение в микроконтроллерах STM32. Наряду с общей серией микросхем, разработчики создали и вариант с пониженным энергопотреблением, обозначаемый STM32L .
В этом процессоре удалось реализовать большое количество разнообразных режимов работы, что позволяет подобрать наиболее оптимальный, с точки зрения расхода энергии в конкретном проекте.
Энергопотребление микроконтроллеров STM32L в рабочем режиме зависит от напряжения питания процессорного ядра и тактовой частоты, на которой это ядро работает. Соответственно, уменьшив до необходимого уровня частоту тактового генератора, можно ожидать и снижения уровня потребляемого тока.
Отдельно следует упомянуть об источниках тактовой частоты. Самым энергозатратным оказывается внешний тактовый резонатор (HSE). Гораздо меньше потребляют встроенный генератор на 16МГц (HSI), и программируемый генератор (MSI).
Учитывая, что последний включается после сброса, а также после выхода из любого «спящего» режима, то в приложениях, критичных к потребляемому току следует использовать именно его, если конечно не требуется большое быстродействие.
В зависимости от используемой тактовой частоты выбирается и напряжение ядра микроконтроллера, что позволяет получить еще более низкие значения потребляемого тока.
Уровень напряжения | Максимальная тактовая частота | |||
MSI | HSI | HSE | PLL | |
1.8 В | 4.2 МГц | 16 МГц | 32 МГц от внешнего источника24 МГЦ от кристалла | 32 МГц(PLLVCOmax =96МГц) |
1.5 В | 4.2 МГц | 16 МГц | 16 МГц | 16 МГц(PLLVCOmax =48 МГц) |
1.2 В | 4.2 МГц | Нет | 8 МГц | 4 МГц(PLLVCOmax =24 МГц) |
Режимы пониженного энергопотребления
Наряду с возможностью управления напряжением ядра и тактовой частотой, в микроконтроллерах STM32L имеется пять режимов пониженного энергопотребления.
Некоторые из этих режимов присутствуют и в микроконтроллерах других серий. Все доступные варианты с базовыми цифрами энергопотребления сведены в таблице.
Следует учесть, что использование периферийных модулей в рабочем и «спящих режимах» отразиться и на значении потребляемого тока.
Режим | I пот. | CPU | Flash | RAM | DMA/Periph | Тактирование | RTC | LCD |
Sleep | 100uA/МГц(Range 1) | Остановлен | Вкл | Вкл | Вкл | Любой источник | Доступны | |
82uA/МГц(Range 2) | ||||||||
65uA/МГц(Range 3) | ||||||||
Low Power RUN | 10.4uA(Flash Выкл, 32 КГц) | Работает | Выклили Вкл. | Вкл | Вкл | MSI | Доступны | |
Low Power Sleep | 5.1uA(периферия выкл.) | Остановлен | Выкл. | Вкл | Вкл | MSI | Доступны | |
6.1uA(1 таймер вкл.) | ||||||||
Stop c RTC | 1.3uA(1.8 В) | Остановлен | Выкл. | Вкл | Остановлен | LSE, LSI | Доступны | |
1.6uA(3 В) | ||||||||
Stop | 500нА | Остановлен | Выкл. | Вкл | Остановлен | LSE, LSI | Выкл. | Выкл. |
Standby c RTC | 1.3uA(3 В) | Выкл | Выкл. | Выкл. | Выкл. | LSE, LSI | Выкл. | Вкл. |
1uA(1.8 В) | ||||||||
Standby | 270нА | Выкл. | Выкл. | Выкл. | Выкл. | LSE, LSI | Выкл. | Выкл. |
Читайте также: Конденсаторы. кодовая маркировка
Low Power RUN
В режиме Low Power RUN работа процессора не приостанавливается. При этом производится только снижение напряжения питания ядра и ограничение тактовой частоты. Уровень потребления в данном режиме зависит от количества задействованных периферийных устройств и тактовой частоты, на которой работает процессор.
Для входа в рабочий режим с пониженным энергопотреблением следует отключить всю неиспользуемую периферию путем настройки регистров RCC_APBxENR и
RCC_AHBENR. Частота системного тактового сигнала должна быть снижена до уровня или ниже 4.2МГц (MSI range1). Непосредственно включение режима выполняется настройкой регулятора напряжения с помощью включения битов LPRUN и LPSDSR.
Выход из рабочего режима с низким энергопотреблением производится переводом регулятора в стандартный режим и дальнейшей необходимой настройкой системы.
Sleep
В спящем режиме, прежде всего, производится остановка процессора. Все периферийные модули при этом могут продолжать работать. Время пробуждения из данного режима оказывается самым коротким, по сравнению с другими вариантами.
Уровень потребления энергии зависит, как и в предыдущем режиме, от количества включенных периферийных модулей и напряжения ядра. Источник тактового сигнала при этом может быть любым.
Состояние линий вывода в режиме Sleep не изменяется по сравнению с рабочим состоянием.
Вход в режим Sleep, как и во все другие «спящие» режимы, выполняется с помощью процессорных команд WFI или WFE. Команда WFI (Wait For Interrupt) останавливает процессор и настраивает его включение при возникновении прерывания.
WFE (Wait for Event) делает то же самое, но выход из «спящего» режим производится по событию.
В зависимости от состояния бита SLEEPONEXIT, вход в спящий режим может выполняться немедленно (SLEEPONEXIT=0), либо по достижению наименьшего значения приоритета ISR (SLEEPONEXIT=1).
Выход из спящего режим после срабатывания команды WFI выполняется по любому прерыванию, разрешенному в контроллере NVIC.
При использовании команды WFE, выход производится, если разрешен запрос прерываний в периферийных устройствах, но запрещен в NVIC (При этом должен быть установлен бит SEVONPEND), либо контроллер EXTI настроен на работу с событиями.
Для нормального продолжения работы, после пробуждения необходим сброс битов ожидания прерывания, как в периферийном устройстве, так и в контроллере прерываний.
Low Power Sleep
Главное отличие спящего режима с низким энергопотреблением от простого спящего режима (Sleep) заключается в источнике тактовой частоты.
Фактически Low Power Sleep является частным случаем, в котором используется внутренний тактовый генератор MSI. Вход в режим выполняется, если MSI работает в диапазоне 1 (range 1), а регулятор напряжения в диапазоне 2 (Range2).
В данном режиме отключается Flash-память, но ОЗУ остается в работе. В остальном режим аналогичен простому Sleep.
Stop
Останов процессора является базовым режимом ядра Cortex-M3, поэтому присутствует во всех микроконтроллерах. Он накладывает большие ограничения на работу внутренних схем, вследствие чего является наименее энергозатратным, из «спящих» режимов.
Так, в данном режиме, производится остановка практически всей периферии микроконтроллера, а также любого основного тактового генератора. Вариантом режима Stop является случай, когда в работе остается модуль часов реального времени с соответствующим источником частоты.
Для обеспечения минимального энергопотребления в данном режиме рекомендуется настройка внутреннего регулятора напряжения, а также отключение систем Vrefnt, BOR, PVD, ADC, DAC и встроенного датчика температуры.
Состояние линий ввода вывода при переходе в режим STOP не изменяется, содержимое памяти и регистров сохраняется.
Для перевода в режим останова необходимо выполнить следующие действия:
- Установить бит SLEEPDIP в регистре SCB
- Очистить бит PDDS в регистре PWR_CR
- Очистить бит WUF в регистре PWR_CSR
- Настроить регулятор напряжения LPSDSR в регистре PWR_CR
- Настроить событие или прерывание выхода из режима STOP
- Подать команду WFI или WFE
Stadby
Режим ожидания стоит несколько особняком от других вариантов пониженного энергопотребления по причине отключения процессора и памяти. Фактически данный режим равноценен состоянию сброса.
Содержимое памяти и всех регистров за исключением PWR_CSR теряется.
Все порты ввода/вывода переводятся в высокоимпедансное состояние за исключением следующих линий, отвечающих за пробуждение процессора:
- вход сброса
- RTC_AF1 (PC13), если он сконфигурирован как вход для функций Wakeup (WKUP2), защиты данных (tamper), захвата времени (time-stamp), или как выход тревоги по таймеру (RTC Alarm), или тактовый выход RTC clock.
- PA0 и PE6, если они сконфигурированы в качестве входов сигнала пробуждения WKUP.
Выход из режима ожидания выполняется при поступлении сигналов сброса, внешних сигналов пробуждения (WKUP), событий часов реального времени, событий несанкционированного доступа к данным или записи временной метки.
Для перевода в режим останова необходимо выполнить следующие действия:
- Установить бит SLEEPDIP в регистре SCB
- Включить бит PDDS в регистре PWR_CR
- Очистить бит WUF в регистре PWR_CSR
- Очистить флаги источников пробуждения
- Подать команду WFI или WFE
You have no rights to post comments
Источник: https://mcucpu.ru/index.php/platformy-32-bit/stm32/156-rezhimy-ponizhennogo-energopotrebleniya-v-stm32l
PWM на STM32
Описание того, как сделать ШИМ не сделал разве что ленивый. И когда возникла необходимость сделать это я начитался и тут же словил немало фана на этот счет.
Прежде всего из-за невнимательности. По этому и решил написать этот постик.
Итак, мне нужен PWM для управления ESC – контроллером скорости для бесколлекторных двигателей.
В принципе все просто, как гласит каждая статья – в stm32 есть множество аппаратных таймеров, почти каждый из которых пригоден для аппаратного PWM.
Как их инициализировать – позже. Сначала о смысле слова “таймер” самым ламмерским языком.
Схема тактирования в STM32 достаточно сложна:
Поясню эту картинку:
- У меня выбран внешний кварц частотой 8Мгц (HSE)
- Далее путем магических констант делителей и умножителей частоты N, P, Q я получаю референсную частоту системного таймера SYSCLK в 168Мгц и 48Мгц для шины SDIO и прочее.
- Затем с помощью AHB Prescaler, APB Prescaler APB1 Prescaler я получил частоты на шинах:
APB1 Bus – 42MHz, APB1 Timers – 84 MHz, APB2 bud – 84MHz, APB2 Timers – 168 MHz.
Последний пункт – самое важное – при расчете делителя и периода нужно сделать следующее.
- Выбираем таймер. Например TIM1.
- Смотрим на какой шине он находится – APB2.
- Видим, что в нашем случае таймер работает на частоте 168МГц. – при другой конфигурации это будет совсем другая частота.
Поясню, это значит, что таймер тикает 168млн раз в секунду.
- Например, нам нужна частота PWM 400Гц – это значит, что период нашего сигнала должен быть 2.5 мс.
- Важно выбрать «разрешение» сигнала: многие современные ESC с улучшенными прошивками понимают 255, 512, а то и 1024 уровня сигнала, а это значит, что в эти 2.
5 мс нам надо заложить: минимальное заполнение, 1024значащих уровня и максимальное заполнение.
Что это значит я попробую пояснить картинкой…Min – если заполненность меньше этой величины – то ESC подумает что сигнала нет и будет пищать. То есть это наш 0%.
Max – тоже самое, но тут – если ESC не поймет если заполненность будет больше чем это значение. Это наши 100%.Следовательно, нам надо в интервал Max – Min впихнуть еще наше разрешение. Например, 1024 градации.Чему равно мин и макс – я не знаю, на примере Turnigy Plush 30A c прошивкой BLHeli это порядка 0.95мс и 2.0мс при ширине импульса в 2.5мс.
То есть 950мкс – это минимум + 1024 (ширины) мы получили 1975мкс (округлим до 2000мкс) при ширине в 2500мкс. Это подсказка – примем микросекунду за тик и это нас устроит.
Собственно, мы определили такой важный параметр как период – 2500.
- Что теперь с периодом делать – надо обеспечить так, чтобы один тик таймера происходил в одну микросекунду. Это очень легко сделать, учитывая, что таймер тикает 168млн раз в секунду – это значит, что нам надо снизить частоту в 168 раз, тогда он будет тикать млн раз в секунду (то есть раз в мкс). Prescaler = 168.
Теперь перейдем к коду:
Включаем тактирование устройст (TIM1 и GPIOE).
1: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); 2: RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
Настраиваем порт на работу с таймером:
1: GPIO_InitTypeDef port; 2: GPIO_StructInit(&port); 3: port.GPIO_Mode = GPIO_Mode_AF; 4: port.GPIO_OType = GPIO_OType_PP; 5: port.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14; 6: port.GPIO_Speed = GPIO_Speed_25MHz; 7: GPIO_Init(GPIOE,&port); 8: 9: GPIO_PinAFConfig(GPIOE,GPIO_PinSource9, GPIO_AF_TIM1); 10: GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_TIM1); 11: GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_TIM1); 12: GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_TIM1);Настраиваем таймер:
1: TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 2: TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 3: TIM_TimeBaseStructure.TIM_Prescaler = 168 – 1; 4: TIM_TimeBaseStructure.TIM_Period = 2500 – 1; 5: TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 6: TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 7: TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);Обратите внимание на –1 в делителе и периоде. Дело в том, что у них счет начинается с 0.
Теперь мы настраиваем компаратор таймера.
1: TIM_OCInitTypeDef TIM_OCInitStructure; 2: TIM_OCStructInit(&TIM_OCInitStructure); 3: TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; 4: TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 5: TIM_OCInitStructure.TIM_Pulse = 0; 6: TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; 7: TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;И включаем его на канале 1.
1: TIM_OC1Init(TIM1, &TIM_OCInitStructure); 2: TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);Эти строки инициируют этот режим компаратора на первом канале таймера.
Ну и наконец стартуем таймер в режиме PWM.
1: // Enable the timer 2: TIM_Cmd(TIM1, ENABLE); 3: // Enable the timer PWM outputs 4: TIM_CtrlPWMOutputs(TIM1, ENABLE)
Темерь для того, что бы выставить х% скважности на канале нам нужно сделать следующее:
1: TIM_SetCompare1(TIM1, map(m1, 0, 100, 950, 2000));В общем главное – это не забывать, что частоты в стмке бывают разные и у каждого устройства свои.
Источник: http://blog.eiva.info/2014/10/pwm-stm32.html
STM32 режимы пониженного энергопотребления
Сначала хотелось бы пару слов сказать о питающих напряжениях микроконтроллера, для нормальной работы STM32 необходимо от 2 до 3.6V, а уже встроенный стабилизатор обеспечивает 1.8 вольта для питания ядра. Ниже схематично изображена общая схема питания контроллера.
Как видно, схема питания разделена на домены и каждый из них отвечает за питание определённой периферии/части МК.
Уменьшить потребление МК можно несколькими способами, самое простое, что можно сделать — это понизить тактовую частоту или отключать шины APB и AHB, когда они не используются.
Другой вариант, который требует чуть больше знаний — это использовать режимы низкого энергопотребления, кстати, их всего три:
- sleep режим
- stop режим
- standby режим
Перед тем как начать описывать режимы хотелось бы отметить, что переход в любой из режимов пониженного энергопотребления осуществляется с помощью ассемблерных инструкций WFI и WFE, их отличие заключается в том, что если контроллер был отправлен в режим пониженного энергопотребления с помощью WFI(wait for interrupt), то выйти из этого режима он может только по прерыванию, если с помощью WFE(wait for event), то выйти из этого режима он может по наступлению события. Хотелось бы напомнить, что событие — это периферийное прерывание, которое не было разрешено в NVIC.
Для режима sleep характерно следующее, прекращается выполнение кода программы, прекращается тактирование ядра, периферия работает, все вывода сохраняют своё состояние.
Вход в режим sleep осуществляется с помощью инструкций WFE и WFI, когда:
- сброшен бит SLEEPDEEP
- выбрано значение бита SLEEPONEXIT
Что касается SLEEPONEXIT , его состояние определяет поведение контроллера при выходе из режима sleep по прерыванию(WFI), если бит сброшен, то контроллер проснётся, отработает прерывание и продолжит, как обычно, исполнять код, но если SLEEPONEXIT установлен, то после пробуждения, отработав прерывание, контроллер снова уснёт. Это позволяет создавать устройства с минимальным потреблением, работающие на прерываниях.
Выход из режима sleep по событию(WFE), позволяет начать выполнение программы с того места, где она была остановлена.
Источником события может быть:
- прерывание в регистре управления периферией, но не в NVIC или установка бита SEVONPEND в регистре управления ядром
- появление сигнала на линии внешних прерываний, когда она сконфигурирована в режиме событий.
Но совсем необязательно, что сигнал будет поступать извне, например, EXTI содержит линию, по которой может приходить запрос прерывания от часов реального времени( RTC)
Выход из режима sleep по прерыванию(WFI), может быть спровоцирован любым прерыванием в NVIC.
Что касается потребления, то тут надо смотреть документацию на конкретный камень, например, минимальное потребление STM32F103VE составляет 0.45mA.
Режим stop характерен остановкой PLL, HSI и HSE генераторов, при этом все пины ввода-вывода сохраняют своё состояние.
Вход в stop режим осуществляется с помощью инструкций WFE и WFI, когда:
- установлен бит SLEEPDEEP
- cброшен бит PDDS в регистре PWR_CR
- выбран режим стабилизатора напряжения(тот о котором упоминалось в начале статьи) битом LPDS в регистре PWR_CR
Выход из режима stop в обоих случаях(WFI и WFE) инициирует сигнал на любой из линий EXTI, только в одном случае сконфигурированной в режиме прерывания, в другом — в режиме события.
Что касается энергопотребления, то в этом случае оно составляет десятки микроампер и зависит от режима стабилизатора напряжения.
Standby режим характеризуется самым низким потреблением, около 2мкА и потерей всех данных, то есть при выходе из этого режима контроллер будет вести себя также как при нажатии на кнопку Reset.
Вход в standby режим осуществляется с помощью инструкций WFE и WFI, когда:
- установлен бит SLEEPDEEP
- установлен бит PDDS в регистре PWR_CR
- сброшен бит WUF, в регистре PWR_CSR
Выход из standby режима осуществляется:
- по нарастающему фронту на пине WKUP(A0), который для этого необходимо сконфигурировать специальным образом
- по нарастающему фронту будильника RTC
- внешнему сбросу(пин NRST)
- сбросу IWGT
О том что контроллер вышел из standby режима говорит установленный флаг SBF. В standby режиме все выводы находятся в высокоимпедансном состоянии кроме NRST, WKUP(если сконфигурирован), Tamper(если сконфигурирован).
Для наглядности оставлю здесь эту таблицу и поясню, что в столбце entry описаны биты которые надо сконфигурировать, а не установить.
Отладку в режимах пониженного энергопотребления можно разрешить, установив соответствующие биты в регистре DBGMCU_CR.
DBGMCU->CR |= DBGMCU_CR_DBG_SLEEP; DBGMCU->CR |= DBGMCU_CR_DBG_STOP; DBGMCU->CR |= DBGMCU_CR_DBG_STANDBY;
Регистр управления питанием(PWR_CR).
Биты 31:9 – зарезервированы, заполнены нулями.
DBP(Disable backup domain write protection) — 1 в этом бите разрешает доступ к регистрам резервного копирования и RTC, 0 – запрещает.
PLS[2:0](PVD level selection) — задают уровень порога срабатывания детектора напряжения.
PVDE(Power voltage detector enable) — 1 в этом бите включает PVD, 0 – отключает.
CSBF(Clear standby flag) — всегда читается как ноль, установка 1 в этот бит очищает SBF(StandbyFlag).
CWUF(Clear wakeup flag) — всегда читается как ноль, установка 1 в этот бит очищает WUF(Wakeup Flag), но происходит это спустя два такта системного генератора.
PDDS(Power down deepsleep) — когда контроллер находится в режиме глубокого сна(Deepsleep), запись 1 в этот бит переводит его в standby режим, при записи 0 — в stop режим, статус стабилизатора напряжения зависит от LPDS, это относится только к stop режиму.
LPDS(Low-power deepsleep) — 0 в этом бите включает стабилизатор напряжения в stop режиме, 1 — переводит стабилизатор в режим пониженного энергопотребления в stop режиме.
Регистр управления/статуса (PWR_CSR)
Биты 31:9 – зарезервированы, заполнены нулями.
EWUP(Enable WKUP pin) — 1 в этом бите разрешает использовать пин WKUP для пробуждения микроконтроллера по нарастающему фронту, при установленном 0 — запрещает, в этом случае пин WKUP используется как обычный GPIO. Автоматически сбрасывается после ресета.
Биты 7:3 – зарезервированы, заполнены нулями.
PVDO(PVD output) — этот бит изменяется аппаратно и имеет силу, только когда установлен бит PVDE. Единица в этом бите означает, что VDD/VDDA опустилось ниже порогового значения, выбранного битами PLS[2:0], 0 — VDD/VDDA выше порогового значения, выбранного битами PLS[2:0].
SBF(Standby flag) — этот бит устанавливается аппаратно, а сбрасывается с помощью POR/PDR(power on reset/power down reset — при включение/выключение питания) или установкой бита CWUF в регистре PWR_CR , 1 в этом бите говорит о том, что контроллер был в режиме standby.
WUF(Wakeup flag) — этот бит устанавливается аппаратно, а сбрасывается с помощью POR/PDR(power on reset/power down reset — при включение/выключение питания) или установкой бита CWUF в регистре PWR_CR, 1 в этом бите говорит о том, что пробуждающее событие пришло с пина WKUP или от будильника RTC.
Узнать какой вывод реализует функцию WKUP, можно в программе STM32CUBE.
Ну и в заключение код, позволяющий входить в режим standby и выходить из него по переднему фронту на пине WKUP, в моём случае(STM32F103VET) — это вывод A0.
RCC->APB1ENR |= RCC_APB1ENR_PWREN;//вкл тактирование PWR SCB->SCR |= SCB_SCR_SLEEPDEEP; //для M3 разрешаем sleepdeep PWR->CR |= PWR_CR_PDDS;//выбираем режим Power Down Deepsleep PWR->CR |= PWR_CR_CWUF ; //очищаем wakeup flag PWR->CSR |= PWR_CSR_EWUP; //разрешаем вэйкап, то есть пробуждение по переднему фронту на А0 __WFE(); //уснули
Для получения более подробной информации можно обратиться к AN2629 Application note
Источник: https://hubstub.ru/stm32/104-stm32-rezhimy-ponizhennogo-energopotrebleniya.html
Спасибо за чтение!