Jump to content

MIOS32 hang on Timer use


Recommended Posts

Hi there!

I'm having trouble using the timers of my STM32-Core, because it keeps hanging and freezing, once I initialise them. Let me show you some code:

/////////////////////////////////////////////////////////////////////////////

// This hook is called after startup to initialize the application

/////////////////////////////////////////////////////////////////////////////

void APP_Init(void)

{

  MIOS32_DELAY_Init(0);	


  // initialize MIOS32 Timer #0, so that our "key-value readout" function is

  // called each 1 ms:

  MIOS32_TIMER_Init(0, 1000, Timer, MIOS32_IRQ_PRIO_LOW);

  // Init a timer for the UI

  MIOS32_TIMER_Init(1, 40000, UI, MIOS32_IRQ_PRIO_LOW);


  MIOS32_SPI_TransferModeInit(0, MIOS32_SPI_MODE_CLK1_PHASE1, MIOS32_SPI_PRESCALER_16);


  Keys_Init();	// only initialising variables

  UI_Init();    // same here


  // initialize all LEDs

  MIOS32_BOARD_LED_Init(0xffffffff);


}
My UI timer works fine, so there's no problem. The strang thing is my other timer:
////////////////////////////////////////////////////////////////////////

// callback function of the readout timer. 

////////////////////////////////////////////////////////////////////////

// Data arrives as 144 bytes. Three bytes actually contain only two 12bit 

// values. At the end, this function melts 1,5 bytes to one 12bit value.

// Because the values are also in the wrong order, it also sorts them

// so they are in the right oder. It would have been better to 

// let the slave CPU do this - however the 20MHz 8bit ATMega used as

// the slave is already busy reading the values from the ADC chips and

// transmitting them to the master.


void callback (void) {	


	Data[counter] = byte[0];

	if (counter >= 144) {	

		MIOS32_SPI_RC_PinSet(0,0,1);

		int i;

		// Bring data in right order

		for (i=0; i<12; i=i+2) {

			Values[8*i    ] = (Data[i*3/2 +   1]<<4)  |  ((Data[i*3/2 +   2]&0b11110000)>>4);			

			Values[8*i + 1] = (Data[i*3/2 +  19]<<4)  |  ((Data[i*3/2 +  20]&0b11110000)>>4);


			Values[8*i + 2] = (Data[i*3/2 +  37]<<4)  |  ((Data[i*3/2 +  38]&0b11110000)>>4);			

			Values[8*i + 3] = (Data[i*3/2 +  55]<<4)  |  ((Data[i*3/2 +  56]&0b11110000)>>4);


			Values[8*i + 4] = (Data[i*3/2 +  73]<<4)  |  ((Data[i*3/2 +  74]&0b11110000)>>4);			

			Values[8*i + 5] = (Data[i*3/2 +  91]<<4)  |  ((Data[i*3/2 +  92]&0b11110000)>>4);


			Values[8*i + 6] = (Data[i*3/2 + 109]<<4)  |  ((Data[i*3/2 + 110]&0b11110000)>>4);		

			Values[8*i + 7] = (Data[i*3/2 + 127]<<4)  |  ((Data[i*3/2 + 128]&0b11110000)>>4);


			Values[8*(i+1)    ] = (Data[i*3/2 +   2]<<8)  |  Data[i*3/2 +   3];			

			Values[8*(i+1) + 1] = (Data[i*3/2 +  20]<<8)  |  Data[i*3/2 +  21];


			Values[8*(i+1) + 2] = (Data[i*3/2 +  38]<<8)  |  Data[i*3/2 +  39];			

			Values[8*(i+1) + 3] = (Data[i*3/2 +  56]<<8)  |  Data[i*3/2 +  57];


			Values[8*(i+1) + 4] = (Data[i*3/2 +  74]<<8)  |  Data[i*3/2 +  75];			

			Values[8*(i+1) + 5] = (Data[i*3/2 +  92]<<8)  |  Data[i*3/2 +  93];


			Values[8*(i+1) + 6] = (Data[i*3/2 + 110]<<8)  |  Data[i*3/2 + 111];		

			Values[8*(i+1) + 7] = (Data[i*3/2 + 128]<<8)  |  Data[i*3/2 + 129];

		}


		MIOS32_SPI_RC_PinSet(0,0,0);

	} else {		

		counter++;

		MIOS32_SPI_TransferBlock(0, NULL, byte, 1, &callback);

	}		

}


////////////////////////////////////////////////////////////////////////

// Readout Timer

////////////////////////////////////////////////////////////////////////

// This Timer is called every ms to start reading values from the slave.

// It only reads one byte an then calls a callback function to read the 

// next byte until all bytes are read.

// This behaviour introduces small gaps in between the single bytes and

// give the slow 20MHz slave CPU some time to refill its sending buffers.


void Timer(void) {	

	counter = 0;

	MIOS32_SPI_TransferBlock(0, NULL, byte, 1, &callback);


}

What am I trying to do?

I'm trying to midify a real piano. For this purpose i have installed an optical sensor under each key. The sensor detects, how deep the key is depressed. I need to scan these every 1ms to get a result, that allows me to calculate a good velocity value. So there are 96 optical sensors, that need to be scanned with at least 1000Hz. I have built me a slave board with an AVR@20MHz to scan 12 8-channel-ADCs and send over the results to the STM32-Core. For the results are 12bit, I need to send them over als 144bytes, where 3 bytes actually contain only two values bit-wise.

The slave board is working fine. However, the time frame is very limited. The main problem is, that the AVR is too slow to refill it's sending buffers in between the transmission of two bytes. So i use the above version to introduce small gaps in between two bytes. This is done by the overhead of recalling the DMA after each byte. I don't know, if it's a good solution, but it seemed to work for 40ms delays. However I need 1ms (or even less).

So what happens, when I upload this code to the STM32? It freezes. I'd like to know why, because my timer function should be an easy task for this CPU (as it has hardware multiplier and division units). All I do is configuring the DMA 144 times (ok, that's sick, I know) and doing some math at the end.

Even if the time between the DMA call and the callback function is very long - there should be some time to serve USB and my UI task, too.

So here I am and I have no idea what to do... Maybe you have an idea for me,

Thank you!

Bääääär

PS: I have not been successfull using the RTOS-cpu-measurements. I followed the guide on the doxygen page, but can't compile due to missing functions (?).

  • Like 1
Link to comment
Share on other sites

You are using two timers which access the same resource, this leads to a conflict if both timers start a serial transfer back-to-back, because MIOS32_SPI_TransferBlock wouldn't queue the second request, but just overwrite the ongoing transfer (and I guess that this will lead to a hang-up)

You would have to handle the queuing resp. scheduling by yourself from a single timer.

Or better: since you are working with mS accuracy anyhow, I would propose to use a FreeRTOS thread instead, because they are typically much easier to program.

E.g. a thread would allow you to wait until the transfer is finished, it would be interrupted by other higher priority threads in a mS timewindow so that your application doesn't get stucked.

