mirror of https://github.com/ARMmbed/mbed-os.git
249 lines
6.1 KiB
C++
249 lines
6.1 KiB
C++
/* 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
|