Страница 1 из 1

опрос кнопок MEGA8

Добавлено: Вт дек 09, 2014 23:12:00
neox
Здравствуйте! помогите плиз с кусочком кода на "С" (пишу в компиляторе CVAVR).
задача такова: восемь кнопок одной стороной сидят на массе а второй подключены к PORTD (PORTD настроен и включена подтяжка), как написать что если любые две из восьми кнопок выключены тогда PORTD.1=0 а если все восемь включены тогда PORTD.1=1 ?

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 05:15:03
Mishany
использовать оператор с условием if
заводим переменную для каждой кнопки удобнее использовать 1 байт для 8 кнопок где 1 нажата 0 отпущена, если сумма всех значений равна 2 то ....., если 8 тогда.....

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 07:13:38
COKPOWEHEU
А можно сдвигами и сложениями. Вспомнить, что сдвиг сохраняет не поместившийся разряд в бит С, а при сложении этот бит можно добавить к слагаемым за 1 команду. На ассемблере это будет реализовать проще и быстрее чем на Си. А потом также, сравнением с 2 и 8. Подсчет кнопок займет 16 тактов. С учетом нициализации, но без учета if'ов и ввода - 18.
А можно использовать битовую магию, у меня получилось 15 тактов на подсчет кнопок. Не считая if'ов и ввода. Вот это решение даже приведу
СпойлерХотя и не проверял

Код: Выделить всё

.def temp=r16
.def sum=r17

ANY_PROC:
     push temp //на всякий случай сохраняем временные регистры
     push sum
     in sum,PINB //считывание состояний кнопок. Наверное, и ввод и вывод в PORTD ошибка, пусть кнопки висят на PORTB
     mov temp,sum
     lsr temp //сдвиг на 1 байт
     andi sum,0x55 //первое волшебное число
     andi temp,0x55
     add sum,temp
     mov temp,sum
     lsr temp //сдвиг на 2 байта
     lsr temp
     andi sum,0x33 // второе волшебное число
     andi temp,0x33
     add sum,temp
     mov temp,sum
     swap temp //сдвиг на 4 байта, старший полубайт в данном случае неважен, он будет отрезан andi sum,0x0F
     add sum,temp
     andi sum,0x0F // третье волшебное число
//проверки. Поскольку предыдущая часть считает лог.1, сравнивать надо отпущенные кнопки, а не нажатые.
     cpi sum,0
          breq BTN_8
     cpi sum,6
          breq BTN_2
FUNC_END: //конец процедуры, посстанавливаем сохраненные значения
     pop sum
     pop temp
ret
BTN_8:
     sbi PORTD,1
rjmp FUNC_END
BTN_2:
     cbi PORTD,1
rjmp FUNC_END

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 07:15:06
pyzhman
Стоять-бояться! Это как? -
neox писал(а):...подключены к PORTD...тогда PORTD.1=0...тогда PORTD.1=1
Включать, выключать подтяжку?

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 07:53:39
ARV
а к чему такая спешка - такты считать?! у меня вот достаточно тупой, без всякой магии, вариант потребляет максимум 55 тактов на вычисление количества единиц во входном байте, причем это максимум, если ни одной единицы в байте нет - и тактов по-моему 3 всего :)

Код: Выделить всё

uint8_t sum=0;
// переменная data содержит анализируемый байт   
for(;data;data>>=1) sum += !!(data & 1);
// в переменной sum количество единиц, которые были в data
зато кратко и понятно, не то, что всякая ассемблерная магия :)))
P.S. про 55 тактов я соврал, поспешил. Прошу простить

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 08:10:19
COKPOWEHEU
Когда разница в 3.5 раза, уже стоит задуматься. Для данной задачи оптимизация не нужна, можно хоть if'ами каждый бит по отдельности считать. И, кстати, это все равно быстрее, 16 тактов независимо от размера. Что-то мне кажется, что среднее время работы у такого кода лучше, чем у Вашего, хотя минимальное, конечно, хуже.

Код: Выделить всё

 char x=PINB;
 char sum=0;
 if(x & (1<<0))sum++;
 if(x & (1<<1))sum++;
 if(x & (1<<2))sum++;
 if(x & (1<<3))sum++;
 if(x & (1<<4))sum++;
 if(x & (1<<5))sum++;
 if(x & (1<<6))sum++;
 if(x & (1<<7))sum++;