You could also add some semaphore handling later if required, this would allow you to access the same resource (e.g. the SPI port) from multiple threads without conflicts.

Here is a complete example which might be very interesting for you:

http://svnmios.midibox.org/filedetails.php?repname=svn.mios32&path=%2Ftrunk%2Fapps%2Fexamples%2Ffastscan_button_matrix_16x16%2Fapp.c

because it determines velocity as well - for up to 128 keys (256 pins)!

PS: I have not been successfull using the RTOS-cpu-measurements. I followed the guide on the doxygen page, but can't compile due to missing functions (?).

You have to add the appr. module to your Makefile:


# For performance measurings
include $(MIOS32_PATH)/modules/freertos_utils/freertos_utils.mk
[/code]

Best Regards, Thorsten.

Link to comment
Share on other sites

  • 3 weeks later...

Hi T.K.!

I have a logic analyser now and some time to go on with development. Using this new great tool, I noticed, that the delays in between two bytes are pretty large with the method of my last post. I tried the version, you advised (low priority task to read values from my slave board). Works pretty fine now after some troubleshooting...

Attached is a screenshot of some transmissions. post-7803-0-41655600-1326024706_thumb.jp

The marked area between the two blue cursors/lines is one transmission. The upper four channels show the transmission between STM32 and my slave board. The lower four lines show whats going on at the slave side (these are the lines between my slave uC and the ADC chips). You can see, after the STM32 pulls Enable low, the slave starts reading values from the ADCs. Halfways the STM32 starts reading values.

But take a look at the left side of the picture: There is a laaaarge gap and a small gap from 1ms to 4,5ms interrupting the transfer marked in red. And from 4,5ms to ~5,8ms there is another gap interrupting another transfer marked in blue. Both effectivly make the time between two transmissions very irregular. This inacceptable for me, how could I calculate corrrect velocity values from this?

Here is my code for this:

[...]



/////////////////////////////////////////////////////////////////////////////

// Local definitions

/////////////////////////////////////////////////////////////////////////////


#define PRIORITY_TASK_READOUT  ( tskIDLE_PRIORITY + 2 )



/////////////////////////////////////////////////////////////////////////////

// Prototypes

/////////////////////////////////////////////////////////////////////////////

static void TASK_ReadOut(void *pvParameters);




////////////////////////////////////////////////////////////////////////

// readout task 

////////////////////////////////////////////////////////////////////////

// This task reads data from the slave board every 1ms.

// Data arrives as 144 bytes. Three bytes actually contain only two 12bit 

// values. At the end, this function melts 1,5 bytes to one 12bit value.

// Because the values are also in the wrong order, it also sorts them

// so they are in the right oder. It would have been better to 

// let the slave CPU do this - however the 20MHz 8bit ATMega used as

// the slave is already busy reading the values from the ADC chips and

// transmitting them to the master.


static void TASK_ReadOut(void *pvParameters)	

{	

	while(1) 

	{	

		// wait for next timesplice (1 mS)

		vTaskDelay(1 / portTICK_RATE_MS);


		counter = 0;


		int i;

		for (i=0; i<144; i++) 

		{

			Data[counter] = MIOS32_SPI_TransferByte(0, 0b10010110);


			counter++;


			MIOS32_DELAY_Wait_uS(1);		


		}



		MIOS32_SPI_RC_PinSet(0,0,1);


		// Bring data in right order

		for (i=0; i<12; i=i+2) 

		{

			Values[8*i    ] = (Data[i*3/2 +   1]<<4)  |  ((Data[i*3/2 +   2]&0b11110000)>>4);			

			Values[8*i + 1] = (Data[i*3/2 +  19]<<4)  |  ((Data[i*3/2 +  20]&0b11110000)>>4);


			Values[8*i + 2] = (Data[i*3/2 +  37]<<4)  |  ((Data[i*3/2 +  38]&0b11110000)>>4);			

			Values[8*i + 3] = (Data[i*3/2 +  55]<<4)  |  ((Data[i*3/2 +  56]&0b11110000)>>4);


			Values[8*i + 4] = (Data[i*3/2 +  73]<<4)  |  ((Data[i*3/2 +  74]&0b11110000)>>4);			

			Values[8*i + 5] = (Data[i*3/2 +  91]<<4)  |  ((Data[i*3/2 +  92]&0b11110000)>>4);


			Values[8*i + 6] = (Data[i*3/2 + 109]<<4)  |  ((Data[i*3/2 + 110]&0b11110000)>>4);		

			Values[8*i + 7] = (Data[i*3/2 + 127]<<4)  |  ((Data[i*3/2 + 128]&0b11110000)>>4);


			Values[8*(i+1)    ] = (Data[i*3/2 +   2]<<8)  |  Data[i*3/2 +   3];			

			Values[8*(i+1) + 1] = (Data[i*3/2 +  20]<<8)  |  Data[i*3/2 +  21];


			Values[8*(i+1) + 2] = (Data[i*3/2 +  38]<<8)  |  Data[i*3/2 +  39];			

			Values[8*(i+1) + 3] = (Data[i*3/2 +  56]<<8)  |  Data[i*3/2 +  57];


			Values[8*(i+1) + 4] = (Data[i*3/2 +  74]<<8)  |  Data[i*3/2 +  75];			

			Values[8*(i+1) + 5] = (Data[i*3/2 +  92]<<8)  |  Data[i*3/2 +  93];


			Values[8*(i+1) + 6] = (Data[i*3/2 + 110]<<8)  |  Data[i*3/2 + 111];		

			Values[8*(i+1) + 7] = (Data[i*3/2 + 128]<<8)  |  Data[i*3/2 + 129];

		}	

		MIOS32_SPI_RC_PinSet(0,0,0);	

	}

}





/////////////////////////////////////////////////////////////////////////////

// This hook is called after startup to initialize the application

/////////////////////////////////////////////////////////////////////////////

void APP_Init(void)

{

  MIOS32_DELAY_Init(0);	


  // Init a timer for the UI

  MIOS32_TIMER_Init(1, 40000, UI, MIOS32_IRQ_PRIO_LOW);

  MIOS32_SPI_TransferModeInit(0, MIOS32_SPI_MODE_CLK1_PHASE1, MIOS32_SPI_PRESCALER_16);


  // start matrix scan task

  xTaskCreate(TASK_ReadOut, (signed portCHAR *)"ReadOut", configMINIMAL_STACK_SIZE, NULL, PRIORITY_TASK_READOUT , NULL);


  Keys_Init();	

  UI_Init(); 


  // initialize all LEDs

  MIOS32_BOARD_LED_Init(0xffffffff);


}



[...]

To get rid of these gaps i have to increase the priority of the task. But if I increase the priority, I have those hangs again...

The only chance to get around this is to force the DMA to introduce small gaps between two bytes. This way, i would not need such a cpu-consuming routine to read out values from the slave board. I could use a high-priority task to start the DMA and a low priority task to do the velocity calculation. Is this possible?

