jenya7 0 8 декабря, 2021 Опубликовано 8 декабря, 2021 (изменено) · Жалоба Настройка 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; Изменено 8 декабря, 2021 пользователем jenya7 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
x893 35 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба Делаете отправку через HAL (5 минут) Смотрите код (час) Получаете знания и даёте советы таким же гуру программирования (можно за деньги) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jenya7 0 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 4 minutes ago, x893 said: Делаете отправку через HAL (5 минут) Смотрите код (час) Получаете знания и даёте советы таким же гуру программирования (можно за деньги) из HAL спустится в регистры это ещё тот фильм ужасов. я надеюсь не я первый на планете земля делает связку UART-DMA. Хотя часто возникает мысль - я таки Гагарин первый. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_G 8 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба C NXP не работал (только STM, Миландр), но общие соображения: 1. DMA при обмене данными используют в том числе для минимизации прерываний, в идеале никаких прерываний от UART обрабатывать не нужно, только прерывание от окончания DMA. 2. При приеме я использую DMA только, если длина посылки заранее известна. В моих случаях с UART - практически никогда, веду прием побайтно по прерыванию или по опросу флага. 3. При передаче заполняю буфер, настраиваю DMA на запуск от события пустого передатчика и передачу заданного числа байт. Запуск передачи - разрешением передачи UART: после все делает DMA. Это в STM. В Миландре в передатчике UART собственный буфер, стараюсь не превышать его длину и не использовать ни DMA, ни прерывания. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jenya7 0 8 декабря, 2021 Опубликовано 8 декабря, 2021 (изменено) · Жалоба 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) в прерывании? Изменено 8 декабря, 2021 пользователем jenya7 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_G 8 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 7 часов назад, jenya7 сказал: нужно проверять флаг - (DMA_TCD0_CSR & DMA_CSR_DONE_MASK) в прерывании? Флаг проверять, конечно, нужно, но главное - запретить ВСЕ прерывания от DMA, кроме прерывания от окончания. Если есть такая возможность. Или по крайней мере, стараться запретить и не обрабатывать наиболее частые прерывания, загружающие проц. Кстати, про оптимальность и про HAL (хотя его в NXP может не быть - своя библиотека). И не про UART, а про высокоскоростной I2S. Я поначалу написал прием данных с АЦП на HAL, скорость не удовлетворила. Потом просто прошелся по шагам внутри функций HAL и исключил всю избыточность для моего конкретного случая. Производительность возросла в разы... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jenya7 0 9 декабря, 2021 Опубликовано 9 декабря, 2021 (изменено) · Жалоба я нашел иакой пример 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. Изменено 9 декабря, 2021 пользователем jenya7 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_G 8 9 декабря, 2021 Опубликовано 9 декабря, 2021 · Жалоба Не-не-не, я ж говорил, с NXP не работал, там все не так, как в STM, тут никаких советов по конкретному коду. Но переписывать тучу регистров при реинициализации DMA - обычное дело. Особенно в Миландре, где DMA особенно кривой Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jenya7 0 9 декабря, 2021 Опубликовано 9 декабря, 2021 · Жалоба я смотрю в 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. трансфер это ж вся посылка, весь буфер. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 9 декабря, 2021 Опубликовано 9 декабря, 2021 · Жалоба 19 минут назад, V_G сказал: Но переписывать тучу регистров при реинициализации DMA - обычное дело. Никакой "тучи" в NXP переписывать не надо (так же как и в большинстве других известных мне семейств МК). На приём: один раз запустил DMA в "linked-list" режиме и больше его не трогаешь (только периодически читаешь текущее принятое число символов, да обрабатываешь прерывания завершения очередного блока). На передачу, тоже негусто: всего несколько записей в регистры (меняются только адрес и размер пересылки). Да и не нужно DMA в NXP для UART как правило: UART там имеет нормальный FIFO. Это не STM32 с его примитивными UART. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jenya7 0 9 декабря, 2021 Опубликовано 9 декабря, 2021 (изменено) · Жалоба 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. так я экономлю время. Изменено 9 декабря, 2021 пользователем jenya7 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться