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

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> SPI+DMA, приём данных
Dubov
сообщение Aug 14 2012, 13:08
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Камень AT91SAMXE512
Есть пример из библиотеки для чтения данных из SPI через DMA

CODE
//------------------------------------------------------------------------------
/// Reads data from a SPI peripheral until the provided buffer is filled. This
/// method does NOT need to be called after SPI_Write or SPI_WriteBuffer.
/// \param spi Pointer to an AT91S_SPI instance.
/// \param buffer Data buffer to store incoming bytes.
/// \param length Length in bytes of the data buffer.
//------------------------------------------------------------------------------
unsigned char SPI_ReadBuffer(AT91S_SPI *spi,
void *buffer,
unsigned int length)
{
#if !defined(CHIP_SPI_DMA)
// Check if the first bank is free
if (spi->SPI_RCR == 0) {

spi->SPI_RPR = (unsigned int) buffer;
spi->SPI_RCR = length;
spi->SPI_PTCR = AT91C_PDC_RXTEN;
return 1;
}
// Check if second bank is free
else if (spi->SPI_RNCR == 0) {

spi->SPI_RNPR = (unsigned int) buffer;
spi->SPI_RNCR = length;
return 1;
}
#endif
// No free bank
return 0;
}


я всегда думал что если что-то нужно получить из SPI, то что-то нужно отправить, а тут что-то непонятное. Как применить эту функцию для получения данных?
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Aug 14 2012, 13:14
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(Dubov @ Aug 14 2012, 17:08) *
я всегда думал что если что-то нужно получить из SPI, то что-то нужно отправить, а тут что-то непонятное.

Совершенно верно.

Цитата(Dubov @ Aug 14 2012, 17:08) *
Как применить эту функцию для получения данных?

Отдельно настроить еще и передачу. А лучше не применять библиотеки, а написать по-своему. Оно и понятнее будет в конечном счете.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Aug 14 2012, 13:29
Сообщение #3


;
******

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



ChanFAT & Martin Thomas
тут нормальный пример, но очень расточительный
требует DMA настроенного на чтение и запись,, в один загоняется на передачу буфер во флеше из 0xFF (там это привязано к размеру сектора, т.е. по умолчанию 512 байт)
второй буфер - на прием.
Пытался когда-то чтоб оно перекрывалось - те же 2 канала, но один буфер в ОЗУ, предварительно забитый 0xff, что-то не пошло... но разбираться не стал, и так устраивало. Интересно, если кто из гуру зайдет - в принципе, работает такая организация на sam7 или нет?

Сообщение отредактировал _Pasha - Aug 14 2012, 13:32
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 14 2012, 14:11
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(aaarrr @ Aug 14 2012, 16:14) *
Совершенно верно.
А лучше не применять библиотеки, а написать по-своему. Оно и понятнее будет в конечном счете.


В том то и дело, что если работать срегистрами SPI напрямую, то всё понятно. Функцию, которая принимает отправляемое значение, а возвращает принятое - писал сам.
Но вот с DMA никак в толк не возьму как работать.

Код что я привёл значит настраивает SPI на приём через PDC? Получается если вызвать эту функцию, то SPI автоматически даст нужное количество клоков(в зависимости от размера запрашиваемого буфера) и заберёт данные?

Цитата(aaarrr @ Aug 14 2012, 16:14) *
Отдельно настроить еще и передачу.


а как же строки

CODE
This
/// method does NOT need to be called after SPI_Write or SPI_WriteBuffer.


когда SPI_WriteBuffer выглядит так:

CODE
//------------------------------------------------------------------------------
/// Sends the contents of buffer through a SPI peripheral, using the PDC to
/// take care of the transfer.
/// \param spi Pointer to an AT91S_SPI instance.
/// \param buffer Data buffer to send.
/// \param length Length of the data buffer.
//------------------------------------------------------------------------------
unsigned char SPI_WriteBuffer(AT91S_SPI *spi,
void *buffer,
unsigned int length)
{
#if !defined(CHIP_SPI_DMA)
// Check if first bank is free
if (spi->SPI_TCR == 0) {

spi->SPI_TPR = (unsigned int) buffer;
spi->SPI_TCR = length;
spi->SPI_PTCR = AT91C_PDC_TXTEN;
return 1;
}
// Check if second bank is free
else if (spi->SPI_TNCR == 0) {

spi->SPI_TNPR = (unsigned int) buffer;
spi->SPI_TNCR = length;
return 1;
}
#endif
// No free banks
return 0;
}


Цитата(_Pasha @ Aug 14 2012, 16:29) *
ChanFAT & Martin Thomas
тут нормальный пример, но очень расточительный
требует DMA настроенного на чтение и запись,, в один загоняется на передачу буфер во флеше из 0xFF (там это привязано к размеру сектора, т.е. по умолчанию 512 байт)
второй буфер - на прием.
Пытался когда-то чтоб оно перекрывалось - те же 2 канала, но один буфер в ОЗУ, предварительно забитый 0xff, что-то не пошло... но разбираться не стал, и так устраивало. Интересно, если кто из гуру зайдет - в принципе, работает такая организация на sam7 или нет?

спасибо за ссылку, судя по всему, мне нужно это
CODE
//! Should be moved to a new file
//------------------------------------------------------------------------------
/// Read data on SPI data bus;
/// Returns 1 if read fails, returns 0 if no error.
/// \param pSdSpi Pointer to a SD SPI driver instance.
/// \param pData Data pointer.
/// \param size Data size.
//------------------------------------------------------------------------------
unsigned char SDSPI_Read(SdSpi *pSdSpi, unsigned char *pData, unsigned int size)
{
unsigned char error;

// MOSI should hold high during read, or there will be wrong data in received data.
memset(pData, 0xff, size);

error = SDSPI_PDC(pSdSpi, pData, size);

while(SDSPI_IsBusy(pSdSpi) == 1);

if( error == 0 ) {
return 0;
}
else {
TRACE_DEBUG("PB SDSPI_Read\n\r");
return 1;
}
}

//------------------------------------------------------------------------------
/// Write data on SPI data bus;
/// Returns 1 if write fails, returns 0 if no error.
/// \param pSdSpi Pointer to a SD SPI driver instance.
/// \param pData Data pointer.
/// \param size Data size.
//------------------------------------------------------------------------------
unsigned char SDSPI_Write(SdSpi *pSdSpi, unsigned char *pData, unsigned int size)
{
unsigned char error;

error = SDSPI_PDC(pSdSpi, pData, size);

while(SDSPI_IsBusy(pSdSpi) == 1);

if( error == 0 ) {
return 0;
}
else {
TRACE_DEBUG("PB SDSPI_Write\n\r");
return 1;
}
}


Но почему SDSPI_Read и SDSPI_Write абсолютно идентичны(несчитая установки линии MOSI в SDSPI_Read)?
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Aug 14 2012, 14:16
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(_Pasha @ Aug 14 2012, 17:29) *
Пытался когда-то чтоб оно перекрывалось - те же 2 канала, но один буфер в ОЗУ, предварительно забитый 0xff, что-то не пошло... но разбираться не стал, и так устраивало. Интересно, если кто из гуру зайдет - в принципе, работает такая организация на sam7 или нет?

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

Еще вариант - перевести MOSI в PIO и выдать '1', данные взять любые.

Цитата(Dubov @ Aug 14 2012, 17:38) *
Код что я привёл значит настраивает SPI на приём через PDC? Получается если вызвать эту функцию, то SPI автоматически даст нужное количество клоков(в зависимости от размера запрашиваемого буфера) и заберёт данные?

Код только настраивает PDC. Чтобы данные появились, все равно нужно начать передачу. Никакой автоматики.

Цитата(Dubov @ Aug 14 2012, 17:38) *
а как же строки

Код
This
/// method does NOT need to be called after SPI_Write or SPI_WriteBuffer.

Эти строки говорят, что нет необходимости делать чтение при записи. Но никак не наоборот.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 14 2012, 14:31
Сообщение #6


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(aaarrr @ Aug 14 2012, 17:16) *
Код только настраивает PDC. Чтобы данные появились, все равно нужно начать передачу. Никакой автоматики.


Передача может быть начата как :

CODE
//------------------------------------------------------------------------------
/// Sends the contents of buffer through a SPI peripheral, using the PDC to
/// take care of the transfer.
/// \param spi Pointer to an AT91S_SPI instance.
/// \param buffer Data buffer to send.
/// \param length Length of the data buffer.
//------------------------------------------------------------------------------
unsigned char SPI_WriteBuffer(AT91S_SPI *spi,
void *buffer,
unsigned int length)
{
#if !defined(CHIP_SPI_DMA)
// Check if first bank is free
if (spi->SPI_TCR == 0) {

spi->SPI_TPR = (unsigned int) buffer;
spi->SPI_TCR = length;
spi->SPI_PTCR = AT91C_PDC_TXTEN;
return 1;
}
// Check if second bank is free
else if (spi->SPI_TNCR == 0) {

spi->SPI_TNPR = (unsigned int) buffer;
spi->SPI_TNCR = length;
return 1;
}
#endif
// No free banks
return 0;
}


или нужно делать так

CODE
void SPI_Write(AT91S_SPI *spi, unsigned int npcs, unsigned short data)
{
// Discard contents of RDR register
//volatile unsigned int discard = spi->SPI_RDR;

// Send data
while ((spi->SPI_SR & AT91C_SPI_TXEMPTY) == 0);
spi->SPI_TDR = data | SPI_PCS(npcs);
while ((spi->SPI_SR & AT91C_SPI_TDRE) == 0);
}


Go to the top of the page
 
+Quote Post
aaarrr
сообщение Aug 14 2012, 14:33
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Передача может осуществляться любым способом. Программирование регистров PDC на передачу немедленно запускает обмен.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 14 2012, 14:39
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(aaarrr @ Aug 14 2012, 17:33) *
Передача может осуществляться любым способом. Программирование регистров PDC на передачу немедленно запускает обмен.

Большущее спасибо. Многое проясняется.
А как получается, если я сначала отправляю блок данных (пустые данные, если мне надо только принимать данные) путём:
CODE
unsigned char SPI_WriteBuffer(AT91S_SPI *spi,
void *buffer,
unsigned int length)
{
// Check if first bank is free
if (spi->SPI_TCR == 0) {

spi->SPI_TPR = (unsigned int) buffer;
spi->SPI_TCR = length;
spi->SPI_PTCR = AT91C_PDC_TXTEN;
return 1;
}
// Check if second bank is free
else if (spi->SPI_TNCR == 0) {

spi->SPI_TNPR = (unsigned int) buffer;
spi->SPI_TNCR = length;
return 1;
}

// No free banks
return 0;
}


а затем принимаю данные(уже то что мне действительно нужно)
CODE
unsigned char SPI_ReadBuffer(AT91S_SPI *spi,
void *buffer,
unsigned int length)
{
// Check if the first bank is free
if (spi->SPI_RCR == 0) {

spi->SPI_RPR = (unsigned int) buffer;
spi->SPI_RCR = length;
spi->SPI_PTCR = AT91C_PDC_RXTEN;
return 1;
}
// Check if second bank is free
else if (spi->SPI_RNCR == 0) {

spi->SPI_RNPR = (unsigned int) buffer;
spi->SPI_RNCR = length;
return 1;
}

// No free bank
return 0;
}


Неужели приём данных осуществиться простым вызовом двух функций одной за другой?
Задаю этот вопрос потому что ранее принимал данные исключительно побайтно, так как один байт отправил, один получил и считал.
или при записи блока по PDC автоматически приходят входные данные?
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Aug 14 2012, 14:50
Сообщение #9


;
******

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



Цитата(Dubov @ Aug 14 2012, 17:11) *
Но почему SDSPI_Read и SDSPI_Write абсолютно идентичны(несчитая установки линии MOSI в SDSPI_Read)?

Это может означать только одно: я Вам указал более свежую версию, чем та, с которой имел дело. Значит, всё-таки должно работать...
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Aug 14 2012, 15:32
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(Dubov @ Aug 14 2012, 18:39) *
А как получается, если я сначала отправляю блок данных...

Нет. Сначала нужно настроить DMA на прием (вызовом SPI_ReadBuffer или вручную), и только потом запустить DMA на передачу,
так как в противном случае Вы рискуете получить переполнение в приемнике.
Затем ждете прерывания ENDRX (или опрашиваете флаг), после чего можно пользоваться принятыми данными.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 14 2012, 15:45
Сообщение #11


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Тогда проще использовать это:

CODE
//------------------------------------------------------------------------------
/// Use PDC for SPI data transfer.
/// Return 0 if no error, otherwise return error status.
/// \param pSdSpi Pointer to a SdSpi instance.
/// \param pData Data pointer.
/// \param size Data transfer byte count.
//------------------------------------------------------------------------------
unsigned char SDSPI_PDC(SdSpi *pSdSpi, unsigned char *pData, unsigned int size)
{
AT91PS_SPI pSpiHw = pSdSpi->pSpiHw;
unsigned int spiIer;

if (pSdSpi->semaphore == 0) {
TRACE_DEBUG("No semaphore\n\r");
return SDSPI_ERROR_LOCK;
}
pSdSpi->semaphore--;

// Enable the SPI clock
AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId);

// Disable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;

// Receive Pointer Register
pSpiHw->SPI_RPR = (int)pData;
// Receive Counter Register
pSpiHw->SPI_RCR = size;
// Transmit Pointer Register
pSpiHw->SPI_TPR = (int) pData;
// Transmit Counter Register
pSpiHw->SPI_TCR = size;

spiIer = AT91C_SPI_RXBUFF;

// Enable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

// Interrupt enable shall be done after PDC TXTEN and RXTEN
pSpiHw->SPI_IER = spiIer;

return 0;
}


но предварительно записав в буфер 0XFF через memset(pData,0xff,size)

Сообщение отредактировал Dubov - Aug 14 2012, 15:53
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Aug 14 2012, 16:18
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(Dubov @ Aug 14 2012, 19:45) *
Тогда проще использовать это

Если нужен вывод 0xFF, то самый простой и эффективный путь я уже описал - '1' на MOSI через GPIO и ROM в качестве источника данных на передачу.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 14 2012, 17:20
Сообщение #13


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Да нет, нужно просто данные принимать, но чтобы что-то принимать, нужно что-то отправить... пусть будет 0xff. А зачем так сложно: ROM в качестве источника данных? по-моему в коде выше просто буфер заполняется 0xFF и отправляется, а при выходе из функции бфер уже содержит принятые данные.

Сообщение отредактировал Dubov - Aug 14 2012, 17:21
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Aug 14 2012, 17:24
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(Dubov @ Aug 14 2012, 21:20) *
А зачем так сложно: ROM в качестве источника данных? по-моему в коде выше просто буфер заполняется 0xFF и отправляется, а при выходе из функции бфер уже содержит принятые данные.

Заполнять каждый раз заново - долго, хранить в RAM готовый - расточительно. Кроме того, использование ROM позволяет более эффективно задействовать шинный коммутатор.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Aug 14 2012, 17:45
Сообщение #15


;
******

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



Цитата(Dubov @ Aug 14 2012, 20:20) *
А зачем так сложно: ROM в качестве источника данных?

Если еще задать своп TPR <-> TNPR буфер "худеет" вдвое

Сообщение отредактировал _Pasha - Aug 14 2012, 17:46
Go to the top of the page
 
+Quote Post
sasamy
сообщение Aug 14 2012, 18:27
Сообщение #16


Знающий
****

Группа: Участник
Сообщений: 783
Регистрация: 22-11-08
Пользователь №: 41 858



Цитата(Dubov @ Aug 14 2012, 17:08) *
Камень AT91SAMXE512
...
я всегда думал что если что-то нужно получить из SPI, то что-то нужно отправить, а тут что-то непонятное. Как применить эту функцию для получения данных?


Вам случайно не slave нужен ? а то я запутался с вами - несколько человек в нескольких ветках говорят об одном и том же на первый взгляд. Если slave - не обязательно заботиться о передаче
Цитата
32.6.4 SPI Slave Mode
...
When a transfer starts, the data shifted out is the data present in the Shift Register. If no data
has been written in the Transmit Data Register (SPI_TDR), the last data received is transferred.
If no data has been received since the last reset, all bits are transmitted low, as the Shift Regis-
ter resets at 0.


передача в PDC отключается
Цитата
26.4.9 Transfer Control Register
...
• TXTDIS: Transmitter Transfer Disable
0 = No effect.
1 = Disables the PDC transmitter channel requests.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 15 2012, 07:11
Сообщение #17


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(_Pasha @ Aug 14 2012, 20:45) *
Если еще задать своп TPR <-> TNPR буфер "худеет" вдвое

а как это сделать? можно подробнее.

и ещё, если хочу задать массив в ROM, достатачно ли объявить массив как const char?(для MSP430 делал именно так)

для sasamy:
мне нужен мастер, но спасибо за замечение, думаю пригодится в будущем.

Сообщение отредактировал Dubov - Aug 15 2012, 07:15
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 15 2012, 11:39
Сообщение #18


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Всё, кажется разобрался. Функция чтения выглядит так:
CODE
unsigned char SDSPI_PDC_read(SdSpi *pSdSpi, unsigned char *pData, unsigned int size)
{
AT91PS_SPI pSpiHw = pSdSpi->pSpiHw;
unsigned int spiIer;
/*
if (pSdSpi->semaphore == 0) {
TRACE_DEBUG("No semaphore\n\r");
return SDSPI_ERROR_LOCK;
}
pSdSpi->semaphore--;
*/
// Enable the SPI clock
AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId);

// Disable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;

// Receive Pointer Register
pSpiHw->SPI_RPR = (int)pData;
// Receive Counter Register
pSpiHw->SPI_RCR = size;
// Transmit Pointer Register
pSpiHw->SPI_TPR = (int)dummy_ff_block;
// Transmit Counter Register
pSpiHw->SPI_TCR = size;

// spiIer = AT91C_SPI_RXBUFF;

// Enable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

while(! (*AT91C_SPI_SR & AT91C_SPI_ENDRX));
pSpiHw->AT91C_SPI_PTCR = AT91C_PDC_RXTDIS;
pSpiHw->AT91C_SPI_PTCR = AT91C_PDC_TXTDIS;
// Interrupt enable shall be done after PDC TXTEN and RXTEN
// pSpiHw->SPI_IER = spiIer;

return 0;
}

dummy_ff_block - массив значений из ROM.
Go to the top of the page
 
+Quote Post
Lotor
сообщение Aug 15 2012, 12:10
Сообщение #19


Местный
***

Группа: Свой
Сообщений: 476
Регистрация: 3-07-07
Из: Санкт-Петербург
Пользователь №: 28 866



Цитата
while(! (*AT91C_SPI_SR & AT91C_SPI_ENDRX));

Смысл в DMA, если есть такое? %)
Да и вообще странная функция, зачем Вы в ней клоки включаете? Это надо делать в функции инициализации.


--------------------
Ковырял чукча отверткой в ухе, звук в телевизоре и пропал.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Aug 15 2012, 12:28
Сообщение #20


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



просто следовал совету:
Цитата(aaarrr @ Aug 14 2012, 18:32) *
Затем ждете прерывания ENDRX (или опрашиваете флаг), после чего можно пользоваться принятыми данными.

Прерывания ждать не хотел, так как использую код для написания модуля ядра Linux.

Цитата(Lotor @ Aug 15 2012, 15:10) *
Смысл в DMA, если есть такое? %)
Да и вообще странная функция, зачем Вы в ней клоки включаете? Это надо делать в функции инициализации.

Да, тоже мне это показалось странным, но в исходниках было так - я так и оставил (клок выключается в функции инициализации и включается в функции приёма данных).

а почему собственно нет смысла в DMA, если я опрашиваю флаг приёма даных?

Сообщение отредактировал Dubov - Aug 15 2012, 12:33
Go to the top of the page
 
+Quote Post

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

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 11:32
Рейтинг@Mail.ru


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