ТавроФорум

ТавроФорум (https://forum.tavria.org.ua/index.php)
-   Компьютеры (https://forum.tavria.org.ua/forumdisplay.php?f=70)
-   -   AVR (https://forum.tavria.org.ua/showthread.php?t=70385)

Pilot 10.10.2013 09:16

да, похоже что ошибся с объемом кода.
Возможно действительно по разному компилирует.

Поставил avr studio 6 (в связи с тем, что на основном ПК она поломалась) на виртуалку, и там мучаюсь.


пошел другим путем - смотрю в дебаггере шаги.

Взял из программы кусок кода, и немного укоротил - сделал чисто дешифратор:

#include <avr/io.h>

int main(void)
{

DDRA = 0xFF;
PORTA = 0b00000000;

DDRD = 0x00;
PORTD = 0b00000000;

for (;;)
{
if ((PIND0==1)&&(PIND1==0)&&(PIND2==0))
PORTA = 0b00000100;
else if ((PIND0==1)&&(PIND1==1)&&(PIND2==0))
PORTA = 0b00110100;
else if ((PIND0==0)&&(PIND1==1)&&(PIND2==0))
PORTA = 0b00100100;
else if ((PIND0==0)&&(PIND1==1)&&(PIND2==1))
PORTA = 0b00000010;
else if ((PIND0==0)&&(PIND1==0)&&(PIND2==1))
PORTA = 0b00000010;
else if ((PIND0==1)&&(PIND1==0)&&(PIND2==1))
PORTA = 0b00111100;
else
PORTA = 0b00000001;
}
}

По шагам, первые 4 (назначение портов) дебаггер отрабатывает, а потом сразу прыгает в строку
else
PORTA = 0b00000001;

не отрабатывая никакие другие. и оттуда его ничем не выудить...

ошибок, предупреждений нет.

Kino 10.10.2013 11:39

значит не отрабатывают PIND0. попробуй прочитать пины по другому.
в еще лучше сделать это через switch.

не помню как в студии, в ирае это объявлено как
#define PIND0 0
естественно никогда единице равняться не будет.

вот. так как-то.
if ((PIND & 1 ==1) &&(PIND & 2==0) && (PIND &4 ==0))

Globus 18.10.2013 18:55

Ничего, я пять копеек вставлю?
Полгодика uC не ковырял, но вот так удобно:
#define cw_pressed !(PIND & 0b00000100)
#define ccw_pressed !(PIND & 0b00001000)


Потом что-то типа:
ISR(INT0_vect){
cli();
_delay_ms(1);
ClrBit(PORTD,PS);
if(cw_pressed) t = ((t << 7) | (t >> 1));
if(t & 1) SetBit(PORTD,DIR1); else ClrBit(PORTD,DIR1);
if(t & 2) SetBit(PORTD,DIR2); else ClrBit(PORTD,DIR2);
_delay_ms(1);
SetBit(PORTD,PS);
sei();
}

Только при куче датчиков может прерываний не хватить, но схемотехнически, через диоды, можно все кнопки на одно внешнее прерывание посадить.

Pilot 20.10.2013 17:49

5 копеек - это хорошо, но мое понимание еще не дошло до данного кода...
проще говоря - я из этого ничего не понял.
Т.к. мое изучение програмирования МК и вообще С++ ограничивается на данный момент только вот этим одним устройством.
Но за код все равно спасибо - найду на работе час-полтора свободного времени - попробую разобраться с данной записью

Globus 20.10.2013 19:32

Я тогда пока накидаю недостающих для понимания строк и картинку.
Также добавлю, что у меня биполярный шаговик.
#define PS PD6
#define DIR1 PD4
#define DIR2 PD5

#define SetBit(Port,bit) Port |= _BV(bit)
#define ClrBit(Port,bit) Port &= ~_BV(bit)
#define Bit(bit) (1 << (bit))

volatile unsigned char t;

void init() {
cli();
DDRD=0b01110000;
PORTD=0b00001110;

ClrBit(MCUCR,ISC01);
ClrBit(MCUCR,ISC00);

ClrBit(MCUCR,ISC11);
ClrBit(MCUCR,ISC10);

SetBit(GICR,INT0);
SetBit(GICR,INT1);
set_sleep_mode(SLEEP_MODE_IDLE);
t = 0b11001100;
sei();
}

int main(void) {

init();
while(1);
return 0;
}

http://radiohlam.ru/control/images/b...tep_motor1.jpg
К картинке необходимо добавить, что я использовал МС управления ШД, который при подаче единиц на DIR1 и DIR2 устанавливает одну полярность на обмотках 1 и 2 соответственно, а при "нулях" - противоположную.
PS управляет режимом с пониженным потреблением - обеспечивает удержание ротора в паузах.

Pilot 29.10.2013 17:36

Начинаю все с начала, в связи с некоторыми изменениями в логике устройства (введение АЦП)

первым делом разбиваю всю программу на подпрограммы, и смотрю как работает.
Первая программа - дешифратор задатчика (порт B), для наглядности выводит информацию в унитарном виде на порту D
(данные на порту В - третий столбец таблицы, диод на порту D - первый столбец таблицы)
прочерки означают незадаваемое положение, кресты - что при указанном состоянии прочих ног, состояние данной не учитывается.






#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>


int main(void)

{

PORTA=0b11111111;
DDRA=0b00000000;

PORTB=0b11111111;
DDRB=0b00000000;

PORTC=0b00000000;
DDRC=0b11111111;

PORTD=0b00000000;
DDRD=0b11111111;

PORTD=0b11111111;
_delay_ms(2000);
PORTD=0b00000000;




while(1)
{


//Задатчик;

//Таблица соответствия состояния энкодера / задатчика положения / индикации положения;

// № | PA4 PA5 PA6 | PB0 PB1 PB2 PB3 | PD2-6;
// --------------------------------------------------;
// 1 | 0 1 1 | 1 1 x 1 | 00010;
// 2 | 0 0 1 | 0 1 0 1 | 11010;
// 3 | 1 0 1 | 0 1 1 1 | 10010;
// 4 | 1 0 0 | x x x 0 | 00001;
// 5 | 1 1 0 | - - - - | 00001;
// 6 | 0 1 0 | 0 0 x 1 | 11110;
// 7 | 0 1 0 | 1 0 x 1 | 00110;


com: if (PORTB3==0)
{
PORTD=0b00001000;
}
else if(PORTB3==1 && PORTB0==1 && PORTB1==1)
{
PORTD=0b00000001;
}
else if(PORTB3==1 && PORTB0==1 && PORTB1==0)
{
PORTD=0b01000000;
}
else if(PORTB3==1 && PORTB0==0 && PORTB1==0)
{
PORTD=0b00100000;
}
else if(PORTB3==1 && PORTB0==0 && PORTB1==1 && PORTB2==0)
{
PORTD=0b00000010;
}
else if(PORTB3==1 && PORTB0==0 && PORTB1==1 && PORTB2==1)
{
PORTD=0b00000100;
}
else
{
goto com;
}
}
}



При проверке дебаггером ошибок и замечаний нет, но когда провести пошаговыую проверку, тоон просматривает назначение портов, и замирает.
до тела программы не доходит.
При проверке в эмуляторе (протеус), ругается на то, что в hex-файле найдена какая-то ошибка.

ps: to Глобус
1 - к сожалению с твоим кодом не разобрался
2 - двигатель у меня не шаговый, а коллекторный.

pps: может кто посоветует более адекватное ПО для написания, отладки и компиляции программы?

Yurasvs 29.10.2013 19:09

Цитата:

Сообщение от Pilot (Сообщение 2492866)

pps: может кто посоветует более адекватное ПО для написания, отладки и компиляции программы?

Для первых шагов (да и не только) я бы очень посоветовал ИмиджКрафт ICC AVR. Сейчас ломаная седьмая версия есть в инете. Совместим с симулятором АВР Студио. Там есть очень удобный так называемый аппликейшн билдер, для быстрой и безошибочной автоматической генерации кода настройки портов, прерываний, таймеров и остальной периферии. Довольно удобная интегрированная среда, есть даже встроенное окно для программирования чипа не отходя от кассы (поддерживает железо Понипрога). Поддерживает также бинарное представление чисел (иногда очень повышает читаемость кода), есть документация на русском языке.
Есть еще Кодевижн, но мне больше нравится ICC, там работающий проект можно создать буквально за несколько кликов мышкой. Ну и конечно IAR, но это для профессионалов, можно позже на него переползти.

Kino 29.10.2013 21:30

Блин. Надо найти время и запилить вам этот проект. Осталось узнать как.

Вы на функции программу не перевели, у Вас остался goto. Это не принципиально, но и не функция.
Немного теории (совсем чуть-чуть). Регистры портов в авр делятся на три типа, направление, выход и вход, называються соответственно DDRx, PORTx и PINx.
Т.е. чтобы выдать на порт значение используем PORT, чтобы считать - используем PIN.

Давайте так.
Создайте функцию. Типа char GetEncoder(void);

И в ней, вместо этого, вызывайте её:

m: if (PORTB3==0)
{
PORTD=0b00001000;
}
else if(PORTB3==1 && PORTB0==1 && PORTB1==1)
{
PORTD=0b00000001;
}
else if(PORTB3==1 && PORTB0==1 && PORTB1==0)
{
PORTD=0b01000000;
}
else if(PORTB3==1 && PORTB0==0 && PORTB1==0)
{
PORTD=0b00100000;
}
else if(PORTB3==1 && PORTB0==0 && PORTB1==1 && PORTB2==0)
{
PORTD=0b00000010;
}
else if(PORTB3==1 && PORTB0==0 && PORTB1==1 && PORTB2==1)
{
PORTD=0b00000100;
}
else
{
goto com;
}
}
}


char GetEncoder(void)
{

if (PINB & 0x80 == 0)
{
PORTD=0b00001000;
return 1; // выход с флагом 1, что сработал по "if (PINB & 0x80 == 0) "
}

// иначе там единица, продолжаем

switch (PINB & 0x0F)
{
case 0x0B:
PORTD=0b00000001;
break;
case 0x09:
PORTD=0b01000000;
break;
case 0x08:
PORTD=0b00100000;
break;
case 0x0A:
PORTD=0b00000010;
break;
case 0x0E:
PORTD=0b00000100;
break;

default: // если ничего не совпало - возвращаем 2.
return 2;
break;
}




в цикле
while(1)
{
char x;

x = GetEncoder();


if (x == 1) знач вернулся вернулся по "if (PINB & 0x80 == 0) "
или так
while (GetEncoder()) == 2) {} // т.е. вызываем GetEncoder, пока не вернется что-то отличное от двойки.

} // скобка конца while()

Globus 30.10.2013 11:26

Pilot, а что в таличке состояний означают "x" и "-"?

Pilot 30.10.2013 13:48

прочерки означают незадаваемое положение (задатчиком его нельзя выбрать, но в энкодере оно есть)

кресты - что при указанном состоянии прочих ног, состояние данной не учитывается.

Said 30.10.2013 14:43

Pilot
Отмечучь и я, чисто в рекомендательном плане.
Уберите goto в коде поста №31, оно там ни к чему вообще, while(1) и так выполнится с первой строчки, если не сработает ни одно условие, при этом желательно в конце while поставить небольшую задержку (~100мс), если планируете использовать какие-то дополнительные процедуры.
И я б, честно, подобные строки if(PORTB3==1 && PORTB0==1 && PORTB1==1)
заменил бы на if (PORTB==0b00001011)
Почему? Да потому что оператор if выполнится всего один раз вместо 5 :)
Ну или для полноты понимания, при помощи define создайте варианты необходимых условий. Вы хоть код понимать будете ну и помочь будет легче вновь читающим.
К примеру
#define VAR0 0b00001011
if (PORTB==VAR0)...
или
if (PORTB & VAR0)...

