реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Программный SPI, реализация на avr
CIIAPTAK
сообщение Oct 18 2009, 13:09
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 27
Регистрация: 22-07-07
Пользователь №: 29 290



Доброго дня уважаемые...
У кого то есть библиотека реализации программного SPI интерфейса на avr?

Нужен именно программный, ибо аппаратный уже занят. Да и девайс разведен уже на на другие выводы Мк.

Заранее вам благодарен.
Go to the top of the page
 
+Quote Post
mempfis_
сообщение Oct 18 2009, 13:27
Сообщение #2


Профессионал
*****

Группа: Свой
Сообщений: 1 001
Регистрация: 27-06-06
Пользователь №: 18 409



Цитата(CIIAPTAK @ Oct 18 2009, 16:09) *
Нужен именно программный, ибо аппаратный уже занят. Да и девайс разведен уже на на другие выводы Мк.


Чтож там сложного то:

Код
#define HI(x) SPI_PORT |= (1<<(x))
#define LO(x) SPI_PORT &= ~(1<<(x))

unsigned int SPIWriteWord(unsigned int cmd) {
  unsigned char i;
  unsigned int recv;
  recv = 0;
  LO(SCK);
  LO(nSS);
  for(i=0; i<16; i++) {
    if(cmd&0x8000) HI(SDI); else LO(SDI);
    HI(SCK);
    recv<<=1;    
    if( SPI_PIN&(1<<SDO) ) {
      recv|=0x0001;
    }
    LO(SCK);
    cmd<<=1;
  }
  HI(nSS);
  return recv;
}


Обзовите где нибудь выводы SDI, SDO, SCK. Тут 16битный вариант, сли нужно код легко переделывается на 8 бит.
Go to the top of the page
 
+Quote Post
CIIAPTAK
сообщение Oct 18 2009, 13:59
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 27
Регистрация: 22-07-07
Пользователь №: 29 290



[quote name='mempfis_' date='Oct 18 2009, 17:27' post='663748']
Чтож там сложного то:

Код
#define HI(x) SPI_PORT |= (1<<(x))
#define LO(x) SPI_PORT &= ~(1<<(x))

unsigned int SPIWriteWord(unsigned int cmd) {
  unsigned char i;
  unsigned int recv;
  recv = 0;
  LO(SCK);
  LO(nSS);
  for(i=0; i<8; i++) {
    if(cmd&0x80) HI(SDI); else LO(SDI);
    HI(SCK);
    recv<<=1;    
    if( SPI_PIN&(1<<SDO) ) {
      recv|=0x0001;
    }
    LO(SCK);
    cmd<<=1;
  }
  HI(nSS);
  return recv;
}


Биг спс. Для 8 ми битного, правильно поправил? =)
Это на запись. А как на чтение? В случае двухстороннего обмена.

Сообщение отредактировал CIIAPTAK - Oct 18 2009, 14:05
Go to the top of the page
 
+Quote Post
prottoss
сообщение Oct 18 2009, 14:25
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Цитата(CIIAPTAK @ Oct 18 2009, 21:59) *
Это на запись. А как на чтение? В случае двухстороннего обмена.
Мда, тяжелый случайsmile.gif Так это оно и есть - загоняете в функцию байт для записи, автоматом функция вернула то, что прочитала.


--------------------
Go to the top of the page
 
+Quote Post
Xenia
сообщение Oct 18 2009, 15:04
Сообщение #5


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



SPI куда элегантнее программировать на ассемблере, если аппаратно этого сделать по каким-то причинам нельзя. Элегантность заключена в том, что так можно эффективно использовать флаг переноса при сдвигах, строго выдержать меандр по времени (хотя последнее, обычно, не требуется) и сделать правильные задержки. На чистом С такое написать нельзя.
Здесь приведены сочиненные мной процедуры:
void Wr_Reg( char byte); // запись по SPI в регистр АЦП
char Rd_Reg( void); // чтение по SPI регистра АЦП
Вся остальная программа у меня на С, откуда я и вызываю функции Wr_Reg(byte) и Rd_Reg(). Их описание в хидере я только что привела, их тела на ассемблере выглядят так:
Код
        RSEG CODE:CODE:NOROOT(1)

        PUBLIC Wr_Reg
Wr_Reg:
        ldi R17, 8
LoopWrReg:
        sbi PORTD, SCLK; установим SCLK в 1
        rol R16; сдвигаем регистр
        brcs SetSDI
        cbi PORTD, SDI; SDI = 0
        rjmp CliSCLK; 2 clocks
