Atmega8. Не работает SPI

Обсуждаем контроллеры компании Atmel.
Ответить
Sharcer
Родился
Сообщения: 15
Зарегистрирован: Сб окт 07, 2023 21:34:17

Atmega8. Не работает SPI

Сообщение Sharcer »

Изучаю SPI. Начинающий. Использую МК atmega8. Написал простенький код для того чтобы пересылать значение переменой counter от одного МК к другому.

Как должно работать:
При начале каждой передачи первый МК(далее-master) зажигает ненадолго свой светодиод (на PORTC.4). Потом передает по SPI переменную counter=2 во второй МК (далее-slave), который принимает переменную counter=2 и зажигает соответственно 2 светодиода.
В протеусе все работает нормально.
Изображение Изображение
По итогу получается: собрал все на макетке и при включении master 1 раз включает светодиод, после чего он светится примерно на 50% от того уровня, какой был при включении, и не гаснет вообще (как будто цикл while не выполняется до конца либо выполняется 1 раз). А slave вообще не реагирует никак.

Пробовал убрать связь по SPI между master и slave (в коде закомментил все, что отвечает за SPI) и менял программу просто чтобы каждый МК отдельно помигал своими светодиодами, чтобы исключить неисправность светодиодов и портов МК и все нормально работало. Но как только я возвращал в код все, что связано с SPI, То опять ничего не работало (как было ранее).
Товарищи, не подскажете в чем может быть проблема? Вроде уже и master со slaave менял местами (и на схеме и прошивки) и результат все тот же

Код master:
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5

int counter=2;

void SPI_master_settings(void) //настройки SPI_Master
{

DDRB|=(1<<MOSI)|(1<<SCK)|(1<<SS);
PORTB|=(1<<SS); //этот пин надо установить раньше настроек регистра контроля т.е. раньше команды SPCR|=(1<<MSTR); уже не помню почему
DDRB&=~(1<<MISO);

SPCR|=(1<<SPIE);//SPCR-регистр контроля SPI.
//Ставим SPIE=1 (разрешаем прерывания по линии SPI). Когда произошел обмен данными и мастер получил все 8 бит, то сработает прерывание и флаг SPIF становится=1
SPCR|=(1<<SPE);//SPI ENABLE. Ставим этот бит=1, чтобы разрешить работу SPI
SPCR|=(1<<MSTR); //если MSTR=1, МК работает как master

//нам нужен предделитель частоты МК F/16: SPI2X=0 SPR1=0 SPR0=1
SPSR&=~(1<<SPI2X); // мы не удваиваем частоту работы SPI
SPCR&=~(1<<SPR1);
SPCR|=(1<<SPR0);

SPCR|=(1<<CPOL); //мы используем импульсы отрицательной полярности согласно табл 48 (даташит atmega8 стр.126)
SPCR|=(1<<CPHA);//работаем по заднему фронту импульса
SPCR&=~(1<<DORD); //сперва передаются старшие биты(MSB), а потом младшие (LSB) . Если =0, то наоборот
}

ISR(SPI_STC_vect)
{
while(~SPSR&(1<<SPIF)) //ждем когда данные передадутся и флаг SPIF станет =1 и мы выйдем из цикла
;
PORTB|=(1<<SS);
}

int main(void)
{

SPI_master_settings();
sei();
DDRC|=(1<<4);//светодиод
PORTC&=~(1<<4);

while (1)
{
PORTC|=(1<<4);//мигаем светодиодом
_delay_ms(300);
PORTC&=~(1<<4);
_delay_ms(300);

PORTB&=~(1<<SS);
SPDR=counter;
_delay_ms(300);
}
}


Код slave:
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5

int counter=0;

void SPI_slave_settings(void) //настройки SPI_SLAVE
{
DDRB&=~((1<<MOSI)|(1<<SCK));
DDRB|=(1<<MISO);

SPCR|=(1<<SPIE);//SPCR-регистр контроля SPI.
SPCR|=(1<<SPE);//SPI ENABLE. Ставим этот бит=1, чтобы разрешить работу SPI
SPCR&=~(1<<MSTR); //ставим MSTR=0, чтобы МК работал как slave (если =1 то как master)

SPCR|=(1<<CPOL); //мы используем импульсы отрицательной полярности согласно табл 48 (даташит atmega8 стр.126)
SPCR|=(1<<CPHA);//работаем по заднему фронту импульса
SPCR&=~(1<<DORD); //сперва передаются старшие биты(MSB), а потом младшие (LSB) . Если =0, то наоборот

DDRB&=~(1<<SS);
PORTB|=(1<<SS); //подтягиваем к единице
}

