Как стать автором
Обновить

Комментарии 34

>>Видео, к сожалению, выложить не могу.
а схему?
Схему в данном проекте я не рисовал, но могу показать плату
image
Первая мысль: «где на этой схеме стоит кран?» =) Чем-то неуловимо похоже на макет.
Нет. Хотя про сам макет могу сказать мало. Моя только электроника. Точно знаю что демонстрирует демонтаж устаревшей электростанции. Типа сначала построили и пустили в эксплуатацию новую, а старую начинают разрушать только после этого.
Петард надо побольше! Петард!
Интересно зачем для атмеги такой суровый предохранитель?
Это у него него только вид суровый, а номинал 1А. Через него ведь еще и ток сервоприводов течет!
кучу if (takt == ..) можно заменить на один switch, читается лучше
Да, действительно. Это неплохая мысль. Я просто скорее электронщик, чем программист.
НА куче if получается несколько компактней. По крайней мере на AVR. Впрочем, тут смотреть надо. Может компилятор догадается развернуть кейс через индексный переход. Не всегда, но у него бывают такие озарения. Особенно у IAR.

Если хочется поменьше условных переходов, то можно еще как-нибудь вот так можно написать.

unsigned int servo = takt / 2; // or takt >> 1

unsigned char angle = angles[servo];
unsigned char mask = 1 << servo;

if (takt & 1) {
        PORTC &= ~mask;
        OCR1A  = cycle - angle;
} else {
        PORTC |= mask;
        OCR1A  = angle;
}

takt = (takt + 1) & 7;
если все выходы висят на одном порту — это, пожалуй, лучший вариант
определяйте порты в дейфайнах. Код не переносим.
Согласен. Я лишь привел пример того, как можно избавиться от полотна if'ов.
максимальная скважность получается 25%?
>Примечательно, что оба аналоговых привода могли работать нормально только пару минут, а потом начинать произвольно дергаться, зажиматься в крайних положениях и т.д.

Вот это в высшей мере странно. Т.к. электроники внутри там почти нет. Тупейшая разностная схема которая зависит ТОЛЬКО от импульсов. А импульсы часом никуда не уплыли?

Бегло глянул на алгоритм — импульсы идут друг за другом, ширина периода каждого зависит от ширины соседей получается?
Сервоприводы специально ездил проверял в магазин — с сервотестером любые работают отлично, но с моей электроникой аналоговые дружить отказываются. Я не смог найти этому никого разумного объяснения.

Частота фиксированная и определяется переменной cycle (хотя ее надо было прописать как константу). У меня период следования был 18000мкс и четыре привода. Я записал в cycle 4500мкс — это время которое отводится на управление одним приводом, то есть передние фронты импульсов задержаны друг относительно друга именно на это время. Когда прерывание вызывается в первый раз — в OCR1A записывается длительность импульса управления приводом. При втором срабатывании, когда импульс заканчивается, в OCR1A записывается (cycle-angle1), время которое осталось до перехода к следующему приводу. В итоге частота следования импульсов не зависит от длительности управляющих импульсов.
Чудес не бывает. Берите осциллограф и сравнивайте сигнал сервотестера и своей схемы. Где то там косяк. Скорей всего джиттер.
У меня в распоряжении сервотестера к сожалению нет. Да и самой платы тоже. Примечательно, что на табло тестера угол выставляется в тех же единицах к которым я пришел, то есть от 800 до 2300, примерно. Это меня наводит на мысль, что я делаю все правильно.
Ну, а из плюсов алгоритма. ОЧЕНЬ быстрая обработка прерывания. Это большой большой плюс.

Минус — плавающая частота импульсов. Не критично для положения, но критично для скорости вращения. Впрочем, тут не подразумевается разгон серв, так что влияние незначительное.
Почему не подходит аппаратный ШИМ на контроллерах?
потому что их обычно 1-3, а для, скажем, гексапода нужно 12-16
у 16 максимальная скважность будет 100/16
тут лучше использовать внешний PWM, например, tlc5940
Вставлю свои пять копеек.

За реализацию респект, уважение и поклон!

Но, критика ценнее и важнее похвалы. Тот кусок кода, который вы привели совершенно не переносим. Более того, если вы переставите сервопривод на другую ножку другого порта, вам придётся перепахивать всю программу.

Конкретно вот: PORTC |= 0b00000001; — это форменное безобразие и источник множества ошибок и проблем! Как это искоренить в коде я не представляю. Самое ужасное, что если вы решите перенести код на другой процессор, или о боже, другой архитектуры, то это будет весьма тривиальной задачей.

Я даже намедни писал пост по теме: dlinyj.livejournal.com/593356.html

Собтвенно говоря, у меня был случай, разные релюшки были разбросаны по разным портам и пинам, полностью хаотично. Самый лучший конечно способ был сделать структуру портов и масок, но у меня нехватило памяти контроллера и запала, и я сделал примерно так:

