Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Готовность SPI
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
777777
Как определить, что в SPDR можно писать очередной байт? SPIF устанавливается после передачи, но после обработки сбрасывается, и при включении там 0. А есть ли какой бит, по которому можно определить, что передача в данный момент не идет? Т.е. который устанавливается сразу после записи в SPDR?
Dog Pawlowa
Цитата(777777 @ May 20 2008, 11:00) *
Как определить, что в SPDR можно писать очередной байт? SPIF устанавливается после передачи, но после обработки сбрасывается, и при включении там 0. А есть ли какой бит, по которому можно определить, что передача в данный момент не идет? Т.е. который устанавливается сразу после записи в SPDR?

Код
uchar SpiReadWrite(uchar data)
{    // set data to send into SPI data register
    SPDR = data;
    // Wait for transmission complete
    while(!(SPSR & (1<<SPIF)));
    // return data read from SPI
    return SPDR;
}
singlskv
Цитата(777777 @ May 20 2008, 12:00) *
Как определить, что в SPDR можно писать очередной байт? SPIF устанавливается после передачи, но после обработки сбрасывается, и при включении там 0. А есть ли какой бит, по которому можно определить, что передача в данный момент не идет? Т.е. который устанавливается сразу после записи в SPDR?
ИМХО, ни как, но если очень надо, заведите свой флаг который будете
выставлять перед записью в SPDR и сбрасывать после чтения SPIF из SPSR.
Maik-vs
Цитата(777777 @ May 20 2008, 12:00) *
Как определить, что в SPDR можно писать очередной байт? SPIF устанавливается после передачи, но после обработки сбрасывается, и при включении там 0. А есть ли какой бит, по которому можно определить, что передача в данный момент не идет? Т.е. который устанавливается сразу после записи в SPDR?

Чего-то напутано. "Сразу после записи в SPDR" передача как раз идёт. Если записать в SPDR пока передача идёт, поднимется флаг WCOL. Писать в SPDR можно через 34 цикла процессора после предыдущей записи (SPCR=0x50). Раньше - синхроимпульсы будут, а данные - нет. Прямо сейчас этим занимаюсь, Мега16. AVR Studio всё правильно симулирует.
defunct
SPI генерит прерывание, сигнализирующее о том, что передача завершена.
777777
Цитата(Maik-vs @ May 20 2008, 13:38) *
Чего-то напутано. "Сразу после записи в SPDR" передача как раз идёт.


Вот я и хочу узнать, когда она закончится. Не используя прерываний, по готовности. Похоже, только читая SPIF и ожидая когда он исчезнет.

А хотелось бы наоборот: перед записью узнать что предыдущая передача уже завершилась, записать SPDR и продолжить заниматься другими делами.
Qwertty
А ответы нужны?
Maik-vs
Цитата(777777 @ May 20 2008, 14:45) *
Вот я и хочу узнать, когда она закончится. Не используя прерываний, по готовности. Похоже, только читая SPIF и ожидая когда он исчезнет.

А хотелось бы наоборот: перед записью узнать что предыдущая передача уже завершилась, записать SPDR и продолжить заниматься другими делами.

"перед записью узнать что предыдущая передача уже завершилась" Как? "читая SPIF". Прочитали - узнали - не завершилась. Что делать? "читая SPIF и ожидая когда он исчезнет". Ну или прерывание скажет, когда.
=GM=
Цитата(777777 @ May 20 2008, 09:45) *
Похоже, только читая SPIF и ожидая когда он исчезнет

А по-моему, наоборот, флаг SPIF появится, когда закончится передача текущего байта. Для самого первого раза (после подачи питания) можно передать фиктивную посылку, чтобы взвести флаг SPIF.
Цитата(777777 @ May 20 2008, 09:45) *
А хотелось бы наоборот: перед записью узнать что предыдущая передача уже завершилась, записать SPDR и продолжить заниматься другими делами

