mbed-os
Dependents: cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more
Diff: drivers/SPI.cpp
- Revision:
- 0:b74591d5ab33
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/SPI.cpp Mon Dec 11 17:54:04 2017 +0000 @@ -0,0 +1,248 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "drivers/SPI.h" +#include "platform/mbed_critical.h" + +#if DEVICE_SPI_ASYNCH +#include "platform/mbed_sleep.h" +#endif + +#if DEVICE_SPI + +namespace mbed { + +#if DEVICE_SPI_ASYNCH && TRANSACTION_QUEUE_SIZE_SPI +CircularBuffer<Transaction<SPI>, TRANSACTION_QUEUE_SIZE_SPI> SPI::_transaction_buffer; +#endif + +SPI::SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel) : + _spi(), +#if DEVICE_SPI_ASYNCH + _irq(this), + _usage(DMA_USAGE_NEVER), +#endif + _bits(8), + _mode(0), + _hz(1000000), + _write_fill(SPI_FILL_CHAR) { + // No lock needed in the constructor + + spi_init(&_spi, mosi, miso, sclk, ssel); + _acquire(); +} + +void SPI::format(int bits, int mode) { + lock(); + _bits = bits; + _mode = mode; + // If changing format while you are the owner than just + // update format, but if owner is changed than even frequency should be + // updated which is done by acquire. + if (_owner == this) { + spi_format(&_spi, _bits, _mode, 0); + } else { + _acquire(); + } + unlock(); +} + +void SPI::frequency(int hz) { + lock(); + _hz = hz; + // If changing format while you are the owner than just + // update frequency, but if owner is changed than even frequency should be + // updated which is done by acquire. + if (_owner == this) { + spi_frequency(&_spi, _hz); + } else { + _acquire(); + } + unlock(); +} + +SPI* SPI::_owner = NULL; +SingletonPtr<PlatformMutex> SPI::_mutex; + +// ignore the fact there are multiple physical spis, and always update if it wasnt us last +void SPI::aquire() { + lock(); + if (_owner != this) { + spi_format(&_spi, _bits, _mode, 0); + spi_frequency(&_spi, _hz); + _owner = this; + } + unlock(); +} + +// Note: Private function with no locking +void SPI::_acquire() { + if (_owner != this) { + spi_format(&_spi, _bits, _mode, 0); + spi_frequency(&_spi, _hz); + _owner = this; + } +} + +int SPI::write(int value) { + lock(); + _acquire(); + int ret = spi_master_write(&_spi, value); + unlock(); + return ret; +} + +int SPI::write(const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length) { + lock(); + _acquire(); + int ret = spi_master_block_write(&_spi, tx_buffer, tx_length, rx_buffer, rx_length, _write_fill); + unlock(); + return ret; +} + +void SPI::lock() { + _mutex->lock(); +} + +void SPI::unlock() { + _mutex->unlock(); +} + +void SPI::set_default_write_value(char data) { + lock(); + _write_fill = data; + unlock(); +} + +#if DEVICE_SPI_ASYNCH + +int SPI::transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t& callback, int event) +{ + if (spi_active(&_spi)) { + return queue_transfer(tx_buffer, tx_length, rx_buffer, rx_length, bit_width, callback, event); + } + start_transfer(tx_buffer, tx_length, rx_buffer, rx_length, bit_width, callback, event); + return 0; +} + +void SPI::abort_transfer() +{ + spi_abort_asynch(&_spi); + sleep_manager_unlock_deep_sleep(); +#if TRANSACTION_QUEUE_SIZE_SPI + dequeue_transaction(); +#endif +} + + +void SPI::clear_transfer_buffer() +{ +#if TRANSACTION_QUEUE_SIZE_SPI + _transaction_buffer.reset(); +#endif +} + +void SPI::abort_all_transfers() +{ + clear_transfer_buffer(); + abort_transfer(); +} + +int SPI::set_dma_usage(DMAUsage usage) +{ + if (spi_active(&_spi)) { + return -1; + } + _usage = usage; + return 0; +} + +int SPI::queue_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t& callback, int event) +{ +#if TRANSACTION_QUEUE_SIZE_SPI + transaction_t t; + + t.tx_buffer = const_cast<void *>(tx_buffer); + t.tx_length = tx_length; + t.rx_buffer = rx_buffer; + t.rx_length = rx_length; + t.event = event; + t.callback = callback; + t.width = bit_width; + Transaction<SPI> transaction(this, t); + if (_transaction_buffer.full()) { + return -1; // the buffer is full + } else { + core_util_critical_section_enter(); + _transaction_buffer.push(transaction); + if (!spi_active(&_spi)) { + dequeue_transaction(); + } + core_util_critical_section_exit(); + return 0; + } +#else + return -1; +#endif +} + +void SPI::start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t& callback, int event) +{ + sleep_manager_lock_deep_sleep(); + _acquire(); + _callback = callback; + _irq.callback(&SPI::irq_handler_asynch); + spi_master_transfer(&_spi, tx_buffer, tx_length, rx_buffer, rx_length, bit_width, _irq.entry(), event , _usage); +} + +#if TRANSACTION_QUEUE_SIZE_SPI + +void SPI::start_transaction(transaction_t *data) +{ + start_transfer(data->tx_buffer, data->tx_length, data->rx_buffer, data->rx_length, data->width, data->callback, data->event); +} + +void SPI::dequeue_transaction() +{ + Transaction<SPI> t; + if (_transaction_buffer.pop(t)) { + SPI* obj = t.get_object(); + transaction_t* data = t.get_transaction(); + obj->start_transaction(data); + } +} + +#endif + +void SPI::irq_handler_asynch(void) +{ + int event = spi_irq_handler_asynch(&_spi); + if (_callback && (event & SPI_EVENT_ALL)) { + sleep_manager_unlock_deep_sleep(); + _callback.call(event & SPI_EVENT_ALL); + } +#if TRANSACTION_QUEUE_SIZE_SPI + if (event & (SPI_EVENT_ALL | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE)) { + // SPI peripheral is free (event happend), dequeue transaction + dequeue_transaction(); + } +#endif +} + +#endif + +} // namespace mbed + +#endif