AVR Урок 31. Связь ATtiny2313 и Atmega8 по SPI. Часть 1

 

 

 

 

Урок 31

Часть 1

 

Связь ATtiny2313 и Atmega8 по SPI

 

 

Продолжаем разговор о шине SPI и, как я уже обещал, начнём дружить между собой два контроллера — ATtiny2313 и Atmega8.

Во-первых, я ещё вот что хочу сказать.

Я изучил даташит ATtiniy2313 и увидел то, что у данного МК как таковой шины SPI нет. Есть подобие данной шины — её заменитель — USI (Universal Serial Interface).

Ну в принципе-то по большому счёту это одно и тоже, но различия всё же есть.

Ну давайте для полного прикола объявим нашу ATTiny ведущим устройством и будем каким-то образом всё это дело сочинять.

Посмотрим схему модуля USI

 

Image00

 

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

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

Также можно здесь настроить режим SPI с помощью определённого бита

 

Image01

 

Вообщем-то здесь мы видим, что процесс не сильно отличается от того же процесса на Atmega, единстенное нет ещё одного бита. Но это всё равно не отнимает у нас права пользоваться данной шиной для приёма и передачи, просто будут только 2 режима вместо 4.

А все регистры мы будем смотреть уже по мере написания кода.

Теперь схема в протеусе (нажмите на картинку для увеличения изображения)

 

Image02_0500

 

Здесь стандартная схема на ATtiny и стандартная схема на ATmega с подключенными дисплеями (смотреть урок 12 и урок 30). Единственное различие, что все информационные входы, отвечающие за интерфейс SPI между собою соединены следующим образом

 

ATTiny2313       ATmega8

MOSI                 MOSI

MISO                 MISO

USCK                SCK

PB4                   SS

 

Ну и ещё подключен виртуальный осциллограф, чтобы смотреть, как передаются биты.

Сначала создадим проект для ATtiny. Имя мы ему присвоим ATTINY_SPI, а код весь возьмём с прошлого занятия.

Затем также создадим проект для Atmega8 и назовём его ATMEGA_SPI. Код для данного проекта использован будет из проекта урока 12. А библиотека для дисплея была взята с урока по часам, так как она там уже более дополненная. А код функции setpos в lcd.c был взят из проекта по подключению дисплея через переходник, так как дисплей мы подключили 4-строчный, чтобы у нас работали все 4 строки. Ну и допишем код для теста нижних строк в main()

 

setpos(2,1);

str_lcd("String 2");

setpos(4,2);

str_lcd("String 3");

setpos(6,3);

str_lcd("String 4");

 

Соберём коды обоих проектов и посмотрим в протеусе, у нас должны работать оба дисплея

 

Image03

 

Пока начнём писать код в проекте ATTINY_SPI.

Первым делом добавим переменную в функции main() для того, чтобы где-то хранить наш байт

 

int main(void)

