Аппаратный вариант

Для работы с аппаратным SPI его нужно сначала, естественно,
инициализировать. Посмотрим, как это будет выглядеть для ATmega8535 (а
также для многих других МК того же класса, например, ATmega8515, ATmega16 и
пр., у которых выводы аппаратного SPI совпадают с интерфейсом
SPI-программирования и занимают старшие 4 бита порта В). Заодно
инициализируем и вывод для управления  "выбором кристалла" — в большинстве
случаев он в интерфейсе SPI играет важную роль (например, микросхемы
памяти, которые мы рассмотрим далее, высокий уровень на этом выводе, кроме
всего прочего, устанавливает в состояние с низким  потреблением). Часто для
этого выбирают вывод /SS, который в режиме "мастера" не  задействован (не
забудем, что его нужно сконфигурировать на выход!), но мы для разнообразия
выберем вывод РВО — часто требуется управлять несколькими  устройствами,
одного вывода /SS тогда все равно недостаточно, и младшие биты  порта В,
идущие подряд, оказываются более удобными (листинг 11.1).

[ Листинг 11.1 ]
.equ	CS =	РВ0
.equ	MOSI =	PB5
.equ	MISO =	РВ6
.equ	SCK =	PB7	;разряды порта В, используемые интерфейсом SPL.

Листинг 11.2 иллюстрирует инициализацию в режиме 0 ("мастер", DORD = 0 —
старший бит первым, SPR1 и SPR0 = 0 — скорость fрез/4).

[ Листинг 11.2 ]
	ldi	temp,(1<<SPE)+(1<<MSTR)
	out	SPCR,temp
	ldi	temp,(l<<SCK)+(1<<MOSI)+(1<<CS)+(1<<PB4)
	out	DDRB,temp	;SCK,MOSI,CS, SS - выходы
	sbi	PORTB,CS	;сразу устанавливаем в 1

Листинг 11.3 иллюстрирует инициализацию в режиме 3 ("мастер", DORD = 0 —
старший бит первым, SPR1 и SPR0 = 0 — скорость fрез/4).

[ Листинг 11.3 ]
	ldi temp, (1«SPE) + (1«MSTR) + (1«CP0L) + (1«CPHA)
	out SPCR,temp
	ldi temp, (1«SCK) + (1«M0SI) + (1«CS) + (1«PB4)
	>out DDRB,temp ;SCK,MOSI,CS, SS - выходы
	ser temp ;0xFF
	out PORTB,temp ;PB7..0 — высокий уровень, весь порт В

Процедура записи-чтения тогда будет выглядеть очень просто (листинг 11.4).

[ Листинг 11.4 ]
WR_spi: ;запись-чтение SPI, в temp данные на входе и на выходе
	out SPDR,temp	; начать передачу
wait_spi:
	sbis SPSR,SPIF	; ожидаем конца передачи
	rjmp wait_spi
	in temp,SPDR	; чтение данных
	ret

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

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Программный вариант

Программный вариант процедур чтения-записи более громоздок, но зато
позволяет задействовать любые удобные выводы портов МК (в том числе и
таких, которые вообще аппаратного SPI не имеют) и не требует особой
инициализации, кроме формирования направления работы соответствующих
выводов. Листинг 11.5  иллюстрирует, как будет выглядеть чтение-запись в
варианте, соответствующем  режиму 0.

[ Листинг 11,5 ]
.equ	CS	= 0	; выводы PortB
.equ	MOSI	= 1
.equ	MISO	= 2
.equ	SCK	= 3
...
; инициализация SPI
; установка MOSI, SCK, CS на выход
	ldi	temp,(1<<CS)|(1<<MOSI)|(1<<SCK)
	out	DDRB,temp
	cbi	PORTB,SCK
	cbi	PORTB,MOSI
	sbi	PORTB,CS
...
;чтение-запись
RW_spi:	;запись/чтение через SPI. посылаемый байт в data_out,
; принимаемый в data_in
	cli			; на случай, если в программе есть длинные
				; прерывания, иначе можно удалить
	ldi	temp,8		; счетчик бит
	clr	data_in
spi_Loop:
	lsl	data_out	;старший бит в перенос
	brcc	put_0		; если в переносе 0 — перейти
	sbi	PORTB,mosi
	nop			; задержка на один такт
	rjmp	r_bit

put_0:
	cbi PORTB,mosi
	nop ; задержка на один такт
r_bit:
	sbi	PORTB,sck	; выдали строб и подождали
	nop			; задержка на один такт
	sbic	PINB,miso	; читаем бит с miso
	rjmp	r1_bit
	clc			; если 0 — сбросим перенос
	rjmp	rend_bit
r1_bit:
	sec			; если 1 — установим перенос
rend_bit:
	rol	data_in		; перенос во входной байт
	cbi	PORTB,sck	; выдали строб и подождали
	nop
	dec	temp
	brne	spi_loop
	sei			; если команды cli нет, то тоже можно удалить
	ret

При тактовой частоте МК 4 МГц такая процедура обеспечит скорость передачи
порядка 0,5 МГц. Пустые операции (пор) нужны, чтобы сформировать задержку
(-250 не при 4 МГц) между формированием данных на линиях MISO и MOSI и
моментом их чтения (перепадом на SCK). Если быстродействие ведомого
позволяет, то эти операции можно убрать.
