Микроконтроллеры PIC отличаются достаточно низкой ценой и возможностями, не уступающими большинству современных микроконтроллеров. Они имеют широкий набор интерфейсов для взаимодействия с другими устройствами: USART, I2C и SPI. Ранее на нашем сайте мы уже рассматривали использование в микроконтроллерах PIC интерфейса I2C и USART (последовательный порт связи). В этой же статье мы рассмотрим использование интерфейса SPI в микроконтроллере PIC16F877A.
Также на нашем сайте мы рассматривали использование интерфейса SPI в других микроконтроллерах (платах):
Что такое SPI
SPI расшифровывается как Serial Peripheral Interface и переводится как последовательный интерфейс (периферийных устройств), а по своей сути он является протоколом последовательной связи. Интерфейс SPI был разработан в компании Motorola в 1970 г. Интерфейс SPI имеет полное дуплексное соединение, что означает что данные передаются и принимаются одновременно. То есть ведущий (master) может передавать данные ведомому (slave), а ведомый одновременно с этим может передавать данные ведущему. SPI является протоколом синхронной последовательной связи, то есть ему для работы необходима синхронизация всех устройств.
Принципы работы интерфейса SPI
Для работы по принципу ведущий/ведомый (master/Slave) протокол SPI использует 4 линии (провода). В этом протоколе ведущий всегда один, а ведомых может быть несколько. В качестве ведущего устройства обычно выступает микроконтроллер, а в качестве ведомых устройств могут выступать микроконтроллеры, датчики, АЦП, ЦАП, ЖК дисплеи и т.д.
На следующем рисунке показан принцип работы протокола SPI с одним ведущим (Master) и одним ведомым (Slave).
SPI интерфейс для своей работы использует 4 линии – MISO, MOSI, SS, and CLK. Их назначение следующее:
- MISO (Master in Slave Out) – линия ведомого для передачи данных ведущему;
- MOSI (Master Out Slave In) – линия ведущего для передачи данных ведомому;
- SCK (Serial Clock) – по этой линии передаются сигналы (импульсы) синхронизации, формируемые ведущим;
- SS (Slave Select) – ведущий (Master) может использовать эту линию для включения и выключения определенных устройств (ведомых).
На следующем рисунке показан принцип работы протокола SPI с одним ведущим (Master) и несколькими ведомыми (Slave).
Чтобы начать связь (взаимодействие) между ведущим и ведомым нам необходимо установить на контакте ведомого Slave Select (SS) напряжение низкого уровня чтобы он мог взаимодействовать с ведущим. Когда на этом контакте напряжение высокого уровня (high) ведомый игнорирует ведущего. Это позволяет иметь множество ведомых устройств, работающих по протоколу SPI, использующих одни и те же (общие) линии MISO, MOSI и CLK ведущего устройства. Как вы можете видеть из представленного рисунка для 4-х ведомых устройств линии SCLK, MISO, MOSI общие для соединения с ведущим, а линии (контакты) SS каждого ведомого устройства подключены к отдельным контактам SS (SS1, SS2, SS3, SS4) ведущего устройства. При помощи установки на требуемом контакте SS напряжения низкого уровня (LOW) ведущий (master) может взаимодействовать с нужным ему ведомым (slave).
Отличия между интерфейсами I2C и SPI
В общем и целом эти два интерфейса решают похожие задачи – передача данных в устройствах встраиваемой электроники на небольшие расстояния. Но каждый из этих интерфейсов имеет свои преимущества и недостатки друг перед другом. Достоинством интерфейса I2C по сравнению с SPI является то, что при связи между двумя устройствами он использует всего 2 линии против 4-х у SPI, соответственно, он задействует меньшее число контактов микроконтроллера. Данное преимущество интерфейса I2C еще более возрастает в ситуациях, когда ведомых устройств (slaves) несколько, интерфейс I2C в данном случае будет точно также использовать 2 линии, а вот количество используемых линий ведущим устройством в интерфейсе SPI значительно увеличится (больше чем 4).
Однако недостатком интерфейса I2C по сравнению с SPI является то, что, поскольку и передача, и прием данных осуществляются по одной и той же линии, то пропускная способность интерфейса I2C будет очевидно меньше чем у интерфейса SPI. Также в некоторых источниках выделяют большую надежность передачи данных по интерфейсу SPI по сравнению с интерфейсом I2C.
Работа с интерфейсом SPI в микроконтроллере PIC16F877A с помощью компилятора XC8
В данной статье мы будем рассматривать работу с интерфейсом SPI в микроконтроллере PIC16F877A только с помощью компилятора XC8, для других микроконтроллеров PIC работа с интерфейсом SPI будет аналогична рассматриваемым нами процессам, однако возможны небольшие изменения. Также для более "продвинутых" серий микроконтроллеров PIC, например, для серии PIC18F, компилятор уже содержит инструменты (библиотеки) для работы с интерфейсом SPI, однако для микроконтроллера PIC16F877A такой библиотеки нет, поэтому нам придется написать ее самим. Код данной библиотеки вы можете скачать по ссылке в конце статьи.
В данном проекте мы напишем небольшую программу, которая будет использовать протокол SPI для записи и чтения данных с шины SPI. Тестирование работы проекта мы произведем с помощью симулятора Proteus. В программе для работы с интерфейсом SPI мы будем подключать заголовочный файл библиотеки PIC16f877a_SPI.h. Вы можете использовать данную библиотеку и в других проектах, в которых требуется работа с интерфейсом SPI. Код программы и код данной библиотеки можно скачать по следующей ссылке.
Объяснение кода библиотеки для работы с интерфейсом SPI
Прежде чем начать работу с интерфейсом SPI в микроконтроллере PIC16F877A желательно посмотреть даташит на данный микроконтроллер. Из него можно узнать что для работы с протоколом SPI в микроконтроллере PIC16F877A используются регистры SSPSTAT и SSPCON. Более подробно об этом можно прочитать на страницах 74 и 75 даташита на микроконтроллер.
Для настройки работы интерфейса SPI можно использовать достаточно много параметров, но наиболее часто используемый из них это частота синхронизации (clock frequency). В данном проекте мы ее установим равной Fosc/4. Вы можете изменить ее по своему усмотрению. Далее кратко рассмотрим функции, которые включает наша библиотека для работы с интерфейсом SPI.
SPI_Initialize_Master()
Эта функция используется для инициализации интерфейса SPI в режиме ведущего устройства (Master). Внутри данной функции мы будем задавать режимы работы контактов RC5 и RC3 на вывод данных. Затем мы будем конфигурировать регистры SSPTAT и SSPCON.
1 2 3 4 5 6 7 |
void SPI_Initialize_Master() { TRISC5 = 0; // SSPSTAT = 0b00000000; //pg 74/234 SSPCON = 0b00100000; //pg 75/234 TRISC3 = 0; //Set as output for slave mode } |
SPI_Initialize_Slave()
Эта функция используется для инициализации интерфейса SPI в режиме ведомого устройства (slave). Внутри нее мы будем устанавливать режим работы контакта RC5 на вывод данных, а контакта RC3 – на ввод данных. Затем мы будем конфигурировать регистры SSPSTAT и SSPCON таким же образом, как и для ведущего устройства.
1 2 3 4 5 6 7 |
void SPI_Initialize_Slave() { TRISC5 = 0; // SDO pin should be declared as output SSPSTAT = 0b00000000; //pg 74/234 SSPCON = 0b00100000; //pg 75/234 TRISC3 = 1; //Set as in out for master mode } |
SPI_Write(char incoming)
Эта функция используется для записи данных на шину SPI. В качестве параметра она использует символ, задаваемый пользователем, который она пересылает в регистр передачи (Buffer register). Регистр SSPBUF будет очищен импульсом синхронизации, после чего данные на шину SPI будут передаваться последовательно, бит за битом.
1 2 3 4 |
void SPI_Write(char incoming) { SSPBUF = incoming; //Write the user given data into buffer } |
SPI_Ready2Read()
Эта функция будет проверять приняты ли данные с шины SPI полностью и могут ли они быть считаны. Регистр SSPSTAT содержит бит BF, который устанавливается в 1 если данные приняты полностью. Поэтому в функции нам всего лишь необходимо проверить значение данного бита.
1 2 3 4 5 6 7 |
unsigned SPI_Ready2Read() { if (SSPSTAT & 0b00000001) return 1; else return 0; } |
SPI_Read()
Эта функция используется для считывания данных с шины SPI в микроконтроллер PIC. Данные, присутствующие на шине SPI, будут сохраняться в регистре SSPBUF, нам необходимо подождать пока данные не будут приняты полностью (для этого мы проверяем бит BF регистра SSPSTAT), после чего мы можем считать их в переменную.
1 2 3 4 5 |
char SPI_Read() //Read the received data { while ( !SSPSTATbits.BF ); // Hold till BF bit is set, to make sure the complete data is read return(SSPBUF); // return the read data } |
Объяснение основной программы для микроконтроллера PIC
С кодом библиотеки мы разобрались, теперь давайте напишем небольшую программу чтобы проверить работу интерфейса SPI. В ней мы будем записывать данные на шину SPI и с помощью инструмента SPI debugger симулятора Proteus мы будем проверять их считывание с шины SPI.
Первым делом в программе мы настроим биты конфигурации и подключим заголовочный файл библиотеки для работы с интерфейсом SPI.
1 2 |
#include <xc.h> #include "PIC16F877a_SPI.h" |
Если вы будете открывать программу из zip файла, скачанного по ссылке ниже, то заголовочный файл библиотеки будет уже присутствовать в каталоге проекта. Иначе вам необходимо будет добавить в проект данный заголовочный файл вручную чтобы у вас структура проекта была как на следующем рисунке:
Внутри функции main нам необходимо инициализировать интерфейс SPI в микроконтроллере PIC в режиме ведущего (Master). После этого в бесконечном цикле мы будем записывать на шину SPI три случайных шестнадцатеричных значения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void main() { SPI_Initialize_Master(); while(1) { SPI_Write(0X0A); __delay_ms(100); SPI_Write(0X0F); __delay_ms(100); SPI_Write(0X15); __delay_ms(100); } } |
Моделирование работы проекта
Моделировать работу нашего проекта мы будем в симуляторе Proteus, который для мониторинга данных, передаваемых по шине SPI, содержит такой удобный инструмент как SPI debugger. Схема нашего проекта, нарисованная в симуляторе Proteus, будет выглядеть следующим образом.
оскольку в нашем проекте используется только одно SPI устройство, то мы не используем контакт SS интерфейса SPI и он должен быть замкнут на землю.
Для тестирования работы проекта загрузите hex файл в микроконтроллер PIC16F877A и нажмите на кнопку play чтобы запустить программу на выполнение. После этого на экране появится всплывающее окно, в котором будут отображаться данные, передаваемые по шине SPI, как показано на следующем рисунке.
Если мы посмотрим на них внимательнее, то мы увидим те самые три значения, которые мы передаем в шину SPI в программе.
Данные будут приниматься в том же самом порядке, в котором мы передаем их в программе. На основе данной программы вы также можете попробовать осуществить связь по интерфейсу SPI между двумя микроконтроллерами PIC, один из которых будет ведущим, а другой – ведомым.
Исходный код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/* * File: PIC_SPI.c * Author: Aswinth * * Created on 15 May, 2018, 1:46 PM */ // CONFIG #pragma config FOSC = XT // Oscillator Selection bits (XT oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled) #pragma config LVP = OFF // Low-Voltage In-Circuit Serial Programming Enable bit #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit #pragma config WRT = OFF // Flash Program Memory Write Enable bits #pragma config CP = OFF // Flash Program Memory Code Protection bit #include <xc.h> #include "PIC16F877a_SPI.h" #define _XTAL_FREQ 20000000 void main() { SPI_Initialize_Master(); while(1) { SPI_Write(0X0A); __delay_ms(100); SPI_Write(0X0F); __delay_ms(100); SPI_Write(0X15); __delay_ms(100); } } |
Все необходимые файлы для проекта вы можете скачать по этой ссылке.
1 257 просмотров