SetSDI:
        sbi PORTD, SDI; SDI = 1
        nop; 1+1 clocks
        nop
CliSCLK:
        cbi PORTD, SCLK; установим SCLK в 0
        dec R17
        brne LoopWrReg; если не 0
        ret

        PUBLIC Rd_Reg
Rd_Reg:
        ldi R17, 8
        clr R16; обнуляем регистр
LoopRdReg:
        sbi PORTD, SCLK; установим SCLK в 1
        nop
        cbi PORTD, SCLK; установим SCLK в 0
        lsl R16; сдвигаем регистр
        sbic PIND, SDO; SDO ?
        ori R16, 1; устанавливаем мл.бит
        dec R17
        brne LoopRdReg; если не 0
        ret

Связь осуществляется через порт D, который можно заменить на любой другой. SDI, SDO и SCLK по назначению соответствуют MOSI, MISO и СLK.
Суть не меняется, если вместо АЦП будет какое-то другое устройство.
P.S. Проект на IAR EWAVR, в котором имеется один ассемблерный файл (этот), а остальные на C. Микроконтроллер ATtiny2313.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Oct 18 2009, 15:15
Сообщение #6


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(Xenia @ Oct 18 2009, 18:04) *

В тексте программы ничего нету про SDO. Поправьте, плз, чтоб студенты не ошибалисьsmile.gif
И еще (для страшных эстетов) - некая зависимость от тактовой частоты проца.
Go to the top of the page
 
+Quote Post
Xenia
сообщение Oct 18 2009, 15:37
Сообщение #7


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(_Pasha @ Oct 18 2009, 19:15) *
В тексте программы ничего нету про SDO. Поправьте, плз, чтоб студенты не ошибалисьsmile.gif


Поправила.

Цитата(_Pasha @ Oct 18 2009, 19:15) *
И еще (для страшных эстетов) - некая зависимость от тактовой частоты проца.


Да-да! Это очень важно! И вовсе не для эстетов. В ассеблерную процедеру есть возможноть натолкать столько nop (пустых операций), чтобы сделать любую дополнительную задержку. Это очень даже может понадобится тогда, когда МК работает на более высокой частоте кварца, чем необходимо, чтобы соблюсти требования ведомого устройства. И тут дело не только в частоте его кварца, сколько в технических требованиях к обмену. Это весьма типичная задача при работе с АЦП, в даташите которых расписаны тайминги на SPI обмен.
Ассемблер очень хорош как раз для соблюдения всех этих таймингов и латентного времени между передачей команды и приемом данных. Однако смысл имеет писать на ассемблере только такую интерфейсную часть программы. Полностью переходить на ассемлер не советую - ошибки на нем совершаются гораздо чаще, чем на C, и программировать на нем медленнее.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Oct 18 2009, 15:45
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(Xenia @ Oct 18 2009, 18:04) *
SPI куда элегантнее программировать на ассемблере...

А откомпилировать несколько сишных строк и посмотреть на результат было нельзя?
Цитата
есть возможноть натолкать столько nop (пустых операций)

__no_operation();
или
asm ( "NOP" );


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
prottoss
сообщение Oct 18 2009, 15:56
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Цитата(Xenia @ Oct 18 2009, 23:37) *
Вот мой вариант софтового/железного SPI, с задержками без всякого ассемблера.
Код
/*****************************************************************************
Обмен данными по SPI
записывает в SPI порт данные wr_data и возвращает прочитанные при обмене данные
******************************************************************************/
UCHAR ExSPI(UCHAR wr_data)
{    
      // проверяем джампер low_sck
      if(ISP_PIN & (1 << PIN_LOW_SCK)) // не замкнут, аппаратный SPI
    {    
          SPI_ON();    // включаем SPI
          SPDR = wr_data;    // загружаем SPI данными
        while(!(SPSR & (1<<SPIF))){};    // ждем окончания передачи
        return SPDR;    // возвращаем принятые данные
    }
    else // иначе софтверный SPI
    {    
          UCHAR rd_data;
        SPI_OFF();    // выключаем аппаратный SPI
          for(UCHAR i = 0; i < 8; i++)
        {    
              // отправляем старший бит отправляемого байта
              if(0 != (wr_data & 0x80))
                ISP_PORT |= (1 << PIN_MOSI); // = 1
            else
                ISP_PORT &= ~(1 << PIN_MOSI);    // = 0

            // подготавливаем следующий бит
            wr_data <<=1;

            // принимаем очередной (начиная со старшего) бит
            // принимаемого байта
            rd_data <<= 1;
            if(0 != (ISP_PIN & (1 << PIN_MISO)))
                rd_data++; // устанавливаем единичный бит, если высокий принимаемый уровень

            // формируем импульс на выводе SCK МК
            ISP_PORT |= (1 << PIN_SCK);    // высокий
            __delay_cycles(LOW_SCK_DELEY); // задержка
            ISP_PORT &= ~(1 << PIN_SCK);    // низкий
            __delay_cycles(LOW_SCK_DELEY); // задержка
        }

        return rd_data;
    }
}


