goldie13 wrote on Monday, June 03, 2013:
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;
}