что полностью заменяет if(PORTB3==1 && PORTB0==1 && PORTB1==1) и облегчает чтение.
Этим разгрузите код и вероятно облегчите жизнь дебугеру.
Конечно, если так можно, а пятой точкой ощущаю, что можно наврняка.

Kino, к стати, правильно подсказывает, насчет отдельной процедуры, но по опыту работы с контроллерами, я б нестал использовать такие тяжелые формы как case, от этого легче не станет, а код усложнит и займет на порядок больше тактов, описанный выше мною вариант более применим к контроллерам, со статическмими условиями регулятора.

Pilot 30.10.2013 14:54

Цитата:

Сообщение от Kino (Сообщение 2493122)
длинное сообщение

вот здесь вроде немного понятно:


char GetEncoder(void)
while (GetEncoder()) == 2)
{
if (PINB & 0x80 == 0)
{
PORTD=0b00001000;
return 1; // выход с флагом 1, что сработал по "if (PINB & 0x80 == 0) "
// иначе там единица, продолжаем
}

// я так понимаю, что если 8-ая нога порта В равна 0, то на выходе порта D будет 00001000, в противном случае идем дальше.


switch (PINB & 0x0F)
{
case 0x0B: PORTD=0b00000001; break;
case 0x09: PORTD=0b01000000; break;
case 0x08: PORTD=0b00100000; break;
case 0x0A: PORTD=0b00000010; break;
case 0x0E: PORTD=0b00000100; break;

default: return 2; break;
// если ничего не совпало - возвращаем 2.
}
// Здесь, как я понял, определение выражения на 4х ногах порта В (PINB & 0x0F) - (в двоичной =00001111), тех, на которых кнопки задатчика
// соответствие входа выхода вроде бы такое:
// 1011 - 00000001
// 1001 - 01000000
// 1000 - 00100000
// 1010 - 00000010
// 1110 - 00000100
}

// До тех пор, пока будет возвращатся 2, будет выполнятся оператор while. в противном случае опреатор прекращается, я имею на порту D нужный сигнал и могу выполнять следующую процедуру.



Правильно понял?

Said 30.10.2013 15:03

Условие if (PINB & 0x80 == 0) как по мне не корректно. В данном случае всегда будет ложь, т.к. 0x80 не равно 0 :)
Для успокоения совести напишите:
if ((PINB & 0x80) == 0)
или просто
if !(PINB & 0x80)

Pilot 30.10.2013 16:20

Цитата:

Сообщение от Said (Сообщение 2493839)
опять большое письмо

т.е. ты предлагаешь сделать следующим образом:

#define com1 0b00001011
#define com2 0b00001010
#define com3 0b00001110
#define com4 0b00000000
#define com6 0b00001000
#define com7 0b00001001


char GetEncoder(void)
while (GetEncoder())==2)

if PORTB==com1
{PORTD=0b00000001}
else if PORTB==com2
{PORTD=0b00000010}

else if PORTB==com3
{PORTD=0b00000100}
else if PORTB==com4
{PORTD=0b00001000}

else if PORTB==com6
{PORTD=0b00100000}
else if PORTB==com7
{PORTD=0b01000000}
else
{return 2}

Said 30.10.2013 16:49

