SPI over USART on SAM4S-EK

Hi, I want to work with two SPI’s in parallel (real parallel work).
I connected one SPI to peripheral A pins PA11-PA14 CS0.
I understand that from peripheral A or peripheral B I cannot work with another SPI (CS1 for example) so it will truly be on the same time…
I probably can work with the USART SPI, if so, my question is:
Can I work with the same FreeRTOS SPI API?
I see that the init function for ASF USART SPI and regular SPI are completely different…  the read and write functions too…
So can it be done? Thanks
Assaf

SPI over USART on SAM4S-EK

The SPI driver can only be used on the dedicated SPI peripheral.  I think some SAM parts have more than one SPI peripheral.  The UART driver just works in standard 232 mode – the mode is passed in at the time of initialisation but 232 is the only supported mode at the moment. Regards.

SPI over USART on SAM4S-EK

USART can definitely be configured to work as SPI – see Atmel_11100_32-bit-Cortex-M4-Microcontroller_SAM4S_Datasheet.pdf chapter 35.7.8
But there are some definitions in freertos_spi_master.c and freertos_spi_master.h (such as #if defined (SPI)…) that prevent from other spi for example from USART to work.
The freertos_spi_master_init() is failing.
Can you show me how to change the definitions or alter other code in order the SPI USART will work?

SPI over USART on SAM4S-EK

I know the USART *can* be used in SPI mode (among other modes), my post was saying that the FreeRTOS driver only works with the dedicated SPI port rather than the USART in SPI mode. If, when using the USART in SPI mode, the registers necessary to configure and use the peripheral match (in both bit positions and offsets from the base address of the peripheral) then it would be relatively easy to use an USART with the FreeRTOS SPI driver – but I don’t think that is the case.  Therefore it would not be a trivial task.  The thing to do would be to extend the USART driver so it supports another mode.  The mode to in which the port is to be used is passed in when you initialise the port, but currently if you pass in any mode other than RS232 you will just get an error code. Regards.

SPI over USART on SAM4S-EK

You suggested to try work on the FreeRTOS USART peripheral and add a SPI mode, but I think that because USART as the  asynchronous bus implemented by the FreeRTOS API is very different implementation from SPI mode (like in configuring the PDC and other functionality to read and write at the same time), maybe it will be easier to take the FreeRTOS SPI API and change it to fit the USAR peripheral.
I looked at the code and tried to modify it to work with the USART1.
Mainly, there are only two functions I need in the FreeRTOS: ‘freertos_spi_master_init’ and  ‘freertos_spi_read_packet_async’.
Now, for the init function, I changed the MAX_SPIS to be 2 (to support both SPI and USART1 SPI) , a USART1 part:
#if defined(USART1)
{USART1, PDC_USART1, ID_USART1, USART1_IRQn}
#endif
Was added to  ‘all_spi_definitions’.       
In the ‘read’ functions, I changed the very few registers that were specific for SPI to USART registers, such as:
SPI_RDR changed to US_RHR, SPI_IER_ENDRX changed to US_IER_ENDRX.
As I understand the PDC functions should work the same for SPI and USART1 in SPI mode as long as you input the right peripheral base address… right?
Never the less, my changes does not work. I am attaching here below the two functions.
I appreciate if you could take a look and tell me if you see something wrong or if I missed something and there are other configurations need to be done…
I’ll be happy if you could tell me what other steps I need to do…
Thanks in advance, The functions:
freertos_spi_if freertos_usart_spi_master_init(Usart *p_usart_spi,
const freertos_peripheral_options_t *const freertos_driver_parameters)
{
usart_spi_opt_t opt;
portBASE_TYPE usart_spi_index;
bool is_valid_operating_mode;
freertos_spi_if return_value;
const enum peripheral_operation_mode valid_operating_modes = {SPI_MASTER}; /* Find the index into the all_spi_definitions array that holds details of
the p_spi peripheral. */
usart_spi_index = get_pdc_peripheral_details(all_spi_definitions, MAX_SPIS,
(void *) p_usart_spi); /* Check the requested operating mode is valid for the peripheral. */
is_valid_operating_mode = check_requested_operating_mode(
freertos_driver_parameters->operation_mode,
valid_operating_modes,
sizeof(valid_operating_modes) /
sizeof(enum peripheral_operation_mode)); /* Don’t do anything unless a valid p_spi pointer was used, and a valid
operating mode was requested. */
if ((usart_spi_index < MAX_SPIS) && (is_valid_operating_mode == true)) {
/* This function must be called exactly once per supported spi.  Check it
has not been called before. */
configASSERT(memcmp((void *) &(tx_dma_control),
&null_dma_control,
sizeof(null_dma_control)) == 0);
configASSERT(memcmp((void *) &(rx_dma_control),
&null_dma_control,
sizeof(null_dma_control)) == 0); /* Ensure everything is disabled before configuration. */
usart_spi_disable(all_spi_definitions.peripheral_base_address);
pdc_disable_transfer(
all_spi_definitions.pdc_base_address,
(PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS));
usart_disable_interrupt(
all_spi_definitions.peripheral_base_address,
MASK_ALL_INTERRUPTS); switch (freertos_driver_parameters->operation_mode) {
case SPI_MASTER:
/* Basic usart SPI configuration. */
opt.baudrate = gs_ul_spi_clock;
opt.char_length = US_MR_CHRL_8_BIT;
opt.spi_mode = SPI_MODE_1;
opt.channel_mode = US_MR_CHMODE_NORMAL;
/* Call the standard ASF init function. */
usart_init_spi_master( all_spi_definitions.peripheral_base_address,
&opt,
sysclk_get_cpu_hz() );
break; default:
/* No other modes are currently supported. */
break;
}
/* Create any required peripheral access mutexes and transaction complete
semaphores.  This peripheral is half duplex so only a single access
mutex is required. */
create_peripheral_control_semaphores(
freertos_driver_parameters->options_flags,
&(tx_dma_control),
&(rx_dma_control)); /* Configure and enable the SPI interrupt in the interrupt controller. */
configure_interrupt_controller(
all_spi_definitions.peripheral_irq,
freertos_driver_parameters->interrupt_priority); // No Error interrupts are available for USART SPI, page 730 on the Atmel’s data-sheet.
// /* Error interrupts are always enabled. */
// usart_enable_interrupt(
// all_spi_definitions.peripheral_base_address,
// IER_ERROR_INTERRUPTS); /* Finally, enable the peripheral. */
usart_spi_enable(all_spi_definitions.peripheral_base_address); return_value = (freertos_spi_if)p_usart_spi;
} else {
return_value = NULL;
} return return_value;
} status_code_t freertos_usart_spi_read_packet_async(freertos_spi_if p_usart_spi,
uint8_t *data, uint32_t len, portTickType block_time_ticks,
xSemaphoreHandle notification_semaphore)
{
status_code_t return_value;
pdc_packet_t pdc_tx_packet;
portBASE_TYPE usart_spi_index;
Usart *usart_spi_base;
volatile uint16_t junk_value; usart_spi_base = (Usart *) p_usart_spi;
usart_spi_index = get_pdc_peripheral_details(all_spi_definitions, MAX_SPIS,
(void *) usart_spi_base); /* Don’t do anything unless a valid SPI pointer was used. */
if (usart_spi_index < MAX_SPIS) {
/* Because the peripheral is half duplex, there is only one access mutex
and the rx uses the tx mutex. */
return_value = freertos_obtain_peripheral_access_mutex(
&(tx_dma_control), &block_time_ticks); if (return_value == STATUS_OK) {
/* Data must be sent for data to be received.  Set the receive
buffer to all 0xffs so it can also be used as the send buffer. */
/* Originally, this was the case in the spi_read_packet function. I omitted this line in order
   to write and read the same time. */
// memset((void *)data, 0xff, (size_t)len); /* Ensure Rx is already empty. */
while(usart_spi_is_rx_full(all_spi_definitions.peripheral_base_address) != 0) {
junk_value = ((Usart*) all_spi_definitions.peripheral_base_address)->US_RHR;
(void) junk_value;
} /* Start the PDC reception, although nothing is received until the
SPI is also transmitting. */
freertos_start_pdc_rx(&(rx_dma_control),
data, len,
all_spi_definitions.pdc_base_address,
notification_semaphore); /* Start the transmit so data is also received. */
pdc_tx_packet.ul_addr = (uint32_t)data;
pdc_tx_packet.ul_size = (uint32_t)len;
pdc_disable_transfer(
all_spi_definitions.pdc_base_address,
PERIPH_PTCR_TXTDIS);
pdc_tx_init(
all_spi_definitions.pdc_base_address, &pdc_tx_packet,
NULL);
pdc_enable_transfer(
all_spi_definitions.pdc_base_address,
PERIPH_PTCR_TXTEN); /* Catch the end of reception so the access mutex can be returned,
and the task notified (if it supplied a notification semaphore).
The interrupt can be enabled here because the ENDRX signal from the
PDC to the peripheral will have been de-asserted when the next
transfer was configured. */
usart_enable_interrupt(usart_spi_base, US_IER_ENDRX); return_value = freertos_optionally_wait_transfer_completion(
&(rx_dma_control),
notification_semaphore,
block_time_ticks);
}
} else {
return_value = ERR_INVALID_ARG;
} return return_value;
}

SPI over USART on SAM4S-EK

I agree with your logic that maybe updating the UART driver would be harder than updating the SPI driver, and yes in my experience the PDC registers have an identical map/layout for every peripheral (just a different base address).  However without trying this myself, reading the manual and experimenting, I’m afraid it is unlikely I could spot something wrong in your code. Regards.