Johannes

Link to comment
Share on other sites

It isn't efficient to transfer the data bytewise, this keeps the CPU too busy.

I would recommend to use a DMA based block transfer.

Instead of:


int i;
for (i=0; i<144; i++)
{
Data[counter] = MIOS32_SPI_TransferByte(0, 0b10010110);

counter++;

MIOS32_DELAY_Wait_uS(1);
}
[/code] write:
[code]
// - shiftOutData should be an array which contains 144 x 0x96 (the TransferBlock function doesn't allow to define a default value)
// - received values will be written into Data array
// - MyDmaCallback specifies a function which will be called after the DMA transfer has been finished.
MIOS32_SPI_TransferBlock(shiftOutData, Data, 144, MyDmaCallback);

This isn't a time consuming function, the DMA will just be configured for autonomous transfer, and then the function exits immediately.

You could call it from APP_SRIO_ServicePrepare() for example - it's called each mS with high priority!

Once all bytes have been transfered, the callback function will be executed from a DMA interrupt handler.

The callback should by short as well (since it's a high-prio function). But I think that it's ok when you are doing the re-ordering stuff in there.

The received values should be processed from a FreeRTOS task, priority 3 should be fine.

/Edit: IMPORTANT: in order to avoid the data access conflict that you had before, it's still important that you copy the received data into a second array (you name it "Values") while re-ordering Data!

It makes also sense to determine and to notify changed values, an example can be found in the MIOS32_SRIO_DMA_Callback function here: http://svnmios.midibox.org/filedetails.php?repname=svn.mios32&path=%2Ftrunk%2Fmios32%2Fcommon%2Fmios32_srio.c

The MIOS32_DIN_SRChangedGetAndClear function shows you how to take over the notification from a task, see http://svnmios.midibox.org/filedetails.php?repname=svn.mios32&path=%2Ftrunk%2Fmios32%2Fcommon%2Fmios32_din.c

Best Regards, Thorsten.

Link to comment
Share on other sites

It isn't efficient to transfer the data bytewise, this keeps the CPU too busy.

I would recommend to use a DMA based block transfer.

I know. But, as I wrote in my first post: The main problem is, that the AVR on the slave board is too slow to refill it's sending buffers in between the transmission of two bytes. I need very small gaps between two bytes (only about 450ns).

I first tried to achieve these gaps by only configuring the DMA to transmit 1 byte, then in the callback to transmit the next byte until all bytes are transmitted. The overhead of reconfiguring the DMA after every byte was just ways too much, so the gaps got too big and the Read-Out function got too long so it blocked USB and other stuff.

The second attempt was my last post, but this it just too much cpu- consuming.

If I use the DMA, as you suggested, then there are no gaps and my slave can't fill its buffers until the STM32 toggles the CLK-line the next time. If I use DMA and use a higher prescaler, there would be enough time to refill the sending buffers for the slave, but the overall time for one transmission would be too long to fit into the timeframe of 1ms.

Edit: I just checked that: the time between two rising edges of CLK is 230ns, this is the time, that I have to reload the buffers at the slave side. According to the simulation in AVR Studio, the interrupt service routine to do this takes at least 0.45us. This is the fastest ISR I can program on this 20MHz CPU. It just enters the ISR (5 cycles), loads data into the register (two cycles), pops them out to the SPI Hardware (overall 9 cycles @20MHz).

I searched the web, if it's possible to make the DMA insert little gaps between two bytes, but thas seems not to be possible.

Bääääär

PS: BTW: Thanks for your support, T.K.. It's really applaudable, that you find time to make all those Midibox developments and still answer so many support questions!

Edited by Bääääär
Link to comment
Share on other sites

Thanks for the compliments! :)

I haven't read your older posting (again), therefore I didn't remember the constraints.

On the other hand, it could make sense to consider changes which allow block transfers, because currently you are facing the advantages...

144 bytes have to be transmitted within 1 mS, accordingly the max. allowed bitrate is 868 uS.

Unfortunately the next matching STM32 SPI prescaler is "MIOS32_SPI_PRESCALER_64" (-> 888.9 uS), which is a bit too much for 1 mS (144 bytes would be transfered within 1.023 mS). It would be very tricky to synchronize this properly.

But: as a counter measure, you could decrease the FreeRTOS realtime clock frequency from 1 kHz to 0.8 kHz (to give some additional headroom for the SW handling).

This can be done in programming_models/traditional/FreeRTOSConfig.h -> configTICK_RATE_HZ (change from 1000 to 800)

If this helps, I will make this constant configurable in mios32_config.h, so that later you wouldn't need to change this global configuration file.

Best Regards, Thorsten.

Link to comment
Share on other sites

Thanks for the compliments! :)

I haven't read your older posting (again), therefore I didn't remember the constraints.

On the other hand, it could make sense to consider changes which allow block transfers, because currently you are facing the advantages...

144 bytes have to be transmitted within 1 mS, accordingly the max. allowed bitrate is 868 uS.

Unfortunately the next matching STM32 SPI prescaler is "MIOS32_SPI_PRESCALER_64" (-> 888.9 uS), which is a bit too much for 1 mS (144 bytes would be transfered within 1.023 mS). It would be very tricky to synchronize this properly.

But: as a counter measure, you could decrease the FreeRTOS realtime clock frequency from 1 kHz to 0.8 kHz (to give some additional headroom for the SW handling).

This can be done in programming_models/traditional/FreeRTOSConfig.h -> configTICK_RATE_HZ (change from 1000 to 800)

If this helps, I will make this constant configurable in mios32_config.h, so that later you wouldn't need to change this global configuration file.

Best Regards, Thorsten.

Could be a solution, but there is one problem: After pulling Enable low, the slave immediately starts reading values. It needs 87,9us to get the first set of values. I already programmed this in assembler, so I can't make it much faster. I therefore have to wait at least 87,9us before I can start reading values. But let me claculate a bit...

The actual transmission takes 1023us, adding another ~90us we get 1113us, this taken as refresh-rate gives 898 Hz. I could try this.

Too sad that I loose lots of precision here. 1ms refresh rate was already a hard step, best would have been 0,8ms. (Background: pressing a key on the keyboard takes at least 1ms when pressing really hard. The faster i can read this, the better. Imagine, you press the key 0,5ms after the last readout, then I would get one value (0,5ms later) with the key half depressed and the next 1,5ms later fully depressed. The caluclated velocity would be less than it should actually be.)

Ok, tried it, but there is some strange behaviour... Prescaler settings of 32 or 64 both result in a clock rate of 3,5us. That should be prescaler_256. I did "make clean" and all that stuff. Whats wrong there?

This is my code:

void ReadOutCallback(void)	

{				

	MIOS32_SPI_RC_PinSet(0,0,1);


	int i;

	// Bring data in right order

	for (i=0; i<12; i=i+2) 

	{

		Values[8*i    ] = (Data[i*3/2 +   1]<<4)  |  ((Data[i*3/2 +   2]&0b11110000)>>4);			

		Values[8*i + 1] = (Data[i*3/2 +  19]<<4)  |  ((Data[i*3/2 +  20]&0b11110000)>>4);


		Values[8*i + 2] = (Data[i*3/2 +  37]<<4)  |  ((Data[i*3/2 +  38]&0b11110000)>>4);			

		Values[8*i + 3] = (Data[i*3/2 +  55]<<4)  |  ((Data[i*3/2 +  56]&0b11110000)>>4);


		Values[8*i + 4] = (Data[i*3/2 +  73]<<4)  |  ((Data[i*3/2 +  74]&0b11110000)>>4);			

		Values[8*i + 5] = (Data[i*3/2 +  91]<<4)  |  ((Data[i*3/2 +  92]&0b11110000)>>4);


		Values[8*i + 6] = (Data[i*3/2 + 109]<<4)  |  ((Data[i*3/2 + 110]&0b11110000)>>4);		

		Values[8*i + 7] = (Data[i*3/2 + 127]<<4)  |  ((Data[i*3/2 + 128]&0b11110000)>>4);


		Values[8*(i+1)    ] = (Data[i*3/2 +   2]<<8)  |  Data[i*3/2 +   3];			

		Values[8*(i+1) + 1] = (Data[i*3/2 +  20]<<8)  |  Data[i*3/2 +  21];


		Values[8*(i+1) + 2] = (Data[i*3/2 +  38]<<8)  |  Data[i*3/2 +  39];			

		Values[8*(i+1) + 3] = (Data[i*3/2 +  56]<<8)  |  Data[i*3/2 +  57];


		Values[8*(i+1) + 4] = (Data[i*3/2 +  74]<<8)  |  Data[i*3/2 +  75];			

		Values[8*(i+1) + 5] = (Data[i*3/2 +  92]<<8)  |  Data[i*3/2 +  93];


		Values[8*(i+1) + 6] = (Data[i*3/2 + 110]<<8)  |  Data[i*3/2 + 111];		

		Values[8*(i+1) + 7] = (Data[i*3/2 + 128]<<8)  |  Data[i*3/2 + 129];

	}	

	MIOS32_SPI_RC_PinSet(0,0,0);	

}


void Timer (void) {

	MIOS32_SPI_TransferBlock(0, NULL , Data, 144, ReadOutCallback);

}



/////////////////////////////////////////////////////////////////////////////

// This hook is called after startup to initialize the application

/////////////////////////////////////////////////////////////////////////////

void APP_Init(void)

{

  MIOS32_DELAY_Init(0);	


  // Init a timer for the UI

  MIOS32_TIMER_Init(1, 40000, UI, MIOS32_IRQ_PRIO_LOW);

  // initialize MIOS32 Timer #0, so that our "key-value readout" function is

  // called each 1,2 ms:


  // This should do the same thing like changing the rtos tick length, I guess

  MIOS32_TIMER_Init(0, 1200, Timer, MIOS32_IRQ_PRIO_LOW);


  MIOS32_SPI_TransferModeInit(0, MIOS32_SPI_MODE_CLK1_PHASE1, MIOS32_SPI_PRESCALER_64);



  Keys_Init();	

  UI_Init(); 


  // initialize all LEDs

  MIOS32_BOARD_LED_Init(0xffffffff);


}

Thanks a lot

Bääääär

Edited by Bääääär
Link to comment
Share on other sites

What about making one byte 9 bits long? Well not exactly of course :D , but I could use the DMA at prescaler_16 just like before and just read some more bytes. Effectivly my slave would miss one clock-edge every time it reloads its buffers.

So if the transmission at prescaler_16 should be

0b11111111 0b11111111 0b11111111 ... 
it would become something like
0b11111111 0b01111111 0b10111111 0b11011111 

(where 0 is the bit that the slave skipped due to reloading its buffers). I would need to transmit some more bytes, but it could work. However I would need to reassemble the received data in a very strange and mind-twisting way. :wacko:

Edited by Bääääär
Link to comment
Share on other sites

Ok, i found the reason for the strange prescaler behaviour: I have to configure it before I configure the timer. Any idea why?

So, when I use The 64 prescaler and a time of 1200us it looks very promising. But, when you look closer, there's still a lot of strange things going on.

Ok, first the overall timing: post-7803-0-34305500-1326046710_thumb.jp

My timer priority is MIOS32_IRQ_PRIO_LOW, thst's why there is this large gap on the right side I suppose? Besides that, it looks good, fits well into the timeframe of 1200us and the transmission starts after the second block of values has been read from the chips (you can separate the blocks by those tiny little spikes on the ADC_CS channel).

But when you look closer:post-7803-0-87514000-1326046718_thumb.jp

I adviced the slave to send always 0b11011011 (btw that makes the ISR for buffer-refilling 1 tick faster). But what do we see? Somehow delayed sometimes. Looks like the AVR ist still to slow...

And there is something related to MIOS32: At the end, you see that the callback is entered, before (!) the last byte is finished. What's going on there?

Bääääär

PS: I tried it with prescaler 128 and 4ms timer-cycle. This low speed gives enough time for buffer refilling and the transmission is without errors. Seems like my ISR does need some cycles more in reality than in the simulator.

What does it mean? I can't use DMA this way. I need to find a way to introduce gaps between two bytes of a transmission. at least 1us. sh*t

Edited by Bääääär
Link to comment
Share on other sites

What about making one byte 9 bits long? Well not exactly of course :D , but I could use the DMA at prescaler_16 just like before and just read some more bytes. Effectivly my slave would miss one clock-edge every time it reloads its buffers.

such tricks could lead to unstable transfers if you can't guarantee that the AVR doesn't read the dummy bit.

But I've maybe a better idea - more about this at the end of this posting.

Ok, i found the reason for the strange prescaler behaviour: I have to configure it before I configure the timer. Any idea why?

The timer already initiates SPI transfers before it has been configured (race condition).

And there is something related to MIOS32: At the end, you see that the callback is entered, before (!) the last byte is finished. What's going on there?

Funny, seems to be a bug in the STM32 port of MIOS32_SPI - I will check this later.

Probably I never noticed this since the callback method is currently only used by MIOS32_SRIO - and here it doesn't really matter if DIN SR values will be read of the last or previous scan.

Ok, now to the idea: data reduction and double byte transfers:

Data reduction: I guess that you want to scan the ADC channels with 12bit resolution since the sensor signals are not linear.

But: you could already linearize the conversion result through a lookup-table to a 8bit value.

The required table size would be 2^12 = 4kb, and I guess that your AVR has enough free memory...

Double byte transfers: you could insert dummy bytes into the datastream which are only used to trigger the ISR, and to prepare the actual data transfer.

This has the advantage, that SPI transfers could be done at a much higher clock rate than the method we tried before (extremely reduced data rate to ensure that the byte is written into the Tx buffer before the next transfer is started)

Rough calculation: assumed you would only send 8 byte values instead of 12bit values, and assumed that the stream is filled with dummy bytes, you would have to send 2*96 = 192 bytes.

