Jump to content

    
Sign in to follow this  
kpv

Микроконтроллеры для начинающих

Recommended Posts

Функция eeprom_update_word() сама этого не делает?

Если не делает, то её фактически нет, разве что какому-то программисту от микроконтроллера понадобится только его EEPROM и ничего более.

 

На время подачи сигнала разблокирования записи в EEPROM прерывания требуется запретить, чтобы выполнить требования по разблокирующей последовательности (у микроконтроллеров PIC, например, это последовательная запись 55 и AA в регистр разблокировки), но далее, на саму запущенную процедуру записи, любая работа программы никакого влияния не оказывает, в т.ч. и её работа в прерываниях.

Share this post


Link to post
Share on other sites

Снова здравствуйте.

 

AVR.

 

Есть 2 самописных функции.

Эти функции используют разные глобальные переменные, вообще никак не пересекаются между собой. Вызываются в оновном цикле, никак не завязаны на прерывания. В одной из них работа с SPI, в другой с АЦП.

Но при этом функция с АЦП влияет на работу функции с SPI. Нашёл 2 строчки из-за которых это происходит, но там просто математические вычисления, никакого отношения не имеющие к другой функции.

 

При включении любого уровня оптимизации это проявляется. На уровне -О0 всё работает нормально.

 

Подскажите в чём может быть дело?

Edited by lyric

Share this post


Link to post
Share on other sites
код нужен

 

#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 строчки, выделенные жирным цветом - если их закомментировать, то мерцание пропадает. Какое отношение они имеют к индикации - непонятно, но влияют. Такие же две строчки есть и чуть выше в этой же функции,

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

Edited by lyric

Share this post


Link to post
Share on other sites

calibr_ADC_max_AI2 - calibr_ADC_min_AI2 у вас неинициализированы, а в этих строках вы делите на их разность, оная будет равна 0, так они в стартапе обе обнуляются.

 

Share this post


Link to post
Share on other sites
calibr_ADC_max_AI2 - calibr_ADC_min_AI2 у вас неинициализированы, а в этих строках вы делите на их разность, оная будет равна 0, так они в стартапе обе обнуляются.

 

Все переменные инициализировал, ничего не изменилось.

Ну и прошу прощения, - ошибся:

Присмотрелся лучше к экрану - мерцания прекращаются только если ADC_convert вообще не вызывать.

Что-то не так с этой функцией (подскажите что?). И почему она портит только один разряд а не все 8 - не пойму.

 

Когда ADMUX=0 и ADMUX=1 функция ADC_convert выполняется дольше, потому что больше расчётов в ней происходит.

Поэтому функция indi() в эти моменты дольше НЕ выполняется.

И, видимо, эти моменты совпадают с моментами, когда должен включаться индикатор (R8).

 

Но между ADMUX=0 и ADMUX=1 функция indi() всё равно должна успеть выполниться 1 раз, и получается что мерцать должны 2 индикатора, а не один.

Так и есть? или это бред?

 

:help: :help: :help:

Edited by lyric

Share this post


Link to post
Share on other sites

Добрый день. Коллеги, просветите меня пожалуйста, в каком порядке правильно разрешать внешние прерывания в С на Atmel Studio 7, микроконтроллер ATMega2560, хотя эта проблема и на других микроконтроллерах AVR. Я поступаю следующим образом. Предположим что программа работает в цикле измерений. Запуск измерения производится по внешнему прерыванию по входу INT4. Я разрешаю прерывание на INT4, затем sei(). По окончании измерения даю cli() и запрещаю прерывание по INT4. В результате первое прерывание происходит произвольно, без каких либо событий на входе INT4, т.е. одно прерывание лишнее. В чём может быть проблема? Спасибо!

Share this post


Link to post
Share on other sites
5 hours ago, Smoky said:

В результате первое прерывание происходит произвольно, без каких либо событий на входе INT4,

Перед разрешением прерываний прочтите состояние INT4 (или что там у ATmega), что бы сбросить pending прерывание.

 

Share this post


Link to post
Share on other sites
1 час назад, xvr сказал:

Перед разрешением прерываний прочтите состояние INT4 (или что там у ATmega), что бы сбросить pending прерывание.

 

Спасибо, вы правы. Весьма удивлён тем что флаги прерываний устанавливаются даже при запрете прерываний...

Share this post


Link to post
Share on other sites
4 minutes ago, Smoky said:

Спасибо, вы правы. Весьма удивлён тем что флаги прерываний устанавливаются даже при запрете прерываний...

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

Share this post


Link to post
Share on other sites
34 minutes ago, rx3apf said:

Запрет прерываний - это запрет обработки. Флаг прерывания - результат события. Так что все совершенно логично.

События и прерывания однако разные вещи в общем случае.
В некоторых архитектурах они эквивалентны, в других обрабатываются разными движками.  

Share this post


Link to post
Share on other sites
1 час назад, rx3apf сказал:

Запрет прерываний - это запрет обработки. Флаг прерывания - результат события. Так что все совершенно логично.

Занимаюсь этим уже не первый год а столкнулся с этим впервые. Обычно использовал прерывания для одного события и всё "сходило с рук", а тут пришлось фиксировать несколько разделённых по времени событий по одному входу и тут же "влип". Спасибо всем за разъяснения, очень помогли.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this