Но как же можно не поразвлекаться с побитовой магией и ассемблером!

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 09:15:59
BCluster
ARV писал(а):

Код: Выделить всё

!!(data & 1);
а зачем данная конструкция туть? я имею ввиду двойное отрицание.

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 09:58:30
WiseLord
Хотел было сначала ответить: чтобы любое ненулевое число превращалось в 1. Но потом понял, что в скобках вообще-то не любое число, а либо 0, либо 1. Так что да, двойное отрицание не нужно. И вполне хватит

Код: Выделить всё

for(sum = 0; data; data >>= 1) sum += (data & 1);

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 11:06:55
Mishany
в виде функции, которую можно вызывать по мере надобности и не влияющую на все остальное, как то так:
Спойлер

Код: Выделить всё

void button(void)
{
unsigned char a,i;
for(i=0;i<8;i++)
   {
    if((PIND&(1<<i))==0)   //проверяем в цикле все порты D
       {a++;}   //считаем сколько кнопок вкл/выкл
   }
if(a==6)   // если 2 из 8 выключены
   {
   //что то делаем....
   }
if(a==8)   // если все включены
   {
   //что то делаем....
   }
a=0;
}

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 11:45:02
ARV
COKPOWEHEU писал(а):уже стоит задуматься
да, стоит.
как раз хотел спросить: а вы этот алгоритм сами придумали или просто содрали из книжки Кнута (если не ошибаюсь)? если сами - честь вам и хвала. однако сумеете ли преобразовать свой код для 16, 32 или 64-битных чисел? согласны, что мой код сработает для любой (в разумных пределах) разрядности переменной data без какой бы то ни был модификации?
WiseLord писал(а):чтобы любое ненулевое число превращалось в 1. Но потом понял
перемудрил, признаю :)

среднее быстродействие моего варианта можно еще повысить, если цикл записать в соответствии с условиями задачи

Код: Выделить всё

for(;(sum < 2) && data; data >>=1)

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 11:52:51
Miw
COKPOWEHEU писал(а): А можно использовать битовую магию, у меня получилось 15 тактов на подсчет кнопок. Не считая if'ов и ввода. Вот это решение даже приведу
Вау! Это какой-то известный алгоритм или сам придумал ?

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 14:21:51
BCluster
А еще можно предподсчет сделать, если не жалко 256 байт. Оч быстро будет )

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 17:23:15
COKPOWEHEU
if((PIND&(1<<i))==0)
Если процессор будет считать честно - медленно будет, лучше завести для (1<<i) отдельныю переменную.
да, стоит.
как раз хотел спросить: а вы этот алгоритм сами придумали или просто содрали из книжки Кнута (если не ошибаюсь)?
Содрал, но не с Кнута, а с говнокода, там же понял принцип работы. Продолжить ряд магических чисел? 0x5555, 0x3333, 0x0F0F, 0x00FF, вроде так для двухбайтных чисел.
Ваш код сработает для любой разрядности, но время иногда бывает критичным, а примерно для половны чисел (у которых старший бит равен 1) оно будет максимальным.
ARV писал(а): среднее быстродействие моего варианта можно еще повысить, если цикл записать в соответствии с условиями задачи

Код: Выделить всё

for(;(sum < 2) && data; data >>=1)
И при sum=2 оно повиснет.
А еще можно предподсчет сделать, если не жалко 256 байт. Оч быстро будет )
Упрощенный вариант с массивом из 16 элементов.

Код: Выделить всё

unsigned char len[16]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
unsigned char calc(unsigned char x){
 return len[x&0x0F]+len[x>>4];
}

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 17:32:00
ARV
COKPOWEHEU писал(а):И при sum=2 оно повиснет.
почему?!

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 18:05:52
COKPOWEHEU
Условие (sum<2) будет всегда возвращать false, а false && x = false независимо от х.

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 18:17:41
ARV
COKPOWEHEU писал(а):Условие (sum<2) будет всегда возвращать false, а false && x = false независимо от х.
ну дык верно, условие false и цикл завершается. с чего ему виснуть?
за пределами цикла смотрим на sum - если оно 0 - значит нет ни одной 1, если не ноль - как минимум что-то есть...

Re: опрос кнопок MEGA8

Добавлено: Ср дек 10, 2014 21:43:27
COKPOWEHEU
Верно, прошу прощения.