Делаете, как описано выше, потом проверяете флаг SPIF, если он установлен, то записываете в SPDR новый байт на передачу, при этом флаг SPIF сбросится.

Цитата(Maik-vs @ May 20 2008, 08:38) *
Писать в SPDR можно через 34 цикла процессора после предыдущей записи (SPCR=0x50). Раньше - синхроимпульсы будут, а данные - нет

Откуда вы взяли про 34 цикла, ссылочкой не поделитесь? И что такое SPCR=0x50, в нём вроде используются только 0, 6 и 7 биты?
singlskv
Цитата(=GM= @ May 20 2008, 17:41) *
А по-моему, наоборот, флаг SPIF появится, когда закончится передача текущего байта. Для самого первого раза (после подачи питания) можно передать фиктивную посылку, чтобы взвести флаг SPIF.
конечно после окончания передачи, и поэтому буз внешнего флага здесь не обойтись.
Ну типа так он устроен...
Цитата
Откуда вы взяли про 34 цикла
Ну это почти правда, только там не 34 smile.gif
ну и для других делителей будет другое количество тактов,
сам пробовал в железе только для X2 режима, но с Вами поделюсь инфой только после
того как Вы раскажите/покажите вариант про который мы долго и упорно
спорили(PWM на 24 канала) smile.gif
=GM=
Цитата(singlskv @ May 20 2008, 15:26) *
конечно после окончания передачи, и поэтому без внешнего флага здесь не обойтись. Ну типа так он устроен...
Ну это почти правда, только там не 34

Я и так знаю, что там не 34. Как устроен спи внутри, к сожалению, не знаю. Что такое внешний флаг, не понимаю, о чём вы? То, что я сказал насчёт первого фиктивного байта правильно, но можно обойтись и без его передачи.

Если уж взялись отвечать за другого, так и отвечайте нормально, а не наводите тень на плетень (:-). Повторю ещё раз: "Откуда вы взяли про 34 цикла, ссылочкой не поделитесь? И что такое SPCR=0x50, в нём вроде используются только 0, 6 и 7 биты?"

Цитата(singlskv @ May 20 2008, 15:26) *
сам пробовал в железе только для X2 режима, но с Вами поделюсь инфой только после того как Вы раскажите/покажите вариант про который мы долго и упорно спорили (PWM на 24 канала)

Sulking, are we (:-)? Напомните, подзабыл я, и вроде бы с вами не спорил, тем более долго и упорно, поскольку вы были не в теме.
singlskv
Цитата(=GM= @ May 20 2008, 20:55) *
Я и так знаю, что там не 34. Как устроен спи внутри, к сожалению, не знаю.
Что такое внешний флаг, не понимаю, о чём вы?
просто переменная в памяти которая будет говорить всем что SPI в данный момент занят
Цитата
То, что я сказал насчёт первого фиктивного байта правильно, но можно обойтись и без его передачи.
А вот здесь я уже недопонял...
Цитата
Если уж взялись отвечать за другого, так и отвечайте нормально, а не наводите тень на плетень (:-). Повторю ещё раз: "Откуда вы взяли про 34 цикла, ссылочкой не поделитесь? И что такое SPCR=0x50, в нём вроде используются только 0, 6 и 7 биты?"
Sulking, are we (:-)? Напомните, подзабыл я, и вроде бы с вами не спорил, тем более долго и упорно, поскольку вы были не в теме.
Да ни вапрос, в отличии от Вас готов просто показать код... smile.gif
Код
  SPDR = OutHi;                    // старшая часть выходов
758:    6f b8           out    0x0f, r6; 15
    ...
  __asm__ __volatile__ ("nop"); // бездельничаем пока передается байт
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
  __asm__ __volatile__ ("nop");
776:    00 00           nop
  Ch2Hi = SPSR;                    // читаем для сброса флага
778:    2e b0           in    r2, 0x0e; 14
  Ch2Hi = SPDR;                    // читаем старшие 8 бит канала2
