|
SAM3U и SPI, Запуск SPI с DMA |
|
|
|
May 10 2011, 05:26
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Добрый день! Кто-нибудь запускал SPI с DMA (прием\передача) на AT91SAM3U? В даташите есть упоминание, что SPI может работать с DMA, в библиотеке от ATMEL есть специальные функции, реализующие эту работу: Код unsigned char SPI_WriteBuffer(AT91S_SPI *spi, void *buffer, unsigned int length) запись Код unsigned char SPI_ReadBuffer(AT91S_SPI *spi, void *buffer, unsigned int length) чтение После инициализации SPI и запуска этих функций, ничего не происходит. Добавлю, что SPI по прерываниям у меня работает, но хотелось бы не занимать процессорное время и перейти на DMA. Внутри этих функций (на примере SPI_ReadBuffer) инициализация и запуск DMA: Код #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_RCR(количество перемещаемых байт), проверяю этот регистр, а он равен 0. Настораживает тот момент, что в ашнике на проц AT91SAM3U4.h нет адресов для регистров DMA работающих с SPI, хотя в структуре SPI эти регистры присутствуют. Заранее благодарен за помощь!
|
|
|
|
|
May 10 2011, 05:46
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Цитата А данные на передачу не забыли запустить? Чтобы что-то принимать по SPI, нужно обязательно что-то одновременно передавать. Нет, не забыл. Чтобы не наделать ошибок я для начала оставил передачу на прерывании. До прерывания DMA уже инициализирован.
|
|
|
|
|
May 10 2011, 11:09
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Вот нашел на одной сайте этот код. Попробую сделать нечто подобное. Посмотрите на него, правильной дорогой меня направили? Код I got it working... down here is the source code if someone is need of help..
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Config_DMA_SPI (void) { AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_HDMA; AT91C_BASE_HDMA->HDMA_GCFG=0;
//SPI RX
AT91C_BASE_HDMA_CH_0->HDMA_CTRLB = AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | AT91C_HDMA_DST_DSCR_FETCH_DISABLE | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED | AT91C_HDMA_DST_ADDRESS_MODE_INCR | AT91C_HDMA_FC_PER2MEM;
AT91C_BASE_HDMA_CH_0->HDMA_CFG = AT91C_HDMA_SRC_PER_2 | AT91C_HDMA_LOCK_IF_DISABLE | AT91C_HDMA_LOCK_B_DISABLE | AT91C_HDMA_SRC_H2SEL_HW | AT91C_HDMA_DST_H2SEL_SW | AT91C_HDMA_FIFOCFG_LARGESTBURST;
AT91C_BASE_HDMA_CH_0->HDMA_DSCR=0; AT91C_BASE_HDMA_CH_0->HDMA_SADDR=(unsigned int)&AT91C_BASE_SPI0->SPI_RDR; AT91C_BASE_HDMA_CH_0->HDMA_DADDR=(unsigned int)buff_in;
AT91C_BASE_HDMA_CH_0->HDMA_CTRLA = AT91C_HDMA_DCSIZE_1 | AT91C_HDMA_SCSIZE_1 | AT91C_HDMA_SRC_WIDTH_BYTE | AT91C_HDMA_SRC_WIDTH_BYTE | TRANSFER_SIZE;
// SPI TX
AT91C_BASE_HDMA_CH_1->HDMA_CTRLB = AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | AT91C_HDMA_DST_DSCR_FETCH_DISABLE | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED | AT91C_HDMA_DST_ADDRESS_MODE_FIXED | AT91C_HDMA_FC_MEM2PER;
AT91C_BASE_HDMA_CH_1->HDMA_CFG = AT91C_HDMA_DST_PER_1 | AT91C_HDMA_SOD_DISABLE | AT91C_HDMA_LOCK_IF_DISABLE | AT91C_HDMA_LOCK_B_DISABLE | AT91C_HDMA_SRC_H2SEL_SW | AT91C_HDMA_DST_H2SEL_HW | AT91C_HDMA_FIFOCFG_LARGESTBURST;
AT91C_BASE_HDMA_CH_1->HDMA_DSCR = 0; AT91C_BASE_HDMA_CH_1->HDMA_SADDR=(unsigned int)&dummy_spi_byte; AT91C_BASE_HDMA_CH_1->HDMA_DADDR=(unsigned int)&AT91C_BASE_SPI0->SPI_TDR; AT91C_BASE_HDMA_CH_1->HDMA_CTRLA = AT91C_HDMA_DCSIZE_1 + AT91C_HDMA_SCSIZE_1 + AT91C_HDMA_SRC_WIDTH_BYTE + AT91C_HDMA_DST_WIDTH_BYTE + TRANSFER_SIZE;
//Interrupt AT91C_BASE_HDMA->HDMA_EBCIER=AT91C_HDMA_BTC0+AT91C_HDMA_BTC1; IRQ_ConfigureIT(AT91C_ID_HDMA, 0, HDMA_IrqHandler);
} //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Enable_DMA_SPI(void) { AT91C_BASE_HDMA_CH_0->HDMA_DADDR=(unsigned int)buff_in; // RX AT91C_BASE_HDMA_CH_0->HDMA_CTRLA |= TRANSFER_SIZE; AT91C_BASE_HDMA_CH_1->HDMA_CTRLA |= TRANSFER_SIZE; AT91C_BASE_HDMA->HDMA_EN = AT91C_HDMA_ENABLE;
AT91C_BASE_HDMA->HDMA_CHER = AT91C_HDMA_ENA0_1; //RX AT91C_BASE_HDMA->HDMA_CHER = AT91C_HDMA_ENA1_1; //TX IRQ_EnableIT(AT91C_ID_HDMA); }
|
|
|
|
|
May 10 2011, 17:01
|
Знающий
   