Но в таком виде код не пройдет компиляцию, ибо ошибок тьма.
И, если GetEncoder разовая функция, то ее нет смысла оформлять отдельной процедурой.
В остальном направление мысли верно.
Ну вот как-то так:

#define com1 0b00001011
#define com2 0b00001010
#define com3 0b00001110
#define com4 0b00000000
#define com6 0b00001000
#define com7 0b00001001


char GetEncoder(void)
{
if (PORTB==com1) PORTD=0b00000001;
else if PORTB==com2 PORTD=0b00000010;
else if (PORTB==com3) PORTD=0b00000100;
else if (PORTB==com4) PORTD=0b00001000;
else if (PORTB==com6) PORTD=0b00100000;
else if (PORTB==com7) PORTD=0b01000000;
else return 2;
return 0;
}

int main(void) // главная рутина
{
//.........какой-то код............

while (GetEncoder()==0) _delay_ms(100);
//или
//while (GetEncoder()>0) _delay_ms(100);
//зависит от желаемого результата

//.........какой-то код............
}

Pilot 31.10.2013 09:11

Вот алгоритм работы устройства, чтоб было понятно о чем речь:
http://savepic.su/3712486m.jpg

Said 31.10.2013 09:29

Так у Вас все есть, вот четко по алгоритму и действуйте. Просто код старайтесь дедать максимально простым и учитывайте, что необходимы задержки при отработке команд до чтения состояния, причем величина задержек прямопропорциональна инертности исполнительных элементов.

Pilot 31.10.2013 09:37

Цитата:

Сообщение от Said (Сообщение 2493941)
И, если GetEncoder разовая функция, то ее нет смысла оформлять отдельной процедурой.

Тут функция должна быть (чтоб не было недоразумения в коде - getcomander) несколько раз, поэтому наверное, все-таки лучше функцией.
И остальные так-же.

#define com1 0b00001011
#define com2 0b00001010
#define com3 0b00001110
#define com4 0b00000000
#define com6 0b00001000
#define com7 0b00001001


char GetComander(void)
{
if (PORTB==com1) com=0b00000001;
else if PORTB==com2 com=0b00000010;
else if (PORTB==com3) com=0b00000100;
else if (PORTB==com4) com=0b00001000;
else if (PORTB==com6) com=0b00100000;
else if (PORTB==com7) com=0b00100000;
else return 2;
return 0;
}
While (GetComander()==2)_delay_ms(100);
//
если какое-то условие верно, то на выходе получим соответствующее значение, а функция GetComander будет равна 0. Ну и соответственно, пойдет дальнейшее выполнение программы.
// если никакое условие не совпадет, то функция GetComander будет равна 2 и повторится через 0,1с.



Теперь вторая часть дешифратора, в которой не может быть неоднозначного положения, т.е. если возникает неопознанное значение, то программа должна "выпасть" в ошибку:




#define enc1 0bx110xxxx
#define enc2
0bx100xxxx
#define enc3
0bx101xxxx
#define enc4
0bx001xxxx
#define enc5
0bx011xxxx
#define enc6
0bx010xxxx // где "х" - может быть любым значением (используется отдельно от дешифратора, но как это прописать - я не знаю вроде бы как то так: )
char GetEncoder(void)
{
if (PORTA &
0b01110000 ==enc1) enc=0b00000001;
else if (PORTA & 0b01110000 == enc2)
enc=0b00000010;
else if (PORTA
& 0b01110000 == enc3) enc=0b00000100;
else if (PORTA
& 0b01110000 == enc4) enc=0b00001000;
else if (PORTA
& 0b01110000 == enc5) enc=0b00100000;
else if (PORTA
& 0b01110000 == enc6) enc=0b00100000;
else return 2;
return 0;
}
if (GetEncoder()==2)
ERROR \\
переход к подпрограмме ошибки
else
\\
продолжаем выполнять основную программу

Pilot 31.10.2013 14:27

Цитата:

Сообщение от Said (Сообщение 2494597)
Так у Вас все есть, вот четко по алгоритму и действуйте. Просто код старайтесь дедать максимально простым и учитывайте, что необходимы задержки при отработке команд до чтения состояния, причем величина задержек прямопропорциональна инертности исполнительных элементов.

Да вот я и стараюсь действовать.
Проблема в том, что я не изучал ранее языки программирования (окромя паскаля в 9м классе).
И вот собственно мне стал вопрос изготовления блока управления.
На нем я и решил поучиться столь интересному занятию.

Конечно, как для первой программы у меня возникло дохренадостаточно много проблем:
- программа на операторе IF/ELSE написана вроде как правильно, замечаний в компиляторе нет, но она не работает в контроллере. Вот и обратился за помощью (и заодно - получу новые знания :D, надеюсь)

Said 31.10.2013 17:40

Ну, судя по представленному Вами алгоритму, вам, кроме if/else, врядли потребуются более изощренные компоненты языка.....

Pilot 31.10.2013 18:28

только он у меня нихрена не заработал... к сожаленью

Хотя 2 человека, которые пишут на С++, но не программируют контроллеры, сказали, что код правильный

Said 31.10.2013 18:44

Значит Вы просто где-то промахиваетесь либо с начальной конфигурацией контроллера либо в схеме.

Yurasvs 31.10.2013 20:32

Фузы правильно зашили?

Kino 31.10.2013 21:01

не фьюзах дело. настройки где-то кривые. он пишет под avrstudio + winavr.

fox_12 04.11.2013 14:24

Сорри что вклиниваюсь в интеллектуальную беседу, а, извиняюсь, - более простые программы писали?
Светодиодиком получалось моргать?
К сожалению - из топика не вижу ни полную схему, ни программу. Но пробовали собирать на макете, и вместо датчиков использовать кнопки, а вместо исполнительных инструментов те же светодиоды?
Проблему дребезга контактов концевиков решили?
Все входы и выходы проинициализировали правильно? Ничего у вас в "воздухе" не осталось висеть?


Текущее время: 18:16. Часовой пояс GMT +3.

Перевод: zCarot Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.