Перейти к содержанию
    

Посылка по UART с DMA

Настройка

void DMA_Init(void)
{
	//enable clock for DMAMUX and DMA
	SIM_SCGC6 |= SIM_SCGC6_DMAMUX0_MASK;
	SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

	//enable channel 0 and set the source - 3 = UART0 Transmit ($3.3.9.1 Table 3-26)
	DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(3);

	//set memory address for source and destination
	DMA_TCD0_SADDR = (uint32_t)&log_buffer_data;  //global scope buffer
	DMA_TCD0_DADDR = (uint32_t)&UART0_D;

    //set an offset
	DMA_TCD0_SOFF = 1;  //???
	DMA_TCD0_DOFF = 0;  //???

	//set data transfer size - byte
	DMA_TCD0_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0);  //???

	//number of bytes to be transfered in each service request
	DMA_TCD0_NBYTES_MLNO = glob_data_chank;  //???

	//current major iteration count (a single iteration)
	DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(1);  //???
	DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(1);  //???

	DMA_TCD0_SLAST = 0;   //???
	DMA_TCD0_DLASTSGA = 0; //???

	//setup control and status register
	//DMA_TCD0_CSR = 0;

	//interrupt when tx/rx buffer is full. stop DMA on single transfer.
	DMA_TCD0_CSR = DMA_CSR_INTMAJOR_MASK | DMA_CSR_DREQ_MASK;
	//enable request signal for channel 0
	DMA_ERQ = DMA_ERQ_ERQ0_MASK;
}

Возникает несколько вопросов.

1. Правильно ли настроены все регистры?

2. размер посылаемого масива (glob_data_chank) может изменится. Нужно изменять DMA_TCD0_NBYTES_MLNO каждый service request?

3. Нужно прерывание? Или просто опрашивать флаг

while (DMA_TCD0_CSR & DMA_CSR_DONE_MASK)

4. На стороне  UART нужны какие то дополнительные настройки? Нужны прерывания?

UART0_C2 |= UART_C2_TIE_MASK;
UART0_C5 |= UART_C5_TDMAS_MASK;

5. Как я начинаю каждую посылку? Так?

DMA_TCD0_CSR |= DMA_CSR_START_MASK;

 

Изменено пользователем jenya7

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Делаете отправку через HAL (5 минут)

Смотрите код (час)

Получаете знания и даёте советы таким же гуру программирования (можно за деньги)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 minutes ago, x893 said:

Делаете отправку через HAL (5 минут)

Смотрите код (час)

Получаете знания и даёте советы таким же гуру программирования (можно за деньги)

из HAL спустится в регистры это ещё тот фильм ужасов. я надеюсь не я первый на планете земля делает связку UART-DMA. Хотя часто возникает мысль - я  таки Гагарин первый.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

C NXP не работал (только STM, Миландр), но общие соображения:

1. DMA при обмене данными используют в том числе для минимизации прерываний, в идеале никаких прерываний от UART обрабатывать не нужно, только прерывание от окончания DMA.

2. При приеме я использую DMA только, если длина посылки заранее известна. В моих случаях с UART - практически никогда, веду прием побайтно по прерыванию или по опросу флага.

3. При передаче заполняю буфер, настраиваю DMA на запуск от события пустого передатчика и передачу заданного числа байт. Запуск передачи - разрешением передачи UART: после все делает DMA. Это в STM. В Миландре в передатчике UART собственный буфер, стараюсь не превышать его длину и не использовать ни DMA, ни прерывания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

38 minutes ago, V_G said:

C NXP не работал (только STM, Миландр), но общие соображения:

1. DMA при обмене данными используют в том числе для минимизации прерываний, в идеале никаких прерываний от UART обрабатывать не нужно, только прерывание от окончания DMA.

2. При приеме я использую DMA только, если длина посылки заранее известна. В моих случаях с UART - практически никогда, веду прием побайтно по прерыванию или по опросу флага.

3. При передаче заполняю буфер, настраиваю DMA на запуск от события пустого передатчика и передачу заданного числа байт. Запуск передачи - разрешением передачи UART: после все делает DMA. Это в STM. В Миландре в передатчике UART собственный буфер, стараюсь не превышать его длину и не использовать ни DMA, ни прерывания.

прерывание от окончания DMA - это тот который в vector table?