77a:    2f b0           in    r2, 0x0f; 15

но это для X2 режима...
Qwertty
Цитата(singlskv @ May 20 2008, 21:29) *
Код
    ...
  __asm__ __volatile__ ("nop"); // бездельничаем пока передается байт

но это для X2 режима...

Так весь смысл в том, чтоб вместо бездельничья занять контроллер чем то более полезным. Например при подключении индикатора по SPI, где ответа нет. Я делаю с передачей холостого байта при неактивном /cs. Положил в SPDR и пошел готовить следующие данные. Как обойтись без холостой записи надеюсь подскажет GM. А вообще в этом плане хорошо использовать USART в тех контроллерах, в которых есть этот режим. Там получается буферизация записи и можно по паре байт отправлять за один раз. Ну и с флагами там все не так запущено...
singlskv
Цитата(Qwertty @ May 20 2008, 23:36) *
Так весь смысл в том, чтоб вместо бездельничья занять контроллер чем то более полезным.
Ну... это был просто пример для разяснения того как работает SPI,
хотя пример был из рабочей проги .... smile.gif Ну типа иногда(если нет других дел), выгоднее просто
ничего не делать...
Цитата
Например при подключении индикатора по SPI, где ответа нет. Я делаю с передачей холостого байта при неактивном /cs.
А вот это мне все-таки кто-нить объясните, что за холостой байт который Многим нужен ?
Qwertty
Цитата(singlskv @ May 21 2008, 00:04) *
А вот это мне все-таки кто-нить объясните, что за холостой байт который Многим нужен ?

Про это выше написано - холостая посылка, только для взвода SPIF. Просто записать в SPDR что попало, при неактивных cs. Я пишу сразу после настройки SPI. Далее примерно так:
Код
void TransmitSPI(uint8_t _data_)
{
while(!(SPSR & (1<<SPIF)));
SPDR = _data_;
// Завершения не ждем.
}
singlskv
Цитата(Qwertty @ May 21 2008, 01:50) *
Про это выше написано - холостая посылка, только для взвода SPIF. Просто записать в SPDR что попало, при неактивных cs. Я пишу сразу после настройки SPI. Далее примерно так:
Спасибо, теперь понял о чем речь, просто для меня такой
вариант не очень применим, поэтому и не мог никак въехать про какую такую
холостую посылку речь.
=GM=
Цитата(Qwertty @ May 20 2008, 18:36) *
Я делаю с передачей холостого байта при неактивном /cs. Положил в SPDR и пошел готовить следующие данные. Как обойтись без холостой записи, надеюсь, подскажет GM

Рецепта на все случаи нет, чуть позже могу показать один вариант, щас немного занят буду. А для разгона покажите код, где вы выводите данные по эспиай. Вы по прерываниям выводите или по готовности?
Maik-vs
Цитата(=GM= @ May 20 2008, 17:41) *
Откуда вы взяли про 34 цикла, ссылочкой не поделитесь? И что такое SPCR=0x50, в нём вроде используются только 0, 6 и 7 биты?


Ссылочкой не поделюсь, т.к. 34 - число, добытое честным экспериментом. Для SPCR=0x50: SPE и MSTR, остальные биты =0, т.е. коэффициент деления 1. Для других коэффициентов, наверное, не 34, поэтому и привёл значение SPCR. AVR Studio это дело симулирует правильно: если между "out SPDR" 34 цикла, передаются байты с промежуткосм в 1 бит. Если меньше - стробы есть, а на линии данных стоит высокий уровеь. Это видел в осциллографе. Частота кварца 20 МГц, МК mega16.
Да, разумеется, SPIF взводится по концу передачи байта. Не понимаю, в чём проблема у топикстартера - можно работать в прерывании, можно - опрашивая флаг, как всегда...
=GM=
Цитата(Qwertty @ May 20 2008, 18:36) *
Я делаю с передачей холостого байта при неактивном /cs. Положил в SPDR и пошел готовить следующие данные. Как обойтись без холостой записи надеюсь подскажет GM

