Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

spi_loДанный урок посвящен последовательному интерфейсу — SPI. Интерфейс позволяет передавать информацию между несколькими устройствами, не обязательно микроконтроллерами. Особенность заключается в том, что имеется одно ведущее устройство и одно или несколько ведомых. Так же интерфейс используется многими программаторами для прошивки микроконтроллеров.

SPI достаточно прост в использовании, рассмотрим пример, в котором используется один ведущий (master — босс :D) и два ведомых микроконтроллера (slave — работяги).

spi1

SCK — Тактовый сигнал. Используется для синхронизации данных.

MOSI — передатчик ведущего, приемник ведомого

MISO — приемник ведущего, передатчик ведомого

SS — выбор ведомого

Для того, чтобы ведомый 0 принимал команды, нужно на его вход SS подать логический 0. Тогда он будет знать, что общение идет именно с ним, ведомый 1 будет все игнорировать. Аналогично, чтобы «активировать» ведомого 1, нужно подать на его вход SS логический 0, а на вход SS ведомого 0 — логическую единицу. В таком случае слушать команды будет только ведомый 1.

В отличие от предыдущих уроков, здесь придется создать 2 прошивки. Задача ведущего: активировать ведомого 0, послать команду мигнуть светодиодом, переключиться на 1, снова послать команду мигнуть светодиодом, повторить. Задача ведомых принять команду и мигать светодиодом. Получится одна прошивка для ведущего, вторая для двух ведомых. Создаем проект, на закладке SPI для master настройки слева, для slave справа. Не забудьте поставить галочку SPI Interrupt у slave.

spi conf

Прошивка для ведущего будет выглядеть так:

#include <mega8.h>
#include <spi.h>
#include <delay.h>
 
void main(void)
{
// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=Out Func4=In Func3=Out Func2=Out Func1=Out Func0=Out
// State7=T State6=T State5=0 State4=T State3=0 State2=0 State1=1 State0=0
PORTB=0x02;
DDRB=0x2F;
 
PORTC=0x03;
DDRC=0x00;
 
// SPI initialization
// SPI Type: Master
// SPI Clock Rate: 125,000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0x52;
SPSR=0x00;
 
while (1)
{
PORTB.2=0; //Переключаемся на ведомого 0
spi('1');  //Отсылаем ему 1, чтобы он включил светодиод 
delay_ms(100); //ждем
spi('0');  //Отсылаем ему 0, чтобы он выключил светодиод
delay_ms(100); //ждем
PORTB.2=1; //Делаем ведомого 0 не активным
 
PORTB.1=0; //Переключаемся на ведомого 1, далее по аналогии с 0
spi('1');
delay_ms(100);
spi('0');
delay_ms(100);
PORTB.1=1;
 
};
}

Для ведомых прошивка будет выглядеть так:

#include <mega8.h>
 
interrupt [SPI_STC] void spi_isr(void)
{
unsigned char data;
data=SPDR;    //читаем приходящие байты
if(data=='1') //если пришла 1, включить светодиод
{
PORTD=0xFF;
}
if(data=='0') //если пришел 0, выключить светодиод
{
PORTD=0x00;
}
 
}
 
void main(void)
{
PORTB=0x00;
DDRB=0x10;
 
PORTD=0x00;
DDRD=0xFF;
 
// SPI initialization
// SPI Type: Slave
// SPI Clock Rate: 125,000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0xC2;
SPSR=0x00;
 
// Clear the SPI interrupt flag
#asm
in   r30,spsr
in   r30,spdr
#endasm
 
// Global enable interrupts
#asm("sei")
 
while (1)
{
 
};
}

Обратите внимание, что прием происходит в прерывании, т.е. в основном цикле мы можем делать, что угодно, а как только информация придет по SPI, автоматически начнет исполняться код в прерывании.

В результате микроконтроллеры будут весело перемигиваться. Прошивки и файл протеуса тут
gg

 

