@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);
	}
}
  • Вопрос задан
  • 197 просмотров
Решения вопроса 1
@vanyamba-electronics
Попробуйте подтягивающие вверх резисторы на 10-20К поставить.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@termo300 Автор вопроса
Итак, решением являлось задействовать ножку SS (в моем случае это PB4) в коде, в явном виде указав её как выход.
Так как я использовал для выбора управляемого устройства совсем другую ногу (PD7), то штатную решил вообще не определять в коде. Подключение к ней подтягивающего вверх резистора сделало её невосприимчивой к случайным сигналам.
В документации написано, что при включении режима мастера по SPI приходящий на данную ногу сигнал автоматически выключает мастера, так как означает, что другой мастер выбрал это устройство, как слейв. А по умолчанию нога является входом, и случайные наводки давали такой результат.
Ответ написан
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы
Insigne Волгоград
от 50 000 до 100 000 руб.
Индекс.СРО Ростов-на-Дону
от 45 000 до 70 000 руб.
16 июн. 2019, в 19:42
500 руб./за проект
16 июн. 2019, в 18:38
5000 руб./за проект
16 июн. 2019, в 17:14
5000 руб./за проект