But you will be able to transfer it at a much higher bitrate, let's say MIOS32_SPI_PRESCALER_8 (111.1 nS)

This means that all bytes could be transfered within 170 uS! :)

Best Regards, Thorsten.

Link to comment
Share on other sites

Rough calculation: assumed you would only send 8 byte values instead of 12bit values, and assumed that the stream is filled with dummy bytes, you would have to send 2*96 = 192 bytes.

But you will be able to transfer it at a much higher bitrate, let's say MIOS32_SPI_PRESCALER_8 (111.1 nS)

This means that all bytes could be transfered within 170 uS! :)

Best Regards, Thorsten.

Ahh, would be great, but thats too fast for the AVR. The datasheet says:

In SPI Slave mode, the control logic will sample the incoming signal of the SCK pin. To ensure

correct sampling of the clock signal, the minimum low and high periods should be:

Low periods: Longer than 2 CPU clock cycles.

High periods: Longer than 2 CPU clock cycles.

Anyways, it's a good idea to reduce data. Unfortunatly I have only a very small AVR on this board, it has just 4kB of flash anyway. So it won't be possible.

I posted a question regarding this on the http://www.mikrocontroller.net forums and a guy answered: It should be possible to make the STM32 slave and the AVR master of the transmission. That's actually a great way to do it, don't know why I didn't notice before. It's always guaranteed, that the readout happens in exactly the same intervals, no matter what the STM32 does at this time. Great!

I read through the mios32-spi files for the STM32 and it seems like it should be possible to init the DMA for SPI slave operation. I'll try to get this working, it's just too promising.

Bääääär

PS:

Double byte transfers: you could insert dummy bytes into the datastream which are only used to trigger the ISR, and to prepare the actual data transfer.

This has the advantage, that SPI transfers could be done at a much higher clock rate than the method we tried before (extremely reduced data rate to ensure that the byte is written into the Tx buffer before the next transfer is started)

I don't understand... the isr would be triggered after the dummy bytes and after the real bytes. Effectivly I would miss both. What's the trick here?

Link to comment
Share on other sites

I posted a question regarding this on the http://www.mikrocontroller.net forums and a guy answered: It should be possible to make the STM32 slave and the AVR master of the transmission. That's actually a great way to do it, don't know why I didn't notice before. It's always guaranteed, that the readout happens in exactly the same intervals, no matter what the STM32 does at this time. Great!

I also thought about this alternative solution - it should work, but you need to learn a bit more about the STM32 internals since I can't give you an example for this.

I wasn't sure if you already considered this, and therefore thought it would be better to give you a "low hanging fruit" proposal first! ;)

You won't need a DMA in this case, just configure the STM32 SPI as a slave and handle incoming bytes from an ISR.

The app. handler for MIOS32 SPI port 0 (== STM32 SPI port 1) is:

void SPI1_IRQHandler(void);

It's declared as "weak" in startup_stm32f10x_hd.c - means: just insert it into your app.c file to overload the function.

I don't understand... the isr would be triggered after the dummy bytes and after the real bytes. Effectivly I would miss both. What's the trick here?

When AVR receives the dummy byte, the ISR can put the next value into the transmit value.

STM32 will get an invalid value which is not correct because of the timing constraints.

But on the next SPI transfer the byte will be correct, because AVR had enough time to write the new value into the transmit buffer.

In other words: by duplicating the SPI transfers it's ensured that the correct value will be transmitted with each second byte

Best Regards, Thorsten.

Link to comment
Share on other sites

Haha, this forum is almost a chat ;)

When AVR receives the dummy byte, the ISR can put the next value into the transmit value.

STM32 will get an invalid value which is not correct because of the timing constraints.

But on the next SPI transfer the byte will be correct, because AVR had enough time to write the new value into the transmit buffer.

I'm not 100% sure but I remember that the transmit buffer is cleared by hardware after each byte. I didn't find something about this in the datasheet. If not, then it could work. Otherwise it's pointless. But I will try to find information about this, because it's generally a nice (but dirty) idea.

Regarding the callback-too-early problem: I found this in a german forum.

Das TC Flag signalisiert nur, dass der DMA Controller das letzte Byte

beim SPI-Puffer abgeliefert hat. Das SPI braucht dann natürlich noch ein

bischen Zeit bis der Kram rausgeschoben ist. Kein Bug, sondern

missverstandene Arbeitsweise von DMA.

Seems to be the problem. At least it's exactly what happens here.

I think, I will go with the slave solution. Can't be wrong to learn a bit more about this processor.

Thanks for your help, you're great! I guess this problem can be marked as "solved".

Bääääär

Link to comment
Share on other sites

I'm not 100% sure but I remember that the transmit buffer is cleared by hardware after each byte. I didn't find something about this in the datasheet. If not, then it could work. Otherwise it's pointless. But I will try to find information about this, because it's generally a nice (but dirty) idea.

It would be interesting for me if the transmit buffer is really cleared - I'm not an AVR user, but sometimes design flaws of other microcontrollers are interesting to know. ;)

Regarding the callback-too-early problem: I found this in a german forum.

Seems to be the problem. At least it's exactly what happens here.

I'm setting the DMA_IT_TC flag on the RX channel, not TX channel, therefore I would expect that the interrupt is triggered when the last data value has been read. But I will doublecheck this sooner or later (low prio)

Best Regards, Thorsten.

Link to comment
Share on other sites

Hi there,

I just found some time to read through several million pages of STM32 docu and DMA/SPI Example applications and put together my version of SPI0 in slave mode. I decided to use DMA, it just seemed like a good idea to me. In App-Background task I wait until the DMA sets it's flag. (I'M using no interrupts here to make it a bit easier first)

Now, what I did: I declared a DONT_USE_SPI0 so MIOS32 does not attempt to configure this peripheral. Then I copied some of your code from the spi.c file into a blank file and modified it. I saved it as hardware.c and included it in my app.c.

However, the compiler spits out lots of errors about missing definitions such as

modules\hardware.c:64: error: 'MIOS32_SPI0_MISO_PIN' undeclared (first use in this function)
These definitions must have been declared somewhere else before. All those MIOS32 functions use them too, so they must be well-known for the compiler, when it starts to compile my stuff. I tried hard to find why there are these errors, but I have no clue. See my app.c file:
#include <mios32.h>

#include "app.h"


#include <FreeRTOS.h>

#include <task.h>

#include <queue.h>


#include "modules\UI.h"

#include "modules\keys.h"


#include "modules\hardware.c" // <== This is the file, where i put my initialisations in

#include "modules\keys.c"

#include "modules\UI.c"

I can post my hardware.c file, too if neccessary.

I'm setting the DMA_IT_TC flag on the RX channel, not TX channel, therefore I would expect that the interrupt is triggered when the last data value has been read.

