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

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

  1. akl

    akl Гуру

    да
    вот так тоже ругается
    uint8_t *PortsArray[] = {&PORTB,&PORTC,&PORTD};
    но уже на что-то другое
    ../a8servo1.c:14:1: warning: initialization discards qualifiers from pointer target type
    с этими указателями можно и мозг сломать
     
  2. akl

    akl Гуру

    только я не очень понимаю что конкретно получается лежит в этом массиве указателей

    вот в этом массиве
    uint8_t PortsArray[] = {&PORTB,&PORTC,&PORTD};
    лежат адреса портов?

    а в этом тогда что лежит?
    uint8_t *PortsArray[] = {&PORTB,&PORTC,&PORTD};
    содержимое портов?

    и обращаться тогда к порту через массив надо вот так
    *PortsArray[0] |= 1<<2
    и это будет эквивалентно
    PORTB |= 1<<2
    ?
    да вроде работает. но на что ругается - непонятно
     
    Последнее редактирование: 7 окт 2017
  3. rkit

    rkit Гуру

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

    Да.
     
    akl нравится это.
  4. akl

    akl Гуру

    а что значат вот эти предупреждения?
    ../a8servo1.c:14:1: warning: initialization discards qualifiers from pointer target type
    может там куда-то volatile добавить или еще что
     
  5. akl

    akl Гуру

    етить, действительно
    volatile uint8_t *PortsArray[] = {&PORTB,&PORTC,&PORTD};
    вот так не орет больше.
     
  6. rkit

    rkit Гуру

    Да, надо. Вообще внимательно смотрите в чужие исходники - все есть.
     
  7. akl

    akl Гуру

    *PortsArray[0] |= 1<<3;
    750 байт
    PORTB |= 1<<3;
    738 байт
    однако расточительно
    то есть один такой "диджиталврайт" через массив жрет аж в 7 раз больше чем обычный
     
  8. rkit

    rkit Гуру

    Такое крохоборство не стоит труда, если вы не разрабатываете устройство, которое будет выпускаться многомиллионым тиражом. Микроконтроллер с лишним килобайтом памяти будет стоить на пять рублей дороже.
     
  9. akl

    akl Гуру

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

    Код (C++):
    #include <avr/io.h>
    #define F_CPU 8000000UL
    #include <avr/interrupt.h>
    #include <util/delay.h>

    #define setH(port,bit)  do{port |= (1 << bit);}while(0)
    #define setL(port,bit)  do{port &= ~(1 << bit);}while(0)
    #define readP(reg,bit)    (reg & (1 << bit))
    #define Led_port PORTB
    #define Led_reg PINB
    #define Led_bit 7

    #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
    //millis
    #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
    #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )
    #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
    #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
    #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
    #define FRACT_MAX (1000 >> 3)
    volatile unsigned long timer0_overflow_count = 0;
    volatile unsigned long timer0_millis = 0;
    static unsigned char timer0_fract = 0;
    //servos
    #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
    {
        volatile uint8_t *port;
        uint8_t bit;
    }Pin;

    Pin Servo0 = {&PORTB, 1<<0};
    Pin Servo1 = {&PORTB, 1<<1};
    Pin Servo2 = {&PORTB, 1<<2};
    Pin Servo3 = {&PORTB, 1<<3};

    Pin Led = {&PORTB, 1<<7};

    Pin* ServoArray[] = {
    &Servo0,
    &Servo1,
    &Servo2,
    &Servo3
    };
    #define allservos 4 // 8 max

    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;}}


    void WriteServo_on(uint8_t num)
    {
      Pin* pin = ServoArray[num];
      *pin->port |= pin->bit;
    }

    void WriteServo_off(uint8_t num)
    {
      Pin* pin = ServoArray[num];
      *pin->port &= ~pin->bit;
    }

    void WriteServo(uint8_t num, uint8_t val)
    {
      Pin* pin = ServoArray[num];
      if (val==0){*pin->port &= ~pin->bit;}
      else {*pin->port |= pin->bit;}
    }

    uint16_t ServoTicks[allservos]={
    usToTicks(DEFAULT_PULSE_WIDTH),
    usToTicks(DEFAULT_PULSE_WIDTH),
    usToTicks(DEFAULT_PULSE_WIDTH),
    usToTicks(DEFAULT_PULSE_WIDTH)};
    uint16_t ticks = usToTicks(DEFAULT_PULSE_WIDTH)*allservos;
    uint16_t sum;
    int8_t s = -1;

    void WriteServo_us(uint8_t num, uint16_t val){
        if (val==0 || (val>=MIN_PULSE_WIDTH && val<=MAX_PULSE_WIDTH)) {
        val=usToTicks(val);}
        else {val=usToTicks(DEFAULT_PULSE_WIDTH);}
        uint8_t oldSREG = SREG;
        cli();
        ServoTicks[num]=val;
        SREG = oldSREG;
        for(uint8_t i=0; i<allservos; i++){
        sum = sum + ServoTicks[i];}
        ticks = sum;
        sum = 0;
        }

    unsigned long millis(){
        unsigned long m;
        uint8_t oldSREG = SREG;
        SREG &= ~(0b10000000);
        m = timer0_millis;
        SREG = oldSREG;
        return m;}


    ISR(TIMER0_OVF_vect){
        unsigned long m = timer0_millis;
        unsigned char f = timer0_fract;
        m += MILLIS_INC;
        f += FRACT_INC;
        if (f >= FRACT_MAX) {
            f -= FRACT_MAX;
            m += 1;}
        timer0_fract = f;
        timer0_millis = m;
        timer0_overflow_count++;}


    ISR(TIMER1_COMPA_vect){
      if (s<0){
      TCNT1 = 0;
      s++;
      OCR1A = TCNT1+ServoTicks[s];
      WriteServo_on(s);}
      else {
      WriteServo_off(s);
      s++;
      if (s<allservos){
      OCR1A = TCNT1+ServoTicks[s];
      WriteServo_on(s);}
      else {
      OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL)-ticks;
      s=-1;}}
    }

    unsigned long time = 0;


    int main(void) {

      TCCR0 = 0b00000011;
      TIMSK |= (1<<0);

      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

      DDRB=0b10001111;
      OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL);
      TIFR |= (1<<4);
      SREG |= (1<<7);


    while (1==1) {

      if(PINB & ( 1 << 6 )){
         WriteServo_us(1,700);
         }

      uint8_t led = readP(Led_reg, Led_bit);
      if (millis()-time > 10) {
      time = millis();
      if (led==0){
      setH(Led_port,Led_bit);}
      else{
      setL(Led_port,Led_bit);}}



        }

        return 0;}

    [​IMG]

    кстати, эти миллисы почему-то отстают на 2 миллисекунды примерно каждый раз, видимо из-за того что пропускают нечетные значения (тоже непонятно почему), так что они не годятся для отсчитывания маленьких промежутков времени.
     
    Последнее редактирование: 8 окт 2017
  10. AlexU

    AlexU Гуру

    Миллис зависит от работы нулевого таймера и обработчика прерывания по переполнению этого нулевого таймера. Если надолго запрещать прерывания или обработчики других прерываний "слишком жирные", то миллис будет пропускать отсчёты.
     
  11. akl

    akl Гуру

    эти отставания наблюдались и когда у меня в скетче вообще не было ничего кроме этих миллис, то есть никакие другие прерывания не мешались. и это не то чтобы отставания - это именно функция почему-то пропускает нечетные значения, то есть если например написать if (millis() == 51), то оно в принципе не среагирует, потому и получается что если например пишешь if (millis() > 50), то реакция будет только когда уже 52. Возможно это как-то зависит от частоты, щас все тестирую на 8мгц, и может на 16 будет нормально, но в принципе это не очень большая погрешность, сильно повлиять может только если небольшие промежутки накапливаются и суммируются, а если например секунды считать, то там вообще пофиг.




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

    Код (C++):
    #include <avr/io.h>
    #define F_CPU 8000000UL
    #include <avr/interrupt.h>
    #include <util/delay.h>

    #define setH(port,bit)  do{port |= (1 << bit);}while(0)
    #define setL(port,bit)  do{port &= ~(1 << bit);}while(0)
    #define readP(reg,bit)    (reg & (1 << bit))
    #define Led_port PORTB
    #define Led_reg PINB
    #define Led_bit 7

    #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
    //millis
    #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
    #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )
    #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
    #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
    #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
    #define FRACT_MAX (1000 >> 3)
    volatile unsigned long timer0_overflow_count = 0;
    volatile unsigned long timer0_millis = 0;
    static unsigned char timer0_fract = 0;
    //servos
    #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
    {
        volatile uint8_t *port;
        uint8_t bit;
    }Pin;

    Pin Servo0 = {&PORTB, 1<<0};
    Pin Servo1 = {&PORTB, 1<<1};
    Pin Servo2 = {&PORTB, 1<<2};
    Pin Servo3 = {&PORTB, 1<<3};

    Pin Led = {&PORTB, 1<<7};

    Pin* ServoArray[] = {
    &Servo0,
    &Servo1,
    &Servo2,
    &Servo3
    };
    #define allservos 4 // 8 max



    void WriteServo_on(uint8_t num)
    {
      Pin* pin = ServoArray[num];
      *pin->port |= pin->bit;
    }

    void WriteServo_off(uint8_t num)
    {
      Pin* pin = ServoArray[num];
      *pin->port &= ~pin->bit;
    }



    uint8_t ServoEnable[allservos]={1,1,1,1};
    uint16_t ServoTicks[allservos]={
        usToTicks(DEFAULT_PULSE_WIDTH),
        usToTicks(DEFAULT_PULSE_WIDTH),
        usToTicks(DEFAULT_PULSE_WIDTH),
        usToTicks(DEFAULT_PULSE_WIDTH)};
    uint16_t ticks = usToTicks(DEFAULT_PULSE_WIDTH)*allservos;
    uint16_t sum;
    int8_t s = -1;

    void WriteServo_us(uint8_t num, uint16_t val){
        uint8_t se;
        if (val>=MIN_PULSE_WIDTH && val<=MAX_PULSE_WIDTH) {
        val=usToTicks(val);
        se=1;}
        else if (val>MAX_PULSE_WIDTH) {
        val=usToTicks(MAX_PULSE_WIDTH);
        se=1;}
        else if (val!=0) {
        val=usToTicks(MIN_PULSE_WIDTH);
        se=1;}
        else {
        val=usToTicks(DEFAULT_PULSE_WIDTH);
        se=0;}
        uint8_t oldSREG = SREG;
        cli();
        ServoTicks[num]=val;
        ServoEnable[num]=se;
        SREG = oldSREG;
        for(uint8_t i=0; i<allservos; i++){
        sum = sum + ServoTicks[i];}
        ticks = sum;
        sum = 0;}

    uint16_t ReadServo_us(uint8_t num){
        uint16_t ms;
        uint8_t oldSREG = SREG;
        cli();
        ms=ServoTicks[num];
        SREG = oldSREG;
        return ms;}

    unsigned long millis(){
        unsigned long m;
        uint8_t oldSREG = SREG;
        SREG &= ~(0b10000000);
        m = timer0_millis;
        SREG = oldSREG;
        return m;}


    ISR(TIMER0_OVF_vect){
        unsigned long m = timer0_millis;
        unsigned char f = timer0_fract;
        m += MILLIS_INC;
        f += FRACT_INC;
        if (f >= FRACT_MAX) {
            f -= FRACT_MAX;
            m += 1;}
        timer0_fract = f;
        timer0_millis = m;
        timer0_overflow_count++;}


    ISR(TIMER1_COMPA_vect){
        if (s<0){
        TCNT1 = 0;
        s++;
        OCR1A = TCNT1+ServoTicks[s];
        if (ServoEnable[s]>0){WriteServo_on(s);}}
        else {
        WriteServo_off(s);
        s++;
        if (s<allservos){
        OCR1A = TCNT1+ServoTicks[s];
        if (ServoEnable[s]>0){WriteServo_on(s);}}
        else {
        OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL)-ticks;
        s=-1;}}
    }

    unsigned long time = 0;


    int main(void) {

      TCCR0 = 0b00000011;
      TIMSK |= (1<<0);

      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

      DDRB=0b10001111;
      OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL);
      TIFR |= (1<<4);
      SREG |= (1<<7);

      uint8_t but1 = 0;
      uint8_t but2 = 0;
      uint16_t x = 2100;
      uint8_t d = 0;

    while (1==1) {

      if (x>2000) {d = 0;}
      if (x<800) {d = 1;}

      if(readP(PINB,6) && !but1){
         but1 = 1;
         if (d==0) {x=x-100;}
          else {x=x+100;}
         WriteServo_us(1,x);
         }
      if(!readP(PINB,6) && but1) {but1 = 0;}

      if(readP(PINB,5) && !but2){
         but2 = 1;
         WriteServo_us(1,0);
         }
      if(!readP(PINB,5) && but2) {but2 = 0;}

      uint8_t led = readP(Led_reg, Led_bit);
      if (millis()-time > 10) {
      time = millis();
      if (led==0){
      setH(Led_port,Led_bit);}
      else{
      setL(Led_port,Led_bit);}}

     
     
        }

        return 0;}
    что подбешивает в авр-студии - всякие эти int8_t не подсвечиваются.
     
    Последнее редактирование: 9 окт 2017
  12. AlexU

    AlexU Гуру

    Только сейчас понял, что балуетесь скорее всего с ATmega8 на 8 МГц (судя по представленному коду можно сделать такие выводы, если не прав -- поправьте). В этом случае при частоте 8МГц установили делитель на 64 и хотите получить срабатывание прерывания каждую миллисекунду? Будет каждые две миллисекунды. Если более точнее, то прерывание будет каждые 2048 микросекунд или 2.048 миллисекунд. Соответственно обработчик прерывания 'TIMER1_COMPA_vect' будет счётчик увеличивать на 2.
    Вот Вам и пропуск нечётных...
     
  13. akl

    akl Гуру

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