SPI для ATtiny2313

Во всех микроконтроллерах семейства Mega имеется аппаратный модуль SPI. Он очень прост в своём использовании. После настройки и инициализации модуля нужно записать в регистр SPDR передаваемый байт данных — это запустит генератор тактового сигнала SPI,  данные начнут передаваться от Master к Slave и обратно. После установки флага окончания передачи в SPDR  будет находиться принятый байт. В общем всё  просто. Да и по моему мнению SPI самый лёгкий в освоении и использовании интерфейс, к тому же достаточно скоростной. Однако в некоторых контроллерах семейства Tiny аппаратного SPI нет — там установлен модуль USI, например в очень распространённой и применяемой ATtiny2313. На различных форумах я встречал неоднократные вопросы по поводу использования USI в качестве SPI, т.е. у народа возникают некоторые трудности. И, хотя, USI, может быть, чуть сложнее SPI, но особых трудностей при разборе его работы возникнуть не должно. Основные части USI — это сдвиговый 8-ми разрядный регистр (для передачи/приёма данных)  и 4-х разрядный счётчик (для определения окончания передачи).  В тонкости построения USI я углубляться не буду — кто захочет разобраться подробнее тот раскурит даташит. Главное отличие работы USI в режиме SPI от аппаратного SPI семейства Mega — то, что синхроимпульсы для передачи/приёма формируются не аппаратно, а,  или программно, или от внешнего источника, или по прерыванию таймера 0. Кроме того, сдвиговый регистр и 4-х разрядный счётчик можно тактировать от разных источников, что позволяет построить на основе USI некоторые дополнительные модули.
В даташите на ATtiny2313 есть программные примеры на ассемблере для работы USI в качестве SPI. Я их переписал на Си, сохранив логику работы один к одному,  и успешно использую в своей работе.
Для демонстрации работы USI в качестве SPI, я возьму микроконтроллерные модули с ATtiny2313 и ATmega8, клавиатуру, в которой использую только одну кнопку и ЖКИ.
Опыт №1. 2313-я MASTER 8-я SLAVE.
Скачиваем архив. В нём две папки. В папке USI_SPI — проект для ATtiny2313, а в папке SPI — для ATmega8A. Внимательно просмотрите исходники каждого проекта — там написано, что нужно раскомментировать и что нужно закомментировать для  настройки MASTER/SLAVE (Подсказка: там два варианта главной функции). Также в каждом проекте подключены заголовочный и Си-шный файлы для работы с ЖКИ HD44780. Они необходимы для отображения принятых символов. Откомпилируйте проекты, чтобы 2313-я выступала в качестве MASTER, а 8-я — SLAVE и прошейте контроллеры. По разделу defane посмотрите назначение выводов, и,  соответственно им соберите макет (Подсказка: для подключения ЖКИ (он подключается к SLAVE), что в 2313-й, что в 8-й используется порт D. PD0 — PD3 МК — DB4 — DB7 ЖКИ, PD4 — RS, PD5 — EN). Кнопка (подключается к MASTER)  тоже вешается одинаково — на PD1.
Собрали макет. Подали питание. На ЖКИ должна высветиться надпись ОК — значит , по крайней мере: 1) ЖКИ подключён правильно; 2) компиляция и прошивка SLAVE прошла успешно. Нажимаем кнопку и наблюдаем на ЖКИ SLAVE приём символов (я использовал десятичные цифры. Вот фотка где 2313 — MASTER.
 Опыт №2. 2313-я SLAVE 8-я MASTER.
Теперь компилируем  проекты, чтобы 2313-я выступала в качестве SLAVE, а 8-я — MASTER и соответственно прошиваем. ЖКИ подключаем на 2313-ю, а кнопку — на 8-ю. Не забудьте ПОМЕНЯТЬ местами соединители с MISO на MOSI для 8-й. Повторяем действия из опыта №1. Убеждаемся в чёткой работе. Фотку демонстрировать не вижу смысла, т.к. она ничем не отличается, кроме подключения проводов.
В данной реализации SPI, что для ATtiny2313, что для ATmega8A, я не использовал прерывания, а также различные варианты тактирования для 2313. Всё это в Ваших руках, вернее в головах. Целью данной статьи было показать простейшие способы реализации интерфейса SPI.   Для tiny, точно так, как и в даташите, есть два варианта функций передачи данных. Я замерял частоту тактовых сигналов «быстрого» варианта с кварцем 20 МГц — реально вышло 10 МГц!
Готовые исходники, я думаю, пригодятся Вам в дальнейшем творчестве. Всем удачи.

Запись опубликована в рубрике Микроконтроллерный конструктор с метками , . Добавьте в закладки постоянную ссылку.

24 комментария: SPI для ATtiny2313

  1. Максим говорит:

    Спасибо, очень полезная инфа.

  2. maks50 говорит:

    Здравствуйте.
    У меня вопрос по SPI. В даташитах для ATiny2313, Atiny25 приводится рисунок модуля, из котого следует, что если выбрать внешний тактовый сигнал и в качестве такового, в свою очередь, выбрать USITC, то он поступает на вход 4-х разрядного счетчика и на вывод USCK, но на сдвигающий регистр USIDR не поступает. Как в таком случае происходит сдвиг информации?

  3. s_black говорит:

    Уважаемый maks50! Свой вопрос Вы поместили в раздел «записи», а нужно было в комменты, что я и сделал за Вас )))
    Теперь отвечаю на вопрос. Тактирование в таком случае осуществляется программно, сбросом/установкой бита USICLK регистра USICR. Этот случай в исходнике указан (там две функции SPI_WR_M). Удачи Вам в освоении МК!

  4. natrv говорит:

    Огромное спасибо за кодик 8)

  5. s_black говорит:

    Всегда рад )))

  6. Михаил говорит:

    «Не забудьте ПОМЕНЯТЬ местами соединители с MISO на MOSI для 8-й.»

    Поясните эту вашу строку, вроде по MOSI всегда данные от мастера уходят, и по MISO приходят соответсвенно не зависимо кто мастером является? Просто у меня как раз и получается, что при мастере mega8 данные уходят почему-то в MISO, а не MOSI.

  7. s_black говорит:

    Насчёт MOSI и MISO Вы верно говорите. Но для 2313 DO как был, так и остался выходом, а DI — входом, независимо от режима MASTER/SLAVE. И, если не поменять местами соединители — обмен не состоится. Вот, что я имел в виду.

  8. Михаил говорит:

    Понятно, спасибо — пины 2313 проигнорировал в спешке.

  9. Илья говорит:

    Здравствуйте.
    У меня вопрос по SPI. Повторил на asm. код из даташита по 2313 . Хрень какаято , флаг USIOIF не меняется ( пробовал в AVR Studio) .добавляю код:

    SPITansfer :
    		 out USIDR,temp
    		 ldi r16,(1<<USIOIF)
    		 out USISR,r16
    SPITransfer_Loop :
                     ldi r16,(1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC)
    		 out USICR,r16
    		 in r16,USIOIF
    		 sbrs r16,USIOIF
    		 rjmp SPITransfer_Loop
    		 in r16,USIDR
    		 st X+,r16
    		 dec count
    		 brne SPITansfer
    		 rcall OUTLED
    		 loop :rjmp loop
  10. s_black говорит:

    Могу предположить, что Вы не читали абзац в даташите, следующий за взятым Вами примером кода. Вот он: … The code is size optimized using only eight instructions (+ ret). The code example assumes that the DO and USCK pins are enabled as output in the DDRB Register. The value stored in register r16 prior to the function is called is transferred to the Slave device, and when the transfer is completed the data received from the Slave is stored back into the r16 Register…
    В нём предполагается, что Вы до вызова этой функции перевели выводы DO и USCK в режим выхода, и, предварительно занесли в r16 значение.
    Угадал?

  11. Владимир говорит:

    Да. Спасибо. А слэйва в третьем режиме работы SPI можете показать? А то думаю-думаю, никак не придумаю. Пока написал программно.

  12. s_black говорит:

    Не совсем понял, что нужно пояснить. Уточните, пожалуйста!

  13. Владимир говорит:

    Сейчас приходится писать SPI на attiny (2313 или 4313), в которых, как я понимаю, нет SPI. Режим работы третий. Т.е. вначале идёт выставление бита на шину, затем его чтение. Но как я посмотрел в даташитах, в USI, в отличие от SPI, старший выставляемый бит уже отображён на линию (MISO). Т.е. в SPI когда мы сдвигаем байт, его старший бит «уходит» на линию, а его место занимает следующий бит, как и полагается. А в USI старший бит уже отображён на линию. И когда мы работаем в режиме «сначала выставление(сдвиг), затем чтение» выставляется не старший а следующий за ним бит. Или может это совсем не так. Но так вот я это понял.

  14. user говорит:

    Большое спасибо!..

  15. Александр говорит:

    Здравствуйте, у меня такой вопрос, мне нужно реализовать связь между AT90USB162 и шестью ATtiny2313. Я правильно понимаю, по SPI(даже программному) передача данных идет быстрее чем на других интерфейсах, с помощью которых можно связать контроллеры(UART, USART, I2C)? Если не прав, то сильно не пинайте, т.к. я только учусь))

  16. s_black говорит:

    Да, только ног такая реализация потребует прилично. Кроме MOSI, MISO, SCK , ещё нужно шесть CS для каждой тиньки. Итого — 9 выводов! А по I2C обойдётесь двумя.

  17. Александр говорит:

    Ну то что много ног это понятно. Только я не могу понять вот что, если подключаю несколько МК, то SS для каждой тиньки я должен самостоятельно заранее предопределить и в программе опять же самостоятельно подтягивать к высокому или низкому уровню. Так? А при этом я могу использовать ту же ногу микроконтроллера, которая по даташиту SS, в качестве одного из шести SS которые мне нужны? Это никак не повлияет на правильность передачи? Просто если подключаешь к Мастеру только один МК, то вывод SS вообще же не трогается(т.е. как я понял он там как то автоматом подтягивается или постоянно подтянут к нулю). Но с другой стороны есть же еще каскадное соединение (я нашел вот здесь http://www.doneathome.ru/archives/750) оно же по идеи возможно в данном случае?

  18. s_black говорит:

    …то SS для каждой тиньки я должен самостоятельно заранее предопределить… — Да
    …А при этом я могу использовать ту же … — Да
    … если подключаешь к Мастеру только один МК, то вывод SS вообще же не трогается… — если в конструкции один ведомый по СПИ — то его CS можно просто посадить на землю.
    … оно же по идеи возможно в данном случае… — Да, но программно более сложно.

  19. Александр говорит:

    а почему вы написали «Да, но программно более сложно.» ? на сколько я понял там основной трюк только в способе подключения, а в программе например если мне надо чтоб байт дошел до шестой тиньки(ведомой), то надо просто передать шесть байт не переставая. Или я что то путаю?

  20. s_black говорит:

    Александр! Ведь пока нужный байт «доползёт» до крайней 6-й тиньки — он же изменит конфигурацию/информацию/режим работы и пр. в пяти перед ней стоящих тиньках! Как Вы планируете » игнорировать» ненужную информацию для слейвов СПИ? Т.е. как будет адресоваться конкретный слейв? Ясно, что числом переданных байт — это первое, что приходит на ум. Но вышеприведённые мною аргументы не позволят это реализовать так просто,и сводят плюсы в виде малого числа линий связи на нет ((( Поэтому придётся вводить какой-то маркер или адресные биты, и, вот эта реализация будет посложней простого использования СПИ.

  21. Александр говорит:

    Понял, спасибо за ответ!

  22. Александр говорит:

    Сегодня запустил, проверил и заметил что для того чтобы данные передавались без искажения частота тактового сигнала SPI модуля должна быть минимум в 8 раз меньше по сравнению с частотой кварца на AT90USB162(тактируется от кварца на 16Мгц), при этом в качестве ведомого использую ATtiny2313V с частотой 8МГц. Если вместо ATtiny2313V использовать ATmega8 с частотой 16МГц, то частота тактового сигнала SPI модуля должна быть в 4 раз меньше по сравнению с частотой кварца на AT90USB162. Как можно поднять частоту модуля SPI до f(кварца)/2 ? Я же правильно понимаю что частота кварца ведомого должна быть не меньше частоты SPI модуля ведущего (а лучше больше)?иначе они друг друга не поймут

  23. s_black говорит:

    Работа контроллера в режиме Slave гарантируется на частотах меньших или равных четверти от Fclk. Для Мастера можно поднять частоту Fclk максимум до половины частоты ядра. На 2313-й у меня получалось 10 МГц.

  24. Александр говорит:

    Почему-то не получается отправить ведомому(ATtiny2313A) подряд два байта. Если отправлять один байт то все нормально. Со стороны мастера вроде все норм. Проверил это путем соединения MOSI и MISO, все что ушло с компа на мастер все вернулось. Заметил что при постоянной передаче одного и того же числа, от ведомого обратно приходит одно и те же число, но не равное отправленному. Вот код со стороны ведомого(вставил только то, что отличается от вашей программы):

    int main (void)
    {
    unsigned char data_r[2];
    unsigned char i=0;
    USI_init_SPI_S ();
    for(;;)
    {
    if(bit_is_clear(PIN_SPI, SS))
    {
    data_r[i] = SPI_WR_S(data_r[i]);
    i++;
    if(i>=1)
    {
    i=0;
    }
    }
    }
    }

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

Ваш e-mail не будет опубликован.