Trying to understand that... DMA_IT_TC - didn't find that somewhere. The datasheet notes some TC_IE and TC_IF flags. I guess you mean TC_IE here (it controls the generation of an interrupt). Becourse setting the TC_IF flag is pointless. This flag is already set by hardware long before the RX channel is finished (in concrete: it is set, when the DMA transfering from RAM to SPI has finished it's work)

Correct me if I got something wrong here (which is likely :rolleyes: )

Thanks for your great support!

Bääääär

Link to comment
Share on other sites

Hi,

These definitions must have been declared somewhere else before. All those MIOS32 functions use them too, so they must be well-known for the compiler, when it starts to compile my stuff.

I tried hard to find why there are these errors, but I have no clue.

this #define is locally declared in mios32_spi.c, since it isn't (normally) used somewhere else.

So: you have to copy&paste the #defines from there into your hardware.c file

Trying to understand that... DMA_IT_TC - didn't find that somewhere.

It's defined in the stm32f10x_dma.h file which is provided by ST: http://svnmios.midibox.org/filedetails.php?repname=svn.mios32&path=%2Ftrunk%2Fdrivers%2FSTM32F10x%2Fv3.3.0%2FSTM32F10x_StdPeriph_Driver%2Finc%2Fstm32f10x_dma.h

The datasheet notes some TC_IE and TC_IF flags. I guess you mean TC_IE here (it controls the generation of an interrupt). Becourse setting the TC_IF flag is pointless. This flag is already set by hardware long before the RX channel is finished (in concrete: it is set, when the DMA transfering from RAM to SPI has finished it's work)

Also when this flag is set for a channel which is doing a SPI->RAM transfer?

Note: I'm using two channels: one channel for RAM->SPI (TX), and another for SPI->RAM (RX) transfers.

The callback function should be called on a "RX" interrupt, but why is the interrupt triggered before the actual SPI->RAM access happened? This seems to be odd!

Best Regards, Thorsten.

Link to comment
Share on other sites

Note: I'm using two channels: one channel for RAM->SPI (TX), and another for SPI->RAM (RX) transfers.

The callback function should be called on a "RX" interrupt, but why is the interrupt triggered before the actual SPI->RAM access happened? This seems to be odd!

Without having checked that, lot's of interrupt sources are OR'ed together to trigger a common interrupt. Might be the same for the two DMA channels. Not sure about that, though.

Ok, I found this here in the datasheet:

In high-density and XL-density devices, DMA2 Channel4 and DMA2 Channel5 interrupts are

mapped onto the same interrupt vector. In connectivity line devices, DMA2 Channel4 and

DMA2 Channel5 interrupts have separate interrupt vectors. All other DMA1 and DMA2

Channel interrupts have their own interrupt vector.

Depends on what interrupts you use. Hopefully not 4+5?! (what line does the STM32F103... belong to btw?

this #define is locally declared in mios32_spi.c, since it isn't (normally) used somewhere else.

So: you have to copy&paste the #defines from there into your hardware.c file

Ouch, didn't read it carefull enough I guess. Thanks, that solved it. Yeha, I can work on :frantics:

Bääääär

Edited by Bääääär
Link to comment
Share on other sites

Sorry, T.K., I have to ask again...

I adapted your code to fit my needs, but it doesn't. The documentation for the STM32 is very rare and the demo porjects don't help a lot at all. Maybe you can help me once again...

this is my hardware.c:


#define BufferSize 144


#define MIOS32_SPI0_PTR        SPI1

#define MIOS32_SPI0_DMA_RX_PTR DMA1_Channel2

#define MIOS32_SPI0_DMA_TX_PTR DMA1_Channel3

#define MIOS32_SPI0_DMA_RX_IRQ_FLAGS (DMA1_FLAG_TC2 | DMA1_FLAG_TE2 | DMA1_FLAG_HT2 | DMA1_FLAG_GL2)

#define MIOS32_SPI0_DMA_IRQ_CHANNEL DMA1_Channel2_IRQn

#define MIOS32_SPI0_DMA_IRQHANDLER_FUNC void DMA1_Channel2_IRQHandler(void)


#define MIOS32_SPI0_RCLK1_PORT GPIOA

#define MIOS32_SPI0_RCLK1_PIN  GPIO_Pin_4

#define MIOS32_SPI0_RCLK2_PORT GPIOC

#define MIOS32_SPI0_RCLK2_PIN  GPIO_Pin_15

#define MIOS32_SPI0_SCLK_PORT  GPIOA

#define MIOS32_SPI0_SCLK_PIN   GPIO_Pin_5

#define MIOS32_SPI0_MISO_PORT  GPIOA

#define MIOS32_SPI0_MISO_PIN   GPIO_Pin_6

#define MIOS32_SPI0_MOSI_PORT  GPIOA

#define MIOS32_SPI0_MOSI_PIN   GPIO_Pin_7


#define SPI_SLAVE_Rx_DMA_FLAG        DMA1_FLAG_TC2


uint8_t Data[BufferSize];


/////////////////////////////////////////////////////////////////////////////

//! (Re-)initializes SPI IO Pins

//! By default, all output pins are configured with weak open drain drivers for 2 MHz

//! \param[in] spi_pin_driver configures the driver strength:

//! <UL>

//!   <LI>MIOS32_SPI_PIN_DRIVER_STRONG: configures outputs for up to 50 MHz

//!   <LI>MIOS32_SPI_PIN_DRIVER_STRONG_OD: configures outputs as open drain 

//!       for up to 50 MHz (allows voltage shifting via pull-resistors)

//!   <LI>MIOS32_SPI_PIN_DRIVER_WEAK: configures outputs for up to 2 MHz (better EMC)

//!   <LI>MIOS32_SPI_PIN_DRIVER_WEAK_OD: configures outputs as open drain for 

//!       up to 2 MHz (allows voltage shifting via pull-resistors)

//! </UL>

//! \return 0 if no error

//! \return -3 if unsupported pin driver mode

/////////////////////////////////////////////////////////////////////////////

s32 SPI_Slave_IO_Init(mios32_spi_pin_driver_t spi_pin_driver)

{

  // init GPIO structure

  GPIO_InitTypeDef GPIO_InitStructure;

  GPIO_StructInit(&GPIO_InitStructure);


  // select pin driver and output mode

  u32 af_mode;

  u32 gp_mode;


  switch( spi_pin_driver ) {

    case MIOS32_SPI_PIN_DRIVER_STRONG:

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      af_mode = GPIO_Mode_AF_PP;

      gp_mode = GPIO_Mode_Out_PP;

      break;


    case MIOS32_SPI_PIN_DRIVER_STRONG_OD:

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      af_mode = GPIO_Mode_AF_OD;

      gp_mode = GPIO_Mode_Out_OD;

      break;


    case MIOS32_SPI_PIN_DRIVER_WEAK:

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

      af_mode = GPIO_Mode_AF_PP;

      gp_mode = GPIO_Mode_Out_PP;

      break;


    case MIOS32_SPI_PIN_DRIVER_WEAK_OD:

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

      af_mode = GPIO_Mode_AF_OD;

      gp_mode = GPIO_Mode_Out_OD;

      break;


    default:

      return -3; // unsupported pin driver mode

  }


      // MISO is output assigned to alternate functions

      GPIO_InitStructure.GPIO_Mode = af_mode;


      GPIO_InitStructure.GPIO_Pin  = MIOS32_SPI0_MISO_PIN;

      GPIO_Init(MIOS32_SPI0_MOSI_PORT, &GPIO_InitStructure);




      // SCLK, MOSI, TCLK are inputs with pull-up

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;


      GPIO_InitStructure.GPIO_Pin  = MIOS32_SPI0_MOSI_PIN;

      GPIO_Init(MIOS32_SPI0_MISO_PORT, &GPIO_InitStructure);


      GPIO_InitStructure.GPIO_Pin  = MIOS32_SPI0_SCLK_PIN;

      GPIO_Init(MIOS32_SPI0_SCLK_PORT, &GPIO_InitStructure);


      // RCLK outputs assigned to GPIO

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

      GPIO_InitStructure.GPIO_Pin  = MIOS32_SPI0_RCLK1_PIN;

      GPIO_Init(MIOS32_SPI0_RCLK1_PORT, &GPIO_InitStructure);

      GPIO_InitStructure.GPIO_Pin  = MIOS32_SPI0_RCLK2_PIN;

      GPIO_Init(MIOS32_SPI0_RCLK2_PORT, &GPIO_InitStructure);


  return 0; // no error

}


/////////////////////////////////////////////////////////////////////////////

//! (Re-)initializes SPI peripheral transfer mode

//! By default, all SPI peripherals are configured with 

//! MIOS32_SPI_MODE_CLK1_PHASE1 and MIOS32_SPI_PRESCALER_128

//!

//! \param[in] spi_mode configures clock and capture phase:

//! <UL>

//!   <LI>MIOS32_SPI_MODE_CLK0_PHASE0: Idle level of clock is 0, data captured at rising edge

//!   <LI>MIOS32_SPI_MODE_CLK0_PHASE1: Idle level of clock is 0, data captured at falling edge

//!   <LI>MIOS32_SPI_MODE_CLK1_PHASE0: Idle level of clock is 1, data captured at falling edge

//!   <LI>MIOS32_SPI_MODE_CLK1_PHASE1: Idle level of clock is 1, data captured at rising edge

//! </UL>

//!

//! \return 0 if no error

//! \return -4 if invalid spi_mode selected

/////////////////////////////////////////////////////////////////////////////

s32 SPI_Slave_TransferModeInit( mios32_spi_mode_t spi_mode/* mios32_spi_prescaler_t spi_prescaler*/)

{

  // SPI configuration

  SPI_InitTypeDef SPI_InitStructure;

  SPI_InitStructure.SPI_Direction     = SPI_Direction_2Lines_FullDuplex;

  SPI_InitStructure.SPI_Mode          = SPI_Mode_Slave;

  SPI_InitStructure.SPI_DataSize      = SPI_DataSize_8b;

  SPI_InitStructure.SPI_FirstBit      = SPI_FirstBit_MSB;


  switch( spi_mode ) {

    case MIOS32_SPI_MODE_CLK0_PHASE0:

      SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

      SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

      break;

    case MIOS32_SPI_MODE_CLK0_PHASE1:

      SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

      SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

      break;

    case MIOS32_SPI_MODE_CLK1_PHASE0:

      SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

      SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

      break;

    case MIOS32_SPI_MODE_CLK1_PHASE1:

      SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

      SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

      break;

    default:

      return -4; // invalid SPI clock/phase mode

  }


  return 0; // no error

}


/////////////////////////////////////////////////////////////////////////////

//! Initializes SPI0 (SPI1 in hardware) peripheral transfer mode

/////////////////////////////////////////////////////////////////////////////

void SPI_Slave_Init(void)

{

  // disable callback function

  //spi_callback[1] = NULL;

  DMA_InitTypeDef DMA_InitStructure;

  DMA_StructInit(&DMA_InitStructure);

  //NVIC_InitTypeDef NVIC_InitStructure;


  // IO configuration

  SPI_Slave_IO_Init(MIOS32_SPI_PIN_DRIVER_STRONG_OD);


  // enable SPI peripheral clock (APB2 == high speed)

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);


  // enable DMA1 clock

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);


  // DMA Configuration for SPI Rx Event

  DMA_Cmd(MIOS32_SPI0_DMA_RX_PTR, DISABLE);

  DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&MIOS32_SPI0_PTR->DR;

  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Data;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

  DMA_InitStructure.DMA_BufferSize = BufferSize;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;

  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

  DMA_Init(MIOS32_SPI0_DMA_RX_PTR, &DMA_InitStructure);


  // initial SPI peripheral configuration

  SPI_Slave_TransferModeInit(MIOS32_SPI_MODE_CLK1_PHASE1);


  // enable SPI

  SPI_Cmd(MIOS32_SPI0_PTR, ENABLE);

  SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);

}


And this is my app.c:

#define DEBUG_VERBOSE_LEVEL 2


/////////////////////////////////////////////////////////////////////////////

// Include files

/////////////////////////////////////////////////////////////////////////////



#include <mios32.h>

#include "app.h"


#include <FreeRTOS.h>

#include <task.h>

#include <queue.h>


#include "modules\UI.h"

#include "modules\keys.h"


#include "modules\hardware.c"

#include "modules\keys.c"

#include "modules\UI.c"




/////////////////////////////////////////////////////////////////////////////

// This hook is called after startup to initialize the application

/////////////////////////////////////////////////////////////////////////////

void APP_Init(void)

{

  MIOS32_DELAY_Init(0);	


  // Init a timer for the UI

  MIOS32_TIMER_Init(1, 40000, UI, MIOS32_IRQ_PRIO_LOW);

  // initialize MIOS32 Timer #0, so that our "key-value readout" function is

  // called each 1,2 ms:

  //MIOS32_TIMER_Init(0, 2000, Timer, MIOS32_IRQ_PRIO_HIGH);





  Keys_Init();	// not relevant yet

  SPI_Slave_Init();

  UI_Init(); // my UI, not relevant too


  // initialize all LEDs

  MIOS32_BOARD_LED_Init(0xffffffff);


}



/////////////////////////////////////////////////////////////////////////////

// This task is running endless in background

/////////////////////////////////////////////////////////////////////////////

void APP_Background(void)