83 комментария: Урок 9. Передача данных через SPI в AVR микроконтроллерах

  • а как сделать чтобы ведомый передавал ведомому, или переключать ведомого на ведущего, мне нужно чтобы любой с любым общался ❓

  • тогда SPI не ваш выбор

  • ясно, но по-любому можно обратно ведущему передавать, как реализуется примерно?

  • По поводу переключения режимов мастер-ведущий под воздействием внешнего сигнала, то в spi это возможно. По крайней мере, я так понял даташит 🙂
    Если МК в режиме мастер, но вывод SS# настроен как вход, то, пока на нем + питания — МК в режиме мастер, появился 0 — переходит в ведомый. Я с этим не сталкивался в живую никогда, но в даташите вроде как-то так написано 🙂

  • Есть мнение, чтобы получить данные от слейва, нужно от мастером передать любую хрень слейву. Тогда в регистре SPDR мастера окажутся данные от слейва, но как бы передачу инициирует все равно мастер. Можно просто переконфигурировать мастер в слейва, а слейва в мастера.

  • Если нужно подключить более двух устройств. Под вывод SS можно использовать любые свободные ноги или обязательно только те которые показаны в примере (порт Б)?

  • любые

  • Спасибо за толковые уроки! если возможно напишите урок по беспроводной передаче данных,например с подключением радиомодулей к МК AVR. В CodeVisionAVR.

  • Подскажите а как с 1й МК передать по очереди значения data1, 2, 3 и записать их по очереди во 2ю МК…в разные переменные.

  • можно посмотреть статью http://avr-start.ru/?p=2230 там про uart, но для spi суть не поменяется. Например, посылаете символ определяющий 1 переменную, за ним само значение переменной и символ окончания строки *100# — отослали значение переменной 1 равное 100. Для второй переменной символ начала можно изменить, например так %200#. Со стороны приемника смотрите,
    if(SPDR==’*’)
    {
    значит последующие данные пишем в переменную 1, до знака #
    }
    if(SPDR==’%’)
    {
    читаем в переменную 2, до знака #
    }

  • Спасибо за статью! Все понятно, все работает.

  • В ведомом контроллере- как передать данные из прерывания в основной цикл программы? Глобальные переменные почему-то не работают.

  • сделать глобальную переменную volatile

  • Подскажите, а по скорости передачи данных какой интерфейс быстрее SPI или UART? я предполагаю что SPI, но из-за малого опыта не могу точно знать. Заранее спасибо.

  • SPI быстрее

  • Спасибо огромное! Это было первое, что я сделал на авр. Подскажите, возможно сделать так, чтобы команды от SPI выполнялись с безусловным приоритетом над основной программой? Если можно, подскажите, плиз)

  • Используйте прерывание по приему, настраивается через CodeWizard в том же разделе галочка SPI Interrupt

  • Так и сделал, но при передаче команд выполняются обе программы разом, насколько понял(.
    Вот код для приёмника)

    #include

    // SPI interrupt service routine
    interrupt [SPI_STC] void spi_isr(void)
    {
    unsigned char data;
    data=SPDR;

    if(data==’1′) //если пришла 1, включить светодиод
    {
    PORTC.1=1;
    }
    if(data==’0′) //если пришел 0, выключить светодиод
    {
    PORTC.1=0;
    }
    if(data==’2′) //если пришла 1, включить светодиод
    {
    PORTC.2=1;
    }
    if(data==’3′) //если пришел 0, выключить светодиод
    {
    PORTC.2=0;
    }
    if(data==’4′) //если пришла 1, включить светодиод
    {
    PORTC.3=1;
    }
    if(data==’5′) //если пришел 0, выключить светодиод
    {
    PORTC.3=0;
    }
    if(data==’6′) //если пришла 1, включить светодиод
    {
    PORTC.4=1;
    }
    if(data==’8′) //если пришел 0, выключить светодиод
    {
    PORTC.4=0;
    }

    }

    // Declare your global variables here

    void main(void)
    {

    PORTB=0x3C;
    DDRB=0x10;

    PORTC=0x00;
    DDRC=0x1E;

    PORTD=0xFF;
    DDRD=0x00;

    ACSR=0x80;
    SFIOR=0x00;

    // SPI initialization
    // SPI Type: Slave
    // SPI Clock Rate: 125,000 kHz
    // SPI Clock Phase: Cycle Half
    // SPI Clock Polarity: Low
    // SPI Data Order: MSB First
    SPCR=0xC1;
    SPSR=0x00;

    // Clear the SPI interrupt flag
    #asm
    in r30,spsr
    in r30,spdr
    #endasm

    // Global enable interrupts
    #asm(«sei»)

    while (1)
    {
    if(PIND.1==0)
    {
    PORTC.1=1;
    PORTC.3=0;
    PORTC.2=0;
    PORTC.4=0;
    }
    if(PIND.2==0)
    {
    PORTC.1=0;
    PORTC.3=0;
    PORTC.2=0;
    PORTC.4=1;
    }

    }
    }

  • не совсем понял что вы хотите сделать

  • Смысл в том, чтобы в отсутствии команд управления выполнялся код внутри бесконечного цикла. Пока он пустой, всё нормально управляется, когда я пытаюсь задать опрос входов, и то, и другое работает плохо

  • С приходом команды по SPI по идее должна выполняться только эта команда… если я правильно понял). Прошу извинить неграмотность чайника)

  • У вас есть 2 события влияющие на состояние выхода, обдумайте то, как должен вести себя микроконтроллер, например пока он в прерывании, основной код не выполняется, но прерывание выполняется достаточно быстро, поэтому можно сделать задержку при выходе из прерывания. Если нужно чтобы прерывание не мешало выполнению основного цикла, то можно запрещать прерывания в моменты опроса кнопок. Вся проблема лишь в одном — составить нормальный пошаговый алгоритм, т.е. придумать то как оно должно себя вести.

  • Всё, разобрался, робот работает)). Управление было через nrf24l01, в этом и была зарыта собака. В протеусе то всё работало… Подкорректировал код и вроде всё путем
    Спасибо ещё раз0. Не подскажете, можно как-нибудь магнитный компас от ардуины приспособить как датчик направления без самой ардуины? Может, есть где-то примеры?

  • может есть 🙂 не знаю

  • Здравствуйте! Подскажите пожалуйста, какая может быть максимально допустимая протяженность шины SPI? У меня задача, разнести четыре контроллера на расстояние 5-6 м один от другого, а управление и синхронизация должны осуществляться от MASTER. Или посоветуйте, как это реализовать.

  • Здравствуйте, нужно передавать через приёмопередатчик с 8-битным SPI интерфейсом данные от датчика DS18B20. Данные с датчика получаются в формате float, а он вроде как 32-битный… Как правильно реализовать разбитие 32 бит по 8 на передающей стороне, и сборку на приёмной ? Или как альтернатива- преобразовать float к byte, и передать один байт ? Точность в один градус устроит, да и сборка-разборка отпадает.

  • посмотрите принцип здесь http://avr-start.ru/?p=2230

  • здравствуйте подскажите или укажите..на грабли..кваром как у вас на рис(режим 0) с настройками настроил 2 проекта для ведущего и ведомого… в проте ничего лишнего только 2 проца….соединены линии моськи и синхро, на ведомом линию выбора жестко заземлил…в программе переменная тупо отправляется в спи в цикле с делаем стандартной функцией спи(дата)….так вот связка из 2 мег8 работает на ура…связка когда мастер мега 128….слейв 8..да пофиг какая, с мастера приходят некорректные данные…мегу128 мастером кода параметрил так как у вас на рисунке…ставлю в проте мастера из меги8 всё корректно идет…
    если не трудно плиз рабочий кусок на спи мастера меги 128 привести…
    заранее спс…

  • наверняка забыли на мк в протеусе установить частоту.

  • спс за ответ…вчера это были скорей эмоции…ночь наверно…более внимательно посмотрел на то что скомпилировал квар и конечно же…как вы и сказали правильно выставил частоту тактирования в проте…в проекте значения из спи мастера слейв автоматом закидывал в виртуал терминал..ия их от туда и наблюдал…так вот + неправильно был настроен терминал(скорость)…из за некорректно установленной частоты тактирования процессора…наступив на одни грабли..сверху еще и прикрыл себя другими граблями…внимательность и еще раз внимательность…и протеус будет меньше подводить
    еще раз спасибо…а уроки у вас зачетные..просто всё изложено.

  • Подскажите, какая максимальная длина кабеля и какой кабель использовать? Uart насколько я помню (во всяком случае при пк-мк) хорош тем, что разница между 0 и 1 24-30 вольт и наводки с помехами не страшны при длине кабеля 10-15 метров точно. Хотя тут наверное все дело в напряжении и если по uart передавать между контроллерами то длинный кабель не прокатит. Есть у Вас какие то наблюдения касательно длины и типа кабеля при соединении разрозненных устройств ( от 1 метра и более)?

  • для передачи на длинные расстояния понадобится конверторы RS232 с обвязкой, схема подключения в даташите. например AD232

  • подскажите, как управлять сдвиговым параллельым регистром

  • Есть SD карта и несколько микроконтроллеров.
    Возможна ли реализовать им всем доступ к одной карте?

  • если наставить внешних мультиплексоров, то наверно возможно.

  • Может и глупый вопрос но все же, возможно ли посредством меги8 и интерфейса SPI прошить флешку из мультимедиаплееров которые прошиваються через программаторы SPI?

  • Если знать протокол, то вполне вероятно

  • Когда еще только столкнулся с контроллерами сделал собственный SPI там почти так же только еще одна ножка переключающая из мастера в слейв и обратно и если больше двух приемников надо транзисторы добавлять для усиления иначе не сработает я писал условия при разных частотах 4.0000 мгц и 4.0096 и 3.6864 мгц все работало по наличию напряжения на выводах как только принял подал знак как только подал знак следующий такт приемника и работали все с разными частотами. Передавались данные с максимально возможной скоростью к проводникам подсоединял сопротивления 10 — 22 кОм на минус. Сколько таких сделал никогда не сбивались со своих тактов.

  • Я его назвал DEMU и DEMI по началу был просто передатчик приемник потом модернизировал протокол 😀

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика