Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Кнопка включения и выключения устройства на STM32F103
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Salamander
Коллеги, посоветуйте, как лучше организовать кнопку включения по типу тех, что в мобильниках - по длительному нажатию.

Мне видится так - устройство при подключении питания стартует и тут же уходит в один из режимов пониженного энергопотребления. Но при этом включает на выход один из пинов, на вход другой пин и ждет, когда на этом пине появится сигнал от первого пина (у меня матричная клавиатура). При этом можно конечно завести таймер, но по мне так лучше инкрементировать некую величину и по достижению ей порога пускать программу дальше - выходить из спящего режима. Как выключать? При нажатии на кнопку начинать отсчет, по достижении временного интервала - делать RESET.

По крайней мере, я так делал несколько лет назад на AVR -работало.

Хочу повторить то же на STM32F103, но сами понимаете, насколько в нем все наворочено, а я новичок в работе с этим камнем. На русском информации по спящим режимам, с примерами кода крайне мало. В связи с чем вопросы:

1. Для описанной задачи - какой режим лучше использовать? Нужно ли что-то отключать на старте, подключенное по умлочанию?
2. Как сделать программный RESET?

P.S. устройство уже собрано и поэтому варианты с использованием RTC (встречал в интернете намеки на его исползование в таких задачах) не подойдет. Вариант с использование пина WKUP тоже не подойдет, у меня на него от батареи через делитель подается напряжение, для анализа его при помощи АЦП.
IgorKossak
Посмотрите в сторону готовых решений. Можно найти в сети поискав "Push Button On/Off Controller"
Например LTC2953 или что нибудь другое, более подходящее Вашей задаче.

Или вот ещё.
A. Fig Lee
Reset примерно так:

Код
#define AIRCR_VECTKEY_MASK    ((u32)0x05FA0000)

/*******************************************************************************
* Function Name  : NVIC_GenerateSystemReset
* Description    : Generates a system reset.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void NVIC_GenerateSystemReset(void)
{
  SCB->AIRCR = AIRCR_VECTKEY_MASK | (u32)0x04;
}
Salamander
За RESET спасибо, работает.

Насчет RTC - был неправ - оказывается его тоже можно тактировать от внутренней RC цепочки - не знал.
В общем, мысль такая - сразу на старте STANBY, раз в 20 мс просыпаться и опрашивать клавиатуру, если нажатие есть, то ждать 1 секунду, после чего еще раз проверять клавиатуру и если клавиша нажата - просыпаться окончательно, если нет - засыпать снова.

Кто-нибудь подкинет примерчик пробуждения от будильника, настроенного на внутренний RC генератор? А то я нашел только пример для STM32L152, а там даже имена регистров другие....

Пока, из того, в чем разобрался сделал вот что:
вставил в программу это:
Код
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
  RCC_LSICmd(ENABLE);    
    RTC_ITConfig(RTC_IT_SEC,ENABLE);
    PWR_EnterSTANDBYMode();


По идее, программа должна зайти в режим ожидания, и через секунду выйти из него. Но не выходит. Как правильно сделать?
nx6310
В библиотеках stm есть примеры работы с RTC
Salamander
Нашел я пример, как раз того, что мне надо.
Код
NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);


    PWR_BackupAccessCmd(ENABLE);

    // RTC clock source configuration ----------------------------------------
    // Reset Backup Domain
    BKP_DeInit();

    // Enable LSE OSC
    RCC_LSICmd(ENABLE);
    // Wait till LSE is ready
    while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);

    // Select the RTC Clock Source
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);

    // Enable the RTC Clock
    RCC_RTCCLKCmd(ENABLE);

    // RTC configuration -----------------------------------------------------
    // Wait for RTC APB registers synchronisation
    RTC_WaitForSynchro();

    // Set the RTC time base to 1s
    RTC_SetPrescaler(32767);
    // Wait until last write operation on RTC registers has finished
    RTC_WaitForLastTask();
    RTC_SetCounter(0);
    // Wait until last write operation on RTC registers has finished
    RTC_WaitForLastTask();
    // Set the RTC Alarm after 3s
    RTC_SetAlarm(3);
    // Wait until last write operation on RTC registers has finished
    RTC_WaitForLastTask();
    RTC_ITConfig(RTC_IT_ALR,ENABLE);


В обработчике прерывания ничего не писал, просто поставил брейкпоинт.
Запускаю - брейкпоинт не срабатывает.
Мониторю через отладчик RTC->CNTL - инкрементируется, то есть часы тикают...
Куда копать?
smk
Есть же специальный пин PA0. При включении питания устройство ждет паузу какую-то и проверяет состояние PA0. Если нажата - выполняет программу, если нет, то засыпает. Просыпается при очередном нажатии, опять ждет и проверяет... ну и т.д.
Код
    /*** настройка режима Standby ***/
    RCC->APB1ENR |= (1UL<<28);
    PWR->CR |= (1UL<<1);
    PWR->CR &=~(1UL<<0);
    PWR->CSR|= (1UL<<8);
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
    if(PWR->CSR & (1UL<<0)) PWR->CR |= (1UL<<2);


Код
    if(!(GPIOA->IDR & (1UL<<0))) __wfi();
Salamander
Цитата
Есть же специальный пин PA0.


А если так? (см. первый пост)
Цитата
устройство уже собрано ....Вариант с использование пина WKUP тоже не подойдет, у меня на него от батареи через делитель подается напряжение, для анализа его при помощи АЦП.

кроме того, я писал, что клавиатура матричная. так что не пойдет пин А0.

Уважаемый smk, вы мне предложили иной вариант работы выключателя в целом. Я же уже выбрал свой вариант, с учетом невозможности использования А0.
В настоящий момент - проблема с прерыванием от Alarm. Отношения к пину А0 она не имеет.
Прощу помощи именно с Alarm.
С уважением.

Тихо сам с собою...
У меня получилось вот так:

основная программа:
Код
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    // RTC clock source configuration ----------------------------------------
    // Reset Backup Domain
    BKP_DeInit();
    // Enable LSE OSC
    RCC_LSICmd(ENABLE);
    // Wait till LSE is ready
    while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
    // Select the RTC Clock Source
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
    // Enable the RTC Clock
    RCC_RTCCLKCmd(ENABLE);
    // RTC configuration -----------------------------------------------------
    // Wait for RTC APB registers synchronisation
    RTC_WaitForSynchro();
    // Set the RTC time base to 1s
    RTC_SetPrescaler(32767);
    // Wait until last write operation on RTC registers has finished
    RTC_WaitForLastTask();
    RTC_SetCounter(0);
    // Wait until last write operation on RTC registers has finished
    RTC_WaitForLastTask();
    // Set the RTC Alarm after 3s
    RTC_SetAlarm(7);
    // Wait until last write operation on RTC registers has finished
    RTC_WaitForLastTask();
  //RTC_ITConfig(RTC_IT_SEC, DISABLE);
    RTC_ITConfig(RTC_IT_ALR,ENABLE);

/* 2 bits for Preemption Priority and 2 bits for Sub Priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

  NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  NVIC_EnableIRQ (RTC_IRQn);
  NVIC_EnableIRQ(RTCAlarm_IRQn);
    PWR_EnterSTANDBYMode();



обработчик прерываний
Код
void RTC_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
    {  
        RTC_ClearITPendingBit(RTC_IT_ALR);
    }


}
/*******************************************************************************
* Function Name  : RTCAlarm_IRQHandler
* Description    : This function handles RTC Alarm interrupt request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/

void RTCAlarm_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
    {  
        RTC_ClearITPendingBit(RTC_IT_ALR);
    }


}


Брейкпоинт в этом случае срабатывает, но почему-то не в обработчике RTCAlarm_IRQHandler, а в RTC_IRQHandler.
Естественно, из режима ожидания в этом случае система не выходит....
В чем ошибка?
nx6310
Вам надо настроить прерывание по внешней линии EXTI17
Код
     EXTI_InitStructure.EXTI_Line = EXTI_Line17;
     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
     EXTI_Init(&EXTI_InitStructure);

Из спящего режима STANDBY можно выйти только по внешнему прерыванию, поэтому прерывание будильника часов заведено на 17 линию.
Еще надо включить тактирование альтернативных функций

У вас в прерываниях проверяются одинаковые флаги по будильнику. В обработчике секунды поставьте проверку флага секунды
Salamander
Цитата
Вам надо настроить прерывание по внешней линии EXTI17


А производитель пишет, что не надо: http://application-notes.digchip.com/005/5-10387.pdf
стр. 13
Цитата
To wake up the STM32F10xxx from Standby mode, there is no need to configure the EXTI
Line 17.


Цитата
У вас в прерываниях проверяются одинаковые флаги по будильнику. В обработчике секунды поставьте проверку флага секунды

мне не нужна обработка секунды. Я просто констатировал факт что глобальное прерывание срабатывает и флаг RTC_IT_ALR обрабатывается.

Цитата
Еще надо включить тактирование альтернативных функций

Это вы про внешние прерывания?

А вот здесь https://my.st.com/public/STe2ecommunities/m...tandby+wake+rtc товарищ пишет, что у него STANDBY тоже не работает и он добавил ради интереса обработку EXTI7 так как в режиме STOP это помогало. В случае с STANDBY - без эффекта.

Погодите... вычитал, что в режиме STANDBY теряется содержимое RAM и регистров... А как тогда вообще им пользоваться, если его настраиваешь, настраиваешь - а все сбрасывается..

Повторюсь еще раз - мне надо чтобы при старте контроллер засыпал, просыпался по будильнику раз в секунду, включал одну УЖЕ ОПРЕДЕЛЕННУЮ ножку на выход, другую УЖЕ ОПРЕДЕЛЕННУЮ на вход и проверял на ней уровень. Если уровень высокий, то просыпаемся окончательно, если низкий - засыпаем еще на одну секунду.
Как черт возьми это сделать без всяких внешних прерываний?
nx6310
При пробуждении из режима standby проц тупо сбрасывается, фактически программа стартует сначала. При пробуждении из stop режима проц запускается с того момента в котором он перешел в stop режим.
Альтернативные функции включаются командой

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);это альтернативные функции пинов.
Я их всегда включаю чтоб потом непонятного гемора не было.
Тока что нарыл. Описание standby мода

After waking up from Standby mode, program execution restarts in the same way as after a
Reset (boot pins sampling, vector reset is fetched, etc.). The SBF status flag in the Power
control/status register (PWR_CSR)indicates that the MCU was in Standby mode.

Тебе получается надо при старте программы проверять флаг SBF в регистре PWR_CSR, который покажет тебе что проц был в режиме standby

Тебе надо после пробуждения провести инициализацию проца, затем проверить флаг SBF, затем подать единицу на пин, а с другого пина состояние прочитать, если нет уровня то засыпать на секунду.
Я так думаю в прерывание по будильнику из данного режима ты не попадешь. Будильник только разбудит проц и он начнет выполнять прогу с самого начала, там то и надо дергать пин и смотреть за входным пином
Salamander
Заработало!

В сухом остатке получилось вот что
CODE
/* --------------------------------------------------------------------------------*/
//
// НАСТРОЙКА ЧАСОВ И ЖДУЩЕГО РЕЖИМА
//
/* --------------------------------------------------------------------------------*/

void StartRTC_and_STANDBY()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
PWR_WakeUpPinCmd(DISABLE);
BKP_DeInit();
RCC_LSICmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
RTC_SetCounter(0);
RTC_WaitForLastTask();
RTC_SetAlarm(1);
RTC_WaitForLastTask();
RTC_ITConfig(RTC_IT_ALR,ENABLE);
RTC_WaitForLastTask();

NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_EnableIRQ(RTCAlarm_IRQn);
PWR_EnterSTANDBYMode();
}

int main(void)

{
int ii;
double ll;

#ifdef USER_DEBUG
DBGMCU->CR |= DBGMCU_CR_DBG_STOP; //for debug purpose
#endif

NVIC_Configuration();
SysTick_Config(SystemCoreClock/1000);
delay_init();
SystemInit();

if(PWR_GetFlagStatus(PWR_FLAG_SB)!=RESET)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2ENR_IOPEEN, ENABLE);
PWR_ClearFlag(PWR_FLAG_SB);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE, GPIO_Pin_6);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOE, &GPIO_InitStructure);

if (GPIOE->IDR & GPIO_IDR_IDR5)
{
delay_ms(1000);
if (GPIOE->IDR & GPIO_IDR_IDR5) GPIO_ResetBits(GPIOE, GPIO_Pin_6); else StartRTC_and_STANDBY();
}
else StartRTC_and_STANDBY();
}
else StartRTC_and_STANDBY();

// ДАЛЬШЕ ОСНОВНАЯ ПРОГРАММА


Будут какие замечания?
nx6310
Вроде все нормально. Если работает то все отлично)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.