да вот так тоже ругается uint8_t *PortsArray[] = {&PORTB,&PORTC,&PORTD}; но уже на что-то другое ../a8servo1.c:14:1: warning: initialization discards qualifiers from pointer target type с этими указателями можно и мозг сломать
только я не очень понимаю что конкретно получается лежит в этом массиве указателей вот в этом массиве uint8_t PortsArray[] = {&PORTB,&PORTC,&PORTD}; лежат адреса портов? а в этом тогда что лежит? uint8_t *PortsArray[] = {&PORTB,&PORTC,&PORTD}; содержимое портов? и обращаться тогда к порту через массив надо вот так *PortsArray[0] |= 1<<2 и это будет эквивалентно PORTB |= 1<<2 ? да вроде работает. но на что ругается - непонятно
а что значат вот эти предупреждения? ../a8servo1.c:14:1: warning: initialization discards qualifiers from pointer target type может там куда-то volatile добавить или еще что
етить, действительно volatile uint8_t *PortsArray[] = {&PORTB,&PORTC,&PORTD}; вот так не орет больше.
*PortsArray[0] |= 1<<3; 750 байт PORTB |= 1<<3; 738 байт однако расточительно то есть один такой "диджиталврайт" через массив жрет аж в 7 раз больше чем обычный
Такое крохоборство не стоит труда, если вы не разрабатываете устройство, которое будет выпускаться многомиллионым тиражом. Микроконтроллер с лишним килобайтом памяти будет стоить на пять рублей дороже.
вроде получился уже более-менее рабочий скетч. причем совмещенный с миллис() (то есть не совмещенный, а я просто туда запихнул их чтобы посмотреть) можно довольно просто наклепать до 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;} кстати, эти миллисы почему-то отстают на 2 миллисекунды примерно каждый раз, видимо из-за того что пропускают нечетные значения (тоже непонятно почему), так что они не годятся для отсчитывания маленьких промежутков времени.
Миллис зависит от работы нулевого таймера и обработчика прерывания по переполнению этого нулевого таймера. Если надолго запрещать прерывания или обработчики других прерываний "слишком жирные", то миллис будет пропускать отсчёты.
эти отставания наблюдались и когда у меня в скетче вообще не было ничего кроме этих миллис, то есть никакие другие прерывания не мешались. и это не то чтобы отставания - это именно функция почему-то пропускает нечетные значения, то есть если например написать 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 не подсвечиваются.
Только сейчас понял, что балуетесь скорее всего с ATmega8 на 8 МГц (судя по представленному коду можно сделать такие выводы, если не прав -- поправьте). В этом случае при частоте 8МГц установили делитель на 64 и хотите получить срабатывание прерывания каждую миллисекунду? Будет каждые две миллисекунды. Если более точнее, то прерывание будет каждые 2048 микросекунд или 2.048 миллисекунд. Соответственно обработчик прерывания 'TIMER1_COMPA_vect' будет счётчик увеличивать на 2. Вот Вам и пропуск нечётных...
да, атмега8 и 8мгц. настройки для таймера взял непосредственно в ардуиновой библиотеке, в целом время меряет правильно, значит настройки те. а что нечетные пропускает по большому счету пофиг.