--------------------
Go to the top of the page
 
+Quote Post
Xenia
сообщение Oct 18 2009, 16:06
Сообщение #10


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(zltigo @ Oct 18 2009, 19:45) *
А откомпилировать несколько сишных строк и посмотреть на результат было нельзя?

__no_operation();
или
asm ( "NOP" );

На языке С даже задачу нельзя сформулировать так, чтобы его компилятор сгенерировал код подобной плотности. Причина в том, что понятие флагов переноса или переполнения на этом языке никак не отражено. А следовательно и воспользваться этим удобным механизмом нет возможности.

Натолкать задержек, конечно же, язык С позволяет. И не только указанным вами способом, но и вообще любого иного рода мусорным кодом.
Приведенный мною пример близок к наиболее ускоренному варианту. Nop используется для "проработки" фронта импульса, который при комбинации соседних команд "поднять и опустить" зачастую не достигает максимально возможного значения (толи ёмкости мешают, толи еще что).

Я не думаю, что длительное смотрение в откомпилированный результат позволит вам написать код на С, который работает быстрее, но в тоже время был бы полностью надежен. На С такие вещи пишут только тогда, когда торопиться некуда. Этот же код можно использовать даже в прерывании! (хотя и не рекомендуется). Не перевелись еще любители опрашивать АЦП прямо в той процедуре обработки прерывания, которое возникает от сигнала готовности данных DREADY.

Цитата(prottoss @ Oct 18 2009, 19:56) *
Вот мой вариант софтового/железного SPI, с задержками без всякого ассемблера.

В этой теме никого ваш вариант не интересует. В начальном сообщении четко сформулирована задача - аппаратный SPI не использовать.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Oct 18 2009, 16:08
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(Xenia @ Oct 18 2009, 19:03) *
Я не думаю...

Я же не просил Вас думать sad.gif я просил просто откомпилировать, что-то вроде
Код
void Wr_Reg( unsigned char cmd )
{
    for( char i=8; i; i-- )
      {    (cmd & 0x80) ? ( HI( MOSI ) ) : ( LO( MOSI ) );
        HI( SCK );
        cmd <<= 1;
        LO( SCK );
      }
}

и посмотреть, что там с "кодом подобной плотности"...


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
Xenia
сообщение Oct 18 2009, 16:27
Сообщение #12


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(zltigo @ Oct 18 2009, 20:08) *
Я же не просил Вас думать sad.gif я просил просто откомпилировать, что-то вроде
...
и посмотреть, что там с "кодом подобной плотности"...

У вас "лишняя" проверка cmd & 0x80, т.к. тестировать быстрее при сдвиге cmd <<= 1, поскольку этот бит выпадающий.
К другим недостаткам вашего кода можно отнести отсутствие однотактной задержки между установкой MOSI и подачей клока SCK. Здесь желательно тоже пропустить один такт, чтобы MOSI успел достичь своего максимального или минимального значения.

И вообще, потуги вроде ваших smile.gif, возникают исключительно вследствии того, что вы знаете, какой должна быть данная процедура в кодах МК, но пыжитесь достигнуть этого, укрощая С. В таких случаях было бы более эффективно, если бы вы воплощали свое знание сразу на том языке (в данном случае ассемблере), который плозволяет учитывать все необходимые нюансы. А так вы вместо этого боретесь с языком С, пытаясь подогнать его под требуемую ассемлерную кодировку. Язык должен быть помощником, не противником. Не стоит мучить С, чтобы он выдавал тот код, который без напряга можно было бы написать на ассеблере. А на языке С пишут обычно то, в отношении чего у вас не будет в дальнейшем притензий к компилятору.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Oct 18 2009, 16:41
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(Xenia @ Oct 18 2009, 19:27) *
У вас "лишняя" проверка....

Ближе к делу, т.е. поминаемой Вами "плотности кода". Сколько команд получили из сишного исходника и насколько это "не плотнее" зачем-то писанного на ASM.
Цитата
И вообще, потуги вроде ваших , возникают исключительно вследствии того, что вы знаете, какой должна быть данная процедура в кодах МК

Потуги??? Абсолютно в лоб писанный сишный исходник. Какой должна в кодах MK мне по барабану - я пишу на в данном случае на 'C' тупейший алгоритм банального ногомахания и с трудом представляю,как можно на 'C' зачем-то написать по другому. Я даже, то что написал не компилировал ввиду полной для меня очевидности получения абсолютно приемлимого кода из под IAR компилятора.
Цитата
отсутствие однотактной задержки между установкой MOSI и подачей клока SCK

Прочитайте предыдущий пост и добавьте сколько и куда хотите.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Oct 18 2009, 16:56
Сообщение #14


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(Xenia @ Oct 18 2009, 19:27) *
Язык должен быть помощником, не противником.

Просто откомпильте - никакого оверхеда там не полУчите. Уже не те времена на дворе, чтоб было стыдно за компиляторы в таких простых ситуациях. А отсутствие операций с битом переноса - это только оттого, что операция типа sbrc Rx,7 ничуть не хуже. Необходимость в использовании бита переноса при приеме последовательных данных у меня лично возникала, но это было в другом контексте - когда читается capture register и полученное значение сравнивается с константой, соотвествующей некой центральной частоте. В общем, это совсем из оффтопа.
По поводу НОПов - имхо достаточно в асме проверку #if(F_CPU > 8000000UL) сделать (как там по-ИАРовски - не уверен, но в winavr сделал бы так) - и добавить один лишний НОП
Go to the top of the page
 
+Quote Post
ReAl
сообщение Oct 18 2009, 18:22
Сообщение #15


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(Xenia @ Oct 18 2009, 19:27) *
У вас "лишняя" проверка cmd & 0x80, т.к. тестировать быстрее при сдвиге cmd <<= 1, поскольку этот бит выпадающий.
Это смотря чего хотеть.
Если скважности SCK 2.000 и предельно точной настройки частоты, то да, лучше асм.
Если "лишь бы более-менее близко к 2 и побыстрее", то это пишется левой задней, быстрее заново написать,
чем искать откуда скопипастить, так как писалось уже неоднократно, в том числе ещё для 51-го.
Да, в зависимости от данных слегка дрожит длительность 1-ки.
Что-то не могу придумать пример, где это будет мешать.
Зато одна процедура и на ввод, и на вывод, довольно компактно и быстро.

Код
#include <avr/io.h>
#include "pin_macros.h"

#define SCK B,0,H
#define MOSI B,1,H
#define MISO B,2,H

/*  ну и где-то в h-файле, если кого-то "полнота набора" волнует
*  static inline uint8_t spi_in(void) { retuirn spi_io(0xFF); }
*  static inline void spi_out(uint8_t b) { spi_io(b); }
*/

/* Это под SPI, у которого сдвиг по спаду, приёмник фиксирует по фронту, ну
* данная подпрограмма - после фронта, лишь бы до спада.
*/
uint8_t spi_io(uint8_t b)
{
    uint8_t i = 8;
    do {
        OFF(SCK);
        OFF(MOSI);
        if(b & 0x80) ON(MOSI);
        b <<= 1;            // а вот она задержка от выдачи до фронта
        ON(SCK);
        if( ACTIVE(MISO) ) ++b;
    } while(--i);
    OFF(SCK);
    return b;
}
И компилируется в такое
Код
.global    spi_io
    .type    spi_io, @function
spi_io:
/* prologue: frame size=0 */
/* prologue end (size=0) */
    ldi r25,lo8(8)
.L2:
    cbi 56-0x20,0  ; SCK = 0
    cbi 56-0x20,1  ; MOSI = 0
    sbrc r24,7
    sbi 56-0x20,1  ; MOSI = 1 если надо
.L3:
    lsl r24             ; задержка перед фронтом
    sbi 56-0x20,0  ; SCK = 1
    sbic 54-0x20,2 ; семплирование входа
    subi r24,lo8(-(1))
.L5:
    subi r25,lo8(-(-1))
    brne .L2
    cbi 56-0x20,0
    ldi r25,lo8(0)
/* epilogue: frame size=0 */
    ret


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th July 2025 - 15:01
Рейтинг@Mail.ru


Страница сгенерированна за 0.01539 секунд с 7
ELECTRONIX ©2004-2016