{

  unsigned char n=0;//переменная для случайного числа

 

Начнём писать теперь функцию для инициализации шины SPI. Мы её не будем называть USI, а будем использовать стандартное привычное название

 

//—————————————-

void SPI_init(void)

{

  DDRB|=((1<<PORTB4)|(1<<PORTB6)|(1<<PORTB7));//Ножки USI на выход

  DDRB&=~(1<<PORTB5);//Ножка DI на выход

  PORTB&=~((1<<PORTB4)|(1<<PORTB6)|(1<<PORTB7));//Ножки USI в низкий уровень

}

//—————————————-

 

Из кода функции и комментариев мы видим, что для инициализации шины мы не используем вообще никакие регистры, кроме регистров портов общего назначения, настроив в них определённые направления и значения.

Вызовем данную функцию в main()

 

LCD_ini(); //Инициализируем дисплей

SPI_init(); //Инициализируем шину

 

Также для красоты между выводом строк, а также в бесконечный цикл вставим небольшие задержки

 

str_lcd("Hello World!");

_delay_ms(1000);

setpos(2,1);

str_lcd("String 2");

_delay_ms(1000);

while(1)

{

  _delay_ms(1000);

 

Передавать мы будем случайное число, поэтому сгенерируем его в бесконечном цикле, используя функцию rand(), предварительно очистив дисплей и установив указатель на начало координат

 

while(1)

{

  clearlcd();

  setpos(0,0);

  n= (unsigned char) rand() % 256;

  _delay_ms(1000);

 

То есть у нас будет генерироваться случайное число от 0 до 255, что с успехом влезет в передаваемый байт.

Создадим функцию для передачи байта в SPI над функцией main(), чтобы не писать прототип

 

//—————————————-

void SPI_SendByte(char byte)

{

}

//—————————————-

 

Теперь идём в даташит и смотрим, куда же мы должны положить байт для передачи по SPI или USI. Оказывается для этого есть вот такой регистр

 

Image04

 

Ну и давайте туда и положим байт

 

void SPI_SendByte(char byte)

{

  USIDR = byte; //данные в регистр

 

Теперь нам нужно как то заставить контроллер начать передавать данные

 

Для этого есть вот такой статусный регистр

 

Image05

 

А в данном регистре есть байт USIOIF, который является флагом начала передачи, не взирая на то, что регистр статусный

Поэтому напишем следующий код в нашу функцию

 

USIDR = byte; //данные в регистр

USISR |= (1<<USIOIF);//установим флаг начала передачи

 

Только данное действие не начнёт само собой передавать данные, так как нет тактирования. Здесь будет условный цикл

 

while(!(USISR & (1<<USIOIF)))

{

}

 

В данном цикле мы будем отслеживать сброс в ноль того же бита в том же регистре.

 

 

Теперь самое интересное — тело данного цикла. Тут не всё так просто.

Нужно будет немного ознакомиться со следующим регистром

 

Image06

 

Это уже полноправный регистр управления. В нём существуют следующие биты

 

USISIE (Start Condition Interrupt Enable) — бит разрешения прерываний.

USIOIE (Counter Overflow Interrupt Enable) — флаг для прерываний по переполнению счётчика.

USIWM1..0: (Wire Mode) — биты режима. За счёт того, что этих бита два, то мы можем включить один из четырёх режимов:

 

Image07

 

То есть так как шина USI она универсальная, может выступать не только как SPI, а и как I2C, но нас будет интересовать именно двухпроводной SPI, поэтому мы выберем режим второй, то есть включать будем бит USIWM0.

USICS1..0 (Clock Source Select) — биты установки варианта тактирования шины. Работают совместно со следующим битом.

USICLK (Clock Strobe) — бит стробирования регистра сдвига.

Вот таблица вариантов трёх вышеуказанных битов

 

Image08

 

Нас будет интересовать вариант шестой — программное тактирование и программное управление регистром сдвига.

USITC (Toggle Clock Port Pin) — бит переключения порта. В случае если контроллер является ведущим устройством, то включается в 1.

Таким образом, в нашей функции мы установим следующие биты управляющего регистра

 

while(!(USISR & (1<<USIOIF)))

{

  USICR |= ((1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC));//постепенно передаем байт

 

Так как у нас всё происходит в цикле, то для обеспечения не слишком большой частоты нам нужно будет установить задержку

 

  USICR |= ((1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC));//постепенно передаем байт

  _delay_us(10);

}

 

Может быть ATmega примет и с большей скоростью и задержка нам будет не нужна, но, во-первых, мы тогда очень сильно загрузим контроллер, а также нам тяжело будет с такой скоростью отследить процесс в протеусе на нашем виртуально осциллографе.

Вернёмся в бесконечный цикл и вызовем нашу новую функцию для того, чтобы передать байт ведомому устройству

 

n= (unsigned char) rand() % 256;

SPI_SendByte(n);

 

 Затем нам нужно будет отобразить наш отправляемый байт на дисплее, для этого преобразуем его сначала в строку. Для этого кроме функции sprinf существует функция itoa.

 

SPI_SendByte(n);

itoa(n,str,10);

 

Первым входным аргументом в данной функции является преобразовываемая целочисленная величина, вторым — указатель на строку, а третьим — система исчисления. Так как мы будем выводить показания в десятичном виде, то у нас будет 10.

Затем мы этот байт непосредственно отобразим на дисплее

 

itoa(n,str,10);

str_lcd(str);

 

Соберём код и запустим его в протеусе. Посмотрим сначала отображение на дисплее

 

Image09

 

Байты нормально отображаются.

Теперь смотрим на осциллограмму и видим, что там также всё нормально

 

Image12

 

Посмотрим как именно передаётся байт. Зелёная осциллограмма показывает тактовые импульсы, а розовая — непосредственно шину передачи байта. Я дождался, когда будет байт попроще. У нас значение 5, если перевести в двоичный формат, то это 00000101. Так оно и есть на осциллограмме. Когда идут первые пять тактовых импульса, то шина передачи у нас находится в нуле, как только пятый импульс у нас заканчивается, то по его спадающему фронту у нас устанавливается шина передачи в единицу, чтобы во время возрастающего фронта шестого импулься она в этой единице находилась, так оно и есть. Затем по спаду шестого импульса шина передачи сбрасывается в ноль, а по спаду седьмого импульса, она поднимается.

Вообщем, я думаю теперь нам стал более понятен протокол передачи данных по шине SPI.

Теперь посмотрим все соединения на макетной плате, касающиеся контроллера ATtiny2313

 

Image11

 

Всё у нас практически как в прошлом занятии, только мы ещё можем наблюдать, что у нас от платы куда-то отходят четыре провода. Это не что иное, как провода шины SPI для соединеия с другим контроллером, и также общий провод. Общие провода должны быть соединены. Хотя, возможно, будет работать и без этого, так как у нас шина USB подключена к программаторам от одного ПК. А вообще будет не так-то это всё просто, так как avrdude не будет знать, какой именно контроллер ей прошивать.

Попробуем прошить наш контроллер. Но прежде чем это делать, мы выберем в avrdude проект, считаем конфигурационные биты. Но главное не в этом. Ещё немаловажная информация! Перейдём на вкладку с фьюзами, считаем их и снимем вот этот вот бит, иначе у нас будет слишком маленькая скрость

 

Image13

 

Всё-таки он для 8 МГц не нужен, мы зря его установили на прошлом занятии.

Прошьём контроллер, и посмотрим

 

Image14

 

У нас всё нормально работает, только работу шины SPI мы сейчас не оценим, так как нам это отследить негде. Мы ещё не подключали другой контроллер, а настоящего осциллографа у меня, к сожалению нет, также нет даже логического анализатора. Возможно, в недалёком будущем всё это будет. Но пока вот так. Мы только видим, что переданные байты отображаются у нас на дисплее. А другим контроллером, а также дружбой между первым и вторым контроллером мы займёмся уже в следующей части нашего такого вот интересного занятия.

 

Предыдущий урок Программирование МК AVR Следующая часть

 

Программатор и дисплеи можно приобрести здесь:

Программатор (продавец надёжный) USBASP USBISP 2.0

Дисплей LCD 16×2

Дисплей LCD 20×4

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR Связь ATtiny2313 и Atmega8 по SPI

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

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

*