Best practice to encapsulate SPI bus activity with SPI.beginTransaction and SPI.endTransaction

Looking for some good practice guidance as to how to encapsulate SPI activity with SPI.beginTransaction() and SPI.endTransaction(). I have demonstrated through testing that one or the other approach below will work and more importantly without it the application can hang. Just wondering if any of the uber experts can advise.

For example is it better to encapsulate a low level function like writedata or at a higher level were a combination of commands and data writes are made.

inline void AA_ILI9341::writedata(uint8_t c)
{
	SPI.beginTransaction(SPISettings((uint32_t)_speed*MHZ, MSBFIRST, SPI_MODE0));

	pinSetFast(_dc);		//digitalWrite(_dc, HIGH);
	pinResetFast(_cs);		//digitalWrite(_cs, LOW);
	spiwrite(c);
	pinSetFast(_cs);		//digitalWrite(_cs, HIGH);

	SPI.endTransaction();
}

or

void AA_ILI9341::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
	SPI.beginTransaction(SPISettings((uint32_t)_speed*MHZ, MSBFIRST, SPI_MODE0));

	writecommand(ILI9341_CASET); // Column addr set
	writedata(x0 >> 8);
	writedata(x0 & 0xFF);     // XSTART 
	writedata(x1 >> 8);
	writedata(x1 & 0xFF);     // XEND

	writecommand(ILI9341_PASET); // Row addr set
	writedata(y0 >> 8);
	writedata(y0);     // YSTART
	writedata(y1 >> 8);
	writedata(y1);     // YEND

	writecommand(ILI9341_RAMWR); // write to RAM

	SPI.endTransaction();
}

@armor, the “low level” function writedata() is not the lowest. It is actually spiwrite(). I would say the best place is at the high level function setAddrWindow() since the entire set of SPI transactions are to be undertaken for the command to complete. This way, a given high level SPI function could, in theory, have a different set of SPISettings() from another function. Thus the lower level functions would operate under the new settings with their code unchanged.

@peekay123 Thanks, what you are saying makes sense. I have so far implemented at the setAddrWindow() as per this example. If I take this further, when drawing a line (a primative) the process is to set the address window and then write the actual pixel data - do you think splitting the set address window and writing the pixel data is a good or bad idea? Should one keep the SPI bus access short or longer?

If you aren’t going to be blocking for too long, go ahead and hold the SPI bus while you complete your entire command. Otherwise another task could come in between your address writes and your actual data writes and take over the bus. This might be OK (depending on the peripheral timing requirements!) but feels a little weird.

If speed is a concern you should also build your operation up in a buffer then write to the SPI bus in one function call. This will be much much faster than writing the bytes one at a time for large transactions. Device OS API | Reference Documentation | Particle