управление портами

Тема в разделе "Микроконтроллеры AVR", создана пользователем akl, 5 окт 2017.

  1. akl

    akl Гик

    вопрос такой:
    допустим я хочу подключить к атмеге несколько сервоприводов и подавать на них сигнал вот таким образом:
    Код (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 подавалась единица и т.д.
     
  2. rkit

    rkit Гуру

    Язык C или C++?
     
  3. akl

    akl Гик

    желательно С
    в ардуино что-то подобное сделано на С++, и я так и не смог толком разобраться как оно там работает.
    в смысле я пытался курить библиотеку servo, кажется понял что там происходит в прерывании, но на остальном сломал мозг, там какие-то конструкторы Servo::Servo() не понимаю как это устроено, и еще это все размазано по многим файлам, я же хочу наиболее простым способом только проверить алгоритм который в прерывании там делается.
     
    Последнее редактирование: 5 окт 2017
  4. rkit

    rkit Гуру

    В C в момент компиляции такие вещи можно сделать только на m4, что требует инфраструктуры линукса, и тоже в целом весьма непросто.
    Так что пишите просто функциями.
     
  5. akl

    akl Гик

    короче в результате гугления и бездумного копипаста получилось что-то вроде этого. Не понимаю как оно работает, но работает.

    Код (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;}
    Не факт что это приближает к результату, то есть к моделированию ардуиновой библиотеки Серво, в которой (насколько я понял) циклически перебираются друг за другом все сервы, и по очереди на каждую пускается сигнал нужной продолжительности.
     
  6. akl

    akl Гик

    короче вот такая фигня

    Код (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;}
    в принципе оно вроде работает

    [​IMG]

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

    То есть идея в том, чтобы реализовать аналог библиотеки Servo, но на "чистом Си", и более тупыми методами.

    страдаю хернёй да? [​IMG]

    суть проблемы в том, что надо как-то проиндексировать сервоприводы, чтобы в прерывании программа могла обращаться к ним по какому-то номеру, неужели на Си такое невозможно?
     
  7. rkit

    rkit Гуру

    Ну смотря что иметь в виду под более тупыми методами. В ардуино-библиотеке сильно нечего упрощать.
     
  8. akl

    akl Гик

    ардуиновая библиотека серво на С++ сделана. а как сделать что-то подобное на С
     
  9. rkit

    rkit Гуру

    Да взять да переписать практически в лоб. Просто без использования классов.
     
  10. akl

    akl Гик

    Надо как-то проиндексировать сервы, чтобы потом можно было обращаться к ним по номеру.
    то есть например серва подключенная к PODTB,0 будет с индексом "1", к PORTD,4 с индеском "2", и т.д. в произвольном порядке, а потом при n==1 обрабатывается PODTB,0, при n==2 PORTD,4 и т.д. но это нереально.
     
  11. DIYMan

    DIYMan Гуру

    После
    Код (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;
    }
    И будет обращение по номеру.
     
    akl и ИгорьК нравится это.
  12. akl

    akl Гик

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

    А как можно правильно засунуть в массив например адреса портов?
    PortsArray[] = {&PORTA,&PORTB,&PORTC,&PORTD};
    так не работает например
    можно так же через структуру, но это наверно не оптимальный вариант
     
    Последнее редактирование: 7 окт 2017
  13. akl

    akl Гик

    еще вопрос такой:
    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 воспринимает только как один аргумент несмотря на все дефайны. Это я что-то не так делаю, или в статье по ссылке напутано?
     
  14. rkit

    rkit Гуру

    А тип кто будет декларировать?

    Не видел такого выпендрежа на практике никогда.
     
  15. akl

    akl Гик

    типы разные пробовал - не помогает
    могло бы быть удобно, если б работало
     
  16. rkit

    rkit Гуру

    Ну тут надо не пробовать, а все-таки прочитать, что на что компилятор ругается
     
  17. akl

    akl Гик

    ааа оно орало дико на портА, конечно, его же нет в атмеге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};
     
  18. rkit

    rkit Гуру

    uint8_t * PortsArray[]
    Вам нужен массив указателей на восьмибитное слово.
     
  19. akl

    akl Гик

    тут вообще странно, но вот так ругается
    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};
     
  20. rkit

    rkit Гуру

    массив указателей