- Форум
- Наши простые устройства
- Программирование
- Разработка программной ШИМ для управления двумя ДПТ на основе статьи "Нисходящее программирование"
Разработка программной ШИМ для управления двумя ДПТ на основе статьи "Нисходящее программирование"
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Вся эта красота будет реализовываться на микроконтроллере ATmega16a (потому что он уже есть) и на языке Си - как на самом низкоуровневом из высокоуровневых.)
Как выглядит задача с моей точки зрения:
1. Получить две независимых последовательности прямоугольных импульсов.
2. Иметь возможность независимого управления этими последовательностями.
Общечеловеческий подход привёл к появлению кода хотя и рабочего (испытанного на настоящем контроллере, двух светодиодах и четырёх кнопках), но с точки зрения людей разбирающихся в программировании вообще и программировании микроконтроллеров в частности - совершенно не приемлемым для того, чтобы считать задачу решённой.
Вот один из вариантов который нравится мне самому
#define F_CPU 4000000UL
#include <avr/io.h>
int main(){
DDRB = 0x00; //кнопки
PORTB = 0xFF; //кнопки с резисторами
DDRD = 0xFF; //выход шим
PORTD = 0x00; //выход шим низкий
int port_state = PINB;
int ton0 = 0; //время PORTD0_on и
int ton1 = 0; //время PORTD1_on и приращение
int T = 0; //период
int T_ = T; //и дубль
const int Tconst = 260;
const int d0 = 13;
const int d1 = 13; //приращение PORTD0 и PORTD1
char n = 0; //счётчик периодического опроса кнопок,
char r = 0; // разрешение изменения
while(1){//основной цикл
//=======================опрос кнопок=======================//
n++; //инкр счётчик периодического опроса кнопок
//==================если у счётчика периодического опроса кнопок три младших разряда ==0
if(!(n&0b00000111)){
port_state=PINB; //запоминаем состояние порта B
//===================кнопки канала PORTD0===================//
//======кнопка "+"======//
//===========если нажат "+" и есть возможность расширения импульса и разрешено изменение
if((!(port_state&0b00000001))&&(Tconst>=(ton0+d0))&&(!r)){
ton0=ton0+d0; //увеличиваем время PORTD0_on
r=1; //запрещаем изменения
} //======кнопка "-"======//
//===========если нажат "-" и есть возможность расширения импульса и разрешено изменение
if((!(port_state&0b00000010))&&(0<=(ton0-d0))&&(!r)){
ton0=ton0-d0; //уменьшаем время PORTD0_on
r=1; //запрещаем изменения
}
//===================кнопки канала PORTD1===================//
//======кнопка "+"======//
//==========если нажат "+" и есть возможность расширения импульса и разрешено изменение
if((!(port_state&0b00000100))&&(Tconst>=(ton1+d1))&&(!r)){
ton1=ton1+d1; //увеличиваем время PORTD1_on
r=1; //запрещаем изменения
} //======кнопка "-"======//
//===========если нажат "-", есть возможность расширения, импульса и разрешено изменение
if((!(port_state&0b00001000))&&(0<=(ton1-d1))&&(!r)){
ton1=ton1-d1; //уменьшаем время PORTD1_on
r=1; //запрещаем изменения
}
}//========================== ШИМ ==========================//
PORTD=0xFF; //включаем всё
while(T<=Tconst){ //период
if(T==ton0) //сравниваем время PORTD0_on
PORTD&=0b11111110; //выключаем PORTD0_on
if(T==ton1) //сравниваем время PORTD1_on
PORTD&=0b11111101; //выключаем PORTD1_on
T++; //считаем период
}
T=T_; //восстанавливаем период
if(port_state==0b11111111){
r=0; //если кнопки отпущены разрешаем изменение
}
}
}
}
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Конкретно у вас в коде есть, например, число 0b00000100 - очевидно, что это битовая маска для работы с портом. Но у вас есть так же и другие похожие числа, и назначение у них похожее - стоит ли говорить, что перепутать их довольно легко? Поэтому настоятельно рекомендую заменить все магические числа на соответствующим образом определенные константы-макросы примерно так:
#define BUTTON_1 0b00000100
#define BUTTON_2 0b00000010
// и т.д.
if((!(port_state & BUTTON_2))&&(Tconst>=(ton0+d0))&&(!r))
Ну а дальше - задавайте вопросы
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Сейчас уже - соглашусь с вами.
Но, давайте код из первого сообщения останется как "... программа первая моя ..."©, чтобы можно было сравнить его с тем, что получится при применении подхода описанного в статье Нисходящее программирование на простом примере .
Обобщённую задачу я описал в первом сообщении.
Жаль, что в языке Си нет такого оператора, который бы выполнял пункт первый.
Да и для второго пункта операторов нет.
Придётся начинать "нисходить".
Пока, я не понимаю какого типа окажутся функции и пишу на удачу:
1. void PWM_SEQUENCE(void){//здесь должны быть сформированы две последовательности модулированных по ширине имульсов}
2. void PRESS_BUTTON(void){//здесь должно осуществляться само модулирование колебаний. Кнопками.}
Пока - так...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
"Нисходить" - это значит идти от общего к частностям, от крупного к мелкому, от задачи к реализации по шагам. Сразу функции - это восходящий принцип, от частного к общему. Так тоже можно проектировать программы, но, во-первых, это не наш стиль (не НИСХОДЯЩЕЕ программирование), а во-вторых, так легче работать с боьлшим опытом за плечами.
Начинать надо не с функций, а с обобщенного алгоритма, который в вашем случае должен выглядеть как-то так (согласно статье):
1. ШИМ получается периодическим выводом в порт таких значений, что на паре линий сформируются нужные нам последовательности импульсов.
2. Периодически - значит, в цикле.
3. Так как это основная задача, следовательно и цикл будет главным.
4. В том же главном цикле опрашиваем кнопки и реагируем на их нажатия, меняя параметры для п.1
5. При необходимости запоминаем на будущее параметры ШИМа
Теперь пробуем записать это на языке Си, используя русский язык там, где еще не ясно, что и как будет:
int main(void){
while(1){
// подготавливаем данные для порта
// выводим эти данные в порт
// опрашиваем кнопки, для обработки кнопок лучше всего оператор switch
switch(результат_опроса_кнопок){
case КНОПКА_1 : // тут обработка кнопок по аналогии
}
// тут все готово к очередной итерации цикла и/или запоминанию в EEPROM
}
}
Теперь попробуйте проделать итерацию замены комментариев на нечто более осмысленное в плане языка Си. Не надо торопиться и менять сразу все комменты - если не очень прониклись духом стиля нисходящего программирования, делайте постепенно, понемногу, до полного просветления
Подсказка: уже понятно, что нам потребуются макросы для кнопок, переменные для подготовки данных в порт, сам порт и т.п.
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Пишу дифайны. Не знаю на чём остановиться. Написание дифайнов - это как ремонт. Закончить нельзя - только прекратить!ARV пишет: <...> понятно, что нам потребуются макросы для кнопок, переменные для подготовки данных в порт, сам порт и т.п.
#define BTN1_UP (!(PINB & 0b00000001)) //нажата кнопка "+" первого канала
#define BTN1_DWN (!(PINB & 0b00000010)) //нажата кнопка "-" первого канала
#define BTN2_UP (!(PINB & 0b00000100)) //нажата кнопка "+" второго канала
#define BTN2_DWN (!(PINB & 0b00001000)) //нажата кнопка "-" второго канала
#define ENABLE (PINB == 0b11111111) //на всякий случай - "разрешение приращения"
#define PWM1_ON (PORTD |= 0b00000001) //первый канал включён
#define PWM2_ON (PORTD |= 0b00000010) //первый канал включён
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Я предлагаю вам не стремиться наделить макросы какой-то функциональностью, а пока что ограничиться минимумом - магическими числами. Попытайтесь осмысленно избавиться от них - и всё. Не надо делать макрос "нажата кнопка", сделайте макрос "кнопка". Потом, если потребуется, из этих простых макросов соберете более сложные... а может быть, станет понятно, что это лишнее.
Подсказка: кнопка - это физическая штука, подключенная к одной из линий порта ввода-вывода. То есть кнопка прямо ассоциируется с битом порта. Вот и придумайте, как этот отдельно взятый бит обозвать.
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
По моему, я в дифайнах неправильно битовые операции написал.
Завтра после работы вопросы появятся...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
1. Организация бесконечного цикла "portOn-portOff.
2. Определение момента включения или выключения - это как нравится.
3. Организация управления моментом включения или выключения.
Правильно?
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Насколько я понял, вы желаете формировать ШИМ не аппаратно, не по прерываниям, а в главном цикле. Ок, это можно при низких требованиях к качеству ШИМа (к стабильности периода и скважности импульсов).1. Организация бесконечного цикла "portOn-portOff.
2. Определение момента включения или выключения - это как нравится.
3. Организация управления моментом включения или выключения.
Но почему вы решили, что ШИМ формируется путем включения и выключения портов? Формально это так, но получается не ДВУМЯ операциями, а ОДНОЙ: ОБНОВЛЕНИЕМ состояния портов!
Когда я набросал главный цикл вашей программы парой постов ранее, я не зря написал
одна команда вывода, но до нее - подготовка. В момент обновления те линии порта, что должны упасть в 0 - упадут в него одновременно с тем, как поднимутся в 1 те, которые должны это сделать! Даже если у вас будет 8 каналов ШИМ - все они одновременно изменят свое состояние. Это решает и проблему с управлением моментом включения/выключения: он всегда будет в одном и том же месте главного цикла...// подготавливаем данные для порта
// выводим эти данные в порт
Улавливаете?
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Всё гениальное - просто.
Я обратил, конечно, внимание на необычное слово, но - "ниасилил".(
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
int tmp=0;
while(1)
{
if(cntr<=on0){ // подготавливаем данные для порта
tmp |= (0b00000001);
}
if(cntr<=on1){
tmp |= (0b00000010); // подготавливаем данные для порта
}
if(cntr>on0){
tmp &= (0b11111110); // подготавливаем данные для порта
}
if(cntr>on1){
tmp &= (0b11111101); // подготавливаем данные для порта
}
PORTD = tmp; // выводим эти данные в порт
Понятно, что без магических чисел будет лучше, но направление верное?
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Вот для чего у вас для каждого порта проверяется 2 условия - для включения и для выключения? ШИМ организуется так: в начале периода ШИМа все каналы выключены, а включаются они в какой-то другой момент. То есть выходит, что для выключения никаких проверок делать не надо, это безусловное действие в начале периода ШИМ. Остается только понять, как это начало отследить.
И вот что я предлагаю: воспользоваться тем, что если однобайтовую переменную на каждой итерации цикла увеличивать на 1, то после значения 255 она получит автоматически значение 0... И смотрите, какой финт ушами получается: мы сравниваем значение этой переменной с заданной скважностью канала... Если заданная скважность больше, чем значение переменной, включать канал еще рано... А если меньше - надо включать...
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- plis
- Автор темы
- Не в сети
- Захожу иногда
- Сообщений: 47
- Спасибо получено: 0
Но и вы торопитесь подсказать.)
Я как-раз думал мысль про "одна за всех". Не скажу, что я её успел ясно сформулировать, но она была.)
P.S. Я со скважностью не очень.(
Сколько раз пытался запомнить что это - период/импульс или импульс/период, столько раз и забывал...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
- ARV
- Не в сети
- Администратор
Рассматривайте скважность, как процент "мощности" сигнала: если он постоянно 1 - это 100%, если постоянно 0 - 0%, ну при других значениях - пропорционально...
я не ленивый, я энергосберегающий...
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.