Спасибо за наводку. Саму книгу скачать не удалось, но нашел где ее можно почитать. Правда для этого пришлось зарегистрироваться. Попутно нашел еще и лабораторный практикум того же автора.
Подскажите, пожалуйста, а как избавиться от дребезга контактов программно? Знаю, что можно задержку добавить после опроса кнопки, но это не лучшее решение, вроде. А как же тогда борются с этим явлением? В электронике для этого используют триггер. Наверное что-то подобное можно программно реализовать?
Задержка. Только не глобальная, которая останавливает всю программу, а задержка на реакцию следующего события кнопки. Реализации могут быть всякие, с таймерами и без, для работы по прерываниям или нет...
PD_DDR_bit.DDR3 = 0; // Активируем порт PD3 в качестве "входа" PD_CR1_bit.C13 = 0; // переключаем порт в качестве Дифф. входа PD_CR2_bit.C23 = 0; // Прерывания запрещены
PB_DDR_bit.DDR5 = 1; // Настраиваем PB5 в качестве "вывода". PB_CR1_bit.C15 = 1; // Переключение порта в режим push-pull. PB_CR2_bit.C25 = 1; // Скорость переключения до 2 МГц
int n = 2; while(1) { if (PD_IDR_bit.IDR3!=1) // Проверка нажатия кнопки. Кнопка NO замыкает PD3 на землю. PD3 через резистор подтянут к плюсу. { for (unsigned long i=0; i<20000; i++); if (PD_IDR_bit.IDR3!=1) { n++; } } switch(n) { case 1: PB_ODR_bit.ODR5 = 0; // Включение светодиода. break; case 2: PB_ODR_bit.ODR5 = 1; // Отключение светодиода. break; default: n=1; } }
}
Задержка на нажатие кнопки в этой программе глобальная, правильно? А как будет выглядеть локальная задержка? Не нравится мне как работает кнопка. Переключения не очень четкие. Оно конечно гораздо лучше чем без задержки, но все равно не то. Подскажите, в какую сторону копать?
int main (void) { i = 0; .... while(1) { if (i > 0) { i--; }
if (PD_IDR_bit.IDR3!=1) // Проверка нажатия кнопки. Кнопка NO замыкает PD3 на землю. PD3 через резистор подтянут к плюсу. { if (i == 0) { i = 20000; n++; } }
..... }// while }
фактически, это тоже самое, только теперь i меняется в глобальном цикле, не препятствуя выполнению основной программы. При первом нажатии происходит обработка (n++;) и инициализируется счётчик (i = 20000;). И пока i не станет равным нулю, последующие состояния кнопки игнорируются. Минус такого решения: в зависимости от ветвления основной программы длительность игнорирования может сильно отличаться. Использование таймера позволит сделать более точное время антидребезга.
И ещё: кнопка может генерировать три события: KeyUp, KeyDown и Click. Вариант подобного кода их не различает, то есть, как только счетчик обнулится и кнопка при этом не отпустится, она снова обработается.
Последний раз редактировалось Martian Ср авг 30, 2023 02:53:28, всего редактировалось 1 раз.
во втором случае проверка на отжатие сработает в момент дребезга и сделает ложный клик. Отжатие в свою очередь тоже вызывает дребезг, поэтому надо немного больше заморочиться.
int main (void) { i = 0; j = 0; .... while(1) { if (i > 0) { i--; } if (PD_IDR_bit.IDR3!=1 & i == 0) { if (j == 0 ) { j = 1; } } if (PD_IDR_bit.IDR3!=0 & j == 1) { if (i == 0) { i = 20000; j = 0; n++; } } ..... }// while }
Дополнение Нет, не будет это работать. Надо еще подумать...
Все, придумал. Проверил. Четкие переключения без ложных срабатываний. Теперь мне нравится работа кнопки.
Код:
int n = 2, i=0, j=0; while(1) { if (i > 0) { i--; }
if (PD_IDR_bit.IDR3!=1 & i == 0) { if (j == 0 ) { j = 1; i = 2000; } } if (PD_IDR_bit.IDR3!=0 & j == 1) { if (i == 0) { i = 2000; j = 0; n++; } } }
Ну, надо просто нарисовать алгоритм, примерно так: произошло событие кнопки, например, замыкание, которое мы отловили и теперь должны переждать дребезг. пока идет дребезг, мы не реагируем ни на что. дребезг закончился, смотрим состояние кнопки, если она всё ещё нажата - запускаем/инкрементируем счётчик, как долго нажата кнопка, если хотим отловить удержание, для, например, входа в меню, если не хотим - продолжаем игнорировать. если не нажата - считаем, что кнопка была отпущена, надо снова переждать дребезг. пока идет дребезг, мы не реагируем ни на что.
То есть, у нас видится как одинаковое что-то, так и разное что-то. Одинаковое - это пережидание дребезга, изменение состояния кнопки (неважно, на какое) и отсутствие реакции:
Код:
int main (void) { drebezg_count = 0; button_status = 0; // 0 - не нажата, 1 - нажата .... while(1) { if (drebezg_count > 0) { drebezg_count--; // пережидаем дребезг } else { // а здесь пишем обработку кнопок if ((PD_IDR_bit.IDR3 == button_status) // кнопка изменила состояние { drebezg_count = 20000; button_status != button_status; //меняем статус кнопки // здесь можно вызвать функцию обработки события кнопки или написать код }
// продолжение программы } }
Я не проверял строго, писал прям здесь, но вот как-то так, попроще (чем проще - тем меньше риск ошибок). Мы теперь можем ловить изменение любого состояния кнопки, минуя оба дребезга и строить дальше уже логику исходя из какое именно изменение (по фронту или спаду) кнопки произошло
Последний раз редактировалось Martian Чт авг 31, 2023 19:56:59, всего редактировалось 1 раз.
Просто я как-то неверно выразил мысль. Она была как-то так - обработка кнопок - это то, что касается опроса состояния портов, где подключены кнопки и то, что меняет что-то в программе в зависимости от результата опроса портов. Вызов функции обработки события кнопки - это как раз последняя часть, то самое инкрементирование n++. Просто работа с кнопками может быть гораздо большей, чем просто инкрементирование, раздувать главный цикл неудобно, поэтому, я бы написал так:
Код:
void KeyChange(unsigned char btn_status); ...
int main (void) { drebezg_count = 0; button_status = 0; // 0 - не нажата, 1 - нажата .... while(1) { if (drebezg_count > 0) { drebezg_count--; // пережидаем дребезг } else { // а здесь пишем обработку кнопок if ((PD_IDR_bit.IDR3 == button_status) // кнопка изменила состояние { drebezg_count = 20000; button_status != button_status; //меняем статус кнопки KeyChange(button_status); } }
// продолжение программы } }
void KeyChange(unsigned char btn_status) { if (btn_status == 1) // проверка, что произошло событие отпускания кнопки { n++; } }
Конечно, ради одного условия и инкремента нет смысла и сожрет больше ресурсов на вызов, если оптимизатор не соптимизирует, но зато удобно и минимизирует ошибки: если антидребезг и опрос портов отлажены, то там уже ничего не трогается, работаем только с KeyChange
Последний раз редактировалось Martian Чт авг 31, 2023 23:45:05, всего редактировалось 1 раз.
Ничего, ничего, как говорят "лиха беда начало". Стремиться к чему-то - это хорошо и нужно. Но главное всё же во всём этом уловить, что основное есть выбор правильного алгоритма для решения задуманного вами процесса! И только второе - это его та или иная (но желательно всё же оптимальная ) реализация на выбранном вами языке. Удачи в освоении программирования.
Но главное всё же во всём этом уловить, что основное есть выбор правильного алгоритма для решения задуманного вами процесса!
Да, полностью с вами согласен. Это я еще со школьной скамьи знаю. (давно это было...) Мы тогда паскаль изучали. Сам язык и не помню, но алгоритмы составлять нас научили(я так думаю ).
Добавлено after 9 hours 30 minutes 46 seconds: Подскажите, пожалуйста, а как мне посчитать частоту моргания светодиода в этой программе:
Код:
#include "IOSTM8S103F3.h"
int main( void ) { int k=0; PB_DDR_bit.DDR5 = 1; // PB_CR1_bit.C15 = 1; // PB_CR2_bit.C25 = 1; // while(1) { if ( k > 0) { k++; } if (k == 0) { PB_ODR_bit.ODR5 = 0; k++; } if (k == 200) { PB_ODR_bit.ODR5 = 1; } if (k == 400) { k=0; } } }
Частота работы процессора по умолчанию 2МГц. Нужно 2000кГц/400? Что-то у меня другую частоту осциллограф показывает...
теперь смотрим листинг ассемблера, как компилятор скомпилировал:
Теперь надо сосчитать количество тактов процессора, которые потребуются для выполнения всех этих команд. И вот на это число и надо делить частоту работы процессора, а не на 400.
Опять вы зрите в корень. Уже начал изучать. А параллельно и прерывания. Пару дней уже не могу понять вот это
Код:
Обработчики прерываний определяются следующим образом: #pragma vector=<номер прерывания> (табл. 3.1) __interrupt void <имя прерывания>, <номер прерывания> – номер прерывания в hex формате. Берется значение из технической документации микроконтроллера и к нему прибавляется 2. <имя прерывания> – имя, которое будет именем подпрограммы обработчика прерывания (определяется пользователем).
__interrupt void Это функция, которая будет исполнятся при наступлении условия прерывания(вектора прерывания)? Как указать значение вектора прерывания? Точнее сказать, какое значение должно стоять, например, для TIM1 захват/сравнение? Не могу разобраться как значение из таблицы перевести в шестнадцатеричный формат.
Большое спасибо за статью. Мне очень понравилось. Написано простыми словами. Очень легко читать. Пожалуй прочитаю весь цикл статей этого автора по теме STM8. Но тут ответ на мой вопрос все же не полный. По сути можно не заморачиваться и пользоваться готовой таблицей из заголовочного файла IOSTM8S103F3.h(ведь не зря же он подключен ) , но мне все же хотелось бы разобраться до конца. Как получилась эта таблица в заголовочном файле? Вот таблица из DataSheet Нужно брать номер вектора прерывания из первого столбца таблицы, прибавлять к нему 2, а затем переводить в шестнадцатеричный формат? Т.е. для TIM2 capture/compare это значение будет 14+2=16(dec)=10(hex),верно?
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 12
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения