- Форум
- Наши простые устройства
- Программирование
- Разработка программной ШИМ для управления двумя ДПТ на основе статьи "Нисходящее программирование"
Разработка программной ШИМ для управления двумя ДПТ на основе статьи "Нисходящее программирование"
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Вот по мне - результатом функции обработки кнопок должна быть новая (или остаться старой) величина, с которой сравнивается счётчик периода при формировании высокого уровня...
Как-то так:
on(0 или 1) = STT_BTTN(){обработка кнопок}//STATE_BUTTON(){}
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Главное - алгоритм. Микроконтроллер обрабатывает поступающие данные. Нажатие кнопки порождает какие-то данные, которые надо обработать. В результате обработки возникают другие данные - то, о чем вы написали: измененная величина скважности импульсов. Если с данными, которые надо получить, более-менее все понятно, то с данными, которые возникают из-за нажатия кнопки, пока кризис.
Итак, ранее я говорил, что любые воздействия на программу извне можно с некоторой степенью достоверности назвать командами, а реакцию программы на эти команды - обработкой команд. То есть надо привести это словесное описание в аналог на языке Си. Для начала введем понятие команды.
Так как извне поступают только нажатия кнопок, пусть все команды у нас так и будут называться - КНОПКА. То есть (сокращенно) BTN. Кнопок у нас 4, причем пара меняет значение одного параметра, а вторая пара - другого. Для простоты просто пронумеруем эти параметры, и тогда команды наши будут отличаться наименованием: BTN_1 и BTN_2 соответственно для первого и второго параметра. Изменение у нас в сторону увеличения и уменьшения (вверх и вниз), соответственно и наименования команд становятся такими:
BTN_1_UP - первый параметр вверх
BTN_1_DN - первый параметр вниз
BTN_2_UP - второй параметр вверх
BTN_2_DN - второй параметр вниз
Как видите, я стремлюсь к максимальному соответствию того, что получается тому, что ранее было "на пальцах" объяснено. Нажали кнопку - получили команду. Не вдаваясь в дебри, пусть команды наши будут однобайтовыми числами, но для приличия обозначим эти числа отдельным типом:
typedef unsigned char btn_command_t; - любая команда в этом случае будет иметь тип btn_command_t.
Итак, есть данные, которые мы хотим получать. Откуда? Да из функции опроса кнопок, ранее об этом речь шла. То есть:
btn_command_t get_button(void); - это функция, которая вернет команду в соответствии с нажатой кнопкой. Как она это сделает - пока не важно (см. принцип нисходящего программирования). Параметров ей не надо, т.к. она в них не нуждается. На данном этапе мы "спохватываемся", что совсем забыли про ситуацию, когда кнопки не нажимаются! Отсутствие нажатия по сути ничем не отличается от команды "не делать ничего". Это странно для человека (он и без команды ничего не делает), но логично для тупой железки, которая вообще в принципе без указания не в состоянии даже ничего не делать. Просто введем дополнительную команду BTN_NOTHING или BTN_NONE, которая и будет означать, что команд пока нет (кнопки не нажаты).
Теперь что мы имеем? функцию, которая даст команду, что надо делать. Остается обработать эту команду. Т.к. команд в принципе несколько, логично использовать для обработки оператор switch, который и предназначен для выполнения разных действий по значению какой-то переменной. получится нечто вроде такого
switch ( get_button() ){
case BTN_1_UP: // обработка команды
break;
case BTN_1_DN:
break;
case BTN_2_UP:
break;
case BTN_2_DN:
break;
case BTN_NONE:
default:
break;
}
Пора задуматься над тем, как реагировать на команды. Хоть параметров у нас два, но они по смыслу очень похожи, то есть обрабатываются они идентично, хоть и разные. Напрашивается обработку параметров поручить отдельной функции. пусть она называется change_pwm (изменить ШИМ). Функция должна менять данные, поэтому обязана получать в параметре ссылку на них. В языке Си для этого служит указатель. То есть у функции будет параметр типа "указатель на скважность ШИМ". Скважность у нас однобайтовая, то есть параметр будет обозначен unsigned char *. Однако, скважность должна меняться вверх или вниз, то есть функция должна как-то узнавать, в какую сторону менять этот параметр... то есть функции надо передать второй параметр, который укажет ей направление изменения скважности.
Вырисовывается нечто вроде
void change_pwm(unsigned char *pwm, signed char direction); - функция ничего не возвращает, но зато получает указатель pwm на изменяемые данные и направление direction этого изменения. Для направления я решил взять знаковую переменную, но можно заменить и на беззнаковую - это уже как вам понравится, сути это не меняет. Например, можно было ввести константы DIR_UP и DIR_DN и передавать в функцию их... Чем это лучше простых -1 и +1? Ничем абсолютно!
Итак, вот готовая обработка команд:
switch ( get_button() ){
case BTN_1_UP: change_pwm( &pwm1, +1 );
break;
case BTN_1_DN: change_pwm( &pwm1, -1 );
break;
case BTN_2_UP: change_pwm( &pwm2, +1 );
break;
case BTN_2_DN: change_pwm( &pwm2, -1 );
break;
case BTN_NONE:
default:
break;
}
Пока все понятно?
Теперь остается разобраться с тем, как мы будем менять скважность... А потом - как будем из опроса физических портов микроконтроллера получать команды... И все!
Попробуйте самостоятельно так же постепенно, неторопливо рассуждая, расписать содержимое функции изменения скважности, рассматривая её как полностью независимую программу, получающую и обрабатывающую данные.
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Ну, как сказать...ARV пишет: <...>Как видите, без единого комментария написан код, при чтении вслух которого не остается сомнений в том, что он делает. Только читать надо раскрывая принятые сокращения. Плюсы перед единичкой я поставил умышленно, чтобы было ну совсем понятно
Пока все понятно?
<...>
Пока читаю - понятно.
Copy-past, если сделаю - тоже буду думать, что понятно...
Но сам я такое повторить пока не могу...
Если с лексикой у меня ещё более или менее, то вот с грамматикой языка - проблемы...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Ну, мне вот для общего развития...ARV пишет: <...> Функция должна менять данные, поэтому обязана получать в параметре ссылку на них. В языке Си для этого служит указатель. То есть у функции будет параметр типа "указатель на скважность ШИМ".<...>
В данном случае, указатель вводится чтобы не вводить дополнительную переменную?
Так?
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Мы завели переменную, которая задает скважность. Эта переменная используется в главном цикле. Теперь мы хотим функцию, которая могла бы ЭТУ переменную менять - как функция узнает, что это за переменная?
Многие скажут: ну так переменные глобальные, и любая функция их "видит".
Это так. НО!!!
У нас РАЗНЫЕ переменные есть, а методы работы над ними ОДИНАКОВЫЕ. То есть функция одна, но должна уметь менять разные данные. Вот для того, чтобы функция могла работать с разными данными, ей и нужен параметр, который УКАЖЕТ, какие именно данные надо обрабатывать. Указатель.
Опять же, кто-то скажет, а зачем указатель, если можно использовать РЕЗУЛЬТАТ обработки переданных данных без указателя? И да, так тоже можно. Сравните сами:
// пример с указателями (абстрактный)
int var1, var2, var3; // это какие-то данные
void modification_one(int *data); // это прототип функции, которая работает через указатель
int modification_two(int data); // а это - прототип функции без указателя
// пример изменения данных вышеперечисленными функциями
modification_one(&var1);
modification_one(&var2);
var1 = modofocation_two(var1);
var2 = modification_two(var2);
Оба подхода имеют право на жизнь, у каждого свои плюсы и минусы... И однозначно сказать, что какой-то вариант безоговорочно лучше, нельзя. Надо знать оба способа и использовать их в удобных случаях.
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Откуда получать данные.
Кнопки подключены к порту ввода-вывода.
Значит, они меняют состояние порта.
Значит, данные о кнопках можно получить, узнав в каком состоянии находится порт, к которому подключены кнопки...
P.s. Может в форме тепло-холодно я смогу осилить...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Кнопки - это одно из базовых понятий, как таблица умножения, разобравшись один раз, в последствии пользуешься автоматически не задумываясь.
Важно осознать тот факт, что кнопка "по-человечески" и кнопка "по-микроконтроллерски" это разные вещи: человеку удобно, когда кнопка имеет осмысленное значение (то ли название, то ли числовой эквивалент), а микроконтролеру совершенно безразлично это, единственное, что важно, что разные кнопки имеют разный смысл. И все.
Это я к тому, что (возвращаясь чуть выше) мы уже определили "человеческое" значение кнопок - BTN_1_UP и так далее, и теперь надо назначить этим символам какое-то практически удобное значение. Вовсе не важно, что "кнопка номер 1" будет соответствовать числу 1. Понимаете? И самое удобное в этом случае - выбрать в качестве значения именно состояние порта в момент опроса. Ну или что-то очень к тому близкое. Улавдиваете идею?
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Если состояние порта - это число, я бы использовал именно его, сохранив его в момент опроса в переменной...ARV пишет: <•••>самое удобное в этом случае - выбрать в качестве значения именно состояние порта в момент опроса.<•••>
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
ARV пишет: <...>У нас РАЗНЫЕ переменные есть, а методы работы над ними ОДИНАКОВЫЕ. То есть функция одна, но должна уметь менять разные данные. Вот для того, чтобы функция могла работать с разными данными, ей и нужен параметр, который УКАЖЕТ, какие именно данные надо обрабатывать. Указатель.<...>
Примеры делают одно и то же, но действия эти оформляются по-разному. В первом случае через указатель функция узнает значение переменной и сохраняет в этой же переменной новое значение. Во втором значение функция узнает через параметр, а новое значение - возвращает.<...><...> modification_one(&var1); modification_one(&var2); var1 = modofocation_two(var1); var2 = modification_two(var2);
Спасибо, в общих чертах понятно...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
ARV пишет: <...>записать это намерение в виде конкретных #define.
#define BTN_1_UP (0b11111110) //можно просто "254" нажата кнопка "+" второго канала
#define BTN_1_DWN (0b11111101) //нажата кнопка "253" "-" второго канала
#define BTN_2_UP (0b11111011) //нажата кнопка "251" "+" второго канала
#define BTN_2_DWN (0b11110111) //нажата кнопка "247" "-" второго канала
Если по верному пути иду, то get_button() должна вернуть одно из этих чисел в виде BTN_X_Y...
Что-то запутался, ещё подумаю...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Все-таки для понимания проще оперировать с номером линии порта. Т.е. первая кнопка занимает первую линию, вторая - вторую и т.д. И вот тут получается такое: положение бита в байте связано с номером этого бита очевидной операцией сдвига: 1<<5 - 5-й бит (нумерация в Си начинается с нуля, т.е. "по-людски" это будет 6-й бит). То есть 1<<5 это 0b00100000. А вот положение нуля сдвигом не обозначить, т.к. сколько ноль не двигай, ноль и будет. Для операции определения положения бита в байте по его номеру в avr-gcc даже специальный макрос предусмотрен, _BV(x) - от слов Bit Value.
#define _BV(x) (1 << (x))
Если вам нравится работать с "инверсными" битами, то ранее указанная вами запись будет выглядеть так:
#define BTN_1_UP ~_BV(0)
#define BTN_1_DWN ~_BV(1) и т.д.
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Отсюда следующее предположение - если это "некоторый момент"- то надо убедиться, что это не случайное состояние (число)?..
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.