Скажу свое видение плюсов/минусов программного и аппаратного антидребезга. То что касается цены - упасите, какое нафиг дорого? Резистор и конденсатор будут стоить в буквальном смысле копейки. Да пол-часа работы программиста, который будет программный антидребезг делать обойдется дороже!
Гибкая настройка? Да что там гибко настраивать то? Лучше подумайте какой ценой вы получаете программный антидребезг. Таймер и прерывания? На жалкую обработку кнопок целый таймер и прерывание? Да в МК может быть всего один-два таймера, и вы ими так просто швыряетесь? А может в фоновом цикле ждать 10-100-200 миллисекунд? В фоновом цикле обычно крутятся всякие приемо-передачи по UART, разные мигания, не критичные ко времени алгоритмы... Вы хотите, чтобы они все подождали, пока у вас кнопочка обработается? Это же бесполезная трата процессорного времени. Нет уж, я всегда буду настаивать на аппаратном антидребезге и только на нем.
Это все не ради спора, просто мое мнение. Вопросы риторические.
Нет уж, я всегда буду настаивать на аппаратном антидребезге и только на нем.
То есть вы хотите сказать , что кнопки вешаете на EXTI + конденсаторы ??? Ну, ну , большое вам счастье скакать по, пройденным уже многими, граблям . Возможно эти грабли вам мОзги вправят...
Если нужно - повешу на внешнее прерывание, если нужно - просто на ножку, если нужно - буду читать через сдвиговый регистр по SPI, но во всех случаях антидребезг буду использовать аппаратный.
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
В программе обычно бывает много разных задач, для которых нужен подсчет времени. И для них всех бывает достаточно одного таймера. Если нужно - используется несколько переменных-счетчиков, переполнение (в смысле достижение определенного уровня) которых обрабатывается в основном цикле. И задержки там тоже не нужны. Циклически проверяется состояние этих программных счетчиков и выполняются нужные операции. Про UART - делается буфер, который спокойно заполняется в основном цикле программы, а запись его непосредственно в регистр модуля UART может происходить по прерыванию этого самого UART.
То что в прерываниях не должно быть задержек - это самое вопиющее, что встретилось в том куске кода. Если главных цикл решает только какую-то одну задачу, то там еще можно допустить наличии задержек циклами, но в прерываниях - увольте.
Про "дорого" зависит от объемов. Если вы инженер компании, которая производит модули миллионными партиями, то экономическая часть вопроса будет стоять у вас по сути на первом месте. Если же нет - что же, можно и добавить пару элементов, но будет ли от них реальная польза? Опрос клавиатуры выше указанными методами займет единицы процентов загрузки ЦП даже на килогерцовых частотах. А если процессор работает на десятках мегагерц, то это вообще не вопрос. ----------
P.S. Вообще, соглашусь, что решение может зависеть от конкретных условий. Не стоит ограничивать себя ни аппаратным, ни программным способом, лучше держать в голове все возможные способы, и выбирать оптимальный для каждой задачи.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Если нужно - повешу на внешнее прерывание, если нужно - просто на ножку, если нужно - буду читать через сдвиговый регистр по SPI, но во всех случаях антидребезг буду использовать аппаратный.
Вы, похоже, никогда кнопки не подключали... бывает... Подключите плёнку... сколько она протянет???
Foks писал(а):
Про "дорого" зависит от объемов. Если вы инженер компании, которая производит модули миллионными партиями, то экономическая часть вопроса будет стоять у вас по сути на первом месте. Если же нет - что же, можно и добавить пару элементов, но будет ли от них реальная польза?
Загляните в схемы мобильников... ведь не зря там всё это навешано...
Цитата:
Вообще, соглашусь, что решение может зависеть от конкретных условий. Не стоит ограничивать себя ни аппаратным, ни программным способом, лучше держать в голове все возможные способы, и выбирать оптимальный для каждой задачи.
100% !!!
_________________ "Я не даю готовых решений, я заставляю думать!"(С)
Т.е. лучше заводить таймер и опрашивать через определенный период, тем самым и дребезг устранить и в главном цикле не тормозить ?
Не только дребезг... Ещё можно отработать короткое нажатие, двойное, тройное, длинное (автоповтор, переход куда-либо) или залипание (механическое повреждение) и т. д. ...
_________________ "Я не даю готовых решений, я заставляю думать!"(С)
Попробовал с таймером SysTick заметно лучше ситуация Спойлер#include "stm32f10x.h" /* "stm32f10x.h" - Файл описания периферии */ #define Green 0 /* Зеленый светодиод */ #define Blue 1 /* Голубой светодиод */ uint8_t flag; /* флаг состояния */
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPAEN;// включаем тактирование порта C и А GPIOC->CRH |= (GPIO_CRH_MODE8_0 | GPIO_CRH_MODE9_0);// Режим выхода порта С, вывода PC8,9 с максимальной скоростью – 10МГц (MODEy[1:0] = 01) GPIOC->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_CNF9);// Режим вывода PC8,9 порта C, Двухтактный выход общего назначения Push-Pull CNFy[1:0] = 00: GPIOA->CRL &= ~ GPIO_CRL_MODE0; // Кнопка "USER" PA0 - на вход. /**---------- * @описание Функция управления состояния светодиодов. * @параметр uint8_t stat * @возвращаемое * значение Нет *----------*/ void Led_on_off(uint8_t stat) { GPIOC->ODR &= 0xFCFF; /* Выкл синий и зеленый светодиод */ switch(stat) { case Green: _PORTC(9)= 1; /* вкл синий GPIOC->ODR |= 1 << 8; */ break; case Blue: _PORTC(8)= 1; /* вкл зеленый GPIOC->ODR |= 1 << 9; */ } }
int main() /*---------- Основная программа ----------*/ { /* Конфигурируем таймер SysTick на срабатывание 100 раз в секунду */ SysTick_Config(SystemCoreClock / 100);
while(1) /* ---------- Основной цикл ----------*/ { Led_on_off(flag); } } Код выше вдруг кому интересно.
Здесь кнопка опрашивается 100 раз в секунду, сколько пробовал дребезга не увидел(если что, думаю можно реже опрашивать будет еще лучше). Еще надо попробовать в цикле антидребезг организовать по таймеру.
...кнопка опрашивается 100 раз в секунду...организовать по таймеру.
собственно можно по разному мутить. смысл один - отслеживать человека человек не быстр. посему есть некий "дооолгий" промежуток времени который однозначно нам скажет - нет это не помеха. в кавычках - потому как это несколько миллисекунд. дальше: нам надо асинхронно воспринимать кнопку от человека. Тут два способа - прерывание либо опрос. опрос проезжаем - это понятно. остаётся прерывание. собственно если мы отжимаемся и время прошло мало - то его в пень. если много - то флаг нажатия. из основной программы, если у нас нет РТОС - то нам кнопка подтребуется когда потребуется (если РТОС - то мы можем тут задействовать всевозможные осевые выкрутасы - типа ожидание со временем и прочее). Т.е. если что то там нажалось, то до запроса этого события из основной логики - нам пофигу. а вот если запросили, то мы можем посмотреть две вещи 1) сколько времени прошло от нажатия (если такое было) 2) стоит ли флаг нажатия кнопочки (если он был выставлен).
надеюсь понятно описал логику, без всякого кода на таймерах (необходимо только всё же чем то считать время - т.е. запустить таймер какой нить можно в условных попугаях - т.е. не обязательно реальные секунды, миллисекунды и т.п.)???
Тут два способа - прерывание либо опрос. опрос проезжаем - это понятно. остаётся прерывание. собственно если мы отжимаемся и время прошло мало - то его в пень. если много - то флаг нажатия.
Ну вот. Так сразу. Только два? Почему? В большинстве случаев случаев применяю третий, совмещенный - опрос в прерывании. К тому же в этом методе есть и защита от дребезга. Без накладных расходов. Довольно удобно как для отдельных кнопок, так и в матрицах из них. Чаще всего использую системный таймер на 1 мсек. Если кому-то будет интересно - спрашивайте. Тратить время "в воздух" нет желания.
Если кому-то будет интересно - спрашивайте. Тратить время "в воздух" нет желания.
Да ладно Вам, очень даже интересно. Я перекопал интернет и везде только разговоры на эту тему или примеры с применением библиотеки SPL, а она хоть и говорят для удобного восприятия у меня в голове вызывает только кашу. Впрочем про таймер SysTick читал именно с применение SPL, так же попадалось очень похожее на пример выше, где учитывается время нажатия. Только здесь я думаю опечатка надо if (++button_cnt == некая_константа) Action();, тогда условие выполняется если нажата кнопка, запускаем счет и ждем когда значение будет равно константе. Если равно выполняем какое то действие, если нет обнуляем счетчик. Надеюсь правильно понимаю код, тем более он прост
Функция ReadKey() вызывается либо из главного цикла программы, либо по прерыванию таймера. В главном цикле обрабатывается переменная Key. if(Key) /* кнопка нажата */ { }
Еще сильно помагает исходник от Galizin, за что ему отдельное спасибо.
Да ладно Вам, очень даже интересно. Я перекопал интернет и везде только разговоры на эту тему или примеры с применением библиотеки SPL, а она хоть и говорят для удобного восприятия у меня в голове вызывает только кашу. Впрочем про таймер SysTick читал именно с применение SPL, так же попадалось очень похожее на пример выше, где учитывается время нажатия. Только здесь я думаю опечатка надо if (++button_cnt == некая_константа) Action();, тогда условие выполняется если нажата кнопка, запускаем счет и ждем когда значение будет равно константе. Если равно выполняем какое то действие, если нет обнуляем счетчик. Надеюсь правильно понимаю код, тем более он прост
Практически то, что я применяю. Немного переиначил условие. Считается, что кнопка в отжатом состоянии в "1", нажатие в "0". Организованы N счетчиков по количеству кнопок. Все счетчики volatile.
И так по количеству кнопок. Линейное сканирование в один проход обработчика. С матрицей немного иначе, но суть та же. Если период прерываний таймера, выбранного в качестве системного равен 1 мсек, то TRESHOLD должен иметь значение порога антидребезга в мсек. Если таймер считает быстрее, соответственно пересчитывается TRESHOLD. И никаких Action() из обработчика! Для получения состояния кнопки ( только результат "кнопка Кн была нажата", длительности и моментальная "нажата/отжата" не отрабатываются, легко додумываются по необходимости ) надо обращаться к функции
Функция возвращает либо код нажатой кнопки асинхронно к моменту обращения, либо ноль, если ни одна их кнопок не была нажата. Подобный подход позволяет не загромождать обработчик ничем, кроме работы со счетчиками, "защелкивая" событие "была нажата" по истечении TRESHOLD-а. Фиксация времени нажатия кнопок или получение состояния "нажата/отжата" возможна с применением того же механизма, но слегка поменяв логику работы функции получения данных по кнопкам. Как видите, все очень просто. Метод не мой, просто уже не помню, из какой литературы я его извлек. Встречал еще несколько модификаций этого алгоритма. Но они, на мой взгляд, были менее оптимальны. Этот же прост и прозрачен. Соотвественно надежен. Об ограничениях применения я уже сказал.
Про "кашу в голове" не комментирую, это ваши проблемы. Никто не заставляет применять SPL.
Опробовал и его через таймер 3, разницы по сравнению с SysTick не заметил, но для себя полезно.Спойлер#include "stm32f10x.h" /* "stm32f10x.h" - Файл описания периферии */
#define Green 0 /* Зеленый светодиод */ #define Blue 1 /* Голубой светодиод */ uint8_t flag; /* флаг состояния */
/*---------- Побитовый доступ к регистрам: ---------- Адрес в области bit band = начальный адрес области bit band + смещение байта в «родной» области ? 20 + номер бита ? 4.*/
void Initall () { RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPAEN;// включаем тактирование порта C и А GPIOC->CRH |= (GPIO_CRH_MODE8_0 | GPIO_CRH_MODE9_0);// Режим выхода порта С, вывода PC8,9 с максимальной скоростью – 10МГц (MODEy[1:0] = 01) GPIOC->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_CNF9);// Режим вывода PC8,9 порта C, Двухтактный выход общего назначения Push-Pull CNFy[1:0] = 00: GPIOA->CRL &= ~ GPIO_CRL_MODE0; // Кнопка "USER" PA0 - на вход.
/*---------- Таймер 3 ----------*/ RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; /* Тактирование таймера TIM3 */ TIM3->ARR = 10000; /* перезагружать каждые (2MHz / 10000 = 200Hz ) - 5mSec (1000/200) */ /* Когда таймер достигнет максимального значения необходимо уведомить об этом контроллер прерываний, */ TIM3->DIER |= TIM_DIER_UIE; /* для этого разрешим генерацию событий от таймера 3 */ TIM3->CR1 |= TIM_CR1_CEN; /* старт таймера TIM3 */ NVIC_EnableIRQ(TIM3_IRQn);/* Задействуем обработчик прерывания от 3-го таймера */ }
/**---------- * @описание : Обработчик прерывания по переполнению таймера 3. * @заметка : * @параметр : Нет * @возвр значение: Нет *----------*/ void TIM3_IRQHandler(void) { TIM3->SR &= ~TIM_SR_UIF; // не забываем сбросить флаг, //TIM3->SR = 0; /* Очистить флаг ожидания*/ // иначе прерывание будет генериться постоянно static unsigned button_cnt = 0; /* счетчик периода нажатия */ if (GPIOA->IDR & 1) /* Если нажата кнопка */ { if (++button_cnt == 100) flag=!flag;/* запускаем счетчик, если в этот период кн еще нажата*/ } else button_cnt = 0;/* выполняем какое то действие, если нет обнуляем счетчик */ }
/**---------- * @описание : Функция управления состояния светодиодов. * @параметр : uint8_t stat (переменная типа unsigned char ) * @возвращаемое значение: Нет *----------*/ void Led_on_off(uint8_t stat) { GPIOC->ODR &= 0xFCFF; /* Выкл синий и зеленый светодиод */ switch(stat) { case Green: _PORTC(9)= 1; /* вкл синий GPIOC->ODR |= 1 << 8; */ break; case Blue: _PORTC(8)= 1; /* вкл зеленый GPIOC->ODR |= 1 << 9; */ } }
int main() /*---------- Основная программа ----------*/ { Initall (); /* инициализируем порты и таймеры */
while(1) /* ---------- Основной цикл ----------*/ { Led_on_off(flag); /* запускаем переключение LED */ } } Здесь один раз нажали горит зеленый светодиод, второй раз синий и так по кругу, опрашиваем с помощью таймера 3.
Вопрос по IAR (6.5): как разместить переменную в сегменте eeprom? (для контроллера STM32L152 EEPROM начинается с 0x08080000) в конфиге для линкера это описано как
define symbol __region_EEPROM_start__ = 0x08080000; define symbol __region_EEPROM_end__ = 0x08080FFF; define region EEPROM_region = mem:[from __region_EEPROM_start__ to __region_EEPROM_end__];
place in EEPROM_region { section .eeprom };
Для AVR это выглядело как что-то вроде __attribute__((section(".eeprom"))) в IAR соотв-но не работает.
Заголовок сообщения: Re: STM32 новичку в ARM что к чему
Добавлено: Чт июн 20, 2013 12:57:25
Друг Кота
Карма: 26
Рейтинг сообщений: 108
Зарегистрирован: Чт ноя 04, 2010 01:56:36 Сообщений: 7439 Откуда: г. Москва
Рейтинг сообщения:0
Вопрос типа "Как под виндами разместить переменную на жестком диске". Никак. EEPROM - это периферия. Инициализируй, пиши, читай функциями. Как оно само в еепром телепортируется ? Это не игрушечный атмел авр студио, где фрейморк незримо за тебя подшаманивет.
Satyr, добрый день! Вы уважаемый мной кот, ни в коем разе не хочу Вас обидеть. Но мне кажется в данном случае Вы заблуждаетесь. в STM32L EEPROM отображается в общее адресное пространство на адреса начиная с 0x08080000. Следовательно переменную можно разместить там явно, указав ее адрес. Или неявно, используя указатель, записать просто в ячейку. И то и другое делается без всяких функций, таких как например в AVR.
Satyr, добрый день! Вы уважаемый мной кот, ни в коем разе не хочу Вас обидеть. Но мне кажется в данном случае Вы заблуждаетесь. в STM32L EEPROM отображается в общее адресное пространство на адреса начиная с 0x08080000. Следовательно переменную можно разместить там явно, указав ее адрес. Или неявно, используя указатель, записать просто в ячейку. И то и другое делается без всяких функций, таких как например в AVR.
Читать можно свободно. А вот с записью не получится. Области EEPROM и FLASH имеют защиту. При обращении к памяти в этой области ее необходимо предварительно снять. Учитывая это, механизм программной инициализации не может быть выражен путем VAR = VALUE. Запись во флеш и еепром имеет механизм, отличный от ОЗУ. Причем далеко не все STM32 имеют этот самый eeprom.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 25
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения