@termo300

Как не терять связь ATmega16A и LCD Nokia5110 по SPI?

Суть проблемы вот в чем. Решил освоить SPI и связь с дисплеем Nokia 5110. Столкнулся с пропаданием связи в случайные моменты времени – дисплей получает данные от 1 до 10 секунд, после чего контроллер бесконечно долго ждет ответа о пересылке данных.

Для программирования использую ATmega16A-PU c внутренним генератором частоты 8МГц, программатор внутрисхемный USBASP v2.0, Atmel Studio 7, и AVRDUDE.

Более-менее стабильная работа программы возможна при выставлении F_CPU=1000UL и делителе частоты SPI f/128. Кроме того, более стабильная работа устройства наблюдается при подключенном разъеме программатора – дисплей выводил счетчик от 10 секунд до пары минут.

При нормальном F_CPU общение с дисплеем заканчивается после второй команды настройки дисплея. После чего контроллер отправляет по одной команде в 2-10 минут, но в итоге изображения на дисплее все равно нет.
Почитал про необходимость внешнего кварца, установил согласно даташиту, подсоединив ноги на землю через конденсаторы пленочные (50В 15пФ). Микроконтроллер прошивался и работал в целом минут 15, после чего на запросы программатора более никогда не отвечал и программу не выполнял. Что тут можно сделать не так?

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

Схема подключения:
5abb68b82017f101544229.png
Программный код
--Вступление
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>

unsigned char ii;
unsigned char s,m,h;
int s2,s3,h2,h3,m2,m3;
--Настройки таймера для вывода на экран
void timer_ini(void)
{
	TCCR1B |= (1<<WGM12); // устанавливаем режим СТС (сброс по совпадению)
	TIMSK |= (1<<OCIE1A); //устанавливаем бит разрешения прерывания 1ого счетчика по совпадению с OCR1A(H и L)
	OCR1AH = 0b00111101; //записываем в регистр число для сравнения
	OCR1AL = 0b00001001;
	TCCR1B |= (1<<CS11)|(1<<CS10);//установим делитель f/128.
}
--Моргание светодиодом статуса
void mig(int dl)
{
	PORTD |= (1<<PORTD7); 
	//моргаем светодиодиком в знак выполнения команд
	for(ii=0;ii<=(dl/100);ii++)
	{
		_delay_ms(100);
	}
	PORTD &= ~(1<<PORTD7);
	for(ii=0;ii<=(dl/100);ii++)
	{
		_delay_ms(100);
	}
}
--Передача по SPI
void senddata(unsigned char snd)
{
	SPDR = snd;
	while(!(SPSR & (1<<SPIF))){
		PORTD |=(1<<PORTD6);//подождем пока данные передадутся и пока посветим диодиком
	}
	PORTD &= ~(1<<PORTD6);
}
--Подача настроечных команд на дисплей
void initscr(void)
{
	PORTD &= ~((1<<PORTD0)|(1<<PORTD1)|(1<<PORTD2)); //ножки RST, CE(SS), D/C низкий уровень
	_delay_ms(1);
	//PORTD |= ((1<<PORTD1)|(1<<PORTD2));//сброс SS и RST
	PORTD |= (1<<PORTD0);//сброс RST
	senddata(0b00100001);// отправка настроечных команд
	senddata(0b00010011);
	senddata(0b00000100);
	senddata(0b11000001);
	senddata(0b00100000);
	senddata(0b00001100);

	PORTD |=(1<<PORTD2); // включим передачу рисовашек
	PORTD |= (1<<PORTD1);//сброс SS
}
--Процедура отрисовки символов
void sendchar(unsigned char str)
{
	// в зависимости от символа рисуем его заранее подобранными командами.
	switch (str)
	{
		case ('A') : senddata(0xF8);senddata(0x24);senddata(0x22);senddata(0x24);senddata(0xF8);senddata(0x00); break;
		// и так далее каждый символ
	}
}
--Вывод на экран строки
void sendstr(char str1[])
{
	unsigned int n;
	// посылаем на экран строки, разбивая их посимвольно
	PORTD &= ~(1<<PORTD1); //ножка CE(SS) низкий уровень
	for(n=0;str1[n]!='\0';n++)
	{
		sendchar(str1[n]);
	}
	PORTD |= (1<<PORTD1);//сброс SS
}
--Очистка экрана
void clearscr(void)
{
	unsigned int n;
	// очитсим экран, послав пробел много раз
	PORTD &= ~(1<<PORTD1); //ножка CE(SS) низкий уровень
	for(n=0;n<=83;n++){
		sendchar(' ');
	}
	PORTD |= (1<<PORTD1);//сброс SS
}
--Настройка SPI
void initSPI(void)
{
	DDRB |= ((1<<PORTB5)|(1<<PORTB7)); //ножки SPI (MOSI и SCK) на выход
	PORTB |= ((1<<PORTB5)|(1<<PORTB7)); //низкий уровень
	DDRB &= ~(1<<PORTB6);// ножка MISO на вход
	SPCR |= ((1<<SPE)|(1<<MSTR));	//Включим шину, объявим ведущим
	SPCR |= ((1<<SPR0)|(1<<SPR1));//установим делитель f/128.
	SPSR &= ~(1<<SPR0);
	
	DDRD |= 0xFF; // ножки на выход
	PORTD &= ~(1<<PORTD7);//выключим диод
}
--Счетчик по прерыванию
ISR (TIMER1_COMPA_vect)
{ //запустим счетчики часов, минут и секунд
		s++;
		if (s==60)
		{
			m++;
			s=0;
			if(m==60)
			{
				h++;
				m=0;
			}
		}
		// отделим десятки и единицы часов, минут и секунд друг от друга
		h2=h/10;
		h3=h%10;
								
		m2=m/10;
		m3=m%10;
								
		s2=s/10;
		s3=s%10;
}
--Главная
int main(void)
{
	timer_ini();
	initSPI();
	mig(1000); //после выполнения процедуры инициализации таймера и SPI моргнем на секунду
	
	initscr();
	mig(300);//после выполнения процедуры инициализации дисплея моргнем быстро три раза
	mig(300);
	mig(300);
	
	clearscr();
	mig(300); // очистим экран и моргнем быстро три раза
	mig(300);
	mig(300);
	
	// поприветствуем кого-нибудь
	sendstr("              ");
	sendstr("     Всем     ");
	sendstr("    привет!   ");
	sendstr("              ");
	sendstr("   -=(o_O)=-  ");
	sendstr("              ");
	_delay_ms(3000);
	sei(); // запустим счетчик
	while(1)
	{
		// и каждые полсекунды посылаем на экран текущее время отсчета
		sendstr("              ");
		sendstr("              ");
		sendstr("   ");
		switch (h2)
		{
			case 0: sendstr("0");break;
			case 1: sendstr("1");break;
			case 2: sendstr("2");break;
			case 3: sendstr("3");break;
			case 4: sendstr("4");break;
			case 5: sendstr("5");break;
			case 6: sendstr("6");break;
			case 7: sendstr("7");break;
			case 8: sendstr("8");break;
			case 9: sendstr("9");break;
		}
		switch (h3)
		{
			case 0: sendstr("0");break;
			case 1: sendstr("1");break;
			case 2: sendstr("2");break;
			case 3: sendstr("3");break;
			case 4: sendstr("4");break;
			case 5: sendstr("5");break;
			case 6: sendstr("6");break;
			case 7: sendstr("7");break;
			case 8: sendstr("8");break;
			case 9: sendstr("9");break;
		}
		sendstr(":");
		switch (m2)
		{
			case 0: sendstr("0");break;
			case 1: sendstr("1");break;
			case 2: sendstr("2");break;
			case 3: sendstr("3");break;
			case 4: sendstr("4");break;
			case 5: sendstr("5");break;
			case 6: sendstr("6");break;
			case 7: sendstr("7");break;
			case 8: sendstr("8");break;
			case 9: sendstr("9");break;
		}
		switch (m3)
		{
			case 0: sendstr("0");break;
			case 1: sendstr("1");break;
			case 2: sendstr("2");break;
			case 3: sendstr("3");break;
			case 4: sendstr("4");break;
			case 5: sendstr("5");break;
			case 6: sendstr("6");break;
			case 7: sendstr("7");break;
			case 8: sendstr("8");break;
			case 9: sendstr("9");break;
		}
		sendstr(":");
		switch (s2)
		{
			case 0: sendstr("0");break;
			case 1: sendstr("1");break;
			case 2: sendstr("2");break;
			case 3: sendstr("3");break;
			case 4: sendstr("4");break;
			case 5: sendstr("5");break;
			case 6: sendstr("6");break;
			case 7: sendstr("7");break;
			case 8: sendstr("8");break;
			case 9: sendstr("9");break;
		}
		switch (s3)
		{
			case 0: sendstr("0");break;
			case 1: sendstr("1");break;
			case 2: sendstr("2");break;
			case 3: sendstr("3");break;
			case 4: sendstr("4");break;
			case 5: sendstr("5");break;
			case 6: sendstr("6");break;
			case 7: sendstr("7");break;
			case 8: sendstr("8");break;
			case 9: sendstr("9");break;
		}
		sendstr("   ");
		sendstr("              ");
		sendstr("              ");
		sendstr("              ");
		_delay_ms(500);
	}
}
  • Вопрос задан
  • 200 просмотров
Решения вопроса 1
@vanyamba-electronics
Попробуйте подтягивающие вверх резисторы на 10-20К поставить.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@termo300 Автор вопроса
Итак, решением являлось задействовать ножку SS (в моем случае это PB4) в коде, в явном виде указав её как выход.
Так как я использовал для выбора управляемого устройства совсем другую ногу (PD7), то штатную решил вообще не определять в коде. Подключение к ней подтягивающего вверх резистора сделало её невосприимчивой к случайным сигналам.
В документации написано, что при включении режима мастера по SPI приходящий на данную ногу сигнал автоматически выключает мастера, так как означает, что другой мастер выбрал это устройство, как слейв. А по умолчанию нога является входом, и случайные наводки давали такой результат.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы