|
SPI+DMA, приём данных |
|
|
|
Aug 14 2012, 13:08
|
Местный
  
Группа: Участник
Сообщений: 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, то что-то нужно отправить, а тут что-то непонятное. Как применить эту функцию для получения данных?
|
|
|
|
|
Aug 14 2012, 13:14
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Dubov @ Aug 14 2012, 17:08)  я всегда думал что если что-то нужно получить из SPI, то что-то нужно отправить, а тут что-то непонятное. Совершенно верно. Цитата(Dubov @ Aug 14 2012, 17:08)  Как применить эту функцию для получения данных? Отдельно настроить еще и передачу. А лучше не применять библиотеки, а написать по-своему. Оно и понятнее будет в конечном счете.
|
|
|
|
|
Aug 14 2012, 13:29
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
ChanFAT & Martin Thomasтут нормальный пример, но очень расточительный требует DMA настроенного на чтение и запись,, в один загоняется на передачу буфер во флеше из 0xFF (там это привязано к размеру сектора, т.е. по умолчанию 512 байт) второй буфер - на прием. Пытался когда-то чтоб оно перекрывалось - те же 2 канала, но один буфер в ОЗУ, предварительно забитый 0xff, что-то не пошло... но разбираться не стал, и так устраивало. Интересно, если кто из гуру зайдет - в принципе, работает такая организация на sam7 или нет?
Сообщение отредактировал _Pasha - Aug 14 2012, 13:32
|
|
|
|
|
Aug 14 2012, 14:11
|
Местный
  
Группа: Участник
Сообщений: 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)?
|
|
|
|
|
Aug 14 2012, 14:16
|
Гуру
     
Группа: Свой
Сообщений: 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. Эти строки говорят, что нет необходимости делать чтение при записи. Но никак не наоборот.
|
|
|
|
|
Aug 14 2012, 14:31
|
Местный
  
Группа: Участник
Сообщений: 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); }
|
|
|
|
|
Aug 14 2012, 14:39
|
Местный
  
Группа: Участник
Сообщений: 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 автоматически приходят входные данные?
|
|
|
|
|
Aug 14 2012, 15:45
|
Местный
  
Группа: Участник
Сообщений: 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
|
|
|
|
|
Aug 14 2012, 18:27
|
Знающий
   
Группа: Участник
Сообщений: 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.
|
|
|
|
|
Aug 15 2012, 07:11
|
Местный
  
Группа: Участник
Сообщений: 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
|
|
|
|
|
Aug 15 2012, 11:39
|
Местный
  
Группа: Участник
Сообщений: 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.
|
|
|
|
|
Aug 15 2012, 12:10
|
Местный
  
Группа: Свой
Сообщений: 476
Регистрация: 3-07-07
Из: Санкт-Петербург
Пользователь №: 28 866

|
Цитата while(! (*AT91C_SPI_SR & AT91C_SPI_ENDRX)); Смысл в DMA, если есть такое? %) Да и вообще странная функция, зачем Вы в ней клоки включаете? Это надо делать в функции инициализации.
--------------------
Ковырял чукча отверткой в ухе, звук в телевизоре и пропал.
|
|
|
|
|
Aug 15 2012, 12:28
|
Местный
  
Группа: Участник
Сообщений: 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
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|