(tIsrFunc)&Cpu_Interrupt,          /* 0x10  0x00000040   -   ivINT_DMA0_DMA16               unused by PE */
(tIsrFunc)&Cpu_Interrupt,          /* 0x11  0x00000044   -   ivINT_DMA1_DMA17               unused by PE */
(tIsrFunc)&Cpu_Interrupt,          /* 0x12  0x00000048   -   ivINT_DMA2_DMA18               unused by PE */
(tIsrFunc)&Cpu_Interrupt,          /* 0x13  0x0000004C   -   ivINT_DMA3_DMA19               unused by PE */
   

там нет разделения по какому ивенту произошло прерывание. нужно проверять флаг - (DMA_TCD0_CSR & DMA_CSR_DONE_MASK) в прерывании?

 

Изменено пользователем jenya7

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 часов назад, jenya7 сказал:

нужно проверять флаг - (DMA_TCD0_CSR & DMA_CSR_DONE_MASK) в прерывании?

Флаг проверять, конечно, нужно, но главное - запретить ВСЕ прерывания от DMA, кроме прерывания от окончания. Если есть такая возможность. Или по крайней мере, стараться запретить и не обрабатывать наиболее частые прерывания, загружающие проц.

Кстати, про оптимальность и про HAL (хотя его в NXP может не быть - своя библиотека). И не про UART, а про высокоскоростной I2S. Я поначалу написал прием данных с АЦП на HAL, скорость не удовлетворила. Потом просто прошелся по шагам внутри функций HAL и исключил всю избыточность для моего конкретного случая. Производительность возросла в разы...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

я нашел иакой пример

int UART1_dma_complete(void){
	//Channel Done wait
	return ((DMA0->TCD[1].CSR) & DMA_CSR_DONE_MASK);
}

void UART1_dma_complete_wait(void){
	while(!((DMA0->TCD[1].CSR) & DMA_CSR_DONE_MASK));
}

void UART1_txBulk(uint8_t *data, uint16_t len){
	//Wait until dma is complete
	while(!(UART1_dma_complete()));
	DMAMUX0->CHCFG[1]=0; //set to zero during configuration

	//Enable request signal for channel 1
	DMA0->ERQ|=DMA_ERQ_ERQ1_MASK;

	//Set memory address
	DMA0->TCD[1].SADDR=(uint32_t) (data);
	DMA0->TCD[1].DADDR=(uint32_t) &(UART->D);

	//Source and destination data transfer size
	DMA0->TCD[1].ATTR=DMA_ATTR_SSIZE(0)|DMA_ATTR_DSIZE(0);
	//Set number of minor loops
	DMA0->TCD[1].CITER_ELINKNO=DMA_CITER_ELINKNO_CITER(len);
	DMA0->TCD[1].BITER_ELINKNO=DMA_BITER_ELINKNO_BITER(len);

	DMAMUX0->CHCFG[1]=DMAMUX_CHCFG_ENBL_MASK|DMAMUX_CHCFG_SOURCE(5); //always enabled
	DMA0->TCD[1].CSR=DMA_CSR_DREQ_MASK;
}

окончание транзакции делается полингом флага. а зачем переписывать все регистры заново? а почему DMA_CSR_DONE_MASK не сбрасывается? а где тригер новой транзакции?

и почему

 

DMA0->TCD[1].CITER_ELINKNO=DMA_CITER_ELINKNO_CITER(len);
DMA0->TCD[1].BITER_ELINKNO=DMA_BITER_ELINKNO_BITER(len);

это ведь minor loop - 1 байт. вся длина в major loop - DMA_TCD0_NBYTES_MLNO.

Изменено пользователем jenya7

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не-не-не, я ж говорил, с NXP не работал, там все не так, как в STM, тут никаких советов по конкретному коду. Но переписывать тучу регистров при реинициализации DMA - обычное дело. Особенно в Миландре, где DMA особенно кривой

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

я смотрю в HAL

