Программа, рассмотренная в этой статье, разработана для контроллера SPI-шлюза (шлюз у нас реализован на ATTiny2313). Эта программа позволяет из терминальной программы персонального компьютера общаться в качестве Master-а с различными SPI устройствами. В программе реализованы все 4 режима SPI, размер пакета может быть установлен от 1 до 64 бит, возможен выбор передачи пакета младшим или старшим битом вперёд, а также можно выбрать ручное или автоматическое управление линией SS. Написана программа полностью на ассемблере, в конце статьи выложены исходники (с комментариями) и прошивка.
Для реализации обмена данными по SPI между контроллером и подключаемым устройством были использованы стандартные, написанные нами ранее процедуры.
Протокол обмена с компьютером был придуман на ходу и состоит из сообщений, размером в 1 или несколько байт.
При передаче данных от компьютера к шлюзу, первый байт сообщения — это всегда команда. Далее, в зависимости от команды, сообщение может включать ещё несколько байт.
Команды используются такие:
01h — загрузка конфигурации в шлюз. После принятия такой команды шлюз ждёт два байта конфигурации, которые определяют режим SPI, размер пакета, режим управления линией SS, порядок передачи бит (младшим или старшим вперёд) и скорость передачи. В шлюзе конфигурация хранится в двух специально выделенных регистрах: C1 и C2. Здесь нужно пожалуй добавить, что максимальная скорость передачи по SPI получилась где-то в районе 75 КБит/с, что довольно медленно для SPI и позволяет работать с любым устройством. Поскольку в конфигурации по умолчанию как раз заложена эта максимальная скорость, то менять её в общем-то особого смысла нет.
02h — считывание конфигурации из шлюза. После принятия такой команды шлюз отсылает на компьютер два байта конфигурации (С1, С2).
03h — загрузить пакет в шлюз. После принятия такой команды шлюз ждёт N байт пакета. N зависит от размера пакета, установленного в конфигурации шлюза и определяется по формуле N=(K-1)/8+1, где К — размер пакета в битах.
04h — считать пакет из шлюза. После принятия этой команды шлюз отсылает на компьютер N используемых байт сдвигового регистра.
05h — переключить SS в единицу. Если в конфигурации установлено ручное управление линией SS, то после принятия этой команды шлюз переключает линию SS в единицу.
06h — переключить SS в ноль. Если в конфигурации установлено ручное управление линией SS, то после принятия этой команды шлюз переключает линию SS в ноль.
07h — старт обмена. После принятия этой команды шлюз выполняет обмен пакетами с подключенным к нему по SPI устройством, в соответствии с установленной конфигурацией (режим SPI, размер пакета и прочее).
При передаче данных от шлюза к компьютеру сообщение также может состоять из одного или нескольких байт. В данном случае служебным является последний байт сообщения, — он сообщает о результатах выполнения последней команды компьютера, а байты перед ним — это байты, запрошенные последней командой (конфигурация или пакет). Соответственно, если предыдущая команда не запрашивала байты от шлюза, то и сообщение шлюза будет состоять из одного только служебного байта.
Служебные байты могут быть такими:
01h — предыдущая команда выполнена успешно.
FFh — шлюзом была получена неизвестная команда.
FEh — попытка установить размер пакета равным нулю (то есть получены неправильные байты конфигурации).
FDh — попытка передачи, при высоком уровне на линии SS (такая ошибка возникает, если послать шлюзу команду 07h при ручном управлении линией SS, когда эта линия установлена в 1).
Вообще ручное и автоматическое управление линией SS работает следующим образом: при ручном управлении линия переключается только специальными командами от компа, при автоматическом — линия переключается в низкий уровень автоматически, сразу после приёма пакета от компьютера, после этого автоматически запускается обмен, а сразу после его завершения SS автоматически возвращается к высокому уровню (тут правильнее было бы проверять — не пришёл ли от компа ещё один пакет, пока мы передавали, и, в случае его обнаружения, — продолжать передачу, но пока переделывать прогу лень).
Прога написана таким образом, что при загрузке пакета из компьютера в шлюз, сначала надо отправлять младшие байты пакета, а потом старшие (т.е. если пакет имеет вид «2Ah 3Bh 4Eh», то шлюзу надо сначала отправлять 4Eh, потом 3Bh, а потом 2Ah).
Из интересных фишек в реализации отмечу ещё вот какую. В зависимости от режима SPI и порядка передачи бит, чтение бита с шины и сдвиг могут происходить по разному (у нас есть 2 процедуры для чтения и 4 для сдвига) и в разном порядке. Чтобы в процессе обмена не выяснять каждый раз заново, какую именно процедуру нужно выполнять (это бы очень сильно снизило скорость работы) — мы делаем это только один раз, на этапе конфигурирования, а потом записываем в 4 специальных регистра (Proc1_Address_L, Proc1_Address_H, Proc2_Address_L, Proc2_Address_H) адреса процедур, нужных нам в данной конфигурации для чтения и сдвига, причём если сначала выполняется чтение, а потом сдвиг, то мы в первые два регистра пишем адрес процедуры чтения, а во вторые два — адрес процедуры сдвига, если сначала нам нужен сдвиг, потом чтение, то наоборот, — в первые два регистра пишем адрес процедуры сдвига, а во вторые два — адрес процедуры чтения. Всё, теперь непосредственно при обмене нам вообще не нужно задумываться о том, что и как делать, — нужно просто по чётным фронтам вызывать с помощью косвенной адресации процедуру по первому сохранённому адресу, а по нечётным — процедуру по второму сохранённому адресу.
Вот пожалуй и всё описание (если возникнут вопросы — добро пожаловать на форум), а теперь перейдём к алгоритму и программе.
Алгоритм:
Итак, в аппаратной части мы имеем:
PB0 — линия SCLK
PB1 — линия MISO
PB2 — линия MOSI
PD5 — линия SS
PD0 — линия Rx
PD1 — линия Tx
Программа:
;--------------------------------------------------------- .device ATtiny2313 .include "tn2313def.inc" .list ;-- определяем свои переменные .def Low_byte_Address = r0 ; здесь мы храним адрес младшего байта регистра .def Hi_byte_Address = r1 ; здесь мы храним адрес старшего байта регистра .def Byte_Number = r2 ; здесь храним размер сдвигового регистра .def C1 = r3 ; первый байт конфигурации ;(старшие 2 бита - режим SPI, младшие 6 бит - размер пакета) .def C2 = r4 ; второй байт конфигурации ;(старшие 2 бита - режим управления линией CS (0-man, 1-aut), ; порядок передачи битов (0-L, 1-H), младшие 6 бит - скорость) .def SR_Offset = r5 ; смещение для выравнивания вправо/влево .def Razmer_bit = r6 ; размер регистра в битах .def Timer_Set = r7 ; значение для регистра таймера (скорость) .def Proc1_Address_L = r8 ; младший байт адреса первой процедуры .def Proc1_Address_H = r9 ; старший байт адреса первой процедуры .def Proc2_Address_L = r10; младший байт адреса второй процедуры .def Proc2_Address_H = r11; младший байт адреса второй процедуры ;-------------------------------------- .def REG_0=r16 ; начало сдвигового регистра (10h) .def REG_1=r17 .def REG_2=r18 .def REG_3=r19 .def REG_4=r20 .def REG_5=r21 .def REG_6=r22 .def REG_7=r23 ; конец сдвигового регистра (17h) ;------------------------------------- .def Front_Counter=r24 ; счётчик фронтов SCLK .def Byte_Counter=r25 ; счётчик байт ;регистры YH=w, YL, ZH=temp1, ZL=temp2 ;------------------------------------- .def w=r29 .def temp1=r31 .def temp2=r30 ;-- Используемые порты --------------- .equ PORT_SPI=0x18 ; порт B - выходы .equ PIN_SPI =0x16 ; порт B - входы ;-- определяем названия выводов ------ .equ SCLK_Line=0 ; PortB0/PinB0 - clock .equ MISO_Line=1 ; PortB1/PinB1 - вход мастера .equ MOSI_Line=2 ; PortB2/PinB2 - выход мастера .equ CS_Line=5 ; PinD5 - выход Chip Select ; кроме того, мы используем линии Rx (PD0), Tx (PD1) ;------------------------------------- ;-- начало программного кода .cseg .org 0 rjmp Init ; переход на начало программы (вектор сброса) ;-- дальше идут вектора прерываний ;-- если не используем - reti, иначе - переход на начало обработчика reti ; внешнее прерывание INT0 reti ; внешнее прерывание INT1 reti ; Input capture interrupt 1 reti ; Timer/Counter1 Compare Match A reti ; Overflow1 Interrupt reti ; Overflow0 Interrupt rjmp RX_INT ; USART0 RX Complete Interrupt reti ; USART0 Data Register Empty Interrupt reti ; USART0 TX Complete Interrupt reti ; Analog Comparator Interrupt reti ; Pin Change Interrupt reti ; Timer/Counter1 Compare Match B rjmp T0_INT ; Timer/Counter0 Compare Match A reti ; Timer/Counter0 Compare Match B reti ; USI start interrupt reti ; USI overflow interrupt reti ; EEPROM write complete reti ; Watchdog Timer Interrupt ;-- начало программы Init: ldi w,RAMEND ; устанавливаем указатель вершины out SPL,w ; стека на старший байт RAM sbi ACSR,ACD ; выключаем компаратор ;-- инициализируем порты ser w ; w=0xFF out DDRA,w ; настраиваем порт A. все линии на выход clr w ; w=0x00 out PORTA,w ; на всех линиях ноль ldi w,0b11111101; настраиваем порт B. PB1 - вход out DDRB,w clr w ; определяем нач.состояние выходов и подт.на входах out PORTB,w ; (выходы - нули, подтяжек нет) ldi w,0b11111100; настраиваем порт D out DDRD,w ; PD0, PD1 - входы clr w ; определяем нач.состояние выходов и подт.на входах out PORTD,w ; (выходы - нули, подтяжек нет) ;-- инициализируем UART out UBRRH,w ; UBRRR (для кварца 20МГц и скорости 115200) ldi w,10 ; равен 10, т.е. UBRRH=0, UBRRL=10 out UBRRL,w ldi w,0b00001110; поднимаем биты USBS, UCSZ1:0 out UCSRC,w ; формат: 1 старт, 8 данные, 2 стоп sbi UCSRB,TXEN ; включить передатчик nop sbi UCSRB,RXEN ; включить приёмник ;-- разрешаем прерывание от приёмника sbi UCSRB,RXCIE ; включить прерывания от приёмника ;-- Загружаем начальную конфигурацию ldi w,0b00001000; Mode0, пакет 8 бит mov C1,w ldi w,0b00010010; CS-man, младшим вперёд, скорость 18 (около 75 кГц) mov C2,w rcall Interface_config ;-- сообщаем компу, что загрузились и ждём команду ldi w,0x01 out UDR,w ;-- разрешаем глобальные прерывания sei ;-- ждём у моря погоды --- Wait_data: rjmp Wait_data ;--------------------------------------------------------- ;--- Обработчик прерывания от таймера --- T0_INT: ;-- генерим фронт (инвертируем clock) in w,PORT_SPI ldi temp1,(1<<SCLK_Line) eor w,temp1 ; инвертируем clock out PORT_SPI,w ;-- выполняем действия в зависимости от конфига sbrc Front_Counter,0 ; если чётный фронт (считаем обратно), то rjmp Proc2 ; выполняем функцию 1, иначе 2 Proc1: mov ZH,Proc1_Address_H mov ZL,Proc1_Address_L icall rjmp Next_T0_INT Proc2: mov ZH,Proc2_Address_H mov ZL,Proc2_Address_L icall ;-- уменьшить и проверить счётчик Next_T0_INT: dec Front_Counter breq End_Transfer reti ;-- Конец передачи End_Transfer: clr w out TCCR0B,w ; выключаем предделитель ;-- выравнивание sbrc C2,6 ; если передавали старшим битом вперёд - пропускаем rcall Offset_Low ;--------------- in w,UDR ; читаем порт (чтоб сбросить флаг приёма, если что) sbi UCSRB,RXCIE ; включаем прерывания от приёмника sbrc C2,7 ; если управление линией CS автоматическое, то rjmp Set_CS_H ; выполняем эту команду ldi w,0x01 ; собщаем компу, что всё сделали out UDR,w reti ; и выходим ;------------------------------------------ ;--- Обработчик прерывания от приёмника --- RX_INT: in w,UDR ; читаем байт из приёмника в w cpi w,0x01 ; принятый байт = 1? breq Config_Recieve ; принимаем конфигурацию с компа cpi w,0x02 ; принятый байт = 2? breq Config_Transmit ; отправляем конфигурацию на комп cpi w,0x03 ; принятый байт = 3? breq Paket_Upload ; загружаем пакет с компа cpi w,0x04 ; принятый байт = 4? breq Paket_Download ; выгружаем пакет на комп cpi w,0x05 ; принятый байт = 5? breq Set_CS_H ; CS=1 cpi w,0x06 ; принятый байт = 6? breq Set_CS_L ; CS=0 cpi w,0x07 ; принятый байт = 7? breq SPI_Start ; производим сеанс обмена ;-- Сообщаем, что приняли неизвестную команду и выходим Error_Command: ldi w,0xFF out UDR,w reti ;--- Говорим компу, что всё сделали и ждём ещё команд Send_Ok: ldi w,0x01 out UDR,w ret ;--- ОБРАБОТЧИКИ КОМАНД -------------------------- ;-------------------------------------------------- ;-- принимаем конфигурацию (два байта от компа) --- Config_Recieve: Wait_First_Byte: sbis UCSRA,RXC ; ждём первый байт от компа rjmp Wait_First_Byte in C1,UDR ; читаем принятый байт в C1 Wait_Second_Byte: sbis UCSRA,RXC ; ждём второй байт от компа rjmp Wait_Second_Byte in C2,UDR ; читаем принятый байт в C2 ;-- конфигурим интерфейс rcall Interface_Config ;---------------- rcall Send_Ok ; собщаем компу, что всё сделали ;---------------- reti ; выходим ;-------------------------------------------------- ;-- отсылаем компу конфиг Config_Transmit: out UDR,C1 Wait_Send_C1: sbis UCSRA,UDRE rjmp Wait_Send_C1 out UDR,C2 Wait_Send_C2: sbis UCSRA,UDRE rjmp Wait_Send_C2 ;---------------- rcall Send_Ok ; собщаем компу, что всё сделали ;---------------- reti ; выходим ;-------------------------------------------------- ;-- Загружаем пакет с компа --- Paket_Upload: clr w mov XL,Low_byte_Address Wait_Paket_Byte: sbis UCSRA,RXC ; ждём первый байт от компа rjmp Wait_Paket_Byte in temp1,UDR ; читаем принятый байт в temp1 st X,temp1 ; сохраняем по адресу, записанному в X dec XL ; переводим указатель на следующий байт subi w,0xF8 ; вычитание F8 равносильно прибавлению восьми cp w,Razmer_bit brge End_of_Paket; если w>= кол-ва бит в пакете - приём пакета окончен rjmp Wait_Paket_Byte End_of_Paket: ;-- если нужно, то выравниваем sbrc C2,6 ; если передаём младшим битом вперёд (6-й бит C2=0), rcall Offset_Hi ; то пропускаем эту команду ;-- если нужно - сразу устанавливаем первый бит на выходе sbrs C1,6 ; если CPHA=1 - пропускаем команду rcall Set_MOSI sbrc C2,7 ; если управление линией CS автоматическое, то rjmp Set_CS_L ; выполняем эту команду ;------------- rcall Send_Ok ;------------- reti ;-------------------------------------------------- ;-- Выгружаем пакет на комп --- Paket_Download: mov w,Byte_Number mov XL,Low_byte_Address Download_next_byte: ld temp1,X out UDR,temp1 dec w breq Download_finished Wait_Send_Byte: sbis UCSRA,UDRE rjmp Wait_Send_Byte rjmp Download_next_byte Download_finished: ;---------------- rcall Send_Ok ; собщаем компу, что всё сделали ;---------------- reti ; выходим ;-------------------------------------------------- ;-- Поднимаем CS Set_CS_H: sbi PORTD, CS_Line rcall Send_Ok ; собщаем компу, что всё сделали reti ;-------------------------------------------------- ;-- Роняем CS и если она в автоматическом режиме - начинаем передачу Set_CS_L: cbi PORTD, CS_Line sbrc C2,7 ; если управление линией CS автоматическое, то rjmp SPI_Start ; выполняем эту команду rcall Send_Ok ; собщаем компу, что всё сделали reti ;-------------------------------------------------- ;-- Начинаем передавать содержимое регистра SPI_Start: ;-- если CS=1, то ошибка sbic PORTD, CS_Line rjmp Error_CS ;----------------------- cbi UCSRB,RXCIE ; выключаем прерывания от приёмника mov Front_Counter, Razmer_bit ; инициализируем счётчик lsl Front_Counter ; умножаем на 2 (количество фронтов) ;-- Сбрасываем таймер, его флаги и включаем прерывание от таймера clr w out TCNT0,w ; сбрасываем таймер ser w out TIFR,w ; сбрасываем флаги таймера ldi w,0b00000010 out TCCR0B,w ; включаем предделитель ldi w,0b00000001 ; разрешаем прерывание по сравнению out TIMSK,w reti Error_CS: ldi w,0xFD out UDR,w reti ;--------------------------------------------------------- ;--- ПРОЦЕДУРЫ SPI --------------------------------------- ;-- Конфигурирование интерфейса Interface_Config: ;-- определяем размер регистра mov w,C1 andi w,0b00111111 ; отрезаем два старших бита breq Error_Size ; если размер=0 - это ошибка mov Razmer_bit,w ; записываем размер регистра в битах ;-- определяем кол-во используемых байт dec w ; вычитаем 1 lsr w ; 3 раза делим на 2 lsr w ; (получается деление на 8) lsr w inc w ; и прибавляем 1 mov Byte_Number,w ; сохраняем кол-во используемых байт ;-- определяем величину сдвига для выравнивания mov w,Razmer_bit subi w,64 ; вычитаем 64 (получается N-64) mov SR_Offset,w ; сохраняем вычисленное значение в регистре ;-- записываем адреса младшего и старшего байтов ldi w,0x17 ; адрес REG_7 mov Low_byte_Address,w ldi w,0x10 ; адрес REG_0 mov Hi_byte_Address,w ;-- настраиваем линию SCLK sbrc C1,7 ; если CPOL=0 - пропускаем следующую команду sbi PORT_SPI, SCLK_Line ; устанавливаем линию clock в единицу sbrs C1,7 ; если CPOL=1 - пропускаем следующую команду cbi PORT_SPI, SCLK_Line ; устанавливаем линию clock в ноль ;-- устанавливаем скорость и настраиваем таймер mov w,C2 andi w,0b00111111 ; отрезаем два старших бита inc w out OCR0A,w ; загружаем значение для сравнения ldi w,0b00000010 ; таймер отключен от выводов OC0A(B), out TCCR0A,w ; сброс при совпадении ;-- определяем смещение адресов процедур, которые будут использоваться ;-- относительно метки Get_Address ;-- CPHA ? -- sbrc C1,6 ; если CPHA=0 - пропускаем команду rjmp CPHA1 CPHA0: ;-- старшим или младшим вперёд sbrc C2,6 ; если С2[6]=0, то младшим вперёд rjmp CPHA0_H CPHA0_L: ; CPHA=0, младшим вперёд ldi temp1,11 ; ссылка на Read_MISO_L0H1 ldi temp2,25 ; ссылка на Shift_i_Set_MOSI_L0 rjmp Next_Int_Config CPHA0_H: ; CPHA=0, старшим вперёд ldi temp1,18 ; ссылка на Read_MISO_H0L1 ldi temp2,28 ; ссылка на Shift_i_Set_MOSI_H0 rjmp Next_Int_Config CPHA1: ;-- старшим или младшим вперёд sbrc C2,6 ; если С2[6]=0, то младшим вперёд rjmp CPHA1_H CPHA1_L: ; CPHA=1, младшим вперёд ldi temp1,31 ; ссылка на Shift_i_Set_MOSI_L1 ldi temp2,18 ; ссылка на Read_MISO_H0L1 rjmp Next_Int_Config CPHA1_H: ; CPHA=1, старшим вперёд ldi temp1,34 ; ссылка на Shift_i_Set_MOSI_H1 ldi temp2,11 ; ссылка на Read_MISO_L0H1 Next_Int_Config: rcall Save_Proc_Address ret Error_Size: ldi w,0xFE ; сообщаем компу об ошибке out UDR,w ret ;----------------------------------------------- ;-- Выравнивание к старшему биту старшего байта Offset_Hi: mov w, SR_Offset tst w Next_Offset_Hi: breq End_Offset_Hi rcall Shift_Left inc w rjmp Next_Offset_Hi ; команда флаги не меняет, tst не нужно End_Offset_Hi: ret ;-- Выравнивание к младшему биту младшего байта Offset_Low: mov w, SR_Offset tst w Next_Offset_Low: breq End_Offset_Low rcall Shift_Right inc w rjmp Next_Offset_Low End_Offset_Low: ret ;----------------------------------------------- ;///////////////////////////////////////////////////////// ;-- Если в этом блоке что-то изменить, то адреса сдвинутся ;///////////////////////////////////////////////////////// ;-- Вычисляем адреса команд, которые будем вызывать Save_Proc_Address: rcall Get_Address Get_Address: pop Proc1_Address_H pop Proc1_Address_L ;адрес предыдущей команды mov Proc2_Address_H,Proc1_Address_H mov Proc2_Address_L,Proc1_Address_L ; копируем его ;------------------------ add Proc1_Address_L,temp1 brcc No_inc_H1 inc Proc1_Address_H No_inc_H1: ;------------------------ add Proc2_Address_L,temp2 brcc No_inc_H2 inc Proc2_Address_H No_inc_H2: ret ;-- Для передачи младшим битом вперёд при CPHA=0 и старшим битом вперёд ;-- при CPHA=1 Read_MISO_L0H1: ; +11 mov XL,Low_byte_Address ld temp1, X cbr temp1, 0b00000001 sbic PIN_SPI, MISO_Line sbr temp1, 0b00000001 st X, temp1 ret ;-- Для передачи старшим битом вперёд при CPHA=0 и младшим битом вперёд ;-- при CPHA=1 Read_MISO_H0L1: ; +18 mov XL,Hi_byte_Address ld temp1, X cbr temp1, 0b10000000 sbic PIN_SPI, MISO_Line sbr temp1, 0b10000000 st X, temp1 ret ;----------------------------------------------- ;-- Для передачи младшим битом вперёд при CPHA=0 Shift_i_Set_MOSI_L0: ; +25 rcall Shift_Right rcall Set_MOSI ret ;-- Для передачи при старшим битом вперёд CPHA=0 Shift_i_Set_MOSI_H0: ; +28 rcall Shift_Left rcall Set_MOSI ret ;-- Для передачи младшим битом вперёд при CPHA=1 Shift_i_Set_MOSI_L1: ; +31 rcall Set_MOSI rcall Shift_Right ret ;-- Для передачи старшим битом вперёд при CPHA=1 Shift_i_Set_MOSI_H1: ; +34 rcall Set_MOSI rcall Shift_Left ret ;////////////////////////////////////////////////////////// ;------------------------------------------------ ;-- Установка MOSI Set_MOSI: in temp1, PORT_SPI cbr temp1, (1 << MOSI_Line) sbrc C2,6 ; если передаём младшим битом вперёд - пропускаем команду rjmp First_HI First_Low: ; при передаче младшим битом вперёд mov XL, Low_byte_Address ld temp2, X sbrc temp2, 0 sbr temp1, (1 << MOSI_Line) rjmp End_Set_MOSI First_Hi: ; при передаче старшим битом вперёд mov XL, Hi_byte_Address ld temp2, X sbrc temp2, 7 sbr temp1, (1 << MOSI_Line) End_Set_MOSI: out PORT_SPI, temp1 ret ;----------------------------- ;-- сдвиг регистра вправо ---- Shift_Right: mov XL, Hi_byte_Address ldi Byte_Counter, 8 clc Next_Shift_Right: ld temp1, X ror temp1 st X, temp1 inc XL dec Byte_Counter brne Next_Shift_Right brcc Shift_Right_Exit mov XL, Hi_byte_Address ld temp1, X sbr temp1, 0b10000000 st X, temp1 Shift_Right_Exit: ret ;-- сдвиг регистра влево ---- Shift_Left: mov XL, Low_byte_Address ldi Byte_Counter, 8 clc Next_Shift_Left: ld temp1, X rol temp1 st X, temp1 dec XL dec Byte_Counter brne Next_Shift_Left brcc Shift_Left_Exit mov XL, Low_byte_Address ld temp1, X sbr temp1, 0b00000001 st X, temp1 Shift_Left_Exit: ret ;--- КОНЕЦ ПРОЦЕДУР SPI -------------------------------
Для правильной работы шлюза в контроллере должны быть «запрограммированы» следующие фьюзы: SPIEN, SUT0
Скачать готовую прошивку и asm-файл
Приведу небольшой пример работы со шлюзом.
Пусть у нас есть драйвер семисегментных индикаторов max7219. Из даташита мы узнаём, что драйвером этим можно рулить по SPI в режиме Mode0, при этом размер пакета должен быть 16 бит и биты при обмене передаются старшим вперёд.
Итак, заходим в терминалку, выбираем порт, скорость обмена (скорость у нас 115200), подключаемся и делаем следующее:
— отправляем шлюзу 05h | // (установить CS=1) |
— получаем от шлюза 01h | // он сообщает, что установил CS=1 |
— отправляем шлюзу 01h | // говорим шлюзу, что будем слать конфигурацию |
— отправляем шлюзу 10h D2h | // Mode0, 16 бит, автоматическое управление SS, передача старшим вперёд, скорость «18» |
— получаем от шлюза 01h | // шлюз сообщает, что загрузил конфигурацию |
— отправляем шлюзу 03h | // говорим шлюзу, что будем слать пакет (поскольку выбрано автоматическое управление SS, то принятый пакет шлюз сразу же и отправит) |
— отправляем шлюзу 01h 0Сh | // пакет для max-а: «0Ch 01h» (выход из режима shutdown) |
— получаем от шлюза 01h | // шлюз сообщает, что всё успешно отправил |
— отправляем шлюзу 03h | // говорим шлюзу, что будем слать пакет |
— отправляем шлюзу 07h 0Bh | // пакет для max-а: «0Bh 07h» (scan limit=7, отображаем 7 символов) |
— получаем от шлюза 01h | // шлюз сообщает, что всё успешно отправил |
— отправляем шлюзу 03h | // говорим шлюзу, что будем слать пакет |
— отправляем шлюзу FFh 09h | // пакет для max-а: «09h FFh» (для всех символов режим декодирования B) |
— получаем от шлюза 01h | // шлюз сообщает, что всё успешно отправил |
— отправляем шлюзу 03h | // говорим шлюзу, что будем слать пакет |
— отправляем шлюзу 02h 0Bh | // пакет для max-а: «0Bh 02h» (отобразить на втором индикаторе символ «E») |
— получаем от шлюза 01h | // шлюз сообщает, что всё успешно отправил |
В результате этих действий на втором семисегментном индикаторе, подключенном к нашему max7219, будет отображаться символ «E».
Источник: http://radiohlam.ru