#define		port_rele1	PORTA
#define		ddr_rele1	DDRA
#define		pin_rele1	PINA
#define		port_rele2	PORTC
#define		ddr_rele2	DDRC
#define		pin_rele2	PINC
...
//pins define
//port_rele1
#define		K1	5
#define		K2	4
#define		K3	3
#define		K4	0
#define		K5	1
#define		K6	2
...


И далее обращался к ним вот так:

//инициализация
void hardware_init()
{
	ddr_rele1	|=(1<<K1)|(1<<K2)|(1<<K3)|(1<<K4)|(1<<K5)|(1<<K6);
	port_rele1	|=(1<<K1)|(1<<K2)|(1<<K3)|(1<<K4)|(1<<K5)|(1<<K6);
	ddr_rele2	|=(1<<K7)|(1<<K8)|(1<<K9);
...
}

//для включения, выключения пользовался макросами

#define BIT_ON(port, bit)		do {port |= (1<<bit);}while(0) 
#define BIT_OFF(port, bit) 		do {port &=~(1<<bit);}while(0)
#define BIT_INVERCE(port, bit) 		do {port ^=(1<<bit);}while(0)

//пример управления:
	BIT_OFF(port_rele1,K1);



Самое ценное, что поменяв только дефайны, я могу перенести данный код даже на другой процессор, если там другой тип обращения к пинам порта, то поменять мкакросы. Мне не нужно для этого будет перепахивать весь код.

Надеюсь комментарий будет полезен.
Кодеры с работы тоже долго надо мной потешались. Просто у меня была очень конкретная разовая задача и я не уделил должного внимания улучшению читаемости кода. Кроме того здорово сроки поджимали — я перепробовал кучу вариантов управления и времени на финальную реализацию почти не осталось. Написал «чтобы работало». Следующим шагом будем думать над красотой кода. Спасибо за пример.
Я так понимаю этот комментарий был адресован мне :).

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

Здесь нет места красоте. Поймите, что такой подход сокращает время разработки и уменьшает количество ошибок раз в десять!
> Надо писать сразу читаемый код
Ну у вас тоже неидеальный код.
К примеру, стандарт де-факто — написание дефайнов только капсом.
Или я так и не понял что за сумрачная конструкция do/while(0), её используют часто для того чтобы можно break юзать вместо goto, но в данном случае совсем мне непонятно.
В дефайнах параметры не обернуты скобки:
BIT_ON(BASE_PORT + PORT_STATUS, 2 * i) очень удивит компилятор.
Ну и всякая мелочевка типа рандомной расставки пробелов.
Ну и всякая мелочевка типа рандомной расставки пробелов.
это не мой косяк, а косяк интрепретации хабром моего кода. У меня всё ок.

К примеру, стандарт де-факто — написание дефайнов только капсом
впервые слышу, быть может это конечно и оправданно.

Или я так и не понял что за сумрачная конструкция do/while(0), её используют часто для того чтобы можно break юзать вместо goto, но в данном случае совсем мне непонятно.
Это макросы Аскольда Волкова, на сколько я понимаю использование ду вайл делается для переносимости кода. Не разбирался.

BIT_ON(BASE_PORT + PORT_STATUS, 2 * i) очень удивит компилятор.
Силился представить, когда мне понадобится такая конструкция, но не смог… В особенности конструкция BASE_PORT + PORT_STATUS — не будет работать со сто процентной вероятностью. Но про скобки возможно замечание и стоящее.
> Это макросы Аскольда Волкова
Вспомнил еще один вариант:
if (XXX)
    SOME_MACRO();

Если макрос состоит из нескольких выражений (#define SOME_MACRO bla(); bla();), то нужно обернуть в {}.
Но в данном случае это не так (всего одно выражение), и разворачивать его в блок совсем не обязательно.
> впервые слышу, быть может это конечно и оправданно.
Я видел в нескольких code style стандартах.
В том числе и гугловском.
> Силился представить, когда мне понадобится такая конструкция, но не смог…
Ну к примеру есть 4 порта — 2 набора команды/данные для 2х внешних устройств.
Выясняем при конфигурации к какому набору подрублено устройство, и пишем в переменную base_port, а к самим портам уже обращаемся через base_port + PORT_DATA и base_port + PORT_COMMAND. В компьютерном железе такое не редкость. В МК думаю тоже бывает.
А вот и видео разрушителя с краном:



и экскаватора:



как говорил раньше — кран все-равно вибрирует, но это уже связано с конструкцией.
Когда начал читать, думал все привода будут задействованы в одной модели на разные движения.
ЗЫ. Плата у вас красивая получилась. ЛУТ?
Хотели изначально сделать чтобы клешня у разрушителя открывалась, но в итоге не получилось и двигается только три модели машин.
Для изготовления плат давно уже использую ЛУТ.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации