Статьи

Главная Новости

ПЛИС. SPI

Опубликовано: 05.09.2018

видео ПЛИС. SPI

Fun and Easy SPI - How the SPI Protocol Works

В прошлый раз мы тестировали отправку байта по UART, при этом данные отправлялись в терминал на ПК. Результат как то не очень вдохновил, поэтому я продолжил изыскания. В результате чего удалось совокупить AVR и ПЛИС.


SPI ADC controlled by FPGA

Задача передать символ нуля. В ASCII кодировке он имеет значение 0x30, в двоичном коде 0b 0011 0000. Интерфейс spi достаточно прост, для того чтобы передать байт нужно выбрать ведомого — прижать ножку SS к земле и передавать импульсы. Синхронно с импульсами передаются 8 бит. Как видно никаких стоп битов, никаких старт битов. Но также становится понятно, что биты могут передаваться либо по восходящему фронту импульса, либо по нисходящему. Кроме того важно в каком состоянии до передачи находится линия SCLK, либо логический ноль, либо единица.


VGA + SDRAM + FPGA = SPI Accessible Graphics card

Таким образом у нас получаются 4 режима работы SPI. Режим 0, в момент когда данные не передаются на SCLK лог 0. Биты записываются по восходящему фронту.

Режим 1. Когда до начала тактовых импульсов сигнал на ноге SCLK равен логическому нулю. И первый бит считывается по спадающему фронту.

Режим 2. Когда до начала тактовых импульсов сигнал на ноге SCLK равен логической единице. И первый бит считывается по нарастающему фронту.

Режим 3. Когда до начала тактовых импульсов сигнал на ноге SCLK равен логическому нулю. И первый бит считывается по нарастающему фронту.

Кроме того, еще может быть важен порядок байтов MSB или LSB, т.е. либо передаем 0011 0000 справа налево, либо наоборот слева на право.

Все эти настройки есть В CAVR

Теперь нужно потестить. Передачу данных. В роли «господина» AVR микроконтроллер, нажимаем PD0 передается ноль, нажимаем PD1 передается единица.

#include <mega8.h> #include <spi.h> void main ( void ) { // Function: Bit7=In Bit6=In Bit5=Out Bit4=In Bit3=Out Bit2=Out Bit1=In Bit0=In DDRB = ( 0 << DDB7 ) | ( 0 << DDB6 ) | ( 1 << DDB5 ) | ( 0 << DDB4 ) | ( 1 << DDB3 ) | ( 1 << DDB2 ) | ( 0 << DDB1 ) | ( 0 << DDB0 ) ; // State: Bit7=T Bit6=T Bit5=0 Bit4=T Bit3=0 Bit2=0 Bit1=T Bit0=T PORTB = ( 0 << PORTB7 ) | ( 0 << PORTB6 ) | ( 0 << PORTB5 ) | ( 0 << PORTB4 ) | ( 0 << PORTB3 ) | ( 0 << PORTB2 ) | ( 0 << PORTB1 ) | ( 0 << PORTB0 ) ; // Port D initialization // Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In DDRD = ( 0 << DDD7 ) | ( 0 << DDD6 ) | ( 0 << DDD5 ) | ( 0 << DDD4 ) | ( 0 << DDD3 ) | ( 0 << DDD2 ) | ( 0 << DDD1 ) | ( 0 << DDD0 ) ; // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=P Bit0=P PORTD = ( 0 << PORTD7 ) | ( 0 << PORTD6 ) | ( 0 << PORTD5 ) | ( 0 << PORTD4 ) | ( 0 << PORTD3 ) | ( 0 << PORTD2 ) | ( 1 << PORTD1 ) | ( 1 << PORTD0 ) ; // SPI initialization // SPI Type: Master // SPI Clock Rate: 2000,000 kHz // SPI Clock Phase: Cycle Start // SPI Clock Polarity: Low // SPI Data Order: MSB First SPCR = ( 0 << SPIE ) | ( 1 << SPE ) | ( 0 << DORD ) | ( 1 << MSTR ) | ( 0 << CPOL ) | ( 0 << CPHA ) | ( 0 << SPR1 ) | ( 0 << SPR0 ) ; SPSR = ( 0 << SPI2X ) ; while ( 1 ) { if ( PIND.0 == 0 ) { spi ( '0' ) ; } if ( PIND.1 == 0 ) { spi ( '1' ) ; } } }

В качестве «раба» будет плисина

module test_spi (clk ,Mosi ,ss ,led ) ; input clk ; //сюда прихолят тактовые импульсы input ss ; //выбор чипа input Mosi ; //ножка приема output reg led = 0 ; //светодиодик reg [ 7 : 0 ] data = 0 ; reg [ 3 : 0 ] nBit = 0 ; reg [ 8 : 0 ] clock = 0 ; always @ ( posedge clk ) //по приходу тактовых импульсов begin if ( ss == 0 ) //если чип селект равен лог нулю begin if ( nBit != 8 ) begin data [ 7 : 0 ] <= { data [ 6 : 0 ] ,Mosi } ; //считываем бит и сдвигаем данные nBit <= nBit + 1 ; //следующий бит end if ( nBit == 8 ) begin //когда байт пришел полностью смотрим if ( data == 8 'b00110000)begin led = 1' b1 ; end //если пришел '0' включаем светодиод if ( data == 8 'b00110001)begin led = 1' b0 ; end //если пришла '1' выключаем светодиод nBit <= 0 ; end end end endmodule

Особо хочется отметить красивый кусок кода

data [ 7 : 0 ] <= { data [ 6 : 0 ] ,Mosi } ;

Сначала от этой строчки у меня немного задымился мозг, но потом вкурил. Представим на манер Си, как объявляется массив

char data [ 3 ] = { 1 , 2 , 3 } ;

т.е. 3 элементам массива мы присвоили 3 числа

Здесь по сути тоже самое, если расписать выражение, то получится вот так:

data [ 7 : 0 ] <= { data [ 6 ] ,data [ 5 ] ,data [ 4 ] ,data [ 3 ] ,data [ 2 ] ,data [ 1 ] ,Mosi } ;

т.е. по сути элементам data[7:1] будут присвоены значения data[6:0], а значению data[0] будет присвоено значение Mosi, т.е. то что на входной ножке. По сути это сдвиг и присвоение нового значения.

Собственно все работает и занимает достаточно мало ресурсов ПЛИСины, а если прибавить еще то, что работает SPI на 2МГц, т.е. байт уйдет 4мкс. Теоретически можно выжать скорость около 250кБайт/c, и это при кварце на 8МГц.

Новости

rss