ISR(SPI_STC_vect)
{
while(~SPSR&(1<<SPIF)) //ждем завершения обмена данными
;
counter=SPDR;//присваиваем переменной counter значение полученное по SPI и сохраненное в регистре данных SPI (SPDR)
}


int main(void)
{
DDRC|=(1<<5)|(1<<4);//светодиоды
PORTC&=~((1<<5)|(1<<4));
DDRB|=(1<<7)|(1<<6);//светодиоды
PORTB&=~((1<<7)|(1<<6));
SPI_slave_settings();
sei();

while (1)
{
if(counter>4)
{
PORTC|=(1<<5);
PORTC&=~(1<<4);
PORTB|=(1<<6);
PORTB&=~(1<<7);
}

if(counter==0)
{
PORTC&=~((1<<5)|(1<<4));
PORTB&=~((1<<7)|(1<<6));
}

if(counter==1)
{
PORTC|=(1<<4);
PORTC&=~(1<<5);
PORTB&=~((1<<7)|(1<<6));
}

if(counter==2) //при передаче COUNTER=2 должно работать вот это
{
PORTC|=(1<<5)|(1<<4);
PORTB&=~((1<<7)|(1<<6));
}

if(counter==3)
{
PORTC|=(1<<5)|(1<<4);
PORTB|=(1<<6);
PORTB&=~(1<<7);
}

if(counter==4)
{
PORTC|=(1<<5)|(1<<4);
PORTB|=(1<<7)|(1<<6);
}
}
}
Реклама
Аватара пользователя
Starichok51
Модератор
Сообщения: 19053
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: Atmega8. Не работает SPI

Сообщение Starichok51 »

категорически нельзя в схеме использовать вывод SS в схеме, ни для мастера, ни для слэйва. соответственно, нельзя соединять эти выводы между обоими МК.
мастер настраивается так:
PORTB = 0
DDRB = (1<<SCK) + (1<<MOSI) + (1<<SS)
SS устанавливается выходом, но нем устанавливается ноль.
SPCR = (1<<SPIE) + (1<<SPE) + (1<<MSTR) + (нужное значение битов предделителя)
SPSR устанавливается в зависимости от необходимости в двойной скорости.

слэйв настраивается так:
у слэйва вывод SS нужно подключить на "землю" - подать ноль, тогда у него SPI автоматически активируется.
а порт В у слейва нужно установить только MISO на выход.
SPCR = (1<<SPIE) + (1<<SPE) + (нужное значение битов предделителя)
SPSR устанавливается в зависимости от необходимости в двойной скорости.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Реклама
OKF
Это не хвост, это антенна
Сообщения: 1393
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Atmega8. Не работает SPI

Сообщение OKF »

ТС, ты бы код сначала научился вставлять правильно. А то и отвечать не хочется...
Starichok51, чего это?
SS мастера на выход и выводи в него что хош. И SS мастера не обязательно должен быть выходом - может и входом, но с 1-цей.
Аватара пользователя
Starichok51
Модератор
Сообщения: 19053
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: Atmega8. Не работает SPI

Сообщение Starichok51 »

я сказал в меру своих знаний.
я сделал собственный последовательный программатор, и там у меня работа мастера. поэтому я рассказал, как у меня настроен мастер.
а для программатора настройку слэва ("пациента") делать не надо. поэтому про настройку слэва я просто высказал своё мнение.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Реклама
Эиком - электронные компоненты и радиодетали
Sharcer
Родился
Сообщения: 15
Зарегистрирован: Сб окт 07, 2023 21:34:17

Re: Atmega8. Не работает SPI

Сообщение Sharcer »

[uquote="Starichok51",url="/forum/viewtopic.php?p=4559471#p4559471"]категорически нельзя в схеме использовать вывод SS в схеме, ни для мастера, ни для слэйва. соответственно, нельзя соединять эти выводы между обоими МК.
мастер настраивается так:
PORTB = 0
DDRB = (1<<SCK) + (1<<MOSI) + (1<<SS)
SS устанавливается выходом, но нем устанавливается ноль.
SPCR = (1<<SPIE) + (1<<SPE) + (1<<MSTR) + (нужное значение битов предделителя)
SPSR устанавливается в зависимости от необходимости в двойной скорости.