status_t UART_SendEDMA(UART_Type *base, uart_edma_handle_t *handle, uart_transfer_t *xfer)
{
    assert(handle);
    assert(handle->txEdmaHandle);
    assert(xfer);
    assert(xfer->data);
    assert(xfer->dataSize);

    edma_transfer_config_t xferConfig;
    status_t status;

    /* If previous TX not finished. */
    if (kUART_TxBusy == handle->txState)
    {
        status = kStatus_UART_TxBusy;
    }
    else
    {
        handle->txState = kUART_TxBusy;
        handle->txDataSizeAll = xfer->dataSize;

        /* Prepare transfer. */
        EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t), (void *)UART_GetDataRegisterAddress(base),
                             sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_MemoryToPeripheral);

        /* Store the initially configured eDMA minor byte transfer count into the UART handle */
        handle->nbytes = sizeof(uint8_t);

        /* Submit transfer. */
        EDMA_SubmitTransfer(handle->txEdmaHandle, &xferConfig);
        EDMA_StartTransfer(handle->txEdmaHandle);

        /* Enable UART TX EDMA. */
        UART_EnableTxDMA(base, true);

        status = kStatus_Success;
    }

    return status;
}

на каждую посылку переписываются ВСЕ регистры. вот лишь малая часть кода

   /* source address */
    tcd->SADDR = config->srcAddr;
    /* destination address */
    tcd->DADDR = config->destAddr;
    /* Source data and destination data transfer size */
    tcd->ATTR = DMA_ATTR_SSIZE(config->srcTransferSize) | DMA_ATTR_DSIZE(config->destTransferSize);
    /* Source address signed offset */
    tcd->SOFF = config->srcOffset;
    /* Destination address signed offset */
    tcd->DOFF = config->destOffset;
    /* Minor byte transfer count */
    tcd->NBYTES = config->minorLoopBytes;
    /* Current major iteration count */
    tcd->CITER = config->majorLoopCounts;
    /* Starting major iteration count */
    tcd->BITER = config->majorLoopCounts;
    /* Enable scatter/gather processing */

и я не понимаю это обязательные действия или нет.

потом

Quote

/* Minor byte transfer count */
    tcd->NBYTES = config->minorLoopBytes;  //minorLoopBytes = sizeof(uin8_t)

в описании регистра
 

Quote

DMA_TCDn_NBYTES_MLNO

This register, or one of the next two registers (TCD_NBYTES_MLOFFNO,
TCD_NBYTES_MLOFFYES), defines the number of bytes to transfer per request.

что тогда - number of bytes to transfer per request. трансфер это ж вся посылка, весь буфер.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 минут назад, V_G сказал:

Но переписывать тучу регистров при реинициализации DMA - обычное дело.

Никакой "тучи" в NXP переписывать не надо (так же как и в большинстве других известных мне семейств МК). На приём: один раз запустил DMA в "linked-list" режиме и больше его не трогаешь (только периодически читаешь текущее принятое число символов, да обрабатываешь прерывания завершения очередного блока). На передачу, тоже негусто: всего несколько записей в регистры (меняются только адрес и размер пересылки).

Да и не нужно DMA в NXP для UART как правило: UART там имеет нормальный FIFO. Это не STM32 с его примитивными UART.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, jcxz said:

Никакой "тучи" в NXP переписывать не надо (так же как и в большинстве других известных мне семейств МК). На приём: один раз запустил DMA в "linked-list" режиме и больше его не трогаешь (только периодически читаешь текущее принятое число символов, да обрабатываешь прерывания завершения очередного блока). На передачу, тоже негусто: всего несколько записей в регистры (меняются только адрес и размер пересылки).

Да и не нужно DMA в NXP для UART как правило: UART там имеет нормальный FIFO. Это не STM32 с его примитивными UART.

а что значит в "linked-list" режиме? какой регистр?

Quote

На передачу, тоже негусто: всего несколько записей в регистры (меняются только адрес и размер пересылки).

а почему адрес меняется? я всегда посылаю статический буфер.

Quote

Да и не нужно DMA в NXP для UART как правило

то есть достаточно проверять (DMA0->TCD[1].CSR) & DMA_CSR_DONE_MASK) ?

это не надо?

UART0_C5 |= UART_C5_TDMAS_MASK;

 

а что там с minor loop и major loop? minor loop - 1 байт, major loop - вся длина? я видел и так и наоборот.

 

а офсеты? я так понял memory to peripherial

DMA0->TCD[1].SOFF=1;
DMA0->TCD[1].DOFF=0;

 

Quote

Да и не нужно DMA в NXP для UART как правило: UART там имеет нормальный FIFO.

у меня посылка 4096 байт. и пока DMA их шлет я должен вычитать следующий сектор из флэш памяти по SPI. так я экономлю время.

Изменено пользователем jenya7

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...