Группа: Свой
Сообщений: 583
Регистрация: 7-06-06
Из: Таганрог
Пользователь №: 17 840

|
Цитата(aaarrr @ May 10 2011, 18:48)  Код AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_HDMA; После этой строчки можно не смотреть, ибо бангалор. Но вполне возможно, что работает. Нда уж, и возможно будет зависеть от ревизии кристала
|
|
|
|
|
May 11 2011, 06:05
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Код Код AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_HDMA;
После этой строчки можно не смотреть, ибо бангалор. Но вполне возможно, что работает. А в чем проблема?
|
|
|
|
|
May 11 2011, 08:58
|
Знающий
   
Группа: Свой
Сообщений: 583
Регистрация: 7-06-06
Из: Таганрог
Пользователь №: 17 840

|
Цитата(*rust* @ May 11 2011, 10:05)  Код Код AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_HDMA;
После этой строчки можно не смотреть, ибо бангалор. Но вполне возможно, что работает. А в чем проблема? В том что PMC_PCER write only.
|
|
|
|
|
May 11 2011, 10:26
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Цитата В том что PMC_PCER write only. так команда Код AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_HDMA; это же не чтение. Вот функция от атмела Код void PMC_EnablePeripheral(unsigned int id) { SANITY_CHECK(id < 32);
if ((AT91C_BASE_PMC->PMC_PCSR & (1 << id)) == (1 << id)) {
TRACE_INFO("PMC_EnablePeripheral: clock of peripheral" " %u is already enabled\n\r", id); } else {
AT91C_BASE_PMC->PMC_PCER = 1 << id; } } где unsigned int id в данной случае будет AT91C_ID_HDMA В чем проблема?
|
|
|
|
|
May 11 2011, 10:28
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(*rust* @ May 11 2011, 14:26)  так команда Код AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_HDMA; это же не чтение. Это не только чтение, это чтение-модификация-запись.
|
|
|
|
|
May 11 2011, 12:12
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Все понятно,  Вы правы. Я сначала подумал вообще о записи в целом в этот регистр, о том, что не нужно туда писать. Постараюсь подчистить код.
|
|
|
|
|
May 24 2011, 12:15
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
После небольшого перерыва опять вернулся к решению проблемы с DMA и SPI Оставил передачу SPI работать по прерываниям, настроил прием в буфер через DMA. Предварительно не один из каналов DMA не работает. CODE //---- #define buffer_size 10 unsigned char DMA_BUFFER_1[buffer_size], *prt_DMA; //----
настройка DMA на прием:
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_HDMA; //тактирование DMA AT91C_BASE_HDMA->HDMA_EN = AT91C_HDMA_ENABLE; //Вкл DMA DMA_SetSourceAddr(0,(unsigned int)&AT91C_BASE_SPI0->SPI_RDR); //Установка адреса источника, в данном случае регистр приемника SPI DMA_SetDestinationAddr(0,(unsigned int)prt_DMA); //Установка адреса буфера, куда данные складываются DMA_SetSourceBufferSize(0, buffer_size, 0, 0, 1); //Конф рег HDMA_CTRLA_0:номерканала,размер перемещаемых данных, формат данных ист(байт), //формат данных прием(байт),done(почему-то стоит 1) AT91C_BASE_HDMA_CH_0->HDMA_CTRLB = (AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | //Конф рег HDMA_CTRLB_0 AT91C_HDMA_DST_DSCR_FETCH_DISABLE | //хз, в даташите для этого случая нужно откл AT91C_HDMA_SRC_ADDRESS_MODE_FIXED | //режим фиксированного адреса для истрочника, т.к это регистр SPI AT91C_HDMA_DST_ADDRESS_MODE_INCR | //режим инкремента для памяти AT91C_HDMA_FC_PER2MEM); //направление периферия->память AT91C_BASE_HDMA_CH_0->HDMA_CFG = (AT91C_HDMA_SRC_PER_2 | AT91C_HDMA_SRC_H2SEL_HW); //Hardware handshaking interface //----- ConfigureSPI(); //-----
//----- Start_spi(); //-----
Start_DMA_RX() { DMA_SetSourceAddr(0,(unsigned int)&AT91C_BASE_SPI0->SPI_RDR); DMA_SetDestinationAddr(0,(unsigned int)prt_DMA); DMA_SetSourceBufferSize(0, buffer_size, 0, 0, 1); DMA_EnableChannel(0); } В общем-то сие работает после каждого заполнения буфера перед след. итерацией нужно опять взводить DMA: Start_DMA_RX() Очень интересует вопрос, что такое Hardware handshaking interface и Software handshaking interface и для чего они нужны? Многократно прочитав DMA в даташите так и не смог добиться полного понимания.
Сообщение отредактировал IgorKossak - May 26 2011, 14:32
Причина редактирования: [codebox]
|
|
|
|
|
May 24 2011, 17:04
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Спасибо за ответ. Цитата В случае Hardware handshaking interface порция данных передается по сигналу запроса от периферии Как я понял это штука работает без участия процессора? Цитата Устанавливается в "0" в режимах Multi Buffer Transfer. У меня режим Single Buffer и вроде бы по описанию на 1021стр DST_DSCR and SRC_DSCR должны быть 1. Исправлю. Забегая немного вперед: Когда DMA передает в SPI на что должен ориентироваться DMA? Нужно ли конфигурировать SPI каким-то специальным образом? Я пытался запустить передачу через DMA но пока никак. Многовато вопросов  )
|
|
|
|
|
May 24 2011, 19:49
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(*rust* @ May 24 2011, 21:04)  Как я понял это штука работает без участия процессора? Именно. Цитата(*rust* @ May 24 2011, 21:04)  У меня режим Single Buffer и вроде бы по описанию на 1021стр DST_DSCR and SRC_DSCR должны быть 1. Исправлю. Так в коде же все правильно - чтения дескрипторов запрещены. Цитата(*rust* @ May 24 2011, 21:04)  Забегая немного вперед: Когда DMA передает в SPI на что должен ориентироваться DMA? Нужно ли конфигурировать SPI каким-то специальным образом? Я пытался запустить передачу через DMA но пока никак. Специально конфигурировать не нужно. Разумеется, не нужно задействовать прерывания SPI и трогать регистр данных. Просмотрите код внимательно, скорее всего, где-нибудь допустили опечатку.
|
|
|
|
|
May 25 2011, 05:43
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Цитата Так в коде же все правильно - чтения дескрипторов запрещены. Поторопился... Вопрос по Hardware handshaking interface. Как я понял для памяти этот интерфейс применять не нужно, т.к память всегда готова. Т.е HWI используется только для периферии, тогда как биты в полях SDC_PER, DST_PER регистра DMAC_CFG опознаватели того устройства с которым HWI работает. Если все так для приема и передачи через DMA, дожны быть разные опознаватели. Прием (SPI источник) (AT91C_HDMA_SRC_PER_2 | AT91C_HDMA_SRC_H2SEL_HW) Прием (SPI приемник) (AT91C_HDMA_DST_PER_1 | AT91C_HDMA_DST_H2SEL_HW) Правильно?
|
|
|
|
|
May 25 2011, 11:27
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
aaarrr, спасибо Вам за помощь! Все получилось SPI через DMA заработал (прием и передача). Конечно очень помог пример с зарубежного сайта, который я приводил несколькими постами выше. Для всех нуждающихся выкладываю код, сразу оговорюсь, что на оригинальность не претендую: CODE //настройка DMA от приемника SPI до буфера // RX AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_HDMA; AT91C_BASE_HDMA->HDMA_GCFG=0; // DMA_SetSourceAddr(0,(unsigned int)&AT91C_BASE_SPI0->SPI_RDR); //регист приемника SPI DMA_SetDestinationAddr(0,(unsigned int)prt_DMA); //указатель на массив DMA_SetSourceBufferSize(0, buffer_size, 0, 0, 1); // buffer_size-кол-во перемещаемых байт AT91C_BASE_HDMA_CH_0->HDMA_CTRLB = (AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | AT91C_HDMA_DST_DSCR_FETCH_DISABLE | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED | AT91C_HDMA_DST_ADDRESS_MODE_INCR | AT91C_HDMA_FC_PER2MEM); AT91C_BASE_HDMA_CH_0->HDMA_CFG = (AT91C_HDMA_SRC_PER_2 | AT91C_HDMA_LOCK_IF_DISABLE | AT91C_HDMA_LOCK_B_DISABLE | AT91C_HDMA_SRC_H2SEL_HW | AT91C_HDMA_DST_H2SEL_SW | AT91C_HDMA_FIFOCFG_LARGESTBURST);
//настройка DMA в передатчик SPI с буфера памяти// ТX //в моем случае я передаю одно и то же число, поэтому инкремента/декремента для памяти у меня нет.
DMA_SetSourceAddr(1,(unsigned int)prt_trr); DMA_SetDestinationAddr(1,(unsigned int)&AT91C_BASE_SPI0->SPI_TDR); DMA_SetSourceBufferSize(1, buffer_size, 0, 0, 1);
AT91C_BASE_HDMA_CH_1->HDMA_DSCR=0; AT91C_BASE_HDMA_CH_1->HDMA_CTRLB = (AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | AT91C_HDMA_DST_DSCR_FETCH_DISABLE | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED| AT91C_HDMA_DST_ADDRESS_MODE_FIXED| AT91C_HDMA_FC_MEM2PER); AT91C_BASE_HDMA_CH_1->HDMA_CFG = (AT91C_HDMA_DST_PER_1 | AT91C_HDMA_SOD_DISABLE | AT91C_HDMA_LOCK_IF_DISABLE | AT91C_HDMA_LOCK_B_DISABLE | AT91C_HDMA_SRC_H2SEL_SW | AT91C_HDMA_DST_H2SEL_HW | AT91C_HDMA_FIFOCFG_LARGESTBURST); //В даташите написано, что нужно с начало запустить DMA, а потом периферию, если используете Flow controller //поэтому: void start_DMA(void) { DMA_SetSourceAddr(0,(unsigned int)&AT91C_BASE_SPI0->SPI_RDR); DMA_SetDestinationAddr(0,(unsigned int)prt_DMA); DMA_SetSourceBufferSize(0, buffer_size, 0, 0, 1); DMA_SetSourceAddr(1,(unsigned int)prt_trr); DMA_SetDestinationAddr(1,(unsigned int)&AT91C_BASE_SPI0->SPI_TDR); DMA_SetSourceBufferSize(1, buffer_size, 0, 0, 1); DMA_EnableChannel(0); DMA_EnableChannel(1); }
//Configure pins for SPI PIO_Configure(pinsSPI, PIO_LISTSIZE(pinsSPI)); ConfigureSPI();// and Enable SPI После каждого прохода DMA нужно не забывать делать переинициализацию DMA: void start_DMA(void) Если заметите ошибки, пишите. Думаю, что это будет полезно всем. Еще раз спасибо за помощь!
Сообщение отредактировал IgorKossak - May 26 2011, 14:33
Причина редактирования: [codebox]
|
|
|
|
|
May 26 2011, 09:44
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
aaarrr, такой вопрос: Можно ли сделать CS(чип-селект) байт-ориентированным, т.е после каждого переданного байта CS подымается, при работе SPI через DMA? В документации я читал что, SPI with DMAC, CS опускает перед передачей и подымает его только когда передал весь указанный в настройках массив. И второй вопрос: Куда смотрит DMA когда выбирает каким CS дергать? CS у меня два, когда все работало по прерываниям, я сам указывал, при необходимости, каким CS нужно дергать а тут как?
|
|
|
|
|
May 26 2011, 10:20
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(*rust* @ May 26 2011, 13:44)  aaarrr, такой вопрос: Можно ли сделать CS(чип-селект) байт-ориентированным, т.е после каждого переданного байта CS подымается, при работе SPI через DMA? В документации я читал что, SPI with DMAC, CS опускает перед передачей и подымает его только когда передал весь указанный в настройках массив. На SAM7 для этого нужно было использовать "левые" передачи с фиктивным CS. Подозреваю, что и здесь нужен такой же финт. Цитата(*rust* @ May 26 2011, 13:44)  И второй вопрос: Куда смотрит DMA когда выбирает каким CS дергать? CS у меня два, когда все работало по прерываниям, я сам указывал, при необходимости, каким CS нужно дергать а тут как? Тут все точно так же. Если работаете в режиме Fixed Peripheral, то CS выбирается в SPI_MR, в случае Variable Peripheral CS указывается в регистре данных.
|
|
|
|
|
May 26 2011, 10:43
|
Частый гость
 
Группа: Участник
Сообщений: 109
Регистрация: 19-01-11
Пользователь №: 62 335

|
Спасибо за ответ! Цитата На SAM7 для этого нужно было использовать "левые" передачи с фиктивным CS. Подозреваю, что и здесь нужен такой же финт. По подробнее можно?
|
|
|
|
|
May 26 2011, 10:54
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(*rust* @ May 26 2011, 14:43)  По подробнее можно?  Заводится некий фиктивный CS из неиспользуемых (скажем, NPCS3), для него выставляется режим с максимально коротким временем передачи - 8 бит, MCK/2. Физически этот сигнал никуда не выводится, т.е. ногу можно использовать под PIO. Для обмена отдельными словами с периферией на другом CS (например, NPCS0), используем такую последовательность: Передача NPCS0 Передача NPCS3 Передача NPCS0 Передача NPCS3 ... Выглядит коряво, конечно, но другого способа не предусмотрено.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|