Перейти к содержанию
    

lyric

Участник
  • Постов

    19
  • Зарегистрирован

  • Посещение

Репутация

0 Обычный

Информация о lyric

  • Звание
    Участник
    Участник

Посетители профиля

524 просмотра профиля
  • zoono

  1. Lmx2315, большое спасибо за разъяснение. А я всё думал что виновата моя программа
  2. Ну да, я немного не так выразился) Сигнал для АЦП задаётся переменным резистором. То, что сигнал не менялся, - я имел в виду что я этот резистор не трогал во время проведения измерений, то есть измеряемая величина никак принудительно не менялась. Для второго такого же канала АЦП сигнал тоже плывёт, иногда даже больше, чем у первого канала с резистором. Хотя здесь сигнал задаётся калибратором токовой петли ОВЕН РЗУ-420, в характеристиках у которого заявлена точность "до десятой доли процента". Проблема в применяемых компонентах измерительных цепей? А по второму вопросу - насчёт того, что включаю вывод и значение АЦП искажается, можете что-то подсказать?
  3. Здравствуйте. Вопрос не про фильтрацию, но про АЦП: контроллер AVR. Напряжение на входе НЕ меняется, но при этом измеренное значение АЦП со временем (полчаса-час, сутки-трое суток) меняется, - уползает вверх, или вниз, когда как... Отчего это может быть? В программе значение АЦП аппроксимировано в диапазон 0-6000. Так вот в таком виде (0-6000) значение уезжает вплоть до 40 единиц, - например установилось значение 3500, и плавно уменьшается, и через час оно уже 3478, хотя измеряемый сигнал никак не менялся. Ещё вторая проблема с АЦП, - если программа выдаёт сигнал включения какого-либо вывода в состояние 0, то значение АЦП искажается, - может разово возрасти, а может разово уменьшится примерно на 10 единиц. Подскажите пожалуйста почему и что с этим делать?
  4. Спасибо! Проверил в этом конверторе по битам на примере числа 175.83. Разложил это число в биты через конвертор задал константами байтам и отправил в порт. В общем из AVR'ки уходит число точно в представлении IEEE 754, но результат точно такой же получается как и до этого, - искажение осталось. Из меги уходит такой набор байтов: 01000011 00101111 11010100 01111011 А в OPC-сервере эти же байты отображаются вот так: 01000011 00101111 00001000 00000001 То есть два байта совпадают и два НЕ совпадают, причём ВООБЩЕ, без какой-либо закономерности меняются. Ну и как бы последовательность битов, которая отображается в ОРС-сервере в конверторе даёт число 175.03127, как раз вот она и ошибка. Получается, виновата кривая реализация MODBUS RTU которую я использую? Или ещё какие-то причины могут быть? UPD: Разобрался с проблемой, всё отправляется правильно. Надо было писать эти 4 байта в обменный буфер начиная с первой его ячейки, а не с учётом прошлой посылки, 2 байта целочисленной переменной отправлялось вместо нужных байтов float-переменной.
  5. Всем привет. Сделал на AVR'ке MODBUS RTU, через конвертер интерфейсов UART - USB подключил её к компьютеру и опрашиваю OPC-сервером. Использую только функцию чтения (03). Целочисленные переменные читаются без проблем. А вот переменные типа FLOAT читаются на компе с погрешностью. Значение FLOAT пробовал представлять в виде массива CHAR двумя способами, и никакой разницы между ними не увидел: unsigned char *ptr; float primer_float=18.43; ptr=(char *)&primer_float; mirror[0]=*(ptr+3); mirror[1]=*(ptr+2); mirror[2]=*(ptr+1); mirror[3]=*(ptr+0); и так: union{ unsigned char t[4]; float f; }un; un.f=18.43; mirror[0]=un.t[3]; mirror[1]=un.t[2]; mirror[2]=un.t[1]; mirror[3]=un.t[0]; В меге задаю константу 18.43, а в компьютер это значение приходит уже равное 18.378908 Почему происходит такое искажение - непонятно. Подскажите пожалуйста?
  6. Все переменные инициализировал, ничего не изменилось. Ну и прошу прощения, - ошибся: Присмотрелся лучше к экрану - мерцания прекращаются только если ADC_convert вообще не вызывать. Что-то не так с этой функцией (подскажите что?). И почему она портит только один разряд а не все 8 - не пойму. Когда ADMUX=0 и ADMUX=1 функция ADC_convert выполняется дольше, потому что больше расчётов в ней происходит. Поэтому функция indi() в эти моменты дольше НЕ выполняется. И, видимо, эти моменты совпадают с моментами, когда должен включаться индикатор (R8). Но между ADMUX=0 и ADMUX=1 функция indi() всё равно должна успеть выполниться 1 раз, и получается что мерцать должны 2 индикатора, а не один. Так и есть? или это бред? :help: :help: :help:
  7. #define F_CPU 8000000 #include <avr/io.h> #include <stdlib.h> #include <math.h> #include <avr/interrupt.h> //--------------------------------------------------------------------------------------------------------------глобальные переменные----------------------------------------------------------------- volatile uint8_t R1=0, R2=0, R3=0, R4=0, R5=0, R6=0, R7=0, R8=0; //Переменные значений разрядов индикатора volatile int16_t ADC_AI_1, ADC_AI_2; //Текущие значения АЦП без фильтрации volatile float ADC_AI_1_ff, ADC_AI_2_ff, AI_1, AI_2, AI_1_fv, AI_2_fv;//Текущие значения АЦП после фильтрации, аппроксимированных значений аналоговых входов до коррекции и после volatile int32_t accu1=0, accu2=0; //переменные для оверсемплинга АЦП volatile uint8_t accu_count1=0, accu_count2=0; //переменные для оверсемплинга АЦП volatile int16_t koeff_AI1=1; volatile int16_t koeff_AI2=1; volatile uint8_t DI_portD_Mask; volatile uint8_t DI_portA_Mask_no_opros; volatile uint8_t DI_portA_Mask; uint8_t tip_AI1=1; uint16_t calibr_ADC_min_AI1; // Калибровочное минимальное значение АЦП аналогового входа AI1 uint16_t calibr_ADC_max_AI1; // Калибровочное максимальное значение АЦП аналогового входа AI1 int16_t NPI_AI1; // Нижний предел измерения параметра с аналогового входа AI1 int16_t VPI_AI1; // Верхний предел измерения параметра с аналогового входа AI1 uint16_t koef_A_AI1; // Коэффициент коррекции А для параметра с аналогового входа AI1 int16_t koef_B_AI1; // Коэффициент коррекции B для параметра с аналогового входа AI1 uint8_t koef_filtra_AI1; // Коэффициент фильтрации параметра с AI1 uint8_t tip_AI2=2; uint16_t calibr_ADC_min_AI2; // Калибровочное минимальное значение АЦП аналогового входа AI2 uint16_t calibr_ADC_max_AI2; // Калибровочное максимальное значение АЦП аналогового входа AI2 int16_t NPI_AI2; // Нижний предел измерения параметра с аналогового входа AI2 int16_t VPI_AI2; // Верхний предел измерения параметра с аналогового входа AI2 uint16_t koef_A_AI2; // Коэффициент коррекции А для параметра с аналогового входа AI2 int16_t koef_B_AI2; // Коэффициент коррекции B для параметра с аналогового входа AI2 uint8_t koef_filtra_AI2; // Коэффициент фильтрации параметра с AI2 //------------------------------------------------------------------------------------------------ void preset()//функция установки портов { //инициализация порта В DDRB = 0b10110000; //конфигурация: 0 - вход. 1 - выход PORTB = 0b01001111; //1 - включение подтягивающих резисторов для входов. 0 - задание выходам порта начальных значений ("отключено" - высокий уровень, "включено" - низкий уровень). //инициализация порта C DDRC = 0b11111111; //конфигурация: 0 - вход. 1 - выход PORTC = 0b01111111; //1 - включение подтягивающих резисторов для входов. 0 - задание выходам порта начальных значений ("отключено" - высокий уровень, "включено" - низкий уровень). //инициализация порта D //конфигурация: 0 - вход: DDRD &=~(1<<PD7); //Настраиваем ножку PD7 в режим входа DDRD &=~(1<<PD6); //Настраиваем ножку PD6 в режим входа DDRD &=~(1<<PD5); //Настраиваем ножку PD5 в режим входа DDRD &=~(1<<PD4); //Настраиваем ножку PD4 в режим входа DDRD &=~(1<<PD3); //Настраиваем ножку PD3 в режим входа DDRD |=(1<<PD2); //Настраиваем ножку PD3 в режим вЫхода //1 - включение подтягивающих резисторов для входов порта D: PORTD |= (1<<PD7); PORTD |= (1<<PD6); PORTD |= (1<<PD5); PORTD |= (1<<PD4); PORTD |= (1<<PD3); //1 - задание выходу №2 порта D начального значения "отключено" - высокий уровень: PORTD |= (1<<PD2); } void symboll(uint8_t symm) //функция отображения символов на индикаторах { switch(symm) { case 1: SPDR = 0b10111011; break; //цифра 1 case 2: SPDR = 0b10001100; break; //цифра 2 case 3: SPDR = 0b10101000; break; //цифра 3 case 4: SPDR = 0b00111001; break; //цифра 4 case 5: SPDR = 0b01101000; break; //цифра 5 case 6: SPDR = 0b01001000; break; //цифра 6 case 7: SPDR = 0b10111010; break; //цифра 7 case 8: SPDR = 0b00001000; break; //цифра 8 case 9: SPDR = 0b00101000; break; //цифра 9 case 0: SPDR = 0b00001010; break; //цифра 0 case 11: SPDR = 0b10110011; break; //цифра 1 с точкой case 12: SPDR = 0b10000100; break; //цифра 2 с точкой case 13: SPDR = 0b10100000; break; //цифра 3 с точкой case 14: SPDR = 0b00110001; break; //цифра 4 с точкой case 15: SPDR = 0b01100000; break; //цифра 5 с точкой case 16: SPDR = 0b01000000; break; //цифра 6 с точкой case 17: SPDR = 0b10110010; break; //цифра 7 с точкой case 18: SPDR = 0b00000000; break; //цифра 8 с точкой case 19: SPDR = 0b00100000; break; //цифра 9 с точкой case 10: SPDR = 0b00000010; break; //цифра 0 с точкой case 20: SPDR = 0b00011000; break; //буква А case 21: SPDR = 0b01001001; break; //буква B case 22: SPDR = 0b01001110; break; //буква C case 23: SPDR = 0b10001001; break; //буква D case 24: SPDR = 0b01001100; break; //буква E case 25: SPDR = 0b01001010; break; //буква G case 26: SPDR = 0b11001110; break; //буква I case 27: SPDR = 0b00010001; break; //буква K case 28: SPDR = 0b01001111; break; //буква L case 29: SPDR = 0b11011001; break; //буква N case 30: SPDR = 0b11001001; break; //буква O case 31: SPDR = 0b11000001; break; //буква O с точкой case 32: SPDR = 0b00011100; break; //буква P case 33: SPDR = 0b11101111; break; //нижнее подчёркивание case 34: SPDR = 0b11111101; break; //минус case 35: SPDR = 0b11111111; break; //ничего case 36: SPDR = 0b10111110; break; //стрелка вверх case 37: SPDR = 0b11001111; break; //стрелка вниз case 38: SPDR = 0b11011101; break; //буква R case 39: SPDR = 0b11010101; break; //буква R с точкой case 40: SPDR = 0b01101000; break; //буква S case 41: SPDR = 0b01100000; break; //буква S с точкой case 42: SPDR = 0b01001101; break; //буква T case 43: SPDR = 0b11001011; break; //буква U case 44: SPDR = 0b00001011; break; //буква V case 45: SPDR = 0b11110001; break; //буква Z default: SPDR = 0b11111111; //ничего } } void SPI_init() { SPCR = ((1<<SPE)|(1<<MSTR)); } void ADC_init() { ADCSRA |= ((1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)); //Разрешение использования АЦП и делитель 64 (частота опроса 125кГц) ADMUX=0; } void ADC_convert(void) //функция чтения каналов АЦП. Первые 2 канала - это аналогоые входы. Остальные 6 каналов - используются как дискретные входы. { uint16_t kod_acp=0; uint8_t lock_0=0; //чтобы за один вызов функции выполнялось только одно преобразование АЦП, а не 0 и 7 в один раз static float prom_out1=0, prom_out2=0; unsigned char savee = SREG; if (ADMUX==7) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); kod_acp = (unsigned int) ADC; if (kod_acp<300) {DI_portA_Mask_no_opros |= 0b00100000;} else {DI_portA_Mask_no_opros &= 0b11011111;} lock_0=1; SREG= savee; ADMUX=0; } if (ADMUX==6) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); kod_acp = (unsigned int) ADC; if (kod_acp<300) {DI_portA_Mask_no_opros |= 0b00010000;} else {DI_portA_Mask_no_opros &= 0b11101111;} SREG= savee; ADMUX=7; } if (ADMUX==5) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); kod_acp = (unsigned int) ADC; if (kod_acp<300) {DI_portA_Mask_no_opros |= 0b00001000;} else {DI_portA_Mask_no_opros &= 0b11110111;} SREG= savee; ADMUX=6; } if (ADMUX==4) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); kod_acp = (unsigned int) ADC; if (kod_acp<300) {DI_portA_Mask_no_opros |= 0b00000100;} else {DI_portA_Mask_no_opros &= 0b11111011;} SREG= savee; ADMUX=5; } if (ADMUX==3) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); kod_acp = (unsigned int) ADC; if (kod_acp<300) {DI_portA_Mask_no_opros |= 0b00000010;} else {DI_portA_Mask_no_opros &= 0b11111101;} SREG= savee; ADMUX=4; } if (ADMUX==2) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); kod_acp = (unsigned int) ADC; if (kod_acp<300) {DI_portA_Mask_no_opros |= 0b00000001;} else {DI_portA_Mask_no_opros &= 0b11111110;} SREG= savee; ADMUX=3; } if (ADMUX==1) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); accu2 += (int32_t) ADC;//Оверсемплинг AI2. Было 10 бит, стало 13 accu_count2++; if (accu_count2>=64) { ADC_AI_2=(int16_t)(accu2/8); accu_count2=0; accu2=0;//фильтрация AI2 ADC_AI_2_ff=prom_out2+(ADC_AI_2-prom_out2)/(float)koef_filtra_AI2; //экспоненциальный фильтр prom_out2=ADC_AI_2_ff; if (tip_AI2==1) { AI_2= ((float)((ADC_AI_2_ff-calibr_ADC_min_AI2)/(calibr_ADC_max_AI2-calibr_ADC_min_AI2)) * (((float)(VPI_AI2-NPI_AI2))/koeff_AI2)+ ((float)(NPI_AI2/koeff_AI2)));//Аппроксимация AI2 AI_2_fv=AI_2*(((float)koef_A_AI2)/1000)+(float)koef_B_AI2/koeff_AI2; //Коррекция AI2 } else {AI_2=0;AI_2_fv=0;} } SREG= savee; ADMUX=2; } if ((ADMUX==0) && (lock_0==0)) { ADCSRA |= (1<<ADSC); //Начинаем преобразование while((ADCSRA & (1<<ADSC))) {}; //проверка закончилось ли аналого-цифровое преобразование savee =SREG; cli (); accu1 += (int32_t) ADC; //Оверсемплинг AI1. Было 10 бит, стало 13 accu_count1++; if (accu_count1>=64) { ADC_AI_1=(int16_t)(accu1/8); accu_count1=0; accu1=0;//фильтрация AI1 ADC_AI_1_ff=prom_out1+(ADC_AI_1-prom_out1)/(float)koef_filtra_AI1; //экспоненциальный фильтр prom_out1=ADC_AI_1_ff; if ((tip_AI1>0) && (tip_AI1<4)) { AI_1= ((float)((ADC_AI_1_ff-calibr_ADC_min_AI1)/(calibr_ADC_max_AI1-calibr_ADC_min_AI1)) * (((float)(VPI_AI1-NPI_AI1))/koeff_AI1)+ ((float)(NPI_AI1/koeff_AI1)));//Аппроксимация AI1 AI_1_fv=AI_1*(((float)koef_A_AI1)/1000)+(float)koef_B_AI1/koeff_AI1;//Коррекция AI1 } else {AI_1=0;AI_1_fv=0;} } SREG= savee; ADMUX=1; } lock_0=0; } void indi() { static char n_count=1; //Переменная для перебора посылаемых байтов-символов на разряды индикатора if (n_count==8) {symboll(R8); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b01111111;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } if (n_count==7) { symboll(R7); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b10111111;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } if (n_count==6) {symboll(R6); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b11011111;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } if (n_count==5) {symboll(R5); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b11101111;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } if (n_count==4) {symboll(R4); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b11110111;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } if (n_count==3) {symboll(R3); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b11111011;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } if (n_count==2) {symboll(R2); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b11111101;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } if (n_count==1) {symboll(R1); while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся SPDR =0b11111110;//выбор индикатора while (!(SPSR & (1<<SPIF))) {}; //ожидание, пока данные передадутся //отрицательный фронт для записи в STORAGE REGISTER PORTB |= (1<<4); // высокий уровень PORTB &= ~(1<<4); // низкий уровень } ++n_count; if (n_count>8) {n_count=1;} } int main(void) { preset(); SPI_init(); ADC_init(); sei(); while (1) { ADC_convert(); R1=7; R2=7; R3=7; R4=7; R5=7; R6=7; R7=7; R8=7; indi(); }//КОНЕЦ ОСНОВНОГО ЦИКЛА while }//КОНЕЦ ОСНОВНОЙ ФУНКЦИИ main Функция ADC_convert() влияет на работу функции indi(). ADC_convert() - опрос 8 каналов АЦП, indi() - индикация на 8-разрядный семисегментный экран по SPI. Проблема - последний разряд (R8) мерцает, в то время как остальные разряды горят нормально. Это не вся программа, только те её части, с которыми проблема. Код компилится (Atmel Studio 7) и проблема в нём проявляется. 2 строчки, выделенные жирным цветом - если их закомментировать, то мерцание пропадает. Какое отношение они имеют к индикации - непонятно, но влияют. Такие же две строчки есть и чуть выше в этой же функции, только с другими переменными работают, - и они почему-то никак не влияют на работу других частей программы.
  8. Снова здравствуйте. AVR. Есть 2 самописных функции. Эти функции используют разные глобальные переменные, вообще никак не пересекаются между собой. Вызываются в оновном цикле, никак не завязаны на прерывания. В одной из них работа с SPI, в другой с АЦП. Но при этом функция с АЦП влияет на работу функции с SPI. Нашёл 2 строчки из-за которых это происходит, но там просто математические вычисления, никакого отношения не имеющие к другой функции. При включении любого уровня оптимизации это проявляется. На уровне -О0 всё работает нормально. Подскажите в чём может быть дело?
  9. Не запрещает. Придётся писать свою функцию записи/обновления EEPROM, видимо. Спасибо за помощь, с EEPROM понятно :a14: . Подскажите пожалуйста ещё на эти вопросы: Какие ответы? :rolleyes:
  10. А если я использую стандартные функции работы с EEPROM, которые содержаться в компиляторе Атмел Студии, - всё равно вручную запрещать прерывания? Функция eeprom_update_word() сама этого не делает? Где-то можно посмотреть её код?
  11. Plain это значит что в моём случае запрещать прерывания не нужно ни при расчёте АЦП ни при записи EEPROM? В обработчике прерывания таймера есть только то что касается индикации, и инкремент трёх переменных для создания программных таймеров в основном цикле.
  12. Здравствуйте. Снова есть вопросы по AVR. В программе сделал динамическую семисегментную индикацию через SPI, индикация вызывается в прерывании таймера каждые 2 миллисекунды. И написал так же функцию, которая опрашивает все 8 каналов АЦП (без прерывания по окончанию преобразования). Эта функция вызывается в основном цикле, опрашивает 1 канал за такт. Для первых двух каналов АЦП в этой функции производятся довольно тяжёлые математические вычисления (оверсемплинг, фильтрация, аппроксимация по 2 точкам, коррекция) в переменных типа FLOAT. Ну и на время этих вычислений я запрещаю прерывания, соответственно индикаторы мерцают... А хотелось бы чтобы они горели с постоянной яркостью. 1а) Если НЕ запрещать прерывания, то прерывания могут испортить переменные в ОЗУ, которые в обработчике этого прерывания никак не участвуют? 1б) Если могут испортить - то, получается, мне в каждом куске основного цикла где есть работа с типами int16_t, int32_t, float - всегда запрещать прерывания? 2) В Atmel Studio 7, если использовать стандартные функции работы с EEPROM, то функция записи/обновления переменной в EEPROM сама отключает прерывания на время своей работы? Или это надо вручную перед вызовом этой функции запретить прерывания, а после выполнения функции - снова разрешать? В обработчике прерывания эти переменные так же не используются. Сейчас без запрета прерываний всё работает вроде, но боюсь что мне просто везёт, а хотелось бы знать наверняка. Можно ли где-то посмотреть сам текст этих стандартных функций работы с EEPROM? В хедер-файле EEPROM.h этого кода нет.
  13. Smoky, x736C Большое спасибо, Ваши советы и замечания помогли. Ну и кроме того я сделал прерывание каждые 5 миллисекунд (вместо каждых 100), и инкрементируемую переменную указал volatile вместо unsigned. теперь секундомер смартфона один в один бьёт с тем, что отсчитывает МК, - на глаз никак не отличить. По моим расчётам погрешность за каждую секунду теперь составляет 2 микросекунды, а за 10 минут, соответственно, - 1,2 миллисекунды. Итого удалось уменьшить погрешность примерно в 10000 раз, неплохо :rolleyes:
  14. Здравствуйте, снова есть смешной (для сколь-нибудь опытных разработчиков) вопрос: Снова про AVR, работаю в атмел студии 7. Ниже привожу проект, который должен включать Порт B на 10 секунд, потом вЫключать на 10 секунд, снова включать и так далее. Использую для этого таймер1 (16 бит). Задача - понять как правильно использовать аппаратный таймер для создания, допустим, 30 своих независимых друг от друга программных таймеров с произвольными моментами включения и сброса для каждого из них в зависимости от состояния, допустим, какой-либо переменной в программе. Пока экспериментирую с одной переменной. МК работает на частоте 8МГц, от внутреннего источника, - как был, я его не калибровал и не знаю как это делать и надо ли вообще.. Делитель 256. #define F_CPU 8000000 #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> unsigned int n_count=0; void preset() { DDRB = 0b11111111; // 0xFF PORTB = 0b11111111; // 0x00 DDRD = 0b00001111; // 0xFF PORTD = 0b00000000; // 0x00 DDRA = 0b00000000; PORTA = 0b11111111; // 0x00 } void timer_ini(void) //функция инициализации таймера { TCCR1B |= (1<<WGM12); //установка режима работы CTC (сброс по совпадению) TIMSK1 |= (1<<OCIE1A); //устанавливаем бит разрешения прерывания первого счётчика по совпадению с OCIR1A (H и L) OCR1AH = 0b00001100; //записываем в регистр OCR1A число 3125 (при работе на частоте 8 МГц это будет давать прерывание каждые 0,1 сек) OCR1AL = 0b00110101; // TCCR1B |= (1<<CS12); //установка делителя 256 } ISR(TIMER1_COMPA_vect) //Прерывание по достижению таймером значения регистра OCR1A (H и L) { n_count++; if (n_count>=200) {n_count=0;} } int main(void) { preset(); timer_ini(); sei(); //глобальное разрешение прерываний while(1) { if ((n_count>0)&&(n_count<100)) {PORTB = 0b11111111;} else if ((n_count>100)&&(n_count<=200)) {PORTB = 0b00000000;}; } } И вот мой Порт В включается и выключается, - ДА, примерно каждые 10 секунд. Как измерил? - положил рядом с включаемым светодиодом свой телефон с запущенным секундомером. И Вот такой подсчёт времени в микроконтроллере даёт погрешность примерно 1 секунду в минуту. А за 4 минуты - МК уже врёт на 4 секунды - убегает вперёд относительно времени, которое на смартфоне... Тогда я переставил код из бесконечного цикла в тело прерывания. Так МК убегает вперёд на 12 секунд за 10 минут... Это слишком большая погрешность.. Как быть? Это нормально и вызвана непостоянностью рабочей частоты? Погрешность эту можно как-то сократить? А если я 30 таких таймеров запилю - мне их все в этом же прерывании обрабатывать будет нормально или они, возможно, не будут успевать обрабатываться за прерывание? Буду рад, примеру простого но более или менее точного программного таймера. И ещё вопрос из фьюзов кроме CKDIV8 ещё какие-то влияют на рабочую частоту МК, если он работает от внутреннего генератора? То есть в моём случае есть всего 2 варианта частоты, - с включенным CKDIV8 это 1МГц, а с выключенным CKDIV8 это 8МГц и третьего не дано? Атмега644, если это важно.
×
×
  • Создать...