Подсказываю, вот так, например, можно делать обмен по спи по прерываниям
Код
spistc: in     temp3,spdr       ;
        st     x+,temp3         ;
spist1: in     tmpsta,SREG      ;
        cpi    xl,low(eobuf)+1  ;end of buffer?
        breq   spist2           ;yes, it is
        ld     temp3,x          ;send current
        out    spdr,temp3       ;byte
spist2: out    SREG,tmpsta      ;
        reti

Принятые байты замещают в буфере переданные. Инициируется обмен вызовом rcall spist1
ILYAUL
Не очень понял зачем SREG сохранять
=GM=
Команда cpi портит флаги в прерывании.
singlskv
Цитата(=GM= @ May 22 2008, 21:08) *
Подсказываю, вот так, например, можно делать обмен по спи по прерываниям
Код
spistc: in     temp3,spdr      ;
        st     x+,temp3        ;
spist1: in     tmpsta,SREG     ;
        cpi    xl,low(eobuf)+1 ;end of buffer?
        breq   spist2          ;yes, it is
        ld     temp3,x         ;send current
        out    spdr,temp3      ;byte
spist2: out    SREG,tmpsta     ;
        reti

Принятые байты замещают в буфере переданные. Инициируется обмен вызовом rcall spist1
Повторное использование кода(начало посылки через rcall spist1) выглядит красиво.
Но к сожалению на С неприменимо, нужно писать отдельно старт транзакции.
Ну и для максимума скорости, ИМХО, такое прерывание не очень, при выборе SPI2X(Focs/2)
самым большим тормозом становится время входа/выхода в прерывание
=GM=
Цитата(singlskv @ May 22 2008, 20:53) *
Повторное использование кода (начало посылки через rcall spist1) выглядит красиво

Спасибо, не бог весть что, конечно, но это я сам придумал. Я бы сказал, что это не повторное использование, а именно запуск передачи пакета байт из буфера.
Цитата(singlskv @ May 22 2008, 20:53) *
Но к сожалению на С неприменимо, нужно писать отдельно старт транзакции

На си можно попробовать задействовать оператор свич на две позиции.
Цитата(singlskv @ May 22 2008, 20:53) *
Ну и для максимума скорости, такое прерывание не очень, при выборе SPI2X(Focs/2) самым большим тормозом становится время входа/выхода в прерывание

Да вроде нет, тело прерывания выполняется максимум за 14МЦ + 6-7 на вход, всего 20-21, а на передачу одного байта по спи на максимальной скорости Fclk/2 требуется минимум 17МЦ.

И небольшое увеличение времени передачи пакета окупается большим ПЛЮСОМ: положили данные в буфер, толкнули обмен, занялись другими делами, прошло некое время, проверили указатель буфера, забрали принятые данные и всё. А если не требуется приёма данных, то на прерывание потребуется 17-18МЦ. Так что прерывания будут идти практически непрерывно и проверять ничего не надо (:-).

Кстати, в вашем фрагменте снимать флаг прерывания в конце транзакции совсем не обязательно.
singlskv
Цитата(=GM= @ May 23 2008, 03:09) *
Спасибо, не бог весть что, конечно, но это я сам придумал. Я бы сказал, что это не повторное использование, а именно запуск передачи пакета байт из буфера.
Ну здесь как раз понравилось именно повторное использование, такой
запуск вполне стандартен и понятен, но вот то что это удалось красиво
вписать прямо в код прерывания....
Цитата
На си можно попробовать задействовать оператор свич на две позиции.
к сожалению оверхед который при этом будет на С убивает саму идею.
Цитата
Да вроде нет, тело прерывания выполняется максимум за 14МЦ + 6-7 на вход, всего 20-21, а на передачу одного байта по спи на максимальной скорости Fclk/2 требуется минимум 17МЦ.
Я не очень точно выразился говоря про вход/выход из прерывания,
то есть вход/выход это первопричина а в сумме на производительность будет больше
всего влиять разница в тактах между:
in temp3,spdr ;
......
out spdr,temp3 ;byte
для Вашего варианта, при непрерывном трансфере большого куска, на передачу
одного байта будет >20-21 тактов
Цитата
И небольшое увеличение времени передачи пакета окупается большим ПЛЮСОМ: положили данные в буфер, толкнули обмен, занялись другими делами, прошло некое время, проверили указатель буфера, забрали принятые данные и всё. А если не требуется приёма данных, то на прерывание потребуется 17-18МЦ. Так что прерывания будут идти практически непрерывно и проверять ничего не надо (:-).

