Перейти к содержанию
  • записей
    8
  • комментариев
    7
  • просмотров
    1 795

USI as SPI


parovoZZ

1 980 просмотров

USI as SPI
Для работы с трансивером нам необходим интерфейс SPI. Извлекать его будем всё из того же модуля USI. Здесь кратенько. Линии данных у SPI однонаправленные, а это значит, что пин DO всегда подключен к концу сдвигового регистра, а DI - к началу. Таким образом организован режим передачи full duplex. Здесь нам не нужны никакие подтягивающие резисторы, т.к. линия ВСЕГДА либо в нуле, либо в единице. А когда мы не работаем по интерфейсу SPI, то нам и пофигу, что творится на линиях. Здесь только про режим master. Здесь нет пина SS - соответственно нет и проблем, которые он создает в аппаратном SPI (пин SS всегда должен быть сконфигурирован как выход, если мы работаем ТОЛЬКО как мастер. Как только на SS приходит низкий уровень, аппаратный SPI бросает всё и переключает себя в режим SLAVE). Трансивер по интерфейсу SPI способен работать со скоростями вплоть до 10 МГц. Поэтому наша задача организовать тактирование максимально энергоэффективно ни теряя понапрасну ни единого такта.
Начнем с функции инициализации.

//... Инициализация SPI
void SPI_Init(void)
{
    SPI_DDR |= (1<<MOSI) | (1<<SCK) | (0<<MISO);        // Все выводы, кроме MISO, выходы
   // SPI_PORT |= (0<<MOSI) | (0<<SCK) | (0<<MISO);

    USICR = (1<<USIWM0) | (1<<USICLK);
}

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

//... Передать байт данных
void SPI_WriteByte(uint8_t data)
{
    uint8_t clk0 = (1<<USIWM0) | (1<<USITC);
    uint8_t clk1 = (1<<USIWM0) | (1<<USITC) | (1<<USICLK);

    //... Копируем байт в регистр USIDR
    USIDR = data;

    USICR = clk0;            // Режим SPI, тактируем шину сами
    USICR = clk1;
    USICR = clk0;
    USICR = clk1;
    USICR = clk0;
    USICR = clk1;
    USICR = clk0;
    USICR = clk1;
    USICR = clk0;
    USICR = clk1;
    USICR = clk0;
    USICR = clk1;
    USICR = clk0;
    USICR = clk1;
    USICR = clk0;
    USICR = clk1;
}

Здесь в регистр USIDR пишем наш байт и начинаем дергать за веревочку. Как писал выше, при тактировании от USICLK нам необходимо самим писать в этот бит то "0", то "1". Нам надо 8 тактов, поэтому за веревочку дергаем аж 16 раз!
Так сложилось исторически, но функция отправки и получения байта отдельно

//... Передать и получить байт данных
uint8_t SPI_ReadByte(uint8_t data)
{
    //... Отправляем байт
    SPI_WriteByte(data);

    //... Принятый байт возвращаем
    return USIDR;
}

Функция отправки массива байт

//... Отправить несколько байт по SPI. cmd - команда, data - данные для отправки
void SPI_WriteArray(uint8_t cmd, uint8_t num, uint8_t *data)
{
    nRF_SELECT();
   
    //... Отправим команду
    SPI_WriteByte(cmd);

    //... Затем данные
    while(num--)
    {
        SPI_WriteByte(*data++);
    }

    nRF_DESELECT();
}

И напоследок макросы 

#define nRF_SELECT()            ClearBit(nRF_CSN_PORT, nRF_CSN)
#define nRF_DESELECT()            SetBit(nRF_CSN_PORT, nRF_CSN)
#define    nRF_CE_PORT        PORTB                //
#define    nRF_CE_DDR        DDRB                //
#define    nRF_CSN_PORT        PORTB
#define    nRF_CSN_DDR        DDRB
#define Bit(bit)  (1<<(bit))

#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
//пример: ClearBit(PORTB, 1); //сбросить 1-й бит PORTB

#define SetBit(reg, bit)          reg |= (1<<(bit))   
//пример: SetBit(PORTB, 3); //установить 3-й бит PORTB

 

7 Комментариев


Рекомендуемые комментарии

О чем думали в Atmel, когда изобретали такой идиотский модуль без аппаратного тактирования?! Я просто не могу найти никакого объяснения этому изврату...

Ссылка на комментарий

Есть возможность затактироваться от таймера, но у меня нахрапом не получилось. И вроде как понял, где затык, но уже задор пропал)) Да и смысла особого нет, если работаем на максимальных скоростях. 8 тактов потеряем на обслуживание прерывания, а за оставшиеся 8 тактов особо полезного ничего не сделаешь.

Ссылка на комментарий
01.04.2019 в 23:25, parovoZZ сказал:

Есть возможность затактироваться от таймера, но у меня нахрапом не получилось. И вроде как понял, где затык, но уже задор пропал)) Да и смысла особого нет, если работаем на максимальных скоростях. 8 тактов потеряем на обслуживание прерывания, а за оставшиеся 8 тактов особо полезного ничего не сделаешь.

все отлично, в протеусе пошло спасиба, а то всю голову сломал.

Ссылка на комментарий

на таймере не пробовал, сейчас на двухпроводный режим перехожу, адресация приемника(хочу попробовать с tiny2313 на tiny2313 числа передать) через define задается последние три числа как я понял?

если есть куски трехпроводной буду премного благодарен (для общего развития) 

Изменено пользователем Александр5786336
Ссылка на комментарий
10.08.2019 в 13:18, Александр5786336 сказал:

сейчас на двухпроводный режим перехожу,

Это про I2C?

10.08.2019 в 13:18, Александр5786336 сказал:

если есть куски трехпроводной буду премного благодарен

Что имеется ввиду?

10.08.2019 в 13:18, Александр5786336 сказал:

адресация приемника(хочу попробовать с tiny2313 на tiny2313 числа передать) через define задается последние три числа как я понял?

Я I2C slave не реализовывал. В любом случае, там придется всё делать ручками.

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Добавить комментарий...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
×
×
  • Создать...