~~This is not PR intended to be a merge.~~
~~I have implemented the I2S features needed to speed up the Trinamic configuration we are considering.~~
– ~~Stops DMA/ISR when i2soutset_passthrough() is called.~~
– ~~Shifts out the state value of the current pin with a bit bang.~~
– ~~Resume DMA by initializing the buffer when i2soutset_stepping() is called.~~
UPDATE
The feasibility of the project was confirmed. Please merge it if you want.
The current implementation is as follows.
– In pass-through mode, use the I2S static mode and allow for a delay of a few microseconds to operate ShiftRegister.
– In stepping mode, the data stream is flowed to I2S by DMA and FIFO and the ShiftRegister is operated.
– The delay function is newly prepared for I2S out. This function has a 8 microseconds delay in pass-through mode and 12 milliseconds delay in stepping mode.
– The delay function of I2S out is called from the CS operation method of TMCStepper and waits for the CS operation properly.
评论 (12)
#2 – odaki 于 2020-06-20
I fixed a fix for the Arduino IDE that made the link fail.
https://github.com/odaki/Grbl_Esp32/commit/d494a673096e8c18bd5063f419802c63b68816e5
#3 – odaki 于 2020-06-21
The way to switch I2S modes in the function calls of pass-through and stepping will require a bit more work.
As far as I’ve tried, I stop I2S once to put it into pass-through mode, but the timing is too early and the trailing steps are cut off by 7. (The estimated step is 999, but 992)
!スクリーンショット 2020-06-21 10 13 50
!スクリーンショット 2020-06-21 10 07 56
I’ll try to fix it by waiting until all the data in the I2S buffer is completely output.
#4 – odaki 于 2020-06-21
First of all I have enabled I2SOUTTOTALEOFINT. When the I2S transfer based on the last descriptor in the DMA descriptor chain is finished The interrupt is raised.
“C++`
//
// Initialize funtion (external function)
//
int IRAMATTR i2soutinit(i2soutinitt &init_param) {
:
I2S0.intena.outtotal_eof = 1; // Triggered when all transmitting linked lists are used up.
:
In the I2S Out task, after calling a pulse callback in the stepper mode, the interrupt is raised. When a request to change to passthrough mode is detected, it is sent to I2S at that time. Set the DMA descriptor so that the buffer is the last buffer. This is to set the Next buffer member of the descriptor to NULL. Breaks the ring of the descriptor.
`C++`
static void IRAM_ATTR i2sOutTask(void* parameter) {
:
I2SOUTPULSEREXITCRITICAL(); // Temporarily unlocked status lock as it may be locked in pulse callback.
(*i2soutpulsefunc)(); // should be pushed into buffer max DMASAMPLESAFECOUNT
I2SOUTPULSERENTERCRITICAL(); // Lock again.
i2soutremaintimeuntilnextpulse = i2soutpulse_period;
if (i2soutpulser_status == CHANGING) {
// This should be tail DMA buffer
dmadesc->qe.stqenext = NULL; // Cut the DMA descriptor ring
}
:
I2SOUTTOTALEOFINT is raised when the transfer of a DMA descriptor with a null Next buffer member is finished.
In the ISR, the FIFO and DMA are disconnected and the TX module is set to STOP.
`C++
static void IRAMATTR i2soutintrhandler(void *arg) {
lldesct *finishdesc;
portBASETYPE highprioritytaskawoken = pdFALSE;
if (I2S0.intst.outeof || I2S0.intst.outtotal_eof) {
if (I2S0.intst.outtotal_eof) {
// This is tail of the DMA descriptors
I2SOUTENTERCRITICALISR();
// Stop FIFO DMA
I2S0.out_link.stop = 1;
// Disconnect DMA from FIFO
I2S0.fifoconf.dscren = 0; //Unset this bit to disable I2S DMA mode. (R/W)
// Stop TX module
I2S0.conf.tx_start = 0;
I2SOUTEXITCRITICALISR();
}
:
``
The buffer at that time is sent to the I2S Out task queue as usual.
C++`
:
// Send a DMA complete event to the I2S bitstreamer task with finished buffer
xQueueSendFromISR(odma.queue, &finishdesc, &highprioritytask_awoken);
}
The I2S Out task can detect the end of the last DMA transfer by looking at the descriptor chain members.
`C++`
static void IRAM_ATTR i2sOutTask(void* parameter) {
:
} else if (i2soutpulser_status == CHANGING) {
if (dmadesc->qe.stqenext == NULL) {
// Tail of the DMA descriptor found
When the I2S Out task detects that the last DMA transfer is over, we formally i2sstop() again, clear the buffer to zero for I2S static mode, and then i2sstart() in I2S static mode to complete the change to passthrough mode.
`C++`
// I2S TX module has alrewdy stopped by ISR
i2soutstop();
i2sclearodmabuffers(0); // 0 for static I2S control mode (right ch. data is always 0)
i2soutpulser_status = PASSTHROUGH;
i2soutstart();
In passthrough mode, i2soutwrite() will take a few microseconds to reflect the result of the i2soutwrite(), since I2S is in static transfer mode.
`C++`
void IRAMATTR i2sout_delay() {
#ifdef USEI2SOUT_STREAM
if (i2soutpulser_status == PASSTHROUGH) {
etsdelayus(I2SOUTUSECPERPULSE);
} else {
delay(I2SOUTDELAY_MS);
}
#else
etsdelayus(I2SOUTUSECPERPULSE);
#endif
}
With this implementation, you can operate a Trinamic SPI CS connected to I2S and not notice the delay.
MotorClass.cpp
`C++“
#ifdef USEI2SOUT
//
// Override default function and insert a short delay
//
void TMC2130Stepper::switchCSpin(bool state) {
digitalWrite(_pinCS, state);
i2soutdelay();
}
#endif
#5 – odaki 于 2020-06-21
The estimated step is 999, and now, 999 again!
!スクリーンショット 2020-06-21 19 18 45
!スクリーンショット 2020-06-21 19 18 30
#6 – odaki 于 2020-06-21
Code cleanup is necessary, including reconsidering new status names and removing dead code, but I’ll commit the code that is working.
#7 – odaki 于 2020-06-21
It needs to be cleaned up, but I’ve adjusted it so that it can be merged with Devt.
#8 – odaki 于 2020-06-21
@MitchBradley @bdring Do you want to use static I2S in the pass-through state?
As a way to make the Trinamic SPI faster, it seems like a good way to make the Grbl_ESP32 fix less of a fix.
This is a feasibility study, but when v0.7.0 of TMCStepper becomes available, the code integrated into Devt?
#9 – odaki 于 2020-06-22
Thank you very much for releasing the latest version of TMCStepper.
https://github.com/teemuatlut/TMCStepper/issues/133
#10 – odaki 于 2020-06-23
When compiled in I2S real-time, it seems to work as expected.
!スクリーンショット 2020-06-24 2 13 44
The end of the homing operation seems to be slightly cut off when compiled with I2S stream (USEI2SOUT_STREAM).
!スクリーンショット 2020-06-24 2 18 27
Perhaps a bit of consideration is needed for a short stream/real-time switch.
Probably waiting for some delay or synchronization.
#11 – odaki 于 2020-06-28
I added a delay to the homing locating process.
As a result, the timing of the reset was delayed and the number of steps in locating is now correctly set to 299.
!スクリーンショット 2020-06-29 2 41 35
#12 – odaki 于 2020-08-19
#1 – odaki 于 2020-06-20
I will also consider how to use I2SCONFSINGLEDATAREG.
How about the following implementation?
The delay processing in TMCStepper’s CS operation is determined by how long it takes to reflect the change of the pin that I2S OUT instructed.
So, I think it would be better if I2S OUT has a function to dynamically delay according to the current processing status and mode.
First of all, if i2s out is set to passthrough, then I2SCONFSINGLEDATAREG is used to change the mode to reflect the pin state to the SR in a short time.
Then, implement dynamic delay function (i2soutsync() or something) to delay 12 ms in stream output state, and delay 4 us in passthrough state.
Then, just call digitalWrite(CS) and i2sout_sync() from TMCStepper’s CS change function in order.