Для "общих" случаев обмена по SPI Ваш вариант видимо практически идеален...
Для частных, большой вопрос, тот кусочек кода который я привел был частным случаем,
я его привел только для того чтобы показать сколько ТОЧНО длится передача по SPI.
Цитата
Кстати, в вашем фрагменте снимать флаг прерывания в конце транзакции совсем не обязательно.

А вот этот вопрос для меня остается загадкой, дело в том что железки на которой работает этот
код я никогда не видел 07.gif
более того, в моем коде такие куски повторяются несколько раз подряд,
те байтики шлются непрерывно(4 штуки)
ну и первоначально предполагалось войти в прерывание и принять 4 байта, а при
этом сброс флага обязателен.
Ну и в конечном итоге чтение SPSR заменяет всего-лишь еще один "nop" smile.gif
Потом концепция чуть изменилась, НО, я на самом деле не знаю нужно ли читать SPSR
для запуска новой передачи, даташит на этот счет молчит а натурных испытаний не
попалось....

Да, еще добавлю на всякий случай, вход в прерывание для SPI есть суть
чтение SPSR с сбросом SPIF.
defunct
Цитата(singlskv @ May 23 2008, 21:46) *
Ну здесь как раз понравилось именно повторное использование, такой
запуск вполне стандартен и понятен, но вот то что это удалось красиво
вписать прямо в код прерывания....

Что делает такой вызов небезопасным.
Это хак и ничего красивого в этом нет.

разве такой старт был бы чем-то хуже?
Код
.macro StartSpiTransfer
       ld    temp3,x;send current
       out    spdr,temp3;byte
.end macro
singlskv
Цитата(defunct @ May 23 2008, 23:15) *
Что делает такой вызов небезопасным.
Это хак и ничего красивого в этом нет.

разве такой старт был бы чем-то хуже?
Код
.macro StartSpiTransfer
       ld    temp3,x;send current
       out    spdr,temp3;byte
.end macro
Несомненно это хак, но разьве такие варианты не украшают нашу работу ?
Ну а если действительно присмотреться, объясните чем Ваш вариант с макросом надежнее
чем просто вызов в нужное место ?
повторный вход в любом случае Вы не обеспечили....
defunct
Цитата(singlskv @ May 23 2008, 22:34) *
Ну а если действительно присмотреться, объясните чем Ваш вариант с макросом надежнее
чем просто вызов в нужное место ?
повторный вход в любом случае Вы не обеспечили....

1. он быстрее (меньше тактов занимает).
2. не использует RETI не по месту (определяющее по надежности отличие)
=GM=
Цитата(singlskv @ May 23 2008, 17:46) *
Для частных, большой вопрос, тот кусочек кода который я привел был частным случаем,
я его привел только для того чтобы показать сколько ТОЧНО длится передача по SPI

Ну вот, родил специально для вашего частного случая
Код
;Обмен данными по SPI на максимальной скорости Fclk/2
spist1: in   temp1,spdr  ;принятый байт
spistc: out  spdr,temp2  ;передадим текущий байт
        st   x+,temp1    ;запомним принятый байт
        andi xl,0x10     ;коррекция указателя
        ld   temp2,x     ;прочитаем
        nop              ;следующий байт
        nop              ;из циклического
        nop              ;буфера
        nop              ;период выдачи
        nop              ;байт на Fclk/2
        nop              ;должен быть
        nop              ;18 МЦ, необходимо
        nop              ;проверить
        dec  bcounter    ;все байты?
        brne spist1      ;нет
        ret

