Выплескивание эмоций

Тема в разделе "Флудилка", создана пользователем alp69, 15 окт 2016.

  1. akl

    akl Гуру

    ох етить кажется более-менее разобрался как работает миллис()
    Код (C++):
    #include <avr/io.h>
    #define F_CPU 8000000UL
    #include <avr/interrupt.h>

    #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
    #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;

    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++;
    }
    unsigned long millis()
    {
        unsigned long m;
        uint8_t oldSREG = SREG;

        // disable interrupts while we read timer0_millis or we might get an
        // inconsistent value (e.g. in the middle of a write to timer0_millis)
        SREG &= ~(0b10000000);
        m = timer0_millis;
        SREG = oldSREG;

        return m;
    }

     
    int main(void) {

        TCCR0 = 0b00000011;
        TIMSK = 0b00000001;
        SREG |= 0b10000000;
        DDRA = 0b11111111;
     

        while (1==1) {
     
          if (millis()>0) {PORTA |= (1<<0);}
          if (millis()>2) {PORTA |= (1<<1);}
          if (millis()>5) {PORTA |= (1<<2);}
          if (millis()>10) {PORTA |= (1<<3);}
          if (millis()>20) {PORTA |= (1<<4);}
          if (millis()>50) {PORTA |= (1<<5);}
          if (millis()>100) {PORTA |= (1<<6);}
          if (millis()>500) {PORTA |= (1<<7);}
     
        }
       
        return 0;}
    теперь можно сделать свою библиотеку даже с этим миллисом :cool:

    жаль что авр-студия не умеет симулировать атмегу8 или 328 или типа того, а то эта атмега16 отличается слегка, хоть и не сильно, но в самых неожиданных местах может вылезти несовпадение. например не сразу допер как настроить таймер

    так этот же, USBasp. я пока не проверял сам, но везде пишут что она с ним не работает.
     
    Последнее редактирование: 1 окт 2017
  2. akl

    akl Гуру

    о блин, вроде симуляция атмега8 и 168 есть. 328 нету
     
  3. rkit

    rkit Гуру

    Интегрирована с фирменными программаторами от атмела. Дудку не умеет, но можно самому заливать собранный файл.
     
  4. Airbus

    Airbus Радиохулиган Модератор

    Вроде шил когдато.Надо проверить.Но у меня Студия 4
     
  5. akl

    akl Гуру

    чет библиотека серво слишком запутанная.
     
  6. akl

    akl Гуру

    есть такая хорошая приблуда для студии - atmel data visualizer. я так понял она с древними версиями (4.19 и т.п.) не работает. печально
     
  7. akl

    akl Гуру

    кто-нибудь понимает логику библиотеки servo хоть в общих чертах?:confused:
     
  8. Airbus

    Airbus Радиохулиган Модератор

    В общих это как драйвер.Или подпрограмма на Асме.Управляет конкретным устройством на уровне дрыгоножества при поступлении макрокоманд из основного цыкла
     
  9. akl

    akl Гуру

    это уж слишком в общих чертах.
    я так понял вот основная суть:
    Код (C++):
    static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
    {
      if( Channel[timer] < 0 )
        *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
      else{
        if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
          digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
      }

      Channel[timer]++;    // increment to the next channel
      if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
        *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
        if(SERVO(timer,Channel[timer]).Pin.isActive == true)     // check if activated
          digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
      }
      else {
        // finished all channels so wait for the refresh period to expire before starting over
        if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) )  // allow a few ticks to ensure the next OCR1A not missed
          *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
        else
          *OCRnA = *TCNTn + 4;  // at least REFRESH_INTERVAL has elapsed
        Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
      }
    }
    приходит от счетчика прерывание по совпадению

    если перебор всех сервов закончился ( Channel[timer]<0), то счетчик обнуляется
    иначе если перебор не закончился, то если эта серва не последняя в списке и если ее пин активен (хз что значит "пин активен", может проверка подключена ли серва), то в пин записывается ноль.

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

    иначе же если эта следующая серва последняя (или предыдущая была последняя хз) или че-то там закончилось по количеству сервов на таймер, то если счетчик еще не натикал на рефреш_интервал (20к микросекунд) и в ближайшие 4 тика не натикает, то в регистре сравнения оставляем этот рефреш_интервал, иначе в регистр сравнения пишем текущее значение счетчика +4 (непонятно вообще что это за люфт +4, может чтобы не пропустить прерывание)
    ну и так как эта серва была последняя, то Channel[timer] = -1, чтобы в следующий раз перебор начался с нуля.


    то есть я не понимаю как это работает. на каждую из серв должно поступать 1 на определенное время раз в 20000 микросекунд. Походу этот алгоритм делает это. но как?

    когда приходит прерывание, он ставит 1 на первую серву, засекает ее время и идет ждать следующего прерывания. следующее прерывание приходит когда истечет время этой сервы, тогда он ставит на нее 0, на следующую 1 и идет ждать время для следующей, и так далее пока сервы не кончатся. когда все сервы кончились, он проверяет прошло ли 20000 микросекунд, если надо ждет пока пойдет, а дальше всё повторяется.
    Так это работает? но тогда получается что при количестве сервов больше 8ми интервал в 20000 может быть превышен? хотя может это и нормально, т.к. в библиотеке написано что это "minumim time to refresh servos in microseconds". Но ведь тогда частота будет плавать? или сервам пох на это, главное чтобы время импульса соблюдалось? Частота вообще будет плавать постоянно, в зависимости от положения предыдущих серв на следующие будет приходить сигнал в разное время.

    Короче - правильно ли я понял алгоритм? из-за всех этих обобщений, указателей и массивов там полнейшая каша
     
    Последнее редактирование: 3 окт 2017
  10. akl

    akl Гуру

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

    akl Гуру

    вопрос такой назрел - в АврСтудии когда в режиме входа например
    DDRС=0 включаю подтяжку
    PORTС |= (1<<2)
    в регистре PINC,2 не появляется единичка.
    Где-то читал что это баг студии, но не помню где и правда ли это
    собственно - в реальности появится же единичка в PIN при включении подтяжки (и исчезнет когда нога замкнется за землю)? или как?
     
  12. Airbus

    Airbus Радиохулиган Модератор

    DDRC=0 это портС на вход а вообще команда бессмысленная но они по умолчанию все на вход поэтому это можно не писать.Вторая команда это и есть подключить подтяжку в Вашем случае на ПортС пин2.Там и не будет ничего потому что это вход если не подавать внешний сигнал извне.Вот если DDRC=1 (порт как выход) и вторую косанду то на пине 2 ПортаС будет 1.Так что все логично.
     
  13. akl

    akl Гуру

    так а резистор на питание (хоть и встроенный) - это че, не сигнал?

    что-то я вообще запутался. если я допустим хочу подключить кнопку, включаю подтяжку
    (PORTС |= (1<<2))
    а потом буду считывать нажата ли кнопка вот так
    PINC & (1 << 2)
    то оно будет возвращать 1 или нет? как тогда кнопки считывать если не из PIN-регистра?

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


    да, так и есть

     
    Последнее редактирование: 12 окт 2017
  14. Limoney

    Limoney Гик

    Подтяжка - это подключение встроенного резистора через VCC на пин, то есть на пине будет VCC.
    Настройка пина производится двумя регистрами, регистр направления DDx вход или выход, и регистр
    порта PORTx. Пин порта может быть настроен, как выход логический нуль, выход логическая единица, или вход с подтягивающем сопротивлением или высокоомный вход.
     
  15. akl

    akl Гуру

    только вот в симуляторе авр-студии придется еще вручную тыкать в регистр PIN, т.к. иначе там 1 не появится хоть заподключайся подтяжками

    хотя это неудобство совершенно ничтожное по сравнению с плюсами наличия этого симулятора-дебаггера как такового. особенно после ардуино, где вообще непонятно что происходит.
     
    Последнее редактирование: 12 окт 2017
  16. Limoney

    Limoney Гик

    Да, для каждого пина нужно устанавливать PINx
     
  17. Airbus

    Airbus Радиохулиган Модератор

    Ну да вручную тыкать по любому иначе как сымитировать приход 1?Напряжение на пине с подтяжкой 2.2 вольта это типа ноль а единица это 5 вольт.Здесь про подключение кнопок и светодиодов на выходе
     
    Последнее редактирование: 13 окт 2017
  18. davidgolikh

    davidgolikh Нуб

    Всем привет
    :)
     
  19. davidgolikh

    davidgolikh Нуб

    Как у вас дела у меня хорошо
     
  20. akl

    akl Гуру

    доделал блок питания на чистомСи™
    Код (C++):
    #include <avr/io.h>
    #define F_CPU 8000000UL
    #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))

    uint16_t tok;
    uint16_t tok1;
    uint16_t tok2;
    uint16_t tok3;
    uint16_t tok4;
    uint8_t tokmod;
    uint8_t tokmodd;
    uint16_t tok0;
    uint8_t tokd;
    uint8_t button;
    uint8_t caltok;
    uint8_t calbut;

    typedef struct pin_t
    {
        volatile uint8_t *port;
        uint8_t bit;
    }Pin;

    Pin Led0 = {&PORTD, 1<<5};
    Pin Led1 = {&PORTD, 1<<6};
    Pin Led2 = {&PORTD, 1<<7};
    Pin Led3 = {&PORTB, 1<<0};
    Pin Led4 = {&PORTB, 1<<1};
    Pin Led5 = {&PORTB, 1<<2};

    Pin* LedArray[] = {
    &Led0,
    &Led1,
    &Led2,
    &Led3,
    &Led4,
    &Led5
    };
    #define allleds 6


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

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

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

    void SetLeds(uint8_t n){
       uint8_t i;
       for(i=0; i<allleds; i++){
       if(i<n){WriteLed_on(i);}
       else{WriteLed_off(i);}}
    }

    static inline uint16_t AdcRead(uint8_t analog_pin){  //ADC
        ADMUX = 0b01000000 | analog_pin; //VCC
        ADCSRA = 0b11000110; // СК/64 (125KHZ)
        while ((ADCSRA & (1 << ADIF)) == 0);
        uint16_t adcw;
        adcw= ADC;
        ADCSRA |= (1 << ADIF);
        return adcw;
    }

    void EepromWrite(uint16_t addr, uint8_t dat){
       while(EECR & (1<<EEWE));
       EEAR = addr;
       EEDR = dat;
       EECR |= (1<<EEMWE);
       EECR |= (1<<EEWE);
    }

    uint8_t EepromRead(uint8_t addr){
       while(EECR & (1<<EEWE));
       EEAR = addr;
       EECR |= (1<<EERE);
       return EEDR;
    }

    void calibrovka(){
       uint8_t tokk;
       tokk = tok-350;
       EepromWrite(110, 123);
       WriteLed_off(0);
       WriteLed_off(1);
       WriteLed_off(2);
       WriteLed_off(3);
       WriteLed_off(4);
       _delay_ms(200);
       EepromWrite(111, tokk);
       WriteLed_on(0);
       _delay_ms(200);
       WriteLed_off(0);
       WriteLed_on(1);
       _delay_ms(200);
       WriteLed_off(1);
       WriteLed_on(2);
       _delay_ms(200);
       WriteLed_off(2);
       WriteLed_on(3);
       _delay_ms(200);
       WriteLed_off(3);
       WriteLed_on(4);
       _delay_ms(200);
       WriteLed_off(4);
       tok0 = 350+tokk;
    }


    int main(void) {

       DDRD=0b11100000;
       DDRB=0b00000111;

       PORTC |= (1<<2);

       PORTD=0b11100000;
       PORTB=0b00000111;
       _delay_ms(500);
       PORTD=0b00000000;
       PORTB=0b00000000;

       if(EepromRead(110)==123) {
       caltok = EepromRead(111);}
         else{caltok = 162;}

       tokd = 2;
       tok0 = 350+caltok;


    while (1==1) {

       tok1 = AdcRead(1);
       tok2 = AdcRead(1);
       tok3 = AdcRead(1);
       tok4 = AdcRead(1);
       tok = (tok1+tok2+tok3+tok4)/4;

       uint8_t j;
       tokmodd=tokmod;
       for(j=allleds; j>0; j--){
         if(tok > (tok0+tokd*(j-1))){
         tokmod=j;
         break;}
         else{tokmod=j-1;}}

       if(tokmodd!=tokmod){
         SetLeds(tokmod);}

       button = readP(PINC,2);
       if(button && !calbut) {
         calbut=1;
         _delay_ms(2000);
         button = readP(PINC,2);
         if(button) {
         calibrovka();
         }}
       if(!button && calbut) {calbut=0;}

       _delay_ms(200);

    }return 0;}
     

    [​IMG]

    правда этот датчик тока весьма малочувствительный - 180мВ/1ампер. А максимальный ток (прежде чем начнет проседать) 300мА. вот и получается, что вся эта штука чтобы адекватно показывать уровень тока (зелеными лампочками) должна регистрировать изменение хотя бы в 0,01вольт, что уже на пределе возможностей АЦП атмеги. Но в принципе работает, если сильно не перемещать в пространстве - какие-то наводки таки влияют, хотя на выходе датчика поставил довольно большой конденсатор. Возможно если засунуть в экранированный корпус будет нормально. А может это и не наводки, а какие-то магнитные явления.

    Обмотка 2А 6,3В через удвоитель (с диодами шотки), потом стабилизатор на 9В. Думал хоть 500мА будет максимальный ток, но не получилось.
     
    Igor68 и NikitOS нравится это.