{

	while(1)

	{

		/* Wait for DMA1 transfer complete */

		while (!DMA_GetFlagStatus(SPI_SLAVE_Rx_DMA_FLAG));


		// Toggle LED

		MIOS32_BOARD_LED_Set(1, ~MIOS32_BOARD_LED_Get());


		int i;

		// Bring data in right order

		for (i=0; i<12; i=i+2) 

		{

			Values[8*i    ] = (Data[i*3/2 +   1]<<4)  |  ((Data[i*3/2 +   2]&0b11110000)>>4);			

			Values[8*i + 1] = (Data[i*3/2 +  19]<<4)  |  ((Data[i*3/2 +  20]&0b11110000)>>4);


			Values[8*i + 2] = (Data[i*3/2 +  37]<<4)  |  ((Data[i*3/2 +  38]&0b11110000)>>4);			

			Values[8*i + 3] = (Data[i*3/2 +  55]<<4)  |  ((Data[i*3/2 +  56]&0b11110000)>>4);


			Values[8*i + 4] = (Data[i*3/2 +  73]<<4)  |  ((Data[i*3/2 +  74]&0b11110000)>>4);			

			Values[8*i + 5] = (Data[i*3/2 +  91]<<4)  |  ((Data[i*3/2 +  92]&0b11110000)>>4);


			Values[8*i + 6] = (Data[i*3/2 + 109]<<4)  |  ((Data[i*3/2 + 110]&0b11110000)>>4);		

			Values[8*i + 7] = (Data[i*3/2 + 127]<<4)  |  ((Data[i*3/2 + 128]&0b11110000)>>4);


			Values[8*(i+1)    ] = (Data[i*3/2 +   2]<<8)  |  Data[i*3/2 +   3];			

			Values[8*(i+1) + 1] = (Data[i*3/2 +  20]<<8)  |  Data[i*3/2 +  21];


			Values[8*(i+1) + 2] = (Data[i*3/2 +  38]<<8)  |  Data[i*3/2 +  39];			

			Values[8*(i+1) + 3] = (Data[i*3/2 +  56]<<8)  |  Data[i*3/2 +  57];


			Values[8*(i+1) + 4] = (Data[i*3/2 +  74]<<8)  |  Data[i*3/2 +  75];			

			Values[8*(i+1) + 5] = (Data[i*3/2 +  92]<<8)  |  Data[i*3/2 +  93];


			Values[8*(i+1) + 6] = (Data[i*3/2 + 110]<<8)  |  Data[i*3/2 + 111];		

			Values[8*(i+1) + 7] = (Data[i*3/2 + 128]<<8)  |  Data[i*3/2 + 129];

		}	

	}

}


[...]

Any Idea?

Bääääär

Link to comment
Share on other sites

I'm missing some details... e.g. what did you already try out, which effects did you notice?

In order to reduce the source of errors, I would start with a simple SPI transfer routine which neither uses the DMA, nor interrupts. Just poll for incoming bytes and send a debug message (to check the content).

I would also add a possibility to observe the SPI Status register (SPI1->SR, called SPI_SR in the manual). E.g. you could print out the content with a MIOS Terminal command, or whenever a MIDI note has been received (from the MIOS Studio keyboard) - see tutorials how to implement this.

Did you already check if the data pins are correctly configured? (e.g. no short circuit)

I noticed a minor bug in your modifications:


GPIO_InitStructure.GPIO_Pin = MIOS32_SPI0_MISO_PIN;
GPIO_Init(MIOS32_SPI0_MOSI_PORT, &GPIO_InitStructure);
[/code] MISO_PIN configured for MOSI_PORT and
[code]
GPIO_InitStructure.GPIO_Pin = MIOS32_SPI0_MOSI_PIN;
GPIO_Init(MIOS32_SPI0_MISO_PORT, &GPIO_InitStructure);

MOSI_PIN configured for MISO_PORT

You haven't noticed an effect since MOSI_PORT and MISO_PORT are identical (GPIOA)

Best Regards, Thorsten.

Link to comment
Share on other sites

Thanks, I fixed that wrong configuration.

I have also tried this here in my app-background task:

	/* Wait for SPI1 data reception */

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

    /* Read SPI1 received data */

    int x;

    x = SPI_I2S_ReceiveData(SPI1);

    // Toggle LED

	MIOS32_BOARD_LED_Set(1, 0);

	}
(running in an endless loop) So if SPI1 receives something, it should turn the board led off. But it's on all the time, so I assume there's no correct transfer going on. I checked the wires, my ChipSelect is attached to RC1 which is connected to PA4 on the STM32. The datasheet says that this is the NSS Pin for SPI1. I also configured this pin as input. I don't see, why there is no sign from the SPI. It's so hard to read through the documentation and the example projects... I'm not even sure if the above code works... It's more or less a copy from one of the example projects but I didn't find where stuff like "SPI_I2S_FLAG_RXNE" is defined. The compiler gives no errors, so I hope it's defined in some header files, that have already been included by MIOS. :cry: I am currently trying to get the status register content. Ich hope to get this working tomorrow so I can post some results here. Bääääär PS: I printed the SR register with this command:
/* Wait for SPI1 data reception */

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) {

	MIOS32_LCD_CursorSet(0,0);

    MIOS32_LCD_PrintFormattedString("SR: %x", SPI1->SR);

    }

    /* Read SPI1 received data */

    int x;

    x = SPI_I2S_ReceiveData(SPI1);



    // Toggle LED

	MIOS32_BOARD_LED_Set(1, 0);

It constantly prints "SR: 2". According to the datasheet, the TXE flag is set, which is normal, becourse I did not fill the send buffer.

Edited by Bääääär
Link to comment
Share on other sites

I did some experiments today and got SPI slave transfers working! :)

The point is, that input pins have to be configured for alternative function, and not for GPIO mode, because the SPI peripheral controls the direction.

I hacked the slave mode into the existing MIOS32_SPI driver, so that you can use it without accessing the SPI peripheral directly.

Just update your repository!

This directory contains the test applications that I used:

http://svnmios.midibox.org/listing.php?repname=svn.mios32&path=%2Ftrunk%2Fapps%2Fmios32_test%2Fspi_master_slave%2F

spi_j16_master: running on a core to send 16 bytes in master mode

spi_j16_slave_polling: running on a second core to receive 16 bytes in slave mode via polling method (not recommended, only for debugging)

spi_j16_slave_dma: running on a second core to receive 16 bytes in slave mode via DMA

Note that I also fixed the callback bug - it was important to enable the DMA channel before configuring SPI

Best Regards, thorsten.

Link to comment
Share on other sites

P.S.: I improved the demo apps to cover your usecase.

In the SPI_Callback routine of spi_j16_slave_dma received data will be copied into a second buffer (rx_buffer) to ensure consistent values while new data is received.

The data_received counter is used to notify new values to TASK_SPI_Handler

Best Regards, Thorsten.

Link to comment
Share on other sites

Oh TK, you are just incredible! :frantics: :hug:

Just updated the repo and adapted your demo. Pretty straight-forward. Seems like I got some typos in there, becourse my DIN is not working anymore, but that's likely a fault of mine and i hope to find the error soon.

Can't believe what you do for your community here... Thanks so much!!

Bääääär

PS:

// init SPI port

  MIOS32_SPI_TransferModeInit(SLAVE_SPI, MIOS32_SPI_MODE_SLAVE_CLK1_PHASE1, MIOS32_SPI_PRESCALER_128);

The prescaler is not relevant here, is it?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...