Здесь две особенности, первая то, что работает точно на Fclk/2, вторая - используется циклический буфер на 16 байт (можно настроить на любой размер кратный степени 2), поэтому не надо задумываться, куда писать или откуда читать. Надо только зарядить bcounter<16 и использовать для чтения из буфера команду ld temp1,-x. Немного непривычно, но когда освоишься, получается компактный код. Можно и нулём останавливать передачу, место есть...

Вызов, как предложил defunct
Код
       ld    temp2,x
       rcall spistc

В железе не проверял, но вроде должно работать.
Qwertty
Отпишусь тут. Сегодня помучал SPI. Узнал много нового smile.gif
Не ждать готовности SPI можно.
Код
void    SpiMasterTx(uint8_t data)
{
do
  {
   SPDR = data;
  }
  while(SPSR & (1<<WCOL));
}

Такая конструкция оказалась вполне работоспособной. То что писал Maik-vs в 4 сообщении этой темы не подтвердилось - данные в SPDR не портятся. Симулятор студии и реальный осциллограф также это подтверждают.
Ждать приходится только при чтении:
Код
uint8_t    SpiMasterTxRx(uint8_t data)
{
    do
        {
        SPDR = data;
        }
        while(SPSR & (1<<WCOL));
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
}

Оказывается WCOL очень полезный флаг....
Maik-vs
Цитата(Qwertty @ Nov 13 2008, 19:47) *
Отпишусь тут. Сегодня помучал SPI. Узнал много нового smile.gif
Не ждать готовности SPI можно.
Код
void    SpiMasterTx(uint8_t data)
{
do
  {
   SPDR = data;
  }
  while(SPSR & (1<<WCOL));
}

Такая конструкция оказалась вполне работоспособной. То что писал Maik-vs в 4 сообщении этой темы не подтвердилось - данные в SPDR не портятся. Симулятор студии и реальный осциллограф также это подтверждают.

М-да. Оказывается, "while" теперь переводится "не ждать"... lol.gif
Где я писал, что портятся данные в SPDR? Они НЕ ПЕРЕДАЮТСЯ, если запись в SPDR произошла в течение передачи. Это коллизия называется. А Вы фигачите байт в SPDR пока передача не закончится и с нею коллизия стало быть тоже. Хотя в 6-м посте пеклись, как бы "записать SPDR и продолжить заниматься другими делами." wacko.gif
Qwertty
Цитата(Maik-vs @ Nov 13 2008, 22:45) *
М-да. Оказывается, "while" теперь переводится "не ждать"... lol.gif

Цель та же - не ждать. Именно это я и получил. Записал в SPDR и пошел гулять, готовить следующие данные. Подготовил быстрее, чем закончилась передача, подожду немного, может пару-тройку тактов. Это лучше, чем например 32. А если данные готовились чуть дольше - вообще ничего не жду. Выигрыш во времени получился вполне ощутимый. И "самосброс" SPIF теперь не помеха.
Цитата(Maik-vs @ Nov 13 2008, 22:45) *
Где я писал, что портятся данные в SPDR? Они НЕ ПЕРЕДАЮТСЯ, если запись в SPDR произошла в течение передачи. Это коллизия называется.

"Писать в SPDR можно через 34 цикла процессора после предыдущей записи (SPCR=0x50). Раньше - синхроимпульсы будут, а данные - нет." Я вот про это. Запись в SPDR во время передачи вызывает только один эффект - взвод WCOL. На передаваемые данные никак не влияет. Когда передача закончится, запись в SPDR вызовет начало новой передачи совершенно обычным способом.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.