слэйв настраивается так:
у слэйва вывод SS нужно подключить на "землю" - подать ноль, тогда у него SPI автоматически активируется.
а порт В у слейва нужно установить только MISO на выход.
SPCR = (1<<SPIE) + (1<<SPE) + (нужное значение битов предделителя)
SPSR устанавливается в зависимости от необходимости в двойной скорости.[/uquote]

______________________
1) В мастере: вывод SS теперь ни к чему не подключен. Провод между SS у master и SS slave убрал
Я хочу использовать импульсы отрицательной полярности. Потому поставил MOSI, SCK, SS в единицу по умолчанию. SS хочу ставить в ноль только для передачи, а потом снова в единицу.
DDRB|=(1<<MOSI)|(1<<SCK)|(1<<SS);
PORTB|=(1<<MOSI)|(1<<SCK)|(1<<SS);
Добавил в master в функцию прерывания по SPI одно мигание сигнальным светодиодом до окончания передачи данных и одно мигание после каждой передачи данных:
ISR(SPI_STC_vect)
{
PORTC|=(1<<4);//первое мигание сигнальным светодиодом
_delay_ms(100);
PORTC&=~(1<<4);
_delay_ms(100);
while(~SPSR&(1<<SPIF)) //ждем когда данные передадутся и флаг SPIF станет =1 и мы выйдем из цикла
;
PORTB|=(1<<SS);

PORTC|=(1<<4);//второе мигание сигнальным светодиодом
_delay_ms(100);
PORTC&=~(1<<4);
_delay_ms(100);
}
2) В slave прописал MOSI, SCK как входы, MISO как выход (по умолчанию подтянут к единице, т.к. импульсы отрицательной полярности):
DDRB&=~((1<<MOSI)|(1<<SCK));
DDRB|=(1<<MISO);
PORTB|=(1<<MISO);
Все остальные настройки master и slave уже были прописаны так, как вы и написали.
Вывод SS в slave проводом подключил к нулю.
В симуляции в proteus все прекрасно работает, но на макетке тот самый сигнальный светодиод, подключенный к master, делает первое мигание, НО не делает второе мигание.
Т.е. почему то сама передача либо не начинается вообще, либо не завершается. Не пойму почему. Такое ощущение, что master не может выйти из цикла while(~SPSR&(1<<SPIF)).
При этом slave включает свой первый светодиод, подключенный к PORTC.4. (что соответствует что slave получил переменную counter=1. Потом этот светодиод продолжает гореть и ничего не меняется. Т.е. получается master один раз все-таки передает данные, а потом перестает?

p.S. как правильно код на форуме вставлять?
Реклама
Аватара пользователя
Starichok51
Модератор
Сообщения: 19053
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: Atmega8. Не работает SPI

Сообщение Starichok51 »

при ответе вверху есть тэг
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Реклама
Sharcer
Родился
Сообщения: 15
Зарегистрирован: Сб окт 07, 2023 21:34:17

Re: Atmega8. Не работает SPI

Сообщение Sharcer »

[uquote="Starichok51",url="/forum/viewtopic.php?p=4560271#p4560271"]при ответе вверху есть тэг

Код: Выделить всё

.[/uquote]
Теперь схема выглядит так. 
[url=https://img.radiokot.ru/files/154442/medium/3eoro7ny97.jpg]
Задумка такова, что в master есть переменная counter, которая поочередно в двух циклах for меняется от 0 до 4 и обратно. Значение этой переменной передается по SPI в slave. Причем  в функции прерывания ISR(SPI_STC_vect) master один раз мигает светодиодом D9 при начале отправки по SPI, и 1 раз после отправки. Slave получает по SPI переменную counter и через stitch зажигает светодиоды D5-D8 (число горящих светодиодов равно переменной counter). В симуляции все мигает как надо, но в реале на макетке  1 раз мигнет светодиод D9 и больше ничего не происходит. Как будто в master прерывание по SPI  с чем то конфликтует, хотя у меня других прерываний нет. 

Код master:
[code]
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5

int counter=0;

void SPI_master_settings(void) //настройки  SPI_Master
{	
	DDRB|=(1<<MOSI)|(1<<SCK)|(1<<SS); 
	DDRB&=~(1<<MISO);
	PORTB|=(1<<MOSI)|(1<<SCK)|(1<<SS);
	
	SPCR|=(1<<SPIE);[color=#800000]// разрешаем прерывания по SPI[/color]
	SPCR|=(1<<SPE);// разрешаем работу SPI
	SPCR|=(1<<MSTR); // МК работает как master
	SPSR&=~(1<<SPI2X); //без удвоения частоты
	SPCR&=~(1<<SPR1);
	SPCR|=(1<<SPR0);
	SPCR|=(1<<CPOL); // импульсы отрицательной полярности
	SPCR|=(1<<CPHA);//работаем по заднему фронту импульса
	SPCR&=~(1<<DORD); //сперва передаются старшие биты(MSB
}

ISR(SPI_STC_vect)
{
	PORTC|=(1<<4); //первый раз мигаем светодиодом D9
	_delay_ms(100);
	PORTC&=~(1<<4);
	_delay_ms(100);

	while(~SPSR&(1<<SPIF)) //ждем окончания передачи
	;
	PORTB|=(1<<SS);
	
	PORTC|=(1<<4); //второй раз мигаем светодиодом D9
	_delay_ms(100);
	PORTC&=~(1<<4);
	_delay_ms(100);
}

int main(void)
{  
	SPI_master_settings();
	sei();
	DDRC|=(1<<4);//светодиод D9
	PORTC&=~(1<<4);	
    while (1) 
    {	
		for (int i=0;i<5;i++)
		{
			counter++;
			if(counter>4)
			{counter=4;}
				
			if(counter<0)
			{counter=0;}
			
			
			PORTB&=~(1<<SS);
			SPDR=counter;
			
			_delay_ms(100);	
		}
		
		for (int i=0;i<4;i++)
		{
			counter--;
			if(counter>4)
			{counter=3;}
				
			if(counter<0)
			{counter=0;}
				
			
			PORTB&=~(1<<SS);
			SPDR=counter;
			
			_delay_ms(100);	
		}
    }
}
Код slave:

Код: Выделить всё

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5

int counter=0;

void SPI_slave_settings(void) //настройки  SPI_SLAVE
{
	DDRB&=~((1<<MOSI)|(1<<SCK)); 
	DDRB|=(1<<MISO);
	PORTB|=(1<<MISO);
	
	SPCR|=(1<<SPIE); //разрешаем прерывания по линии SPI
	SPCR|=(1<<SPE);// разрешаем работу SPI
	SPCR&=~(1<<MSTR); // МК работает как slave	
	SPCR|=(1<<CPOL); //мы используем импульсы отрицательной полярности	SPCR|=(1<<CPHA);//работаем по заднему фронту импульса
	SPCR&=~(1<<DORD); //сперва передаются старшие биты(MSB	
	DDRB&=~(1<<SS);
	PORTB&=~(1<<SS);
}
ISR(SPI_STC_vect)
{
	while(~SPSR&(1<<SPIF)) //ждем завершения обмена данными
	;
	counter=SPDR;//присваиваем переменной counter значение полученное по SPI 
}

int main(void)
{
   DDRC|=(1<<5)|(1<<4);//светодиоды D5, D6
   PORTC&=~((1<<5)|(1<<4));
   DDRB|=(1<<7)|(1<<6);//светодиоды D7, D8
   PORTB&=~((1<<7)|(1<<6));
   
   SPI_slave_settings();
   sei();

    while (1) 
    {		
		switch(counter) //мигаем светодиодами
		{
			case 0:PORTC&=~((1<<5)|(1<<4));PORTB&=~((1<<7)|(1<<6));break;
			case 1:PORTC|=(1<<4);PORTC&=~(1<<5);PORTB&=~((1<<7)|(1<<6));break;
			case 2:PORTC|=(1<<5)|(1<<4);PORTB&=~((1<<7)|(1<<6));;break;
			case 3:PORTC|=(1<<5)|(1<<4);PORTB|=(1<<6);PORTB&=~(1<<7);break;
			case 4:PORTC|=(1<<5)|(1<<4);PORTB|=(1<<6)|(1<<7);break;
		}
    }
}

Sharcer
Родился
Сообщения: 15
Зарегистрирован: Сб окт 07, 2023 21:34:17

Re: Atmega8. Не работает SPI

Сообщение Sharcer »

Методом проб и ошибок я частично решил проблему.
Если не использовать прерывания, то цифры 1,2,3,4 нормально передаются от master к slave. Если использовать прерывания, то как будто slave принимает цифры в другом порядке: 1,3,2,4. Собственно мой вопрос почему?
Теперь подробнее.
Не стал использовать прерывания для передачи данных по SPI (поначалу), а передавал их вручную внутри main. В сами настройки SPI для master и slave почти в самом начале добавил обнуление регистров SPCR=0; SPSR=0;SPDR=0;, а остальные настройки оставил прежними. Т При монтаже не занулял SS в slave, а все-таки между master и slave я соединил между собой проводами все выводы, ответственные за SPI. И у меня нормально пошла передача цифр "0","1","2","3","4".
Вот код для master:

Код: Выделить всё

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5

void SPI_master_settings(void) //настройки  SPI_Master
{
	DDRB|=(1<<MOSI)|(1<<SCK)|(1<<SS); 
	PORTB|=(1<<SS); 
	DDRB&=~(1<<MISO);
	PORTB|=(1<<MOSI)|(1<<SCK)|(1<<SS);
	SPCR=0;
	SPSR=0;
	SPDR=0;
	SPCR|=(1<<SPIE);//разрешаем прерывания по линии SPI
	SPCR|=(1<<SPE);//разрешаем работу SPI
	SPCR|=(1<<MSTR); // МК работает как master

	SPSR&=~(1<<SPI2X); //без удвоения частоты
	SPCR&=~(1<<SPR1);//нам нужен предделитель частоты МК clk/16:
	SPCR|=(1<<SPR0);

	SPCR|=(1<<CPOL); //используем импульсы отрицательной полярности
	SPCR|=(1<<CPHA);//работаем по заднему фронту импульса
	SPCR&=~(1<<DORD); //сперва передаются старшие биты
}
int main(void)
{
	SPI_master_settings();
        for (int i=0;i<5;i++)
		{	
			PORTB&=~(1<<SS);
			SPDR=i;
                       while(!(SPSR&(1<<SPIF))) ; //Дождаться окончания передачи
		       PORTB |=(1<<SS); 
			_delay_ms(100);		
		}
}
Аналогично для слейва прием данных я сделал в main без использования прерываний. Полученные по SPI цифры "0","1","2","3","4" я присваивал переменной counter, а в цикле for я зажигал соответствующий светодиод.
Вот код slave:

Код: Выделить всё

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5

void SPI_slave_settings(void) //настройки  SPI_SLAVE
{
	
	DDRB&=~((1<<MOSI)|(1<<SCK)); 
	DDRB|=(1<<MISO);
	PORTB|=(1<<MISO);
	SPCR=0;
	SPSR=0;
	SPDR=0;
	SPCR|=(1<<SPIE);
	SPCR|=(1<<SPE);
	SPCR&=~(1<<MSTR); //МК работает как slave	
	SPCR|=(1<<CPOL); 
	SPCR|=(1<<CPHA);
	SPCR&=~(1<<DORD); 
	
	DDRB&=~(1<<SS);
	PORTB&=~(1<<SS);
}
int main(void)
{
 	DDRC|=(1<<5)|(1<<4);// 1й и 2йсветодиоды 
	PORTC&=~((1<<5)|(1<<4));
	DDRB|=(1<<7)|(1<<6);// 3й и 4й светодиоды
	PORTB&=~((1<<7)|(1<<6));
	_delay_ms(50);
	SPI_slave_settings();
 	while (1) 
 	{
 	 	while(~SPSR&(1<<SPIF)) //ждем завершения обмена данными
		;
		counter=SPDR;//получили цифру от master

 	 	switch(counter)
		{
			case 0:PORTC&=~((1<<5)|(1<<4));PORTB&=~((1<<7)|(1<<6));break; //светодиоды погашены
			case 1:PORTC|=(1<<4);PORTC&=~(1<<5);PORTB&=~((1<<7)|(1<<6));break; //зажигаем 1й светодиод
			case 2:PORTC|=(1<<5);PORTC&=~(1<<4);PORTB&=~((1<<7)|(1<<6));;break; //зажигаем 2й светодиод
			case 3:PORTB|=(1<<6);PORTB&=~(1<<7);PORTC&=~((1<<5)|(1<<4));break; //зажигаем 3й светодиод
			case 4:PORTB|=(1<<7);PORTB&=~(1<<6);PORTC&=~((1<<5)|(1<<4));break; //зажигаем 4й светодиод
		}
 	}
}
Все хорошо работает и последовательно загораются 1й, 2й , 3й , 4й светодиоды.
НО хочется использовать прерывания (вроде это круче, чем вручную в main). Для этого изменил main. При этом у меня со строкой sei() вообще передача не шла на макетке (а в протеусе норм). Но когда я sei() убрал, то передача по SPI заработала(ПОЧЕМУ??? ведь должно быть наоборот ибо sei разрешает прерывания). НО slave зажигает светодиоды в следующем порядке: 1й, 3й, 2й, 4й , хотя master как и прежде передает цифры в порядке 1,2,3,4.
Код master с прерываниями:

Код: Выделить всё

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5

void SPI_master_settings(void) //настройки  SPI_Master
{
	DDRB|=(1<<MOSI)|(1<<SCK)|(1<<SS); 
	PORTB|=(1<<SS); 
	DDRB&=~(1<<MISO);
	PORTB|=(1<<MOSI)|(1<<SCK)|(1<<SS);
	SPCR=0;
	SPSR=0;
	SPDR=0;
	SPCR|=(1<<SPIE);//разрешаем прерывания по линии SPI
	SPCR|=(1<<SPE);//разрешаем работу SPI
	SPCR|=(1<<MSTR); // МК работает как master

	SPSR&=~(1<<SPI2X); //без удвоения частоты
	SPCR&=~(1<<SPR1);//нам нужен предделитель частоты МК clk/16:
	SPCR|=(1<<SPR0);

	SPCR|=(1<<CPOL); //используем импульсы отрицательной полярности
	SPCR|=(1<<CPHA);//работаем по заднему фронту импульса
	SPCR&=~(1<<DORD); //сперва передаются старшие биты
}

ISR(SPI_STC_vect)
{
	while(~SPSR&(1<<SPIF)) //ждем когда данные передадутся и флаг SPIF станет =1 и мы выйдем из цикла
	;
	PORTB|=(1<<SS);
}

int main(void)
{
    
	SPI_master_settings();
 	while (1) 
	{	
		for (int i=0;i<5;i++) //по очереди отправляем цифры  0,1,2,3,4 в slave
		{
			PORTB&=~(1<<SS);
			SPDR=i;
			_delay_ms(100);				
		}	
	}
}
Вот код slave с прерываниями:

Код: Выделить всё

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define SS 2
#define MOSI 3
#define MISO 4
#define SCK 5
int counter=0;

void SPI_slave_settings(void) //настройки  SPI_SLAVE
{
	DDRB&=~((1<<MOSI)|(1<<SCK)); 
	DDRB|=(1<<MISO);
	PORTB|=(1<<MISO);
	SPCR=0;
	SPSR=0;
	SPDR=0;		
	SPCR|=(1<<SPIE);
	SPCR|=(1<<SPE);
	SPCR&=~(1<<MSTR);	
	//мы не выбираем частоты работы SPI для slave, т.к. он подчиняется master 	
	SPCR|=(1<<CPOL); 
	SPCR|=(1<<CPHA);
	SPCR&=~(1<<DORD);
	DDRB&=~(1<<SS);
	PORTB&=~(1<<SS);
}

ISR(SPI_STC_vect)
{
	while(~SPSR&(1<<SPIF)) //ждем завершения обмена данными
	;
	counter=SPDR;//присваиваем переменной counter значение полученное по SPI и сохраненное в регистре данных SPI (SPDR)
}


int main(void)
{  
   DDRC|=(1<<5)|(1<<4);// 1й и 2йсветодиоды
   PORTC&=~((1<<5)|(1<<4));
   DDRB|=(1<<7)|(1<<6);// 3й и 4й светодиоды
   PORTB&=~((1<<7)|(1<<6));
   _delay_ms(50);   
  
   SPI_slave_settings();   
   sei();
   while (1) 
    {
		switch(counter)
		{
			case 0:PORTC&=~((1<<5)|(1<<4));PORTB&=~((1<<7)|(1<<6));break; //светодиоды погашены
			case 1:PORTC|=(1<<4);PORTC&=~(1<<5);PORTB&=~((1<<7)|(1<<6));break; //зажигаем 1й светодиод
			case 2:PORTC|=(1<<5);PORTC&=~(1<<4);PORTB&=~((1<<7)|(1<<6));;break; //зажигаем 2й светодиод
			case 3:PORTB|=(1<<6);PORTB&=~(1<<7);PORTC&=~((1<<5)|(1<<4));break; //зажигаем 3й светодиод
			case 4:PORTB|=(1<<7);PORTB&=~(1<<6);PORTC&=~((1<<5)|(1<<4));break; //зажигаем 4й светодиод
		}
		_delay_ms(100);
    }
}
Ответить

Вернуться в «AVR»