вопрос такой: допустим я хочу подключить к атмеге несколько сервоприводов и подавать на них сигнал вот таким образом: Код (C++): #define _setH(port,bit) do{port |= (1 << bit);}while(0) #define _setL(port,bit) do{port &= ~(1 << bit);}while(0) #define ServoX PORTB, 0 #define ServoY PORTB, 1 #define ServoZ PORTB, 2 можно ли как-то через какую-нибудь функцию или макрос, сделать так, чтобы при вводе номера (индекса) на выход поступало одно из этих имен (например ServoX), содержащих в нутри себя порт и бит? то есть типа функция(номер) { если (номер ==1){ ретурн(ServoX);} если (номер ==2){ ретурн(ServoY);} и т.д. } то есть чтобы потом можно было написать _setH(функция(n)) и при n==1 на ногу PORTB, 0 подавалась единица, при n==2 на ногу PORTB, 1 подавалась единица и т.д.
желательно С в ардуино что-то подобное сделано на С++, и я так и не смог толком разобраться как оно там работает. в смысле я пытался курить библиотеку servo, кажется понял что там происходит в прерывании, но на остальном сломал мозг, там какие-то конструкторы Servo::Servo() не понимаю как это устроено, и еще это все размазано по многим файлам, я же хочу наиболее простым способом только проверить алгоритм который в прерывании там делается.
В C в момент компиляции такие вещи можно сделать только на m4, что требует инфраструктуры линукса, и тоже в целом весьма непросто. Так что пишите просто функциями.
короче в результате гугления и бездумного копипаста получилось что-то вроде этого. Не понимаю как оно работает, но работает. Код (C++): #include <avr/io.h> #define F_CPU 8000000UL #include <avr/interrupt.h> #include <util/delay.h> typedef struct servo_t { volatile uint8_t *port; uint8_t pin; }Servo; void WriteServo(Servo *servo, uint8_t value) { if(value==1) {*servo->port |= servo->pin;} else {*servo->port &= ~servo->pin;} } Servo ServoX = {&PORTB, 1<<0}; Servo ServoY = {&PORTB, 1<<1}; Servo ServoZ = {&PORTB, 1<<2}; int main(void) { DDRB=0b00000111; while (1==1) { WriteServo(&ServoX, 1); _delay_ms(2); WriteServo(&ServoX, 0); WriteServo(&ServoY, 1); _delay_ms(2); WriteServo(&ServoY, 0); WriteServo(&ServoZ, 1); _delay_ms(2); WriteServo(&ServoZ, 0); _delay_ms(2); } return 0;} Не факт что это приближает к результату, то есть к моделированию ардуиновой библиотеки Серво, в которой (насколько я понял) циклически перебираются друг за другом все сервы, и по очереди на каждую пускается сигнал нужной продолжительности.
короче вот такая фигня Код (C++): #include <avr/io.h> #define F_CPU 8000000UL #include <avr/interrupt.h> #include <util/delay.h> #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) #define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) #define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo #define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo #define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached #define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds typedef struct pin_t { uint8_t *port; uint8_t bit; }Pin; void WritePin_on(Pin *pin) {*pin->port |= pin->bit;} void WritePin_off(Pin *pin) {*pin->port &= ~pin->bit;} void WritePin(Pin *pin, uint8_t val){ if (val==0){*pin->port &= ~pin->bit;} else {*pin->port |= pin->bit;} } Pin ServoX = {&PORTB, 1<<0}; Pin ServoY = {&PORTB, 1<<1}; Pin ServoZ = {&PORTB, 1<<2}; Pin Led = {&PORTB, 1<<7}; uint16_t ticksX=(unsigned int)usToTicks(DEFAULT_PULSE_WIDTH); uint16_t ticksY=(unsigned int)usToTicks(DEFAULT_PULSE_WIDTH); uint16_t ticksZ=(unsigned int)usToTicks(DEFAULT_PULSE_WIDTH); uint16_t ticks=(unsigned int)usToTicks(DEFAULT_PULSE_WIDTH)*3; uint16_t value1=(unsigned int)usToTicks(DEFAULT_PULSE_WIDTH); uint16_t value2=(unsigned int)usToTicks(DEFAULT_PULSE_WIDTH); uint16_t value3=(unsigned int)usToTicks(DEFAULT_PULSE_WIDTH); uint8_t s; ISR(TIMER1_COMPA_vect) { if (s==0){ TCNT1 = 0; OCR1A = TCNT1 + ticksX; WritePin_on(&ServoX); s++;} else if (s==1){ OCR1A = TCNT1 + ticksY; WritePin_off(&ServoX); WritePin_on(&ServoY); s++;} else if (s==2){ OCR1A = TCNT1 + ticksZ; WritePin_off(&ServoY); WritePin_on(&ServoZ); s++;} else if (s==3){ TCNT1=0; OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL)-ticks; WritePin_off(&ServoZ); s=0;} } void ServosTicsRefresh(void){ uint8_t oldSREG = SREG; cli(); ticksX = value1; ticksY = value2; ticksZ = value3; ticks=ticksX+ticksY+ticksZ; SREG = oldSREG;} int main(void) { TCCR1A = 0; // normal counting mode TCCR1B = 0b00000010; // set prescaler of 8 TCNT1 = 0; // clear the timer count TIFR |= (1<<4); // Atmega8 clear any pending interrupts; TIMSK |= (1<<4); // Atmega8 enable the output compare interrupt // TIFR1 |= _BV(OCF1A); // clear any pending interrupts; // TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt SREG |= (1<<7); DDRB=0b10000111; OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL); while (1==1) { if(PINB & ( 1 << 6 )){ value1=usToTicks(1000); ServosTicsRefresh();} WritePin(&Led,1); _delay_ms(2); WritePin(&Led,0); _delay_ms(2); } return 0;} в принципе оно вроде работает то есть последовательно обрабатывает сервоприводы с периодом в 20миллисекунд. проблема в том, что я не представляю как сделать чтобы это могло работать с произвольным количеством сервоприводов (не более 8ми например), и при этом можно было подключать сервоприводы, не переписывая всю программу. То есть идея в том, чтобы реализовать аналог библиотеки Servo, но на "чистом Си", и более тупыми методами. страдаю хернёй да? суть проблемы в том, что надо как-то проиндексировать сервоприводы, чтобы в прерывании программа могла обращаться к ним по какому-то номеру, неужели на Си такое невозможно?
Надо как-то проиндексировать сервы, чтобы потом можно было обращаться к ним по номеру. то есть например серва подключенная к PODTB,0 будет с индексом "1", к PORTD,4 с индеском "2", и т.д. в произвольном порядке, а потом при n==1 обрабатывается PODTB,0, при n==2 PORTD,4 и т.д. но это нереально.
После Код (C++): Pin ServoX = {&PORTB, 1<<0}; Pin ServoY = {&PORTB, 1<<1}; Pin ServoZ = {&PORTB, 1<<2}; напишите: Код (C++): Pin* ServoArray[] = { &ServoX, &ServoY, &ServoZ }; Обращение: Код (C++): // Было WritePin_off(&ServoX); // Стало WritePin_off(0); // где void WritePin_off(uint8_t num) { Pin* pin = ServoArray[num]; *pin->port &= ~pin->bit; } И будет обращение по номеру.
Заработало! блин я пытался засунуть эти штуки в массив по всякому и ничего не получалось. второй день курю про эти указатели, но не вкуривается. А как можно правильно засунуть в массив например адреса портов? PortsArray[] = {&PORTA,&PORTB,&PORTC,&PORTD}; так не работает например можно так же через структуру, но это наверно не оптимальный вариант
еще вопрос такой: http://easyelectronics.ru/rabota-s-portami-vvoda-vyvoda-mikrokontrollerov-na-si.html "Развивая тему с препроцессором можно задавать номер ножки и ее порт в одном определении, ведь Си-шный препроцессор работает не с идентификаторами и не какими-то ни-было языковыми конструкциями, а просто со строковыми литералами. Код (C++): #define LCD_RS PORTA, 0 //define MCU pin connected to LCD RS #define LCD_RW PORTB, 1 //define MCU pin connected to LCD R/W #define LCD_E PORTB, 2 //define MCU pin connected to LCD E Добавим к этому средства для манипуляции линией и получим так называемые макросы Аскольда Волкова: Код (C++): #define _setL(port,bit) do { port &= ~(1 << bit); } while(0) #define _setH(port,bit) do { port |= (1 << bit); } while(0) #define _clrL(port,bit) do { port |= (1 << bit); } while(0) #define _clrH(port,bit) do { port &= ~(1 << bit); } while(0) #define _bitL(port,bit) (!(port & (1 << bit))) #define _bitH(port,bit) (port & (1 << bit)) #define _cpl(port,bit,val) do {port ^= (1 << bit); } while(0) " правильно ли я понял, что автор имел в виду, что можно задефайнить например вот так: #define Pin PORTB, 0 #define _setH(port,bit) do { port |= (1 << bit); } while(0) а потом надпись _setH(Pin); в коде будет зажигать этот пин? у меня это почему-то не работает - говорит что _setH() требует два аргумента, а вышеперечисленный Pin воспринимает только как один аргумент несмотря на все дефайны. Это я что-то не так делаю, или в статье по ссылке напутано?
ааа оно орало дико на портА, конечно, его же нет в атмеге8. вот так компилится int PortsArray[] = {&PORTB,&PORTC,&PORTD}; но выдает три предупреждения вот такие: ../a8servo1.c:14:1: warning: initialization makes integer from pointer without a cast о вот так вообще нормально стало int PortsArray[] = {(int)&PORTB,(int)&PORTC,(int)&PORTD};
тут вообще странно, но вот так ругается int8_t PortsArray[] = {&PORTB,&PORTC,&PORTD}; и вот так ругается int8_t PortsArray[] = {(int8_t)&PORTB,(int8_t)&PORTC,(int8_t)&PORTD}; а вот так не ругается: int8_t PortsArray[] = {(int16_t)&PORTB,(int16_t)&PORTC,(int16_t)&PORTD};